Advertisements

Archive for category Software

Arduino vs. Significant Figures: Useful 64-bit Fixed Point

Devoting eight bytes to every fixed point number may be excessive, but having nine significant figures apiece for the integer and fraction parts pushes the frequency calculations well beyond the limits of the DDS hardware, without involving any floating point library routines. This chunk of code performs a few more calculations using the format laid out earlier and explores a few idioms that may come in handy later.

Rounding the numbers to a specific number of decimal places gets rid of the repeating-digit problem that turns 0.10 into 0.099999:

uint64_t RoundFixedPt(union ll_u TheNumber,unsigned Decimals) {
union ll_u Rnd;

  Rnd.fx_64 = (One.fx_64 / 2) / (pow(10LL,Decimals));
  TheNumber.fx_64 = TheNumber.fx_64 + Rnd.fx_64;
  return TheNumber.fx_64;
}

That pretty well trashes the digits beyond the rounded place, so you shouldn’t display any more of them:

void PrintFixedPtRounded(char *pBuffer,union ll_u FixedPt,unsigned Decimals) {
char *pDecPt;

  FixedPt.fx_64 = RoundFixedPt(FixedPt,Decimals);

  PrintIntegerLL(pBuffer,FixedPt);  // do the integer part

  pBuffer += strlen(pBuffer);       // aim pointer beyond integer

  pDecPt = pBuffer;                 // save the point location
  *pBuffer++ = '.';                 // drop in the decimal point, tick pointer

  PrintFractionLL(pBuffer,FixedPt);

  if (Decimals == 0)
    *pDecPt = 0;                    // 0 places means discard the decimal point
  else
    *(pDecPt + Decimals + 1) = 0;   // truncate string to leave . and Decimals chars
}

Which definitely makes the numbers look prettier:

  Tenth.fx_64 = One.fx_64 / 10;             // Likewise, 0.1
  PrintFixedPt(Buffer,Tenth);
  printf("\n0.1: %s\n",Buffer);
  PrintFixedPtRounded(Buffer,Tenth,9);                    // show rounded value
  printf("0.1 to 9 dec: %s\n",Buffer);

  TestFreq.fx_64 = RoundFixedPt(Tenth,3);                 // show full string after rounding
  PrintFixedPt(Buffer,TestFreq);
  printf("0.1 to 3 dec: %s (full string)\n",Buffer);

  PrintFixedPtRounded(Buffer,Tenth,3);                    // show truncated string with rounded value
  printf("0.1 to 3 dec: %s (truncated string)\n",Buffer);

0.1: 0.099999999
0.1 to 9 dec: 0.100000000
0.1 to 3 dec: 0.100499999 (full string)
0.1 to 3 dec: 0.100 (truncated string)

  CtPerHz.fx_64 = -1;                       // Set up 2^31 - 1, which is close enough
  CtPerHz.fx_64 /= 125 * MEGA;              // divide by nominal oscillator
  PrintFixedPt(Buffer,CtPerHz);
  printf("\nCt/Hz = %s\n",Buffer);

  printf("Rounding: \n");
  for (int d = 9; d >= 0; d--) {
    PrintFixedPtRounded(Buffer,CtPerHz,d);
    printf("     %d: %s\n",d,Buffer);
  }

Ct/Hz = 34.359738367
Rounding:
     9: 34.359738368
     8: 34.35973837
     7: 34.3597384
     6: 34.359738
     5: 34.35974
     4: 34.3597
     3: 34.360
     2: 34.36
     1: 34.4
     0: 34

Multiplying two scaled 64-bit fixed-point numbers should produce a 128-bit result. For all the values we (well, I) care about, the product will fit into a 64-bit result, because the integer parts will always multiply out to less than 232 and we don’t care about more than 32 bits of fraction. This function multiplies two fixed point numbers of the form a.b × c.d by adding up the partial products thusly: ac + bd + ad + bc. The product of the integers ac won’t overflow 32 bits, the cross products ad and bc will always be slightly less than their integer factors, and the fractional product bd will always be less than 1.0.

