Skip to main content

kinavis_traffic/
observation.rs

1//! Single target observation: position and time.
2
3use kinavis::error::Result;
4use kinavis::relative_motion::Contact;
5use kinavis::sailings::rhumb_destination;
6use kinavis_kernel::angle::TrueCourse;
7use kinavis_kernel::event::TargetId;
8use kinavis_kernel::position::Position;
9use kinavis_kernel::snapshot::GroundTrack;
10use kinavis_kernel::time::{Instant, Utc};
11
12/// One sighting of a target: radar bearing and range, AIS position report, or
13/// any other position source.
14///
15/// Position is the common denominator from which tracks are built. Sources that
16/// also report course and speed over ground (AIS) add them; the track prefers
17/// them to values fitted from positions.
18#[derive(Debug, Clone, Copy, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct TargetObservation {
21    target: TargetId,
22    position: Position,
23    at: Instant<Utc>,
24    ground_track: Option<GroundTrack>,
25    heading: Option<TrueCourse>,
26}
27
28impl TargetObservation {
29    /// Target at a position at an instant.
30    #[must_use]
31    pub const fn new(target: TargetId, position: Position, at: Instant<Utc>) -> Self {
32        Self {
33            target,
34            position,
35            at,
36            ground_track: None,
37            heading: None,
38        }
39    }
40
41    /// Radar plot: true bearing and range from own position at the time of the
42    /// plot.
43    ///
44    /// # Errors
45    ///
46    /// As [`rhumb_destination`]: a plot beyond a pole.
47    pub fn from_contact(
48        target: TargetId,
49        own: Position,
50        contact: Contact,
51        at: Instant<Utc>,
52    ) -> Result<Self> {
53        let position = rhumb_destination(
54            own,
55            TrueCourse::new(contact.bearing.degrees())?,
56            contact.range,
57        )?;
58        Ok(Self::new(target, position, at))
59    }
60
61    /// Adds the target's reported course and speed over ground.
62    #[must_use]
63    pub const fn with_ground_track(mut self, ground_track: GroundTrack) -> Self {
64        self.ground_track = Some(ground_track);
65        self
66    }
67
68    /// Adds the target's reported heading.
69    #[must_use]
70    pub const fn with_heading(mut self, heading: TrueCourse) -> Self {
71        self.heading = Some(heading);
72        self
73    }
74
75    /// Target.
76    #[must_use]
77    pub const fn target(&self) -> TargetId {
78        self.target
79    }
80
81    /// Position.
82    #[must_use]
83    pub const fn position(&self) -> Position {
84        self.position
85    }
86
87    /// Time.
88    #[must_use]
89    pub const fn at(&self) -> Instant<Utc> {
90        self.at
91    }
92
93    /// Reported course and speed, if any.
94    #[must_use]
95    pub const fn ground_track(&self) -> Option<GroundTrack> {
96        self.ground_track
97    }
98
99    /// Reported heading, if any.
100    #[must_use]
101    pub const fn heading(&self) -> Option<TrueCourse> {
102        self.heading
103    }
104}