Session story · 2026-08-31

Two frequency errors, and the generator's was in spec

Session: 2026-08-31, 01:03 → 05:08 (one sitting, a break in the middle) · Outcome: DUT-mode level offset measured at 1.27 dB; FFT sample rate calibrated to 65546.5 Hz; the generator's frequency error traced to AP's default Fast mode. Commit a123017, pushed.

Where we started

The previous session had fixed relative level tracking in DUT mode and retracted the "7.4 dB cable loss". The DUT-mode THD path still read about 7 dB low in absolute terms, and the FFT, validated against APWIN, was the only trusted reference.

The working belief was: the remaining DUT offset is roughly 1.4 dB of something plus ~6 dB of frozen exponent, and the FFT can pin it.

Chris opened with: "how would you tackle the DUT absolute level issue?"

The question

What is the DUT-mode absolute offset, and is it one constant?

What happened

A plan with prerequisites. Claude proposed four steps: verify the slope first, then derive a DUT-mode constant, then validate it on data not used to derive it. "what does step 1 need as a pre-requisite?" Claude listed four, including one that would quietly ruin the test: stay within −30 to +4 dBV, because the generator table clamps below −30 and snaps between 4 and 6. A known bug in one subsystem invalidates tests of another that depends on it. Claude added level and range-code logging to the sweep so step 1 became one run.

Step 1 passes. Across 18 levels and five range changes the error held at −1.39 ± 0.035 dB.

Step 2: the FFT as truth. Chris supplied the rig details: "this is with the cable and adapters i mentioned before. no schiit sys". At −20 dBV, N=8192, the FFT read −20.13 dBV: 0.13 dB of chain loss. The 0 dBV capture was invalid: the mantissa sat at 65534, and the log said the range write had not taken. Claude called its own forced range code the mistake and changed the FFT to autorange, then freeze what the analyzer chose, as APWIN does. The 0 dBV re-run read −0.13. Offsets: −1.26 dB at code 6, −1.28 at code 3. Claude stored kDUTLevelOffsetDB = 1.27, with a comment that it belongs to apib_thd_dut_setup_0dbv.bin and must be re-derived if that blob changes.

A noisy register tested for equality. The confirming sweep was flat at −0.11 to −0.15 dB, until range code 7, where Δ jumped to −30.20. The staleness check was octave == anchor.octave, and the frozen register jittered from 1826 to 1834. Claude widened the tolerance to half a range step.

Strange and suspicious. "here is the log. To be honest i really find that sawtooth pattern strange and suspcious". Claude showed the residual was constant at about 6800 across the sweep, so THD+N was fixed noise over a rising signal, resetting at each range change. A real DUT's distortion would sit above that. Claude also said the residual was about six times Test 3's. Chris: "yes i am confused as to why the "measured" noise floor is so high with the loopback . you said "To get a clean curve you'd pin the analyzer range for the whole sweep — which the FFT path already does — or plot against measured level within each range." I'm not sure i understand." Claude withdrew the second option as hand-waving.

Reading the floor. "is that something you can do without the cascade app up" Claude said no: the FFT needs a blob replay, which is where its Python probes had failed the day before. Chris ran it in the app at −40 ("note the error at setting the amplitude to -40"; the generator clamped to −30 and said so). The floor was flat from about 2 kHz to 20 kHz, with no 50/60 Hz picket fence and no harmonics above the floor. Claude flagged a low-frequency rise and a peak sitting slightly below 1000 Hz.

Chris's hypothesis. "it could be that the oscillator is set to "fast" and not "accurate"?" Claude fitted the bin asymmetry against a Blackman-Harris response: the tone was 0.20 bins, about 1.6 Hz, below 1000 Hz. Our encoding explained 0.125 Hz of it (02 83 encodes 999.875 Hz, a 1-LSB table bug). Claude pointed out that a 0.16% fs error would look identical.

A counter breaks the tie. "how do you suggest resolving it? I do have external frequency counters that can be used to measure if needed" Claude made the log print what each word actually encodes and the interpolated FFT peak, and asked for five frequencies across two decades.