Soooo, just multiply ’em out as 64-bit integers, shift the products around to align the appropriate parts, and add up the pieces:


uint64_t MultiplyFixedPt(union ll_u Mcand, union ll_u Mplier) {
union ll_u Result;

  Result.fx_64  = ((uint64_t)Mcand.fx_32.high * (uint64_t)Mplier.fx_32.high) << 32; // integer parts (clear fract) 
  Result.fx_64 += ((uint64_t)Mcand.fx_32.low * (uint64_t)Mplier.fx_32.low) >> 32;   // fraction parts (always < 1)
  Result.fx_64 += (uint64_t)Mcand.fx_32.high * (uint64_t)Mplier.fx_32.low;          // cross products
  Result.fx_64 += (uint64_t)Mcand.fx_32.low * (uint64_t)Mplier.fx_32.high;

  return Result.fx_64;
}

This may be a useful way to set magic numbers with a few decimal places, although it does require keeping the decimal point in mind:

  TestFreq.fx_64 = (599999LL * One.fx_64) / 10;           // set 59999.9 kHz differently
  PrintFixedPt(Buffer,TestFreq);
  printf("\nTest frequency: %s\n",Buffer);
  PrintFixedPtRounded(Buffer,TestFreq,1);
  printf("         round: %s\n",Buffer);

Test frequency: 59999.899999999
         round: 59999.9

Contrary to what I thought, computing the CtPerHz coefficient doesn’t require pre-dividing both 232 and the oscillator by 2, thus preventing the former from overflowing a 32 bit integer. All you do is knock the numerator down by one little itty bitty count you’ll never notice:

  CtPerHz.fx_64 = -1;                       // Set up 2^31 - 1, which is close enough
  CtPerHz.fx_64 /= 125 * MEGA;              // divide by nominal oscillator
  PrintFixedPt(Buffer,CtPerHz);
  printf("\nCt/Hz = %s\n",Buffer);

Ct/Hz = 34.359738367

That’s also the largest possible fixed-point number, because unsigned:

  TempFX.fx_64 = -1;
  PrintFixedPt(Buffer,TempFX);
  printf("Max fixed point: %s\n",Buffer);

Max fixed point: 4294967295.999999999

With nine.nine significant figures in the mix, tweaking the 125 MHz oscillator to within 2 Hz will work:

Oscillator tune: CtPerHz
 Oscillator: 125000000.00
 -10 -> 34.359741116
  -9 -> 34.359741116
  -8 -> 34.359740566
  -7 -> 34.359740566
  -6 -> 34.359740017
  -5 -> 34.359740017
  -4 -> 34.359739467
  -3 -> 34.359739467
  -2 -> 34.359738917
  -1 -> 34.359738917
  +0 -> 34.359738367
  +1 -> 34.359738367
  +2 -> 34.359737818
  +3 -> 34.359737818
  +4 -> 34.359737268
  +5 -> 34.359737268
  +6 -> 34.359736718
  +7 -> 34.359736718
  +8 -> 34.359736168
  +9 -> 34.359736168
 +10 -> 34.359735619

So, all in all, this looks good. The vast number of strings in the test program bulk it up beyond reason, but in actual practice I think the code will be smaller than the equivalent floating point version, with more significant figures. Speed isn’t an issue either way, because the delays waiting for the crystal tester to settle down at each frequency step should be larger than any possible computation.

The results were all verified with my trusty HP 50g and HP-15C calculators, both of which wipe the floor with any other way of handling mixed binary / hex / decimal arithmetic. If you do bit-wise calculations, even on an irregular basis, get yourself a SwissMicro DM16L; you can thank me later.

The Arduino source code as a GitHub Gist:

Advertisements

, ,

6 Comments

Copying Video Files From Action Cameras to a NAS Drive

For unknown reasons, a recent VLC update caused it to ignore uppercase file extensions: MP4 and AVI files no longer appear in its directory listings, while mp4 and avi files do. The least-awful solution involved renaming the files after copying them:

