GUI: seed the actual-trace history with the real t=0 point on a fresh start

The actual trace's first sample otherwise only lands at on_plot_timer()'s
next 1-second tick - under a real sim_warp_factor that's already a
noticeable way into the run, leaving a small visible gap between the
forecast's exact t=0 anchor and where the actual trace first appears.

Seeds forecast_history with (elapsed, plot_temp_ist) the moment a fresh
Elapsed push arrives, gated on elapsed still being small
(FRESH_RUN_ELAPSED_THRESHOLD_S) so reconnecting mid-brew - where the
current reading has nothing to do with that run's actual t=0 - doesn't get
a bogus point plotted at x=0.

Verified live: without this, the actual trace's first point landed at
(0.5min, 20.6°) against a forecast anchored at (0min, 19.92°) - a visible
gap. With it, the seeded point (0.017min, 19.9°) lines up with the anchor
almost exactly, and the following natural samples continue smoothly from
there.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkkuG48uHFCGKe6dPSERFk
This commit is contained in:
2026-06-22 22:19:08 +02:00
co-authored by Claude Sonnet 4.6
parent d5a8c2422b
commit b5755f8a52
+23
View File
@@ -24,6 +24,16 @@ SUD_RUNNING_STATES = {"SudState.RAMPING", "SudState.HOLDING", "SudState.WAIT_USE
SUD_WAIT_USER_STATE = "SudState.WAIT_USER"
SUD_PAUSED_STATE = "SudState.PAUSED"
# Below this (simulated seconds), an 'Elapsed' push is treated as "this run
# just started" rather than "reconnecting mid-brew" - see on_sud_changed()'s
# Elapsed handling. Generous relative to typical dt (0.1-1s/tick): a fresh
# start's first push always lands well under it, while reconnecting to an
# established run (anything past its first handful of ticks) always lands
# well over it - and seeding the history a few seconds "late" within a
# genuinely fresh run is harmless anyway, since the temperature there is
# still essentially the t=0 anchor.
FRESH_RUN_ELAPSED_THRESHOLD_S = 5.0
def _style_axis(ax):
ax.set_facecolor('white')
@@ -827,6 +837,19 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
# a run starting.
if self.sud_elapsed_seconds is not None:
self.sud_elapsed_seconds = msg['Elapsed']
# Seed the actual-trace history with this run's real
# t=0 point - without it, the first sample only lands
# at on_plot_timer()'s next 1-second tick, which under
# a real sim_warp_factor can already be a noticeable
# way into the run, leaving a small visible gap between
# the forecast's exact t=0 anchor and where the actual
# trace first appears. Gated on elapsed still being
# small so reconnecting mid-brew (large elapsed) - where
# this temperature reading has nothing to do with that
# run's actual t=0 - doesn't get a bogus point plotted
# at x=0.
if not self.forecast_history and self.plot_temp_ist and msg['Elapsed'] < FRESH_RUN_ELAPSED_THRESHOLD_S:
self.forecast_history.append((msg['Elapsed'] / 60.0, self.plot_temp_ist))
elif "Forecast" in key:
self.on_sud_forecast_received(msg['Forecast'])
elif "Error" in key: