Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp tracking schedule building #261

Merged
merged 23 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7495961
Rewrote OD scheduler building
ChristopherRabotin Dec 3, 2023
e60fcfd
Add builder pattern to scheduler
ChristopherRabotin Dec 3, 2023
7dcefc0
Integration tests build, but probably fail
ChristopherRabotin Dec 3, 2023
9861b57
Debugging in building tracking schedules
ChristopherRabotin Dec 4, 2023
8f71322
Event evaluator of elevation from GS is still broken
ChristopherRabotin Dec 4, 2023
631000a
Trajectory finding can build arcs of rising and falling edges
ChristopherRabotin Dec 6, 2023
4a403e5
Now using ground station elevation event arcs to build measurements
ChristopherRabotin Dec 6, 2023
439f0fd
Fix step in OD with overlapping measurement
ChristopherRabotin Dec 9, 2023
97e1fec
Fix find_arcs and iteration convergence
ChristopherRabotin Dec 10, 2023
c0e4228
Prevent bubbling up the error if no strands are defined
ChristopherRabotin Dec 11, 2023
494b227
Fix Python build. Need to debug OD results
ChristopherRabotin Dec 19, 2023
2b4e7f0
Using next step for OD prevents lon loops
ChristopherRabotin Dec 20, 2023
6ef276b
Fixed tracking arc tests
ChristopherRabotin Dec 21, 2023
6a9be42
Add a minimum number of samples for a valid tracking arc
ChristopherRabotin Dec 21, 2023
dc3250e
Fixed OD TB validation with tracking arc
ChristopherRabotin Dec 22, 2023
ee525fb
Implemented Eager vs Greedy tracking arc
ChristopherRabotin Dec 23, 2023
86c7457
Fixed OD validation by aligning measurement epochs to the truth propa…
ChristopherRabotin Dec 23, 2023
1363214
Fixed serialization of TrkConfig
ChristopherRabotin Dec 23, 2023
4ecd77a
Fixed residual check test
ChristopherRabotin Dec 23, 2023
c2eea25
Two more tests to fix
ChristopherRabotin Dec 23, 2023
5dce943
Fixed remaining tests.
ChristopherRabotin Dec 23, 2023
c2845b4
Rename EpochRange to Strand
ChristopherRabotin Dec 23, 2023
c5bd169
Clean up documentation
ChristopherRabotin Dec 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions data/tests/config/tracking_cfg.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
Demo ground station:
start: Visible
end: Visible
schedule: Continuous
scheduler:
handoff: Overlap
cadence: Continuous
min_samples: 10
sample_alignment: null
sampling: 1 min

Canberra:
start: Visible
end: Visible
schedule: Continuous
scheduler:
handoff: Eager
cadence: Continuous
min_samples: 10
sample_alignment: 10 s
sampling: 1 min
17 changes: 0 additions & 17 deletions data/tests/config/tracking_cfg_incl_excl.yaml

This file was deleted.

7 changes: 7 additions & 0 deletions data/tests/config/trk_cfg_od_val.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
scheduler:
handoff: Overlap # Allow for overlapping measurement
cadence: Continuous
min_samples: 10
sample_alignment: 10 s
sampling: 10 s
strands: null
22 changes: 11 additions & 11 deletions data/tests/config/trk_cfg_od_val_arc.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
Madrid:
start: Visible
end: !Epoch 2020-01-01 03:00:00 UTC
schedule: Continuous
strands:
- start: 2020-01-01 00:00:00 TAI
end: 2020-01-01 03:00:00 TAI
sampling: 1 min

Canberra:
start: Visible
end: !Epoch 2020-01-01 19:00:00 UTC
schedule: !Intermittent
on: 4 h
off: 8 h
strands:
- start: 2020-01-01 02:40:00 TAI
end: 2020-01-01 07:00:00 TAI
sampling: 1 min

