# 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, 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.