2     parse_lsf
, parse_packet
, parse_stream
, sync_burst_correlation
, SyncBurst
, SYNC_THRESHOLD
, 
   5     encode_lsf
, encode_packet
, encode_stream
, generate_end_of_transmission
, generate_preamble
, 
   7 use crate::protocol
::{Frame
, LsfFrame
, PacketFrame
, StreamFrame
}; 
   8 use crate::shaping
::RRC_48K
; 
  11 pub trait Demodulator 
{ 
  12     fn demod(&mut self, sample
: i16) -> Option
<Frame
>; 
  13     fn data_carrier_detect(&self) -> bool
; 
  16 /// Converts a sequence of samples into frames. 
  17 pub struct SoftDemodulator 
{ 
  18     /// Circular buffer of incoming samples for calculating the RRC filtered value 
  19     filter_win
: [i16; 81], 
  20     /// Current position in filter_win 
  22     /// Circular buffer of shaped samples for performing decodes based on the last 192 symbols 
  24     /// Current position in rx_cursor 
  26     /// A position that we are considering decoding due to decent sync 
  27     candidate
: Option
<DecodeCandidate
>, 
  28     /// How many samples have we received? 
  30     /// Remaining samples to ignore so once we already parse a frame we flush it out in full 
  34 impl SoftDemodulator 
{ 
  35     pub fn new() -> Self { 
  37             filter_win
: [0i16; 81], 
  48 impl Demodulator 
for SoftDemodulator 
{ 
  49     fn demod(&mut self, sample
: i16) -> Option
<Frame
> { 
  50         self.filter
_w
in
[self.filter
_c
ursor
] = sample
; 
  51         self.filter
_c
ursor 
= (self.filter
_c
ursor 
+ 1) % 81; 
  52         let mut out
: f32 = 0.0; 
  54             let filter_idx 
= (self.filter
_c
ursor 
+ i
) % 81; 
  55             out 
+= RRC_48K
[i
] * self.filter
_w
in
[filter_idx
] as f32; 
  58         self.rx_win
[self.rx_cursor
] = out
; 
  59         self.rx_cursor 
= (self.rx_cursor 
+ 1) % 1920; 
  63         if self.suppress 
> 0 { 
  68         let mut burst_window 
= [0f32; 71]; 
  70             let c 
= (self.rx_cursor 
+ i
) % 1920; 
  71             burst_window
[i
] = self.rx_win
[c
]; 
  80             let (diff
, max
, shift
) = sync_burst_correlation(burst
.target(), &burst_window
); 
  81             if diff 
< SYNC_THRESHOLD 
{ 
  82                 let mut new_candidate 
= true; 
  83                 if let Some(c
) = self.candidate
.as_mut() { 
  86                         new_candidate 
= false; 
  90                     self.candidate 
= Some(DecodeCandidate 
{ 
  99             if diff 
>= SYNC_THRESHOLD
 
 103                     .map(|c
| c
.burst 
== burst
) 
 106                 if let Some(c
) = self.candidate
.take() { 
 107                     let start_idx 
= self.rx_cursor 
+ 1920 - (c
.age 
as usize); 
 108                     let start_sample 
= self.sample 
- c
.age 
as u64; 
 109                     let mut pkt_samples 
= [0f32; 192]; 
 111                         let rx_idx 
= (start_idx 
+ i 
* 10) % 1920; 
 112                         pkt_samples
[i
] = (self.rx_win
[rx_idx
] - c
.shift
) / c
.gain
; 
 117                                 "Found LSF at sample {} diff {} max {} shift {}", 
 118                                 start_sample
, c
.diff
, c
.gain
, c
.shift
 
 120                             if let Some(frame
) = parse_lsf(&pkt_samples
) { 
 121                                 self.suppress 
= 191 * 10; 
 122                                 return Some(Frame
::Lsf(frame
)); 
 126                             debug
!("Found BERT at sample {} diff {}", start_sample
, c
.diff
); 
 128                         SyncBurst
::Stream 
=> { 
 130                                 "Found STREAM at sample {} diff {} max {} shift {}", 
 131                                 start_sample
, c
.diff
, c
.gain
, c
.shift
 
 133                             if let Some(frame
) = parse_stream(&pkt_samples
) { 
 134                                 self.suppress 
= 191 * 10; 
 135                                 return Some(Frame
::Stream(frame
)); 
 138                         SyncBurst
::Packet 
=> { 
 139                             debug
!("Found PACKET at sample {} diff {}", start_sample
, c
.diff
); 
 140                             if let Some(frame
) = parse_packet(&pkt_samples
) { 
 141                                 self.suppress 
= 191 * 10; 
 142                                 return Some(Frame
::Packet(frame
)); 
 153     fn data_carrier_detect(&self) -> bool 
{ 
 158 impl Default 
for SoftDemodulator 
{ 
 159     fn default() -> Self { 
 164 pub trait Modulator 
{ 
 165     /// Inform the modulator how many samples remain pending for output and latency updates. 
 167     /// For the buffer between `Modulator` and the process which is supplying samples to the 
 168     /// output sound card, `samples_to_play` is the number of bytes which the modulator has 
 169     /// provided that have not yet been picked up, and `capacity` is the maximum size we can 
 170     /// fill this particular buffer, i.e., maximum number of samples. 
 172     /// Furthermore we attempt to track and account for the latency between the output 
 173     /// soundcard callback, and when those samples will actually be on the wire. CPAL helpfully 
 174     /// gives us an estimate. The latest estimate of latency is converted to a duration in terms 
 175     /// of number of samples and provided as `output_latency`. Added to this is the current 
 176     /// number of samples we expect remain to be processed from the last read. 
 178     /// Call this whenever bytes have been read out of the buffer. 
 179     fn update_output_buffer( 
 181         samples_to_play
: usize, 
 183         output_latency
: usize, 
 186     /// Supply the next frame available from the TNC, if it was requested. 
 187     fn provide_next_frame(&mut self, frame
: Option
<ModulatorFrame
>); 
 189     /// Calculate and write out output samples for the soundcard. 
 191     /// Returns the number of bytes valid in `out`. Should generally be called in a loop until 
 193     fn read_output_samples(&mut self, out
: &mut [i16]) -> usize; 
 195     /// Run the modulator and receive actions to process. 
 197     /// Should be called in a loop until it returns `None`. 
 198     fn run(&mut self) -> Option
<ModulatorAction
>; 
 201 pub enum ModulatorAction 
{ 
 202     /// If true, once all samples have been exhausted output should revert to equilibrium. 
 204     /// If false, failure to pick up enough samples for output sound card is an underrun error. 
 207     /// Check with the TNC if there is a frame available for transmission. 
 209     /// Call `next_frame()` with either the next frame, or `None` if TNC has nothing more to offer. 
 212     /// Modulator wishes to send samples to the output buffer - call `read_output_samples`. 
 215     /// Advise the TNC that we will complete sending End Of Transmission after the given number of 
 216     /// samples has elapsed, and therefore PTT should be deasserted at this time. 
 217     TransmissionWillEnd(usize), 
 220 /// Frames for transmission, emitted by the TNC and received by the Modulator. 
 222 /// The TNC is responsible for all timing decisions, making sure these frames are emitted in the 
 223 /// correct order, breaks between transmissions, PTT and CSMA. If the modulator is given a 
 224 /// `ModulatorFrame` value, its job is to transmit it immediately by modulating it into the output 
 225 /// buffer, or otherwise directly after any previously-supplied frames. 
 227 /// The modulator controls the rate at which frames are drawn out of the TNC. Therefore if the send 
 228 /// rate is too high (or there is too much channel activity) then the effect of this backpressure is 
 229 /// that the TNC's internal queues will overflow and it will either discard earlier frames in the 
 230 /// current stream, or some packets awaiting transmission. 
 231 pub enum ModulatorFrame 
{ 
 233         /// TNC's configured TxDelay setting, increments of 10ms. 
 235         /// TNC fires PTT and it's up to modulator to apply the setting, taking advantage of whatever 
 236         /// buffering already exists in the sound card to reduce the artificial delay. 
 246 pub struct SoftModulator 
{ 
 247     // TODO: 2000 was overflowing around EOT, track down why 
 248     /// Next modulated frame to output - 1920 samples for 40ms frame plus 80 for ramp-down 
 249     next_transmission
: [i16; 4000], 
 250     /// How much of next_transmission should in fact be transmitted 
 252     /// How much of next_transmission has been read out 
 254     /// How many pending zero samples to emit to align start of preamble with PTT taking effect 
 255     tx_delay_padding
: usize, 
 257     /// Do we need to update idle state? 
 259     /// What is that idle status? 
 262     /// Do we need to calculate a transmission end time? 
 264     /// (True after we encoded an EOT.) We will wait until we get a precise timing update. 
 265     calculate_tx_end
: bool
, 
 266     /// Do we need to report a transmission end time? 
 268     /// This is a duration expressed in number of samples. 
 269     report_tx_end
: Option
<usize>, 
 271     /// Circular buffer of most recently output samples for calculating the RRC filtered value. 
 273     /// This should naturally degrade to an oldest value plus 80 zeroes after an EOT. 
 274     filter_win
: [f32; 81], 
 275     /// Current position in filter_win 
 276     filter_cursor
: usize, 
 278     /// Should we ask the TNC for another frame. True after each call to update_output_buffer. 
 281     /// Expected delay beyond the buffer to reach the DAC 
 282     output_latency
: usize, 
 283     /// Number of samples we have placed in the buffer for the output soundcard not yet picked up. 
 284     samples_in_buf
: usize, 
 285     /// Total size to which the output buffer is allowed to expand. 
 290     pub fn new() -> Self { 
 292             next_transmission
: [0i16; 4000], 
 298             calculate_tx_end
: false, 
 300             filter_win
: [0f32; 81], 
 302             try_get_frame
: false, 
 309     fn push_sample(&mut self, dibit
: f32) { 
 310         // TODO: 48 kHz assumption again 
 312             // Right now we are encoding everything as 1.0-scaled dibit floats 
 313             // This is a bit silly but it will do for a minute 
 314             // Max possible gain from the RRC filter with upsampling is about 0.462 
 315             // Let's bump everything to a baseline of 16383 / 0.462 = 35461 
 316             // For normal signals this yields roughly 0.5 magnitude which is plenty 
 318                 self.filter
_w
in
[self.filter
_c
ursor
] = dibit 
* 35461.0; 
 320                 self.filter
_w
in
[self.filter
_c
ursor
] = 0.0; 
 322             self.filter
_c
ursor 
= (self.filter
_c
ursor 
+ 1) % 81; 
 323             let mut out
: f32 = 0.0; 
 325                 let filter_idx 
= (self.filter
_c
ursor 
+ i
) % 81; 
 326                 out 
+= RRC_48K
[i
] * self.filter
_w
in
[filter_idx
]; 
 328             self.next_transmission
[self.next_len
] = out 
as i16; 
 333     fn request_frame_if_space(&mut self) { 
 334         if self.buf_capacity 
- self.samples_in_buf 
>= 2000 { 
 335             self.try_get_frame 
= true; 
 340 impl Modulator 
for SoftModulator 
{ 
 341     fn update_output_buffer( 
 343         samples_to_play
: usize, 
 345         output_latency
: usize, 
 347         //log::debug!("modulator update_output_buffer {samples_to_play} {capacity} {output_latency}"); 
 348         self.output_latency 
= output_latency
; 
 349         self.buf_capacity 
= capacity
; 
 350         self.samples_in_buf 
= samples_to_play
; 
 352         if self.calculate_tx_end 
{ 
 353             self.calculate_tx_end 
= false; 
 354             // next_transmission should already have been read out to the buffer by now 
 355             // so we don't have to consider it 
 356             self.report_tx_end 
= Some(self.samples_in_buf 
+ self.output_latency
); 
 359         self.request_frame_if_space(); 
 362     fn provide_next_frame(&mut self, frame
: Option
<ModulatorFrame
>) { 
 363         let Some(frame
) = frame 
else { 
 364             self.try_get_frame 
= false; 
 372             ModulatorFrame
::Preamble 
{ tx_delay 
} => { 
 373                 // TODO: Stop assuming 48 kHz everywhere. 24 kHz should be fine too. 
 374                 let tx_delay_samples 
= tx_delay 
as usize * 480; 
 375                 // TxDelay and output latency have the same effect - account for whichever is bigger. 
 376                 // We want our sound card DAC hitting preamble right when PTT fully engages. 
 377                 // The modulator calls the shots here - TNC hands over Preamble and asserts PTT, then 
 378                 // waits to be told when transmission will be complete. This estimate will not be 
 379                 // made and delivered until we generate the EOT frame. 
 380                 self.tx_delay_padding 
= tx_delay_samples
.max(self.output_latency
); 
 382                 // We should be starting from a filter_win of zeroes 
 383                 // Transmission is effectively smeared by 80 taps and we'll capture that in EOT 
 384                 for dibit 
in generate_preamble() { 
 385                     self.push_sample(dibit
); 
 388             ModulatorFrame
::Lsf(lsf_frame
) => { 
 389                 for dibit 
in encode_lsf(&lsf_frame
) { 
 390                     self.push_sample(dibit
); 
 393             ModulatorFrame
::Stream(stream_frame
) => { 
 394                 for dibit 
in encode_stream(&stream_frame
) { 
 395                     self.push_sample(dibit
); 
 398             ModulatorFrame
::Packet(packet_frame
) => { 
 399                 for dibit 
in encode_packet(&packet_frame
) { 
 400                     self.push_sample(dibit
); 
 403             ModulatorFrame
::EndOfTransmission 
=> { 
 404                 for dibit 
in generate_end_of_transmission() { 
 405                     self.push_sample(dibit
); 
 408                     // This is not a real symbol value 
 409                     // However we want to flush the filter 
 410                     self.push_sample(0f32); 
 412                 self.calculate_tx_end 
= true; 
 417     fn read_output_samples(&mut self, out
: &mut [i16]) -> usize { 
 420         // if we have pre-TX padding to accommodate TxDelay then expend that first 
 421         if self.tx_delay_padding 
> 0 { 
 422             let len 
= out
.len().min(self.tx_delay_padding
); 
 423             self.tx_delay_padding 
-= len
; 
 430         // then follow it with whatever might be left in next_transmission 
 431         let next_remaining 
= self.next_len 
- self.next_read
; 
 432         if next_remaining 
> 0 { 
 433             let len 
= (out
.len() - written
).min(next_remaining
); 
 434             out
[written
..(written 
+ len
)] 
 435                 .copy_from_slice(&self.next_transmission
[self.next_read
..(self.next_read 
+ len
)]); 
 436             self.next_read 
+= len
; 
 443     fn run(&mut self) -> Option
<ModulatorAction
> { 
 444         // Time-sensitive for accuracy, so handle it first 
 445         if let Some(end
) = self.report_tx_end
.take() { 
 446             return Some(ModulatorAction
::TransmissionWillEnd(end
)); 
 449         if self.next_read 
< self.next_len 
{ 
 450             return Some(ModulatorAction
::ReadOutput
); 
 453         if self.update
_idle 
{ 
 454             self.update
_idle 
= false; 
 455             return Some(ModulatorAction
::SetIdle(self.idle
)); 
 458         if self.try_get_frame 
{ 
 459             return Some(ModulatorAction
::GetNextFrame
); 
 467 pub(crate) struct DecodeCandidate 
{