Files
aes67-ESP32-P4/components/aes67_ptp/aes67_ptp.c
T
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

73 lines
2.1 KiB
C

#include "aes67_ptp.h"
#include "aes67_cfg.h"
#include "aes67_web.h"
#include "ptp_clock.h"
#include "ptp_hw.h"
// Core defaults: Riedel SIC intervals, role auto with fallback priorities (docs/aes67-core-base.md).
static const char PTP_DEFAULTS[] =
"{\"mode\":\"multicast\",\"role\":\"auto\",\"domain\":0,\"priority1\":250,\"priority2\":250,"
"\"log_sync\":0,\"log_announce\":1,\"announce_timeout\":3,\"log_delay_req\":0,"
"\"dscp\":46,\"hw_ts\":true}";
static bool ptp_validate(const cJSON *g, char *err, size_t n)
{
static const char *const modes[] = { "multicast", "hybrid", NULL };
static const char *const roles[] = { "slave", "auto", "master", NULL };
return cfg_check_enum(g, "mode", modes, err, n) &&
cfg_check_enum(g, "role", roles, err, n) &&
cfg_check_int(g, "domain", 0, 127, err, n) &&
cfg_check_int(g, "priority1", 0, 255, err, n) &&
cfg_check_int(g, "priority2", 0, 255, err, n) &&
cfg_check_int(g, "log_sync", -4, 1, err, n) &&
cfg_check_int(g, "log_announce", 0, 4, err, n) &&
cfg_check_int(g, "announce_timeout", 2, 10, err, n) &&
cfg_check_int(g, "log_delay_req", -4, 1, err, n) &&
cfg_check_int(g, "dscp", 0, 63, err, n);
}
static void ptp_apply(const cJSON *g)
{
ptp_clock_reconfig();
}
esp_err_t aes67_ptp_init(void)
{
return cfg_register("ptp", PTP_DEFAULTS, ptp_validate, ptp_apply);
}
esp_err_t aes67_ptp_start(esp_eth_handle_t eth)
{
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("ETH_DEF");
esp_err_t err = ptp_hw_init(eth, netif);
if (err != ESP_OK) {
return err;
}
err = ptp_clock_start(netif);
if (err == ESP_OK) {
err = status_register(ptp_clock_status);
}
return err;
}
bool aes67_ptp_gm_id(char out[24])
{
return ptp_clock_gm_id(out);
}
bool aes67_ptp_locked(void)
{
return ptp_clock_locked();
}
esp_err_t aes67_ptp_now_ns(int64_t *ns)
{
eth_mac_time_t t;
esp_err_t err = ptp_hw_get_time(&t);
if (err == ESP_OK) {
*ns = (int64_t)t.seconds * 1000000000LL + t.nanoseconds;
}
return err;
}