ptp_clock.h 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #pragma once
  2. #include <stdint.h>
  3. #include <stdbool.h>
  4. #include "esp_err.h"
  5. /**
  6. * Simple PTP (IEEE 1588) slave for AirPlay time synchronization.
  7. * Listens for SYNC/FOLLOW_UP messages and tracks offset to PTP master.
  8. */
  9. /**
  10. * Initialize and start PTP clock synchronization.
  11. * Creates a task that listens for PTP multicast messages.
  12. */
  13. esp_err_t ptp_clock_init(void);
  14. /**
  15. * Stop PTP clock and free resources.
  16. */
  17. void ptp_clock_stop(void);
  18. /**
  19. * Clear PTP clock synchronization state.
  20. * Resets offset and lock status without stopping the clock.
  21. * Called during TEARDOWN to allow re-sync on new session.
  22. */
  23. void ptp_clock_clear(void);
  24. /**
  25. * Check if PTP is locked to a master clock.
  26. * @return true if synchronized with acceptable accuracy
  27. */
  28. bool ptp_clock_is_locked(void);
  29. /**
  30. * Get current PTP time in nanoseconds.
  31. * Returns local time adjusted by PTP offset.
  32. * @return PTP time in nanoseconds since epoch
  33. */
  34. uint64_t ptp_clock_get_time_ns(void);
  35. /**
  36. * Get current offset from local clock to PTP time in nanoseconds.
  37. * PTP_time = local_time + offset
  38. */
  39. int64_t ptp_clock_get_offset_ns(void);
  40. /**
  41. * Notify the PTP clock that playback is resuming after a pause.
  42. *
  43. * If the pause lasted longer than PTP_LONG_PAUSE_THRESHOLD_MS, this
  44. * resets the asymmetric smoothing filter so the next received PTP sample is
  45. * accepted unconditionally — mirroring the nqptp "B" (begin) signal behaviour
  46. * described in nqptp-shm-structures.h.
  47. *
  48. * Without this, after a pause long enough for the local crystal to drift,
  49. * the 1/256-negative-jitter damping would take several minutes to re-converge,
  50. * causing audible multi-room sync loss on resume.
  51. *
  52. * @param pause_duration_ms Wall-clock length of the pause in milliseconds.
  53. */
  54. void ptp_clock_notify_resume(uint32_t pause_duration_ms);
  55. /**
  56. * Get synchronization statistics.
  57. */
  58. typedef struct {
  59. uint32_t sync_count; // Number of SYNC messages received
  60. uint32_t followup_count; // Number of FOLLOW_UP messages received
  61. int64_t last_offset_ns; // Last measured offset
  62. int64_t filtered_offset_ns; // Filtered/averaged offset
  63. uint32_t lock_time_ms; // Time since lock achieved (0 if not locked)
  64. } ptp_stats_t;
  65. void ptp_clock_get_stats(ptp_stats_t *stats);
  66. /**
  67. * Restrict the PTP clock to a single master identified by its 8-byte
  68. * clockIdentity (the value carried in the AirPlay 2 0xD7 anchor packet at
  69. * offset +20, and in the PTP common-header sourcePortIdentity field at
  70. * bytes 20-27).
  71. *
  72. * Pass 0 to clear the filter (accept any master — the default at startup).
  73. *
  74. * When the expected clock_id changes, the filter resets samples and lock
  75. * state so a stale offset to a previous (possibly wrong) master is not
  76. * carried over. On a network with multiple PTP-speaking Apple devices
  77. * (HomePods, AppleTVs, other receivers), this is what prevents us from
  78. * locking to the wrong master and computing nonsense early/late deltas.
  79. */
  80. void ptp_clock_set_master_clock_id(uint64_t clock_id);
  81. /**
  82. * Read the current expected master clock_id (0 if none / filter cleared).
  83. */
  84. uint64_t ptp_clock_get_master_clock_id(void);