nyx_space/od/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/*
Nyx, blazing fast astrodynamics
Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::dynamics::DynamicsError;
pub use crate::dynamics::{Dynamics, NyxError};
use crate::io::{ConfigError, InputOutputError};
use crate::linalg::OVector;
use crate::md::trajectory::TrajError;
use crate::propagators::PropagationError;
use crate::time::Epoch;
pub use crate::{State, TimeTagged};
use anise::almanac::planetary::PlanetaryDataError;
use anise::errors::AlmanacError;
use hifitime::Duration;
use snafu::prelude::Snafu;
use std::sync::Arc;
pub mod filter;
pub use filter::Filter;
/// Provides a range and range rate measuring models.
mod ground_station;
pub use ground_station::GroundStation;
/// Provides Estimate handling functionalities.
pub mod estimate;
/// Provides noise modeling
pub mod noise;
/// Provides all of the support measurement models
pub mod msr;
/// Provides all of the functionality to simulate measurements from ground stations
pub mod simulator;
/// Provides the interfaces to the orbit determination process
pub mod process;
pub use simulator::TrackingDevice;
/// Provides all state noise compensation functionality
pub mod snc;
/// A helper type for spacecraft orbit determination.
pub type SpacecraftODProcess<'a> = self::process::ODProcess<
'a,
crate::md::prelude::SpacecraftDynamics,
nalgebra::Const<2>,
nalgebra::Const<3>,
filter::kalman::KF<crate::Spacecraft, nalgebra::Const<3>, nalgebra::Const<2>>,
GroundStation,
>;
#[allow(unused_imports)]
pub mod prelude {
pub use super::estimate::*;
pub use super::filter::kalman::*;
pub use super::ground_station::*;
pub use super::msr::*;
pub use super::noise::{GaussMarkov, StochasticNoise, WhiteNoise};
pub use super::process::*;
pub use super::simulator::TrackingArcSim;
pub use super::simulator::*;
pub use super::snc::*;
pub use super::*;
pub use crate::time::{Duration, Epoch, TimeUnits, Unit};
}
// /// A trait defining a measurement that can be used in the orbit determination process.
// pub trait Measurement: Copy + TimeTagged {
// /// Defines how much data is measured. For example, if measuring range and range rate, this should be of size 2 (nalgebra::U2).
// type MeasurementSize: DimName;
// /// Returns the fields for this kind of measurement.
// /// The metadata must include a `unit` field with the unit.
// fn fields() -> Vec<Field>;
// /// Initializes a new measurement from the provided data.
// fn from_observation(epoch: Epoch, obs: OVector<f64, Self::MeasurementSize>) -> Self
// where
// DefaultAllocator: Allocator<Self::MeasurementSize>;
// /// Returns the measurement/observation as a vector.
// fn observation(&self) -> OVector<f64, Self::MeasurementSize>
// where
// DefaultAllocator: Allocator<Self::MeasurementSize>;
// }
// /// The Estimate trait defines the interface that is the opposite of a `SolveFor`.
// /// For example, `impl EstimateFrom<Spacecraft> for Orbit` means that the `Orbit` can be estimated (i.e. "solved for") from a `Spacecraft`.
// ///
// /// In the future, there will be a way to estimate ground station biases, for example. This will need a new State that includes both the Spacecraft and
// /// the ground station bias information. Then, the `impl EstimateFrom<SpacecraftAndBias> for OrbitAndBias` will be added, where `OrbitAndBias` is the
// /// new State that includes the orbit and the bias of one ground station.
// pub trait EstimateFrom<O: State, M: Measurement>
// where
// Self: State,
// DefaultAllocator: Allocator<<O as State>::Size>
// + Allocator<<O as State>::VecLength>
// + Allocator<<O as State>::Size, <O as State>::Size>
// + Allocator<Self::Size>
// + Allocator<Self::VecLength>
// + Allocator<Self::Size, Self::Size>,
// {
// /// From the state extract the state to be estimated
// fn extract(from: O) -> Self;
// /// Returns the measurement sensitivity (often referred to as H tilde).
// ///
// /// # Limitations
// /// The transmitter is necessarily an Orbit. This implies that any non-orbit parameter in the estimation vector must be a zero-bias estimator, i.e. it must be assumed that the parameter should be zero.
// /// This is a limitation of the current implementation. It could be fixed by specifying another State like trait in the EstimateFrom trait, significantly adding complexity with little practical use.
// /// To solve for non zero bias parameters, one ought to be able to estimate the _delta_ of that parameter and want that delta to return to zero, thereby becoming a zero-bias estimator.
// fn sensitivity(
// msr: &M,
// receiver: Self,
// transmitter: Orbit,
// ) -> OMatrix<f64, M::MeasurementSize, Self::Size>
// where
// DefaultAllocator: Allocator<M::MeasurementSize, Self::Size>;
// }
// /// A generic implementation of EstimateFrom for any State that is also a Measurement, e.g. if there is a direct observation of the full state.
// /// WARNING: The frame of the full state measurement is _not_ checked to match that of `Self` or of the filtering frame.
// impl<O> EstimateFrom<O, O> for O
// where
// O: State + Measurement,
// Self: State,
// DefaultAllocator: Allocator<<O as State>::Size>
// + Allocator<<O as State>::VecLength>
// + Allocator<<O as State>::Size, <O as State>::Size>
// + Allocator<Self::Size>
// + Allocator<Self::VecLength>
// + Allocator<Self::Size, Self::Size>,
// {
// fn extract(from: O) -> Self {
// from
// }
// fn sensitivity(
// _full_state_msr: &O,
// _receiver: Self,
// _transmitter: Orbit,
// ) -> OMatrix<f64, <O as Measurement>::MeasurementSize, Self::Size>
// where
// DefaultAllocator: Allocator<<O as Measurement>::MeasurementSize, Self::Size>,
// {
// OMatrix::<f64, O::MeasurementSize, Self::Size>::identity()
// }
// }
#[derive(Debug, PartialEq, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum ODError {
#[snafu(display("during an orbit determination, encountered {source}"))]
ODPropError { source: PropagationError },
#[snafu(display("during an orbit determination, encountered {source}"))]
ODDynamicsError { source: DynamicsError },
#[snafu(display("at least {need} measurements required for {action}"))]
TooFewMeasurements { need: usize, action: &'static str },
#[snafu(display("invalid step size: {step}"))]
StepSizeError { step: Duration },
#[snafu(display("filter iteration did not converge in {loops} iterations"))]
Diverged { loops: usize },
#[snafu(display("STM is singular"))]
SingularStateTransitionMatrix,
#[snafu(display("invalid measurement @ {epoch} = {val}"))]
InvalidMeasurement { epoch: Epoch, val: f64 },
#[snafu(display("sensitivity matrix must be updated before this call"))]
SensitivityNotUpdated,
#[snafu(display("Kalman gain is singular"))]
SingularKalmanGain,
#[snafu(display("Noise matrix is singular"))]
SingularNoiseRk,
#[snafu(display("{kind} noise not configured"))]
NoiseNotConfigured { kind: String },
#[snafu(display("measurement sim error: {details}"))]
MeasurementSimError { details: String },
#[snafu(display("during an OD encountered {source}"))]
ODTrajError { source: TrajError },
#[snafu(display("OD failed because {source}"))]
ODConfigError { source: ConfigError },
#[snafu(display("OD failed because of an I/O error: {source}"))]
ODIOError { source: InputOutputError },
#[snafu(display("OD failed due to Almanac: {action} {source}"))]
ODAlmanac {
#[snafu(source(from(AlmanacError, Box::new)))]
source: Box<AlmanacError>,
action: &'static str,
},
#[snafu(display("OD failed due to planetary data in Almanac: {action} {source}"))]
ODPlanetaryData {
#[snafu(source(from(PlanetaryDataError, Box::new)))]
source: Box<PlanetaryDataError>,
action: &'static str,
},
#[snafu(display("not enough residuals to {action}"))]
ODNoResiduals { action: &'static str },
}