fix: make sim_warp_factor real-hardware-safe and sud logs forecast-reproducible
sim_warp_factor was scaling DT_TASK unconditionally, including against real hardware, and was leaking into recorded sample timestamps via wall-clock time - both violate its intended meaning (a reciprocal scale on task wait time only). Clamp it to 1.0 whenever Heater.type isn't "sim", and track elapsed time via tick-counted accumulators (Sud.elapsed for SudLogTask, a matching one for ServerLogTask) instead of time.monotonic(). Sud/server logs now also carry HeaterPowers, the effective SimWarpFactor, the raw Doc, a correct Name, and ForecastAnchors (one entry per real _reanchor_forecast() trigger) - enough for utils/analyze_log.py to rebuild a SudForecastEstimator and replay the exact same splice-per-transition forecast correction offline that a live client sees, replacing the old (always-dead) forecast_*.json sidecar lookup. Two bugs found and fixed along the way: - ServerLogTask's periodic wall-clock checkpoint had no way to know a SudLogTask run had just ended, so it would immediately re-write the just-completed log with PlantParams/ForecastAnchors cleared back to empty. Gated the checkpoint on _should_sample(). - _reanchor_forecast() truncated the old forecast against real elapsed time, which can end up numerically less than the old forecast's own (very wrong) speculative timestamps once a WAIT_USER confirmation runs long - leaving a "ghost" segment instead of a clean cut. Truncate against forecast_step_starts[index] instead, which lives in the same coordinate space as the forecast being cut. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JFDeoTKBDkXRhPfnyDEoBt
This commit is contained in:
+10
-4
@@ -76,9 +76,14 @@ if __name__ == '__main__':
|
||||
taskmgr = TaskManager()
|
||||
|
||||
DT = args.dt
|
||||
DT_TASK = 1.0 / args.sim_warp_factor
|
||||
theta_amb = config['ambient_temperature']
|
||||
plant_sim = config['Heater'].get('type', 'sim') == 'sim'
|
||||
# sim_warp_factor only reciprocally scales *wait* time between task
|
||||
# ticks (DT_TASK) - it has no meaning against real hardware, which
|
||||
# ticks at its own physical pace regardless of how this flag is set,
|
||||
# so real runs are always paced at DT_TASK == 1.0 (true real time).
|
||||
sim_warp_factor = args.sim_warp_factor if plant_sim else 1.0
|
||||
DT_TASK = 1.0 / sim_warp_factor
|
||||
|
||||
# Mash schedule - starts out empty; a client loads one of sude/*.json's
|
||||
# several schedules onto it later (see components/sud.py's Sud).
|
||||
@@ -127,7 +132,7 @@ if __name__ == '__main__':
|
||||
# see components/sud_forecast.py.
|
||||
forecast_estimator = SudForecastEstimator(
|
||||
DT, theta_amb, config['TempCtrl']['pid_type'], config['TempCtrl'],
|
||||
heater.get_powers(), args.sim_warp_factor, config.get('Pot', {}))
|
||||
heater.get_powers(), sim_warp_factor, config.get('Pot', {}))
|
||||
sud_task = SudTask(sud, tc, stirrer, pot, DT, DT_TASK, dispatcher.msgio_get("Sud"), forecast_estimator)
|
||||
taskmgr.add(sud_task)
|
||||
|
||||
@@ -141,7 +146,7 @@ if __name__ == '__main__':
|
||||
# Continuous server-session log - records from startup to shutdown
|
||||
# regardless of Sud state; written to logs/log_{date_time}.json on exit
|
||||
# and every log_interval seconds in between.
|
||||
server_log_task = ServerLogTask(tc, heater, heater_task, DT_TASK, args.logdir, config=config, dt=DT, log_interval=log_interval)
|
||||
server_log_task = ServerLogTask(tc, heater, heater_task, DT_TASK, args.logdir, config=config, dt=DT, log_interval=log_interval, sim_warp_factor=sim_warp_factor)
|
||||
if startup_params is not None:
|
||||
server_log_task.log_plant_params(0, startup_params)
|
||||
taskmgr.add(server_log_task)
|
||||
@@ -149,12 +154,13 @@ if __name__ == '__main__':
|
||||
# Per-run log - only records while a Sud is actually playing; written to
|
||||
# logs/log_{date_time}_{sud_name}.log on Stop (or natural completion) and
|
||||
# every log_interval seconds in between.
|
||||
sud_log_task = SudLogTask(tc, heater, heater_task, sud, DT_TASK, args.logdir, config=config, dt=DT, log_interval=log_interval)
|
||||
sud_log_task = SudLogTask(tc, heater, heater_task, sud, DT_TASK, args.logdir, config=config, dt=DT, log_interval=log_interval, sim_warp_factor=sim_warp_factor)
|
||||
taskmgr.add(sud_log_task)
|
||||
|
||||
sud_task.set_on_plant_params(lambda elapsed, params: (
|
||||
server_log_task.log_plant_params(elapsed, params),
|
||||
sud_log_task.log_plant_params(elapsed, params)))
|
||||
sud_task.set_on_reanchor(sud_log_task.log_reanchor)
|
||||
|
||||
# Assign data flow
|
||||
# Assign tc control value to heater
|
||||
|
||||
Reference in New Issue
Block a user