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 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     /// Next modulated frame to output - 1920 samples for 40ms frame plus 80 for ramp-down 
 248     next_transmission
: [i16; 2000], 
 249     /// How much of next_transmission should in fact be transmitted 
 251     /// How much of next_transmission has been read out 
 253     /// How many pending zero samples to emit to align start of preamble with PTT taking effect 
 254     tx_delay_padding
: usize, 
 256     /// Do we need to update idle state? 
 258     /// What is that idle status? 
 261     /// Do we need to calculate a transmission end time? 
 263     /// (True after we encoded an EOT.) We will wait until we get a precise timing update. 
 264     calculate_tx_end
: bool
, 
 265     /// Do we need to report a transmission end time? 
 267     /// This is a duration expressed in number of samples. 
 268     report_tx_end
: Option
<usize>, 
 270     /// Circular buffer of most recently output samples for calculating the RRC filtered value. 
 272     /// This should naturally degrade to an oldest value plus 80 zeroes after an EOT. 
 273     filter_win
: [f32; 81], 
 274     /// Current position in filter_win 
 275     filter_cursor
: usize, 
 277     /// Should we ask the TNC for another frame. True after each call to update_output_buffer. 
 280     /// Expected delay beyond the buffer to reach the DAC 
 281     output_latency
: usize, 
 282     /// Number of samples we have placed in the buffer for the output soundcard not yet picked up. 
 283     samples_in_buf
: usize, 
 284     /// Total size to which the output buffer is allowed to expand. 
 289     pub fn new() -> Self { 
 291             next_transmission
: [0i16; 2000], 
 297             calculate_tx_end
: false, 
 299             filter_win
: [0f32; 81], 
 301             try_get_frame
: false, 
 308     fn push_sample(&mut self, dibit
: f32) { 
 309         // Right now we are encoding everything as 1.0-scaled dibit floats 
 310         // This is a bit silly but it will do for a minute 
 311         // Max theoretical gain from the RRC filter is 4.328 
 312         // Let's bump everything to a baseline of 16383 / 4.328 = 3785.35 
 313         // This is not particularly high but at least we won't ever hit the top 
 314         self.filter
_w
in
[self.filter
_c
ursor
] = dibit 
* 3785.0; 
 315         self.filter
_c
ursor 
= (self.filter
_c
ursor 
+ 1) % 81; 
 316         let mut out
: f32 = 0.0; 
 318             let filter_idx 
= (self.filter
_c
ursor 
+ i
) % 81; 
 319             out 
+= RRC_48K
[i
] * self.filter
_w
in
[filter_idx
]; 
 321         self.next_transmission
[self.next_len
] = out 
as i16; 
 325     fn request_frame_if_space(&mut self) { 
 326         if self.buf_capacity 
- self.samples_in_buf 
>= 2000 { 
 327             self.try_get_frame 
= true; 
 332 impl Modulator 
for SoftModulator 
{ 
 333     fn update_output_buffer( 
 335         samples_to_play
: usize, 
 337         output_latency
: usize, 
 339         self.output_latency 
= output_latency
; 
 340         self.buf_capacity 
= capacity
; 
 341         self.samples_in_buf 
= samples_to_play
; 
 343         if self.calculate_tx_end 
{ 
 344             self.calculate_tx_end 
= false; 
 345             // next_transmission should already have been read out to the buffer by now 
 346             // so we don't have to consider it 
 347             self.report_tx_end 
= Some(self.samples_in_buf 
+ self.output_latency
); 
 350         self.request_frame_if_space(); 
 353     fn next_frame(&mut self, frame
: Option
<ModulatorFrame
>) { 
 354         let Some(frame
) = frame 
else { 
 355             self.try_get_frame 
= false; 
 363             ModulatorFrame
::Preamble 
{ tx_delay 
} => { 
 364                 // TODO: Stop assuming 48 kHz everywhere. 24 kHz should be fine too. 
 365                 let tx_delay_samples 
= tx_delay 
as usize * 480; 
 366                 // TxDelay and output latency have the same effect - account for whichever is bigger. 
 367                 // We want our sound card DAC hitting preamble right when PTT fully engages. 
 368                 // The modulator calls the shots here - TNC hands over Preamble and asserts PTT, then 
 369                 // waits to be told when transmission will be complete. This estimate will not be 
 370                 // made and delivered until we generate the EOT frame. 
 371                 self.tx_delay_padding 
= tx_delay_samples
.max(self.output_latency
); 
 373                 // We should be starting from a filter_win of zeroes 
 374                 // Transmission is effectively smeared by 80 taps and we'll capture that in EOT 
 375                 for dibit 
in generate_preamble() { 
 376                     self.push_sample(dibit
); 
 379             ModulatorFrame
::Lsf(lsf_frame
) => { 
 380                 for dibit 
in encode_lsf(&lsf_frame
) { 
 381                     self.push_sample(dibit
); 
 384             ModulatorFrame
::Stream(stream_frame
) => { 
 385                 for dibit 
in encode_stream(&stream_frame
) { 
 386                     self.push_sample(dibit
); 
 389             ModulatorFrame
::Packet(packet_frame
) => { 
 390                 for dibit 
in encode_packet(&packet_frame
) { 
 391                     self.push_sample(dibit
); 
 394             ModulatorFrame
::EndOfTransmission 
=> { 
 395                 for dibit 
in generate_end_of_transmission() { 
 396                     self.push_sample(dibit
); 
 399                     // This is not a real symbol value 
 400                     // However we want to flush the filter 
 401                     self.push_sample(0f32); 
 403                 self.calculate_tx_end 
= true; 
 408     fn read_output_samples(&mut self, out
: &mut [i16]) -> usize { 
 411         // if we have pre-TX padding to accommodate TxDelay then expend that first 
 412         if self.tx_delay_padding 
> 0 { 
 413             let len 
= out
.len().max(self.tx_delay_padding
); 
 414             self.tx_delay_padding 
-= len
; 
 421         // then follow it with whatever might be left in next_transmission 
 422         let next_remaining 
= self.next_len 
- self.next_read
; 
 423         if next_remaining 
> 0 { 
 424             let len 
= (out
.len() - written
).max(next_remaining
); 
 425             out
[written
..(written 
+ len
)] 
 426                 .copy_from_slice(&self.next_transmission
[self.next_read
..(self.next_read 
+ len
)]); 
 427             self.next_read 
+= len
; 
 434     fn run(&mut self) -> Option
<ModulatorAction
> { 
 435         // Time-sensitive for accuracy, so handle it first 
 436         if let Some(end
) = self.report_tx_end
.take() { 
 437             return Some(ModulatorAction
::TransmissionWillEnd(end
)); 
 440         if self.next_read 
< self.next_len 
{ 
 441             return Some(ModulatorAction
::ReadOutput
); 
 444         if self.update
_idle 
{ 
 445             self.update
_idle 
= false; 
 446             return Some(ModulatorAction
::SetIdle(self.idle
)); 
 449         if self.try_get_frame 
{ 
 450             return Some(ModulatorAction
::GetNextFrame
); 
 458 pub(crate) struct DecodeCandidate 
{