Skip to content

Bug: kalman_plugin computes initial_value then discards it — kf.smooth() result unused and predict() ignores the initial state - #205

Open
musaaero wants to merge 1 commit into
nasa:mainfrom
musaaero:main
Open

musaaero wants to merge 1 commit into
nasa:mainfrom
musaaero:main

Conversation

@musaaero

@musaaero musaaero commented Oct 7, 2026 •

Copy link
Copy Markdown

Fixes #204

⚠️ Heads-up about your test suite

test/plugins/kalman/test_kalman_plugin.py::test_Kalman__predict_smoothes_data_and_predicts_result_using_KalmanFilter_functions_as_expected_when_initial_val_is_set (line ~362) mocks the KalmanFilter and asserts smooth.call_count == 1 while asserting nothing about predict()'s initial_value. So the tests lock in the dead call and can never detect that the initial value is ignored. A proper fix needs both the source change above and an updated test asserting predict is called with initial_value=arg_initial_val.

In plugins/kalman/kalman_plugin.py, Plugin._predict() does:

self.kf.smooth(subframe, initial_value=initial_val)   # return value discarded
predictions = self.kf.predict(subframe, forward_steps) # initial_val NOT passed

simdkalman.KalmanFilter is stateless — smooth() returns a result object and has no side effects on the filter, so the first line is dead computation (~2x cost per render). Meanwhile the initial_val built in _generate_residuals() (seeding each attribute's state with its first windowed observation) is never applied to the actual prediction, which silently falls back to simdkalman's default prior. This biases every residual used for fault flagging.

Verified with simdkalman 1.0.4: running predict() immediately after smooth() returns bit-identical predictions to running predict() alone, proving the smooth call contributes nothing.

Suggested fix:

predictions = self.kf.predict(subframe, forward_steps, initial_value=initial_val)

Note: the existing unit test mocks the KalmanFilter and asserts smooth was called while never checking predict's kwargs, so the test suite currently enforces the buggy behavior and would need updating alongside the fix. Happy to open a PR.

🐛 THE ISSUE: Dead-code Kalman smoothing — initial_value computed but never used in prediction

File: plugins/kalman/kalman_plugin.py
Function: Plugin._predict() — line 166–167

Current code (buggy):

def _predict(self, subframe, forward_steps, initial_val):
    ...
    self.kf.smooth(subframe, initial_value=initial_val)      # ← line 166: RESULT DISCARDED
    predictions = self.kf.predict(subframe, forward_steps)   # ← line 167: initial_val NOT passed!
    return predictions

I verified all of this against simdkalman 1.0.4 source + runtime experiments:

  • simdkalman.KalmanFilter.smooth() is a pure function. It returns a result object; it does not mutate any internal state of the filter. I proved this empirically: calling predict() before vs. after a smooth() call gives bit-identical results (np.allclose == True).
  • Therefore the entire self.kf.smooth(...) line is dead computation — _generate_residuals() carefully builds initial_val (a (n_headers, 2, 1) array seeding each channel's state with its first frame value), passes it into _predict, and it is thrown away. The docstring even promises "Initial value for Kalman filter" as a parameter, but the prediction runs with simdkalman's default prior instead.
  • Consequence: residuals are systematically biased. In my measurements, e.g. a constant signal at 400 yields residual 0.114 with the current code vs exactly 0.0 when initial_value is honored — noise floor drifts with signal magnitude/window position, degrading the fault detector's precision right where a safety-reasoning plugin should be most trustworthy.

⚠️Ai disclaimer - ai used for rewriting text for comment, english is definitely my 2nd language

@musaaero

musaaero commented Oct 17, 2026 •

Copy link
Copy Markdown
Author

Hi @nasa/OnAIR maintainers @asgibson 👋

I noticed this PR has been open for a little while, and the checks are currently waiting for approval to run. I’m new to contributing here, so I just wanted to give it a gentle nudge in case it slipped through.

Could someone please take a look and approve the workflow so the checks can run? I’m happy to make any changes if needed. Thanks for your time! 😊

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kalman plugin produces systematic false-positive faults on healthy telemetry

1 participant