17static void put_u16(uint8_t *p, uint16_t v)
19 p[0] = (uint8_t)(v >> 8);
23static uint16_t get_u16(
const uint8_t *p)
25 return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
28static void put_u32(uint8_t *p, uint32_t v)
30 p[0] = (uint8_t)(v >> 24);
31 p[1] = (uint8_t)(v >> 16);
32 p[2] = (uint8_t)(v >> 8);
36static uint32_t get_u32(
const uint8_t *p)
38 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
41static void put_u64(uint8_t *p, uint64_t v)
43 for (
int i = 0; i < 8; i++)
45 p[i] = (uint8_t)(v >> (8 * (7 - i)));
49static uint64_t get_u64(
const uint8_t *p)
52 for (
int i = 0; i < 8; i++)
61void pc_ptp_ts_write(uint8_t *p,
const pc_ptp_timestamp *ts)
63 uint64_t s = ts->seconds;
64 p[0] = (uint8_t)(s >> 40);
65 p[1] = (uint8_t)(s >> 32);
66 p[2] = (uint8_t)(s >> 24);
67 p[3] = (uint8_t)(s >> 16);
68 p[4] = (uint8_t)(s >> 8);
70 put_u32(p + 6, ts->nanoseconds);
73void pc_ptp_ts_read(
const uint8_t *p, pc_ptp_timestamp *ts)
75 ts->seconds = ((uint64_t)p[0] << 40) | ((uint64_t)p[1] << 32) | ((uint64_t)p[2] << 24) | ((uint64_t)p[3] << 16) |
76 ((uint64_t)p[4] << 8) | (uint64_t)p[5];
77 ts->nanoseconds = get_u32(p + 6);
80int64_t pc_ptp_ts_to_ns(
const pc_ptp_timestamp *ts)
82 return (int64_t)ts->seconds * 1000000000LL + (int64_t)ts->nanoseconds;
85void pc_ptp_ts_from_ns(int64_t ns, pc_ptp_timestamp *ts)
91 ts->seconds = (uint64_t)(ns / 1000000000LL);
92 ts->nanoseconds = (uint32_t)(ns % 1000000000LL);
97size_t pc_ptp_build_header(uint8_t *buf,
size_t cap,
const pc_ptp_header *h, uint16_t body_len)
99 if (!buf || !h || cap < PC_PTP_HEADER_LEN)
103 memset(buf, 0, PC_PTP_HEADER_LEN);
104 buf[0] = (uint8_t)((h->transport_specific << 4) | (h->message_type & 0x0F));
105 buf[1] = (uint8_t)(h->version & 0x0F);
106 put_u16(buf + 2, (uint16_t)(PC_PTP_HEADER_LEN + body_len));
108 put_u16(buf + 6, h->flags);
109 put_u64(buf + 8, (uint64_t)h->correction);
110 memcpy(buf + 20, h->clock_identity, 8);
111 put_u16(buf + 28, h->port_number);
112 put_u16(buf + 30, h->sequence_id);
113 buf[32] = h->control;
114 buf[33] = (uint8_t)h->log_interval;
115 return PC_PTP_HEADER_LEN;
118bool pc_ptp_parse_header(
const uint8_t *s,
size_t len, pc_ptp_header *h)
120 if (!s || !h || len < PC_PTP_HEADER_LEN)
124 h->message_type = (uint8_t)(s[0] & 0x0F);
125 h->transport_specific = (uint8_t)(s[0] >> 4);
126 h->version = (uint8_t)(s[1] & 0x0F);
127 h->message_length = get_u16(s + 2);
129 h->flags = get_u16(s + 6);
130 h->correction = (int64_t)get_u64(s + 8);
131 memcpy(h->clock_identity, s + 20, 8);
132 h->port_number = get_u16(s + 28);
133 h->sequence_id = get_u16(s + 30);
135 h->log_interval = (int8_t)s[33];
141static size_t build_ts_msg(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *ts, uint8_t mtype,
144 if (!buf || !h || !ts || cap < PC_PTP_HEADER_LEN + PC_PTP_TS_LEN)
148 pc_ptp_header hh = *h;
149 hh.message_type = mtype;
150 hh.control = control;
155 pc_ptp_build_header(buf, cap, &hh, PC_PTP_TS_LEN);
156 pc_ptp_ts_write(buf + PC_PTP_HEADER_LEN, ts);
157 return PC_PTP_HEADER_LEN + PC_PTP_TS_LEN;
160size_t pc_ptp_build_sync(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *origin)
162 return build_ts_msg(buf, cap, h, origin, PC_PTP_SYNC, 0x00);
165size_t pc_ptp_build_delay_req(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *origin)
167 return build_ts_msg(buf, cap, h, origin, PC_PTP_DELAY_REQ, 0x01);
170size_t pc_ptp_build_follow_up(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *precise)
172 return build_ts_msg(buf, cap, h, precise, PC_PTP_FOLLOW_UP, 0x02);
175size_t pc_ptp_build_delay_resp(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *recv,
176 const uint8_t *req_clock_id, uint16_t req_port)
178 const size_t body = PC_PTP_TS_LEN + 10;
179 if (!buf || !h || !recv || !req_clock_id || cap < PC_PTP_HEADER_LEN + body)
183 pc_ptp_header hh = *h;
184 hh.message_type = PC_PTP_DELAY_RESP;
190 pc_ptp_build_header(buf, cap, &hh, (uint16_t)body);
191 uint8_t *p = buf + PC_PTP_HEADER_LEN;
192 pc_ptp_ts_write(p, recv);
194 memcpy(p, req_clock_id, 8);
196 put_u16(p, req_port);
197 return PC_PTP_HEADER_LEN + body;
200size_t pc_ptp_build_pdelay_req(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *origin)
202 const size_t body = PC_PTP_TS_LEN + 10;
203 if (!buf || !h || !origin || cap < PC_PTP_HEADER_LEN + body)
207 pc_ptp_header hh = *h;
208 hh.message_type = PC_PTP_PDELAY_REQ;
214 pc_ptp_build_header(buf, cap, &hh, (uint16_t)body);
215 uint8_t *p = buf + PC_PTP_HEADER_LEN;
216 pc_ptp_ts_write(p, origin);
217 memset(p + PC_PTP_TS_LEN, 0, 10);
218 return PC_PTP_HEADER_LEN + body;
223static size_t build_pdelay_resp_msg(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *ts,
224 const uint8_t *req_clock_id, uint16_t req_port, uint8_t mtype)
226 const size_t body = PC_PTP_TS_LEN + 10;
227 if (!buf || !h || !ts || !req_clock_id || cap < PC_PTP_HEADER_LEN + body)
231 pc_ptp_header hh = *h;
232 hh.message_type = mtype;
238 pc_ptp_build_header(buf, cap, &hh, (uint16_t)body);
239 uint8_t *p = buf + PC_PTP_HEADER_LEN;
240 pc_ptp_ts_write(p, ts);
242 memcpy(p, req_clock_id, 8);
244 put_u16(p, req_port);
245 return PC_PTP_HEADER_LEN + body;
248size_t pc_ptp_build_pdelay_resp(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_timestamp *recv,
249 const uint8_t *req_clock_id, uint16_t req_port)
251 return build_pdelay_resp_msg(buf, cap, h, recv, req_clock_id, req_port, PC_PTP_PDELAY_RESP);
254size_t pc_ptp_build_pdelay_resp_follow_up(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
255 const pc_ptp_timestamp *origin,
const uint8_t *req_clock_id,
258 return build_pdelay_resp_msg(buf, cap, h, origin, req_clock_id, req_port, PC_PTP_PDELAY_RESP_FOLLOW_UP);
261size_t pc_ptp_build_announce(uint8_t *buf,
size_t cap,
const pc_ptp_header *h,
const pc_ptp_announce *a)
263 const size_t body = 30;
264 if (!buf || !h || !a || cap < PC_PTP_HEADER_LEN + body)
268 pc_ptp_header hh = *h;
269 hh.message_type = PC_PTP_ANNOUNCE;
275 pc_ptp_build_header(buf, cap, &hh, (uint16_t)body);
276 uint8_t *p = buf + PC_PTP_HEADER_LEN;
277 pc_ptp_ts_write(p, &a->origin);
279 put_u16(p, (uint16_t)a->utc_offset);
282 *p++ = a->gm_priority1;
283 *p++ = a->gm_clock_class;
284 *p++ = a->gm_clock_accuracy;
285 put_u16(p, a->gm_variance);
287 *p++ = a->gm_priority2;
288 memcpy(p, a->gm_identity, 8);
290 put_u16(p, a->steps_removed);
293 return PC_PTP_HEADER_LEN + body;
296bool pc_ptp_parse_timestamp_msg(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_timestamp *ts)
298 if (!ts || !pc_ptp_parse_header(s, len, h))
302 if (len < PC_PTP_HEADER_LEN + PC_PTP_TS_LEN)
306 if (h->message_type != PC_PTP_SYNC && h->message_type != PC_PTP_DELAY_REQ && h->message_type != PC_PTP_FOLLOW_UP)
310 pc_ptp_ts_read(s + PC_PTP_HEADER_LEN, ts);
314bool pc_ptp_parse_delay_resp(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_delay_resp *out)
316 if (!out || !pc_ptp_parse_header(s, len, h))
320 if (h->message_type != PC_PTP_DELAY_RESP)
324 if (len < PC_PTP_HEADER_LEN + PC_PTP_TS_LEN + 10)
328 const uint8_t *p = s + PC_PTP_HEADER_LEN;
329 pc_ptp_ts_read(p, &out->receive);
331 memcpy(out->req_clock_id, p, 8);
333 out->req_port = get_u16(p);
337bool pc_ptp_parse_pdelay_req(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_timestamp *ts)
339 if (!ts || !pc_ptp_parse_header(s, len, h))
343 if (h->message_type != PC_PTP_PDELAY_REQ)
347 if (len < PC_PTP_HEADER_LEN + PC_PTP_TS_LEN)
351 pc_ptp_ts_read(s + PC_PTP_HEADER_LEN, ts);
356static bool parse_pdelay_resp_msg(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_pdelay_resp *out,
359 if (!out || !pc_ptp_parse_header(s, len, h))
363 if (h->message_type != mtype)
367 if (len < PC_PTP_HEADER_LEN + PC_PTP_TS_LEN + 10)
371 const uint8_t *p = s + PC_PTP_HEADER_LEN;
372 pc_ptp_ts_read(p, &out->timestamp);
374 memcpy(out->req_clock_id, p, 8);
376 out->req_port = get_u16(p);
380bool pc_ptp_parse_pdelay_resp(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_pdelay_resp *out)
382 return parse_pdelay_resp_msg(s, len, h, out, PC_PTP_PDELAY_RESP);
385bool pc_ptp_parse_pdelay_resp_follow_up(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_pdelay_resp *out)
387 return parse_pdelay_resp_msg(s, len, h, out, PC_PTP_PDELAY_RESP_FOLLOW_UP);
390bool pc_ptp_parse_announce(
const uint8_t *s,
size_t len, pc_ptp_header *h, pc_ptp_announce *out)
392 if (!out || !pc_ptp_parse_header(s, len, h))
396 if (h->message_type != PC_PTP_ANNOUNCE)
400 if (len < PC_PTP_HEADER_LEN + 30)
404 const uint8_t *p = s + PC_PTP_HEADER_LEN;
405 pc_ptp_ts_read(p, &out->origin);
407 out->utc_offset = (int16_t)get_u16(p);
410 out->gm_priority1 = *p++;
411 out->gm_clock_class = *p++;
412 out->gm_clock_accuracy = *p++;
413 out->gm_variance = get_u16(p);
415 out->gm_priority2 = *p++;
416 memcpy(out->gm_identity, p, 8);
418 out->steps_removed = get_u16(p);
420 out->time_source = *p;
426void pc_ptp_compute(int64_t t1, int64_t t2, int64_t t3, int64_t t4, pc_ptp_sync *out)
432 int64_t ms = t2 - t1;
433 int64_t sm = t4 - t3;
434 out->offset_ns = (ms - sm) / 2;
435 out->delay_ns = (ms + sm) / 2;
438int64_t pc_ptp_compute_link_delay(int64_t t1, int64_t t2, int64_t t3, int64_t t4)
441 return ((t4 - t1) - (t3 - t2)) / 2;
PTP / IEEE 1588-2008 (PTPv2) message codec + slave clock math (PC_ENABLE_PTP).