Advertisements

Archive for category Science

LF Crystal Tester: Pretty Plots

A slight modification spits out the (actual) frequency and dBV response (without subtracting the 108 dB intercept to avoid negative numbers for now) to the serial port in CSV format, wherein a quick copypasta into a LibreOffice Calc spreadsheet produces this:

Spectrum-32

Spectrum-32

Changing the center frequency and swapping in a 60 kHz resonator:

Spectrum-60

Spectrum-60

Much prettier than the raw scope shot with the same data, there can be no denyin’:

Log V vs F - 32766 4 Hz - CX overlay

Log V vs F – 32766 4 Hz – CX overlay

I think the wobbulations around the parallel resonant dip come from the excessively hugely too large 10 µF caps in the signal path, particularly right before the log amp input, although the video bandwidth hack on the AD8310 module may contribute to the problem. In any event, I can see the log amp output wobbling for about a second, which is way too long.

Anyhow, the series-resonant peaks look about 1 Hz wide at the -3 dBV points, more or less agreeing with what I found with the HP 8591 spectrum analyzer. The series cap is a bit smaller, producing a slightly larger frequency change in the series resonant frequency: a bit under 2 Hz, rather than the 1 Hz estimated with the function generator and spectrum analyzer.

I still don’t understand why the parallel resonant dip changes, although I haven’t actually done the pencil pushing required for true understanding.

Ain’t they lovely, though?

Advertisements

, ,

2 Comments

Golden Tortoise Beetle

An iridescent ball appeared on the kitchen wall:

Golden Tortoise Beetle - left top - light

Golden Tortoise Beetle – left top – light

Despite the silvery shine under LED lighting, it was a Golden Tortoise Beetle:

Golden Tortoise Beetle - right top

Golden Tortoise Beetle – right top

The iridescence shows up better with a bit of underexposure:

Golden Tortoise Beetle - left top - dark

Golden Tortoise Beetle – left top – dark

Transparent armor: who’d’a thunk it?

Golden Tortoise Beetle - left front

Golden Tortoise Beetle – left front

Mary spotted one in the garden some years ago; I’ve never seen such a thing.

6 Comments

Monthly Science: Significant Figures vs. Accuracy vs. Precision, Marathon Edition

The rail trail recently sprouted white mile markers:

Rail Trail - Marathon 13 mile marker

Rail Trail – Marathon 13 mile marker

This one stood out:

Rail Trail - Marathon 13.10938 mile marker

Rail Trail – Marathon 13.10938 mile marker

Not being a marathoner, I had the vague notion a marathon should be an even number of kilometers, because it’s not an even number of miles, but nooooo it’s just an arbitrary distance everybody agreed would be about right for a good long run.

During the rest of the ride, I worked out that 1 micro mile = 5+ milli foot = 60+ milli inch, so the rightmost significant figure in that marker represents increments of, oh, a smidge under ¾ inch. Middle of the hash line marks the spot, perhaps?

I’ve seen similar markers along other courses, with varying numbers of ahem significant figures, and will not say how long it took me to recognize what it represented.

2 Comments

AD9850 DDS Module: Temperature Sensitivity

While tinkering with the SPI code for the AD9850 DDS module, I wrote down the ambient temperature and the frequency tweak required to zero-beat the 10 MHz output with the GPS-locked oscillator. A quick-n-dirty plot summarizing two days of randomly timed observations ensued:

AD9850 DDS Module - Frequency vs Temperature

AD9850 DDS Module – Frequency vs Temperature

The frequency offset comes from the tweak required to zero-beat the output by adjusting the initial oscillator error: a positive tweak produces a smaller count-per-hertz coefficient and reduces the output frequency. As a result, the thermal coefficient sign is backwards, because increasing temperature raises the oscillator frequency and reduces the necessary tweak. I think so, anyway; you know how these things can go wrong. More automation and reliable data would be a nice touch.

Foam sheets formed a block around the DDS module, isolating it from stray air currents and reducing the clock oscillator’s sensitivity:

AD9850 DDS module - foam insulation

AD9850 DDS module – foam insulation

