3#ifndef _TIME_SHIELD_NTP_PACKET_HPP_INCLUDED
4#define _TIME_SHIELD_NTP_PACKET_HPP_INCLUDED
9#if TIME_SHIELD_PLATFORM_WINDOWS
12# include <arpa/inet.h>
38 static_assert(
sizeof(
NtpPacket) == 48,
"NtpPacket must be 48 bytes");
53 static inline uint8_t
ntp_li(uint8_t li_vn_mode)
noexcept {
54 return static_cast<uint8_t
>((li_vn_mode >> 6) & 0x03);
58 static inline uint8_t
ntp_vn(uint8_t li_vn_mode)
noexcept {
59 return static_cast<uint8_t
>((li_vn_mode >> 3) & 0x07);
63 static inline uint8_t
ntp_mode(uint8_t li_vn_mode)
noexcept {
64 return static_cast<uint8_t
>(li_vn_mode & 0x07);
69 const uint64_t frac =
static_cast<uint64_t
>(ntohl(frac_net));
70 return (frac * 1000000ULL) >> 32;
74 static inline bool ntp_ts_to_unix_us(uint32_t sec_net, uint32_t frac_net, uint64_t& out_us)
noexcept {
75 static const int64_t NTP_TIMESTAMP_DELTA = 2208988800ll;
76 const int64_t sec =
static_cast<int64_t
>(ntohl(sec_net)) - NTP_TIMESTAMP_DELTA;
77 if (sec < 0)
return false;
78 out_us =
static_cast<uint64_t
>(sec) * 1000000ULL +
ntp_frac_to_us(frac_net);
84 std::memset(&pkt, 0,
sizeof(pkt));
85 pkt.
li_vn_mode =
static_cast<uint8_t
>((0 << 6) | (3 << 3) | 3);
87 const uint64_t sec = now_us / 1000000 + 2208988800ULL;
88 const uint64_t frac = ((now_us % 1000000) * 0x100000000ULL) / 1000000;
90 pkt.
tx_ts_sec = htonl(
static_cast<uint32_t
>(sec));
91 pkt.
tx_ts_frac = htonl(
static_cast<uint32_t
>(frac));
100 int& out_error_code)
noexcept {
101 const uint8_t li =
ntp_li(pkt.li_vn_mode);
102 const uint8_t vn =
ntp_vn(pkt.li_vn_mode);
103 const uint8_t mode =
ntp_mode(pkt.li_vn_mode);
109 if (vn < 3 || vn > 4) {
117 if (pkt.stratum == 0) {
121 if (pkt.stratum >= 16) {
126 uint64_t originate_us = 0;
127 uint64_t receive_us = 0;
128 uint64_t transmit_us = 0;
143 const int64_t t1 =
static_cast<int64_t
>(originate_us);
144 const int64_t t2 =
static_cast<int64_t
>(receive_us);
145 const int64_t t3 =
static_cast<int64_t
>(transmit_us);
146 const int64_t t4 =
static_cast<int64_t
>(arrival_us);
153 offset_us = ((t2 - t1) + (t3 - t4)) / 2;
154 delay_us = (t4 - t1) - (t3 - t2);
159 stratum = pkt.stratum;
NtpProtoError
Protocol-level error codes for NTP parsing.
static uint64_t ntp_frac_to_us(uint32_t frac_net) noexcept
Convert NTP fractional seconds to microseconds.
static uint8_t ntp_li(uint8_t li_vn_mode) noexcept
Extract leap indicator from LI/VN/Mode field.
static uint8_t ntp_mode(uint8_t li_vn_mode) noexcept
Extract mode from LI/VN/Mode field.
static uint8_t ntp_vn(uint8_t li_vn_mode) noexcept
Extract version number from LI/VN/Mode field.
static bool ntp_ts_to_unix_us(uint32_t sec_net, uint32_t frac_net, uint64_t &out_us) noexcept
Convert NTP timestamp parts to Unix microseconds.
static bool parse_server_packet(const NtpPacket &pkt, uint64_t arrival_us, int64_t &offset_us, int64_t &delay_us, int &stratum, int &out_error_code) noexcept
Parse server response and compute offset and delay.
static void fill_client_packet(NtpPacket &pkt, uint64_t now_us)
Fill an NTP client request packet using local time.
Main namespace for the Time Shield library.
NTP packet layout (48 bytes).