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: Improvements

Making the world a better place, one piece at a time

  • High-availability, High-reliability Trash Can

    We spotted this upgrade on a recent trip to a Powerhouse Theater production:

    Vassar Old Main - High-availability Trash Can
    Vassar Old Main – High-availability Trash Can

    Compared with the older version, I’d say it’s a great improvement:

    Vassar Old Main - Broken Trash Can 1
    Vassar Old Main – Broken Trash Can 1

    Who says things never get better?

  • Random LED Dots: Entropy Library for Moah Speed with Less Gimcrackery

    A discussion over the Squidwrench Operating Table about injecting entropy into VMs before / during their boot sequence reminded me that I wanted to try the Entropy library with my 8×8 RGB LED matrix:

    8x8 RGB LED Matrix - board overview
    8×8 RGB LED Matrix – board overview

    The original version trundled along with random numbers produced by timing Geiger counter ticks. The second version, digitizing the amplified noise from a reverse-biased PN junction, ran much faster.

    What’s new & different: the Entropy library measures the jitter between the ATmega328 watchdog timer’s RC oscillator and the ceramic resonator (on Pro Mini boards) driving the CPU. It cranks out four bytes of uncorrelated bits every half-second, which isn’t quite fast enough for a sparkly display, but re-seeding the Arduino PRNG whenever enough entropy arrives works well enough.

    One could, of course, re-seed the PRNG with Geiger bits or junction noise to the same effect. The key advantage of the Entropy library: no external hardware required. The downside: no external hardware required, so, minus those techie transistors / resistors / op amps, it will look like Just Another Arduino Project.

    Reverse-bias noise amplifier - detail
    Reverse-bias noise amplifier – detail

    Le sigh.

    In any event, the Entropy library has excellent documentation and works perfectly.

    The Arduino PRNG can produce results fast enough for wonderfully twinkly output that’s visually indistinguishable from the “true” random numbers from the Geiger counter or PN junction. I dialed it back to one update every 5 ms, because letting it free-run turned the display into an unattractive blur.

    The top trace shows the update actually happens every 6 ms:

    Entropy TRNG - LED update vs refresh
    Entropy TRNG – LED update vs refresh

    The lower trace shows that each matrix row refresh takes about a millisecond. Refreshes occur on every main loop iteration and interfere with the update, not that that makes any difference. Should it matter, subtract one from the update period and it’ll be all good.

    The Arduino source code as a GitHub Gist:

    // Random LED Dots
    // Based on Entropy library using watchdog timer jitter
    // https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library
    // Ed Nisley – KE4ANU – August 2016
    #include <Entropy.h>
    //———-
    // Pin assignments
    const byte PIN_HEARTBEAT = 8; // DO – heartbeat LED
    const byte PIN_SYNC = A3; // DO – scope sync
    const byte PIN_LATCH = 4; // DO – shift register latch clock
    const byte PIN_DIMMING = 9; // AO – LED dimming control
    // These are *hardware* SPI pins
    const byte PIN_MOSI = 11; // DO – data to shift reg
    const byte PIN_MISO = 12; // DI – data from shift reg (unused)
    const byte PIN_SCK = 13; // DO – shift clock to shift reg (also Arduino LED)
    const byte PIN_SS = 10; // DO – -slave select (must be positive for SPI output)
    //———-
    // Constants
    #define UPDATE_MS 5
    //———-
    // Globals
    // LED selects are high-active bits and low-active signals: flipped in UpdateLEDs()
    // *exactly* one row select must be active in each element
    typedef struct {
    const byte Row;
    byte ColR;
    byte ColG;
    byte ColB;
    } LED_BYTES;
    // altering the number of rows & columns will require substantial code changes…
    #define NUMROWS 8
    #define NUMCOLS 8
    LED_BYTES LEDs[NUMROWS] = {
    {0x80,0,0,0},
    {0x40,0,0,0},
    {0x20,0,0,0},
    {0x10,0,0,0},
    {0x08,0,0,0},
    {0x04,0,0,0},
    {0x02,0,0,0},
    {0x01,0,0,0},
    };
    byte RowIndex;
    #define LEDS_ON 0
    #define LEDS_OFF 255
    unsigned long MillisNow;
    unsigned long MillisThen;
    //– Helper routine for printf()
    int s_putc(char c, FILE *t) {
    Serial.write(c);
    }
    //– Useful stuff
    // Free RAM space monitor
    // From http://playground.arduino.cc/Code/AvailableMemory
    uint8_t * heapptr, * stackptr;
    void check_mem() {
    stackptr = (uint8_t *)malloc(4); // use stackptr temporarily
    heapptr = stackptr; // save value of heap pointer
    free(stackptr); // free up the memory again (sets stackptr to 0)
    stackptr = (uint8_t *)(SP); // save value of stack pointer
    }
    void TogglePin(char bitpin) {
    digitalWrite(bitpin,!digitalRead(bitpin)); // toggle the bit based on previous output
    }
    void PulsePin(char bitpin) {
    TogglePin(bitpin);
    TogglePin(bitpin);
    }
    //———
    //– SPI utilities
    void EnableSPI(void) {
    digitalWrite(PIN_SS,HIGH); // make sure this is high!
    SPCR |= 1 << SPE;
    }
    void DisableSPI(void) {
    SPCR &= ~(1 << SPE);
    }
    void WaitSPIF(void) {
    while (! (SPSR & (1 << SPIF))) {
    // TogglePin(PIN_HEARTBEAT);
    continue;
    }
    }
    byte SendRecSPI(byte Dbyte) { // send one byte, get another in exchange
    SPDR = Dbyte;
    WaitSPIF();
    return SPDR; // SPIF will be cleared
    }
    void UpdateLEDs(byte i) {
    SendRecSPI(~LEDs[i].ColB); // low-active outputs
    SendRecSPI(~LEDs[i].ColG);
    SendRecSPI(~LEDs[i].ColR);
    SendRecSPI(~LEDs[i].Row);
    analogWrite(PIN_DIMMING,LEDS_OFF); // turn off LED to quench current
    PulsePin(PIN_LATCH); // make new shift reg contents visible
    analogWrite(PIN_DIMMING,LEDS_ON);
    }
    //—————
    // Set LED from integer
    // On average, this leaves the LED unchanged for 1/8 of the calls…
    void SetLED(unsigned long Value) {
    byte Row = Value & 0x07;
    byte Col = (Value >> 3) & 0x07;
    byte Color = (Value >> 6) & 0x07;
    byte BitMask = (0x80 >> Col);
    // printf("%u %u %u %u\r\n",Row,Col,Color,BitMask);
    LEDs[Row].ColR &= ~BitMask;
    LEDs[Row].ColR |= (Color & 0x04) ? BitMask : 0;
    LEDs[Row].ColG &= ~BitMask;
    LEDs[Row].ColG |= (Color & 0x02) ? BitMask : 0;
    LEDs[Row].ColB &= ~BitMask;
    LEDs[Row].ColB |= (Color & 0x01) ? BitMask : 0;
    }
    //——————
    // Set things up
    void setup() {
    pinMode(PIN_HEARTBEAT,OUTPUT);
    digitalWrite(PIN_HEARTBEAT,HIGH); // show we arrived
    pinMode(PIN_SYNC,OUTPUT);
    digitalWrite(PIN_SYNC,LOW);
    pinMode(PIN_MOSI,OUTPUT); // SPI-as-output is not strictly necessary
    digitalWrite(PIN_MOSI,LOW);
    pinMode(PIN_SCK,OUTPUT);
    digitalWrite(PIN_SCK,LOW);
    pinMode(PIN_SS,OUTPUT);
    digitalWrite(PIN_SS,HIGH); // OUTPUT + HIGH is required to make SPI output work
    pinMode(PIN_LATCH,OUTPUT);
    digitalWrite(PIN_LATCH,LOW);
    Serial.begin(57600);
    fdevopen(&s_putc,0); // set up serial output for printf()
    printf("Random LED Dots – Watchdog Entropy\r\nEd Nisley – KE4ZNU – August 2016\r\n");
    Entropy.initialize(); // start up entropy collector
    //– Set up SPI hardware
    SPCR = B01110001; // Auto SPI: no int, enable, LSB first, master, + edge, leading, f/16
    SPSR = B00000000; // not double data rate
    EnableSPI(); // turn on the SPI hardware
    SendRecSPI(0); // set valid data in shift registers: select Row 0, all LEDs off
    //– Dimming pin must use fast PWM to avoid beat flicker with LED refresh rate
    // Timer 1: PWM 9 PWM 10
    analogWrite(PIN_DIMMING,LEDS_OFF); // disable column drive (hardware pulled it low before startup)
    TCCR1A = B10000001; // Mode 5 = fast 8-bit PWM with TOP=FF
    TCCR1B = B00001001; // … WGM, 1:1 clock scale -> 64 kHz
    //– lamp test: send a white flash through all LEDs
    printf("Lamp test begins: white flash each LED…");
    digitalWrite(PIN_HEARTBEAT,LOW); // turn off while panel blinks
    analogWrite(PIN_DIMMING,LEDS_ON); // enable column drive
    for (byte i=0; i<NUMROWS; i++) {
    for (byte j=0; j<NUMCOLS; j++) {
    LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0x80 >> j;
    for (byte k=0; k<NUMROWS; k++) {
    UpdateLEDs(k);
    delay(25);
    }
    LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0;
    }
    }
    UpdateLEDs(NUMROWS-1); // clear the last LED
    printf(" done!\r\n");
    //– Preload LEDs with random values
    digitalWrite(PIN_HEARTBEAT,LOW);
    uint32_t rn = Entropy.random();
    printf("Preloading LED array with seed: %08lx\r\n",rn);
    randomSeed(rn);
    for (byte Row=0; Row<NUMROWS; Row++) {
    for (byte Col=0; Col<NUMCOLS; Col++) { // Col runs backwards, but we don't care
    LEDs[Row].ColR |= random(2) << Col; // random(2) returns 0 or 1
    LEDs[Row].ColG |= random(2) << Col;
    LEDs[Row].ColB |= random(2) << Col;
    }
    UpdateLEDs(Row);
    }
    check_mem();
    printf("SP: %u HP: %u Free RAM: %u\r\n",stackptr,heapptr,stackptr – heapptr);
    printf("Running…\r\n");
    MillisThen = millis();
    }
    //——————
    // Run the test loop
    void loop() {
    unsigned long Hash;
    uint32_t rn;
    MillisNow = millis();
    // Re-seed the generator whenever we get enough entropy
    if (Entropy.available()) {
    digitalWrite(PIN_HEARTBEAT,HIGH);
    rn = Entropy.random();
    // printf("Random: %08lx ",rn);
    randomSeed(rn);
    digitalWrite(PIN_HEARTBEAT,LOW);
    }
    // If it's time for a change, whack a random LED
    if ((MillisNow – MillisThen) > UPDATE_MS) {
    MillisThen = MillisNow;
    SetLED(random());
    }
    // Refresh LED array to maintain the illusion of constant light
    UpdateLEDs(RowIndex++);
    if (RowIndex >= NUMROWS) {
    RowIndex = 0;
    PulsePin(PIN_SYNC);
    }
    }
    view raw TimerDots.ino hosted with ❤ by GitHub
  • Kenmore Progressive Vacuum Tool Adapters: First Failure

    I picked up a horsehair dust brush from eBay as a lightweight substitute for the Electrolux aluminum ball, discovered that an adapter I’d already made fit perfectly, did the happy dance, and printed one for the brush. That worked perfectly for half a year, whereupon:

    Dust Brush Adapter - broken parts
    Dust Brush Adapter – broken parts

    It broke about where I expected, along the layer lines at the cross section where the snout joins the fitting. You can see the three perimeter shells I hoped would strengthen the part:

    Dust Brush Adapter - layer separation
    Dust Brush Adapter – layer separation

    That has the usual 15% 3D Honeycomb infill, although there’s not a lot area for infill.

    There’s obviously a stress concentration there and making the wall somewhat thicker (to get more plastic-to-plastic area) might suffice. I’m not convinced the layer bonding would be good enough, even with more wall area, to resist the stress; that’s pretty much a textbook example of how & where 3D printed parts fail.

    That cross section should look like this:

    Dust Brush Adapter - Snout infill - Slic3r preview
    Dust Brush Adapter – Snout infill – Slic3r preview

    Anyhow, I buttered the snout’s broken end with JB Kwik epoxy, aligned the parts, and clamped them overnight:

    Dust Brush Adapter - clamping
    Dust Brush Adapter – clamping

    The source code now has a separate solid model for the dust brush featuring a slightly shorter snout; if when the epoxy fails, we’ll see how that changes the results. I could add ribs and suchlike along the outside, none of which seem worth the effort right now. Fairing the joint between those two straight sections would achieve the same end, with even more effort, because OpenSCAD.

    The OpenSCAD source code as a GitHub Gist:

    // Kenmore vacuum cleaner nozzle adapters
    // Ed Nisley KE4ZNU August 2016
    // Layout options
    Layout = "DustBrush"; // MaleFitting CoilWand FloorBrush CreviceTool ScrubbyTool LuxBrush DustBrush
    //- Extrusion parameters must match reality!
    // Print with +1 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    //———————-
    // Dimensions
    ID1 = 0; // for tapered tubes
    ID2 = 1;
    OD1 = 2;
    OD2 = 3;
    LENGTH = 4;
    OEMTube = [35.0,35.0,41.7,40.5,30.0]; // main fitting tube
    EndStop = [OEMTube[ID1],OEMTube[ID2],47.5,47.5,6.5]; // flange at end of main tube
    FittingOAL = OEMTube[LENGTH] + EndStop[LENGTH];
    $fn = 12*4;
    //———————-
    // Useful routines
    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);
    }
    //——————-
    // Male fitting on end of Kenmore tools
    // This slides into the end of the handle or wand and latches firmly in place
    module MaleFitting() {
    Latch = [40,11.5,5.0]; // rectangle latch opening
    EntryAngle = 45; // latch entry ramp
    EntrySides = 16;
    EntryHeight = 15.0; // lower edge on *inside* of fitting
    KeyRadius = 1.0;
    translate([0,0,6.5])
    difference() {
    union() {
    cylinder(d1=OEMTube[OD1],d2=OEMTube[OD2],h=OEMTube[LENGTH]); // main tube
    hull() // insertion guide
    for (i=[-(6.0/2 – KeyRadius),(6.0/2 – KeyRadius)],
    j=[-(28.0/2 – KeyRadius),(28.0/2 – KeyRadius)],
    k=[-(26.0/2 – KeyRadius),(26.0/2 – KeyRadius)])
    translate([(i – (OEMTube[ID1]/2 + OEMTube[OD1]/2)/2 + 6.0/2),j,(k + 26.0/2 – 1.0)])
    sphere(r=KeyRadius,$fn=8);
    translate([0,0,-EndStop[LENGTH]]) // wand tube butts against this
    cylinder(d=EndStop[OD1],h=EndStop[LENGTH] + Protrusion);
    }
    translate([0,0,-OEMTube[LENGTH]]) // main bore
    cylinder(d=OEMTube[ID1],h=2*OEMTube[LENGTH] + 2*Protrusion);
    translate([0,-11.5/2,23.0 – 5.0]) // latch opening
    cube(Latch);
    translate([OEMTube[ID1]/2 + EntryHeight/tan(90-EntryAngle),0,0]) // latch ramp
    translate([(Latch[1]/cos(180/EntrySides))*cos(EntryAngle)/2,0,(Latch[1]/cos(180/EntrySides))*sin(EntryAngle)/2])
    rotate([0,-EntryAngle,0])
    intersection() {
    rotate(180/EntrySides)
    PolyCyl(Latch[1],Latch[0],EntrySides);
    translate([-(2*Latch[0])/2,0,-Protrusion])
    cube(2*Latch[0],center=true);
    }
    }
    }
    //——————-
    // Refrigerator evaporator coil wand
    module CoilWand() {
    union() {
    translate([0,0,50.0])
    rotate([180,0,0])
    difference() {
    cylinder(d1=EndStop[OD1],d2=42.0,h=50.0);
    translate([0,0,-Protrusion])
    cylinder(d1=35.0,d2=35.8,h=100);
    }
    translate([0,0,50.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Samsung floor brush
    module FloorBrush() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.4,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.4,d2=30.7,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Crevice tool
    module CreviceTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.0,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.0,d2=30.4,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Mystery brush
    module ScrubbyTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=31.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=31.8,d2=31.0,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=26.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // eBay horsehair dusting brush
    module DustBrush() {
    union() {
    translate([0,0,40.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=31.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=31.6,d2=31.8,h=30.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=26.0,d2=24.0,h=100);
    }
    translate([0,0,40.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Electrolux brush ball
    module LuxBrush() {
    union() {
    translate([0,0,30.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=30.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=30.8,d2=30.0,h=20.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=25.0,d2=23.0,h=30 + 2*Protrusion);
    }
    translate([0,0,30.0 – Protrusion])
    MaleFitting();
    }
    }
    //———————-
    // Build it!
    if (Layout == "MaleFitting")
    MaleFitting();
    if (Layout == "CoilWand")
    CoilWand();
    if (Layout == "FloorBrush")
    FloorBrush();
    if (Layout == "CreviceTool")
    CreviceTool();
    if (Layout == "DustBrush")
    DustBrush();
    if (Layout == "ScrubbyTool")
    ScrubbyTool();
    if (Layout == "LuxBrush")
    LuxBrush();

     

  • Kenmore Model 158: Needle Lights, Now With Moah LEDs

    The first pass at retrofitting SMD LEDs to light the needle area in Mary’s Model 158 sewing machine worked well enough:

    Kenmore 158 Needle Light - heatsink
    Kenmore 158 Needle Light – heatsink

    However, she wanted more light on the right side of the needle, so now she has it:

    Needle LEDs - front
    Needle LEDs – front

    That’s without any LEDs along the front and back of the arm, hence the dark pool beyond the sewing machine’s base.

    Those are the same 5050 warm white LEDs I used on the other side:

    Needle LEDs - lower right
    Needle LEDs – lower right

    Seen without the glare:

    Needle LEDs - bottom
    Needle LEDs – bottom

    They’re mounted on a 32 mil brass strip from the shimstock stash, carefully hand-bent and twisted to match the curvature of the arm, and held in place with JB Kwik steel-filled epoxy for good heat conduction to the aluminum arm. One can argue with the epoxy oozing out from under the brass, but it’s invisible from above.

    No construction photos, alas, because I made this in a white-hot frenzy one afternoon and managed to not take any pix during the entire session. Call it working in the flow, OK?

    All four SMD LEDs sit in epoxy blobs that isolate them from the brass strip, with 26 AWG solid wire “bus bars” soldered to the top of their terminals and a length of that lovely PTFE-insulated miniature coax leading off into the endcap. More epoxy encloses all the wiring & connections to provide a surprisingly smooth surface that shouldn’t snag the fabric.

    The power supply uses an 18 W 120 VAC to 12 VDC brick intended for small LED installations:

    Needle LEDs power supply - exterior
    Needle LEDs power supply – exterior

    The AC comes from the same zip cord that formerly supplied the original 15 W incandescent bulb in the endcap, so the new lights behave the same way: push the power button to turn on the machine and the LEDs pop on just like they should. I put quick-disconnect terminals in the AC line to make it removable, although those need some sort of insulated plug to cover the exposed blades inside their housing.

    Inside the black box, a small boost supply steps the voltage up to just under the nominal operating level of 21 VDC:

    Needle LEDs power supply - interior
    Needle LEDs power supply – interior

    You can just see the adjusting screw hole in front of the AC brick in the overall view.

    The DC output exits in the middle of the far side, through a coax jack epoxied to the base.

    As before, all six LEDs run in parallel at (for now) 18.5 VDC and maybe 50 mA each, for a total of 300 mA, and seem fearsomely bright even at that. We can now tune for best light as needed.

    This is a major major major improvement over the previous tangle of wires stuck on the outside of the machine, with all the wiring internal to the arm and the power supply out of sight under the sewing table.

    After an hour, the arm above the four LEDs runs 13 °C above ambient and the endcap over the two LED heatsink is 6 °C over ambient. The AC supply runs at 104 °C and its plastic case offers no provision for heatsinking. All in all, things are warm and not hazardous.

    I haven’t retrofit this machine with LED strips along the front & back of the arm, as those may not be needed with the intense needle lighting; the NisLite desk lamp may suffice for area illumination.

  • Vacuum Tube LEDs: 500 W Frosted Incandescent Bulb

    This turned out surprisingly well:

    500 W Incandescent - backlit dark
    500 W Incandescent – backlit dark

    In the harsh light of the Electronics Workbench, you can see there’s less than meets the eye: a single knockoff Neopixel taped to the back side of the bulb just below the equator and a knockoff Arduino Pro Mini taped to the Mogul lamp socket:

    500 W Incandescent - backlit light
    500 W Incandescent – backlit light

    The electrical box serves as a base and the cord doesn’t do anything in this incarnation.

    The 5050 SMD LED package (inside an ugly 3D printed plate cap) looks enough like a point source to shadow the filament & support structure against the frosted bulb. The blurry upper part of the filament is closer to the LED, which isn’t really a point source and must fight its way through the frosting.

    The Pro Mini runs the same firmware as the Bowl o’ Fire floodlamp, of course, dialed back for slow fades.

    It lights up the room something wonderful …

  • LF Loop Antenna: GPS Frequency Check

    I stuck some old 12 V 7 A·h batteries in my homebrew power supply for the HP 3801A GPS Time / Frequency Standard, fired it up, put the antenna where it could see a good chunk of the sky, gave it a day to warm up / settle out, and it’s perfectly happy:

    ------------------------------- Receiver Status -------------------------------
    
    SYNCHRONIZATION ............................................. [ Outputs Valid ]
    SmartClock Mode ___________________________   Reference Outputs _______________
    >> Locked to GPS                              TFOM     3             FFOM     0
       Recovery                                   1PPS TI -38.3 ns relative to GPS
       Holdover                                   HOLD THR 1.000 us
       Power-up                                   Holdover Uncertainty ____________
                                                  Predict  366.2 us/initial 24 hrs
    
    ACQUISITION ............................................ [ GPS 1PPS CLK Valid ]
    Satellite Status __________________________   Time _____ +1 leap second pending
    Tracking: 4        Not Tracking: 6            UTC      18:22:19     22 Jul 2016
    PRN  El  Az   SS   PRN  El  Az                1PPS CLK Synchronized to UTC
      3  34 104   48   * 1  36  48                ANT DLY  0 ns
     17  62 308  103     6  27 220                Position ________________________
     19  39 281   50    11  21  58                MODE     Hold
     28  80 133   64   *22  Acq .
                        24  12 319                LAT      N  41:39:32.328
                        30  15 191                LON      W  73:52:26.733
    ELEV MASK 10 deg   *attempting to track       HGT               +82.87 m  (MSL)
    HEALTH MONITOR ......................................................... [ OK ]
    Self Test: OK    Int Pwr: OK   Oven Pwr: OK   OCXO: OK   EFC: OK   GPS Rcv: OK
    scpi >
    

    The FFOM 0 entry says the Frequency Figure Of Merit is “within specifications” of 10-9, averaged over one day. That means the actual frequency should be within 0.010 Hz of 10 MHz.

    Feeding the 10 MHz frequency reference into the (equally warmed up) HP 8591E spectrum analyzer and selecting an absurdly narrow span produces a comforting sight:

    HP Z2801A GPS Receiver - 10 MHz ref - HP 8591E
    HP Z2801A GPS Receiver – 10 MHz ref – HP 8591E

    Given the horizontal resolution, that’s dead on 10 MHz.

    So, yeah, that signal at 57-ish kHz really isn’t at 60.000 kHz:

    Loop - 40T 1nF - spectrum
    Loop – 40T 1nF – spectrum

    Which is good to know …

  • Michelin Protek Max Tubes

    Within the space of four days, we had three rear-tire flats:

    • A tire liner wear-through, after which I didn’t replace the liner
    • Four miles later, a blowout through a tread gash previously covered by the tire liner
    • A puncture flat directly through the tread

    Basically, erosion from the (last remaining, I think) liner in the rear tire of Mary’s bike caused the first flat; I patched the tube and didn’t notice the gash. After the blowout, I patched the tube again, booted the gash (with a snippet from a roll of PET bottle plastic I carry around for exactly that purpose), stuck an ordinary patch atop the boot to cover its edges, and the whole mess has held air just fine for the last week. I’m reluctant to mess with success.

    Not having a tire liner caused the third flat, this time on my bike. The wound looked like a nail or glass shard punched directly through the Kevlar armor behind the tread. Fortunately, it happened (or, more exactly, I realized I had a flat) half a mile from home, so I fired a CO2 cartridge into the tube and pedaled like crazy, which got me halfway to the goal and I rolled the rest of the way on a dead-flat tire.

    Ya can’t win.

    So I picked up a pair of Michelin Protek Max tubes, the weirdest things I’ve ever stuffed into a bike tire:

    Michelin Protek Max Tube - carton
    Michelin Protek Max Tube – carton

    The bumps along the tread surface are much larger and uglier than shown in that picture:

    Michelin Protek Max tube
    Michelin Protek Max tube

    The rubber forming the protrusions has the same thickness as the rest of the tube, so you’re looking at soft, flexible shapes, rather than thick bumps.

    The “liquid” inside must be a thin film over the inner surface. I’ve never been a big fan of tire sealants, mostly because they’re reputed to ooze to the bottom of the tire into off-balance puddles.

    For future reference, the Official Quasi-Instruction Manual / Blurb (clicky for more dots):

    Michelin Protek Max Tube - instructions
    Michelin Protek Max Tube – instructions

    We’ll see how well these work…