Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions docs/architecture/ARSTACK_PTP_SV_ALWAYS_ON_CLOCK_M3_20261023.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ARStack61850 — Always-On PTP Clock & SV Sample Grid (M3 architecture contract)

**Status:** DESIGN ONLY. Baseline: checkpoint merge `e82040716c549af887579b369965ba96a890a4db` (PR #288); exact physical-PCAP firmware source `7c95f70cced4d126`. Do not change embedded SV wire encoder, 4I4V measurements, PTP SOURCE framing, or release state until gated test passes. Track implementation in #162/#154; coordinate with existing Draft #166. No main merge/public stable.

## Observed fault and why current approach cannot meet the user's requirement

2026-10-23 physical capture: 490881 SV + 375 PTP packets at 4kfps; one ~2.3599-s pause between SV packets during STOP/START. PTP Announce/Sync/Follow_Up continue, but `smpSynch` goes 2→0 and requalifies ~5.25 s after injection restarts. The requested operation is NOT PTP restart on SV STOP; it is a persistent time domain and phase-aligned sample grid for the device's full uptime.

Current `app_main.cpp`:
- `start_sample_clock()` allocates GPTimer and resets `g_sample_clock_context`, `phase_index`, `epoch_generation` and `armed_ptp_clock`.
- `stop_sample_clock()` **always** calls `revoke_sv_auto_claims(SvSyncLossReason::publisher_stop)`, stops/deletes GPTimer and zeros phase authority even if PTP SOURCE remains stable.
- In `publisher_task()`, STOP can lead to dry preflight recreation. START reuses it only if `SvAutoPreflightPolicy::can_reuse(...)` passes; otherwise clock restarts. PTP SOURCE is independent in `ptp_lab_task.cpp`, but the SV phase bridge is not.
- SV emission state is controlled by `live_control.cpp` `g_running`; loss of the 6-s Studio heartbeat stops SV, which must **not** stop PTP authority or sample clock.

## Proposed ownership — three strictly separated lifecycles

1. **ClockAuthorityService** (free-running since boot, owns EMAC PTP clock): starts independently of SV and remains active through SV STOP, host reconnect, capture, and CFG live edits. Provides internal lab GM and external receiver paths. Serves immutable `ClockSnapshot{role, state, selectedGM, generation, epoch, offset, uncertainty, age, link}`; no SV publisher ownership.
2. **SampleGridService** (independent 4000/4800-Hz GPTimer ISR + low-rate phase servo): once PTP clock is ready, keeps its logical `(ptpSecond, sampleIndex, smpCnt, phaseState)` advancing even while SV packets are suppressed. No ISR allocations, network calls, PTP I/O, blocking mutex, UART or log output; at 4000 Hz only bounded O(1) operations and RTOS notifications while transmitting.
3. **SvPublisher**: START/STOP changes **only** `svTxEnabled` and session/transport lease. START builds and commits canonical 4I4V profile then enables sending on a safe sample boundary, ideally next `smpCnt=0`. STOP suppresses packets, deasserts output lease, and never deletes/zeros ClockAuthority or SampleGrid. Atomic live value updates do not affect clock phase. Publisher faults do not revoke a healthy clock; they revoke *publisher qualification* separately.

Keep *clock synchronization state* and *SV subscription/injection state* orthogonal. There is no `smpSynch` field while SV is not transmitting; first resumed frame reflects qualified clock+grid evidence. Never falsely declare lock when source or phase is actually stale.

## Advanced policy — configurable, non-surprising

`TimeSourcePolicy`: **INTERNAL_GM_ONLY**, **EXTERNAL_ONLY**, **AUTO_PREFER_EXTERNAL**, **MONITOR_ONLY** (plus explicit OFF if needed).
- INTERNAL_GM_ONLY: boot EMAC PTP local source, reply Pdelay requests, maintain sample grid and simulator settings even when SV TX is off.
- EXTERNAL_ONLY: listen to external Announce/Sync/Follow_Up and peer delay; select compatible GM, discipline EMAC; if absent show WAITING/UNQUALIFIED, never silently become GM.
- AUTO_PREFER_EXTERNAL: initialize local PTP/grid immediately while independently discovering external candidates; only promote a compatible, qualified, stable GM after BMCA/profile checks and hysteresis. Switch local SOURCE off before external RECEIVER becomes timing authority on the same PTP domain; no simultaneous conflicting GM announcements. After GM loss use bounded HOLDOVER with uncertainty budget; optional local fallback *only if explicitly enabled*, with safe network election, source-generation changes, and requalification.
- MONITOR_ONLY: observe but neither inject SV nor act as GM (when selected).
- Expose domain, VLAN/PCP, transportSpecific, Announce/Sync cadence, Pdelay, source policy, holdover window, failover hysteresis, acceptance mode, selected GM and clock/phase status in Advanced UI. Persist policy, not unverified LOCKED state.

Important: `AUTO` cannot guarantee uninterrupted qualified phase through an arbitrary external GM time-step/change/link loss. Preserve clock continuity where physically possible, otherwise transient UNQUALIFIED → requalification; **SV STOP/START alone must never trigger this**. Do not conflflate free-running local GM, external UTC traceability, or relay PTP-slave LOCK.

## Implementation sequence (no broad rewrite)

**M3.A (P0): remove STOP coupling only.** Split `stop_sample_clock()` into `stop_sv_output()` and `shutdown_sample_grid(reason)`; make STOP/lease-expiry/START no longer revoke healthy PTP sync or delete GPTimer. Preserve logical grid while TX disabled; verified START launches output on aligned epoch. Retain source generation and phase-authority whenever link, rate/profile and clock-source generation unchanged. Validate without changing SV BER/codec or PTP packet framing.

**M3.B (P0): background supervisor.** Retarget existing `SvAutoPreflightPolicy`, phase observer and timer ownership to uptime service; make lifecycle transitions pure and testable. Publish `ClockState` and `SampleGridState` independently, retain stale-generation guards. Only real clock/GM/profile incompatibility re-arms phase.

**M3.C (P1): external receiver and BMCA.** Integrate existing `ptp_receiver_task.cpp` from #166 only after code/protocol audit and same checkpoint merge-base reconciliation. Implement announce candidate aging, proper master selection, hardware peer delay and offset discipline; add stable hysteresis/holdover/fallback. Default to no unexpected GM promotion on an energized process bus.

**M3.D (P1): Advanced settings and diagnostics.** UI policy dropdown, status tags LOCAL_GM_READY / EXTERNAL_LOCKED / HOLDOVER / WAITING / FAULT, source generation, last source change, phase uncertainty, clock age. START/STOP remains only SV TX.

## Non-regression FAT gates (must all pass to merge into checkpoint)

1. **100 STOP/START cycles**, including USB reconnect and host control-lease expiry: PTP Announce/Sync/Follow_Up continue without resetting clock generation, no SV while STOPPED, first SV resumed at defined epoch; if phase is qualified before STOP and environment unchanged, no 2→0→2 reset solely from STOP.
2. **120 s and 3600 s PCAP**, 4000 fps 4I4V, no invalid BER, duplicate/missing `smpCnt` during continuous transmission, no PTP cycles missing. Exclude operator-commanded TX OFF from packet-loss metric, but retain gap event.
3. Enter/focus-out IA and UA automatically apply on wire while SV/PTP continuously running, no restart/reset (retain PR #288 GUI+capture tests).
4. LOCAL_ONLY, EXTERNAL_ONLY, AUTO candidate-appears/disappears, stable-GM switch and HOLDOVER fault injection. Verify exact PTP frames and source identity, not only UI status; test 4k and 4.8k grids and long timer rollover.
5. ARSAS + DigSubAnalyzer decode and maintain sync; SIPROTEC 5 selected-GM/PTP LOCKED + actual 4I4V readings are final physical acceptance. CI PASS alone is never release PASS.

## Integration discipline

Branch from checkpoint `e820407...`; keep Draft #166 as reference, not blind cherry-pick. Separate PRs for M3.A, B, C, D with portable unit tests before ESP32 build, physically test M3.A before advanced external-source changes. Do not merge to main or publish stable until independent FAT.
Loading