diff --git a/src/md/events/details.rs b/src/md/events/details.rs index ba8078ed..8694df0b 100644 --- a/src/md/events/details.rs +++ b/src/md/events/details.rs @@ -27,14 +27,13 @@ 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. -/// -/// # Variants -/// - `Rising` - 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. -/// - `Falling` - 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 #[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, } @@ -44,11 +43,6 @@ pub enum EventEdge { /// /// # 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. -/// -/// # Fields -/// - `state: S` - -/// - `edge: EventEdge` - -/// - `value: f64` - The numerical evaluation of the event for the returned state. #[derive(Clone, Debug, PartialEq)] pub struct EventDetails where diff --git a/src/od/simulator/arc.rs b/src/od/simulator/arc.rs index 08254077..20723a62 100644 --- a/src/od/simulator/arc.rs +++ b/src/od/simulator/arc.rs @@ -58,9 +58,6 @@ where pub trajectory: Traj, /// Configuration of each device pub configs: HashMap, - /// Set to true to allow for overlapping measurements (enabled by default), - /// i.e. if N ground stations are available at a given epoch, generate N measurements not just one. - pub allow_overlap: bool, /// Random number generator used for this tracking arc, ensures repeatability rng: Pcg64Mcg, /// Greatest common denominator time series that allows this arc to meet all of the conditions. @@ -131,7 +128,6 @@ where devices: devices_map, trajectory, configs, - allow_overlap: false, rng, time_series, _msr_in: PhantomData, @@ -290,7 +286,10 @@ impl TrackingArcSim { /// 3. Find when the vehicle trajectory has an elevation less than zero (i.e. disappears below the horizon), after that initial epoch /// 4. Repeat 2, 3 until the end of the trajectory /// 5. Build each of these as "tracking strands" for this tracking device. - /// 6. THEN REMOVE OVERLAPPING MEASUREMENTS - ALGO TBD + /// 6. Organize all of the built tracking strands chronologically. + /// 7. Iterate through all of the strands: + /// 7.a. if that tracker is marked as `Greedy` and it ends after the start of the next strand, change the start date of the next strand. + /// 7.b. if that tracker is marked as `Eager` and it ends after the start of the next strand, change the end date of the current strand. pub fn generate_schedule( &self, cosm: Arc, @@ -438,7 +437,10 @@ impl TrackingArcSim { /// 3. Find when the vehicle trajectory has an elevation less than zero (i.e. disappears below the horizon), after that initial epoch /// 4. Repeat 2, 3 until the end of the trajectory /// 5. Build each of these as "tracking strands" for this tracking device. - /// 6. THEN REMOVE OVERLAPPING MEASUREMENTS - ALGO TBD + /// 6. Organize all of the built tracking strands chronologically. + /// 7. Iterate through all of the strands: + /// 7.a. if that tracker is marked as `Greedy` and it ends after the start of the next strand, change the start date of the next strand. + /// 7.b. if that tracker is marked as `Eager` and it ends after the start of the next strand, change the end date of the current strand. pub fn generate_schedule( &self, cosm: Arc, @@ -513,7 +515,50 @@ impl TrackingArcSim { } } } - // todo!("remove overlaps") + + // Build all of the strands, remembering which tracker they come from. + let mut cfg_as_vec = Vec::new(); + for (name, cfg) in &built_cfg { + for (ii, strand) in cfg.strands.as_ref().unwrap().iter().enumerate() { + cfg_as_vec.push((name.clone(), ii, *strand)); + } + } + // Iterate through the strands by chronological order. Cannot use maps because we change types. + cfg_as_vec.sort_by_key(|(_, _, strand)| strand.start); + for (ii, (this_name, this_pos, this_strand)) in + cfg_as_vec.iter().take(cfg_as_vec.len() - 1).enumerate() + { + // Grab the config + if let Some(config) = self.configs[this_name].scheduler.as_ref() { + // Grab the next strand, chronologically + if let Some((next_name, next_pos, next_strand)) = cfg_as_vec.get(ii + 1) { + if config.handoff == Handoff::Greedy && this_strand.end >= next_strand.start { + // Modify the built configurations to change the start time of the next strand because the current one is greedy. + let next_config = built_cfg.get_mut(next_name).unwrap(); + let new_start = this_strand.end + next_config.sampling; + next_config.strands.as_mut().unwrap()[*next_pos].start = new_start; + info!( + "{this_name} configured as {:?}, so {next_name} now starts on {new_start}", + config.handoff + ); + } else if config.handoff == Handoff::Eager + && this_strand.end >= next_strand.start + { + let this_config = built_cfg.get_mut(this_name).unwrap(); + let new_end = next_strand.start - this_config.sampling; + this_config.strands.as_mut().unwrap()[*this_pos].end = new_end; + info!( + "{this_name} now hands off to {next_name} on {new_end} because it's configured as {:?}", + config.handoff + ); + } + } else { + // Reached the end + break; + } + } + } + Ok(built_cfg) } diff --git a/src/od/simulator/scheduler.rs b/src/od/simulator/scheduler.rs index d4f79e19..821da618 100644 --- a/src/od/simulator/scheduler.rs +++ b/src/od/simulator/scheduler.rs @@ -172,7 +172,7 @@ mod scheduler_ut { let serialized = serde_yaml::to_string(&scheduler).unwrap(); assert_eq!( serialized, - "handoff: Eager\ncadence: !Intermittent\n on: 12 min\n off: 17 h 5 min\nmin_samples: 10\nsample_alignment: null\n" + "handoff: Eager\ncadence: !Intermittent\n on: 12 min\n off: 17 h 5 min\nmin_samples: 10\nsample_alignment: 1 s\n" ); let deserd: Scheduler = serde_yaml::from_str(&serialized).unwrap(); assert_eq!(deserd, scheduler); diff --git a/src/od/simulator/trkconfig.rs b/src/od/simulator/trkconfig.rs index d71ee653..d8b593c3 100644 --- a/src/od/simulator/trkconfig.rs +++ b/src/od/simulator/trkconfig.rs @@ -213,7 +213,10 @@ mod trkconfig_ut { println!("{serialized}"); let deserd: TrkConfig = serde_yaml::from_str(&serialized).unwrap(); assert_eq!(deserd, cfg); - assert_eq!(cfg.scheduler.unwrap(), Scheduler::default()); + assert_eq!( + cfg.scheduler.unwrap(), + Scheduler::builder().min_samples(10).build() + ); assert!(cfg.strands.is_none()); // Specify an intermittent schedule and a specific start epoch. diff --git a/src/python/orbit_determination/arc.rs b/src/python/orbit_determination/arc.rs index 6e2d7dac..ff5ea9f1 100644 --- a/src/python/orbit_determination/arc.rs +++ b/src/python/orbit_determination/arc.rs @@ -46,7 +46,6 @@ impl GroundTrackingArcSim { trajectory: TrajectoryLoader, configs: HashMap, seed: u64, - _allow_overlap: Option, ) -> Result { // Try to convert the dynamic trajectory into a trajectory let inner = if let Ok(sc_traj) = trajectory.to_traj::() { diff --git a/tests/orbit_determination/two_body.rs b/tests/orbit_determination/two_body.rs index e3f2b666..0f50726b 100644 --- a/tests/orbit_determination/two_body.rs +++ b/tests/orbit_determination/two_body.rs @@ -644,9 +644,6 @@ fn od_tb_ckf_fixed_step_iteration_test() { let arc = arc_sim.generate_measurements(cosm.clone()).unwrap(); - // Check that we have the same number of measurements as before the behavior change. - // assert_eq!(arc.measurements.len(), 7954); - // Now that we have the truth data, let's start an OD with no noise at all and compute the estimates. // We expect the estimated orbit to be perfect since we're using strictly the same dynamics, no noise on // the measurements, and the same time step.