find /mnt/video -name \*AVI -print0 | xargs -0 rename -v -f 's/AVI/avi/'
find /mnt/video -name \*MP4 -print0 | xargs -0 rename -v -f 's/MP4/mp4/'
find /mnt/video -name \*THM -print0 | xargs -0 rename -v -f 's/THM/thm/'

Yup, that scans the whole drive every time, which takes care of stray files, manual tweaks, and suchlike. The THM files are useless thumbnails; I should just delete them.

While I had the hood up, I listed the remaining space on the NAS drive and cleaned up a few misfeatures. I manually delete old video files / directories as needed, usually immediately after the script crashes for lack of room.

The Sony HDR-AS30V can act as a USB memory device, but it dependably segfaults the ExFAT driver; I now transfer its MicroSD card to an adapter and jam it into the media slot on the monitor, where it works fine.

Protip: always turn the AS30V on to verify the MicroSD card has seated correctly in its socket. Unfortunately, the socket can also hold Sony’s proprietary Memory Stick Micro cards (32 GB maximum capacity = roadkill), but the dual-use / dual-direction socket isn’t a snug fit around MicroSD cards. You (well, I) can insert a card so it looks fine, while sitting slightly canted and not making proper contact. The camera will kvetch about that and it’s easier to fix with the camera in hand.

I’ve disabled USB device automounting, as I vastly prefer to handle them manually, so the script asks for permission in order to mount the drives. The transfer requires about an hour, so I’ve extended the time the sudo password remains active.

The script lets both cards transfer data simultaneously; the Fly6 generally finishes first because it produces less data. That produces a jumbled progress display and the script waits for both drives to finish before continuing.

The Bash source code as a GitHub Gist:

4 Comments

Arduino Joystick

A bag of sub-one-dollar resistive joysticks arrived from halfway around the planet:

Arduino UNO - resistive joystick

Arduino UNO – resistive joystick

A quick-and-dirty test routine showed the sticks start out close to VCC/2:

Welcome to minicom 2.7

OPTIONS: I18n
Compiled on Feb  7 2016, 13:37:27.
Port /dev/ttyACM0, 10:23:45

Press CTRL-A Z for help on special keys

Joystick exercise
Ed Nisley - KE4ZNU - May 2017
00524 - 00513 - 1

That’s from minicom on the serial port, as the Arduino IDE’s built-in serial monitor ignores bare Carriage Return characters.

The joystick hat tilts ±25° from its spring-loaded center position, but the active region seems to cover only 15° of that arc, with a 5° dead zone around the center and 5° of overtravel at the limits. This is not a high-resolution instrument intended for fine motor control operations.

The analog input values range from 0x000 to 0x3FF across the active region. Aim the connector at your tummy to make the axes work the way you’d expect: left / down = minimum, right / up = maximum.

The delay(100) statements may or may not be needed for good analog input values, depending on some imponderables that seem not to apply for this lashup, but they pace the loop() to a reasonable update rate.

Pushing the hat toward the PCB activates the simple switch you can see in the picture. It requires an external pullup resistor (hence the INPUT_PULLUP configuration) and reports low = 0 when pressed.

Those are 0.125 inch (exactly!) holes on a 19.5×26.25 mm grid in a 26.5×34.25 mm PCB. Makes no sense to me, either.

The trivial Arduino source code as a GitHub Gist:

Leave a comment

Dropbox Tour: To Keep Learning, Click Cancel

After copying a Digital Machinist column to my Dropbox folder, I went to the site to get the link, discovered they improved the UI, declined a Flash-based tour of the new features, and got this baffling confirmation dialog:

Dropbox - tour exit dialog

Dropbox – tour exit dialog

So. Many. Wrongs.

4 Comments

Arduino vs. Significant Figures: BigNumber Library

