Skip to main content

winds_engine/
lib.rs

1//! Deterministic wind fields and radiosonde validation for trajectory
2//! simulation, consumed by rockets-engine.
3//!
4//! Everything here is stateless and reproducible from inputs: no network, no
5//! wall clock, no unseeded randomness. The [`WindField`] trait, the profile
6//! types and the analytic profiles are the staging ground for the modeling
7//! core planned for ballistics-engine's `wind` module (see
8//! `docs/winds-engine-plan.md`); when that kernel release lands they move
9//! there unchanged and this crate re-exports them.
10//!
11//! - [`field`]: the [`WindField`] trait, [`ConstantField`], [`ProfileField`].
12//! - [`analytic`]: [`LogProfile`](analytic::LogProfile) and
13//!   [`EkmanProfile`](analytic::EkmanProfile) boundary-layer models.
14//! - [`sounding`]: radiosonde interchange-format parsing.
15//! - [`validate`]: profile-vs-sounding comparison metrics.
16
17pub mod analytic;
18pub mod field;
19pub mod sounding;
20pub mod validate;
21
22pub use field::{ConstantField, ProfileField, ProfileLevel, Wind, WindField};
23pub use sounding::{Sounding, SoundingLevel};
24pub use validate::{LayerError, ValidationReport};
25
26/// Crate-wide error: invalid input with a field name and reason.
27#[derive(Debug, Clone, PartialEq)]
28pub struct Error {
29    /// The offending input or field.
30    pub field: &'static str,
31    /// Why it was rejected.
32    pub reason: String,
33}
34
35impl Error {
36    pub(crate) fn invalid(field: &'static str, reason: impl Into<String>) -> Self {
37        Self {
38            field,
39            reason: reason.into(),
40        }
41    }
42}
43
44impl std::fmt::Display for Error {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{}: {}", self.field, self.reason)
47    }
48}
49
50impl std::error::Error for Error {}
51
52/// Crate-wide result.
53pub type Result<T> = std::result::Result<T, Error>;