| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- #pragma once
- #include <stdint.h>
- #include <stdbool.h>
- #include "esp_err.h"
- /**
- * Simple PTP (IEEE 1588) slave for AirPlay time synchronization.
- * Listens for SYNC/FOLLOW_UP messages and tracks offset to PTP master.
- */
- /**
- * Initialize and start PTP clock synchronization.
- * Creates a task that listens for PTP multicast messages.
- */
- esp_err_t ptp_clock_init(void);
- /**
- * Stop PTP clock and free resources.
- */
- void ptp_clock_stop(void);
- /**
- * Clear PTP clock synchronization state.
- * Resets offset and lock status without stopping the clock.
- * Called during TEARDOWN to allow re-sync on new session.
- */
- void ptp_clock_clear(void);
- /**
- * Check if PTP is locked to a master clock.
- * @return true if synchronized with acceptable accuracy
- */
- bool ptp_clock_is_locked(void);
- /**
- * Get current PTP time in nanoseconds.
- * Returns local time adjusted by PTP offset.
- * @return PTP time in nanoseconds since epoch
- */
- uint64_t ptp_clock_get_time_ns(void);
- /**
- * Get current offset from local clock to PTP time in nanoseconds.
- * PTP_time = local_time + offset
- */
- int64_t ptp_clock_get_offset_ns(void);
- /**
- * Notify the PTP clock that playback is resuming after a pause.
- *
- * If the pause lasted longer than PTP_LONG_PAUSE_THRESHOLD_MS, this
- * resets the asymmetric smoothing filter so the next received PTP sample is
- * accepted unconditionally — mirroring the nqptp "B" (begin) signal behaviour
- * described in nqptp-shm-structures.h.
- *
- * Without this, after a pause long enough for the local crystal to drift,
- * the 1/256-negative-jitter damping would take several minutes to re-converge,
- * causing audible multi-room sync loss on resume.
- *
- * @param pause_duration_ms Wall-clock length of the pause in milliseconds.
- */
- void ptp_clock_notify_resume(uint32_t pause_duration_ms);
- /**
- * Get synchronization statistics.
- */
- typedef struct {
- uint32_t sync_count; // Number of SYNC messages received
- uint32_t followup_count; // Number of FOLLOW_UP messages received
- int64_t last_offset_ns; // Last measured offset
- int64_t filtered_offset_ns; // Filtered/averaged offset
- uint32_t lock_time_ms; // Time since lock achieved (0 if not locked)
- } ptp_stats_t;
- void ptp_clock_get_stats(ptp_stats_t *stats);
- /**
- * Restrict the PTP clock to a single master identified by its 8-byte
- * clockIdentity (the value carried in the AirPlay 2 0xD7 anchor packet at
- * offset +20, and in the PTP common-header sourcePortIdentity field at
- * bytes 20-27).
- *
- * Pass 0 to clear the filter (accept any master — the default at startup).
- *
- * When the expected clock_id changes, the filter resets samples and lock
- * state so a stale offset to a previous (possibly wrong) master is not
- * carried over. On a network with multiple PTP-speaking Apple devices
- * (HomePods, AppleTVs, other receivers), this is what prevents us from
- * locking to the wrong master and computing nonsense early/late deltas.
- */
- void ptp_clock_set_master_clock_id(uint64_t clock_id);
- /**
- * Read the current expected master clock_id (0 if none / filter cleared).
- */
- uint64_t ptp_clock_get_master_clock_id(void);
|