pub mod fit;
pub mod model;
pub mod observe;
pub mod optim;
pub mod report;
pub mod schema;
pub mod synth;
pub use fit::{analyse, LabFitReport, LabRun};
pub use model::ModelKind;
pub use schema::LabFitScenario;
use sha2::{Digest, Sha256};
use crate::receiver_trust::ingest::read_log;
use crate::receiver_trust::scenario::load_source;
use crate::receiver_trust::LogFormat;
#[derive(Clone, Debug, PartialEq)]
pub struct LabFitOutput {
pub report: LabFitReport,
pub json: String,
pub residuals_csv: String,
pub predictions_csv: String,
pub markdown: String,
}
fn format_name(f: LogFormat) -> &'static str {
match f {
LogFormat::Ubx => "ubx log",
LogFormat::Rinex => "rinex log",
LogFormat::Android => "android log",
LogFormat::Nmea => "nmea log",
}
}
pub fn load_runs(sc: &LabFitScenario) -> Result<Vec<LabRun>, String> {
sc.runs
.iter()
.map(|r| {
let bytes = load_source(&r.log.source, &format!("run `{}` log", r.label))?;
let timeline =
read_log(r.log.format, &bytes).map_err(|e| format!("run `{}`: {e}", r.label))?;
Ok(LabRun {
label: r.label.clone(),
sha256: format!("{:x}", Sha256::digest(&bytes)),
source: format_name(r.log.format).into(),
timeline,
conditions: r.conditions.clone(),
})
})
.collect()
}
pub fn resolve_paths(sc: &mut LabFitScenario, base: &std::path::Path) {
for r in &mut sc.runs {
if let Some(p) = &r.log.source.path {
let pb = std::path::Path::new(p);
if pb.is_relative() {
r.log.source.path = Some(base.join(pb).to_string_lossy().into_owned());
}
}
}
}
pub fn run_scenario(sc: &LabFitScenario) -> Result<LabFitOutput, String> {
let runs = load_runs(sc)?;
let report = analyse(&runs, sc)?;
Ok(LabFitOutput {
json: report::to_json(&report),
residuals_csv: report::residuals_csv(&report),
predictions_csv: report::predictions_csv(&report),
markdown: report::to_markdown(&report),
report,
})
}
pub fn run_toml(src: &str) -> Result<LabFitOutput, String> {
let sc: LabFitScenario = toml::from_str(src).map_err(|e| format!("iq-labfit scenario: {e}"))?;
run_scenario(&sc)
}