I used the ambient temperature, because the thermocouple inside the foam (not shown in the picture) really wasn’t making good contact with the board, the readings didn’t make consistent sense, and, given a (nearly) constant power dissipation, the (average) oscillator temperature inside the foam should track ambient temperature with a constant offset. I think so, anyway.

The coefficient works out to 0.02 ppm/°C. Of course, the initial frequency offset is something like -400 Hz = 3 ppm, so we’re not dealing with lab-grade instrumentation here.

,

8 Comments

Arduino vs. Significant Figures: Preliminary 64-bit Fixed Point Exercise

Although it’s not advertised, the Arduino / AVR compiler mostly does the right thing with long long = uint64_t variables: add & subtract work fine, but multiplication & division discard anything that doesn’t fit into 64 bits. Fitting a 32 bit integer and a 32 bit fraction into such a thing should eliminate (most) problems with significant figures.

The general idea is to set up a struct giving access to the two 32 bit halves for direct manipulation, then overlay / union them with a single 64 bit integer for arithmetic purposes:

struct ll_s {
  uint32_t low;
  uint32_t high;
};

union ll_u {
  uint64_t ll_64;
  struct ll_s ll_32;
};

Of course, the integer part still falls one bit shy of holding 2³². At the cost of one bit’s worth of resolution, you can still compute 2³² / 125×10⁶ by pre-dividing each quantity by 2:

2^63 = [80000000 00000000]
2^63 / 125/2 M = [00000022 5c17d04d]

The low-order digit should be 0xe, not 0xd, but I think that’s survivable.

Unfortunately, printf doesn’t handle 64 bit quantities, necessitating some awkward conversion routines. “Printing” to a string seems the least awful method, as I’ll eventually squirt the strings to a display, not send them to the serial port:

void PrintFractionLL(char *pBuffer,uint64_t *pLL) {
  uint64_t Fraction;

  Fraction = (uint32_t)*pLL;                      // copy 32 fraction bits, high order = 0
  Fraction *= ONEGIG;                             // times 10^9 for conversion
  Fraction >>= 32;                                // align integer part in low long
  sprintf(pBuffer,"%09lu",(uint32_t)Fraction);    // convert low long to decimal
}

void PrintIntegerLL(char *pBuffer,uint64_t *pLL) {
  sprintf(pBuffer,"%lu",*((uint32_t *)pLL+1));
}

void PrintDecimalLL(char *pBuffer,uint64_t *pLL) {
  PrintIntegerLL(pBuffer,pLL);
  pBuffer += strlen(pBuffer);       // pointer to end of integer part
  *pBuffer++ = '.';                 // drop in the decimal point, tick pointer
  PrintFractionLL(pBuffer,pLL);
}

The result seems nearly indistinguishable from the Right Answer:

Integer:  34
Fraction: 359738367
Decimal: 34.359738367

This whole mess has a bunch of rough edges, but it looks promising. The code coalesced while fiddling around, so the union notation didn’t get much love at first.

The Arduino source code as a GitHub Gist:

,

3 Comments

Arduino vs Significant Figures: Floating Point Calculations

Herewith, to nail down the reasons why you can’t (or, perhaps, shouldn’t) use Arduino float variables, a small collection of DDS-oid calculations.

Remember that float and double variable are both IEEE 754 single-precision floating point numbers:

Size of float: 4
       double: 4

The Arduino floating-point formatter gags on some values, although they calculate correctly:

2^24: 16777216.000
printf:        ?
2^32: ovf or ovf
2^32: ovf
2^32 / 256: 16777216.000

Don’t add values differing by more than seven orders of magnitude and suspect any results beyond the first half-dozen significant figures:

