54 lines
2.6 KiB
Markdown
54 lines
2.6 KiB
Markdown
# Shared GUI spectrum
|
||
|
||
`include/set_spectrum.h` / `src/set_spectrum.c` implement the same numerical
|
||
pipeline for desktop and Android. CMake, `tools/build_host.py`, and the Android
|
||
NDK build include it. The wire protocol is unchanged. The host-only analyzer
|
||
allocates at most two FFT arrays (2×N doubles), releasing them before return.
|
||
|
||
The last N samples are used: the largest power of two within available data and
|
||
`max_size`, 16…16384. Timestamps are seconds; inputs are immutable. Fs is measured
|
||
from the selected endpoints. Non-monotonic/duplicate times and interval deviations
|
||
over 50% of the mean are rejected. Lesser jitter is linearly interpolated onto an
|
||
even grid (this can affect high-frequency amplitude; inspect returned jitter).
|
||
|
||
Processing order: interpolation, optional mean subtraction, filter, periodic
|
||
window, forward FFT, one-sided peak amplitude. Windows: rectangular, Hann,
|
||
Hamming, Blackman, Flat Top. Amplitudes are divided by window sum, doubled except
|
||
DC and Nyquist. Output is not RMS/PSD/dB. Coherent gain correction is exact for
|
||
bin-centered tones away from DC/Nyquist; off-bin tones still have scalloping.
|
||
|
||
Filters run before the window, on the unmodified sample copy. LP/HP are second
|
||
order Butterworth; band pass cascades HP and LP; notch uses Q=30.
|
||
Coefficients: [W3C Audio EQ Cookbook](https://www.w3.org/TR/audio-eq-cookbook/).
|
||
Initial state is the first sample's steady state, not a carried streaming state;
|
||
block-start transients may occur. Cutoffs must be strictly within (0, Fs/2).
|
||
|
||
## Python
|
||
|
||
```python
|
||
from protocan.spectrum import NativeSpectrum, Window, Filter
|
||
|
||
# lib = an existing ctypes.CDLL from protocan.native.NativeProtocol
|
||
spectrum = NativeSpectrum(lib).analyze(times_seconds, values,
|
||
window=Window.HANN, filter=Filter.LOW_PASS, high_hz=100,
|
||
max_size=4096, remove_mean=True)
|
||
plot(spectrum.frequencies, spectrum.amplitudes)
|
||
```
|
||
|
||
Adapter errors are ValueError, not empty/misleading arrays. No NumPy/Qt dependency.
|
||
|
||
## Android
|
||
|
||
`SpectrumAnalyzer.analyze(List<TrendPoint>, SpectrumOptions)` calls JNI using
|
||
relative seconds from millisecond timestamps. `TrendSpectrum` contains N, Fs,
|
||
maximum relative jitter, amplitudes or a displayable error. Call on a worker
|
||
dispatcher. `PlotViewport` provides axis-independent zoom/pan, focus anchoring,
|
||
128× limit and bounds clamping without any Compose dependency.
|
||
|
||
## Checks
|
||
|
||
Build CMake with tests and run CTest in Debug (assertions enabled). Python
|
||
`tests/test_spectrum.py` runs against an explicitly supplied `SETPROTOCOL_LIBRARY`:
|
||
independent direct DFT, amplitude correction, DC/Nyquist, off-bin leakage, filter
|
||
bands, timing rejection/interpolation, bounded tail selection and immutable inputs.
|