Goldstone:
start: !Epoch 2020-01-01 09:00:00 UTC
end: Visible
schedule: Continuous
strands:
- start: 2020-01-01 19:30:00 TAI
end: 2020-01-02 00:00:00 TAI
- start: 2020-01-01 09:00:00 TAI
end: 2020-01-01 12:00:00 TAI
sampling: 1 min
30 changes: 30 additions & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use pyo3::prelude::*;
/// Configuration for exporting a trajectory to parquet.
#[derive(Clone, Default, Serialize, Deserialize, TypedBuilder)]
#[cfg_attr(feature = "python", pyclass)]
#[builder(doc)]
pub struct ExportCfg {
/// Fields to export, if unset, defaults to all possible fields.
#[builder(default, setter(strip_option))]
Expand Down Expand Up @@ -327,6 +328,35 @@ where
Ok(orbit_serde.into())
}

pub(crate) fn maybe_duration_to_str<S>(
duration: &Option<Duration>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(duration) = duration {
duration_to_str(duration, serializer)
} else {
serializer.serialize_none()
}
}

pub(crate) fn maybe_duration_from_str<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
if let Ok(s) = String::deserialize(deserializer) {
if let Ok(duration) = Duration::from_str(&s) {
Ok(Some(duration))
} else {
Ok(None)
}
} else {
Ok(None)
}
}

#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
pub enum ParsingError {
Expand Down
193 changes: 193 additions & 0 deletions src/md/events/details.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
Nyx, blazing fast astrodynamics
Copyright (C) 2023 Christopher Rabotin <[email protected]>

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::errors::NyxError;
use crate::linalg::allocator::Allocator;
use crate::linalg::DefaultAllocator;
use crate::md::prelude::{Interpolatable, Traj};
use crate::md::EventEvaluator;
use crate::time::Duration;
use core::fmt;

/// Enumerates the possible edges of an event in a trajectory.
///
/// `EventEdge` is used to describe the nature of a trajectory event, particularly in terms of its temporal dynamics relative to a specified condition or threshold. This enum helps in distinguishing whether the event is occurring at a rising edge, a falling edge, or if the edge is unclear due to insufficient data or ambiguous conditions.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum EventEdge {
/// Represents a rising edge of the event. This indicates that the event is transitioning from a lower to a higher evaluation of the event. For example, in the context of elevation, a rising edge would indicate an increase in elevation from a lower angle.
Rising,
/// Represents a falling edge of the event. This is the opposite of the rising edge, indicating a transition from a higher to a lower value of the event evaluator. For example, if tracking the elevation of an object, a falling edge would signify a
Falling,
/// If the edge cannot be clearly defined, it will be marked as unclear. This happens if the event is at a saddle point and the epoch precision is too large to find the exact slope.
Unclear,
}

/// Represents the details of an event occurring along a trajectory.
///
/// `EventDetails` encapsulates the state at which a particular event occurs in a trajectory, along with additional information about the nature of the event. This struct is particularly useful for understanding the dynamics of the event, such as whether it represents a rising or falling edge, or if the edge is unclear.
///
/// # Generics
/// S: Interpolatable - A type that represents the state of the trajectory. This type must implement the `Interpolatable` trait, ensuring that it can be interpolated and manipulated according to the trajectory's requirements.
#[derive(Clone, Debug, PartialEq)]
pub struct EventDetails<S: Interpolatable>
where
DefaultAllocator:
Allocator<f64, S::VecLength> + Allocator<f64, S::Size> + Allocator<f64, S::Size, S::Size>,
{
/// The state of the trajectory at the found event.
pub state: S,
/// Indicates whether the event is a rising edge, falling edge, or unclear. This helps in understanding the direction of change at the event point.
pub edge: EventEdge,
/// Numerical evaluation of the event condition, e.g. if seeking the apoapsis, this returns the near zero
pub value: f64,
/// Numertical evaluation of the event condition one epoch step before the found event (used to compute the rising/falling edge).
pub prev_value: Option<f64>,
/// Numertical evaluation of the event condition one epoch step after the found event (used to compute the rising/falling edge).
pub next_value: Option<f64>,
/// Precision of the epoch for this value
pub pm_duration: Duration,
// Store the representation of this event as a string because we can't move or clone the event reference
pub repr: String,
}

