docs: refresh Forecast vs. actual duration for the real-temperature anchoring fixes
Documents the recent SudTask.send_forecast()/recv() changes (anchored at get_theta_ist_set(), re-anchored on every fresh Start) and the two spurious-divergence fixes: client/brewpi_gui.py's forecast_history t=0 seeding, and the hardcoded last_theta_ist=20 bug in both TempController variants. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DkkuG48uHFCGKe6dPSERFk
This commit is contained in:
@@ -308,11 +308,20 @@ server uses - plant params (`M`/`C`/`L`/`Td`) aren't a constructor
|
||||
argument at all, since they come from the doc being forecast itself
|
||||
(`Sud.derive_plant_params()`, same as the real run), not from any generic
|
||||
default. A multi-hour brew simulates in well under a second since it's
|
||||
pure CPU-bound iteration, no real time/IO involved. This
|
||||
is deliberately *not* recomputed as a run progresses: a forecast that keeps
|
||||
re-anchoring itself to match whatever's actually happening would no longer
|
||||
be a meaningful baseline to compare reality against. It's computed once,
|
||||
up front, and left alone.
|
||||
pure CPU-bound iteration, no real time/IO involved.
|
||||
|
||||
It's anchored to the real current temperature
|
||||
(`TempControllerBase.get_theta_ist_set()` - the raw sensor reading, valid
|
||||
even before this controller's first `process()` tick, unlike
|
||||
`get_theta_ist()`), not a cold start at ambient - `SudTask.send_forecast()`
|
||||
computes it once on Load (a preview of what's coming), and again on every
|
||||
fresh `Start` (not a Pause→resume, which keeps whatever forecast a run
|
||||
already established rather than discarding it mid-run), since the real
|
||||
temperature can have drifted in the meantime
|
||||
(time passing, manual heating via the Manual tab in between). Once a run
|
||||
is actually progressing, though, it's deliberately *not* continuously
|
||||
re-anchored to match whatever's happening: a forecast that keeps chasing
|
||||
reality would no longer be a meaningful baseline to compare it against.
|
||||
|
||||
`SudTask._send_forecast()` thins the curve to at most `MAX_FORECAST_POINTS`
|
||||
(1000) before broadcasting it - a fine enough `dt` over a multi-hour brew
|
||||
@@ -366,6 +375,33 @@ speedup runs measurably below it (confirmed: ~80x instead of a configured
|
||||
100x), which on its own used to produce a divergence large enough to look
|
||||
like a control problem.
|
||||
|
||||
Two other sources of spurious (non-control-related) divergence, both
|
||||
fixed:
|
||||
|
||||
- `client/brewpi_gui.py`'s actual-trace history used to only get its first
|
||||
sample at `on_plot_timer()`'s next 1-second (real time) tick - under a
|
||||
real `sim_warp_factor`, simulated time has already moved measurably past
|
||||
`t=0` by then, leaving a small visible gap between the forecast's exact
|
||||
`t=0` anchor and where the actual line first appears. `on_sud_changed()`
|
||||
now seeds `forecast_history` with the real `(elapsed, plot_temp_ist)`
|
||||
point the moment the first `Elapsed` push of a *fresh* run arrives
|
||||
(gated on elapsed still being small, `FRESH_RUN_ELAPSED_THRESHOLD_S`, so
|
||||
reconnecting mid-brew doesn't get a bogus point plotted at `x=0`).
|
||||
- Both `TempController` variants used to hardcode `last_theta_ist = 20` at
|
||||
construction, used to compute the very first `process()` tick's
|
||||
`heatrate_ist` - a wildly bogus rate spike whenever the real starting
|
||||
temperature wasn't actually 20 (invisible until a forecast actually got
|
||||
anchored to something other than 20, per the fix above), which then took
|
||||
several exponentially-smoothed ticks to decay out of the rate-PID loop,
|
||||
visibly distorting the trajectory during exactly that window. It now
|
||||
starts `None` and treats the first tick's rate as `0` (no real history
|
||||
yet) instead of computing it against a hardcoded, usually-wrong
|
||||
baseline - `SudForecastEstimator` is the case that actually surfaced
|
||||
this: it builds a fresh `TempController` on every call, so its first
|
||||
tick always hit this fresh, unlike the real controller, which only ever
|
||||
goes through it once near server startup and has long since
|
||||
self-corrected by the time any forecast comparison matters.
|
||||
|
||||
## Logging & analysis
|
||||
|
||||
`tasks/sud_log.py`'s `SudLogTask` records every Sud run's measured data -
|
||||
|
||||
Reference in New Issue
Block a user