use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct Signature {
pub source: String,
pub version: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OrbitClass {
Atira,
Aten,
Apollo,
Amor,
MarsCrosser,
MainBelt,
JupiterFamilyComet,
HalleyTypeComet,
EnckeTypeComet,
Comet,
JupiterTrojan,
Centaur,
TransNeptunian,
Asteroid,
ParabolicAsteroid,
HyperbolicAsteroid,
Other(String),
}
impl OrbitClass {
pub fn from_code(code: &str) -> Self {
match code {
"IEO" => OrbitClass::Atira,
"ATE" => OrbitClass::Aten,
"APO" => OrbitClass::Apollo,
"AMO" => OrbitClass::Amor,
"MCA" => OrbitClass::MarsCrosser,
"MBA" => OrbitClass::MainBelt,
"JFC" | "JFc" => OrbitClass::JupiterFamilyComet,
"HTC" => OrbitClass::HalleyTypeComet,
"ETc" => OrbitClass::EnckeTypeComet,
"COM" => OrbitClass::Comet,
"TJN" => OrbitClass::JupiterTrojan,
"CEN" => OrbitClass::Centaur,
"TNO" => OrbitClass::TransNeptunian,
"AST" => OrbitClass::Asteroid,
"PAA" => OrbitClass::ParabolicAsteroid,
"HYA" => OrbitClass::HyperbolicAsteroid,
other => OrbitClass::Other(other.to_string()),
}
}
pub fn as_code(&self) -> &str {
match self {
OrbitClass::Atira => "IEO",
OrbitClass::Aten => "ATE",
OrbitClass::Apollo => "APO",
OrbitClass::Amor => "AMO",
OrbitClass::MarsCrosser => "MCA",
OrbitClass::MainBelt => "MBA",
OrbitClass::JupiterFamilyComet => "JFC",
OrbitClass::HalleyTypeComet => "HTC",
OrbitClass::EnckeTypeComet => "ETc",
OrbitClass::Comet => "COM",
OrbitClass::JupiterTrojan => "TJN",
OrbitClass::Centaur => "CEN",
OrbitClass::TransNeptunian => "TNO",
OrbitClass::Asteroid => "AST",
OrbitClass::ParabolicAsteroid => "PAA",
OrbitClass::HyperbolicAsteroid => "HYA",
OrbitClass::Other(code) => code.as_str(),
}
}
}
#[derive(Debug, Clone)]
pub struct SmallBodyObject {
pub designation: String,
pub spkid: Option<String>,
pub fullname: Option<String>,
pub shortname: Option<String>,
pub kind: Option<String>,
pub neo: bool,
pub pha: bool,
pub orbit_class: Option<OrbitClass>,
}
#[derive(Debug, Clone)]
pub struct SmallBodyOrbit {
pub orbit_id: Option<String>,
pub epoch_jd: Option<f64>,
pub eccentricity: Option<f64>,
pub semi_major_axis: Option<f64>,
pub perihelion_dist: Option<f64>,
pub inclination: Option<f64>,
pub long_asc_node: Option<f64>,
pub arg_perihelion: Option<f64>,
pub mean_anomaly: Option<f64>,
pub time_perihelion: Option<f64>,
pub mean_motion: Option<f64>,
pub period: Option<f64>,
pub aphelion_dist: Option<f64>,
pub moid_au: Option<f64>,
pub first_obs: Option<String>,
pub last_obs: Option<String>,
pub n_obs_used: Option<u32>,
pub data_arc_days: Option<u32>,
pub condition_code: Option<String>,
pub rms: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct PhysicalParams {
pub abs_magnitude_h: Option<f64>,
pub magnitude_slope_g: Option<f64>,
pub diameter_km: Option<f64>,
pub albedo: Option<f64>,
pub rotation_period_h: Option<f64>,
pub spectral_type: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CloseApproachRecord {
pub designation: String,
pub orbit_id: Option<String>,
pub jd_tdb: Option<f64>,
pub date: String,
pub dist_au: f64,
pub dist_min_au: Option<f64>,
pub dist_max_au: Option<f64>,
pub v_rel_km_s: Option<f64>,
pub v_inf_km_s: Option<f64>,
pub h_mag: Option<f64>,
pub diameter_km: Option<f64>,
pub fullname: Option<String>,
pub body: String,
}
#[derive(Debug, Clone)]
pub struct FireballRecord {
pub date: String,
pub energy_joules_e10: Option<f64>,
pub impact_energy_kt: Option<f64>,
pub latitude: Option<f64>,
pub lat_dir: Option<String>,
pub longitude: Option<f64>,
pub lon_dir: Option<String>,
pub altitude_km: Option<f64>,
pub velocity_km_s: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MissionDesignCriterion {
MinDepartureVinf = 1,
MinArrivalVinf = 2,
MinTotalDv = 3,
MinTofMinDepVinf = 4,
MinTofMinArrVinf = 5,
MinTofMinTotalDv = 6,
}
impl MissionDesignCriterion {
pub fn as_api_value(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone)]
pub struct MissionAccessibleParams {
pub crit: MissionDesignCriterion,
pub year: Vec<u32>,
pub lim: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct MissionAccessibleEntry {
pub name: String,
pub pdes: Option<String>,
pub date0: String,
pub mjd0: f64,
pub datef: String,
pub mjdf: f64,
pub c3_dep: f64,
pub vinf_dep: f64,
pub vinf_arr: f64,
pub dv_tot: f64,
pub tof: f64,
pub class: Option<String>,
pub h_mag: Option<f64>,
pub condition_code: Option<String>,
pub neo: bool,
pub pha: bool,
}
#[derive(Debug, Clone)]
pub struct MissionAccessibleResponse {
pub count: u32,
pub data: Vec<MissionAccessibleEntry>,
}
#[derive(Debug, Clone)]
pub struct MissionQueryObject {
pub des: String,
pub fullname: Option<String>,
pub spkid: Option<String>,
pub orbit_class: Option<String>,
pub condition_code: Option<String>,
pub data_arc: Option<String>,
pub orbit_id: Option<String>,
pub md_orbit_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct MissionQueryResponse {
pub object: MissionQueryObject,
pub fields: Vec<String>,
pub selected_missions: Vec<Vec<f64>>,
}
#[derive(Debug, Clone)]
pub struct MissionFlybyParams {
pub ec: f64,
pub qr: f64,
pub tp: f64,
pub inc: f64,
pub om: f64,
pub w: f64,
pub jd0: f64,
pub jdf: f64,
pub maxout: Option<u32>,
pub maxdist: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct MissionFlybyEntry {
pub full_name: String,
pub pdes: Option<String>,
pub spkid: Option<String>,
pub date: String,
pub jd: f64,
pub min_dist_au: f64,
pub min_dist_km: Option<f64>,
pub rel_vel: f64,
pub class: Option<String>,
pub h_mag: Option<f64>,
pub condition_code: Option<String>,
pub neo: bool,
pub pha: bool,
}
#[derive(Debug, Clone)]
pub struct MissionFlybyResponse {
pub count: u32,
pub data: Vec<MissionFlybyEntry>,
}
#[derive(Debug, Clone)]
pub struct SentryEntry {
pub designation: String,
pub fullname: Option<String>,
pub h_mag: Option<f64>,
pub diameter_km: Option<f64>,
pub n_imp: Option<u32>,
pub ip: Option<f64>,
pub ps_cum: Option<f64>,
pub ps_max: Option<f64>,
pub ts_max: Option<u32>,
pub last_obs: Option<String>,
pub ip_range: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ScoutSummaryEntry {
pub object_name: String,
pub n_obs: Option<u32>,
pub arc: Option<f64>,
pub rms_n: Option<f64>,
pub h_mag: Option<f64>,
pub rating: Option<u32>,
pub moid: Option<f64>,
pub ca_dist: Option<f64>,
pub v_inf: Option<f64>,
pub pha_score: Option<i32>,
pub neo_score: Option<i32>,
pub geocentric_score: Option<i32>,
pub ieo_score: Option<i32>,
pub tisserand_score: Option<i32>,
pub last_run: Option<String>,
pub ra: Option<String>,
pub dec: Option<String>,
pub elong: Option<String>,
pub rate: Option<f64>,
pub v_mag: Option<f64>,
pub unc: Option<f64>,
pub unc_p1: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct ScoutObjectDetail {
pub summary: ScoutSummaryEntry,
pub neo1km_score: Option<String>,
pub t_ephem: Option<String>,
pub orbits: Option<ScoutOrbitData>,
}
#[derive(Debug, Clone)]
pub struct ScoutOrbitData {
pub count: u32,
pub fields: Vec<String>,
pub data: Vec<Vec<serde_json::Value>>,
}
#[derive(Debug, Clone)]
pub struct ScoutSummaryResponse {
pub count: u32,
pub data: Vec<ScoutSummaryEntry>,
}
#[derive(Debug, Clone)]
pub struct ScoutObjectResponse {
pub detail: ScoutObjectDetail,
}
#[derive(Debug, Clone, Default)]
pub struct RadarParams {
pub spk: Option<i64>,
pub des: Option<String>,
pub kind: Option<String>,
pub bp: Option<String>,
pub measurement_type: Option<String>,
pub observer: bool,
pub notes: bool,
pub ref_field: bool,
pub fullname: bool,
pub modified: bool,
pub coords: bool,
pub c_coords: bool,
}
impl RadarParams {
pub fn to_query_params(&self) -> Vec<(String, String)> {
let mut params = Vec::new();
if let Some(v) = self.spk {
params.push(("spk".into(), v.to_string()));
}
if let Some(ref v) = self.des {
params.push(("des".into(), v.clone()));
}
if let Some(ref v) = self.kind {
params.push(("kind".into(), v.clone()));
}
if let Some(ref v) = self.bp {
params.push(("bp".into(), v.clone()));
}
if let Some(ref v) = self.measurement_type {
params.push(("type".into(), v.clone()));
}
if self.observer {
params.push(("observer".into(), "true".into()));
}
if self.notes {
params.push(("notes".into(), "true".into()));
}
if self.ref_field {
params.push(("ref".into(), "true".into()));
}
if self.fullname {
params.push(("fullname".into(), "true".into()));
}
if self.modified {
params.push(("modified".into(), "true".into()));
}
if self.coords {
params.push(("coords".into(), "true".into()));
}
if self.c_coords {
params.push(("c-coords".into(), "true".into()));
}
params
}
}
#[derive(Debug, Clone)]
pub enum SbIdentObserver {
MpcCode(String),
Geodetic { lat: f64, lon: f64, alt: f64 },
Geocentric(String),
Heliocentric(String),
}
#[derive(Debug, Clone)]
pub enum SbIdentFov {
Edges {
ra_lim: String,
dec_lim: String,
},
Center {
ra_center: String,
dec_center: String,
ra_hwidth: Option<f64>,
dec_hwidth: Option<f64>,
},
}
#[derive(Debug, Clone)]
pub struct SbIdentParams {
pub observer: SbIdentObserver,
pub fov: SbIdentFov,
pub obs_time: String,
pub vmag_lim: Option<f64>,
pub two_pass: bool,
pub mag_required: Option<bool>,
pub sb_kind: Option<String>,
pub sb_group: Option<String>,
pub req_elem: bool,
}
impl SbIdentParams {
pub fn to_query_params(&self) -> Vec<(String, String)> {
let mut params = Vec::new();
match &self.observer {
SbIdentObserver::MpcCode(code) => {
params.push(("mpc-code".into(), code.clone()));
}
SbIdentObserver::Geodetic { lat, lon, alt } => {
params.push(("lat".into(), lat.to_string()));
params.push(("lon".into(), lon.to_string()));
params.push(("alt".into(), alt.to_string()));
}
SbIdentObserver::Geocentric(state) => {
params.push(("xobs".into(), state.clone()));
}
SbIdentObserver::Heliocentric(state) => {
params.push(("xobs-hel".into(), state.clone()));
}
}
match &self.fov {
SbIdentFov::Edges { ra_lim, dec_lim } => {
params.push(("fov-ra-lim".into(), ra_lim.clone()));
params.push(("fov-dec-lim".into(), dec_lim.clone()));
}
SbIdentFov::Center {
ra_center,
dec_center,
ra_hwidth,
dec_hwidth,
} => {
params.push(("fov-ra-center".into(), ra_center.clone()));
params.push(("fov-dec-center".into(), dec_center.clone()));
if let Some(w) = ra_hwidth {
params.push(("fov-ra-hwidth".into(), w.to_string()));
}
if let Some(w) = dec_hwidth {
params.push(("fov-dec-hwidth".into(), w.to_string()));
}
}
}
params.push(("obs-time".into(), self.obs_time.clone()));
if let Some(v) = self.vmag_lim {
params.push(("vmag-lim".into(), v.to_string()));
}
if self.two_pass {
params.push(("two-pass".into(), "true".into()));
params.push(("suppress-first-pass".into(), "false".into()));
}
if let Some(v) = self.mag_required {
params.push(("mag-required".into(), v.to_string()));
}
if let Some(ref v) = self.sb_kind {
params.push(("sb-kind".into(), v.clone()));
}
if let Some(ref v) = self.sb_group {
params.push(("sb-group".into(), v.clone()));
}
if self.req_elem {
params.push(("req-elem".into(), "true".into()));
}
params
}
}
#[derive(Debug, Clone)]
pub struct RadarRecord {
pub designation: String,
pub epoch: String,
pub value: Option<f64>,
pub sigma: Option<f64>,
pub units: Option<String>,
pub freq: Option<f64>,
pub rcvr: Option<String>,
pub xmit: Option<String>,
pub bp: Option<String>,
pub observer: Option<String>,
pub notes: Option<String>,
pub reference: Option<String>,
pub fullname: Option<String>,
pub modified: Option<String>,
pub longitude: Option<f64>,
pub latitude: Option<f64>,
pub altitude: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct RadarResponse {
pub count: u32,
pub records: Vec<RadarRecord>,
}
#[derive(Debug, Clone)]
pub struct SbIdentEntry {
pub name: String,
pub ra: Option<String>,
pub dec: Option<String>,
pub ra_offset: Option<f64>,
pub dec_offset: Option<f64>,
pub total_offset: Option<f64>,
pub vmag: Option<f64>,
pub ra_rate: Option<f64>,
pub dec_rate: Option<f64>,
pub ra_err: Option<f64>,
pub dec_err: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct SbIdentOrbitalElements {
pub name: String,
pub h: Option<f64>,
pub g: Option<f64>,
pub e: Option<f64>,
pub q: Option<f64>,
pub tp: Option<f64>,
pub om: Option<f64>,
pub w: Option<f64>,
pub i: Option<f64>,
pub epoch: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct SbIdentObserverInfo {
pub obs_date: Option<String>,
pub location: Option<String>,
pub fov_center: Option<String>,
pub fov_offset: Option<String>,
pub frame: Option<String>,
}
#[derive(Debug, Clone)]
pub struct SbIdentResponse {
pub observer: SbIdentObserverInfo,
pub n_first_pass: u32,
pub n_second_pass: u32,
pub data_first_pass: Vec<SbIdentEntry>,
pub data_second_pass: Vec<SbIdentEntry>,
pub elem_first_pass: Vec<SbIdentOrbitalElements>,
pub elem_second_pass: Vec<SbIdentOrbitalElements>,
}
#[derive(Debug, Clone)]
pub enum ObservabilityObserver {
MpcCode(String),
Geodetic {
lat: f64,
lon: f64,
alt: f64,
},
}
#[derive(Debug, Clone)]
pub struct ObservabilityParams {
pub observer: ObservabilityObserver,
pub obs_time: String,
pub obs_end: Option<String>,
pub optical: Option<bool>,
pub elong_min: Option<f64>,
pub elong_max: Option<f64>,
pub glat_min: Option<f64>,
pub glat_max: Option<f64>,
pub elev_min: Option<f64>,
pub time_min: Option<u32>,
pub vmag_min: Option<f64>,
pub vmag_max: Option<f64>,
pub mag_required: Option<bool>,
pub helio_min: Option<f64>,
pub helio_max: Option<f64>,
pub dist_min: Option<f64>,
pub dist_max: Option<f64>,
pub maxoutput: Option<u32>,
pub output_sort: Option<String>,
pub output_sort_r: Option<bool>,
pub sb_kind: Option<String>,
pub sb_group: Option<String>,
}
impl ObservabilityParams {
pub fn to_query_params(&self) -> Vec<(String, String)> {
let mut params = Vec::new();
match &self.observer {
ObservabilityObserver::MpcCode(code) => {
params.push(("mpc-code".into(), code.clone()));
}
ObservabilityObserver::Geodetic { lat, lon, alt } => {
params.push(("lat".into(), lat.to_string()));
params.push(("lon".into(), lon.to_string()));
params.push(("alt".into(), alt.to_string()));
}
}
params.push(("obs-time".into(), self.obs_time.clone()));
if let Some(ref v) = self.obs_end {
params.push(("obs-end".into(), v.clone()));
}
if let Some(v) = self.optical {
params.push(("optical".into(), v.to_string()));
}
if let Some(v) = self.elong_min {
params.push(("elong-min".into(), v.to_string()));
}
if let Some(v) = self.elong_max {
params.push(("elong-max".into(), v.to_string()));
}
if let Some(v) = self.glat_min {
params.push(("glat-min".into(), v.to_string()));
}
if let Some(v) = self.glat_max {
params.push(("glat-max".into(), v.to_string()));
}
if let Some(v) = self.elev_min {
params.push(("elev-min".into(), v.to_string()));
}
if let Some(v) = self.time_min {
params.push(("time-min".into(), v.to_string()));
}
if let Some(v) = self.vmag_min {
params.push(("vmag-min".into(), v.to_string()));
}
if let Some(v) = self.vmag_max {
params.push(("vmag-max".into(), v.to_string()));
}
if let Some(v) = self.mag_required {
params.push(("mag-required".into(), v.to_string()));
}
if let Some(v) = self.helio_min {
params.push(("helio-min".into(), v.to_string()));
}
if let Some(v) = self.helio_max {
params.push(("helio-max".into(), v.to_string()));
}
if let Some(v) = self.dist_min {
params.push(("dist-min".into(), v.to_string()));
}
if let Some(v) = self.dist_max {
params.push(("dist-max".into(), v.to_string()));
}
if let Some(v) = self.maxoutput {
params.push(("maxoutput".into(), v.to_string()));
}
if let Some(ref v) = self.output_sort {
params.push(("output-sort".into(), v.clone()));
}
if let Some(v) = self.output_sort_r {
params.push(("output-sort-r".into(), v.to_string()));
}
if let Some(ref v) = self.sb_kind {
params.push(("sb-kind".into(), v.clone()));
}
if let Some(ref v) = self.sb_group {
params.push(("sb-group".into(), v.clone()));
}
params
}
}
#[derive(Debug, Clone)]
pub struct ObservabilityNightInfo {
pub sun_set: Option<String>,
pub sun_rise: Option<String>,
pub sun_set_az: Option<String>,
pub sun_rise_az: Option<String>,
pub begin_astronomical: Option<String>,
pub end_astronomical: Option<String>,
pub begin_civil: Option<String>,
pub end_civil: Option<String>,
pub begin_nautical: Option<String>,
pub end_nautical: Option<String>,
pub moon_rise: Option<String>,
pub moon_rise_phase: Option<String>,
pub moon_set: Option<String>,
pub moon_set_phase: Option<String>,
pub transit: Option<String>,
pub transit_phase: Option<String>,
pub begin_dark: Option<String>,
pub mid_dark: Option<String>,
pub end_dark: Option<String>,
pub dark_time: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ObservableObject {
pub des: String,
pub fullname: Option<String>,
pub rise: Option<String>,
pub transit: Option<String>,
pub set: Option<String>,
pub max_time: Option<String>,
pub ra: Option<String>,
pub dec: Option<String>,
pub vmag: Option<f64>,
pub helio_range_au: Option<f64>,
pub topo_range_au: Option<f64>,
pub sun_angle: Option<f64>,
pub moon_angle: Option<f64>,
pub galactic_lat: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct ObservabilityResponse {
pub night_info: ObservabilityNightInfo,
pub count: u32,
pub objects: Vec<ObservableObject>,
}
#[derive(Debug, Clone, Default)]
pub struct NhatsParams {
pub dv: Option<u32>,
pub dur: Option<u32>,
pub stay: Option<u32>,
pub launch: Option<String>,
pub h: Option<u32>,
pub occ: Option<u32>,
}
impl NhatsParams {
pub fn to_query_params(&self) -> Vec<(String, String)> {
let mut params = Vec::new();
if let Some(v) = self.dv {
params.push(("dv".into(), v.to_string()));
}
if let Some(v) = self.dur {
params.push(("dur".into(), v.to_string()));
}
if let Some(v) = self.stay {
params.push(("stay".into(), v.to_string()));
}
if let Some(ref v) = self.launch {
params.push(("launch".into(), v.clone()));
}
if let Some(v) = self.h {
params.push(("h".into(), v.to_string()));
}
if let Some(v) = self.occ {
params.push(("occ".into(), v.to_string()));
}
params
}
}
#[derive(Debug, Clone)]
pub struct NhatsDvDur {
pub dv: Option<f64>,
pub dur: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct NhatsTrajectory {
pub tid: Option<String>,
pub dv_total: Option<f64>,
pub dur_total: Option<f64>,
pub dur_out: Option<f64>,
pub dur_at: Option<f64>,
pub dur_ret: Option<f64>,
pub launch: Option<String>,
pub c3: Option<f64>,
pub v_dep_earth: Option<f64>,
pub dv_dep_park: Option<f64>,
pub vrel_arr_neo: Option<f64>,
pub vrel_dep_neo: Option<f64>,
pub vrel_arr_earth: Option<f64>,
pub v_arr_earth: Option<f64>,
pub dec_dep: Option<f64>,
pub dec_arr: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct NhatsSummaryEntry {
pub des: String,
pub fullname: Option<String>,
pub orbit_id: Option<String>,
pub h: Option<f64>,
pub min_size: Option<f64>,
pub max_size: Option<f64>,
pub size: Option<f64>,
pub occ: Option<u32>,
pub min_dv: Option<NhatsDvDur>,
pub min_dur: Option<NhatsDvDur>,
pub n_via_traj: Option<u32>,
pub obs_start: Option<String>,
pub obs_end: Option<String>,
}
#[derive(Debug, Clone)]
pub struct NhatsSummaryResponse {
pub count: u32,
pub data: Vec<NhatsSummaryEntry>,
}
#[derive(Debug, Clone)]
pub struct NhatsObjectResponse {
pub des: String,
pub fullname: Option<String>,
pub orbit_id: Option<String>,
pub h: Option<f64>,
pub min_size: Option<f64>,
pub max_size: Option<f64>,
pub size: Option<f64>,
pub occ: Option<u32>,
pub n_via_traj: Option<u32>,
pub min_dv_traj: Option<NhatsTrajectory>,
pub min_dur_traj: Option<NhatsTrajectory>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orbit_class_from_code() {
assert_eq!(OrbitClass::from_code("APO"), OrbitClass::Apollo);
assert_eq!(OrbitClass::from_code("AMO"), OrbitClass::Amor);
assert_eq!(OrbitClass::from_code("MBA"), OrbitClass::MainBelt);
assert_eq!(OrbitClass::from_code("TNO"), OrbitClass::TransNeptunian);
assert_eq!(
OrbitClass::from_code("XYZ"),
OrbitClass::Other("XYZ".to_string())
);
}
#[test]
fn test_mission_design_criterion_values() {
assert_eq!(MissionDesignCriterion::MinDepartureVinf.as_api_value(), 1);
assert_eq!(MissionDesignCriterion::MinArrivalVinf.as_api_value(), 2);
assert_eq!(MissionDesignCriterion::MinTotalDv.as_api_value(), 3);
assert_eq!(MissionDesignCriterion::MinTofMinDepVinf.as_api_value(), 4);
assert_eq!(MissionDesignCriterion::MinTofMinArrVinf.as_api_value(), 5);
assert_eq!(MissionDesignCriterion::MinTofMinTotalDv.as_api_value(), 6);
}
#[test]
fn test_orbit_class_roundtrip() {
let classes = [
OrbitClass::Atira,
OrbitClass::Aten,
OrbitClass::Apollo,
OrbitClass::Amor,
OrbitClass::MainBelt,
OrbitClass::Centaur,
];
for class in &classes {
assert_eq!(&OrbitClass::from_code(class.as_code()), class);
}
}
#[test]
fn test_sb_ident_params_mpc_code() {
let params = SbIdentParams {
observer: SbIdentObserver::MpcCode("F51".into()),
fov: SbIdentFov::Center {
ra_center: "05-00-00".into(),
dec_center: "20-00-00".into(),
ra_hwidth: Some(1.0),
dec_hwidth: Some(1.0),
},
obs_time: "2024-01-01".into(),
vmag_lim: Some(20.0),
two_pass: false,
mag_required: None,
sb_kind: None,
sb_group: None,
req_elem: false,
};
let query = params.to_query_params();
let map: std::collections::HashMap<String, String> = query.into_iter().collect();
assert_eq!(map.get("mpc-code").unwrap(), "F51");
assert_eq!(map.get("fov-ra-center").unwrap(), "05-00-00");
assert_eq!(map.get("fov-dec-center").unwrap(), "20-00-00");
assert_eq!(map.get("fov-ra-hwidth").unwrap(), "1");
assert_eq!(map.get("obs-time").unwrap(), "2024-01-01");
assert_eq!(map.get("vmag-lim").unwrap(), "20");
assert!(map.get("two-pass").is_none());
}
#[test]
fn test_sb_ident_params_geodetic_edges() {
let params = SbIdentParams {
observer: SbIdentObserver::Geodetic {
lat: 34.05,
lon: -118.25,
alt: 0.0,
},
fov: SbIdentFov::Edges {
ra_lim: "05-00-00,06-00-00".into(),
dec_lim: "19-00-00,21-00-00".into(),
},
obs_time: "2024-06-15_12:00:00".into(),
vmag_lim: None,
two_pass: true,
mag_required: Some(true),
sb_kind: Some("a".into()),
sb_group: Some("neo".into()),
req_elem: true,
};
let query = params.to_query_params();
let map: std::collections::HashMap<String, String> = query.into_iter().collect();
assert_eq!(map.get("lat").unwrap(), "34.05");
assert_eq!(map.get("lon").unwrap(), "-118.25");
assert_eq!(map.get("alt").unwrap(), "0");
assert_eq!(map.get("fov-ra-lim").unwrap(), "05-00-00,06-00-00");
assert_eq!(map.get("fov-dec-lim").unwrap(), "19-00-00,21-00-00");
assert_eq!(map.get("two-pass").unwrap(), "true");
assert_eq!(map.get("suppress-first-pass").unwrap(), "false");
assert_eq!(map.get("mag-required").unwrap(), "true");
assert_eq!(map.get("sb-kind").unwrap(), "a");
assert_eq!(map.get("sb-group").unwrap(), "neo");
assert_eq!(map.get("req-elem").unwrap(), "true");
}
#[test]
fn test_observability_params_mpc_code() {
let params = ObservabilityParams {
observer: ObservabilityObserver::MpcCode("F51".into()),
obs_time: "2026-03-01".into(),
obs_end: None,
optical: None,
elong_min: None,
elong_max: None,
glat_min: None,
glat_max: None,
elev_min: None,
time_min: None,
vmag_min: None,
vmag_max: Some(18.0),
mag_required: None,
helio_min: None,
helio_max: None,
dist_min: None,
dist_max: None,
maxoutput: Some(10),
output_sort: Some("vmag".into()),
output_sort_r: None,
sb_kind: None,
sb_group: None,
};
let query = params.to_query_params();
let map: std::collections::HashMap<String, String> = query.into_iter().collect();
assert_eq!(map.get("mpc-code").unwrap(), "F51");
assert_eq!(map.get("obs-time").unwrap(), "2026-03-01");
assert_eq!(map.get("vmag-max").unwrap(), "18");
assert_eq!(map.get("maxoutput").unwrap(), "10");
assert_eq!(map.get("output-sort").unwrap(), "vmag");
assert!(!map.contains_key("lat"));
}
#[test]
fn test_observability_params_geodetic() {
let params = ObservabilityParams {
observer: ObservabilityObserver::Geodetic {
lat: 34.05,
lon: -118.25,
alt: 0.1,
},
obs_time: "2026-06-15".into(),
obs_end: None,
optical: Some(false),
elong_min: Some(30.0),
elong_max: None,
glat_min: None,
glat_max: None,
elev_min: Some(20.0),
time_min: None,
vmag_min: None,
vmag_max: None,
mag_required: None,
helio_min: None,
helio_max: None,
dist_min: None,
dist_max: None,
maxoutput: None,
output_sort: None,
output_sort_r: None,
sb_kind: Some("a".into()),
sb_group: Some("neo".into()),
};
let query = params.to_query_params();
let map: std::collections::HashMap<String, String> = query.into_iter().collect();
assert_eq!(map.get("lat").unwrap(), "34.05");
assert_eq!(map.get("lon").unwrap(), "-118.25");
assert_eq!(map.get("alt").unwrap(), "0.1");
assert_eq!(map.get("optical").unwrap(), "false");
assert_eq!(map.get("elong-min").unwrap(), "30");
assert_eq!(map.get("elev-min").unwrap(), "20");
assert_eq!(map.get("sb-kind").unwrap(), "a");
assert_eq!(map.get("sb-group").unwrap(), "neo");
assert!(!map.contains_key("mpc-code"));
}
}