Hi Robert, Thanks for the detailed reply -- this was exactly the kind of feedback I was hoping the list would provide. I took your numbers seriously enough to verify them against my actual filters, and the short version is: your rules of thumb are impressively accurate.
> I found that L=512 (along with linear interpolation between neighbors) gets you 120+ dB S/N ratio from the energy getting folded back into the baseband. L=256 is okay. About 108 dB S/N. Confirmed, almost exactly. I fractionally resampled a 19.5 kHz tone (near-worst case) through my tables at 1 + 200 ppm, LS-fit the expected tone, and measured the residual: L=256 gives 108.6 dB, L=512 gives 120.7 dB, slope +12.0 dB per doubling with the linear inter-phase blend, and +6.0 dB per doubling for drop-sample -- all four of your figures to within half a dB. The check is now a committed notebook in the repo (notebooks/asrc_rbj_analysis.ipynb) with the results assertion-pinned. One nuance worth stating: the floor is frequency-dependent; the same L=256 table measures ~144 dB at 997 Hz, which is how my mid-band measurements (126-135 dB through the full converter) coexist with the ~108 dB near-Nyquist interpolation floor. > Ooooh, that's a lotta taps. I think you can get away with T=32 taps. Here I get a different answer, and I think it comes down to the stopband budget rather than the phase count. T buys the FIR's own transition width and stopband depth: at my band edges (flat to 20k, first image suppressed from 28k, 120 dB) the Kaiser/harris estimate lands at T=47-48, and measurement agrees -- pairing T=32 (whose achievable design at those edges is ~96 dB) with L=512 still tops out at 84 dB at 19.5 kHz, because the FIR stopband dominates before L matters. T=32 is exactly my "fast" preset, offered for the 96 dB tier. With your Parks-McClellan designs you'd shave a few taps (~44 for the same spec by the equiripple estimate), which is on my list to try. > But it also puts zeros right at the integer multiples of the sample rate ... You can have the script if you want it. Yes please -- I'd like to see it. I measured the naive version of the idea (convolving my Kaiser prototype with a one-sample rect): the benefit is real (5.6 dB deeper worst image around fs, ~14 dB around 2fs) but so is the sinc droop, -2.64 dB at 20 kHz, which my passband spec (+/-0.01 dB) can't absorb. So doing it properly means folding the droop compensation into the passband target of an optimal design, which I gather is exactly what your MATLAB script does. > That's sounds more expensive than swapping the order. I think creating two output samples and linearly interpolating that pair of values for the output would be quicker. For mono it's essentially a wash (2T MACs either way, and your order saves the scratch-row traffic). The reason I blend coefficients is multichannel: every channel of a frame evaluates at the same mu, so the blended row is computed once and shared -- (N+1)*T ops vs 2*N*T. That restructuring measured -36% wall-clock for stereo and -52% for 8-channel when I made it, and the high-channel-count path (one blended row broadcast across channel lanes, frame-major) depends on the shared row existing. > Not sure what you mean by "occupancy". The FIFO backlog between the two clock domains, in frames -- it's the loop's phase detector. The consumer-side observable is backlog + mu, which makes it continuous across whole-sample slips (the +/-1-frame staircase disappears into the fraction). Your SHArC time-stamping scheme is a more informative observable and I can see it buying faster acquisition; my occupancy servo gets there with gain scheduling instead (wide to acquire, narrow to reject the block-quantization sawtooth). And on the 44.101 -> 44.100 point: the once-per-second slip does happen in my scheme, by design, but it's bit-continuous -- the table carries L+1 rows so "mu wraps to 0 on a shifted window" is the same filter, and a slip-continuity test bounds the output's second difference against the analytic bound of a clean sine at +500 ppm. Your Q32.32 index and my accumulator are close cousins, by the way -- mine is Q0.64 carrying only the deviation from unity (the integer "1" rides the window advance), which near-unity operation makes possible; 64 fractional bits is admittedly more than anyone needs. Thanks again -- between this and the earlier notes I have two concrete experiments queued: a PM/LS prototype with the k*fs zeros and droop pre-compensation, and a mono benchmark of your two-dots-then-lerp ordering. Cheers, Tim
