Skip to content

Implement predictive location modeling with Kalman filtering, trajectory extrapolation, and anomaly detection for sub-second ETA and geofence pre-alerts #263

Description

@levibliz

Difficulty

10/10 — Expert. Estimated effort: 6–8 days for a senior engineer.

Context

The README.md targets "low-latency, high-throughput telemetry scenarios such as fleet tracking, asset monitoring, geofencing enforcement, and live mapping" (line 25). Current system is purely reactive — broadcasts location after received. For proactive operations (ETA to depot, geofence breach prediction, collision warning), the gateway must predict near-future positions from noisy GPS streams. A Kalman filter fusing position, velocity, and heading can predict 30–60 seconds ahead with <10m error for highway-speed vehicles.

Problem statement

Design and implement a predictive modeling engine that:

  1. Per-client Kalman filter: For each tracked asset, maintain a constant-velocity (CV) or constant-turn-rate (CTRV) Kalman filter state { x, y, vx, vy, heading, headingRate } in local tangent plane (ENU coordinates). Update on each location_update (measurement: lat, lon, speed, heading). Predict predict(dt) → future position at now + dt.

  2. Coordinate transformation: GPS (WGS84 lat/lon) → local ENU (meters) centered on filter origin. Use origin = first valid fix. Predict in ENU, transform back to lat/lon for output. Handle origin drift for long tracks (re-origin every 50km).

  3. Trajectory extrapolation API: predictor.getTrajectory(clientId, horizons: [10, 30, 60] seconds){ horizon, lat, lon, speed, heading, confidenceEllipse }. Confidence ellipse from filter covariance P (2D position submatrix).

  4. Geofence pre-alerts: For each predicted point, check geofence engine (issue 12) — if trajectory intersects fence within preAlertHorizon (default 60s), emit geofence_pre_alert event: { fenceId, fenceName, predictedEntryTime, predictedEntryPoint, confidence }. Allows dispatchers to reroute before breach.

  5. Anomaly detection:

    • Measurement anomaly: Innovation (residual) z - Hx exceeds 3σ → flag gps_anomaly (multipath, spoofing, dropout).
    • Behavioral anomaly: Speed/heading change exceeds physical limits (e.g., 0→100 km/h in 1s) → flag kinematic_anomaly.
    • Predictive anomaly: Predicted position diverges from actual by > 3σ after 30s → flag model_divergence (filter needs reset).
  6. ETA computation: For a target (depot, customer site, next waypoint), compute ETA distribution from predicted trajectory + covariance. predictor.getETA(clientId, targetLat, targetLon){ etaMean, etaStdDev, arrivalProbabilityAt(t) }.

  7. Integration with broadcast: Predictive events (geofence_pre_alert, gps_anomaly, eta_update) are new message types broadcast to room (sequenced, replayable per issue 6/16).

  8. Configuration: PREDICTOR_ENABLE: true, PREDICTOR_MODEL: "CV" | "CTRV", PREDICTOR_PROCESS_NOISE: 0.1, PREDICTOR_MEASUREMENT_NOISE: 5.0 (meters), PREDICTOR_PRE_ALERT_HORIZON_S: 60, PREDICTOR_MAX_HORIZON_S: 120.

Current behavior

  • No predictive modeling.
  • geofence-engine.js (issue 12) only evaluates current position.
  • No Kalman filter, no anomaly detection, no ETA.

Required behavior

  • New module src/predictor.js exporting PredictiveEngine class.
  • PredictiveEngine constructor: { geofenceEngine, roomManager, config }.
  • predictor.update(clientId, location) — runs Kalman filter predict+update cycle.
  • predictor.getTrajectory(clientId, horizons) — returns predicted positions with confidence.
  • predictor.getETA(clientId, targetLat, targetLon) — returns ETA distribution.
  • predictor.checkPreAlerts(clientId) — evaluates predicted trajectory against geofences, emits pre-alerts.
  • predictor.detectAnomalies(clientId, location) — returns anomaly flags.
  • Kalman filter implementation (CV model):
    • State: x = [x, y, vx, vy]^T (ENU meters, m/s)
    • Transition: F = [[1, 0, dt, 0], [0, 1, 0, dt], [0, 0, 1, 0], [0, 0, 0, 1]]
    • Measurement: H = [[1, 0, 0, 0], [0, 1, 0, 0]] (position only)
    • Process noise Q, measurement noise R — configurable.
    • Standard predict/update equations.
  • CTRV model (optional): adds heading and heading rate.
  • ENU transform: latLonToEnu(lat, lon, originLat, originLon) and inverse.
  • Memory: one filter per active client. Cleanup on disconnect (TTL 1 hour).

