]>
code.octet-stream.net Git - m17rt/blob - m17core/src/tnc.rs
a64f367e70c1f152eafb1892c311ee663b36ec35
1 use crate::address
::{Address
, Callsign
};
3 KissBuffer
, KissCommand
, KissFrame
, PORT_PACKET_BASIC
, PORT_PACKET_FULL
, PORT_STREAM
,
5 use crate::modem
::ModulatorFrame
;
7 Frame
, LichCollection
, LsfFrame
, Mode
, PacketFrame
, PacketFrameCounter
, StreamFrame
,
10 /// Handles the KISS protocol and frame management for `SoftModulator` and `SoftDemodulator`.
12 /// These components work alongside each other. User is responsible for chaining them together
13 /// or doing something else with the data.
15 /// Handle framing of KISS commands from the host, which may arrive in arbitrary binary blobs.
16 kiss_buffer
: KissBuffer
,
18 /// Kiss message that needs to be sent to the host.
19 outgoing_kiss
: Option
<OutgoingKiss
>,
21 /// Current RX or TX function of the TNC.
24 /// Latest state of data carrier detect from demodulator - controls whether we can go to TX
27 /// If CSMA declined to transmit into an idle slot, at what point do we next check it?
28 next_csma_check
: Option
<u64>,
30 /// Current monotonic time, counted in samples
33 // TODO: use a static ring buffer crate of some sort?
34 /// Circular buffer of packets enqueued for transmission
35 packet_queue
: [PendingPacket
; 4],
40 /// Current packet index, which is either partly transmitted or not transmitted at all.
43 /// If true, packet_next == packet_curr implies full queue. packet_next is invalid.
44 /// If false, it implies empty queue.
47 /// The LSF for a stream we are going to start transmitting.
49 /// This serves as a general indicator that we want to tx a stream.
50 stream_pending_lsf
: Option
<LsfFrame
>,
52 /// Circular buffer of stream data enqueued for transmission.
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.
57 /// Overruns are less troublesome - we can drop frames and receiving stations should cope.
58 stream_queue
: [StreamFrame
; 8],
63 /// Current unsent stream frame index
66 /// True if stream_next == stream_curr because the queue is full. stream_next is invalid.
69 /// Should PTT be on right now? Polled by external
72 /// TxDelay raw value, number of 10ms units. We will optimistically start with default 0.
75 /// This is a full duplex channel so we do not need to monitor DCD or use CSMA. Default false.
80 pub fn new() -> Self {
82 kiss_buffer
: KissBuffer
::new(),
86 next_csma_check
: None
,
88 packet_queue
: Default
::default(),
92 stream_pending_lsf
: None
,
93 stream_queue
: Default
::default(),
103 /// Process an individual `Frame` that has been decoded by the modem.
104 pub fn handle_frame(&mut self, frame
: Frame
) {
106 // Ignore self-decodes
111 // A new LSF implies a clean slate.
112 // If we were partway through decoding something else then we missed it.
115 self.state
= State
::RxPacket(RxPacketState
{
122 let kiss
= KissFrame
::new_stream_setup(&lsf
.0).unwrap
();
123 self.kiss_to_host(kiss
);
124 self.state
= State
::RxStream(RxStreamState
{
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
);
141 // unexpected order - something has gone wrong
142 self.state
= State
::Idle
;
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
152 KissFrame
::new_full_packet(&rx
.lsf
.0, &rx
.packet
[0..end
])
154 self.kiss_to_host(kiss
);
155 self.state
= State
::Idle
;
160 // Invalid transition
161 self.state
= State
::Idle
;
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
_n
umber
< 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
});
174 rx
.index
= stream
.frame
_n
umber
+ 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
;
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
{
197 index
: stream
.frame
_n
umber
+ 1,
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
})
214 pub fn set_data_carrier_detect(&mut self, dcd
: bool
) {
218 pub fn set_now(&mut self, now_samples
: u64) {
219 self.now
= now_samples
;
220 if let State
::TxEndingAtTime(time
) = self.state
{
221 if now_samples
>= time
{
223 self.state
= State
::Idle
;
228 pub fn ptt(&self) -> bool
{
232 pub fn set_tx_end_time(&mut self, in_samples
: usize) {
233 log
::debug
!("tnc has been told that tx will complete in {in_samples} samples");
234 if let State
::TxEnding
= self.state
{
235 self.state
= State
::TxEndingAtTime(self.now
+ in_samples
as u64);
239 pub fn read_tx_frame(&mut self) -> Option
<ModulatorFrame
> {
241 State
::Idle
| State
::RxAcquiringStream(_
) | State
::RxStream(_
) | State
::RxPacket(_
) => {
242 let stream_wants_to_tx
= self.stream_pending_lsf
.is
_some
();
243 let packet_wants_to_tx
= self.packet_full
|| (self.packet_next
!= self.packet_curr
);
244 if !stream_wants_to_tx
&& !packet_wants_to_tx
{
248 // We have something we might send if the channel is free
250 // TODO: Proper full duplex support
251 // A true full duplex TNC should be able to rx and tx concurrently, implying
253 if !self.full
_d
uplex
{
254 match self.next_csma_check
{
257 self.next_csma_check
= Some(self.now
+ 1920);
260 // channel is idle at the moment we get a frame to send
265 if self.now
< at_time
{
268 // 25% chance that we'll transmit this slot.
269 // Using self.now as random is probably fine so long as it's not being set in
270 // a lumpy manner. m17app's soundmodem should be fine.
271 // TODO: bring in prng to help in cases where `now` never ends in 0b11
272 let p1_4
= (self.now
& 3) == 3;
273 if !self.dcd
|| !p1_4
{
274 self.next_csma_check
= Some(self.now
+ 1920);
277 self.next_csma_check
= None
;
283 if stream_wants_to_tx
{
284 self.state
= State
::TxStream
;
286 self.state
= State
::TxPacket
;
289 Some(ModulatorFrame
::Preamble
{
290 tx_delay
: self.tx_delay
,
294 if !self.stream_full
&& self.stream_next
== self.stream_curr
{
297 if let Some(lsf
) = self.stream_pending_lsf
.take() {
298 return Some(ModulatorFrame
::Lsf(lsf
));
300 let frame
= self.stream_queue
[self.stream_curr
].clone();
301 if self.stream_full
{
302 self.stream_full
= false;
304 self.stream_curr
= (self.stream_curr
+ 1) % 8;
305 if frame
.end_of_stream
{
306 self.state
= State
::TxStreamSentEndOfStream
;
308 Some(ModulatorFrame
::Stream(frame
))
310 State
::TxStreamSentEndOfStream
=> {
311 self.state
= State
::TxEnding
;
312 Some(ModulatorFrame
::EndOfTransmission
)
315 if !self.packet_full
&& self.packet_next
== self.packet_curr
{
318 while self.packet_next
!= self.packet_curr
{
319 match self.packet_queue
[self.packet_curr
].next_frame() {
324 self.packet_curr
= (self.packet_curr
+ 1) % 4;
328 self.state
= State
::TxEnding
;
329 Some(ModulatorFrame
::EndOfTransmission
)
331 State
::TxEnding
| State
::TxEndingAtTime(_
) => {
332 // Once we have signalled EOT we withold any new frames until
333 // the channel fully clears and we are ready to TX again
339 /// Read KISS message to be sent to host.
341 /// After each frame input, this should be consumed in a loop until length 0 is returned.
342 /// This component will never block. Upstream interface can provide blocking `read()` if desired.
343 pub fn read_kiss(&mut self, target_buf
: &mut [u8]) -> usize {
344 match self.outgoing_kiss
.as_mut() {
346 let n
= (outgoing
.kiss_frame
.len
- outgoing
.sent
).min(target_buf
.len());
348 .copy_from_slice(&outgoing
.kiss_frame
.data
[outgoing
.sent
..(outgoing
.sent
+ n
)]);
350 if outgoing
.sent
== outgoing
.kiss_frame
.len
{
351 self.outgoing_kiss
= None
;
359 /// Host sends in some KISS data.
360 pub fn write_kiss(&mut self, buf
: &[u8]) -> usize {
361 let target_buf
= self.kiss_buffer
.buf_remaining();
362 let n
= buf
.len().min(target_buf
.len());
363 target_buf
[0..n
].copy_from_slice(&buf
[0..n
]);
364 self.kiss_buffer
.did_write(n
);
365 while let Some(kiss_frame
) = self.kiss_buffer
.next_frame() {
366 let Ok(port
) = kiss_frame
.port() else {
369 let Ok(command
) = kiss_frame
.command() else {
372 if port
!= PORT_PACKET_BASIC
&& port
!= PORT_PACKET_FULL
&& port
!= PORT_STREAM
{
375 if command
== KissCommand
::TxDelay
{
376 let mut new_delay
= [0u8; 1];
377 if kiss_frame
.decode_payload(&mut new_delay
) == Ok(1) {
378 self.tx_delay
= new_delay
[0];
382 if command
== KissCommand
::FullDuplex
{
383 let mut new_duplex
= [0u8; 1];
384 if kiss_frame
.decode_payload(&mut new_duplex
) == Ok(1) {
385 self.full
_d
uplex
= new_duplex
[0] != 0;
389 if command
!= KissCommand
::DataFrame
{
390 // Not supporting any other settings yet
391 // TODO: allow adjusting P persistence parameter for CSMA
394 if port
== PORT_PACKET_BASIC
{
395 if self.packet_full
{
398 let mut pending
= PendingPacket
::new();
399 pending
.app_data
[0] = 0x00; // RAW
400 let Ok(mut len
) = kiss_frame
.decode_payload(&mut pending
.app_data
[1..]) else {
403 len
+= 1; // for RAW prefix
404 let packet_crc
= crate::crc
::m17_crc(&pending
.app_data
[0..len
]);
405 pending
.app_data
[len
..len
+ 2].copy_from_slice(&packet_crc
.to_be_bytes());
406 pending
.app_data_len
= len
+ 2;
407 pending
.lsf
= Some(LsfFrame
::new_packet(
408 &Address
::Callsign(Callsign(*b
"M17RT-PKT")),
411 self.packet_queue
[self.packet_next
] = pending
;
412 self.packet_next
= (self.packet_next
+ 1) % 4;
413 if self.packet_next
== self.packet_curr
{
414 self.packet_full
= true;
416 } else if port
== PORT_PACKET_FULL
{
417 if self.packet_full
{
420 let mut pending
= PendingPacket
::new();
421 let mut payload
= [0u8; 855];
422 let Ok(len
) = kiss_frame
.decode_payload(&mut payload
) else {
428 let mut lsf
= LsfFrame([0u8; 30]);
429 lsf
.0.copy_from_slice(&payload
[0..30]);
430 if lsf
.check_crc() != 0 {
433 pending
.lsf
= Some(lsf
);
434 let app_data_len
= len
- 30;
435 pending
.app_data
[0..app_data_len
].copy_from_slice(&payload
[30..len
]);
436 pending
.app_data_len
= app_data_len
;
437 self.packet_queue
[self.packet_next
] = pending
;
438 self.packet_next
= (self.packet_next
+ 1) % 4;
439 if self.packet_next
== self.packet_curr
{
440 self.packet_full
= true;
442 } else if port
== PORT_STREAM
{
443 let mut payload
= [0u8; 30];
444 let Ok(len
) = kiss_frame
.decode_payload(&mut payload
) else {
448 log
::debug
!("payload len too short");
452 let lsf
= LsfFrame(payload
);
453 if lsf
.check_crc() != 0 {
456 self.stream_pending_lsf
= Some(lsf
);
458 if self.stream_full
{
459 log
::debug
!("stream full");
462 let frame_num_part
= u16::from_be_bytes([payload
[6], payload
[7]]);
463 self.stream_queue
[self.stream_next
] = StreamFrame
{
464 lich_idx
: payload
[5] >> 5,
465 lich_part
: payload
[0..5].try_into().unwrap
(),
466 frame_number
: frame_num_part
& 0x7fff,
467 end_of_stream
: frame_num_part
& 0x8000 > 0,
468 stream_data
: payload
[8..24].try_into().unwrap
(),
470 self.stream_next
= (self.stream_next
+ 1) % 8;
471 if self.stream_next
== self.stream_curr
{
472 self.stream_full
= true;
480 fn kiss_to_host(&mut self, kiss_frame
: KissFrame
) {
481 self.outgoing_kiss
= Some(OutgoingKiss
{
488 impl Default
for SoftTnc
{
489 fn default() -> Self {
494 #[derive(Debug, PartialEq, Eq, Clone)]
495 pub enum SoftTncError
{
496 General(&'
static str),
500 struct OutgoingKiss
{
501 kiss_frame
: KissFrame
,
505 #[allow(clippy::large_enum_variant)]
507 /// Nothing happening. We may have TX data queued but we won't act on it until CSMA opens up.
510 /// We received some stream data but missed the leading LSF so we are trying to assemble from LICH.
511 RxAcquiringStream(RxAcquiringStreamState
),
513 /// We have acquired an identified stream transmission and are sending data payloads to the host.
514 RxStream(RxStreamState
),
516 /// We are receiving a packet. All is well so far, and there is more data to come before we tell the host.
517 RxPacket(RxPacketState
),
519 /// PTT is on and this is a stream-type transmission. New data may be added.
522 /// We have delivered the last frame in the current stream
523 TxStreamSentEndOfStream
,
525 /// PTT is on and this is a packet-type transmission. New packets may be enqueued.
528 /// We gave modulator an EndOfTransmission. PTT is still on, waiting for modulator to advise end time.
531 /// Ending transmission, PTT remains on, but we know the timestamp at which we should disengage it.
535 struct RxAcquiringStreamState
{
536 /// Partial assembly of LSF by accumulating LICH fields.
537 lich
: LichCollection
,
540 struct RxStreamState
{
541 /// Track identifying information for this transmission so we can tell if it changes.
544 /// Expected next frame number. Allowed to skip values on RX, but not go backwards.
548 struct RxPacketState
{
552 /// Accumulation of packet data that we have received so far.
555 /// Number of payload frames we have received. If we are stably in the RxPacket state,
556 /// this will be between 0 and 32 inclusive.
560 struct PendingPacket
{
561 lsf
: Option
<LsfFrame
>,
565 app_data_transmitted
: usize,
572 app_data
: [0u8; 825],
574 app_data_transmitted
: 0,
578 /// Returns next frame, not including preamble or EOT.
580 /// False means all data frames have been sent.
581 fn next_frame(&mut self) -> Option
<ModulatorFrame
> {
582 if let Some(lsf
) = self.lsf
.take() {
583 return Some(ModulatorFrame
::Lsf(lsf
));
585 if self.app_data_len
== self.app_data_transmitted
{
588 let remaining
= self.app_data_len
- self.app_data_transmitted
;
589 let (counter
, data_len
) = if remaining
<= 25 {
591 PacketFrameCounter
::FinalFrame
{
592 payload_len
: remaining
,
598 PacketFrameCounter
::Frame
{
599 index
: self.app_data_transmitted
/ 25,
604 let mut payload
= [0u8; 25];
605 payload
[0..data_len
].copy_from_slice(
606 &self.app_data
[self.app_data_transmitted
..(self.app_data_transmitted
+ data_len
)],
608 self.app_data_transmitted
+= data_len
;
609 Some(ModulatorFrame
::Packet(PacketFrame
{ payload
, counter
}))
613 impl Default
for PendingPacket
{
614 fn default() -> Self {
617 app_data
: [0u8; 825],
619 app_data_transmitted
: 0,
627 use crate::kiss
::{KissCommand
, PORT_STREAM
};
628 use crate::protocol
::StreamFrame
;
630 // TODO: finish all handle_frame tests as below
631 // this will be much more straightforward when we have a way to create LSFs programatically
633 // receiving a single-frame packet
635 // receiving a multi-frame packet
637 // part of one packet and then another
640 fn tnc_receive_stream() {
642 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
643 0, 0, 0, 0, 0, 131, 53,
645 let stream1
= StreamFrame
{
647 lich_part
: [255, 255, 255, 255, 255],
649 end_of_stream
: false,
651 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
654 let stream2
= StreamFrame
{
656 lich_part
: [255, 0, 0, 0, 159],
660 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
663 let mut tnc
= SoftTnc
::new();
664 let mut kiss
= KissFrame
::new_empty();
665 assert_eq
!(tnc
.read_kiss(&mut kiss
.data
), 0);
667 tnc
.handle_frame(Frame
::Lsf(lsf
));
668 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
669 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
670 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
672 let mut payload_buf
= [0u8; 2048];
673 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
676 tnc
.handle_frame(Frame
::Stream(stream1
));
677 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
678 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
679 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
681 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
684 tnc
.handle_frame(Frame
::Stream(stream2
));
685 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
686 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
687 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
689 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
694 fn tnc_acquire_stream() {
698 lich_part
: [255, 255, 255, 255, 255],
700 end_of_stream
: false,
702 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
707 lich_part
: [255, 0, 0, 0, 159],
709 end_of_stream
: false,
711 17, 0, 94, 82, 216, 135, 181, 15, 30, 0, 125, 195, 152, 183, 41, 57,
716 lich_part
: [221, 81, 5, 5, 0],
718 end_of_stream
: false,
720 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
725 lich_part
: [0, 0, 0, 0, 0],
727 end_of_stream
: false,
729 15, 128, 114, 83, 218, 252, 59, 111, 31, 128, 116, 91, 84, 231, 45, 105,
734 lich_part
: [0, 0, 0, 0, 0],
736 end_of_stream
: false,
738 9, 128, 119, 115, 220, 220, 57, 15, 48, 128, 124, 83, 158, 236, 181, 91,
743 lich_part
: [0, 0, 0, 131, 53],
745 end_of_stream
: false,
747 52, 0, 116, 90, 152, 167, 225, 216, 32, 0, 116, 83, 156, 212, 33, 216,
752 let mut tnc
= SoftTnc
::new();
753 let mut kiss
= KissFrame
::new_empty();
755 tnc
.handle_frame(Frame
::Stream(f
));
757 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
758 let mut payload_buf
= [0u8; 2048];
759 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
764 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0,
765 0, 0, 0, 0, 0, 0, 131, 53,
771 fn tnc_handle_skipped_stream_frame() {
773 255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
774 0, 0, 0, 0, 0, 131, 53,
776 let stream1
= StreamFrame
{
778 lich_part
: [255, 255, 255, 255, 255],
780 end_of_stream
: false,
782 128, 0, 119, 115, 220, 252, 41, 235, 8, 0, 116, 195, 94, 244, 45, 75,
785 let stream3
= StreamFrame
{
787 lich_part
: [221, 81, 5, 5, 0],
789 end_of_stream
: false,
791 17, 128, 93, 74, 154, 167, 169, 11, 20, 0, 116, 91, 158, 220, 45, 111,
794 let mut tnc
= SoftTnc
::new();
795 let mut kiss
= KissFrame
::new_empty();
796 assert_eq
!(tnc
.read_kiss(&mut kiss
.data
), 0);
798 tnc
.handle_frame(Frame
::Lsf(lsf
));
799 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
800 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
801 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
803 let mut payload_buf
= [0u8; 2048];
804 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
807 tnc
.handle_frame(Frame
::Stream(stream1
));
808 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
809 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
810 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
812 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();
815 tnc
.handle_frame(Frame
::Stream(stream3
));
816 kiss
.len
= tnc
.read_kiss(&mut kiss
.data
);
817 assert_eq
!(kiss
.command().unwrap
(), KissCommand
::DataFrame
);
818 assert_eq
!(kiss
.port().unwrap
(), PORT_STREAM
);
820 let n
= kiss
.decode_payload(&mut payload_buf
).unwrap
();