14 using satcat5::ccsds_spp::APID_IDLE;
25 #define DEBUG_VERBOSE 0
28 static constexpr u8 STATE_PRESYNC = 0x80;
29 static constexpr u8 STATE_DATA = 0x40;
30 static constexpr u8 STATE_SYNC = 0x07;
33 static constexpr u16 MPDU_MASK = 0x07FF;
34 static constexpr u16 MPDU_NONE = MPDU_MASK;
35 static constexpr u16 BPDU_MASK = 0xCFFF;
36 static constexpr u16 BPDU_FULL = BPDU_MASK;
37 static constexpr u16 BPDU_NULL = BPDU_MASK - 1;
40 static constexpr
unsigned MIN_FILLER = 7;
41 static constexpr
unsigned MAX_FILLER = 256;
44 u8 ext = u8(
count >> 24) & FRCT_VAL_MASK;
45 u8 sig =
signal & (REPLAY_MASK | FRCT_EXT_MASK);
46 u32 cbo = (
count << 8) | u32(sig);
47 if (
signal & FRCT_EXT_MASK) cbo |= ext;
55 count = rd->read_u24();
57 if (
signal & FRCT_EXT_MASK)
63 u32 rollover = (
signal & FRCT_EXT_MASK) ? (1u << 28) : (1u << 24);
70 u8 scid, u8 vcid,
bool pkt)
71 : Protocol(satcat5::net::TYPE_NONE)
75 , m_rx_spp(m_rx_tmp, sizeof(m_rx_tmp))
76 , m_rx_next(scid, vcid)
77 , m_tx_next(scid, vcid)
78 , m_rx_state(pkt ? State::RESYNC : State::RAW)
86 if (m_iface) m_iface->
add(
this);
90 #if SATCAT5_ALLOW_DELETION
92 if (m_iface) m_iface->
remove(
this);
98 if (DEBUG_VERBOSE > 0)
99 Log(DEBUG,
"ccsds_aos::Channel Desync");
101 if (m_rx_state != State::RAW) {
102 m_rx_state = State::RESYNC;
106 m_iface->error_incr();
124 u16 pdu_hdr = src.read_u16();
127 if (m_rx_state == State::RAW) {
130 if ((pdu_hdr & BPDU_MASK) == BPDU_NULL)
return;
131 unsigned pdu_bits = 1 + unsigned(pdu_hdr & BPDU_MASK);
132 unsigned pdu_bytes = min_unsigned(src.
get_read_ready(), pdu_bits/8);
137 if (DEBUG_VERBOSE > 0 && !ok)
138 Log(DEBUG,
"ccsds_aos::Channel BPDU-Overflow");
141 unsigned first_spp = unsigned(pdu_hdr & MPDU_MASK);
144 unsigned expect_spp = min_unsigned(m_rx_rem, src.
get_read_ready());
145 bool bad_align = (m_rx_state == State::DATA) && (first_spp != expect_spp);
146 bool bad_count = (m_rx_next.
count != frm_hdr.count);
147 if (bad_align || bad_count)
desync();
149 if (m_rx_state == State::RESYNC) {
155 if (!read_header(&src))
break;
158 if (m_rx_state == State::DATA) {
162 if (DEBUG_VERBOSE > 0 && !ok)
163 Log(DEBUG,
"ccsds_aos::Channel MPDU-Overflow");
181 const unsigned dmax = m_iface->
dsize() - 2;
189 if (m_rx_state == State::RAW) {
193 wr->write_u16((nbytes < dmax) ? u16(8*nbytes-1) : BPDU_FULL);
196 for (
unsigned a = nbytes ; a < dmax ; ++a) wr->
write_u8(0);
204 wr->write_u16((rem < dmax) ? u16(rem) : MPDU_NONE);
207 wr->write_u16(m_tx_irem);
212 if (m_tx_irem) m_tx_irem = idle_filler(&aos, MIN_FILLER);
214 unsigned trailing = 0;
220 m_tx_busy = (trailing ? 1 : 0);
228 if (DEBUG_VERBOSE > 0 && !ok)
229 Log(DEBUG,
"ccsds_aos::Channel Rx Overflow");
235 u8 Channel::idle_filler(
Writeable* dst,
unsigned req) {
238 if (req < MIN_FILLER) req = MIN_FILLER;
239 if (req > MAX_FILLER) req = MAX_FILLER;
242 if (req < rem && rem < req + MIN_FILLER) req -= MIN_FILLER;
245 hdr.
set(
false, APID_IDLE, m_tx_iseq);
247 u8 tmp[MAX_FILLER] = {0};
249 wr.write_u32(hdr.value);
250 wr.write_u16(req - 7);
253 unsigned skip = m_tx_irem ? (req - m_tx_irem) : 0;
256 return u8(req - skip - copy);
259 bool Channel::read_header(
Readable* src) {
261 constexpr
unsigned SPP_HDR_LEN = 6;
262 if (m_rx_state != State::HEADER) {
263 if (m_rx_rem)
return true;
264 m_rx_state = State::HEADER;
265 m_rx_rem = SPP_HDR_LEN;
270 m_rx_rem -= src->
copy_to(&m_rx_spp);
271 if (m_rx_rem)
return false;
273 ArrayRead rd(m_rx_tmp,
sizeof(m_rx_tmp));
275 m_rx_rem = 1 + unsigned(rd.read_u16());
277 if (spp.apid() == APID_IDLE) {
278 m_rx_state = State::SKIP;
280 m_rx_state = State::DATA;
293 , m_work(buff, tsize())
294 , m_crc_rx(&m_work, 0xFFFF)
295 , m_crc_tx(dst, 0xFFFF)
300 #if SATCAT5_ALLOW_DELETION
301 Dispatch::~Dispatch() {
311 if (!m_dst)
return 0;
314 unsigned required =
tsize() + (m_insert ? 4 : 0);
329 if (read_sync(src) && read_data(src)) {
334 if (m_rcvd_hdr.
vcid() != VCID_IDLE)
336 }
else if (DEBUG_VERBOSE > 0) {
337 Log(DEBUG,
"ccsds_aos::Dispatch CRC mismatch");
349 bool Dispatch::read_sync(
Readable* src) {
351 if (!m_insert)
return true;
353 constexpr
unsigned SYNC_LEN =
sizeof(TM_SYNC_BYTES);
354 while (m_sync_state < SYNC_LEN && src->get_read_ready()) {
356 if (next == TM_SYNC_BYTES[0]) {
358 }
else if (next == TM_SYNC_BYTES[m_sync_state]) {
364 return (m_sync_state >= SYNC_LEN);
367 bool Dispatch::read_data(
Readable* src) {
CCSDS "Advanced Orbiting Systems" (AOS) Space Data Link Protocol.
constexpr u32 TM_SYNC_WORD
Sync-word for CCSDS "TM Synchronization and Channel Coding".
CCSDS Space Packet Protocol.
CCSDS-AOS Virtual Channel.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Process each received AOS frame.
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
Channel(satcat5::ccsds_aos::Dispatch *iface, satcat5::io::Readable *src, satcat5::io::Writeable *dst, u8 scid, u8 vcid, bool pkt)
Create a virtual channel, bound to an AOS interface.
void data_unlink(satcat5::io::Readable *src) override
Unlink this EventListener from the designated source, because the designated Readable object is being...
void set_wildcard()
Enable wildcard mode, ignoring incoming SCID and VCID.
void desync()
Force resynchronization after an error.
Implemention of "net::Dispatch" API for CCSDS-AOS protocol.
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
satcat5::io::Writeable * open_write(const satcat5::ccsds_aos::Header &hdr)
Write CCSDS-AOS frame header and get Writeable object.
void data_unlink(satcat5::io::Readable *src) override
Unlink this EventListener from the designated source, because the designated Readable object is being...
satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned len) override
Open a reply to the sender of the most recent message by writing frame header(s) and returning a stre...
unsigned tsize() const
< Total size = Header + Data
unsigned dsize() const
< Data field size
const satcat5::ccsds_aos::Header & rcvd_hdr() const
Most recent received header.
bool write_finalize() override
Mark end of frame and release temporary working data.
Ephemeral Readable interface for a simple array.
Ephemeral Writeable interface for a simple array.
unsigned written_len() const
Report total length after write_finalize() is called.
bool write_finalize() override
Mark end of frame and release temporary working data.
void write_abort() override
If possible, abort the current partially-written packet.
const u8 * buffer() const
Read-only access to the working buffer.
unsigned get_write_space() const override
How many bytes can be written without blocking?
unsigned get_write_space() const override
How many bytes can be written without blocking?
Limited read of next N bytes.
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Limited write up to N bytes.
unsigned get_write_space() const override
How many bytes can be written without blocking?
Abstract API for reading byte-streams and packets.
bool read_obj(T &t)
Templated wrapper for any object with the following method: bool read_from(satcat5::io::Readable* rd)...
u8 read_u8()
One of many functions for reading integer/floating point values, see details.
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
virtual void set_callback(satcat5::io::EventListener *callback)
Update registered callback for data_rcvd() events.
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
Abstract API for writing byte-streams and packets.
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
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.
virtual bool write_finalize()
Mark end of frame and release temporary working data.
void write_obj(const T &obj)
Templated wrapper for any object with the following method: void write_to(satcat5::io::Writeable* wr)...
The Log class creates and formats one log message.
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Dispatch()
Constructor and destructor access restricted to children.
bool deliver(const satcat5::net::Type &type, satcat5::io::Readable *src, unsigned len)
Deliver current message by calling Protocol::frame_rcvd(...).
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
satcat5::net::Type m_filter
Incoming packet filter.
Diagnostic logging to UART and/or Ethernet ports.
constexpr s8 DEBUG
Define basic priority codes for log messages.
Multipurpose filter for matching fields in network packets.
Miscellaneous mathematical utility functions.
constexpr unsigned min_unsigned(unsigned a, unsigned b)
Min and max functions.