Constraints

  • Do not modify auth.js, validator.js, rate-limiter.js, conn-rate-limiter.js, logger.js, errors.js, room-manager.js, geofence-engine.js, protocol-registry.js, distributed-room-manager.js, tls-manager.js, admin-server.js, session-manager.js, compression.js, topology-manager.js, event-sourcing.js, collaborative-editor.js.
  • Do not modify existing test files. New test files required.
  • No new npm dependencies — implement Kalman filter from scratch (~100 lines).
  • Filter must be numerically stable (use Joseph form for covariance update).
  • Prediction horizon max 120s — beyond that, uncertainty too high.
  • Pre-alerts must not spam: debounce per (clientId, fenceId) — only alert on first prediction of entry.
  • Anomaly events rate-limited to 1/min per client per anomaly type.
  • Works with distributed mode (issue 11) — filter state synced via session resumption (issue 18).

Acceptance criteria

  • Kalman filter converges: stationary vehicle → position uncertainty decreases over time
  • Moving vehicle at 30 m/s → 30s prediction error < 10m (simulated GPS noise σ=5m)
  • getTrajectory returns correct ENU→lat/lon transform
  • Geofence pre-alert emitted 60s before predicted entry
  • GPS anomaly detected when innovation > 3σ
  • Kinematic anomaly detected for impossible acceleration
  • ETA distribution mean ± stddev matches Monte Carlo simulation
  • Filter state saved/restored via session resumption (issue 18)
  • npm run lint passes
  • All existing tests pass
  • New test file: tests/predictor.test.js with filter accuracy tests, pre-alert scenarios, anomaly detection, ETA validation

Out of scope

  • Multi-model IMM (Interacting Multiple Model) — single CV/CTRV model sufficient.
  • Map-matching / road network constraints — free-space prediction only.
  • Long-term prediction (>2 min) — uncertainty unbounded.
  • Client-side prediction — server-only for this issue.

Hints and references

  • Kalman filter (Joseph form for numerical stability):
    // Predict
    x = F @ x
    P = F @ P @ F.T + Q
    // Update
    y = z - H @ x
    S = H @ P @ H.T + R
    K = P @ H.T @ inv(S)
    x = x + K @ y
    P = (I - K @ H) @ P @ (I - K @ H).T + K @ R @ K.T  // Joseph form
  • ENU transformation (origin at lat0, lon0):
    const R = 6371000; // Earth radius m
    function latLonToEnu(lat, lon, lat0, lon0) {
      const dLat = (lat - lat0) * Math.PI / 180;
      const dLon = (lon - lon0) * Math.PI / 180;
      const x = R * dLon * Math.cos(lat0 * Math.PI / 180);
      const y = R * dLat;
      return { x, y };
    }
    function enuToLatLon(x, y, lat0, lon0) {
      const lat = lat0 + y / R * 180 / Math.PI;
      const lon = lon0 + x / (R * Math.cos(lat0 * Math.PI / 180)) * 180 / Math.PI;
      return { lat, lon };
    }
  • Confidence ellipse: eigenvalues of 2x2 position covariance submatrix → semi-major/minor axes, orientation.
  • Geofence intersection with predicted trajectory: sample predicted positions at 10s intervals, check point-in-polygon (issue 12). For linear motion, can compute exact intersection time with polygon edges.
  • Integration point: in server.js location_update case, after geofence processing, call predictor.update(actualClientId, { lat, lon, speed, heading, timestamp }) and predictor.checkPreAlerts(actualClientId).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions