kinavis_traffic/
observation.rs1use 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#[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 #[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 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 #[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 #[must_use]
70 pub const fn with_heading(mut self, heading: TrueCourse) -> Self {
71 self.heading = Some(heading);
72 self
73 }
74
75 #[must_use]
77 pub const fn target(&self) -> TargetId {
78 self.target
79 }
80
81 #[must_use]
83 pub const fn position(&self) -> Position {
84 self.position
85 }
86
87 #[must_use]
89 pub const fn at(&self) -> Instant<Utc> {
90 self.at
91 }
92
93 #[must_use]
95 pub const fn ground_track(&self) -> Option<GroundTrack> {
96 self.ground_track
97 }
98
99 #[must_use]
101 pub const fn heading(&self) -> Option<TrueCourse> {
102 self.heading
103 }
104}