The BigNumber library wraps the bc arbitrary precision calculator into a set of Arduino routines that seem like a reasonable basis for DDS calculations requiring more than the half-dozen digits of a floating point number or the limited range of scaled fixed point numbers tucked into an long int.

Treating programming as an experimental science produces some Arduino source code and its output as a GitHub Gist:

All that happened incrementally, as you might expect, with the intent of seeing how it works, rather than actually doing anything.

Some musings, in no particular order:

The library soaks up quite a hunk of program space:

Sketch uses 13304 bytes (43%) of program storage space. Maximum is 30720 bytes.

I think you could cut that back a little by eliminating unused bc routines, like square root / exponential / modulus.

That test code also blots up quite a bit of RAM:

Global variables use 508 bytes (24%) of dynamic memory, leaving 1540 bytes for local variables. Maximum is 2048 bytes.

All the BigNumber variables live inside the setup() function (or whatever it’s called in Arduino-speak), so they count as local variables. They’re four bytes each, excluding the dynamically allocated storage for the actual numbers at roughly a byte per digit. With 10 decimal places for all numbers, plus (maybe) an average of half a dozen integer digits, those ten BigNumbers soak up 200 = 10 × (4 + 16) bytes of precious RAM.

You can load a BigNumber from an int (not a long) or a string, then export the results to a long or a string. Given that controlling a DDS frequency with a knob involves mostly adding and subtracting a specific step size, strings would probably work fine, using snprintf() to jam the string equivalent of a long into a BigNumber as needed.

You must have about ten decimal places to hold enough significant figures in the HertzPerCount and CountPerHertz values. The library scale factor evidently forces all the numbers to have at least that many digits, with the decimal point stuck in front of them during string output conversions.

The biggest integers happen in the Oscillator and ThirtyTwoBits values, with 9 and 10 digits, respectively.

It looks useful, although I’m uncomfortable with the program space required. I have no way to estimate the program space for a simpleminded DDS controller, other than knowing it’ll be more than I estimate.

While poking around, however, I discovered the Arduino compiler does provide (limited) support for long long int variables. Given a 64 bit unit for simple arithmetic operations, a simpler implementation of fixed point numbers may be do-able: 32 bits for the integer and fraction should suffice! More on that shortly.

,

6 Comments

Badge Lanyard Reel Mount

A certain young engineer of my acquaintance now carries an ID badge and, so I hear, works in a PCB design & test venue. Seeing as how her favorite color is purple, this seemed appropriate:

Badge Lanyard Reel - front - overall

Badge Lanyard Reel – front – overall

The guts came from Circuit Breaker Labs in the form of a recycled PCB trapped in acrylic resin atop a plastic housing with a spring-loaded reel inside.

It arrived with a plastic bullet at the end of the lanyard:

Badge Lanyard Reel - plastic bullet link

Badge Lanyard Reel – plastic bullet link

Which I immediately replaced with brass, because Steampunk:

Badge Lanyard Reel - bullet cross-drill

Badge Lanyard Reel – bullet cross-drill

That made the plastic housing look weak, so, in a series of stepwise refinements, I conjured a much better case from the vasty digital deep:

Badge Lanyard Reel - iterations

Badge Lanyard Reel – iterations

All of the many, many critical dimensions lie inside the case, where they can’t be measured accurately, so each of those iterations could improve only one or two features. The absolutely wonderful thing about OpenSCAD is having it regenerate the whole model after loosening, say, the carabiner slot by two thread thicknesses; you can do that with a full-on relational CAD drawing, but CAD drawings always seems like a lot of unnecessary work if I must figure out the equations anyway.

The back sports my favorite Hilbert Curve infill with a nicely textured finish:

Badge Lanyard Reel - rear - oblique

Badge Lanyard Reel – rear – oblique

It’d surely look better in solid brass with Hilbert curve etching.

Black PETG doesn’t photograph well, but at least you can see the M2 brass inserts:

Badge Lanyard Reel - lower interior

Badge Lanyard Reel – lower interior

The first prototype showed the inserts needed far more traction than the usual reamed holes could provide, so I added internal epoxy grooves in each hole:

