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.

Category: Software

General-purpose computers doing something specific

  • WWVB Time Code Format Diagram

    This is by-and-large the same diagram of the WWVB Time Code Format that you’ll find there, but with:

    • the PR frame reference pulse identified
    • the last four bits clearly labeled
    • some verbiage chopped out
    WWVB Time Code Format - with PR marker
    WWVB Time Code Format – with PR marker

    Memo to Self: Remember that…

    • The time code applies to the minute that’s currently happening, so …
    • The PR pulse happens before the rest of the time code arrives
    • You must know the end-of-month date to apply the leap second bit, but EOM isn’t part of the data stream
  • Extracting Text from PDF Documents

    Mary had to extract an extensive table from a PDF (think financial statements) and found that a simple cut-and-paste failed with mysterious symptoms. I’ve had that happen, too, as PDF documents sometimes have a complete disconnect between the rendered page and the original text; sometimes you can’t even select the text block you want without copying huge chunks of the surrounding document or pasting meaningless junk.

    Easy solution: feed the PDF into pdftotext and extract the table from the ensuing flat text file.

    It’s a command-line thing:

    
    pdftotext -layout whatever.pdf
    
    

    That produces whatever.txt with the ASCII characters bearing more-or-less the same spatial arrangement as the original PDF, minus all the font and graphic frippery. It tends to insert a ton of blanks in an attempt to make the formatting come out right, which may not be quite what you want.

    Omitting the -layout option gives you something vaguely resembling the PDF, although precisely arranged tables tend to fare poorly.

    If you have a bazillion-page PDF document and need the text from just a page or two, feed it into the pdftk brush chipper, extract the appropriate pages, and then run those files through pdftotext. You can probably get similar results using just pdftk, but pdftotext seems to work better on the files I’ve had to deal with.

    This is a GNU/Linux thing; the programs are likely part of your favorite distribution; follow the links if not. If you’re still using Windows, maybe they’ll work for you, but maybe it’d be easier to just go buy something similar.

  • WWVB Reception Quality

    Here on the East Coast of the US, WWVB reception is iffy during the day, due to low signal strength and high ambient noise. Actual data seems hard to come by, so here’s a small contribution.

    This is a plot of the number of glitches per minute, where a glitch is any pulse that’s not within ±60 ms of the expected pulse durations (200, 500, and 800 ms), for a 24-hour period starting at UTC 0257 on 25 Dec 2009 (9:57 EST Christmas Eve 2009). There are 1448 data points, each representing the glitches during the previous minute; each minute starts within 2 seconds of the WWVB on-time frame marker.

    Here’s the raw data, log-scaled on the Y axis to cover the dynamic range. Log scaling can’t handle 0-valued points, so I forced counts of 0 to 0.1 to make them visible.

    Glitchiness - raw - 24 hrs - 2009-12-24
    Glitchiness – raw – 24 hrs – 2009-12-24

    Here’s the same data, Bezier smoothed to make the trends more obvious;  all the points below 1.0 are approximations of a trend toward counts of 0.

    Glitchiness - smoothed - 24 hrs - 2009-12-24
    Glitchiness – smoothed – 24 hrs – 2009-12-24

    Even better, splines show the glitch-free minutes without forcing the data points.

    Glitchiness - splines - 24 hrs - 2009-12-24
    Glitchiness – splines – 24 hrs – 2009-12-24

    My firmware requires four successive glitch-free minutes of reception (plus some additional verification) before synching its local time to WWVB, so it’s exceedingly fussy. Despite that, it still synched 17 times during those 24 hours. The longest free-running time between synchs was 6.8 hours.

    Note that there are 17 downward peaks below 1.0 in that last graph.

    Winter is, of course, the time of best ground-wave propagation from WWVB, so this is about as good as it’s ever going to get.

    Memo to Self: useful Bash and Gnuplot commands…

    
    grep Glitch WWVB_2009-12-24a.log | cut -d H -f 1 > Glitches.txt
    
    set logscale y
    
    set samples 250
    
    plot 'Glitches.txt' using ($2<1?0.1:$2) with points lt 3 pt 2
    
    plot 'Glitches.txt' using 2 smooth csplines with linespoints lt 3 pt 0
    
    
    log-scaled on the Y axis to cover the dynamic range. Log scaling can’t handle 0-valued points, so I forced them to 0.1;
  • HP54602 Oscilloscope Trace Conversion Tweakage

    The script (writeups there and there) I use to convert the HPGL screen dumps from my HP54602 into PNG images produced a transparent background. I put the files into an OpenOffice mockup of my Circuit Cellar columns and the background turns white, so I figured it worked OK.

    Turns out that the workflow at Circuit Cellar Galactic HQ turns the background black. A bit of digging showed that the ImageMagick convert program produced an alpha channel that selected only the traces and left everything else unselected. Why that produces white here and black there is a mystery, but there’s no point in putting up with such nonsense.

    Another wrestling match produced this revision (the two changed lines are highlighted), which has no alpha channel and a white background. That ought to simplify things: an image shouldn’t depend on where it’s dropped to look right.

    #!/usr/bin/kermit +
    # Fetches screen shot from HP54602B oscilloscope
    # Presumes it's set up for plotter output...
    # Converts HPGL to PNG image
    
    set modem none
    set line /dev/ttyUSB0
    set speed 19200
    set flow rts/cts
    set carrier-watch off
    
    # Make sure we have a param
    if not defined \%1 ask \%1 {File name? }
    
    set input echo off
    set input buffer-length 200000
    
    # Wait for PRINT button to send the plot
    echo Set HP54602B for HP Plotter, FACTORS ON, 19200, DTR
    echo Press PRINT SCREEN button on HP54602B...
    
    log session "\%1.hgl"
    
    # Factors On
    input 480 \x03
    
    close session
    close
    
    echo Converting HPGL in
    echo --\%1.hgl
    echo to PNG in
    echo --\%1.png
    
    # Factors Off
    #run hp2xx -q -m png -a 1.762 -h 91 -c 14 "\%1.hgl"
    #run mogrify -density 300 -resize 200% "\%1.png"
    
    # Factors On
    run sed '/lb/!d' "\%1.hgl" > "\%1-1.hgl"
    run hp2xx -q -m eps -r 270 -a 0.447 -c 14 -f "\%1.eps" "\%1-1.hgl"
    run rm "\%1-1.hgl"
    run convert "\%1.eps" -alpha off -resize 675x452 "\%1.png"
    
    echo Finished!
    
    exit 0
    
  • TLC5916 Configuration Code Setting

    TLC5916 Writing Config Code
    TLC5916 Writing Config Code

    The TLC5916 data sheet clearly shows that you write the configuration code (which controls the LED current) by shifting seven bits in, then raising LE during the 8th SCK pulse while simultaneously shifting the 8th bit.

    That makes no sense whatsoever: you couldn’t use standard SPI hardware in a chained configuration, because you’d have to blip LE while shifting.

    In fact, the chip doesn’t work that way. You set the config code in Special Mode just like you set the LED driver bits in Normal Mode: shift ’em all in, then blip LE to latch ’em into the parallel holding register.

    Here’s the code to make it happen…

    DisableSPI();                               // manual SPI control
    
     digitalWrite(PIN_DISABLE_DISPLAY,HIGH);    // initial condition
     digitalWrite(PIN_LATCH_DO,LOW);
    
     PulsePin(PIN_SCK);                         // 1
     digitalWrite(PIN_DISABLE_DISPLAY,LOW);
     PulsePin(PIN_SCK);                         // 2
     digitalWrite(PIN_DISABLE_DISPLAY,HIGH);
     PulsePin(PIN_SCK);                         // 3
     digitalWrite(PIN_LATCH_DO,HIGH);           //   sets Special Mode
     PulsePin(PIN_SCK);                         // 4
     digitalWrite(PIN_LATCH_DO,LOW);
     PulsePin(PIN_SCK);                         // 5
    
    //-- Send brightness level
    
     EnableSPI();                               // turn on SPI hardware
    
     SendRecSPI(Brightness);
     SendRecSPI(Brightness);
     SendRecSPI(Brightness);
     SendRecSPI(Brightness);
     SendRecSPI(Brightness);
    
     PulsePin(PIN_LATCH_DO);                    // latch new shift reg contents into drivers
    
    //-- put LED drivers back in Normal Mode
    
     DisableSPI();
    
     digitalWrite(PIN_DISABLE_DISPLAY,HIGH);     // initial condition
     digitalWrite(PIN_LATCH_DO,LOW);
    
     PulsePin(PIN_SCK);                          // 1
     digitalWrite(PIN_DISABLE_DISPLAY,LOW);
     PulsePin(PIN_SCK);                          // 2
     digitalWrite(PIN_DISABLE_DISPLAY,HIGH);
     PulsePin(PIN_SCK);                          // 3
     digitalWrite(PIN_LATCH_DO,LOW);             //   sets Normal Mode
     PulsePin(PIN_SCK);                          // 4
     digitalWrite(PIN_LATCH_DO,LOW);
     PulsePin(PIN_SCK);                          // 5
    
     digitalWrite(PIN_DISABLE_DISPLAY,LOW);      // turn the LEDs on again
    

    The SendRecSPI() function does exactly what you’d expect:

    byte SendRecSPI(byte Dbyte) {                // send one byte, get another in exchange
    
     SPDR = Dbyte;                      // assume it's OK to send a new byte
    
     while (! (SPSR & (1 << SPIF))) {   // wait for shift to finish
      continue;
     }
    
     return SPDR;                       // SPIF will be cleared
    }
    

    I don’t know. Maybe the chip also works the way they show in the datasheet, but I doubt it’s worth finding out.

  • TLC5916 LED Driver Current Monotonicity Hack

    TLC5916 Current Gain vs Config Code
    TLC5916 Current Gain vs Config Code

    I’m using Texas Instruments TLC5916 constant-current LED driver chips for my my friend’s Totally Featureless Clock. An 8-bit configuration value sets the output current, with the external resistor defining the maximum value as described there.

    The problem is that the current-versus-config-value curve has a non-monotonic discontinuity in the middle, where the Current Multiplier bit switches from 0 to 1. I don’t know in which alternate universe this design decision made sense, but right here and now…

    It. Does. Not.

    Why it’s a problem: the LED brightness tracks room illumination as seen by a CdS photoresistor, averaged over maybe half a minute. The brightness changes very slowly, so the jump when it goes from 0x7f to 0x80 is really eyecatching. At least to me, anyway.

    Eyeballometric measurement of the curve shows the current at 0x80 pretty much matches the current at 0x60, soooo let’s just shove the second quarter of the curve (between 0x40 and 0x7f) downward until it meets the high-current value at 0x80.

    This code does the trick:

    if ((Brightness >= 0x40) && (Brightness <= 0x7f)) {
     Brightness = 0x40 + ((Brightness & 0x3f) >> 1);
    }
    

    Basically, that maps 0x7f to 0x5f. The output current for 0x5f is pretty close to the current for 0x80, making the step pretty much a non-issue.

    You could, if you were fussy enough, work out the actual current mapping values from the data sheet equations and make the ends match up perfectly.

  • Arduino: Slave Select Pin MUST Be An Output in Master Mode

    I’m using hardware-assisted SPI for a project, copied in my own boilerplate code, assigned the bits, and… it didn’t work.

    Jammed hard with mysterious symptoms. Looked like a stack crash, looked like the hardware was broken, looked like a lot of things.

    The final hint, found by stuffing Serial.print() statements in all the usual spots, was that the SPCR register mysteriously changed from the desired 0x71 to 0x61, without any of my code doing the writing.

    Turns out that the Fine Manual has this to say:

    Bit 4 – MSTR: Master/Slave Select

    [snippage] If -SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.

    I planned to use Arduino Pin 10 (PWM10) as the signal to latch the output shift registers, but because I’m developing the code on an Arduino Pro before making the circuit board, I hadn’t gotten around to initializing that pin… which, as you might expect, is also the -SS pin.

    With the pin not set up as an output, it defaults to an input. My cut-n-paste code blipped the pin high and left it low to simulate latching the ‘595 shift registers… but, for input pins, writing a value simulates what would happen when an external signal drives the pin.

    Soooo, I had inadvertently set -SS low, which turned off Master mode, which meant the hardware wasn’t going to send the next byte, which means SPIF wasn’t going to automatically go high when I dropped a byte in SPDR. The code, of course, waited until SPIF was clear before loading SPDR, then hung waiting for it to go high again.

    As always, stupid errors are easy to fix after figuring things out, but ouch did it take a while…

    Moral of the story: always initialize all the I/O pins! (But you knew that, right?)