Commit Graph

11 Commits

Author SHA1 Message Date
bsncubed ee03d9f9f1 PTP: hardware timestamping is not a setting any more
The hw_ts config key was never read (hardware timestamps are always used);
the checkbox suggested it could be turned off. Removed from the config,
the page and the docs; status.ptp.hw_ts still reports it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 07:55:54 +10:00
bsncubed 5bd1a20e43 Step 6.3: PTP hybrid mode (unicast Delay_Req/Resp), step 6 done
- As TimeReceiver in ptp.mode hybrid: Delay_Req unicast to the GM (IP
  from its Announce, MAC recorded by the RX hook from its Sync frames),
  unicastFlag set.
- As TimeTransmitter: a Delay_Req with the unicastFlag gets a unicast
  Delay_Resp; multicast requests get multicast replies.
- EMAC timestamps every received frame (en_ts4all): its PTP filter left
  unicast Delay_Req unstamped ("no HW RX timestamp").
- A unicast Delay_Resp's logMessageInterval 0x7F is ignored (use
  ptp.log_delay_req); log_us() clamps to -7..6 (the shift was undefined).
- Verified vs ptp4l --hybrid_e2e 1: board hybrid TimeReceiver 18
  Delay_Req in 20 s, all answered, locked; board TimeTransmitter
  (p1 100): tcpdump shows Sync/Follow_Up/Announce to 224.0.1.129,
  Delay_Req 192.168.192.233 -> .244 [unicast] and Delay_Resp .244 ->
  .233 [unicast] within 0.4 ms; ptp4l s2.
- Docs: TimeTransmitter/hybrid notes; step 6 ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 08:21:09 +10:00
bsncubed 08b83a069c Step 6.1: PTP TimeTransmitter with BMCA (multicast)
- Own dataset from config: slave -> class 255, never TimeTransmitter;
  auto/master -> class 248 with ptp.priority1/2; accuracy 0xFE, variance
  0xFFFF, timeSource 0xA0, clockIdentity EUI-64 from MAC.
- BMCA: a foreign TimeTransmitter is followed only if its Announce beats
  our dataset (always for slave-only); if the followed one turns worse we
  take over; a better one makes us step down. LISTENING -> MASTER after
  announceReceiptTimeout x our announce interval.
- MASTER: Announce (PTP timescale flag) every 2^log_announce, two-step
  Sync every 2^log_sync (raw frame, HW TX time in Follow_Up), Delay_Resp
  for each Delay_Req with its HW RX time and logMessageInterval =
  log_delay_req. Frequency correction kept (holdover).
- ptp config applies live (role, priorities, intervals, domain, DSCP).
- status.ptp: state MASTER, gm_id = own, TX Sync/Announce intervals,
  Delay_Req/Resp counters; locked is true as master so AES67 TX keeps
  running; SDP ts-refclk and SAP follow.
- Verified: with ptp4l GM p1 128 the board (auto, p1 250) stays SLAVE;
  ptp4l stopped -> board MASTER; ptp4l -s --priority1 255 locks to it
  (s2) in ~4 s, offset within +-320 ns, path delay 10.3 us.
  Known: Sync send times jitter ~10 ms (FreeRTOS tick); accuracy is not
  affected (two-step Follow_Up carries the exact HW time).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:42:43 +10:00
bsncubed 2522901918 PTP: don't report the pre-step offset
After a clock step, status.ptp showed the whole step (~1.79e18 ns) as
offset_ns until the next Sync, and the 60 s summary reported it as
max |offset|. Offset is reset to 0 and the summary restarts on a step.
Verified: status goes 0 -> pull-in values; first summary max 24960 ns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:08:42 +10:00
bsncubed 3d05768f00 Step 4.1: /stream.sdp from the live config and PTP GM
- aes67_sdp_sap: aes67_sdp_build() builds the AES67 SDP (RFC 4566 +
  RFC 7273) with the same lines and order as the web UI preview;
  GET /stream.sdp serves it as application/sdp.
- aes67_ptp: public aes67_ptp_gm_id(), aes67_ptp_locked(),
  aes67_ptp_now_ns(). Clock IDs are now uppercase (RFC 7273 style), also
  in status.ptp, which the UI copies into ts-refclk.
- aes67_net: aes67_net_netif() returns the AES67 interface.
- Verified on board: /stream.sdp byte-identical to the UI's sdp() for
  the same config/status; mono, L16, ptime 0.333, ttl 8, clk_offset
  4294967295 and session_ver all follow the config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:38:55 +10:00
bsncubed 9af6c53294 Step 3.3b: status.ptp for the web UI
- ptp_clock_status() adds status.ptp: state (LISTENING / UNCALIBRATED /
  SLAVE), locked, gm_id, offset_ns, freq_ppb, path_delay_ns +
  path_delay_sd_ns (window of 64, from lock on), steps_removed,
  gm_time/freq_traceable, version, own_class (255 slave / 248 auto,
  master), clock_id, gm_class/accuracy/p1/p2, sync_avg/min/max_ms and
  sync_jitter_us (HW Sync intervals), announce_avg_ms, delay_req/resp
  counters, hw_ts, window. Snapshot under a mutex shared with the PTP task.
- Per-Sync log moved to debug; info level logs state changes plus a
  60 s summary (max |offset|, freq, delay).
