SPB Git

spb/earth-now Public License

earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.

TypeScript 93% Shell 2.3% SQL 1.4% JavaScript 1.3% Dockerfile 1.2% CSS 0.8%
1.9 KB · 60 lines typescript
Raw Blame History
1/**2 * earth-now.co3 * Author:  Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File:    packages/models/src/kalman.ts6 * Purpose: 1D Kalman filter (local-level) — observation/forecast fusion: track observations near them, converge to forecast further out7 */89export interface KalmanOptions {10  /** Process noise variance per step (how fast truth can drift). */11  processVariance: number;12  /** Measurement noise variance (how much we trust each observation). */13  measurementVariance: number;14  /** Initial state estimate. */15  initialValue: number;16  /** Initial estimate variance. */17  initialVariance: number;18}1920export interface KalmanStep {21  value: number;22  variance: number;23}2425/** Run a local-level Kalman filter over the series; returns the filtered state at each step. */26export function kalmanFilter(series: readonly number[], opts: KalmanOptions): KalmanStep[] {27  const { processVariance: q, measurementVariance: r } = opts;28  let x = opts.initialValue;29  let p = opts.initialVariance;30  const out: KalmanStep[] = [];31  for (const z of series) {32    // Predict.33    p += q;34    // Update.35    const k = p / (p + r);36    x += k * (z - x);37    p *= 1 - k;38    out.push({ value: x, variance: p });39  }40  return out;41}4243/**44 * Blend the last filtered observation state with an external forecast:45 * inverse-variance weighting where the observation's variance grows with the46 * horizon (q per step), so weight shifts smoothly toward the forecast.47 */48export function blendWithForecast(49  lastState: KalmanStep,50  stepsAhead: number,51  processVariance: number,52  forecast: { value: number; variance: number },53): KalmanStep {54  const obsVariance = lastState.variance + Math.max(0, stepsAhead) * processVariance;55  const wObs = 1 / obsVariance;56  const wFc = 1 / forecast.variance;57  const value = (lastState.value * wObs + forecast.value * wFc) / (wObs + wFc);58  return { value, variance: 1 / (wObs + wFc) };59}60