SatCat5
ptp_doppler.cc
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/log.h>
7 #include <satcat5/ptp_client.h>
8 #include <satcat5/ptp_doppler.h>
10 #include <satcat5/utils.h>
11 #include <satcat5/wide_integer.h>
12 
15 using satcat5::ptp::ClientMode;
20 using satcat5::ptp::Time;
25 
26 // Enable support for SPTP?
27 #ifndef SATCAT5_SPTP_ENABLE
28 #define SATCAT5_SPTP_ENABLE 2
29 #endif
30 
31 // Timestamp compensation enabled by default?
32 #ifndef SATCAT5_DOPPLER_TCOMP
33 #define SATCAT5_DOPPLER_TCOMP false
34 #endif
35 
36 // Set logging verbosity level (0/1/2).
37 static constexpr unsigned DEBUG_VERBOSE = 1;
38 
39 // DopplerTLV tags are fixed-length, with a 6-byte payload.
40 static constexpr TlvHeader TLVHDR_DOPPLER = {TLVTYPE_DOPPLER, 6, 0, 0};
41 
42 DopplerTlv::DopplerTlv(satcat5::ptp::Client* client)
43  : TlvHandler(client)
44  , m_predict()
45  , m_dstamp(0)
46  , m_tref(SATCAT5_CLOCK->now())
47  , m_tcomp(SATCAT5_DOPPLER_TCOMP)
48 {
49  // Nothing else to initialize.
50 }
51 
52 bool DopplerTlv::tlv_rcvd(const Header& hdr, const TlvHeader& tlv, LimitedRead& rd)
53 {
54  // Ignore everything except DopplerTLV tags.
55  if (tlv.type != TLVTYPE_DOPPLER) return false;
56 
57  // Read the contents of the DopplerTLV tag.
58  // (In many cases, the Doppler field is echoed in the reply.)
59  m_dstamp = rd.read_s48();
60 
61  // Is this the final message in a two-way handshake?
62  bool rcvd_sptp = SATCAT5_SPTP_ENABLE
63  && (m_client->get_mode() == ClientMode::SLAVE_SPTP)
64  && (hdr.flags & Header::FLAG_SPTP);
65  bool rcvd_final = (hdr.type == Header::TYPE_DELAY_RESP)
66  || (rcvd_sptp && hdr.type == Header::TYPE_SYNC);
67 
68  // Update tracking filter for each complete Doppler measurement.
69  if (rcvd_final) {
70  u32 elapsed_usec = m_tref.increment_usec();
71  elapsed_usec = min_u32(1000000, elapsed_usec);
72  m_predict.update(m_dstamp, elapsed_usec);
73  }
74 
75  // Optional logging
76  if (DEBUG_VERBOSE > 1) {
77  satcat5::log::Log(satcat5::log::DEBUG, "DopplerTlv::tlv_recv")
78  .write("\r\n typ").write(hdr.type) // PTP message type
79  .write("\r\n raw").write((u64)m_dstamp) // Raw measurement (hex)
80  .write("\r\n raw").write10(m_dstamp); // Raw measurement (dec)
81  }
82 
83  // Matching tag has been read.
84  return true;
85 }
86 
88 {
89  // Flags from the client state, PTP header, etc.
90  bool flag_sptp = SATCAT5_SPTP_ENABLE && (hdr.flags & Header::FLAG_SPTP);
91 
92  // Which outgoing messages start or continue a Doppler handshake?
93  // * Normal: SYNC -> DELAY_REQ -> DELAY_RESP
94  // * Peer: PDELAY_REQ -> PDELAY_RESP
95  // * SPTP: DELAY_REQ -> SYNC
96  bool send_any = (hdr.type == Header::TYPE_SYNC)
97  || (hdr.type == Header::TYPE_DELAY_REQ)
98  || (hdr.type == Header::TYPE_PDELAY_REQ)
99  || (hdr.type == Header::TYPE_PDELAY_RESP)
100  || (hdr.type == Header::TYPE_DELAY_RESP);
101  bool send_first = (hdr.type == Header::TYPE_SYNC && !flag_sptp)
102  || (hdr.type == Header::TYPE_PDELAY_REQ)
103  || (hdr.type == Header::TYPE_DELAY_REQ && flag_sptp);
104 
105  // Write header+tag if applicable.
106  if (send_any && wr) {
107  wr->write_obj(TLVHDR_DOPPLER); // Tag header
108  wr->write_s48(send_first ? 0 : m_dstamp); // Tag contents
109  }
110  return send_any ? TLVHDR_DOPPLER.len_total() : 0;
111 }
112 
113 // TODO: Verify this model somehow?
115 {
116  // Calculate round-trip time including all network delays.
117  // (Use absolute value because T4 - T1 is negative in SPTP mode.)
118  // TODO: Better accuracy if we split CF1, CF2 from T1/T2/T3/T4?
119  s64 t = (meas.t4 - meas.t1).abs().delta_subns(); // subns
120 
121  // Calculate the current velocity and acceleration.
122  s64 v = m_predict.predict(0); // subns/sec
123  s64 a = m_predict.predict(500000) - v; // 0.5 * subns/sec^2
124 
125  // Renormalize T4 to mitigate the effect of motion.
126  int128_t tt(t), vv(v), aa(a), s(SUBNS_PER_SEC);
127  s64 delta = s64((((aa * tt).div_round(s) + vv) * tt).div_round(s));
128  if (m_tcomp) meas.t4 -= Time(delta);
129 
130  // Optional logging
131  if (DEBUG_VERBOSE > 0) {
132  satcat5::log::Log(satcat5::log::DEBUG, "DopplerTlv::tlv_meas")
133  .write("\r\n time ").write10(t) // Elapsed time
134  .write("\r\n vraw ").write10(m_dstamp) // Raw velocity
135  .write("\r\n vfilt").write10(v) // Filtered velocity
136  .write("\r\n accel").write10(a) // Filtered acceleration
137  .write("\r\n tcomp").write10(delta); // Compensation amount
138  }
139 }
140 
141 static constexpr satcat5::ptp::CoeffPI DEFAULT_TIME_CONSTANT(3.0);
142 
143 DopplerSimple::DopplerSimple(satcat5::ptp::Client* client)
144  : DopplerTlv(client)
145  , m_ampl()
146  , m_ctrl(DEFAULT_TIME_CONSTANT)
147 {
148  add_filter(&m_ampl);
149  add_filter(&m_ctrl);
150 }
Limited read of next N bytes.
Definition: io_readable.h:255
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
void write_obj(const T &obj)
Templated wrapper for any object with the following method: void write_to(satcat5::io::Writeable* wr)...
Definition: io_writeable.h:104
The Log class creates and formats one log message.
Definition: log.h:195
Log & write10(s32 val)
Print integer as a decimal value with no leading zeros.
Definition: log.cc:261
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
Client for the IEEE 1588-2019 Precision Time Protocol (PTP)
Definition: ptp_client.h:67
satcat5::ptp::ClientMode get_mode() const
Mode and state accessors.
Definition: ptp_client.h:109
Streamlined variant of ptp::DopplerTlv, with a built-in filter chain that is adequate for most PTP ap...
Definition: ptp_doppler.h:74
TlvHandler for experimental Doppler-TLV tags.
Definition: ptp_doppler.h:28
void add_filter(satcat5::ptp::Filter *filter)
Add to the chain of processing filters.
Definition: ptp_doppler.h:35
bool tlv_rcvd(const satcat5::ptp::Header &hdr, const satcat5::ptp::TlvHeader &tlv, satcat5::io::LimitedRead &rd) override
Child class SHOULD override this method to read incoming TLV(s).
Definition: ptp_doppler.cc:52
void tlv_meas(satcat5::ptp::Measurement &meas) override
Child class MAY override this method to read or modify each complete two-way handshake event.
Definition: ptp_doppler.cc:114
unsigned tlv_send(const satcat5::ptp::Header &hdr, satcat5::io::Writeable *wr) override
Child class SHOULD override this method to append outgoing TLV(s).
Definition: ptp_doppler.cc:87
s64 predict(u32 elapsed_usec) const
Extrapolate trendline relative to most recent update() event.
Definition: ptp_filters.cc:485
s64 update(s64 next, u32 elapsed_usec) override
Required API from ptp::Filter.
Definition: ptp_filters.cc:462
High-precision timestamp for use with PTP / IEEE1588.
Definition: ptp_time.h:41
Users should derive custom TLV objects from this base class.
Definition: ptp_tlv.h:111
satcat5::ptp::Client *const m_client
Pointer to the associated PTP client.
Definition: ptp_tlv.h:141
Diagnostic logging to UART and/or Ethernet ports.
Timekeeper timekeeper
There is a single global instance of the Timekeeper class.
Definition: polling.cc:50
Define the data structure for a two-way time transfer handshake.
constexpr s64 SUBNS_PER_SEC
Define commonly used scaling factors.
Definition: ptp_time.h:31
constexpr u16 TLVTYPE_DOPPLER
Define some key tlvType values.
Definition: ptp_tlv.h:60
Loop-filter coefficients for use with the "ControllerPI" class.
Definition: ptp_filters.h:224
Struct representing the PTP header used for all message types.
Definition: ptp_header.h:46
u16 flags
flagField
Definition: ptp_header.h:53
static constexpr u16 FLAG_SPTP
SPTP uses the PROFILE1 flag, so define an alias.
Definition: ptp_header.h:93
u8 type
messageType (0-15)
Definition: ptp_header.h:48
static constexpr u8 TYPE_SYNC
Message types (Section 13.3.2.3 / Table 36)
Definition: ptp_header.h:66
Timestamps and metadata for a two-way time-transfer handshake.
satcat5::ptp::Time t1
Timestamp T1 (A to B / Tx)
satcat5::ptp::Time t4
Timestamp T4 (B to A / Rx)
Data structure for identifying TLV headers.
Definition: ptp_tlv.h:70
u16 type
Access to the raw header fields Note: This "length" field always reflects the user data length,...
Definition: ptp_tlv.h:74
unsigned increment_usec()
Measure elapsed time in microseconds, then increment by the returned quantized value.
Definition: timeref.cc:33
Miscellaneous mathematical utility functions.
constexpr u32 min_u32(u32 a, u32 b)
Min and max functions.
Definition: utils.h:103
Wide-integer arithmetic.