- Verified vs ptp4l: LISTENING -> UNCALIBRATED -> SLAVE in ~35 s;
  locked offset within a few hundred ns, delay 10.27 us +-0.20 us,
  Delay_Req/Resp 69/69, Sync avg 1000.097 ms, Announce avg 2000 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:22:41 +10:00
bsncubed 685060e12c Step 3.3a: PTP servo, clock locks to the GM
- ptp_hw: ptp_hw_adj_freq() (ETH_MAC_ESP_CMD_ADJ_PTP_TIME, absolute ppb
  vs nominal, clamped +-500 ppm, retried while the addend update is
  busy) and ptp_hw_step() (read + write time).
- ptp_clock: first Sync after a GM is selected seeds the integral with
  the measured rate and steps the clock; then linuxptp-style PI (kp/ki
  from the Sync interval: 0.7/0.3 at 1 s). Re-step above 1 ms. Locked
  after 8 Syncs with |offset| < 1 us, unlocked after 3 above; GM change
  or loss unlocks and keeps the frequency (holdover).
- Verified vs ptp4l: stepped to GM time, locked after ~19 s; locked
  offset within +-510 ns (mostly < 300 ns), correction +39.9 ppm
  (crystal -39.8 ppm), path delay ~10.2 us.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:15:26 +10:00
bsncubed e2f4d81326 Step 3.2: TimeReceiver measurement (GM selection, Delay_Req/Resp, path delay)
- ptp_clock.c replaces the 3.1 logger: UDP/IPv4 multicast, E2E.
  Announce: IEEE 1588 dataset comparison picks the best GM, dropped after
  announceReceiptTimeout x its announce interval. Sync/Follow_Up (two-step
  and one-step) with HW t2 and correctionField. Delay_Req sent as a raw
  frame (DSCP ptp.dscp, TTL 1, clockIdentity = EUI-64 from MAC) with HW
  TX timestamp t3, randomised at the GM's Delay_Resp interval; Delay_Resp
  matched on requestingPortIdentity + seq.
- Path delay corrected for offset drift between t2 and t3 (rate from
  consecutive Syncs) so it is right before the clock is syntonised.
- ptp_hw: ptp_hw_send_event() builds Eth/IPv4/UDP 319 and returns the HW
  TX timestamp.
- Verified vs ptp4l (i210 GM, HP 2530 non-PTP switch, PC 1G / board 100M):
  GM selected, path delay settles at ~10.3 us and stays flat, rate
  -39.8 +-0.4 ppm, offset drifts at -40 us/s (no servo yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 23:50:46 +10:00
bsncubed 6090ceb239 Step 3.1: EMAC hardware timestamps for PTP over UDP/IPv4
- aes67_ptp/ptp_hw.c: start the EMAC IEEE 1588 clock (IDF
  ETH_MAC_ESP_CMD_PTP_ENABLE) and additionally enable snapshots for
  PTP over UDP/IPv4 (IDF only enables PTP over Ethernet/L2). An RX hook
  on the driver's info input path records {type, seq, sourcePortId, HW
  timestamp} of port-319 PTPv2 event messages, then hands every frame to
  lwIP unchanged.
- aes67_ptp.c: temporary 3.1 logger joins 224.0.1.129:319/320 and pairs
  Sync HW RX timestamps with Follow_Up origin timestamps.
- Checked against linuxptp ptp4l (-4 -E -H, Intel igb) as GM: every Sync
  has a HW timestamp; HW Sync intervals track the GM intervals with a
  constant -39.8 us/s (+-0.3 us), i.e. local clock -39.8 ppm vs GM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 23:44:11 +10:00
bsncubed c2621f192a Step 2.2: config registry and NVS store, GET/POST /api/config
- aes67_web/cfg.c: cfg_register(group, defaults, validate, apply),
  cfg_override_defaults() for project defaults, cfg_get(). Stored values
  are loaded on register and merged over defaults (known keys, matching
  types). NVS keeps only keys that differ from defaults, one JSON string
  per group, so new firmware defaults still reach untouched settings.
- POST /api/config: type check against defaults, per-group validation,
  nothing stored unless every group passes; 400 "group.key: reason".
  Unknown keys/groups are ignored. Returns the new config.
- Core groups registered by their components with the UI's ranges:
  ptp (aes67_ptp), aes67 (aes67_tx, incl. multicast range and mono_sum
  => 1 channel), net/inet (aes67_net), log (aes67_syslog).
- Project: PROJECT.def overrides (hostname p4-aes67, name P4 AES67) and
  the "source" group in main/project_cfg.c.
- Config is stored only; nothing is applied live yet.
- Verified on board: defaults, valid save, 8 rejected values (nothing
  stored), persistence across reboot, restore to defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 23:00:55 +10:00
bsncubed bbfd3506bf Step 0: IDF project skeleton for esp32p4 with core component stubs
- Board checked with esptool: ESP32-P4 rev v1.3 (engineering silicon),
  32 MB GigaDevice flash. Rev <3 support and 32 MB set in sdkconfig.defaults.
- OTA partition layout: nvs, otadata, phy_init, ota_0/ota_1 (6 MB each,
  below 16 MB), coredump; no factory app. Partition table at 0x10000 so
  the bootloader can grow.
- App rollback enabled in the bootloader from the start.
- PROJECT_VER from git describe --tags --always --dirty.
- Empty stubs for the nine aes67_* core components; main logs version
  and chip revision.
- Built with ESP-IDF v5.5.5; not yet flashed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 22:47:21 +10:00