Skip to content

Commit

Permalink
feat: implement DynTimeScale
Browse files Browse the repository at this point in the history
  • Loading branch information
helgee committed Jan 24, 2025
1 parent d0fae0a commit ebd110a
Show file tree
Hide file tree
Showing 37 changed files with 1,968 additions and 2,416 deletions.
96 changes: 42 additions & 54 deletions crates/lox-orbits/src/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
use std::f64::consts::PI;

/*
* Copyright (c) 2024. Helge Eichhorn and the LOX contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*/

use lox_bodies::{Origin, RotationalElements, Spheroid, TrySpheroid};
use lox_math::roots::Brent;
use lox_math::series::{Series, SeriesError};
use lox_math::types::units::Radians;
use lox_time::deltas::TimeDelta;
use lox_time::time_scales::Tdb;
use lox_time::transformations::TryToScale;
use lox_time::TimeLike;
use lox_time::time_scales::{DynTimeScale, TryToScale};
use lox_time::time_scales::{Tdb, TimeScale};
use lox_time::ut1::DeltaUt1TaiProvider;
use lox_time::{DynTime, Time};
use std::f64::consts::PI;
use thiserror::Error;

use crate::events::{find_windows, Window};
use crate::frames::{
BodyFixed, DynFrame, FrameTransformationProvider, Icrf, TryRotateTo, TryToFrame,
};
use crate::frames::{DynFrame, Iau, Icrf, TryRotateTo};
use crate::ground::{DynGroundLocation, GroundLocation};
use crate::states::State;
use crate::trajectories::{DynTrajectory, Trajectory};
Expand Down Expand Up @@ -63,84 +62,84 @@ impl ElevationMask {
}
}

