use alloc::vec;
use alloc::vec::Vec;
use core::f32::consts::PI;
#[cfg(not(feature = "std"))]
use num_traits::Float;
use super::WSPR_SYNC_VECTOR;
use super::baseband::BASEBAND_RATE;
pub const TONE_SPACING_HZ: f32 = BASEBAND_RATE / 256.0;
pub const NSPS_BASEBAND: usize = 256;
pub const N_SYMBOLS: usize = 162;
#[derive(Clone, Debug)]
pub struct IsQs {
pub is: [[f32; N_SYMBOLS]; 4],
pub qs: [[f32; N_SYMBOLS]; 4],
pub cf: [[f32; N_SYMBOLS]; 4],
pub sf: [[f32; N_SYMBOLS]; 4],
}
pub fn tone_amplitudes(
idat: &[f32],
qdat: &[f32],
f0_baseband_hz: f32,
lag: i32,
drift_hz: f32,
) -> IsQs {
debug_assert_eq!(idat.len(), qdat.len());
let np = idat.len() as i32;
let dt = 1.0 / BASEBAND_RATE;
let df = TONE_SPACING_HZ;
let twopidt = 2.0 * PI * dt;
let df15 = df * 1.5;
let df05 = df * 0.5;
let mut isqs = IsQs {
is: [[0.0f32; N_SYMBOLS]; 4],
qs: [[0.0f32; N_SYMBOLS]; 4],
cf: [[0.0f32; N_SYMBOLS]; 4],
sf: [[0.0f32; N_SYMBOLS]; 4],
};
let mut c0 = [0.0f32; NSPS_BASEBAND + 1];
let mut s0 = [0.0f32; NSPS_BASEBAND + 1];
let mut c1 = [0.0f32; NSPS_BASEBAND + 1];
let mut s1 = [0.0f32; NSPS_BASEBAND + 1];
let mut c2 = [0.0f32; NSPS_BASEBAND + 1];
let mut s2 = [0.0f32; NSPS_BASEBAND + 1];
let mut c3 = [0.0f32; NSPS_BASEBAND + 1];
let mut s3 = [0.0f32; NSPS_BASEBAND + 1];
for i in 0..N_SYMBOLS {
let fp = f0_baseband_hz + (drift_hz / 2.0) * ((i as f32 - 81.0) / 81.0);
let dphi0 = twopidt * (fp - df15);
let dphi1 = twopidt * (fp - df05);
let dphi2 = twopidt * (fp + df05);
let dphi3 = twopidt * (fp + df15);
let (cdphi0, sdphi0) = (dphi0.cos(), dphi0.sin());
let (cdphi1, sdphi1) = (dphi1.cos(), dphi1.sin());
let (cdphi2, sdphi2) = (dphi2.cos(), dphi2.sin());
let (cdphi3, sdphi3) = (dphi3.cos(), dphi3.sin());
c0[0] = 1.0;
s0[0] = 0.0;
c1[0] = 1.0;
s1[0] = 0.0;
c2[0] = 1.0;
s2[0] = 0.0;
c3[0] = 1.0;
s3[0] = 0.0;
for j in 1..=NSPS_BASEBAND {
c0[j] = c0[j - 1] * cdphi0 - s0[j - 1] * sdphi0;
s0[j] = c0[j - 1] * sdphi0 + s0[j - 1] * cdphi0;
c1[j] = c1[j - 1] * cdphi1 - s1[j - 1] * sdphi1;
s1[j] = c1[j - 1] * sdphi1 + s1[j - 1] * cdphi1;
c2[j] = c2[j - 1] * cdphi2 - s2[j - 1] * sdphi2;
s2[j] = c2[j - 1] * sdphi2 + s2[j - 1] * cdphi2;
c3[j] = c3[j - 1] * cdphi3 - s3[j - 1] * sdphi3;
s3[j] = c3[j - 1] * sdphi3 + s3[j - 1] * cdphi3;
}
isqs.cf[0][i] = c0[NSPS_BASEBAND];
isqs.sf[0][i] = s0[NSPS_BASEBAND];
isqs.cf[1][i] = c1[NSPS_BASEBAND];
isqs.sf[1][i] = s1[NSPS_BASEBAND];
isqs.cf[2][i] = c2[NSPS_BASEBAND];
isqs.sf[2][i] = s2[NSPS_BASEBAND];
isqs.cf[3][i] = c3[NSPS_BASEBAND];
isqs.sf[3][i] = s3[NSPS_BASEBAND];
let mut i0_acc = 0.0f32;
let mut q0_acc = 0.0f32;
let mut i1_acc = 0.0f32;
let mut q1_acc = 0.0f32;
let mut i2_acc = 0.0f32;
let mut q2_acc = 0.0f32;
let mut i3_acc = 0.0f32;
let mut q3_acc = 0.0f32;
for j in 0..NSPS_BASEBAND {
let k = lag + (i as i32) * (NSPS_BASEBAND as i32) + (j as i32);
if k > 0 && k < np {
let id = idat[k as usize];
let qd = qdat[k as usize];
i0_acc += id * c0[j] + qd * s0[j];
q0_acc += -id * s0[j] + qd * c0[j];
i1_acc += id * c1[j] + qd * s1[j];
q1_acc += -id * s1[j] + qd * c1[j];
i2_acc += id * c2[j] + qd * s2[j];
q2_acc += -id * s2[j] + qd * c2[j];
i3_acc += id * c3[j] + qd * s3[j];
q3_acc += -id * s3[j] + qd * c3[j];
}
}
isqs.is[0][i] = i0_acc;
isqs.qs[0][i] = q0_acc;
isqs.is[1][i] = i1_acc;
isqs.qs[1][i] = q1_acc;
isqs.is[2][i] = i2_acc;
isqs.qs[2][i] = q2_acc;
isqs.is[3][i] = i3_acc;
isqs.qs[3][i] = q3_acc;
}
isqs
}
pub fn nblock1_bit_metrics(isqs: &IsQs) -> [f32; N_SYMBOLS] {
let mut bm = [0.0f32; N_SYMBOLS];
for i in 0..N_SYMBOLS {
let sync = WSPR_SYNC_VECTOR[i] as usize;
let t0 = sync; let t1 = sync + 2; let p0 = (isqs.is[t0][i].powi(2) + isqs.qs[t0][i].powi(2)).sqrt();
let p1 = (isqs.is[t1][i].powi(2) + isqs.qs[t1][i].powi(2)).sqrt();
bm[i] = p0 - p1;
}
let n = N_SYMBOLS as f32;
let mean = bm.iter().sum::<f32>() / n;
let mean_sq = bm.iter().map(|x| x * x).sum::<f32>() / n;
let var = mean_sq - mean * mean;
let sig = if var > 0.0 {
var.sqrt()
} else {
mean_sq.sqrt()
};
if sig > 0.0 {
for x in bm.iter_mut() {
*x /= sig;
}
}
const LLR_SCALE: f32 = 2.83;
for x in bm.iter_mut() {
*x *= LLR_SCALE;
}
bm
}
pub fn sync_score_isqs(isqs: &IsQs) -> f32 {
let mut ss = 0.0f32;
let mut pow = 0.0f32;
for i in 0..N_SYMBOLS {
let m: [f32; 4] =
core::array::from_fn(|t| (isqs.is[t][i].powi(2) + isqs.qs[t][i].powi(2)).sqrt());
let pr3 = WSPR_SYNC_VECTOR[i] as f32;
ss += (2.0 * pr3 - 1.0) * ((m[1] + m[3]) - (m[0] + m[2]));
pow += m[0] + m[1] + m[2] + m[3];
}
if pow > 0.0 { ss / pow } else { 0.0 }
}
pub fn nblock_bit_metrics(isqs: &IsQs, nblock: usize) -> [f32; N_SYMBOLS] {
debug_assert!(matches!(nblock, 1..=3));
if nblock == 1 {
return nblock1_bit_metrics(isqs);
}
let nseq = 1usize << nblock;
let mut bm = [0.0f32; N_SYMBOLS];
let mut p = vec![0.0f32; nseq];
let mut i = 0usize;
while i + nblock <= N_SYMBOLS {
for j in 0..nseq {
let mut xi = 0.0f32;
let mut xq = 0.0f32;
let mut cm = 1.0f32;
let mut sm = 0.0f32;
for ib in 0..nblock {
let b = (j >> (nblock - 1 - ib)) & 1;
let itone = WSPR_SYNC_VECTOR[i + ib] as usize + 2 * b;
let is_t = isqs.is[itone][i + ib];
let qs_t = isqs.qs[itone][i + ib];
xi += is_t * cm + qs_t * sm;
xq += qs_t * cm - is_t * sm;
let cf_t = isqs.cf[itone][i + ib];
let sf_t = isqs.sf[itone][i + ib];
let cmp = cf_t * cm - sf_t * sm;
let smp = sf_t * cm + cf_t * sm;
cm = cmp;
sm = smp;
}
p[j] = (xi * xi + xq * xq).sqrt();
}
for ib in 0..nblock {
let imask = 1usize << (nblock - 1 - ib);
let mut xm1 = 0.0f32;
let mut xm0 = 0.0f32;
for j in 0..nseq {
if (j & imask) != 0 {
if p[j] > xm1 {
xm1 = p[j];
}
} else if p[j] > xm0 {
xm0 = p[j];
}
}
bm[i + ib] = xm0 - xm1;
}
i += nblock;
}
while i < N_SYMBOLS {
let sync = WSPR_SYNC_VECTOR[i] as usize;
let p0 = (isqs.is[sync][i].powi(2) + isqs.qs[sync][i].powi(2)).sqrt();
let p1 = (isqs.is[sync + 2][i].powi(2) + isqs.qs[sync + 2][i].powi(2)).sqrt();
bm[i] = p0 - p1;
i += 1;
}
let n = N_SYMBOLS as f32;
let mean = bm.iter().sum::<f32>() / n;
let mean_sq = bm.iter().map(|x| x * x).sum::<f32>() / n;
let var = mean_sq - mean * mean;
let sig = if var > 0.0 {
var.sqrt()
} else {
mean_sq.sqrt()
};
if sig > 0.0 {
for x in bm.iter_mut() {
*x /= sig;
}
}
const LLR_SCALE: f32 = 2.83;
for x in bm.iter_mut() {
*x *= LLR_SCALE;
}
bm
}
pub fn bit_metrics_from_baseband(
idat: &[f32],
qdat: &[f32],
f0_audio_hz: f32,
lag_audio: i32,
drift_hz: f32,
) -> [f32; N_SYMBOLS] {
bit_metrics_from_baseband_nblock(idat, qdat, f0_audio_hz, lag_audio, drift_hz, 1)
}
pub fn bit_metrics_from_baseband_nblock(
idat: &[f32],
qdat: &[f32],
f0_audio_hz: f32,
lag_audio: i32,
drift_hz: f32,
nblock: usize,
) -> [f32; N_SYMBOLS] {
let f0_baseband_hz = f0_audio_hz - super::baseband::CENTER_HZ;
let lag_baseband = lag_audio / 32;
let isqs = tone_amplitudes(idat, qdat, f0_baseband_hz, lag_baseband, drift_hz);
nblock_bit_metrics(&isqs, nblock)
}
pub fn bit_metrics_from_audio(
audio: &[f32],
f0_audio_hz: f32,
lag_audio: i32,
drift_hz: f32,
) -> [f32; N_SYMBOLS] {
let (idat, qdat) = super::baseband::decimate_to_baseband(audio);
bit_metrics_from_baseband(&idat, &qdat, f0_audio_hz, lag_audio, drift_hz)
}
#[allow(dead_code)]
fn _silence_unused_imports() {
let _ = vec![0u8];
let _: Vec<u8> = Vec::new();
}