X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/9058451e46e4d36264282abe381aa9b6fd2c773f..c3a031d9fa12f5fc9888838a4793e47512ad7326:/m17core/src/tnc.rs diff --git a/m17core/src/tnc.rs b/m17core/src/tnc.rs index 0d8bf4a..a7cbc53 100644 --- a/m17core/src/tnc.rs +++ b/m17core/src/tnc.rs @@ -1,3 +1,4 @@ +use crate::address::{Address, Callsign}; use crate::kiss::{KissBuffer, KissFrame, PORT_PACKET_BASIC, PORT_PACKET_FULL, PORT_STREAM}; use crate::modem::ModulatorFrame; use crate::protocol::{ @@ -21,6 +22,9 @@ pub struct SoftTnc { /// Latest state of data carrier detect from demodulator - controls whether we can go to TX dcd: bool, + /// If CSMA declined to transmit into an idle slot, at what point do we next check it? + next_csma_check: Option, + /// Current monotonic time, counted in samples now: u64, @@ -71,6 +75,7 @@ impl SoftTnc { outgoing_kiss: None, state: State::Idle, dcd: false, + next_csma_check: None, now: 0, packet_queue: Default::default(), packet_next: 0, @@ -211,6 +216,7 @@ impl SoftTnc { } pub fn set_tx_end_time(&mut self, in_samples: usize) { + log::debug!("tnc has been told that tx will complete in {in_samples} samples"); match self.state { State::TxEnding => { self.state = State::TxEndingAtTime(self.now + in_samples as u64); @@ -222,17 +228,45 @@ impl SoftTnc { pub fn read_tx_frame(&mut self) -> Option { match self.state { State::Idle | State::RxAcquiringStream(_) | State::RxStream(_) | State::RxPacket(_) => { - // We will let CSMA decide whether to actually go ahead. - // That's not implemented yet, so let's just check DCD. - let channel_free = !self.dcd; let stream_wants_to_tx = self.stream_pending_lsf.is_some(); let packet_wants_to_tx = self.packet_full || (self.packet_next != self.packet_curr); - if channel_free && stream_wants_to_tx { + if !stream_wants_to_tx && !packet_wants_to_tx { + return None; + } + + // We have something we might send if the channel is free + match self.next_csma_check { + None => { + if self.dcd { + self.next_csma_check = Some(self.now + 1920); + return None; + } else { + // channel is idle at the moment we get a frame to send + // go right ahead + } + } + Some(at_time) => { + if self.now < at_time { + return None; + } + // 25% chance that we'll transmit this slot. + // Using self.now as random is probably fine so long as it's not being set in + // a lumpy manner. m17app's soundmodem should be fine. + // TODO: bring in prng to help in cases where `now` never ends in 0b11 + let p1_4 = (self.now & 3) == 3; + if !self.dcd || !p1_4 { + self.next_csma_check = Some(self.now + 1920); + return None; + } else { + self.next_csma_check = None; + } + } + } + + if stream_wants_to_tx { self.state = State::TxStream; - } else if channel_free && packet_wants_to_tx { - self.state = State::TxPacket; } else { - return None; + self.state = State::TxPacket; } self.ptt = true; // TODO: true txdelay @@ -251,7 +285,7 @@ impl SoftTnc { } self.stream_curr = (self.stream_curr + 1) % 8; if frame.end_of_stream { - self.state = State::Idle; + self.state = State::TxStreamSentEndOfStream; } Some(ModulatorFrame::Stream(frame)) } @@ -315,8 +349,86 @@ impl SoftTnc { continue; }; if port == PORT_PACKET_BASIC { + if self.packet_full { + continue; + } + let mut pending = PendingPacket::new(); + pending.app_data[0] = 0x00; // RAW + let Ok(mut len) = kiss_frame.decode_payload(&mut pending.app_data[1..]) else { + continue; + }; + len += 1; // for RAW prefix + let packet_crc = crate::crc::m17_crc(&pending.app_data[0..len]); + pending.app_data[len..len + 2].copy_from_slice(&packet_crc.to_be_bytes()); + pending.app_data_len = len + 2; + pending.lsf = Some(LsfFrame::new_packet( + &Address::Callsign(Callsign(b"M17RT-PKT".clone())), + &Address::Broadcast, + )); + self.packet_queue[self.packet_next] = pending; + self.packet_next = (self.packet_next + 1) % 4; + if self.packet_next == self.packet_curr { + self.packet_full = true; + } } else if port == PORT_PACKET_FULL { + if self.packet_full { + continue; + } + let mut pending = PendingPacket::new(); + let mut payload = [0u8; 855]; + let Ok(len) = kiss_frame.decode_payload(&mut payload) else { + continue; + }; + if len < 33 { + continue; + } + let mut lsf = LsfFrame([0u8; 30]); + lsf.0.copy_from_slice(&payload[0..30]); + if lsf.check_crc() != 0 { + continue; + } + pending.lsf = Some(lsf); + let app_data_len = len - 30; + pending.app_data[0..app_data_len].copy_from_slice(&payload[30..]); + pending.app_data_len = app_data_len; + self.packet_queue[self.packet_next] = pending; + self.packet_next = (self.packet_next + 1) % 4; + if self.packet_next == self.packet_curr { + self.packet_full = true; + } } else if port == PORT_STREAM { + let mut payload = [0u8; 30]; + let Ok(len) = kiss_frame.decode_payload(&mut payload) else { + continue; + }; + if len < 26 { + log::debug!("payload len too short"); + continue; + } + if len == 30 { + let lsf = LsfFrame(payload); + if lsf.check_crc() != 0 { + continue; + } + self.stream_pending_lsf = Some(lsf); + } else { + if self.stream_full { + log::debug!("stream full"); + continue; + } + let frame_num_part = u16::from_be_bytes([payload[6], payload[7]]); + self.stream_queue[self.stream_next] = StreamFrame { + lich_idx: payload[5] >> 5, + lich_part: payload[0..5].try_into().unwrap(), + frame_number: frame_num_part & 0x7fff, + end_of_stream: frame_num_part & 0x8000 > 0, + stream_data: payload[8..24].try_into().unwrap(), + }; + self.stream_next = (self.stream_next + 1) % 8; + if self.stream_next == self.stream_curr { + self.stream_full = true; + } + } } } n @@ -404,6 +516,15 @@ struct PendingPacket { } impl PendingPacket { + fn new() -> Self { + Self { + lsf: None, + app_data: [0u8; 825], + app_data_len: 0, + app_data_transmitted: 0, + } + } + /// Returns next frame, not including preamble or EOT. /// /// False means all data frames have been sent.