pub fn elevation_dyn<T: TimeLike + TryToScale<Tdb, P> + Clone, P: FrameTransformationProvider>(
time: T,
pub fn elevation_dyn<P: DeltaUt1TaiProvider>(
time: DynTime,
gs: &DynGroundLocation,
mask: &ElevationMask,
sc: &DynTrajectory<T>,
provider: &P,
sc: &DynTrajectory,
provider: Option<&P>,
) -> Radians {
let body_fixed = DynFrame::BodyFixed(gs.origin());
let sc = sc.interpolate_at(time.clone());
let rot = DynFrame::Icrf.try_rotation(&body_fixed, time, provider);
let body_fixed = DynFrame::Iau(gs.origin());
let sc = sc.interpolate_at(time);
let rot = DynFrame::Icrf.try_rotation(body_fixed, time, provider);
let (r1, v1) = rot.unwrap().rotate_state(sc.position(), sc.velocity());
let sc = State::new(sc.time(), r1, v1, sc.origin(), body_fixed);
let obs = gs.observables_dyn(sc);
obs.elevation() - mask.min_elevation(obs.azimuth())
}

pub fn visibility_dyn<T: TimeLike + TryToScale<Tdb, P> + Clone, P: FrameTransformationProvider>(
times: &[T],
pub fn visibility_dyn<P: DeltaUt1TaiProvider>(
times: &[DynTime],
gs: &DynGroundLocation,
mask: &ElevationMask,
sc: &DynTrajectory<T>,
provider: &P,
) -> Vec<Window<T>> {
sc: &DynTrajectory,
provider: Option<&P>,
) -> Vec<Window<DynTimeScale>> {
if times.len() < 2 {
return vec![];
}
let start = times.first().unwrap().clone();
let end = times.last().unwrap().clone();
let start = *times.first().unwrap();
let end = *times.last().unwrap();
let times: Vec<f64> = times
.iter()
.map(|t| (t.clone() - start.clone()).to_decimal_seconds())
.map(|t| (*t - start).to_decimal_seconds())
.collect();
let root_finder = Brent::default();
find_windows(
|t| {
elevation_dyn(
start.clone() + TimeDelta::from_decimal_seconds(t).unwrap(),
start + TimeDelta::try_from_decimal_seconds(t).unwrap(),
gs,
mask,
sc,
provider,
)
},
start.clone(),
end.clone(),
start,
end,
&times,
root_finder,
)
}

pub fn elevation<
T: TimeLike + TryToScale<Tdb, P> + Clone,
T: TimeScale + TryToScale<Tdb, P> + Clone,
O: Origin + TrySpheroid + RotationalElements + Clone,
P: FrameTransformationProvider,
P,
>(
time: T,
time: Time<T>,
gs: &GroundLocation<O>,
mask: &ElevationMask,
sc: &Trajectory<T, O, Icrf>,
provider: &P,
provider: Option<&P>,
) -> Radians {
let body_fixed = BodyFixed(gs.origin());
let body_fixed = Iau(gs.origin());
let sc = sc.interpolate_at(time.clone());
let sc = sc.try_to_frame(body_fixed, provider).unwrap();
let obs = gs.observables(sc);
obs.elevation() - mask.min_elevation(obs.azimuth())
}

pub fn visibility<
T: TimeLike + TryToScale<Tdb, P> + Clone,
T: TimeScale + TryToScale<Tdb, P> + Clone,
O: Origin + Spheroid + RotationalElements + Clone,
P: FrameTransformationProvider,
P,
>(
times: &[T],
times: &[Time<T>],
gs: &GroundLocation<O>,
mask: &ElevationMask,
sc: &Trajectory<T, O, Icrf>,
provider: &P,
provider: Option<&P>,
) -> Vec<Window<T>> {
if times.len() < 2 {
return vec![];
Expand All @@ -155,7 +154,7 @@ pub fn visibility<
find_windows(
|t| {
elevation(
start.clone() + TimeDelta::from_decimal_seconds(t).unwrap(),
start.clone() + TimeDelta::try_from_decimal_seconds(t).unwrap(),
gs,
mask,
sc,
Expand All @@ -175,13 +174,10 @@ mod tests {
use lox_math::assert_close;
use lox_math::is_close::IsClose;
use lox_time::time_scales::Tai;
use lox_time::transformations::ToTai;
use lox_time::utc::Utc;
use lox_time::Time;
use std::iter::zip;

use crate::frames::NoOpFrameTransformationProvider;

use super::*;

#[test]
Expand All @@ -196,15 +192,7 @@ mod tests {
let actual: Vec<Radians> = gs
.times()
.iter()
.map(|t| {
elevation(
*t,
&location(),
&mask,
&sc,
&NoOpFrameTransformationProvider,
)
})
.map(|t| elevation(*t, &location(), &mask, &sc, None::<&()>))
.collect();
for (actual, expected) in actual.iter().zip(expected.iter()) {
assert_close!(actual, expected, 1e-1);
Expand Down Expand Up @@ -237,15 +225,15 @@ mod tests {
let sc = spacecraft_trajectory();
let times: Vec<Time<Tai>> = sc.states().iter().map(|s| s.time()).collect();
let expected = contacts();
let actual = visibility(&times, &gs, &mask, &sc, &NoOpFrameTransformationProvider);
let actual = visibility(&times, &gs, &mask, &sc, None::<&()>);
assert_eq!(actual.len(), expected.len());
for (actual, expected) in zip(actual, expected) {
assert_close!(actual.start(), expected.start(), 0.0, 1e-4);
assert_close!(actual.end(), expected.end(), 0.0, 1e-4);
}
}

fn ground_station_trajectory() -> Trajectory<Time<Tai>, Earth, Icrf> {
fn ground_station_trajectory() -> Trajectory<Tai, Earth, Icrf> {
Trajectory::from_csv(
include_str!("../../../data/trajectory_cebr.csv"),
Earth,
Expand All @@ -254,7 +242,7 @@ mod tests {
.unwrap()
}

fn spacecraft_trajectory() -> Trajectory<Time<Tai>, Earth, Icrf> {
fn spacecraft_trajectory() -> Trajectory<Tai, Earth, Icrf> {
Trajectory::from_csv(
include_str!("../../../data/trajectory_lunar.csv"),
Earth,
Expand All @@ -269,14 +257,14 @@ mod tests {
GroundLocation::new(longitude, latitude, 0.0, Earth)
}

fn contacts() -> Vec<Window<Time<Tai>>> {
fn contacts() -> Vec<Window<Tai>> {
let mut windows = vec![];
let mut reader =
csv::Reader::from_reader(include_str!("../../../data/contacts.csv").as_bytes());
for result in reader.records() {
let record = result.unwrap();
let start = record[0].parse::<Utc>().unwrap().to_tai();
let end = record[1].parse::<Utc>().unwrap().to_tai();
let start = record[0].parse::<Utc>().unwrap().to_time();
let end = record[1].parse::<Utc>().unwrap().to_time();
windows.push(Window::new(start, end));
}
windows
Expand Down
45 changes: 21 additions & 24 deletions crates/lox-orbits/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ use glam::{DMat3, DVec3};

use lox_bodies::{DynOrigin, PointMass, TryPointMass, UndefinedOriginPropertyError};
use lox_time::deltas::TimeDelta;
use lox_time::TimeLike;
use lox_time::time_scales::{DynTimeScale, TimeScale};
use lox_time::{DynTime, Time};

use crate::frames::{CoordinateSystem, DynFrame, Icrf, ReferenceFrame};
use crate::frames::{DynFrame, Icrf, ReferenceFrame};
use crate::states::State;

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -29,8 +30,8 @@ pub(crate) struct KeplerianElements {
}

#[derive(Debug, Clone, PartialEq)]
pub struct Keplerian<T: TimeLike, O: TryPointMass, R: ReferenceFrame> {
time: T,
pub struct Keplerian<T: TimeScale, O: TryPointMass, R: ReferenceFrame> {
time: Time<T>,
origin: O,
frame: R,
semi_major_axis: f64,
Expand All @@ -41,16 +42,16 @@ pub struct Keplerian<T: TimeLike, O: TryPointMass, R: ReferenceFrame> {
true_anomaly: f64,
}

pub type DynKeplerian<T> = Keplerian<T, DynOrigin, DynFrame>;
pub type DynKeplerian = Keplerian<DynTimeScale, DynOrigin, DynFrame>;

impl<T, O> Keplerian<T, O, Icrf>
where
T: TimeLike,
T: TimeScale,
O: PointMass,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
time: T,
time: Time<T>,
origin: O,
semi_major_axis: f64,
eccentricity: f64,
Expand All @@ -73,13 +74,10 @@ where
}
}

impl<T> DynKeplerian<T>
where
T: TimeLike,
{
impl DynKeplerian {
#[allow(clippy::too_many_arguments)]
pub fn with_dynamic(
time: T,
time: DynTime,
origin: DynOrigin,
semi_major_axis: f64,
eccentricity: f64,
Expand All @@ -105,7 +103,7 @@ where

impl<T, O, R> Keplerian<T, O, R>
where
T: TimeLike,
T: TimeScale,
O: TryPointMass,
R: ReferenceFrame,
{
Expand All @@ -116,7 +114,14 @@ where
self.origin.clone()
}

pub fn time(&self) -> T
fn reference_frame(&self) -> R
where
R: Clone,
{
self.frame.clone()
}

pub fn time(&self) -> Time<T>
where
T: Clone,
{
Expand Down Expand Up @@ -176,21 +181,13 @@ where
pub fn orbital_period(&self) -> TimeDelta {
let mu = self.gravitational_parameter();
let a = self.semi_major_axis();
TimeDelta::from_decimal_seconds(TAU * (a.powi(3) / mu).sqrt()).unwrap()
}
}

impl<T: TimeLike, O: TryPointMass, R: ReferenceFrame + Clone> CoordinateSystem<R>
for Keplerian<T, O, R>
{
fn reference_frame(&self) -> R {
self.frame.clone()
TimeDelta::try_from_decimal_seconds(TAU * (a.powi(3) / mu).sqrt()).unwrap()
}
}

impl<T, O, R> Keplerian<T, O, R>
where
T: TimeLike + Clone,
T: TimeScale + Clone,
O: TryPointMass + Clone,
R: ReferenceFrame + Clone,
{
Expand Down
52 changes: 0 additions & 52 deletions crates/lox-orbits/src/ensembles.rs

This file was deleted.

Loading

0 comments on commit ebd110a

Please sign in to comment.