impl<S: Interpolatable> EventDetails<S>
where
DefaultAllocator:
Allocator<f64, S::VecLength> + Allocator<f64, S::Size> + Allocator<f64, S::Size, S::Size>,
{
/// Generates detailed information about an event at a specific epoch in a trajectory.
///
/// This takes an `Epoch` as an input and returns a `Result<Self, NyxError>`.
/// It is designed to determine the state of a trajectory at a given epoch, evaluate the specific event at that state, and ascertain the nature of the event (rising, falling, or unclear).
/// The initialization intelligently determines the edge type of the event by comparing the event's value at the current, previous, and next epochs.
/// It ensures robust event characterization in trajectories.
///
/// # Returns
/// - `Ok(EventDetails<S>)` if the state at the given epoch can be determined and the event details are successfully evaluated.
/// - `Err(NyxError)` if there is an error in retrieving the state at the specified epoch.
///
pub fn new<E: EventEvaluator<S>>(
state: S,
value: f64,
event: &E,
traj: &Traj<S>,
) -> Result<Self, NyxError> {
let epoch = state.epoch();
let prev_value = if let Ok(state) = traj.at(epoch - event.epoch_precision()) {
Some(event.eval(&state))
} else {
None
};

let next_value = if let Ok(state) = traj.at(epoch + event.epoch_precision()) {
Some(event.eval(&state))
} else {
None
};

let edge = if let Some(prev_value) = prev_value {
if let Some(next_value) = next_value {
if prev_value > value && value > next_value {
EventEdge::Falling
} else if prev_value < value && value < next_value {
EventEdge::Rising
} else {
warn!("could not determine edge of {} at {}", event, state.epoch(),);
EventEdge::Unclear
}
} else if prev_value > value {
EventEdge::Falling
} else {
EventEdge::Rising
}
} else if let Some(next_value) = next_value {
if next_value > value {
EventEdge::Rising
} else {
EventEdge::Falling
}
} else {
warn!(
"could not determine edge of {} because trajectory could be queried around {}",
event,
state.epoch()
);
EventEdge::Unclear
};

Ok(EventDetails {
edge,
state,
value,
prev_value,
next_value,
pm_duration: event.epoch_precision(),
repr: event.eval_string(&state).to_string(),
})
}
}

impl<S: Interpolatable> fmt::Display for EventDetails<S>
where
DefaultAllocator:
Allocator<f64, S::VecLength> + Allocator<f64, S::Size> + Allocator<f64, S::Size, S::Size>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let prev_fmt = match self.prev_value {
Some(value) => format!("{value:.6}"),
None => "".to_string(),
};

let next_fmt = match self.next_value {
Some(value) => format!("{value:.6}"),
None => "".to_string(),
};

write!(
f,
"{} and is {:?} (roots with {} intervals: {}, {:.6}, {})",
self.repr, self.edge, self.pm_duration, prev_fmt, self.value, next_fmt
)
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct EventArc<S: Interpolatable>
where
DefaultAllocator:
Allocator<f64, S::VecLength> + Allocator<f64, S::Size> + Allocator<f64, S::Size, S::Size>,
{
pub rise: EventDetails<S>,
pub fall: EventDetails<S>,
}

impl<S: Interpolatable> fmt::Display for EventArc<S>
where
DefaultAllocator:
Allocator<f64, S::VecLength> + Allocator<f64, S::Size> + Allocator<f64, S::Size, S::Size>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} until {} (lasts {})",
self.rise,
self.fall.state.epoch(),
self.fall.state.epoch() - self.rise.state.epoch()
)
}
}
2 changes: 1 addition & 1 deletion src/md/events/evaluators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::md::StateParameter;
use crate::utils::between_pm_x;
use crate::{Spacecraft, State};

fn angled_value(cur_angle: f64, desired_angle: f64) -> f64 {
pub(crate) fn angled_value(cur_angle: f64, desired_angle: f64) -> f64 {
if between_pm_x(cur_angle, desired_angle) > 0.0 {
cur_angle - desired_angle
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/md/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

pub mod details;
pub mod evaluators;
pub mod search;
use super::StateParameter;
use crate::cosmic::{Cosm, Frame};
use crate::linalg::allocator::Allocator;
Expand Down
Loading
Loading