Skip to content

Self-learning holdover temperature compensation#9

Draft
peterlewis wants to merge 2 commits into
mitxela:masterfrom
peterlewis:tempcomp
Draft

Self-learning holdover temperature compensation#9
peterlewis wants to merge 2 commits into
mitxela:masterfrom
peterlewis:tempcomp

Conversation

@peterlewis

Copy link
Copy Markdown

Draft — self-learning holdover temperature compensation, built on the $PMTXTS telemetry from #5.

Stacking note (please read first)

This depends on #5 ($PMTXTS) — it consumes the per-PPS pps_cap snapshot and die-temperature it adds. Until #5 lands, this PR's diff includes #5's commit too; the temp-comp change is the single top commit, 55d0695 (the one to review). I'll rebase to just that once #5 merges. Note #5's own text says "no compensation shipped" — this is the deliberate follow-up.

What it does

While GPS-locked the clock learns each oscillator's frequency error vs die temperature (binned, weighted least-squares). During a GPS-loss holdover it then steers the SysTick display timebase from the HSE model to cancel temperature-driven drift (tc_apply), and optionally trims RTC->CALR from the LSE model so the battery RTC hands over better time across a power loss (tc_rtc). All enables default off — stock behaviour unchanged.

Design notes

  • No maths in the ISR. All fitting/evaluation is main-loop (the calibrateRTC float precedent); the tick ISR does only an integer Bresenham SysTick->LOAD stretch — a fractional-ppm rate steer, no phase steps.
  • Origin-free HSE model. Each PPS re-zeroes the ms cascade, so a capture is a self-contained one-second accumulation with an arbitrary standing offset. The model is learned in ticks with that origin; steering applies only model(T_now) − model(T_at_loss), so the origin cancels and the per-edge phase snap makes re-lock instantly neutral. (It corrects temperature-driven drift; the absolute TCXO offset at loss is its spec — re-lock anchoring is a possible follow-up.)
  • Ticks-per-ppm derived at runtime from the captured SysTick period (no core-clock constant baked in).
  • tc_dump = on over serial prints the learned coefficients as paste-ready config.txt lines (+ a $PMTXTC sentence); pasting them back freezes the model. Serial-only, guarded against config-file reload.
  • MODE_TEMPCOMP diagnostic display pages die temp / model offset / sample count.
  • CALR trims happen only in holdover (never contends with calibrateRTC); the WPR sequence runs IRQ-off against PendSV's write_rtc.

Status

Hardware-validated on an Mk IV — the LSE model learned 18.68 ppm vs 18.9 ppm measured directly (~1%). Builds clean; passed an adversarial review pass (ISR-safety, integer math, sign conventions, re-lock neutrality, off-means-identical). Draft pending broader holdover-steering validation.

peterlewis and others added 2 commits July 4, 2026 09:57
With `pps = on` in config.txt, emit one proprietary NMEA sentence per PPS edge
over the existing CDC stream, carrying the sub-second phase captured at the edge
plus calibration / holdover / die-temperature telemetry. Time-critical fields are
snapshotted in the PPS ISR; formatting and CDC submission happen in the main loop,
serialised against the NMEA passthrough. With no enumerated host the record is
dropped before any formatting.

mk4-time/Core/Src/main.c: capturePPS(), emitPPSTimestamp(), measure_temp(), a
`pps` config key and one main-loop hook. qspi/config.txt documents the key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds on the $PMTXTS telemetry: while GPS-locked the clock learns each
oscillator's frequency error vs die temperature (binned, per-oscillator), then
during a GPS-loss holdover it steers the SysTick timebase from the HSE model to
cancel temperature-driven drift, and optionally trims RTC->CALR from the LSE
model so the battery RTC hands over better time across a power loss.

- tc_learn / tc_apply / tc_rtc config keys (all default off = stock behaviour)
- steering is a Bresenham SysTick->LOAD stretch inside the tick ISR (integer
  only; all model maths is main-loop); origin-free HSE model so only the
  temperature DIFFERENCE since GPS loss is applied, and the per-edge PPS phase
  snap makes re-lock instantly neutral
- 'tc_dump = on' over serial prints the learned coefficients as paste-ready
  config lines (+ a $PMTXTC sentence); pasting them back freezes the model
- MODE_TEMPCOMP diagnostic display: die temp / model offset / sample count
- ticks-per-ppm derived from the runtime SysTick period (no core-clock assumption)

Hardware-validated on an Mk IV: the LSE model learned 18.68 ppm vs 18.9 ppm
measured directly (~1%). Survived an adversarial review pass (ISR-safety,
integer math, sign conventions, re-lock neutrality, off-means-identical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant