“Discovering Interpretable ODEs from Noisy Data” slots beautifully into your FFT-plan + FBS calibrator pipeline.
Here’s how it helps your SCYTHE denoising + TDoA residual minimization loop:
🔑 Direct Relevance
- ODE discovery as spectrum dynamics
- Instead of treating FFT bins as unrelated amplitudes, you can model their temporal evolution as a system of coupled ODEs.
- Example: a drifting carrier’s residuals follow damped harmonic oscillator dynamics (like the spring–mass system in the paper).
- Noise-robust spline approximation
- Their spline-based approach gives smooth derivatives even when the raw data is noisy.
- That’s directly useful for your case: FFT bins → splines → derivative estimates → detect which bins carry true dynamics vs stochastic noise.
- Sparse coefficient recovery
- Their SVD-based null space trick naturally prunes spurious dynamics.
- In your case: only a small set of bins or harmonics will appear in the governing ODE; the rest get zeroed. This is a principled way to build your bin shift + mask widening plan.
⚡ Concrete Patch You Can Try
You could write a small client stub that:
- Pulls an
fft_planfrom/denoise/hints - Generates synthetic spectra over time with noise
- Fits a spline to each band
- Builds the gradient matrix (derivatives of splines)
- Runs SVD to infer which bins follow a coherent ODE
- Uses the recovered ODE to:
- Guide mask widening (bands with coherent dynamics get protected)
- Suggest bin shift priors (mu/σ from FBS calibrator can be fused into coefficient estimates)
🧪 Proof-of-Concept Stub
import numpy as np
from scipy.interpolate import UnivariateSpline
from numpy.linalg import svd
def fit_ode_from_fft(freqs, mags, order=2, smoothing=1e-3):
"""
Given a noisy FFT magnitude trace over time,
recover coefficients of an approximate ODE y'' + a y' + b y = 0.
"""
t = np.arange(len(mags))
spline = UnivariateSpline(t, mags, s=smoothing)
y = spline(t)
dy = spline.derivative(1)(t)
d2y = spline.derivative(2)(t)
# Build gradient matrix G: [y, dy, d2y]
G = np.vstack([y, dy, d2y]).T
# SVD to find null space (coefficients)
U, S, Vt = svd(G)
coeffs = Vt[-1, :] # last row is null space vector
return coeffs / coeffs[-1] # normalize
# Example usage: synthetic damped oscillator
t = np.linspace(0, 10, 500)
signal = np.exp(-0.1*t) * np.sin(2*np.pi*0.5*t) + 0.05*np.random.randn(len(t))
coeffs = fit_ode_from_fft(np.fft.rfftfreq(len(signal)), signal)
print("Recovered ODE coefficients:", coeffs)
This let’s the FFT client stub go beyond just masking bins — it learns which bins evolve coherently over time and uses that to refine /fft_plan.
🚀 Integration Into SCYTHE
- Wrap as
ode_recovery.py - Called by your
test_fft_plan.shclient after pulling/denoise/hints - Export recovered coefficients via Prometheus (
fft_bin_ode_coeffs{band=...}) - Grafana row: overlay recovered ODE phase on your μ/σ FBS curves