SatCat5
ccsds_spp.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/ccsds_spp.h>
7 #include <satcat5/log.h>
8 #include <satcat5/utils.h>
9 
21 using satcat5::log::Log;
23 using satcat5::net::Type;
25 
26 // Set debugging verbosity (0/1/2)
27 #define DEBUG_VERBOSE 0
28 
29 void Header::set(bool cmd, u16 apid, u16 seq) {
30  value = VERSION_1 | SEQF_UNSEG
31  | (cmd ? TYPE_CMD : TYPE_TLM)
32  | pack_apid(apid) | pack_seqc(seq);
33 }
34 
36  // Increment the sequence-count field, with wraparound.
37  u32 old_hdr = u32(value & ~SEQC_MASK);
38  u16 old_seq = u16(value & SEQC_MASK);
39  value = old_hdr | pack_seqc(old_seq + 1);
40  return *this;
41 }
42 
43 Packetizer::Packetizer(u8* buff, unsigned rxbytes, unsigned rxpkt, Readable* src)
44  : ReadableRedirect(&m_buff)
45  , m_copy(src, this)
46  , m_buff(buff, rxbytes, rxpkt)
47  , m_rem(0)
48  , m_timeout(1000)
49  , m_wridx(0)
50  , m_sreg(0)
51 {
52  // Nothing else to initialize.
53 }
54 
55 unsigned Packetizer::get_write_space() const {
56  return m_buff.get_write_space();
57 }
58 
60  auto src = m_copy.src();
61  m_buff.write_abort(); // Flush partial output
62  if (src) src->read_finalize(); // Flush partial input
63  m_rem = 0; // Reset packet state
64  m_wridx = 0; // Reset header state
65 }
66 
68  m_buff.clear(); // Discard buffer contents
69  flush(); // Reset parser state
70 }
71 
73  Log(WARNING, "CCSDS-SDS packetizer timeout.");
74  flush(); // Discard partials and reset state
75 }
76 
77 void Packetizer::write_next(u8 data) {
78  // Always copy new data to the output buffer.
79  m_buff.write_u8(data);
80 
81  // Shift-register holds the two most recent bytes.
82  m_sreg = 256 * m_sreg + data;
83 
84  // Update packet-parsing state...
85  if (m_rem) {
86  // Countdown to end of SPP packet.
87  bool ok = (--m_rem) || m_buff.write_finalize();
88  if (DEBUG_VERBOSE > 0 && !ok)
89  Log(DEBUG, "ccsds_spp::Packetizer overflow");
90  } else if (++m_wridx == 6) {
91  // End of 6-byte SPP primary header.
92  m_rem = 1 + unsigned(m_sreg);
93  m_wridx = 0;
94  }
95 
96  // Refresh the watchdog timer.
98  else timer_stop();
99 }
100 
102  return m_iface;
103 }
104 
106  Writeable* wr = m_iface->open_write(m_dst, len);
107  if (wr) ++m_dst; // Increment sequence count?
108  return wr; // Return Writeable object.
109 }
110 
112  m_dst.value = 0;
113 }
114 
115 bool Address::ready() const {
116  return !!m_dst.value;
117 }
118 
120  return m_dst.apid() == m_iface->rcvd_hdr().apid();
121 }
122 
124  Header tmp = m_iface->rcvd_hdr();
125  m_dst.set(tmp.type_tlm(), tmp.apid(), tmp.seqc());
126 }
127 
129  : m_src(src)
130  , m_dst(dst)
131  , m_rcvd_hdr{0}
132 {
133  if (m_src) m_src->set_callback(this);
134 }
135 
136 #if SATCAT5_ALLOW_DELETION
137 Dispatch::~Dispatch() {
138  if (m_src) m_src->set_callback(0);
139 }
140 #endif
141 
142 Writeable* Dispatch::open_reply(const Type& type, unsigned len) {
143  // Use the same APID, but invert cmd/tlm type.
144  // Echo the sequence counter. (Not ideal, but better than zero.)
145  Header hdr;
146  hdr.set(!m_rcvd_hdr.type_cmd(), m_rcvd_hdr.apid(), m_rcvd_hdr.seqc());
147  return open_write(hdr, len);
148 }
149 
150 Writeable* Dispatch::open_write(const Header& hdr, unsigned len) {
151  // Sanity check if user provided a null destination.
152  if (!m_dst) return nullptr;
153  if (DEBUG_VERBOSE > 1)
154  Log(DEBUG, "ccsds_spp::Transmit").write(hdr.apid()).write10(u32(len));
155  // Flush leftovers from incomplete previous transmissions.
156  m_dst->write_abort();
157  // Sanity check: Is user trying to send an empty packet?
158  if (len < 1) return 0;
159  // Sanity check: Can we fit a complete and valid packet?
160  if (m_dst->get_write_space() < len + 6) return 0;
161  // If so, write the header and let user write contents.
162  m_dst->write_u32(hdr.value);
163  m_dst->write_u16(len - 1);
164  return m_dst;
165 }
166 
168  // Attempt to read the incoming SPP header.
169  u32 len = 0; bool ok = false;
170  if (src->get_read_ready() >= 6) {
171  m_rcvd_hdr.value = src->read_u32();
172  len = 1 + u32(src->read_u16());
173  ok = (src->get_read_ready() >= len)
174  && (m_rcvd_hdr.apid() != APID_IDLE);
175  }
176 
177  // Optionally log each received packet.
178  if (DEBUG_VERBOSE > 1 && ok)
179  Log(DEBUG, "ccsds_spp::Received").write(m_rcvd_hdr.apid()).write10(u32(len));
180 
181  // Attempt delivery, filtered by APID.
182  Type typ(m_rcvd_hdr.apid());
183  if (ok) deliver(typ, src, len);
184 
185  // Cleanup any trailing bytes.
186  src->read_finalize();
187 }
188 
189 void Dispatch::data_unlink(Readable* src) {m_src = 0;} // GCOVR_EXCL_LINE
190 
191 Protocol::Protocol(Dispatch* iface, u16 apid)
192  : satcat5::net::Protocol(Type(apid))
193  , m_iface(iface)
194 {
195  m_iface->add(this);
196 }
197 
198 #if SATCAT5_ALLOW_DELETION
199 Protocol::~Protocol() {
200  m_iface->remove(this);
201 }
202 #endif
203 
205  bool cmd, u16 apid, unsigned max_chunk)
206  : m_dst(dst)
207  , m_strm(src, &m_dst, max_chunk)
208 {
209  m_dst.connect(cmd, apid);
210 }
211 
213  : Protocol(src, apid)
214  , m_dst(dst)
215 {
216  // Nothing else to initialize.
217 }
218 
220  // Copy all data, ignoring header fields.
221  src.copy_and_finalize(m_dst, satcat5::io::CopyMode::STREAM);
222 }
CCSDS Space Packet Protocol.
Implemention of "net::Address" API for CCSDS-SPP packets.
Definition: ccsds_spp.h:169
bool ready() const override
Is this address object ready for use? Child MUST override this method.
Definition: ccsds_spp.cc:115
void close() override
Close any open connections and revert to idle.
Definition: ccsds_spp.cc:111
void connect(bool cmd, u16 apid)
Set the packet type and APID.
Definition: ccsds_spp.h:177
void save_reply_address() override
Bind this Address object to the parent interface's current reply address, as provided in net::Dispatc...
Definition: ccsds_spp.cc:123
satcat5::net::Dispatch * iface() const override
Fetch a pointer to the underlying interface.
Definition: ccsds_spp.cc:101
satcat5::io::Writeable * open_write(unsigned len) override
Open a new frame to the designated address and type.
Definition: ccsds_spp.cc:105
bool matches_reply_address() const override
Does this Address object match the parent interface's current reply address? Multicast destinations s...
Definition: ccsds_spp.cc:119
Convert a raw byte-stream by inserting CCSDS-SPP headers.
Definition: ccsds_spp.h:258
BytesToSpp(satcat5::io::Readable *src, satcat5::ccsds_spp::Dispatch *dst, bool cmd, u16 apid, unsigned max_chunk=256)
Set source, destination, APID, and chunk-size.
Definition: ccsds_spp.cc:204
Implemention of "net::Dispatch" API for CCSDS-SPP packets.
Definition: ccsds_spp.h:199
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
Definition: ccsds_spp.cc:167
void data_unlink(satcat5::io::Readable *src) override
Unlink this EventListener from the designated source, because the designated Readable object is being...
Definition: ccsds_spp.cc:189
satcat5::io::Writeable * open_write(const satcat5::ccsds_spp::Header &hdr, unsigned len)
Write CCSDS-SPP frame header and get Writeable object.
Definition: ccsds_spp.cc:150
satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned len) override
Write CCSDS-SPP frame header and get Writeable object.
Definition: ccsds_spp.cc:142
const satcat5::ccsds_spp::Header & rcvd_hdr() const
Fetch the last received CCSDS-SPP header.
Definition: ccsds_spp.h:221
Find packet boundaries in a raw CCSDS-SPP byte-stream.
Definition: ccsds_spp.h:101
void timer_event() override
Child class MUST override this method.
Definition: ccsds_spp.cc:72
satcat5::io::BufferedCopy m_copy
Push-pull adapter.
Definition: ccsds_spp.h:146
void reset()
Full reset of buffer and synchronization state.
Definition: ccsds_spp.cc:67
void flush()
Reset partial packets and synchronization state.
Definition: ccsds_spp.cc:59
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: ccsds_spp.cc:55
Packetizer(u8 *buff, unsigned rxbytes, unsigned rxpkt, satcat5::io::Readable *src=nullptr)
Child class must provide a working buffer.
Definition: ccsds_spp.cc:43
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
Definition: ccsds_spp.cc:77
unsigned m_rem
Bytes remaining in current packet.
Definition: ccsds_spp.h:148
unsigned m_timeout
User-configurable resync timeout.
Definition: ccsds_spp.h:149
u16 m_sreg
Packet header working buffer.
Definition: ccsds_spp.h:151
satcat5::io::PacketBuffer m_buff
Output buffer.
Definition: ccsds_spp.h:147
u8 m_wridx
Packet header write pointer.
Definition: ccsds_spp.h:150
Implemention of "net::Protocol" API for CCSDS-SPP packets.
Definition: ccsds_spp.h:239
satcat5::ccsds_spp::Dispatch *const m_iface
Pointer to the parent interface.
Definition: ccsds_spp.h:250
Protocol(satcat5::ccsds_spp::Dispatch *iface, u16 apid)
Constructor and destructor access restricted to children.
Definition: ccsds_spp.cc:191
Convert a CCSDS-SPP packet stream by removing CCSDS-SPP headers.
Definition: ccsds_spp.h:280
void frame_rcvd(satcat5::io::LimitedRead &src) override
For each SPP frame, remove header and forward data.
Definition: ccsds_spp.cc:219
SppToBytes(satcat5::ccsds_spp::Dispatch *src, satcat5::io::Writeable *dst, u16 apid)
Set source, destination, and APID.
Definition: ccsds_spp.cc:212
satcat5::io::Readable * src()
Other accessors.
Definition: io_buffer.h:87
Limited read of next N bytes.
Definition: io_readable.h:255
void clear()
Reset buffer contents.
Definition: pkt_buffer.cc:22
bool write_finalize() override
Mark end of frame and release temporary working data.
Definition: pkt_buffer.cc:105
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: pkt_buffer.cc:52
void write_abort() override
If possible, abort the current partially-written packet.
Definition: pkt_buffer.cc:89
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:270
bool copy_and_finalize(satcat5::io::Writeable *dst, satcat5::io::CopyMode mode=CopyMode::PACKET)
Copy data to a Writeable object, then finalize.
Definition: io_readable.cc:234
virtual void set_callback(satcat5::io::EventListener *callback)
Update registered callback for data_rcvd() events.
Definition: io_readable.cc:39
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
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
The "Dispatch" is a generic interface that knows how to read a designated protocol layer and sort inc...
Definition: net_dispatch.h:36
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
Dispatch()
Constructor and destructor access restricted to children.
Definition: net_dispatch.h:68
bool deliver(const satcat5::net::Type &type, satcat5::io::Readable *src, unsigned len)
Deliver current message by calling Protocol::frame_rcvd(...).
Definition: net_dispatch.cc:25
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
void timer_stop()
Stop all future notifications.
Definition: polling.cc:326
void timer_once(unsigned msec)
Configure a one-time notification after X milliseconds.
Definition: polling.cc:316
Diagnostic logging to UART and/or Ethernet ports.
constexpr s8 WARNING
Define basic priority codes for log messages.
Definition: log.h:111
constexpr s8 DEBUG
Define basic priority codes for log messages.
Definition: log.h:109
Helper object for the CCSDS-SPP primary packet header.
Definition: ccsds_spp.h:47
void set(bool cmd, u16 apid, u16 seq)
Construct a basic single-part SPP header.
Definition: ccsds_spp.cc:29
static constexpr u32 pack_seqc(u16 seqc)
Convert raw SEQC to the packed internal format.
Definition: ccsds_spp.h:58
bool type_tlm() const
< Type field is telemetry?
Definition: ccsds_spp.h:66
bool type_cmd() const
< Type field is command?
Definition: ccsds_spp.h:64
static constexpr u32 pack_apid(u16 apid)
Convert raw APID to the packed internal format.
Definition: ccsds_spp.h:55
Header & operator++()
Increment the sequence-count field.
Definition: ccsds_spp.cc:35
u16 seqc() const
< Sequence counter
Definition: ccsds_spp.h:74
u16 apid() const
< Application process identifier (APID)
Definition: ccsds_spp.h:70
Multipurpose filter for matching fields in network packets.
Definition: net_type.h:38
Miscellaneous mathematical utility functions.
constexpr unsigned min_unsigned(unsigned a, unsigned b)
Min and max functions.
Definition: utils.h:111