What
DspTap tap/DspTap#19 (FFT plan Stage 5) adds include/tap/dsp/fft/spectrum.h: tap::dsp::packed_spectrum<Sample>, a non-owning, constexpr, allocation-free view over the packed real spectrum that basic_real_fft produces and consumes. Its docstring is now the one home of the packing (bin[k] = a[2k] + i·a[2k+1], DC at a[0], Nyquist at a[1], W = exp(+2πi/N), inverse unnormalized), with native accessors dc(), nyquist(), re(k), im(k), power(k), num_bins(), and one convention-flipping accessor, bin_engineering(k), which conjugates.
MuTap re-derives that packing by hand at 77 sites. Per the plan's adversarial review (P2), the DspTap PR migrates DspTap's own consumers only; MuTap adopts the view per header, when each header is next touched, never as one wholesale rewrite.
The sites
| header |
hand-indexed spectrum sites |
fdaf.h |
25 |
fd_kalman.h |
28 |
nn_suppressor.h |
15 |
postfilter.h |
9 |
The gate, per header
Each adopting PR is gated by both of:
- Fingerprint identical. MuTap's fingerprint harness (Stage 1) must hash byte-identically before and after, on the same host, compiler and flags, for every chain the header participates in. The view inlines to the same index expressions, so any difference is a migration error, not rounding.
- 0% icount. The ratcheted instruction counts for the scenarios that exercise the header must not move (an accepted regression is a written commit, per D11).
The rule for the hot loops
The ratcheted loops in these headers compute native-convention complex products by hand (spectral products between spectra from this library need no conjugation). They adopt the native accessors only: re(k), im(k), dc(), nyquist(), power(k). They must never use bin_engineering(): it conjugates, it constructs a std::complex temporary, and it exists for textbook phase math (pvoc), not for regressor accumulation. A hot loop that grows a sign flip under this migration has been migrated wrong, and the fingerprint gate above is what catches it.
num_bins() replaces every hand-computed N/2 + 1 at the same time.
Order
Wait for the DspTap PR to merge and the submodule pin to move (rollout rule: DspTap squash → MuTap pin + code → MuTap-Max pin). Then one PR per header, in whatever order the headers are next touched for other reasons.
References: tap/DspTap#19; DspTap docs/audit-fft-and-code-smells.md, Part 1 F8, Part 3 Stage 5, Part 6 P2, Part 13.
🤖 Generated with Claude Code
https://claude.ai/code/session_019ZPTzNxo5Fe4EtpXXKf7Sy
What
DspTap tap/DspTap#19 (FFT plan Stage 5) adds
include/tap/dsp/fft/spectrum.h:tap::dsp::packed_spectrum<Sample>, a non-owning, constexpr, allocation-free view over the packed real spectrum thatbasic_real_fftproduces and consumes. Its docstring is now the one home of the packing (bin[k] = a[2k] + i·a[2k+1], DC ata[0], Nyquist ata[1],W = exp(+2πi/N), inverse unnormalized), with native accessorsdc(),nyquist(),re(k),im(k),power(k),num_bins(), and one convention-flipping accessor,bin_engineering(k), which conjugates.MuTap re-derives that packing by hand at 77 sites. Per the plan's adversarial review (P2), the DspTap PR migrates DspTap's own consumers only; MuTap adopts the view per header, when each header is next touched, never as one wholesale rewrite.
The sites
fdaf.hfd_kalman.hnn_suppressor.hpostfilter.hThe gate, per header
Each adopting PR is gated by both of:
The rule for the hot loops
The ratcheted loops in these headers compute native-convention complex products by hand (spectral products between spectra from this library need no conjugation). They adopt the native accessors only:
re(k),im(k),dc(),nyquist(),power(k). They must never usebin_engineering(): it conjugates, it constructs astd::complextemporary, and it exists for textbook phase math (pvoc), not for regressor accumulation. A hot loop that grows a sign flip under this migration has been migrated wrong, and the fingerprint gate above is what catches it.num_bins()replaces every hand-computedN/2 + 1at the same time.Order
Wait for the DspTap PR to merge and the submodule pin to move (rollout rule: DspTap squash → MuTap pin + code → MuTap-Max pin). Then one PR per header, in whatever order the headers are next touched for other reasons.
References: tap/DspTap#19; DspTap
docs/audit-fft-and-code-smells.md, Part 1 F8, Part 3 Stage 5, Part 6 P2, Part 13.🤖 Generated with Claude Code
https://claude.ai/code/session_019ZPTzNxo5Fe4EtpXXKf7Sy