use crate::trajectories::mpc_80col_reader::ParseObsError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum OutfitError {
#[error("Invalid JPL string format: {0}")]
InvalidJPLStringFormat(String),
#[error("Invalid JPL ephemeris file source: {0}")]
InvalidJPLEphemFileSource(String),
#[error("Invalid JPL ephemeris file version: {0}")]
InvalidJPLEphemFileVersion(String),
#[error("Invalid URL string: {0}")]
InvalidUrl(String),
#[error("HTTP request failed (ureq): {0}")]
UreqHttpError(#[from] ureq::Error),
#[error("Filesystem I/O error: {0}")]
IoError(#[from] std::io::Error),
#[cfg(feature = "jpl-download")]
#[error("HTTP request failed (reqwest): {0}")]
ReqwestError(#[from] reqwest::Error),
#[error("Failed to create base directory for JPL ephemeris file: {0}")]
UnableToCreateBaseDir(String),
#[error("Filesystem path is not valid UTF-8: {0}")]
Utf8PathError(String),
#[error("JPL ephemeris file not found: {0}")]
JPLFileNotFound(String),
#[error("Numerical root finding failed: {0}")]
RootFindingError(#[from] roots::SearchError),
#[error("Observation not found at index: {0}")]
ObservationNotFound(usize),
#[error("Invalid error model identifier: {0}")]
InvalidErrorModel(String),
#[error("Invalid error model file path: {0}")]
InvalidErrorModelFilePath(String),
#[error("Parsing error (nom): {0}")]
NomParsingError(String),
#[error("Parsing error in 80-column observation file: {0}")]
Parsing80ColumnFileError(ParseObsError),
#[error("Gaussian noise generation failed: {0:?}")]
NoiseInjectionError(rand_distr::NormalError),
#[error("Singular direction matrix (cannot invert); observations may be coplanar")]
SingularDirectionMatrix,
#[error("Polynomial root finding failed (Aberth–Ehrlich method did not converge)")]
PolynomialRootFindingFailed,
#[error("Spurious root detected (negative or near-zero geocentric distance)")]
SpuriousRootDetected,
#[error("Initial orbit determination (Gauss method) failed to find valid roots")]
GaussNoRootsFound,
#[error("Invalid SPK segment data type: {0}")]
InvalidSpkDataType(i32),
#[error("Invalid parameter for initial orbit determination: {0}")]
InvalidIODParameter(String),
#[error("Invalid reference system: {0}")]
InvalidRefSystem(String),
#[error("Velocity correction procedure failed: {0}")]
VelocityCorrectionError(String),
#[error("Invalid orbital state or inconsistent elements: {0}")]
InvalidOrbit(String),
#[error("Invalid input conversion: {0}")]
InvalidConversion(String),
#[error("Invalid floating-point value (NaN encountered): {0}")]
InvalidFloatValue(ordered_float::FloatIsNan),
#[error("RMS computation failed: {0}")]
RmsComputationFailed(String),
#[error("Gauss preliminary orbit determination failed: {0}")]
GaussPrelimOrbitFailed(String),
#[error(transparent)]
Parquet(#[from] parquet::errors::ParquetError),
#[error("No viable orbit could be determined after {attempts} attempts: {cause}")]
NoViableOrbit {
cause: Box<OutfitError>,
attempts: usize,
},
#[error(
"No feasible triplets (span={span:.6} d, n_obs={n_obs}, dt_min={dt_min}, dt_max={dt_max})"
)]
NoFeasibleTriplets {
span: f64,
n_obs: usize,
dt_min: f64,
dt_max: f64,
},
#[error("Non-finite score encountered: {0}")]
NonFiniteScore(f64),
}
impl From<rand_distr::NormalError> for OutfitError {
fn from(err: rand_distr::NormalError) -> Self {
OutfitError::NoiseInjectionError(err)
}
}
impl From<ordered_float::FloatIsNan> for OutfitError {
fn from(err: ordered_float::FloatIsNan) -> Self {
OutfitError::InvalidFloatValue(err)
}
}
impl PartialEq for OutfitError {
fn eq(&self, other: &Self) -> bool {
use OutfitError::*;
match (self, other) {
(InvalidJPLStringFormat(a), InvalidJPLStringFormat(b)) => a == b,
(InvalidJPLEphemFileSource(a), InvalidJPLEphemFileSource(b)) => a == b,
(InvalidJPLEphemFileVersion(a), InvalidJPLEphemFileVersion(b)) => a == b,
(InvalidUrl(a), InvalidUrl(b)) => a == b,
(UreqHttpError(_), UreqHttpError(_)) => true,
(IoError(_), IoError(_)) => true,
#[cfg(feature = "jpl-download")]
(ReqwestError(_), ReqwestError(_)) => true,
(Parquet(_), Parquet(_)) => true,
(UnableToCreateBaseDir(a), UnableToCreateBaseDir(b)) => a == b,
(Utf8PathError(a), Utf8PathError(b)) => a == b,
(JPLFileNotFound(a), JPLFileNotFound(b)) => a == b,
(RootFindingError(a), RootFindingError(b)) => a == b,
(ObservationNotFound(a), ObservationNotFound(b)) => a == b,
(InvalidErrorModel(a), InvalidErrorModel(b)) => a == b,
(InvalidErrorModelFilePath(a), InvalidErrorModelFilePath(b)) => a == b,
(NomParsingError(a), NomParsingError(b)) => a == b,
(Parsing80ColumnFileError(a), Parsing80ColumnFileError(b)) => a == b,
(NoiseInjectionError(a), NoiseInjectionError(b)) => a == b,
(InvalidSpkDataType(a), InvalidSpkDataType(b)) => a == b,
(InvalidIODParameter(a), InvalidIODParameter(b)) => a == b,
(InvalidRefSystem(a), InvalidRefSystem(b)) => a == b,
(VelocityCorrectionError(a), VelocityCorrectionError(b)) => a == b,
(InvalidOrbit(a), InvalidOrbit(b)) => a == b,
(InvalidConversion(a), InvalidConversion(b)) => a == b,
(InvalidFloatValue(a), InvalidFloatValue(b)) => a == b,
(RmsComputationFailed(a), RmsComputationFailed(b)) => a == b,
(GaussPrelimOrbitFailed(a), GaussPrelimOrbitFailed(b)) => a == b,
(NonFiniteScore(a), NonFiniteScore(b)) => a == b,
(
NoViableOrbit {
cause: a,
attempts: na,
},
NoViableOrbit {
cause: b,
attempts: nb,
},
) => a == b && na == nb,
(
NoFeasibleTriplets {
span: a,
n_obs: na,
dt_min: da,
dt_max: ma,
},
NoFeasibleTriplets {
span: b,
n_obs: nb,
dt_min: db,
dt_max: mb,
},
) => a == b && na == nb && da == db && ma == mb,
(SingularDirectionMatrix, SingularDirectionMatrix) => true,
(PolynomialRootFindingFailed, PolynomialRootFindingFailed) => true,
(SpuriousRootDetected, SpuriousRootDetected) => true,
(GaussNoRootsFound, GaussNoRootsFound) => true,
_ => false,
}
}
}