The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Tag: SDR

Software Defined Radios and circuitry

  • Quartz Tuning Fork Resonator Teardown

    Thinking of a 60 kHz crystal filter front end for the WWVB receiver brought a little bag of 32.768 kHz crystals to the surface; I figured I could use them as crash test dummies while a bag of 60 kHz crystals travels around the planet. Come to find out they don’t behave quite like crystals and a bit of investigation shows the little cans contain tuning fork resonators, not crystal slabs.

    I had to see that, so I grabbed the base of one in a pin vise:

    Quartz resonator - pin vise
    Quartz resonator – pin vise

    I don’t know the part number for those resonators, but it’s something like AT26, where the “26” means a cylindrical can 2 mm OD and 6 mm long, more or less.

    Notching the can at the chuck with a triangular file, then wiggling the can with needle-nose pliers, eventually broke it off:

    Quartz resonator - A side
    Quartz resonator – A side

    The other side:

    Quartz resonator - B side
    Quartz resonator – B side

    A look through the microscope show they’re transparent, with laser trim scars on the ends:

    Quartz resonator - detail
    Quartz resonator – detail

    The “holes” are unplated quartz areas, clear as the finest glass.

    Not what I was expecting to see, at all!

  • SMA Attenuators vs. Broadcast FM vs. NooElec SDR

    Four SMA attenuators arrived from halfway around the planet:

    SMA Attenuators
    SMA Attenuators

    The top line has ATTENUATOR wrapped around the body. They’re rated for 2 W = +33 dBm, suitable for antennas and SDR and suchlike, not real radios or even HTs.

    That assortment provides 39 dB of attenuation in 3 dB steps:

    • 3 6 9
    • 10 13 16 19
    • 20 23 26 29
    • 30 33 36 39

    Sweeping them on the spectrum analyzer shows they’re doing what they claim, to within the resolution of the analyzer, and remain flat through 1.5 GHz, where my cheap N-to-SMA adapter cables roll off by 3 dB. Stacking them produces 38 dB of attenuation, which is certainly the small difference of large values and fine for my simple needs.

    Conversely, a quick test with a NooElec SDR shows plenty of hocus-pocus betwixt antenna and display: the RF doesn’t attenuate nearly the way you’d (well, I’d) expect.

    Direct from the antenna, with AGC off and 50 dB of RF gain:

    WPDH Spectrum - 0 dB atten
    WPDH Spectrum – 0 dB atten

    3 dB attenuator:

    WPDH Spectrum - 3 dB atten
    WPDH Spectrum – 3 dB atten

    6 dB attenuator:

    WPDH Spectrum - 6 dB atten
    WPDH Spectrum – 6 dB atten

    10 dB attenuator:

    WPDH Spectrum - 10 dB atten
    WPDH Spectrum – 10 dB atten

    20 dB attenuator:

    WPDH Spectrum - 20 dB atten
    WPDH Spectrum – 20 dB atten

    Ain’t nothin’ simple…

  • NESDR Mini 2+ vs. Input Terminator

    A tiny handful of known-good-quality SMA terminators arrived from eBay:

    KDI T187GS - 50 ohm 1 W SMA attenuators
    KDI T187GS – 50 ohm 1 W SMA attenuators

    They’re described as KDI Triangle T187GS SMA Female Terminator, 50Ω, 1W, 0-4GHz. A bit of searching suggests MCE (whoever they are) borged KDI quite a while ago (their website, last updated in 2003, has been lightly vandalized) and a datasheet won’t be forthcoming.

    In any event, a NooElec NESDR Mini 2+ radio connected to a dual-band VHF-UHF antenna perched near a window shows this for a local FM station:

    FM 101.5 NESDR - direct
    FM 101.5 NESDR – direct

    Zooming to 5 dB/div:

    FM 101.5 NESDR - 5 dB steps
    FM 101.5 NESDR – 5 dB steps

    Installing the terminator at the end of an MCX-to-SMA adapter cable:

    FM 101.5 NESDR - 50 ohm terminator
    FM 101.5 NESDR – 50 ohm terminator

    Haven’t a clue about those tiny little spikes with the terminator in place, but they don’t line up with any of the high-energy inputs and are, most likely, junk brewed up within the radio. That’s with the RF gain set to 49.6 dB and AGC turned off.

    The hardware looks like this:

    NESDR with SMA attenuators
    NESDR with SMA attenuators

    The MCX connector on the radio isn’t the most durable-looking thing I’ve ever seen, so strapping the adapter cable to the case seems like a Good Idea. You can get an NESDR radio with an SMA connector for about the same price, which I’d have done if were available a while ago.

    The terminated input looks to be about -75 dBFS, about 15 dB below the between-station noise, and the carrier tops out around -25 dBFS, for a “dynamic range” of 50 dB. Oddly, that’s just about dead on the maximum dynamic range you can get from the 8 bit RTL2832U demodulator / ADC stuffed inside the NESDR: 8 bits × 6 dB/bit.

    It is not obvious to me the signal from a randomly chosen (albeit powerful) FM station should exactly fill the receiver’s dynamic range, particularly without AGC riding herd on the RF gain. Some hardware tinkering seems in order.

    The GNU Radio flow graph:

    FM Broadcast - GNU Radio flow
    FM Broadcast – GNU Radio flow

     

  • Monthly Science: WWVB Reception Sample

    Further results from the SDR-based WWVB receiver:

    60 kHz Receiver - preamp HIT N3 Pi3 - attic layout
    60 kHz Receiver – preamp HIT N3 Pi3 – attic layout

    Seven hours of mid-January RF, tight-zoomed in both frequency and amplitude, from 0350 to 1050 local:

    WWVB waterfall - N3 - 2017-01-24 1050 - composite
    WWVB waterfall – N3 – 2017-01-24 1050 – composite

    The yellow line of the WWVB carrier comes out 2 ppm high, which means the local oscillator chain is 2 ppm low. We know the WWVB transmitter frequency is exactly 60.000 kHz, translated up by 125 MHz to the N3’s tuning range; you can, ahem, set your clock by it.

    The blue band marks the loop antenna + preamp passaband, which isn’t quite centered around 60.000 kHz. Tweaking the mica compression caps just a bit tighter should remedy that situation.

    Given that input, a very very tight bandpass filter should isolate the WWVB carrier and then it’s all a matter of fine tuning…

  • WWVB Receiver: First Light!

    All the blocks for a WWVB receiver, lined up on the attic floor:

    60 kHz Receiver - preamp HIT N3 Pi3 - attic layout
    60 kHz Receiver – preamp HIT N3 Pi3 – attic layout

    The dramatis personae:

    The headless Pi connects to the house WLAN through its built-in WiFi link, so I can run the whole mess from the Comfy Chair at my desk through Remmina / VNC.

    Recording 24 hours of WWVB shows it works:

    WWVB - 24 hr reception AGC - 2017-01-16 to 17 - cropped
    WWVB – 24 hr reception AGC – 2017-01-16 to 17 – cropped

    The wavy line along the left edge looks like a birdie formed by a local oscillator in the attic, because the frequency varies (inversely) with temperature. It’s probably a signal on the Pi board, rectified by some junction, and translated in-band by some Ham-It-Up harmonic. Whatever.

    The other traces come out bar-straight, suggesting that the 0.5 ppm (presumably, per °C) temperature-compensated oscillators along the whole RF chain behave as they should.

    There’s a slight frequency shift, on the order of a few parts-per-million, between the absolutely accurate WWVB carrier and the indicated display. Not a big deal.

    The broad, albeit irregular, orange band down the middle shows the loop antenna / preamp bandwidth, which is on the order of 2 kHz at -3 dB and a few kilohertz more down to the noise level.

    The broad horizontal gashes seem to come from the N3’s on-board hardware AGC reacting to signals far outside the waterfall. Various birdies appear & disappear, even in this limited view, so you can just imagine what’s happening off-screen; anything popping up within the SDR’s tuning range clobbers the gain, which becomes painfully visible when zoomed this far in along both frequency and amplitude. Turning AGC off should stabilize things; perhaps software can tweak the SDR gain based on a very narrowband filter around 60.000 kHz.

    The upper half of the waterfall shows decent reception for most of the night. The bottom half shows there’s basically nothing goin’ down during the day, which is about what I’d expect based watching the Alpha Geek Clock for seven years.

    In any event, another 24 hours with the AGC turned off looks better:

    WWVB 24 hr waterfall - Thumbnet N3 - 2017-01-19
    WWVB 24 hr waterfall – Thumbnet N3 – 2017-01-19

    Various sources still clobber the receiver response, but it’s not quite so dramatic.

     

  • Unicode Keyboard Flameout and Workaround

    For unknown reasons, probably having to do with the unmitigated disaster of trying to get an SDRPlay radio working with GNU Radio (about which, more later), Unicode keyboard input stopped working. This is not to be tolerated, because engineering notation requires a lot of Greek letters.

    Unicode support seems to be baked into the lowest levels of the Linux operating system, although it’s not clear to me whether it’s in X, QT, GTK, or somewhere else. Googling the obvious keywords was unavailing; evidently this feature never ever fails or, more likely, very few people use it to any extent.

    Note that I already have the Compose key set up, but Compose sequences don’t include Greek letters.

    After considerable flailing, I added the Simple Greek keyboard layout and defined the (otherwised unused) Menu key as the keyboard layout switcher. That’s a pretty big hammer for a rather small problem; I devoutly hope Unicode mysteriously starts working again.

    For reference, the Greek keyboard layout looks like this:

    Greek keyboard layout
    Greek keyboard layout

    I’d have put Ω on the W key, rather than V, but that’s just because so many fonts do exactly that.

  • 60 kHz Preamp: Board Holder

    A cleaned up version of my trusty circuit board holder now keeps the 60 kHz preamp off what passes for a floor in the attic:

    Preamp in attic
    Preamp in attic

    The solid model became slightly taller than before, due to a serious tangle of wiring below the board, with a narrower flange that fits just as well in the benchtop gripper:

    Proto Board - 80x110
    Proto Board – 80×110

    Tidy brass inserts epoxied in the corners replace the previous raw screw holes in the plastic:

    Proto Board Holder - 4-40 inserts and screws
    Proto Board Holder – 4-40 inserts and screws

    The screws standing on their heads have washers epoxied in place, although that’s certainly not necessary; the dab of left-over epoxy called out for something. The screws got cut down to 7 mm after curing.

    The preamp attaches to a lumpy circle of loop antenna hung from the rafters and returns reasonable results:

    WWVB - morning - 2017-01-16
    WWVB – morning – 2017-01-16

    The OpenSCAD source code as a GitHub Gist:

    // Test support frame for proto boards
    // Ed Nisley KE4ZNU – Jan 2017
    ClampFlange = true;
    Channel = false;
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    inch = 25.4;
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [3.9,4.6,5.8];
    //- PCB sizes
    PCBSize = [110.0,80.0,1.5];
    PCBShelf = 2.0;
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    WallThick = 5.0;
    FrameHeight = 10.0;
    ScrewOffset = 0.0 + Clear4_40/2;
    ScrewSites = [[-1,1],[-1,1]]; // -1/0/+1 = left/mid/right and bottom/mid/top
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2];
    FlangeExtension = 3.0;
    FlangeThick = IntegerMultiple(2.0,ThreadThick);
    Flange = PCBSize
    + 2*[ScrewOffset,ScrewOffset,0]
    + 2*[Washer4_40OD,Washer4_40OD,0]
    + [2*FlangeExtension,2*FlangeExtension,(FlangeThick – PCBSize[2])]
    ;
    echo("Flange: ",Flange);
    NumSides = 4*5;
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]];
    WireChannelOffset = [Flange[0]/2,25.0,(FrameHeight + PCBSize[2] – WireChannel[2]/2)];
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Build it
    difference() {
    union() { // body block
    translate([0,0,OAHeight/2])
    cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    for (x=[-1,1], y=[-1,1]) { // screw bosses
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    }
    if (ClampFlange) // flange for work holder
    linear_extrude(height=Flange[2])
    hull()
    for (i=[-1,1], j=[-1,1]) {
    translate([i*(Flange[0]/2 – Washer4_40OD/2),j*(Flange[1]/2 – Washer4_40OD/2)])
    circle(d=Washer4_40OD,$fn=NumSides);
    }
    }
    for (x=[-1,1], y=[-1,1]) { // screw position indexes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6); // screw clearance holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2] – Insert[LENGTH]])
    rotate(x*y*180/(2*6))
    PolyCyl(Insert[OD],Insert[LENGTH] + Protrusion,6); // inserts
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // washers
    }
    translate([0,0,OAHeight/2]) // through hole below PCB
    cube(PCBSize – 2*[PCBShelf,PCBShelf,0] + [0,0,2*OAHeight],center=true);
    translate([0,0,(OAHeight – (PCBSize[2] + Clearance[2])/2 + Protrusion/2)]) // PCB pocket on top
    cube(PCBSize + Clearance + [0,0,Protrusion],center=true);
    if (Channel)
    translate(WireChannelOffset) // opening for wires from bottom side
    cube(WireChannel + [0,0,Protrusion],center=true);
    }