Switched to gfsk

This commit is contained in:
2025-10-20 21:25:34 +02:00
parent f5ae204c98
commit 1a13c7b372
2 changed files with 60 additions and 50 deletions

View File

@ -108,71 +108,51 @@ impl SampleSender for WavSampleSender {
}
struct FSKReceiver {
pos_correllator: FIRFilter,
neg_correllator: FIRFilter,
eye_sender: Sender<Vec<f32>>,
matched_lowpass: FIRFilter,
phase_lowpass: FIRFilter,
elg: ELGate,
dc_block: DCBlocker,
last_byte: u8,
frame_constructor: FrameConstructor,
bit_count: Option<u32>,
last_sample: Complex32,
}
impl FSKReceiver {
fn new(eye_sender: Sender<Vec<f32>>) -> Self {
let samples_per_symbol = (SAMPLE_RATE as f32) / (BAUD_RATE as f32);
let correllator_length = samples_per_symbol as usize;
let mut pos_nco = Nco::new(hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32));
let mut neg_nco = Nco::new(hz_to_rad_per_sample(-DEVIATION, SAMPLE_RATE as f32));
let pos_ir = (0..correllator_length).map(|i| {
pos_nco.step();
pos_nco.cexp() * windows::blackmann(i as f32 / correllator_length as f32)
});
let neg_ir = (0..correllator_length).map(|i| {
neg_nco.step();
neg_nco.cexp() * windows::blackmann(i as f32 / correllator_length as f32)
});
let mut pos_correllator = FIRFilter::new(&pos_ir.collect::<Vec<_>>());
let mut neg_correllator = FIRFilter::new(&neg_ir.collect::<Vec<_>>());
pos_correllator.normalize_freq(hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32));
neg_correllator.normalize_freq(hz_to_rad_per_sample(-DEVIATION, SAMPLE_RATE as f32));
let mut matched_lowpass = FIRFilter::new(&vec![
let mut phase_lowpass = FIRFilter::new(&vec![
Complex32::new(1., 0.);
samples_per_symbol as usize / 2
]);
matched_lowpass.normalize_freq(hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32));
phase_lowpass.normalize_dc();
//let mut dc_block = DCBlocker::new(0.999);
let mut dc_block = DCBlocker::new(1.);
//let mut dc_block = DCBlocker::new(1.);
let loop_i = 0.03;
let loop_p = 0.1;
let mut loop_ir = vec![Complex32::new(loop_i, 0.); samples_per_symbol as usize / 2];
loop_ir.push(Complex32::new(loop_p, 0.));
let mut elg = ELGate::new(samples_per_symbol, FIRFilter::new(&loop_ir));
let elg = ELGate::new(samples_per_symbol, FIRFilter::new(&loop_ir));
Self {
//iq_sampler: IQSampler::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32)),
pos_correllator,
neg_correllator,
matched_lowpass,
dc_block,
phase_lowpass,
elg,
last_byte: 0x00u8,
frame_constructor: FrameConstructor::new(),
bit_count: None,
eye_sender,
last_sample: Complex32::new(1., 0.),
}
}
async fn receive(&mut self, iq: Complex32) -> Result<Option<Frame>, FrameConstructionError> {
// Frame reconstruction
let matched =
self.matched_lowpass.next_real(self.dc_block.next_real(
self.pos_correllator.next(iq).mag() - self.neg_correllator.next(iq).mag(),
));
if let Some((bit_sample, eye)) = self.elg.next_eye(matched) {
let dphi = self
.phase_lowpass
.next_real((self.last_sample.conj() * iq).arg());
self.last_sample = iq;
if let Some((bit_sample, eye)) = self.elg.next_eye(dphi) {
let _ = self.eye_sender.send(eye).await;
self.last_byte >>= 1;
self.last_byte |= ((bit_sample > 0.) as u8) << 7;
@ -321,19 +301,47 @@ impl Transceiver {
pub async fn transmit(frame: Frame, samples_sender: &mut Sender<Vec<f32>>) {
let bytes = frame.bytes();
let mut bit_stream = bytes.iter().flat_map(|x| byte_to_bits(*x));
let modulator = BFSKMod::new(
(SAMPLE_RATE as f32 / BAUD_RATE as f32).round() as u32,
hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32),
&mut bit_stream,
);
let data = bytes
.iter()
.flat_map(|x| byte_to_bits(*x))
.collect::<Vec<_>>();
let up_lo = Nco::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32));
let mut samples = vec![];
for (m, up) in modulator.zip(up_lo) {
let sample = m * up;
samples.push(sample.re);
let sample_per_symbols = SAMPLE_RATE / BAUD_RATE;
let bitstream = (0..(bytes.len() * 8 * sample_per_symbols as usize)).map(|i| {
if data[i / sample_per_symbols as usize] {
1.
} else {
-1.
}
});
// Synthesise impulse response
let mut impulse_response =
vec![Complex32::zero(); sample_per_symbols as usize].into_boxed_slice();
for (i, x) in impulse_response.iter_mut().enumerate() {
*x = Complex32::new(
windows::gaussian(0.3, i as f32 / sample_per_symbols as f32),
0.,
);
}
let mut gaussian_filter = FIRFilter::new(&impulse_response);
gaussian_filter.normalize_dc();
let filtered_bitstream = bitstream.map(|x| gaussian_filter.next_real(x));
let mut nco = Nco::new(0.);
let mut lo = Nco::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32));
// Generate passband
let samples = filtered_bitstream
.map(|f| {
nco.set_frequency(hz_to_rad_per_sample(f * DEVIATION, SAMPLE_RATE as f32));
nco.step_n(1);
lo.step_n(1);
(nco.cexp() * lo.cexp()).re
})
.collect::<Vec<_>>();
let len = samples.len();
samples_sender.send(samples).await.unwrap();
tokio::time::sleep(Duration::from_secs_f32(len as f32 / SAMPLE_RATE as f32)).await;