Mis-Punched Scope Probe Hook

Back in the day, HP scope probes had a rugged music-wire hook on the tip:

HP scope probe tip

HP scope probe tip

These days, scope probe tips use ordinary sheet steel punched into a hook shape:

Siglent scope probe - good tip

Siglent scope probe – good tip

By sheer bad luck, the first probe out of the bag had a mis-punched end with no griptivity:

Siglent scope probe - mis-cut tip

Siglent scope probe – mis-cut tip

Dunno what happened, but it was definitely sheared off in the factory.

After I finally recognized the problem, I shaped a crude hook with a safe-edge needle file and continued the mission:

Siglent scope probe - filed tip

Siglent scope probe – filed tip

A quick note to Siglent put a replacement probe tip in the mail, so it’s all good.



FM DDS: Floating Point Timing

Inserting a few simple floating point operations between the SPI transfers provides a quick-n-dirty look at the timings:

Math timing - double ops

Math timing – double ops

The corresponding code runs in the ADC end-of-conversion handler:

void adc0_isr(void) {


  AnalogSample = adc->readSingle();                     // fetch just-finished sample

  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  digitalWriteFast(DDS_FQUD_PIN, LOW);

  SPI.transfer(DDSBuffer.Phase);                // interleave with FM calculations
  TestFreq += DDSStepFreq;
  TestFreq -= DDSStepFreq;
  TestFreq *= DDSStepFreq;
  TestFreq /= DDSStepFreq;
  SPI.endTransaction();                         // do not raise FQ_UD until next timer tick!


The FlipPin() function twiddling the output bit takes a surprising amount of time, as shown by the first two gaps in the blocks of SPI clocks (D4). Some cursor fiddling on a zoomed scale says 300 ns = 50-ish cycles for each call. In round numbers, actual code doing useful work will take longer than that.

Double precision floating add / subtract / multiply seem to take about 600 ns. That’s entirely survivable if you don’t get carried away.

Double precision division, on the other paw, eats up 3 μs = 3000 ns, so it’s not something you want to casually plunk into an interrupt handler required to finish before the next audio sample arrives in 20 μs.

Overall, the CPU utilization seems way too high for comfort, mostly due to the SPI transfers, even without any computation. I must study the SPI-by-DMA examples to see if it’s a win.


Leave a comment

Motorola K1003A Channel Element: Oscillation!

A handful of Motorola K1003A Receive Channel Elements arrived from eBay:

Motorola Channel Elements - overview

Motorola Channel Elements – overview

Having three 13466.666 kHz candidates, two with gold labels (2 ppm tempco) I disemboweled a silver-label (5 ppm) victim:

Motorola Channel Element - silver label

Motorola Channel Element – silver label

They’re well-studied, with readily available schematics:



For lack of anything smarter, I put a 1 kΩ resistor from RF Out to Ground to get some DC current going, then used a 470 nF cap and 47 Ω resistor as an AC load:

K1003 Channel Element - bias lashup

K1003 Channel Element – bias lashup

Which oscillated around a mid-scale DC bias, but looked ugly:

K1003 Channel Element - 13.4 MHz output - 1k bias

K1003 Channel Element – 13.4 MHz output – 1k bias

Perusing some receiver schematics suggested a heavier DC load, so I swapped in a 470 Ω resistor:

K1003 Channel Element - 470 ohm bias - 13.4 MHz output

K1003 Channel Element – 470 ohm bias – 13.4 MHz output

It’s now running around 3 V bias with fewer harmonics; the scope’s frequency display in the upper right corner seems happier, too.

The receiver will run that through a filter to wipe off the harmonics, then multiply the frequency by three to get the mixer LO.

There are many, many different Channel Elements out there, in receive and transmit flavors, but at least I have some idea what’s going on inside.


Magnetic Field Visualization

Thinking about springs to apply downforce on plotting pen holders suggested magnets, so I extricated some neodymium bars from my collection of power toothbrush heads:

Magnets - single

Magnets – single

A snippet of magnetic field visualization film shows a dipole pattern:

Magnets - single - field visualization

Magnets – single – field visualization

Snapping two of them together in line:

Magnets - in line

Magnets – in line

… produces a quadrupole:

Magnets - in line - field visualization

Magnets – in line – field visualization

Now, if only I had some magnetic monopoles, this whole thing would be easier!

Leave a comment

Frequency Modulated DDS: SPI Mock 1

The general idea is to frequency modulate the sine wave coming from a DDS, thereby generating a signal suitable for upconverting in amateur repeaters now tied to unobtainable crystals. The crystals run from 4-ish to 20-ish MHz, with frequency multiplication from 3 to 36 producing RF outputs from 30-ish MHz through 900-ish MHz; more details as I work through the choices.

The demo code runs on a bare Teensy 3.6 as a dipstick test for the overall timing and functionality:

FM DDS - Teensy 3.6 SPI demo

FM DDS – Teensy 3.6 SPI demo

The fugliest thing you’ve seen in a while, eh?

An overview of the results:

Analog 4 kHz @ 40 kHz - SPI demo overview

Analog 4 kHz @ 40 kHz – SPI demo overview

The pulses in D1 (orange digital) mark timer ticks at a 40 kHz pace, grossly oversampling the 4 kHz audio bandwidth in the hope of trivializing the antialiasing filters. The timer tick raises the DDS latch pin (D6, top trace) to change the DDS frequency, fires off another ADC conversion, and (for now) copies the previous ADC value to the DAC output:

void timer_callback(void) {
  digitalWriteFast(DDS_FQUD_PIN,HIGH);                // latch previously shifted bits
  adc->startSingleRead(AUDIO_PIN, ADC_0);             // start ADC conversion
  analogWriteDAC0(AnalogSample);                      // show previous audio sample

The purple analog trace is the input sine wave at 4 kHz. The yellow analog stairstep comes from the DAC, with no hint of a reconstruction filter knocking off the sharp edges.

The X1 cursor (bold vertical dots) marks the start of the ADC read. I hope triggering it from the timer tick eliminates most of the jitter.

The Y1 cursor (upper dotted line, intersecting X1 just left of the purple curve) shows the ADC sample apparently happens just slightly after the conversion. The analog scales may be slightly off, so I wouldn’t leap to any conclusions.

The pulses in D2 mark the ADC end-of-conversion interrupts:

void adc0_isr(void) {
  AnalogSample = adc->readSingle();                     // fetch just-finished sample
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  digitalWriteFast(DDS_FQUD_PIN, LOW);
  SPI.transfer(DDSBuffer.Phase);                // interleave with FM calculations
  SPI.endTransaction();                         // do not raise FQ_UD until next timer tick!

The real FM code will multiply the ADC reading by the amplitude-to-frequency-deviation factor, add it to the nominal “crystal” frequency, convert the sum to the DDS delta-phase register value, then send it to the DDS through the SPI port. For now, I just send five constant bytes to get an idea of the minimum timing with the SPI clock ticking along at 8 MHz.

The tidy blurs in D4 show the SPI clock, with the corresponding data in D5.

D6 (top trace) shows the DDS FQ_UD (pronounced “frequency update”) signal dropping just before the SPI data transfer begins. Basically, FQ_UD is the DDS Latch Clock: low during the delta-phase value transfer, with the low-to-high transition latching all 40 control + data bits into the DDS to trigger the new frequency.

A closer look at the sample and transfer:

Analog 4 kHz @ 40 kHz - SPI demo detail

Analog 4 kHz @ 40 kHz – SPI demo detail

For reference, the digital players from bottom to top:

  • D0 – unused here, shows pulses marking main loop
  • D1 – 40 kHz timer ticks = ADC start conversion
  • D2 – ADC end of conversion,”FM calculation”, send DDS data
  • D3 – unused here, shows error conditions
  • D4 – SPI clock = rising edge active
  • D5 – SPI MOSI data to DDS = MSB first
  • D6 – SPI CS = FQ_UD = DDS latch

Remember, the yellow analog stairstepped trace is just a comfort signal showing the ADC actually samples the intended input.

The ARM CPU has floating-point hardware, but I suspect fixed-point arithmetic will once again win out over double-precision multiplies & divides.

Dropping the sampling to 20 kHz would likely work just as well and double the time available for calculations. At least now I can measure what’s going on.

All in all, it looks feasible.

And, yes, the scope is a shiny new Siglent SDS2304X with the MSO logic-analyzer option. It has some grievous UX warts & omissions suggesting an architectural botch job, but it’s mostly Good Enough for what I need. More later.

The TeensyDuino source code as a GitHub Gist:


1 Comment

Teensy 3.6 Double Precision Floats

Having spent a bit of effort wringing enough precision from an Arduino to make the 60 kHz quartz resonator tester, this came as a relief:

DDS frequency:  180000000.0000000 Hz
      epsilon:          0.0000001 Hz
         step:          0.0419095 Hz

Center frequency:  146520000.0000000 Hz
  146520000.0000001 Hz
  146520000.0000002 Hz
  146520000.0000003 Hz
  146520000.0000004 Hz
  146520000.0000004 Hz
  146520000.0000005 Hz
  146520000.0000006 Hz
  146520000.0000007 Hz
  146520000.0000008 Hz
  146520000.0000009 Hz
  146520000.0000010 Hz

... snippage ...

  146520000.0000099 Hz
  146520000.0000100 Hz
  146520000.0419195 Hz
  146520000.0838290 Hz
  146520000.1257386 Hz
  146520000.1676481 Hz
  146520000.2095576 Hz
  146520000.2514671 Hz
  146520000.2933766 Hz
  146520000.3352861 Hz
  146520000.3771957 Hz
  146520000.4191052 Hz
  146520000.4610147 Hz
  146520000.5029242 Hz
  146520000.5448337 Hz
  146520000.5867432 Hz
  146520000.6286528 Hz
  146520000.6705623 Hz
  146520000.7124718 Hz
  146520000.7543813 Hz
  146520000.7962908 Hz
  146520000.8382003 Hz
  146520000.8801098 Hz
  146520000.9220194 Hz
  146520000.9639289 Hz
  146520001.0058384 Hz
  146520001.0477479 Hz
  146520001.0896574 Hz
  146520001.1315669 Hz
  146520001.1734765 Hz

Which comes from a PJRC Teensy 3.6 running this code:

double DDSFreq, EpsilonFreq, DDSStepFreq;
double CenterFreq, TestFreq;

... in setup() ...

  DDSFreq = 180.0e6;
  EpsilonFreq = 1.0e-7;
  DDSStepFreq = DDSFreq / (1LL << 32);
  Serial.printf("DDS frequency: %18.7f Hz\n",DDSFreq);
  Serial.printf("      epsilon: %18.7f Hz\n",EpsilonFreq);
  Serial.printf("         step: %18.7f Hz\n\n",DDSStepFreq);

  CenterFreq = 146520000.0;
  TestFreq = CenterFreq;
  Serial.printf("Center frequency: %18.7f Hz\n",CenterFreq);

... in loop() ...

  if (TestFreq < (CenterFreq + 100*EpsilonFreq))
    TestFreq += EpsilonFreq;
    TestFreq += DDSStepFreq;

  Serial.printf(" %18.7f Hz\n",TestFreq);

The IEEE-754 spec says a double floating-point variable carries about 15.9 decimal digits, which agrees with the 9 integer + 7 fraction digits. The highlight lowlight (gray bar) in the first figure shows the slight stumble where adding 1e-7 changes the sum, but not quite enough to affect the displayed fraction.

In round numbers, an increment of 1e-5 would work just fine:

  146520000.0000100 Hz
  146520000.0000200 Hz
  146520000.0000300 Hz
  146520000.0000401 Hz
  146520000.0000501 Hz
  146520000.0000601 Hz
  146520000.0000701 Hz
  146520000.0000801 Hz
  146520000.0000901 Hz
  146520000.0001001 Hz
  146520000.0001101 Hz
  146520000.0001202 Hz
  146520000.0001302 Hz
  146520000.0001402 Hz
  146520000.0001502 Hz
  146520000.0001602 Hz
  146520000.0001702 Hz
  146520000.0001802 Hz
  146520000.0001903 Hz
  146520000.0002003 Hz
  146520000.0002103 Hz
  146520000.0002203 Hz
  146520000.0002303 Hz

You’d use the “smallest of all” epsilon in a multiplied increment, perhaps to tick a value based on a knob or some such. Fine-tuning a VHF frequency with millihertz steps probably doesn’t make much practical sense.

The DDS frequency increment works out to 41.9095 mHz, slightly larger than with the Arduino, because it’s fot a cheap DDS eBay module with an AD9851 running a 180 MHz (6 × 30 MHz ) clock.



MPCNC: Ground Shaft Pen Holder

Drilling a pair of holes into a length of ground steel shaft turned it into a holder for a Sakura Micron pen:

DW660 Pen Holder - printed plastic vs ground steel

DW660 Pen Holder – printed plastic vs ground steel

The aluminum ring epoxied to the top keeps it from falling completely through the linear bearing.

The hole sizes are the nearest inch drills matching the pen’s hard metric sizes:

Ground 12 mm rod - Sakura pen drill diameters

Ground 12 mm rod – Sakura pen drill diameters

While I was at the lathe, I turned another layer of epoxy on the printed holder down to a consistent 11.95+ OD. It fits the bearing nearly as well as the steel shaft, although it’s not quite as smooth.

The steel version weighs about 20 g with the pen, so it applies about the same downforce on the pen nib as the HP 7475A plotter. The force varies from about 19 g as the Z axis moves upward to 23 g as it move downward, so the stiction amounts to less than 10% of the weight:

DW660 Pen Holder - ground shaft

DW660 Pen Holder – ground shaft

However, the more I ponder this setup, the less I like it.

When the Z-axis moves downward and the nib hits the paper, it must decelerate the weight of the pen + holder + ballast within a fraction of a millimeter, without crushing the nib. If the pen moves downward at 3000 mm/min = 50 mm/s, stopping in 0.3 mm requires an acceleration of 4.2 m/s² and a 20 g = 2/3 oz mass will apply 0.08 N = 0.3 oz to the nib. Seems survivable, but smashing the tip a few hundred times while drawing the legends can’t possibly be good for it.

Also, the tool length probe switch trips at 60 (-ish) g, which means the pen can’t activate the switch. Adding a manual latch seems absurd, but you can get used to anything if you do it enough.

, , ,