Oscillator steps: HzPerCt
 Oscillator: 125000000.00
 -25 -> 0.02910382461
 -24 -> 0.02910382461
 -23 -> 0.02910382461
 -22 -> 0.02910382461
 -21 -> 0.02910382461
 -20 -> 0.02910382747
 -19 -> 0.02910382747
 -18 -> 0.02910382747
 -17 -> 0.02910382747
 -16 -> 0.02910382747
 -15 -> 0.02910382747
 -14 -> 0.02910382747
 -13 -> 0.02910382747
 -12 -> 0.02910382747
 -11 -> 0.02910382747
 -10 -> 0.02910382747
  -9 -> 0.02910382747
  -8 -> 0.02910382747
  -7 -> 0.02910382747
  -6 -> 0.02910382747
  -5 -> 0.02910382747
  -4 -> 0.02910383033
  -3 -> 0.02910383033
  -2 -> 0.02910383033
  -1 -> 0.02910383033
  +0 -> 0.02910383033

The Arduino source code as a GitHub Gist:

,

3 Comments

DDS Musings: Arithmetic with 32-bit Fixed Point Numbers

Spoiler alert: having spent a while trying to fit the DDS calculations into fixed-point numbers stuffed into a single 32 bit unsigned long value, it’s just a whole bunch of nope.

The basic problem, as alluded to earlier, comes from calculations on numbers near 32768.0 and 60000.0 Hz, which require at least 6 significant digits. Indeed, 0.1 Hz at 60 kHz works out to 1.7 ppm, so anything around 0.05 Hz requires seven digits.

The motivation for fixed-point arithmetic, as alluded to earlier, comes from the amount of program memory and RAM blotted up by the BigNumber arbitrary precision arithmetic library, which seems like a much bigger hammer than necessary for this problem.

So, we begin.

Because the basic tuning increment works out to 0.0291 Hz, you can’t adjust the output frequency in nice, clean 0.01 Hz clicks. That doesn’t matter, as long as you know the actual frequency with some accuracy.

Setting up the DDS requires calculations involving numbers near 125.000000 MHz and 2³², both of which sport nine or ten significant figures, depending on how fussy you are about calibrating the actual oscillator frequency and how you go about doing it. Based on a sample of one AD8950 DDS board, the 125 MHz oscillator runs 300 to 400 Hz below its nominal 125 MHz: about 3 ppm low, with a -2.3 Hz/°C tempco responding to a breath. It’s obviously not stable enough for precise calibration, but even 1 ppm = 125 Hz chunks seem awkwardly large.

Many of the doodles below explore various ways to fit integer values up to 125 MHz and fractions down to 0.0291 Hz/count into fixed point numbers with 24 integer bits + 8 fraction bits, perhaps squeezed a few bits either way. Fairly obviously, at least in retrospect, it can’t possibly work: 125×10⁶ requires 28 bits. Worse, 8 fraction bits yield steps of 0.0039, so you start with crappy resolution.

The DDS tuning word is about 2×10⁶ for outputs around 60 kHz, barely covered by 21 bits. You really need at least seven significant figures = 0.1 ppm for those computations, which means the 125 MHz / 2³² ratio must carry seven significant figures, which means eight decimal places: 0.02910383 and not a digit less.

En passant, it’s disturbing how many Arduino DDS libraries declare all their variables as double and move on as if the quantities were thereby encoded in 64 bit floating point numbers. Were that the case, I’d agree 125e6 / pow(2.0,32) actually meant something, but it ain’t so.

The original non-linear doodles, which, despite containing some values useful in later computations, probably aren’t worth your scrutiny:

AD9850 DDS Fixed-point Number Doodles - 1

AD9850 DDS Fixed-point Number Doodles – 1

AD9850 DDS Fixed-point Number Doodles - 2

AD9850 DDS Fixed-point Number Doodles – 2

AD9850 DDS Fixed-point Number Doodles - 3

AD9850 DDS Fixed-point Number Doodles – 3

AD9850 DDS Fixed-point Number Doodles - 4

AD9850 DDS Fixed-point Number Doodles – 4

AD9850 DDS Fixed-point Number Doodles - 5

AD9850 DDS Fixed-point Number Doodles – 5

AD9850 DDS Fixed-point Number Doodles - 6

AD9850 DDS Fixed-point Number Doodles – 6

AD9850 DDS Fixed-point Number Doodles - 7

AD9850 DDS Fixed-point Number Doodles – 7

AD9850 DDS Fixed-point Number Doodles - 8

AD9850 DDS Fixed-point Number Doodles – 8

,

12 Comments