]>
code.octet-stream.net Git - m17rt/blob - m17core/src/modem.rs
2 parse_lsf
, parse_packet
, parse_stream
, sync_burst_correlation
, SyncBurst
, SYNC_THRESHOLD
,
4 use crate::protocol
::{Frame
, LsfFrame
, PacketFrame
, StreamFrame
};
5 use crate::shaping
::RRC_48K
;
8 pub trait Demodulator
{
9 fn demod(&mut self, sample
: i16) -> Option
<Frame
>;
10 fn data_carrier_detect(&self) -> bool
;
13 /// Converts a sequence of samples into frames.
14 pub struct SoftDemodulator
{
15 /// Circular buffer of incoming samples for calculating the RRC filtered value
16 filter_win
: [i16; 81],
17 /// Current position in filter_win
19 /// Circular buffer of shaped samples for performing decodes based on the last 192 symbols
21 /// Current position in rx_cursor
23 /// A position that we are considering decoding due to decent sync
24 candidate
: Option
<DecodeCandidate
>,
25 /// How many samples have we received?
27 /// Remaining samples to ignore so once we already parse a frame we flush it out in full
31 impl SoftDemodulator
{
32 pub fn new() -> Self {
34 filter_win
: [0i16; 81],
45 impl Demodulator
for SoftDemodulator
{
46 fn demod(&mut self, sample
: i16) -> Option
<Frame
> {
47 self.filter
_w
in
[self.filter
_c
ursor
] = sample
;
48 self.filter
_c
ursor
= (self.filter
_c
ursor
+ 1) % 81;
49 let mut out
: f32 = 0.0;
51 let filter_idx
= (self.filter
_c
ursor
+ i
) % 81;
52 out
+= RRC_48K
[i
] * self.filter
_w
in
[filter_idx
] as f32;
55 self.rx_win
[self.rx_cursor
] = out
;
56 self.rx_cursor
= (self.rx_cursor
+ 1) % 1920;
60 if self.suppress
> 0 {
65 let mut burst_window
= [0f32; 71];
67 let c
= (self.rx_cursor
+ i
) % 1920;
68 burst_window
[i
] = self.rx_win
[c
];
77 let (diff
, max
, shift
) = sync_burst_correlation(burst
.target(), &burst_window
);
78 if diff
< SYNC_THRESHOLD
{
79 let mut new_candidate
= true;
80 if let Some(c
) = self.candidate
.as_mut() {
83 new_candidate
= false;
87 self.candidate
= Some(DecodeCandidate
{
96 if diff
>= SYNC_THRESHOLD
100 .map(|c
| c
.burst
== burst
)
103 if let Some(c
) = self.candidate
.take() {
104 let start_idx
= self.rx_cursor
+ 1920 - (c
.age
as usize);
105 let start_sample
= self.sample
- c
.age
as u64;
106 let mut pkt_samples
= [0f32; 192];
108 let rx_idx
= (start_idx
+ i
* 10) % 1920;
109 pkt_samples
[i
] = (self.rx_win
[rx_idx
] - c
.shift
) / c
.gain
;
114 "Found LSF at sample {} diff {} max {} shift {}",
115 start_sample
, c
.diff
, c
.gain
, c
.shift
117 if let Some(frame
) = parse_lsf(&pkt_samples
) {
118 self.suppress
= 191 * 10;
119 return Some(Frame
::Lsf(frame
));
123 debug
!("Found BERT at sample {} diff {}", start_sample
, c
.diff
);
125 SyncBurst
::Stream
=> {
127 "Found STREAM at sample {} diff {} max {} shift {}",
128 start_sample
, c
.diff
, c
.gain
, c
.shift
130 if let Some(frame
) = parse_stream(&pkt_samples
) {
131 self.suppress
= 191 * 10;
132 return Some(Frame
::Stream(frame
));
135 SyncBurst
::Packet
=> {
136 debug
!("Found PACKET at sample {} diff {}", start_sample
, c
.diff
);
137 if let Some(frame
) = parse_packet(&pkt_samples
) {
138 self.suppress
= 191 * 10;
139 return Some(Frame
::Packet(frame
));
150 fn data_carrier_detect(&self) -> bool
{
155 impl Default
for SoftDemodulator
{
156 fn default() -> Self {
161 pub trait Modulator
{
162 /// Inform the modulator how many samples remain pending for output and latency updates.
164 /// For the buffer between `Modulator` and the process which is supplying samples to the
165 /// output sound card, `samples_to_play` is the number of bytes which the modulator has
166 /// provided that have not yet been picked up, and `capacity` is the maximum size we can
167 /// fill this particular buffer, i.e., maximum number of samples.
169 /// Furthermore we attempt to track and account for the latency between the output
170 /// soundcard callback, and when those samples will actually be on the wire. CPAL helpfully
171 /// gives us an estimate. The latest estimate of latency is converted to a duration in terms
172 /// of number of samples and provided as `output_latency`.
174 /// Finally, we give the modem a monotonic timer expressed in the number of samples of time
175 /// that have elapsed since the modem began operation. When the modulator advises the TNC
176 /// exactly when a transmission is expected to complete, it should be expressed in terms of
179 /// Call this whenever bytes have been read out of the buffer, or the TNC may have something
181 fn update_output_buffer(
183 samples_to_play
: usize,
185 output_latency
: usize,
189 /// Supply the next frame available from the TNC, if it was requested.
190 fn next_frame(&mut self, frame
: Option
<ModulatorFrame
>);
192 /// Calculate and write out output samples for the soundcard.
194 /// Returns the number of bytes valid in `out`. Should generally be called in a loop until
196 fn read_output_samples(&mut self, out
: &mut [i16]) -> usize;
198 /// Run the modulator and receive actions to process.
200 /// Should be called in a loop until it returns `None`.
201 fn run(&mut self) -> Option
<ModulatorAction
>;
204 pub enum ModulatorAction
{
205 /// If true, once all samples have been exhausted output should revert to equilibrium.
207 /// If false, failure to pick up enough samples for output sound card is an underrun error.
210 /// Check with the TNC if there is a frame available for transmission.
212 /// Call `next_frame()` with either the next frame, or `None` if TNC has nothing more to offer.
215 /// Modulator wishes to send samples to the output buffer - call `read_output_samples`.
218 /// Advise the TNC that we will complete sending End Of Transmission at the given time and
219 TransmissionWillEnd(u64),
222 /// Frames for transmission, emitted by the TNC and received by the Modulator.
224 /// The TNC is responsible for all timing decisions, making sure these frames are emitted in the
225 /// correct order, breaks between transmissions, PTT and CSMA. If the modulator is given a
226 /// `ModulatorFrame` value, its job is to transmit it immediately by modulating it into the output
227 /// buffer, or otherwise directly after any previously-supplied frames.
229 /// The modulator controls the rate at which frames are drawn out of the TNC. Therefore if the send
230 /// rate is too high (or there is too much channel activity) then the effect of this backpressure is
231 /// that the TNC's internal queues will overflow and it will either discard earlier frames in the
232 /// current stream, or some packets awaiting transmission.
233 pub enum ModulatorFrame
{
235 /// TNC's configured TxDelay setting, increments of 10ms.
237 /// TNC fires PTT and it's up to modulator to apply the setting, taking advantage of whatever
238 /// buffering already exists in the sound card to reduce the artificial delay.
248 struct SoftModulator
{
249 /// Next modulated frame to output - 1920 samples for 40ms frame plus 80 for ramp-up/ramp-down
250 next_transmission
: [i16; 2000],
251 /// How much of next_transmission should in fact be transmitted
253 /// How much of next_transmission has been read out
255 /// How many pending zero samples to emit to align start of preamble with PTT taking effect
256 tx_delay_padding
: usize,
258 /// Do we need to update idle state?
260 /// What is that idle status?
263 /// Do we need to report that a transmission end time? (True after we encoded an EOT)
265 /// What will that end time be?
268 /// Circular buffer of incoming samples for calculating the RRC filtered value
269 filter_win
: [i16; 81],
270 /// Current position in filter_win
271 filter_cursor
: usize,
273 /// Should we ask the TNC for another frame. True after each call to update_output_buffer.
277 output_latency
: usize,
278 samples_in_buf
: usize,
282 impl Modulator
for SoftModulator
{
283 fn update_output_buffer(
285 samples_to_play
: usize,
287 output_latency
: usize,
290 self.now
= now_samples
;
291 self.output_latency
= output_latency
;
292 self.buf_capacity
= capacity
;
293 self.samples_in_buf
= samples_to_play
;
295 if capacity
- samples_to_play
>= 2000 {
296 self.try_get_frame
= true;
300 fn next_frame(&mut self, frame
: Option
<ModulatorFrame
>) {
304 self.try_get_frame
= false;
308 // this is where we write it to next_transmission
310 // this is where we set report_tx_end
314 fn read_output_samples(&mut self, out
: &mut [i16]) -> usize {
317 // if we have pre-TX padding to accommodate TxDelay then expend that first
318 if self.tx_delay_padding
> 0 {
319 let len
= out
.len().max(self.tx_delay_padding
);
320 self.tx_delay_padding
-= len
;
327 // then follow it with whatever might be left in next_transmission
328 let next_remaining
= self.next_len
- self.next_read
;
329 if next_remaining
> 0 {
330 let len
= (out
.len() - written
).max(next_remaining
);
331 out
[written
..(written
+ len
)]
332 .copy_from_slice(&self.next_transmission
[self.next_read
..(self.next_read
+ len
)]);
333 self.next_read
+= len
;
340 fn run(&mut self) -> Option
<ModulatorAction
> {
341 if self.next_read
< self.next_len
{
342 return Some(ModulatorAction
::ReadOutput
);
345 if self.update
_idle
{
346 self.update
_idle
= false;
347 return Some(ModulatorAction
::SetIdle(self.idle
));
350 if self.report_tx_end
{
351 self.report_tx_end
= false;
352 return Some(ModulatorAction
::TransmissionWillEnd(self.tx_end_time
));
355 if self.try_get_frame
{
356 return Some(ModulatorAction
::GetNextFrame
);
364 pub(crate) struct DecodeCandidate
{