Reconstructed chapter · 2026-04
Reconstructed chapter: 2026-04-16 → 2026-06-01 · Sources: 8 parent commits, 2 app commits, 37 memory-file changes · No transcript survives; this is rebuilt from commits and memory diffs.
The instrument is an Audio Precision SYS-2522. It talks APIB, AP's own bus, not GPIB. A third-party adapter bridges APIB to USB bulk transfers (the first memory file names it: Duke Aguiar, VID 0x16D0, PID 0x0AF0). No Mac software existed. The only working control software was APWIN 2.24 on Windows.
The method was capture and replay. A pcap is a recording of every USB packet between APWIN and the adapter, made with Wireshark on the Windows machine. A blob replay is sending a captured stretch of those packets back to the instrument, byte for byte, to put it into the same state.
The app began on 2026-04-16 as an empty Xcode template (4309d16: 8 files, CascadeApp.swift and ContentView.swift). Nothing else from April survives except two dates in later files. PLAN.md says test_init.py was "confirmed working April 2026", and protocol_notes.md cites a scan_sweep.py run of 2026-04-19.
Can a Mac drive the SYS-2522 at all? That meant: open the adapter, replay an init, set a frequency, read a level back, and do all of it without damaging the instrument.
The first commit was a protocol notebook. The parent repo's "Initial commit" (35c1933, 2026-05-04) added 66 files: about 40 analyze_*, scan_* and test_* Python scripts, a 500-line apib_driver.py, AP's PDF manuals, and apib_init_data.py, the init packets extracted from a capture. Its protocol_notes.md decoded the opcodes: EB reads a register, E6 writes one, E9 reads a 24-bit DSP value, EC writes an 11-byte DSP block, 8A is an "alt format" write "used for init". Several were still "Unknown".
The first frequency theory was a DDS. That same file had a "working hypothesis": frequency rides in the 24-bit value of an EC write, as a DDS phase accumulator (direct digital synthesis, where a counter increment sets the output frequency): frequency_Hz = register_value × 204800 / 16777216. Three examples were marked ✓, including 200.0 Hz. It fit the captures. It did not survive the next snapshot.
The adapter wedged, and Windows was lost. docs/PLAN.md (in 81ab367) records that test_anlr_restart.py crashed mid-init on 2026-05-04 and left the adapter firmware "in a partial state". APWIN then failed with Wait for multiple objects failed with error 258. Power-cycling the PC and the AP did not fix it. The plan's table read: "New pcap captures | ❌ Not possible until Windows APWIN restored". The Mac side still worked, because macOS reset the adapter on set_configuration().
The safety rule. The first synced memory, feedback_safety.md (8198a38, 2026-05-11), is described as: "User has explicitly and repeatedly required safe, non-destructive tests only". It records that guessed values written to DSP registers caused a "bad RAM" error that crashed APWIN and needed a full instrument power cycle. The rule it set: writes come verbatim from a pcap, and before any new write, state the packet number it was seen at. The record does not say on what date the bad RAM error happened, or whether it was the same event as the 2026-05-04 wedge.
Two silent bugs in the transport. PLAN.md diagnoses a 2026-05-05 hardware run where every sweep row came back INVALID. Cause 1: test_sweep_point.py called dev.reset(), which the working test_init.py never did. Cause 2: opcode 0x02 was missing from the RESP_LEN table (how many reply bytes each opcode returns). The init data held 102 of those reads. Their 102 unread replies sat in the USB buffer and shifted every later read. A second memory, feedback_scan_all_usb.md, records a related find: three vendor control requests (bReq 0x54/0x53/0x55) that "enable consecutive bulk writes" were visible only in the control-transfer events, at positions 386–391 of coldstart.pcapng. Looking at bulk traffic alone had led to "a dead end". Then 81ab367 (05-11) regrew the init blob from 26,903 packets to 40,065, because the "trigger was failing", and added a 13,237-packet measurement-setup blob. The memory calls this "two-phase init".
Frequency moved to an 8A word. By 81ab367 the notes said the opposite of 35c1933: "EC writes to GEN_A module are completely ineffective for frequency control post-init." Frequency is 8A 02 21 b0 b1. The notes gave a power-law fit, word = 65535 - round(64002 * (f_hz / 20000) ** 0.3), with "Error < 0.01% across all 10 pcap verified points". A doc-sync audit dated 2026-05-09 had already found that Swift used 65536 - 64003 while Python used 65535 - 64002.
Then the fit was replaced by a table. protocol_frequency.md (synced 8198a38) records the working version. Bits [15:2] of the word are a 14-bit index, interpolated from an 11-entry table taken from captures. Bits [1:0] select the decade: 01 for 20 kHz–2 kHz, 10 for 2 kHz–200 Hz, 11 for 200 Hz–20 Hz. The memory adds that the earlier table "was off-by-one: each freq was paired with the next freq's word". It says all 31 points from 20 Hz to 20 kHz were verified on an oscilloscope on 2026-05-11. project_sub2500hz.md is titled "SOLVED" and says "The earlier theory about EC DDS + prescaler was wrong."
The doc-sync agent. The same evening, three commits set it up (8198a38, c65345e, df94af8, 22:50–23:12). Memory files were copied into docs/memory/ so a remote agent could read them. project_docsync_schedule.md went from "pending" (blocked because the project "isn't in a git repo") to a daily 9am Pacific trigger running claude-sonnet-4-6 against github.com/lcdvs/apcascade. The record does not explain why it was "not in a git repo" when a local commit existed from 2026-05-04; most likely it had not been pushed, but nothing says so. Two feedback memories came in df94af8. One: sync memory to the repo at session end. The other records that Chris corrected Claude for "going in circles suggesting workarounds" instead of asking whether APWIN could do an amplitude sweep. It can.
Calibrated level, and a first-run offset. By the 2026-05-12 state in 3fdcc6c, the sweep read dBV through a linear calibration: "val16 31913 = -0.033 dBV, ~4214 counts/dBV", taken from APWIN's own values in freq-response.pcapng. A new memory, project_firstrun_offset.md, recorded that the first sweep after generator-on read about −7.5 dBV and later runs read near 0. One or three throwaway measurements and a 2-second settle all failed to fix it.
Doc-sync's last full audit. 3fdcc6c (2026-05-15) applied the "safe" fixes, and the findings file was rewritten as "Last audit: 2026-05-15", with 7 hardware-facing items left for Chris to review, not auto-fixed. One: protocol_notes.md still said "poll for 0x9B" while the code polled for 0x22.
THD and DUT mode, briefly. docs/cascade-roadmap.md ("Created: 2026-05-13") planned this as an "Autonomous Agent Execution Plan". DUT mode (analyzer input from the XLR jacks instead of the internal generator) was decoded on 2026-05-14 as 8A 01 24/25 writes, copied verbatim from two pcaps. The whole app source, 1,615 lines of APDevice.swift among it, first entered the app repo in one commit, a98b1ab (2026-05-15). Through the month an E8 command sent as write-only left 48 unread replies (feedback_e8_transact.md), and a "dual-tone formula" for THD was "disproven". 94dfed4 (06-01) marked DUT and THD "complete" in the roadmap.
8A 02 21 word, and the generator obeyed. The decade selector bits then took it from 2.5 kHz down to 20 Hz.0x02, then 48 from E8. Each time the symptom was a wrong register value, not an error.8198a38 on, beliefs are committed, and that is why this chapter can be rebuilt at all.| Believed | Then | What overturned it |
|---|---|---|
Frequency is a DDS value in an EC write (35c1933, 05-04) | EC frequency writes are "completely ineffective"; the generator follows 8A 02 21 | notes in 81ab367, 05-11 |
| Frequency word = power law, < 0.01% error on 10 points | 11-entry table plus decade bits; earlier table off by one | protocol_frequency.md, 31 points on scope, 05-11 |
| Sub-2.5 kHz needs EC DDS + prescaler | same 8A 02 21 word, decade bits 10/11 | project_sub2500hz.md, 05-11 |
Data ready is EB 01 0F = 0x9B (PLAN.md) | lifecycle runs to 0x22; reset comes after the read | protocol_measurement.md, 05-10/11 |
Level reply is [XX 7C], XX the level, 7C a valid flag | a 16-bit value; byte[1] is a range code; 0x00 is a valid range | protocol_measurement.md vs protocol_decoded.md, both 05-11 |
| Doc-sync runs daily | last full audit 05-15; 05-31 was a "manual sync" | findings file, 6b3c296, 05-31 |
THD = dual-tone formula from EB 02 1C | "disproven"; per-range formula from EB 02 08 + EB 02 1C | protocol_thd_measurement.md, 6b3c296, 05-31 |
8A writes. EC writes to the generator were inert for both after init.gen-ampl.pcapng and marked "DO NOT compute".feedback_memory_at_compaction.md records Chris asking for this at about 10% remaining), and push memory to the repo so the auditor sees it.35c1933 (initial, 05-04), 81ab367 (scripts, 40k init blob, PLAN.md, 05-11), 8198a38, c65345e, df94af8 (doc-sync setup, 05-11), 3fdcc6c (THD/DUT docs, audit, 05-15), 6b3c296 (manual sync, 05-31), 94dfed4 (doc-sync update, 06-01)4309d16 (template, 04-16), a98b1ab (all app source, 05-15)docs/memory/): feedback_safety.md, feedback_scan_all_usb.md, feedback_ask_dont_assume.md, protocol_frequency.md, project_sub2500hz.md, protocol_measurement.md, docsync_findings.md, project_firstrun_offset.mdprotocol_notes.md (versions at 35c1933 and 81ab367), docs/PLAN.md, docs/cascade-roadmap.mdcoldstart.pcapng, freq-response.pcapng, gen-ampl.pcapngBy 2026-06-01 the Mac could init the 2522, set any frequency from 20 Hz to 20 kHz, step 5 amplitudes, and read a calibrated level sweep. Open: the −7.5 dBV first-run offset, the old frequency formula still in protocol_notes.md, and THD numbers not yet checked against APWIN. One gap in the record: 94dfed4 says "Add amplitude override to runTHDSweep() (APDevice.swift)", but it changes no Swift file and does not move the app submodule. The last app commit was a98b1ab, two weeks earlier. Whether the change existed uncommitted, the record cannot say.
Hindsight from later memory: the −7.5 offset was "the LINEAR level-cal floor", the THD formula was replaced on 2026-06-18, and doc-sync was dead from May on. On 2026-10-02 the record says "every EC 40 0A write is INERT" and the generator obeys only 8A 02 21. That is the same lesson the frequency work learned in May.