/// 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<u64>,
+
/// Current monotonic time, counted in samples
now: u64,
outgoing_kiss: None,
state: State::Idle,
dcd: false,
+ next_csma_check: None,
now: 0,
packet_queue: Default::default(),
packet_next: 0,
}
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);
pub fn read_tx_frame(&mut self) -> Option<ModulatorFrame> {
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
}
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))
}
self.packet_full = true;
}
} else if port == PORT_STREAM {
- // TODO: handle port 2
+ 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