pub mod cn0;
pub mod discrim;
pub mod filter;
mod bank;
mod channel;
pub use bank::{replay, ReplayResult, TrackingBank};
pub use channel::{Channel, ChannelInit, EpochOutput};
use self::cn0::BitSyncConfig;
use self::discrim::{DllDiscriminator, FllDiscriminator, PllDiscriminator};
use self::filter::LoopFilter;
use super::Cf64;
use std::f64::consts::TAU;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CarrierLoop {
Pll {
order: u8,
bn_hz: f64,
},
Fll {
order: u8,
bn_hz: f64,
},
FllAssistedPll {
pll_order: u8,
pll_bn_hz: f64,
fll_order: u8,
fll_bn_hz: f64,
},
}
impl CarrierLoop {
pub fn filter(&self) -> Result<LoopFilter, String> {
match *self {
CarrierLoop::Pll { order, bn_hz } => LoopFilter::pll(order, bn_hz),
CarrierLoop::Fll { order, bn_hz } => LoopFilter::fll(order, bn_hz),
CarrierLoop::FllAssistedPll {
pll_order,
pll_bn_hz,
fll_order,
fll_bn_hz,
} => LoopFilter::new(Some((pll_order, pll_bn_hz)), Some((fll_order, fll_bn_hz))),
}
}
pub fn has_pll(&self) -> bool {
!matches!(self, CarrierLoop::Fll { .. })
}
pub fn has_fll(&self) -> bool {
!matches!(self, CarrierLoop::Pll { .. })
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LoopConfig {
pub label: String,
pub coherent_periods: usize,
pub spacing_chips: f64,
pub dll: DllDiscriminator,
pub dll_order: u8,
pub dll_bn_hz: f64,
pub carrier_aiding: bool,
pub carrier: CarrierLoop,
pub pll_discriminator: PllDiscriminator,
pub fll_discriminator: FllDiscriminator,
pub pli_threshold: f64,
pub code_lock_cn0_dbhz: f64,
pub cn0_windows: usize,
pub cn0_window_periods: usize,
pub bit_sync: BitSyncConfig,
}
impl Default for LoopConfig {
fn default() -> Self {
Self {
label: "default".into(),
coherent_periods: 1,
spacing_chips: 0.5,
dll: DllDiscriminator::EarlyMinusLatePower,
dll_order: 1,
dll_bn_hz: 2.0,
carrier_aiding: true,
carrier: CarrierLoop::FllAssistedPll {
pll_order: 2,
pll_bn_hz: 15.0,
fll_order: 1,
fll_bn_hz: 10.0,
},
pll_discriminator: PllDiscriminator::CostasAtan,
fll_discriminator: FllDiscriminator::Atan2,
pli_threshold: 0.8,
code_lock_cn0_dbhz: 26.0,
cn0_windows: 50,
cn0_window_periods: 20,
bit_sync: BitSyncConfig::default(),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Discriminators {
pub pll_rad: f64,
pub fll_hz: f64,
pub dll_chips: f64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LoopCore {
cfg: LoopConfig,
carrier: LoopFilter,
code: LoopFilter,
chip_rate_hz: f64,
carrier_hz: f64,
init_doppler_hz: f64,
doppler_hz: f64,
code_rate_hz: f64,
prev_prompt: Option<Cf64>,
}
impl LoopCore {
pub fn new(
cfg: &LoopConfig,
chip_rate_hz: f64,
carrier_hz: f64,
init_doppler_hz: f64,
) -> Result<Self, String> {
if !(cfg.spacing_chips > 0.0 && cfg.spacing_chips <= 2.0) {
return Err(format!(
"early-late spacing must lie in (0, 2] chips (got {})",
cfg.spacing_chips
));
}
if cfg.dll_order == 0 || cfg.dll_order > 2 {
return Err(format!("DLL order must be 1 or 2 (got {})", cfg.dll_order));
}
if !(chip_rate_hz > 0.0 && carrier_hz > 0.0) {
return Err("chip rate and carrier frequency must be positive".into());
}
let mut s = Self {
cfg: cfg.clone(),
carrier: cfg.carrier.filter()?,
code: LoopFilter::pll(cfg.dll_order, cfg.dll_bn_hz)?,
chip_rate_hz,
carrier_hz,
init_doppler_hz,
doppler_hz: init_doppler_hz,
code_rate_hz: 0.0,
prev_prompt: None,
};
s.code_rate_hz = s.base_code_rate();
Ok(s)
}
fn base_code_rate(&self) -> f64 {
let d = if self.cfg.carrier_aiding {
self.doppler_hz
} else {
self.init_doppler_hz
};
self.chip_rate_hz * (1.0 + d / self.carrier_hz)
}
pub fn update(&mut self, e: Cf64, p: Cf64, l: Cf64, t_s: f64) -> Discriminators {
let pll_rad = if self.cfg.carrier.has_pll() {
self.cfg.pll_discriminator.discriminate(p)
} else {
0.0
};
let fll_hz = match self.prev_prompt {
Some(prev) => self.cfg.fll_discriminator.discriminate(prev, p, t_s),
None => 0.0,
};
let fe = if self.cfg.carrier.has_fll() {
TAU * fll_hz
} else {
0.0
};
let w = self.carrier.update(pll_rad, fe, t_s);
self.doppler_hz = self.init_doppler_hz + w / TAU;
let dll_chips = self.cfg.dll.discriminate(e, p, l, self.cfg.spacing_chips);
let corr = self.code.update(dll_chips, 0.0, t_s);
self.code_rate_hz = self.base_code_rate() + corr;
self.prev_prompt = Some(p);
Discriminators {
pll_rad,
fll_hz,
dll_chips,
}
}
pub fn doppler_hz(&self) -> f64 {
self.doppler_hz
}
pub fn code_rate_hz(&self) -> f64 {
self.code_rate_hz
}
pub fn config(&self) -> &LoopConfig {
&self.cfg
}
}