use num_complex::Complex32 as C32;
use crate::sync::psk31_sync::psk31_sync;
use crate::modulate::psk31::{Bpsk31Mod, Qpsk31Mod, PSK31_BAUD};
use crate::demodulate::psk31::{Bpsk31Demod, Bpsk31Decider, Qpsk31Demod, Qpsk31Decider};
use crate::codec::varicode::VaricodeDecoder;
use crate::core::Block;
use super::add_awgn;
const FS: f32 = 8_000.0;
const REF_BW_HZ: f32 = 2_500.0;
const SIG_POWER: f32 = 1.0;
pub fn snr_to_noise_power(snr_db: f32) -> f32 {
SIG_POWER * FS / (REF_BW_HZ * 10.0_f32.powf(snr_db / 10.0))
}
fn try_bpsk31(text: &[u8], snr_db: f32, seed: u64) -> bool {
let base_hz = 900.0_f32;
let carrier_hz = base_hz + 3.0 * PSK31_BAUD;
let sig_iq = Bpsk31Mod::new(FS, carrier_hz, 1.0)
.modulate_text(text, 64, 32);
let sig_len = sig_iq.len();
let total = sig_len + FS as usize;
let mut buf = sig_iq;
buf.resize(total, C32::new(0.0, 0.0));
add_awgn(&mut buf, snr_to_noise_power(snr_db), seed);
let results = psk31_sync(&buf, FS, base_hz, base_hz + 200.0, 4, 3.0, 32, 5);
if results.is_empty() { return false; }
if (results[0].carrier_hz - carrier_hz).abs() > 40.0 { return false; }
let found_hz = results[0].carrier_hz;
let mut demod = Bpsk31Demod::new(FS, found_hz, 1.0);
let max_syms = sig_len / (FS / PSK31_BAUD).round() as usize + 2;
let mut soft = vec![0.0f32; max_syms];
let wr = demod.process(&buf[..sig_len], &mut soft);
soft.truncate(wr.out_written);
let mut bits = vec![0u8; soft.len()];
let dr = Bpsk31Decider::new().process(&soft, &mut bits);
bits.truncate(dr.out_written);
let mut vdec = VaricodeDecoder::new();
for &b in &bits { vdec.push_bit(b); }
vdec.push_bit(0); vdec.push_bit(0);
let mut decoded = Vec::new();
while let Some(c) = vdec.pop_char() { decoded.push(c); }
decoded.windows(text.len()).any(|w| w == text)
}
fn try_qpsk31(text: &[u8], snr_db: f32, seed: u64) -> bool {
let base_hz = 1400.0_f32;
let carrier_hz = base_hz + 2.0 * PSK31_BAUD;
let sig_iq = Qpsk31Mod::new(FS, carrier_hz, 1.0)
.modulate_text(text, 64, 32);
let sig_len = sig_iq.len();
let total = sig_len + FS as usize;
let mut buf = sig_iq;
buf.resize(total, C32::new(0.0, 0.0));
add_awgn(&mut buf, snr_to_noise_power(snr_db), seed);
let results = psk31_sync(&buf, FS, base_hz, base_hz + 200.0, 4, 3.0, 32, 5);
if results.is_empty() { return false; }
let best = results.iter().min_by(|a, b| {
let da = (a.carrier_hz - carrier_hz).abs();
let db = (b.carrier_hz - carrier_hz).abs();
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
}).unwrap();
if (best.carrier_hz - carrier_hz).abs() > 2.0 * PSK31_BAUD { return false; }
let sps = (FS / PSK31_BAUD).round() as usize;
let mut demod = Qpsk31Demod::new(FS, best.carrier_hz, 1.0);
let max_soft = (sig_len / sps + 2) * 2;
let mut soft = vec![0.0f32; max_soft];
let wr = demod.process(&buf[..sig_len], &mut soft);
soft.truncate(wr.out_written);
let mut decider = Qpsk31Decider::new();
decider.process(&soft, &mut vec![]);
let mut decoded_bits = Vec::new();
decider.flush(&mut decoded_bits);
let mut vdec = VaricodeDecoder::new();
for &b in &decoded_bits { vdec.push_bit(b); }
vdec.push_bit(0); vdec.push_bit(0);
let mut decoded = Vec::new();
while let Some(c) = vdec.pop_char() { decoded.push(c); }
decoded.windows(text.len()).any(|w| w == text)
}
#[test]
fn bpsk31_decodes_at_plus_14db_snr_2500hz() {
let snr_db = 14.0_f32;
let text = b"CQ TEST";
let noise_power = snr_to_noise_power(snr_db);
assert!(
try_bpsk31(text, snr_db, 0x1234_5678_9ABC_DEF0),
"BPSK31 failed to decode at +{} dB SNR/2500 Hz (noise_power={:.5})",
snr_db, noise_power
);
}
#[test]
fn qpsk31_decodes_at_plus_32db_snr_2500hz() {
let snr_db = 32.0_f32;
let text = b"CQ TEST";
let noise_power = snr_to_noise_power(snr_db);
assert!(
try_qpsk31(text, snr_db, 0xFEDC_BA98_7654_3210),
"QPSK31 failed to decode at +{} dB SNR/2500 Hz (noise_power={:.5})",
snr_db, noise_power
);
}