]> code.octet-stream.net Git - m17rt/blob - m17core/src/tnc.rs
Fix timing bugs and add documentation
[m17rt] / m17core / src / tnc.rs
1 use crate::address::{Address, Callsign};
2 use crate::kiss::{
3 KissBuffer, KissCommand, KissFrame, PORT_PACKET_BASIC, PORT_PACKET_FULL, PORT_STREAM,
4 };
5 use crate::modem::ModulatorFrame;
6 use crate::protocol::{
7 Frame, LichCollection, LsfFrame, Mode, PacketFrame, PacketFrameCounter, StreamFrame,
8 };
9
10 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
11 ///
12 /// These components work alongside each other. User is responsible for chaining them together
13 /// or doing something else with the data.
14 pub struct SoftTnc {
15 /// Handle framing of KISS commands from the host, which may arrive in arbitrary binary blobs.
16 kiss_buffer: KissBuffer,
17
18 /// Kiss message that needs to be sent to the host.
19 outgoing_kiss: Option<OutgoingKiss>,
20
21 /// Current RX or TX function of the TNC.
22 state: State,
23
24 /// Latest state of data carrier detect from demodulator - controls whether we can go to TX
25 dcd: bool,
26
27 /// If CSMA declined to transmit into an idle slot, at what point do we next check it?
28 next_csma_check: Option<u64>,
29
30 /// Current monotonic time, counted in samples
31 now: u64,
32
33 // TODO: use a static ring buffer crate of some sort?
34 /// Circular buffer of packets enqueued for transmission
35 packet_queue: [PendingPacket; 4],
36
37 /// Next slot to fill
38 packet_next: usize,
39
40 /// Current packet index, which is either partly transmitted or not transmitted at all.
41 packet_curr: usize,
42
43 /// If true, packet_next == packet_curr implies full queue. packet_next is invalid.
44 /// If false, it implies empty queue.
45 packet_full: bool,
46
47 /// The LSF for a stream we are going to start transmitting.
48 ///
49 /// This serves as a general indicator that we want to tx a stream.
50 stream_pending_lsf: Option<LsfFrame>,
51
52 /// Circular buffer of stream data enqueued for transmission.
53 ///
54 /// When the queue empties out, we hope that the last one has the end-of-stream flag set.
55 /// Otherwise a buffer underrun has occurred.
56 ///
57 /// Overruns are less troublesome - we can drop frames and receiving stations should cope.
58 stream_queue: [StreamFrame; 8],
59
60 /// Next slot to fill
61 stream_next: usize,
62
63 /// Current unsent stream frame index
64 stream_curr: usize,
65
66 /// True if stream_next == stream_curr because the queue is full. stream_next is invalid.
67 stream_full: bool,
68
69 /// Should PTT be on right now? Polled by external
70 ptt: bool,
71
72 /// TxDelay raw value, number of 10ms units. We will optimistically start with default 0.
73 tx_delay: u8,
74
75 /// This is a full duplex channel so we do not need to monitor DCD or use CSMA. Default false.
76 full_duplex: bool,
77 }
78
79 impl SoftTnc {
80 pub fn new() -> Self {
81 Self {
82 kiss_buffer: KissBuffer::new(),
83 outgoing_kiss: None,
84 state: State::Idle,
85 dcd: false,
86 next_csma_check: None,
87 now: 0,
88 packet_queue: Default::default(),
89 packet_next: 0,
90 packet_curr: 0,
91 packet_full: false,
92 stream_pending_lsf: None,
93 stream_queue: Default::default(),
94 stream_next: 0,
95 stream_curr: 0,
96 stream_full: false,
97 ptt: false,
98 tx_delay: 0,
99 full_duplex: false,
100 }
101 }
102
103 /// Process an individual `Frame` that has been decoded by the modem.
104 pub fn handle_frame(&mut self, frame: Frame) {
105 if self.ptt {
106 // Ignore self-decodes
107 return;
108 }
109 match frame {
110 Frame::Lsf(lsf) => {
111 // A new LSF implies a clean slate.
112 // If we were partway through decoding something else then we missed it.
113 match lsf.mode() {
114 Mode::Packet => {
115 self.state = State::RxPacket(RxPacketState {
116 lsf,
117 packet: [0u8; 825],
118 count: 0,
119 })
120 }
121 Mode::Stream => {
122 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
123 self.kiss_to_host(kiss);
124 self.state = State::RxStream(RxStreamState {
125 _lsf: lsf,
126 index: 0,
127 });
128 }
129 }
130 }
131 Frame::Packet(packet) => {
132 match &mut self.state {
133 State::RxPacket(ref mut rx) => {
134 match packet.counter {
135 PacketFrameCounter::Frame { index } => {
136 if index == rx.count && index < 32 {
137 let start = 25 * index;
138 rx.packet[start..(start + 25)].copy_from_slice(&packet.payload);
139 rx.count += 1;
140 } else {
141 // unexpected order - something has gone wrong
142 self.state = State::Idle;
143 }
144 }
145 PacketFrameCounter::FinalFrame { payload_len } => {
146 let start = 25 * rx.count;
147 let end = start + payload_len;
148 rx.packet[start..(start + payload_len)]
149 .copy_from_slice(&packet.payload[0..payload_len]);
150 // TODO: compatible packets should be sent on port 0 too
151 let kiss =
152 KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
153 .unwrap();
154 self.kiss_to_host(kiss);
155 self.state = State::Idle;
156 }
157 }
158 }
159 _ => {
160 // Invalid transition
161 self.state = State::Idle;
162 }
163 }
164 }
165 Frame::Stream(stream) => {
166 match &mut self.state {
167 State::RxStream(ref mut rx) => {
168 // TODO: consider wraparound from 0x7fff
169 if stream.frame_number < rx.index {
170 let mut lich = LichCollection::new();
171 lich.set_segment(stream.lich_idx, stream.lich_part);
172 self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich });
173 } else {
174 rx.index = stream.frame_number + 1;
175 let kiss = KissFrame::new_stream_data(&stream).unwrap();
176 self.kiss_to_host(kiss);
177 // TODO: end stream if LICH updates indicate non-META part has changed
178 // (this implies a new station)
179 if stream.end_of_stream {
180 self.state = State::Idle;
181 }
182 }
183 }
184 State::RxAcquiringStream(ref mut rx) => {
185 rx.lich.set_segment(stream.lich_idx, stream.lich_part);
186 if let Some(maybe_lsf) = rx.lich.try_assemble() {
187 let lsf = LsfFrame(maybe_lsf);
188 // LICH can change mid-transmission so wait until the CRC is correct
189 // to ensure (to high probability) we haven't done a "torn read"
190 if lsf.check_crc() == 0 {
191 let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
192 self.kiss_to_host(kiss);
193 // TODO: avoid discarding the first data payload here
194 // need a queue depth of 2 for outgoing kiss
195 self.state = State::RxStream(RxStreamState {
196 _lsf: lsf,
197 index: stream.frame_number + 1,
198 });
199 }
200 }
201 }
202 _ => {
203 // If coming from another state, we have missed something.
204 // Never mind, let's start tracking LICH.
205 let mut lich = LichCollection::new();
206 lich.set_segment(stream.lich_idx, stream.lich_part);
207 self.state = State::RxAcquiringStream(RxAcquiringStreamState { lich })
208 }
209 }
210 }
211 }
212 }
213
214 pub fn set_data_carrier_detect(&mut self, dcd: bool) {
215 self.dcd = dcd;
216 }
217
218 pub fn set_now(&mut self, now_samples: u64) {
219 self.now = now_samples;
220 // TODO: expose this to higher layer so we can schedule a precise delay
221 // rather than waiting for some soundcard I/O event
222 if let State::TxEndingAtTime(time) = self.state {
223 if now_samples >= time {
224 self.ptt = false;
225 self.state = State::Idle;
226 }
227 }
228 }
229
230 pub fn ptt(&self) -> bool {
231 self.ptt
232 }
233
234 pub fn set_tx_end_time(&mut self, in_samples: usize) {
235 log::debug!("tnc has been told that tx will complete in {in_samples} samples");
236 if let State::TxEnding = self.state {
237 self.state = State::TxEndingAtTime(self.now + in_samples as u64);
238 }
239 }
240
241 pub fn read_tx_frame(&mut self) -> Option<ModulatorFrame> {
242 match self.state {
243 State::Idle | State::RxAcquiringStream(_) | State::RxStream(_) | State::RxPacket(_) => {
244 let stream_wants_to_tx = self.stream_pending_lsf.is_some();
245 let packet_wants_to_tx = self.packet_full || (self.packet_next != self.packet_curr);
246 if !stream_wants_to_tx && !packet_wants_to_tx {
247 return None;
248 }
249
250 // We have something we might send if the channel is free
251
252 // TODO: Proper full duplex support
253 // A true full duplex TNC should be able to rx and tx concurrently, implying
254 // separate states.
255 if !self.full_duplex {
256 match self.next_csma_check {
257 None => {
258 if self.dcd {
259 self.next_csma_check = Some(self.now + 1920);
260 return None;
261 } else {
262 // channel is idle at the moment we get a frame to send
263 // go right ahead
264 }
265 }
266 Some(at_time) => {
267 if self.now < at_time {
268 return None;
269 }
270 // 25% chance that we'll transmit this slot.
271 // Using self.now as random is probably fine so long as it's not being set in
272 // a lumpy manner. m17app's soundmodem should be fine.
273 // TODO: bring in prng to help in cases where `now` never ends in 0b11
274 let p1_4 = (self.now & 3) == 3;
275 if !self.dcd || !p1_4 {
276 self.next_csma_check = Some(self.now + 1920);
277 return None;
278 } else {
279 self.next_csma_check = None;
280 }
281 }
282 }
283 }
284
285 if stream_wants_to_tx {
286 self.state = State::TxStream;
287 } else {
288 self.state = State::TxPacket;
289 }
290 self.ptt = true;
291 Some(ModulatorFrame::Preamble {
292 tx_delay: self.tx_delay,
293 })
294 }
295 State::TxStream => {
296 if !self.stream_full && self.stream_next == self.stream_curr {
297 return None;
298 }
299 if let Some(lsf) = self.stream_pending_lsf.take() {
300 return Some(ModulatorFrame::Lsf(lsf));
301 }
302 let frame = self.stream_queue[self.stream_curr].clone();
303 if self.stream_full {
304 self.stream_full = false;
305 }
306 self.stream_curr = (self.stream_curr + 1) % 8;
307 if frame.end_of_stream {
308 self.state = State::TxStreamSentEndOfStream;
309 }
310 Some(ModulatorFrame::Stream(frame))
311 }
312 State::TxStreamSentEndOfStream => {
313 self.state = State::TxEnding;
314 Some(ModulatorFrame::EndOfTransmission)
315 }
316 State::TxPacket => {
317 if !self.packet_full && self.packet_next == self.packet_curr {
318 return None;
319 }
320 while self.packet_next != self.packet_curr {
321 match self.packet_queue[self.packet_curr].next_frame() {
322 Some(frame) => {
323 return Some(frame);
324 }
325 None => {
326 self.packet_curr = (self.packet_curr + 1) % 4;
327 }
328 }
329 }
330 self.state = State::TxEnding;
331 Some(ModulatorFrame::EndOfTransmission)
332 }
333 State::TxEnding | State::TxEndingAtTime(_) => {
334 // Once we have signalled EOT we withold any new frames until
335 // the channel fully clears and we are ready to TX again
336 None
337 }
338 }
339 }
340
341 /// Read KISS message to be sent to host.
342 ///
343 /// After each frame input, this should be consumed in a loop until length 0 is returned.
344 /// This component will never block. Upstream interface can provide blocking `read()` if desired.
345 pub fn read_kiss(&mut self, target_buf: &mut [u8]) -> usize {
346 match self.outgoing_kiss.as_mut() {
347 Some(outgoing) => {
348 let n = (outgoing.kiss_frame.len - outgoing.sent).min(target_buf.len());
349 target_buf[0..n]
350 .copy_from_slice(&outgoing.kiss_frame.data[outgoing.sent..(outgoing.sent + n)]);
351 outgoing.sent += n;
352 if outgoing.sent == outgoing.kiss_frame.len {
353 self.outgoing_kiss = None;
354 }
355 n
356 }
357 None => 0,
358 }
359 }
360
361 /// Host sends in some KISS data.
362 pub fn write_kiss(&mut self, buf: &[u8]) -> usize {
363 let target_buf = self.kiss_buffer.buf_remaining();
364 let n = buf.len().min(target_buf.len());
365 target_buf[0..n].copy_from_slice(&buf[0..n]);
366 self.kiss_buffer.did_write(n);
367 while let Some(kiss_frame) = self.kiss_buffer.next_frame() {
368 let Ok(port) = kiss_frame.port() else {
369 continue;
370 };
371 let Ok(command) = kiss_frame.command() else {
372 continue;
373 };
374 if port != PORT_PACKET_BASIC && port != PORT_PACKET_FULL && port != PORT_STREAM {
375 continue;
376 }
377 if command == KissCommand::TxDelay {
378 let mut new_delay = [0u8; 1];
379 if kiss_frame.decode_payload(&mut new_delay) == Ok(1) {
380 self.tx_delay = new_delay[0];
381 }
382 continue;
383 }
384 if command == KissCommand::FullDuplex {
385 let mut new_duplex = [0u8; 1];
386 if kiss_frame.decode_payload(&mut new_duplex) == Ok(1) {
387 self.full_duplex = new_duplex[0] != 0;
388 }
389 continue;
390 }
391 if command != KissCommand::DataFrame {
392 // Not supporting any other settings yet
393 // TODO: allow adjusting P persistence parameter for CSMA
394 continue;
395 }
396 if port == PORT_PACKET_BASIC {
397 if self.packet_full {
398 continue;
399 }
400 let mut pending = PendingPacket::new();
401 pending.app_data[0] = 0x00; // RAW
402 let Ok(mut len) = kiss_frame.decode_payload(&mut pending.app_data[1..]) else {
403 continue;
404 };
405 len += 1; // for RAW prefix
406 let packet_crc = crate::crc::m17_crc(&pending.app_data[0..len]);
407 pending.app_data[len..len + 2].copy_from_slice(&packet_crc.to_be_bytes());
408 pending.app_data_len = len + 2;
409 pending.lsf = Some(LsfFrame::new_packet(
410 &Address::Callsign(Callsign(*b"M17RT-PKT")),
411 &Address::Broadcast,
412 ));
413 self.packet_queue[self.packet_next] = pending;
414 self.packet_next = (self.packet_next + 1) % 4;
415 if self.packet_next == self.packet_curr {
416 self.packet_full = true;
417 }
418 } else if port == PORT_PACKET_FULL {
419 if self.packet_full {
420 continue;
421 }
422 let mut pending = PendingPacket::new();
423 let mut payload = [0u8; 855];
424 let Ok(len) = kiss_frame.decode_payload(&mut payload) else {
425 continue;
426 };
427 if len < 33 {
428 continue;
429 }
430 let mut lsf = LsfFrame([0u8; 30]);
431 lsf.0.copy_from_slice(&payload[0..30]);
432 if lsf.check_crc() != 0 {
433 continue;
434 }
435 pending.lsf = Some(lsf);
436 let app_data_len = len - 30;
437 pending.app_data[0..app_data_len].copy_from_slice(&payload[30..len]);
438 pending.app_data_len = app_data_len;
439 self.packet_queue[self.packet_next] = pending;
440 self.packet_next = (self.packet_next + 1) % 4;
441 if self.packet_next == self.packet_curr {
442 self.packet_full = true;
443 }
444 } else if port == PORT_STREAM {
445 let mut payload = [0u8; 30];
446 let Ok(len) = kiss_frame.decode_payload(&mut payload) else {
447 continue;
448 };
449 if len < 26 {
450 log::debug!("payload len too short");
451 continue;
452 }
453 if len == 30 {
454 let lsf = LsfFrame(payload);
455 if lsf.check_crc() != 0 {
456 continue;
457 }
458 self.stream_pending_lsf = Some(lsf);
459 } else {
460 if self.stream_full {
461 log::debug!("stream full");
462 continue;
463 }
464 let frame_num_part = u16::from_be_bytes([payload[6], payload[7]]);
465 self.stream_queue[self.stream_next] = StreamFrame {
466 lich_idx: payload[5] >> 5,
467 lich_part: payload[0..5].try_into().unwrap(),
468 frame_number: frame_num_part & 0x7fff,
469 end_of_stream: frame_num_part & 0x8000 > 0,
470 stream_data: payload[8..24].try_into().unwrap(),
471 };
472 self.stream_next = (self.stream_next + 1) % 8;
473 if self.stream_next == self.stream_curr {
474 self.stream_full = true;
475 }
476 }
477 }
478 }
479 n
480 }
481
482 fn kiss_to_host(&mut self, kiss_frame: KissFrame) {
483 self.outgoing_kiss = Some(OutgoingKiss {
484 kiss_frame,
485 sent: 0,
486 });
487 }
488 }
489
490 impl Default for SoftTnc {
491 fn default() -> Self {
492 Self::new()
493 }
494 }
495
496 #[derive(Debug, PartialEq, Eq, Clone)]
497 pub enum SoftTncError {
498 General(&'static str),
499 InvalidState,
500 }
501
502 struct OutgoingKiss {
503 kiss_frame: KissFrame,
504 sent: usize,
505 }
506
507 #[allow(clippy::large_enum_variant)]
508 enum State {
509 /// Nothing happening. We may have TX data queued but we won't act on it until CSMA opens up.
510 Idle,
511
512 /// We received some stream data but missed the leading LSF so we are trying to assemble from LICH.
513 RxAcquiringStream(RxAcquiringStreamState),
514
515 /// We have acquired an identified stream transmission and are sending data payloads to the host.
516 RxStream(RxStreamState),
517
518 /// We are receiving a packet. All is well so far, and there is more data to come before we tell the host.
519 RxPacket(RxPacketState),
520
521 /// PTT is on and this is a stream-type transmission. New data may be added.
522 TxStream,
523
524 /// We have delivered the last frame in the current stream
525 TxStreamSentEndOfStream,
526
527 /// PTT is on and this is a packet-type transmission. New packets may be enqueued.
528 TxPacket,
529
530 /// We gave modulator an EndOfTransmission. PTT is still on, waiting for modulator to advise end time.
531 TxEnding,
532
533 /// Ending transmission, PTT remains on, but we know the timestamp at which we should disengage it.
534 TxEndingAtTime(u64),
535 }
536
537 struct RxAcquiringStreamState {
538 /// Partial assembly of LSF by accumulating LICH fields.
539 lich: LichCollection,
540 }
541
542 struct RxStreamState {
543 /// Track identifying information for this transmission so we can tell if it changes.
544 _lsf: LsfFrame,
545
546 /// Expected next frame number. Allowed to skip values on RX, but not go backwards.
547 index: u16,
548 }
549
550 struct RxPacketState {
551 /// Initial LSF
552 lsf: LsfFrame,
553
554 /// Accumulation of packet data that we have received so far.
555 packet: [u8; 825],
556
557 /// Number of payload frames we have received. If we are stably in the RxPacket state,
558 /// this will be between 0 and 32 inclusive.
559 count: usize,
560 }
561
562 struct PendingPacket {
563 lsf: Option<LsfFrame>,
564
565 app_data: [u8; 825],
566 app_data_len: usize,
567 app_data_transmitted: usize,
568 }
569
570 impl PendingPacket {
571 fn new() -> Self {
572 Self {
573 lsf: None,
574 app_data: [0u8; 825],
575 app_data_len: 0,
576 app_data_transmitted: 0,
577 }
578 }
579
580 /// Returns next frame, not including preamble or EOT.
581 ///
582 /// False means all data frames have been sent.
583 fn next_frame(&mut self) -> Option<ModulatorFrame> {
584 if let Some(lsf) = self.lsf.take() {
585 return Some(ModulatorFrame::Lsf(lsf));
586 }
587 if self.app_data_len == self.app_data_transmitted {
588 return None;
589 }
590 let remaining = self.app_data_len - self.app_data_transmitted;
591 let (counter, data_len) = if remaining <= 25 {
592 (
593 PacketFrameCounter::FinalFrame {
594 payload_len: remaining,
595 },
596 remaining,
597 )
598 } else {
599 (
600 PacketFrameCounter::Frame {
601 index: self.app_data_transmitted / 25,
602 },
603 25,
604 )
605 };
606 let mut payload = [0u8; 25];
607 payload[0..data_len].copy_from_slice(
608 &self.app_data[self.app_data_transmitted..(self.app_data_transmitted + data_len)],
609 );
610 self.app_data_transmitted += data_len;
611 Some(ModulatorFrame::Packet(PacketFrame { payload, counter }))
612 }
613 }
614
615 impl Default for PendingPacket {
616 fn default() -> Self {
617 Self {
618 lsf: None,
619 app_data: [0u8; 825],
620 app_data_len: 0,
621 app_data_transmitted: 0,
622 }
623 }
624 }
625
626 #[cfg(test)]
627 mod tests {
628 use super::*;
629 use crate::kiss::{KissCommand, PORT_STREAM};
630 use crate::protocol::StreamFrame;
631
632 // TODO: finish all handle_frame tests as below
633 // this will be much more straightforward when we have a way to create LSFs programatically
634
635 // receiving a single-frame packet
636
637 // receiving a multi-frame packet
638
639 // part of one packet and then another
640
641 #[test]
642 fn tnc_receive_stream() {
643 let lsf = LsfFrame([
644 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
645 0, 0, 0, 0, 0, 131, 53,
646 ]);
647 let stream1 = StreamFrame {
648 lich_idx: 0,
649 lich_part: [255, 255, 255, 255, 255],
650 frame_number: 0,
651 end_of_stream: false,
652 stream_data: [
653 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
654 ],
655 };
656 let stream2 = StreamFrame {
657 lich_idx: 1,
658 lich_part: [255, 0, 0, 0, 159],
659 frame_number: 1,
660 end_of_stream: true,
661 stream_data: [
662 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
663 ],
664 };
665 let mut tnc = SoftTnc::new();
666 let mut kiss = KissFrame::new_empty();
667 assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
668
669 tnc.handle_frame(Frame::Lsf(lsf));
670 kiss.len = tnc.read_kiss(&mut kiss.data);
671 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
672 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
673
674 let mut payload_buf = [0u8; 2048];
675 let n = kiss.decode_payload(&mut payload_buf).unwrap();
676 assert_eq!(n, 30);
677
678 tnc.handle_frame(Frame::Stream(stream1));
679 kiss.len = tnc.read_kiss(&mut kiss.data);
680 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
681 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
682
683 let n = kiss.decode_payload(&mut payload_buf).unwrap();
684 assert_eq!(n, 26);
685
686 tnc.handle_frame(Frame::Stream(stream2));
687 kiss.len = tnc.read_kiss(&mut kiss.data);
688 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
689 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
690
691 let n = kiss.decode_payload(&mut payload_buf).unwrap();
692 assert_eq!(n, 26);
693 }
694
695 #[test]
696 fn tnc_acquire_stream() {
697 let frames = [
698 StreamFrame {
699 lich_idx: 0,
700 lich_part: [255, 255, 255, 255, 255],
701 frame_number: 0,
702 end_of_stream: false,
703 stream_data: [
704 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
705 ],
706 },
707 StreamFrame {
708 lich_idx: 1,
709 lich_part: [255, 0, 0, 0, 159],
710 frame_number: 1,
711 end_of_stream: false,
712 stream_data: [
713 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
714 ],
715 },
716 StreamFrame {
717 lich_idx: 2,
718 lich_part: [221, 81, 5, 5, 0],
719 frame_number: 2,
720 end_of_stream: false,
721 stream_data: [
722 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
723 ],
724 },
725 StreamFrame {
726 lich_idx: 3,
727 lich_part: [0, 0, 0, 0, 0],
728 frame_number: 3,
729 end_of_stream: false,
730 stream_data: [
731 15, 128, 114, 83, 218, 252, 59, 111, 31, 128, 116, 91, 84, 231, 45, 105,
732 ],
733 },
734 StreamFrame {
735 lich_idx: 4,
736 lich_part: [0, 0, 0, 0, 0],
737 frame_number: 4,
738 end_of_stream: false,
739 stream_data: [
740 9, 128, 119, 115, 220, 220, 57, 15, 48, 128, 124, 83, 158, 236, 181, 91,
741 ],
742 },
743 StreamFrame {
744 lich_idx: 5,
745 lich_part: [0, 0, 0, 131, 53],
746 frame_number: 5,
747 end_of_stream: false,
748 stream_data: [
749 52, 0, 116, 90, 152, 167, 225, 216, 32, 0, 116, 83, 156, 212, 33, 216,
750 ],
751 },
752 ];
753
754 let mut tnc = SoftTnc::new();
755 let mut kiss = KissFrame::new_empty();
756 for f in frames {
757 tnc.handle_frame(Frame::Stream(f));
758 }
759 kiss.len = tnc.read_kiss(&mut kiss.data);
760 let mut payload_buf = [0u8; 2048];
761 let n = kiss.decode_payload(&mut payload_buf).unwrap();
762 assert_eq!(n, 30);
763 assert_eq!(
764 &payload_buf[0..30],
765 [
766 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0,
767 0, 0, 0, 0, 0, 0, 131, 53,
768 ]
769 );
770 }
771
772 #[test]
773 fn tnc_handle_skipped_stream_frame() {
774 let lsf = LsfFrame([
775 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
776 0, 0, 0, 0, 0, 131, 53,
777 ]);
778 let stream1 = StreamFrame {
779 lich_idx: 0,
780 lich_part: [255, 255, 255, 255, 255],
781 frame_number: 0,
782 end_of_stream: false,
783 stream_data: [
784 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
785 ],
786 };
787 let stream3 = StreamFrame {
788 lich_idx: 2,
789 lich_part: [221, 81, 5, 5, 0],
790 frame_number: 2,
791 end_of_stream: false,
792 stream_data: [
793 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
794 ],
795 };
796 let mut tnc = SoftTnc::new();
797 let mut kiss = KissFrame::new_empty();
798 assert_eq!(tnc.read_kiss(&mut kiss.data), 0);
799
800 tnc.handle_frame(Frame::Lsf(lsf));
801 kiss.len = tnc.read_kiss(&mut kiss.data);
802 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
803 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
804
805 let mut payload_buf = [0u8; 2048];
806 let n = kiss.decode_payload(&mut payload_buf).unwrap();
807 assert_eq!(n, 30);
808
809 tnc.handle_frame(Frame::Stream(stream1));
810 kiss.len = tnc.read_kiss(&mut kiss.data);
811 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
812 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
813
814 let n = kiss.decode_payload(&mut payload_buf).unwrap();
815 assert_eq!(n, 26);
816
817 tnc.handle_frame(Frame::Stream(stream3));
818 kiss.len = tnc.read_kiss(&mut kiss.data);
819 assert_eq!(kiss.command().unwrap(), KissCommand::DataFrame);
820 assert_eq!(kiss.port().unwrap(), PORT_STREAM);
821
822 let n = kiss.decode_payload(&mut payload_buf).unwrap();
823 assert_eq!(n, 26);
824 }
825 }