///|
pub struct CatalogEntry {
id : String
name : String
elements : ClassicalElements
epoch_day : Double
active : Bool
} derive(Debug, Eq)
///|
pub struct CatalogQuery {
minimum_altitude_km : Double
maximum_altitude_km : Double
minimum_inclination_rad : Double
maximum_inclination_rad : Double
active_only : Bool
} derive(Debug, Eq)
///|
pub fn CatalogQuery::any() -> CatalogQuery {
{
minimum_altitude_km: -earth_radius_km,
maximum_altitude_km: 1.0e9,
minimum_inclination_rad: -two_pi,
maximum_inclination_rad: two_pi,
active_only: false,
}
}
///|
pub fn CatalogQuery::leo() -> CatalogQuery {
{
minimum_altitude_km: 150.0,
maximum_altitude_km: 2000.0,
minimum_inclination_rad: -two_pi,
maximum_inclination_rad: two_pi,
active_only: true,
}
}
///|
pub fn CatalogEntry::new(
id : String,
name : String,
elements : ClassicalElements,
epoch_day : Double,
active? : Bool = true,
) -> CatalogEntry {
{ id, name, elements, epoch_day, active }
}
///|
pub fn catalog_altitude_km(entry : CatalogEntry) -> Double {
entry.elements.semi_major_axis_km - earth_radius_km
}
///|
pub fn catalog_matches(query : CatalogQuery, entry : CatalogEntry) -> Bool {
catalog_altitude_km(entry) >= query.minimum_altitude_km &&
catalog_altitude_km(entry) <= query.maximum_altitude_km &&
entry.elements.inclination_rad >= query.minimum_inclination_rad &&
entry.elements.inclination_rad <= query.maximum_inclination_rad &&
(!query.active_only || entry.active)
}
///|
pub fn filter_catalog(
entries : Array[CatalogEntry],
query : CatalogQuery,
) -> Array[CatalogEntry] {
entries.filter(entry => catalog_matches(query, entry))
}
///|
pub fn sort_catalog_by_altitude(
entries : Array[CatalogEntry],
) -> Array[CatalogEntry] {
let result = entries.copy()
for i in 0.. CatalogEntry? {
for entry in entries {
if entry.id == id {
return Some(entry)
}
}
None
}
///|
pub fn catalog_names(entries : Array[CatalogEntry]) -> Array[String] {
entries.map(entry => entry.name)
}
///|
pub fn catalog_altitude_stats(entries : Array[CatalogEntry]) -> SampleStats {
summarize_samples(entries.map(catalog_altitude_km))
}
///|
pub fn catalog_period_stats(entries : Array[CatalogEntry]) -> SampleStats {
summarize_samples(
entries.map(entry => {
orbital_period(earth_mu_km3_s2, entry.elements.semi_major_axis_km)
}),
)
}
///|
pub fn propagate_catalog(
entries : Array[CatalogEntry],
delta_t_s : Double,
) -> Array[CatalogEntry] {
entries.map(entry => {
..entry,
elements: propagate_kepler(earth_mu_km3_s2, entry.elements, delta_t_s),
})
}
///|
pub fn catalog_merge(
left : Array[CatalogEntry],
right : Array[CatalogEntry],
) -> Array[CatalogEntry] {
let result = left.copy()
for entry in right {
if find_catalog_entry(result, entry.id) is None {
result.push(entry)
}
}
result
}
///|
pub fn catalog_density(
entries : Array[CatalogEntry],
altitude_bin_km : Double,
) -> Array[Int] {
let result : Array[Int] = []
if altitude_bin_km <= 0.0 {
return result
}
for entry in entries {
let bin = (catalog_altitude_km(entry) / altitude_bin_km).floor().to_int()
while result.length() <= bin {
result.push(0)
}
result[bin] += 1
}
result
}
///|
pub fn catalog_active_fraction(entries : Array[CatalogEntry]) -> Double {
if entries.length() == 0 {
0.0
} else {
Double::from_int(entries.filter(entry => entry.active).length()) /
Double::from_int(entries.length())
}
}