Badge Lanyard Reel Mount - show

Badge Lanyard Reel Mount – show

Recessing the screw heads into the top plate made them more decorative and smoother to the touch. Button-head screws would be even smoother, but IMO didn’t look quite as bold.

After seeing how well the grooves worked, I must conjure a module tabulating all the inserts on hand and automagically generating the grooves.

The yellow star holds up the roof of the reel recess in the build layout:

Badge Lanyard Reel Mount - build layout - bottom

Badge Lanyard Reel Mount – build layout – bottom

Slic3r produced the rest of the support material for the carabiner exit slot:

Badge Lanyard Reel Mount - bottom - Slic3r support

Badge Lanyard Reel Mount – bottom – Slic3r support

Those two support lumps on the right don’t actually support anything, but tweaking the support settings to disable them also killed the useful support on the left; come to find out Slic3r’s modifier meshes don’t let you disable support generation.

The top plate required support all the way around the inside of the bezel:

Badge Lanyard Reel Mount - top - Slic3r support

Badge Lanyard Reel Mount – top – Slic3r support

I carved the original plastic housing in half, roughly along its midline, and discarded the bottom section with the belt clip (it’s on the far left of the scrap pile). The top section, with PCB firmly affixed, holds the lanyard reel and anchors the retracting spring in a central slotted peg. No pictures of that, as it’s either a loose assembly of parts or a spring-loaded bomb and I am not taking it apart again.

The lanyard passes through an eyelet that pays it out to the rotating reel. I’d definitely do that differently, were I building it from scratch, because mounting the eyelet in exactly the proper position to prevent the lanyard from stacking up on the reel and jamming against the inside of the housing turned out to be absolutely critical and nearly impossible.

The top plate presses the original housing against the carabiner, with the cut-off section inside the carabiner’s circular embrace, which just barely worked: the PCB bezel is a millimeter smaller than the shoulder of the housing.

All in all, I think it came out really well for a 3D printed object made by a guy who usually builds brackets:

Badge Lanyard Reel - front - oblique

Badge Lanyard Reel – front – oblique

I hope she likes it …

The OpenSCAD source code as a GitHub Gist:

 

,

7 Comments

Microscope 60 LED Ring Light Adapter

The Barbie-themed microscope light I built from an angel eye LED ring worked fine for the last six years (!), but a much brighter ring with 60 aimed 5 mm LEDs for $17 delivered from a US seller caught my eye:

Microscope 60 LED ring light - in use

Microscope 60 LED ring light – in use

Although this ring looks much more professional, it didn’t quite fit the microscope, being designed for a round snout rather than a squarish one. This snout has a 47-ish mm threaded ring intended for filters & suchlike, so I built an adapter between that and the 60 mm ID of the LED ring:

Microscope 60 LED Ring Light Adapter - top - Slic3r

Microscope 60 LED Ring Light Adapter – top – Slic3r

The ring came with three long knurled screws which I replaced with much tidier M3 socket-head screws going into those holes:

Microscope 60 LED ring light - assembled - top

Microscope 60 LED ring light – assembled – top

The part going into the snout threads is deliberately (honest!) a bit small, so I could wrap it with soft tape for a good friction fit. The Barbie Ring didn’t weigh anything and I wound up using squares of double-sticky foam tape; it could come to that for this ring, too.

The adapter features a taper on the bottom for no particularly good reason, as the field-of-view tapers inward, not outward:

Microscope 60 LED Ring Light Adapter - bottom - Slicer

Microscope 60 LED Ring Light Adapter – bottom – Slicer

Seen from the bug’s POV, it’s a rather impressive spectacle:

Microscope 60 LED ring light - assembled - bottom

Microscope 60 LED ring light – assembled – bottom

The control box sports a power switch and a brightness knob. Come to find out the ring is actually too bright at full throttle; a nice problem to have.

That was easy!

The OpenSCAD source code as a GitHub Gist:

 

,

4 Comments