First reading, without the counter. The FFT-only data showed a fractional error falling from −0.18% at 1 kHz to −0.046% at 20 kHz, which a wrong fs cannot produce. Claude also led with a "natural experiment": 100 Hz and 1 kHz share tuning code 8384 and showed the same error. In the same message Claude retracted the noise-floor claim. The integral had been measuring DC-offset energy in the 8 and 16 Hz bins and Blackman-Harris sidelobes. With a median per band the floor was about −129 dBV/bin, roughly −95 dB THD+N at 0 dBV. Chris had queued a separate point: "thd measurements over about 5k can't be calculated really, because our measurements don't get over 20k, so we don't see enough harmonics". Claude agreed and suggested blanking harmonic readouts that fall off the end.

Then the counter. "Oh on the counter - the actual measuremnts :", followed by five readings: 99.9, 998.2, 4995.3, 9990 and 19994.1. Counter divided by the interpolated FFT peak gave a constant ratio at 1k, 5k, 10k and 20k: implied fs 65546.3 to 65546.7, agreeing to 6 ppm. kFFTSampleRateHz = 65546.5, about +160 ppm, recorded as a per-unit calibration. The generator error was separate and frequency-dependent. Claude retracted the natural experiment: it rested entirely on the 100 Hz point, whose implied fs carried ±32.8 Hz against ±0.2 Hz at 20 kHz. That became feedback_noisiest_point.md. Then: "we can run tests more times to find repeatability. but for now, write this to memory and i will have a break".

Questions after the break. ""The generator error is the one that matters — it's ten times the fs error," -what is "fs error"?" Claude explained fs and laid out the 1 kHz chain: asked 1000.000, encoded 999.875, produced 998.2, FFT reported 998.043. Chris asked about the retraction too: "can you explain this? I don't understand". Claude explained that a fixed ±0.3 Hz interpolation error is ±0.3% at 100 Hz, nearly twice the 0.18% effect being measured.

Am i wrong? Claude proposed mapping the generator curve with a longer counter gate. Chris: "it seems from what you are saying like setting the generator to "accurate" and verifying it will make the work above not necessary. am i wrong?" Claude agreed it had the order backwards and suggested toggling the setting in APWIN while watching the counter. ""toggle the panel" - what panel?" Claude checked and admitted that every "Gen panel = ACCURATE" instruction in the repo was its own earlier note, repeated across T65–T69, and T65's header said the property name was unconfirmed. Chris: "i know where the setting is in APWIN. no problem".

The manual. A search agent found it documented: radio pair Fast / High Acc., BASIC AP.Gen.FreqAccuracy, GPIB :AGEN:HIACcuracy, default Fast. Fast is ±0.5%, High Acc. ±0.03%, and High Acc. adds 150 ms of settling per frequency change. The measured errors were inside the Fast spec. The label was "High Acc.", not "ACCURATE" as the procedures had said.

The turns

  1. Naming the −30/+4 window before running step 1.
  2. "To be honest i really find that sawtooth pattern strange and suspcious"
  3. "it could be that the oscillator is set to "fast" and not "accurate"?"
  4. A frequency counter: the only instrument in the loop that depended on neither assumption.
  5. "am i wrong?": the fix before the fallback.

Beliefs that changed this session

BelievedThenWhat overturned it
DUT offset ~1.4 dB, partly cabling1.27 dB calibration + 0.13 dB cableFFT at two range codes
forcing the FFT range code worksit fails when overloaded0 dBV capture, mantissa 65534
the noise floor is 6× too highmostly Claude's integralmedian-per-band rework
fs = 65536 Hz exactly65546.5 Hz (+160 ppm)counter, four points to 6 ppm
same code ⇒ same error (100 Hz)inside the noisecounter did not reproduce it
"ACCURATE" is the setting"High Acc.", default FastAP manuals

What we learned about the instrument

What we learned about working together

Evidence

Where we ended

DUT level fixed to a measured constant, fs calibrated, the generator error explained by spec. Open: re-capture T69 with High Acc. set and verify against the counter; repeatability runs; the encoder's 1-LSB table bug; and Claude's untested idea that the tuning DAC's lowest bit does nothing. Claude's two tables of the generator error disagreed (−0.167/−0.119/−0.088% and −0.180/−0.094/−0.100%); the counter readings themselves give the second set.