use super::{
NavData, NoiseConfig, RangeProfile, SatGeometry, Scene, SceneConfig, SceneSatellite, T0_K,
};
use crate::iq::signals::by_name::build_code;
use crate::iq::{SampleSpec, SpreadingCode};
pub(crate) struct SceneParams {
pub(crate) fs_hz: f64,
pub(crate) duration_s: f64,
pub(crate) signal: String,
pub(crate) ids: Vec<i64>,
pub(crate) dopplers: Vec<f64>,
pub(crate) center_hz: Option<f64>,
pub(crate) if_hz: f64,
pub(crate) cn0_dbhz: Option<f64>,
pub(crate) noise_figure_db: f64,
pub(crate) noise: bool,
pub(crate) seed: u64,
pub(crate) data: bool,
pub(crate) threads: usize,
}
pub(crate) fn build_scene(p: &SceneParams) -> Result<Scene, String> {
if p.ids.is_empty() {
return Err("a scene needs at least one satellite (--prn)".into());
}
if !p.dopplers.is_empty() && p.dopplers.len() != 1 && p.dopplers.len() != p.ids.len() {
return Err(format!(
"--doppler lists {} value(s); give one per --prn ({}) or a single value",
p.dopplers.len(),
p.ids.len()
));
}
let codes = p
.ids
.iter()
.map(|&id| build_code(&p.signal, id))
.collect::<Result<Vec<_>, _>>()?;
let center = p.center_hz.unwrap_or_else(|| codes[0].carrier_hz());
let spec = SampleSpec {
fs_hz: p.fs_hz,
center_hz: center,
if_hz: p.if_hz,
};
let mut cfg = SceneConfig::new(spec, p.duration_s);
cfg.seed = p.seed;
cfg.threads = p.threads.max(1);
cfg.noise = if p.noise {
NoiseConfig::from_noise_figure(p.noise_figure_db, T0_K)
} else {
NoiseConfig {
enabled: false,
..NoiseConfig::from_noise_figure(p.noise_figure_db, T0_K)
}
};
let mut scene = Scene::new(cfg).map_err(|e| e.to_string())?;
for (i, (id, code)) in p.ids.iter().zip(codes).enumerate() {
let doppler = match p.dopplers.len() {
0 => 0.0,
1 => p.dopplers[0],
_ => p.dopplers[i],
};
let lambda = crate::iq::C_M_PER_S / code.carrier_hz();
let profile = RangeProfile {
range_m: 2.0e7 + i as f64 * 3.0e5,
range_rate_mps: -doppler * lambda,
range_accel_mps2: 0.0,
elevation_deg: 45.0,
azimuth_deg: 0.0,
};
let nav = if p.data {
NavData::Seeded {
seed: *id as u64 + 1,
}
} else {
NavData::None
};
nav.check_modulation(code.data_modulation())
.map_err(|e| format!("--data: {} {e}", code.name()))?;
scene.add_satellite(SceneSatellite {
id: *id as u32,
code: Box::new(code),
geometry: SatGeometry::Profile(profile),
cn0_dbhz: p.cn0_dbhz,
nav,
});
}
Ok(scene)
}