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
{