diff --git a/src/lib/table-model.test.ts b/src/lib/table-model.test.ts index c13c6b7..e6d8012 100644 --- a/src/lib/table-model.test.ts +++ b/src/lib/table-model.test.ts @@ -115,7 +115,6 @@ describe("buildTable", () => { expect(placement("pos_error_counts")).toEqual(["Settings", "Safety"]); expect(placement("sensor_bad_count")).toEqual(["Settings", "Safety"]); expect(placement("cutoff_cc")).toEqual(["Settings", "Thermal"]); - expect(placement("rtherm_i_min_counts")).toEqual(["Settings", "Thermal"]); }); test("id and baud are read-only with links; the deadline is editable", () => { @@ -147,6 +146,7 @@ describe("buildTable", () => { expect(placement("shunt_r_mohm")).toEqual(["Board", "Board constants"]); expect(placement("v_window_min_ticks")).toEqual(["Board", "Board constants"]); expect(placement("ntc_beta")).toEqual(["Board", "Board constants"]); + expect(placement("th_rise_q016")).toEqual(["Board", "Board constants"]); expect(placement("vmotor_bias_nom_counts")).toEqual(["Board", "Board constants"]); expect(rowOf("shunt_r_mohm").editable).toBe(false); expect(rowOf("vdd_mv").editable).toBe(true); diff --git a/src/lib/table-model.ts b/src/lib/table-model.ts index f77e57f..ae67944 100644 --- a/src/lib/table-model.ts +++ b/src/lib/table-model.ts @@ -74,7 +74,6 @@ const LOOP_VELOCITY = 0x038; const LOOP_POSITION = 0x040; const LIMITS = 0x048; const THERMAL = 0x060; -const THERMAL_EXT = 0x06a; const FUSION = 0x06e; const FAULT_CFG = 0x074; const MOTOR = 0x080; @@ -119,7 +118,7 @@ const TABS: readonly TabSpec[] = [ }, // Fault thresholds are the other half of the stall and trip policy. { label: "Safety", help: true, blocks: [LIMITS, FAULT_CFG] }, - { label: "Thermal", help: true, collapsed: true, blocks: [THERMAL, THERMAL_EXT] }, + { label: "Thermal", help: true, collapsed: true, blocks: [THERMAL] }, ], }, { diff --git a/src/lib/thermal.test.ts b/src/lib/thermal.test.ts index c83c89f..edf0d60 100644 --- a/src/lib/thermal.test.ts +++ b/src/lib/thermal.test.ts @@ -41,13 +41,12 @@ describe("therm_flags", () => { [0, []], [1 << 0, ["off"]], [1 << 1, []], - [1 << 2, ["check cold R"]], + [1 << 2, []], [1 << 3, []], [1 << 4, []], [1 << 5, []], [1 << 6, []], [1 << 7, []], - [(1 << 2) | (1 << 3), ["check cold R"]], [0xff, ["off"]], ])("flags %i states %j", (flags, words) => { expect(thermStates({ ...warm, flags }).map((s) => s.word)).toEqual(words); @@ -65,10 +64,7 @@ describe("therm_flags", () => { expect(thermStates({ ...hot, iLimCounts: 280 })).toEqual([]); expect(thermStates({ ...hot, windingCc: 8000 })).toEqual([]); expect(thermStates({ ...warm, iLimCounts: 60 })).toEqual([]); - expect(thermStates({ ...hot, flags: 1 << 2 }).map((s) => s.word)).toEqual([ - "derating", - "check cold R", - ]); + expect(thermStates({ ...hot, flags: 0xfe }).map((s) => s.word)).toEqual(["derating"]); }); }); diff --git a/src/lib/thermal.ts b/src/lib/thermal.ts index cb97c0a..33cf13f 100644 --- a/src/lib/thermal.ts +++ b/src/lib/thermal.ts @@ -1,18 +1,14 @@ -// The winding thermometer's telemetry (firmware core estimator/thermal.rs) +// The winding estimate's telemetry (firmware core estimator/thermal.rs) // and the CONFIG thresholds the current limit derates against // (kernel/limits.rs). import type { ReadRegister } from "./units"; -/** `t_winding_cc` while the thermometer is off (thermal.rs `UNSET_CC`). */ +/** `t_winding_cc` while the estimate is unset (thermal.rs `UNSET_CC`). */ export const UNSET_CC = -32768; -/** - * `therm_flags` bits (thermal.rs `flag`); bit 1, TRACK (a same-seat reference - * is live), names no state, and bits 3-7 are reserved. - */ +/** `therm_flags` bit 0 (thermal.rs `flag`); bits 1-7 are reserved. */ const UNSET = 1 << 0; -const COLD_RECAL = 1 << 2; export const THERMAL_REGISTERS: readonly string[] = [ "current_limit_counts", @@ -53,7 +49,7 @@ export function celsius(cc: number): number { return cc / 100; } -/** The winding in C, NaN while the thermometer reads its sentinel. */ +/** The winding in C, NaN while the estimate reads its sentinel. */ export function windingC(cc: number): number { return cc === UNSET_CC ? NaN : celsius(cc); } @@ -70,7 +66,7 @@ export function thermOff(t: Thermal): boolean { } /** - * Off stands alone: an unset thermometer clears every other bit. Derating is + * Off stands alone: an unset estimate clears every other bit. Derating is * the winding past derate_start with the limit actually folded under the * configured one, so a stall yield alone never reads as heat. */ @@ -80,6 +76,5 @@ export function thermStates(t: Thermal): ThermState[] { if (t.windingCc > t.derateStartCc && t.iLimCounts < t.currentLimitCounts) { out.push({ word: "derating", level: "warn" }); } - if ((t.flags & COLD_RECAL) !== 0) out.push({ word: "check cold R", level: "notice" }); return out; } diff --git a/src/lib/units.test.ts b/src/lib/units.test.ts index df5a618..b394e48 100644 --- a/src/lib/units.test.ts +++ b/src/lib/units.test.ts @@ -153,8 +153,8 @@ test("degenerate calibration gives zero scale instead of infinity", () => { expect(degToCounts(10, flat)).toBe(flat.rawMin); }); -// No firmware math to mirror: t_winding_cc comes from the winding-resistance -// thermometer, not from ntc_raw, so the divider plus beta lives only here. +// No firmware math to mirror: the firmware reads ntc_raw through the +// host-written quadratic in CALIB, so the divider plus beta lives only here. test("temperatureC reads 25 C when the NTC equals its R25", () => { expect(temperatureC(ADC_FULL_SCALE / 2, sense)).toBeCloseTo(25, 9); }); diff --git a/src/routes/table.tsx b/src/routes/table.tsx index 71db4f8..36d65c3 100644 --- a/src/routes/table.tsx +++ b/src/routes/table.tsx @@ -51,7 +51,7 @@ const HELP: ReadonlyMap = new Map([ "Gains of the current, velocity and position loops and the observer behind them.", ], ["Safety", "Trip thresholds and what the servo does when one is crossed."], - ["Thermal", "The winding temperature model and its cutoff."], + ["Thermal", "Derate and cutoff on the winding estimate."], ["Motor model", "Electrical and mechanical constants of the motor the estimators rely on."], ["Profile/capture", "Read profiles and the telemetry burst window."], ]); diff --git a/tests/e2e/winding.spec.ts b/tests/e2e/winding.spec.ts index bd2f8be..f6131c1 100644 --- a/tests/e2e/winding.spec.ts +++ b/tests/e2e/winding.spec.ts @@ -1,8 +1,8 @@ import { expect, test, type Page } from "@playwright/test"; import { gotoSim, openTable, tableRow } from "./helpers"; -// The fake servo has no kernel: its thermometer registers hold what the -// table boots with, and CONFIG carries the firmware's default thresholds. +// The fake servo publishes the winding estimate as the kernel would, and +// CONFIG carries the firmware's default thresholds. const THRESHOLDS = "Derate at 80.0 C, cutoff at 100.0 C, recover at 90.0 C"; function winding(page: Page) {