aprs_encode/
ddm.rs

1use crate::stack_str::PackArrayString;
2
3/// Description of an angle in Degrees + Minutes
4#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
5pub struct DegreeMinutes {
6    pub degrees: i64,
7    pub minutes: f32,
8}
9
10impl From<f32> for DegreeMinutes {
11    // Create DegreeMinutes from a number of degrees
12    fn from(dd: f32) -> Self {
13        let degrees = (dd
14            - match dd % 1.0 >= 0.0 {
15                true => dd % 1.0,
16                false => 1.0 + dd % 1.0,
17            }) as i64;
18        return DegreeMinutes {
19            degrees,
20            minutes: (dd - degrees as f32) * 60.0,
21        };
22    }
23}
24
25impl PackArrayString for DegreeMinutes {
26    fn pack_into<const SIZE: usize>(
27        &self,
28        s: &mut arrayvec::ArrayString<SIZE>,
29    ) -> Result<(), crate::errors::PackError> {
30        // Handle Degrees
31        s.try_push(('0' as u8 + ((self.degrees / 10) % 10) as u8) as char)?;
32        s.try_push(('0' as u8 + (self.degrees % 10) as u8) as char)?;
33
34        // Handle Minutes
35        s.try_push(('0' as u8 + ((self.minutes / 10.0) % 10.0) as u8) as char)?;
36        s.try_push(('0' as u8 + (self.minutes % 10.0) as u8) as char)?;
37
38        // Push decimal
39        s.try_push('.')?;
40
41        // Handle Minute decimals
42        s.try_push(('0' as u8 + ((self.minutes * 10.0) % 10.0) as u8) as char)?;
43        s.try_push(('0' as u8 + ((self.minutes * 100.0) % 10.0) as u8) as char)?;
44
45        Ok(())
46    }
47}
48
49/// Enum representation of the 4 cardinal Directions
50#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
51pub enum CardinalDirection {
52    North,
53    East,
54    South,
55    West,
56}
57
58impl PackArrayString for CardinalDirection {
59    fn pack_into<const SIZE: usize>(
60        &self,
61        s: &mut arrayvec::ArrayString<SIZE>,
62    ) -> Result<(), crate::errors::PackError> {
63        s.try_push(match self {
64            Self::North => 'N',
65            Self::East => 'E',
66            Self::South => 'S',
67            Self::West => 'W',
68        })?;
69        Ok(())
70    }
71}
72
73/// Representation of an angle in Degree/Decimal/Minutes
74pub trait DdmAngle {
75    fn get_direction(&self) -> CardinalDirection;
76    fn get_degree_minutes(&self) -> DegreeMinutes;
77}
78
79/// Latitude in DDM
80#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
81pub struct DdmLatitude {
82    pub ddm: DegreeMinutes,
83    pub direction: CardinalDirection,
84}
85
86impl From<f32> for DdmLatitude {
87    fn from(latitude: f32) -> Self {
88        Self {
89            ddm: match latitude < 0.0 {
90                true => latitude * -1.0,
91                false => latitude,
92            }
93            .into(),
94            direction: match latitude < 0.0 {
95                true => CardinalDirection::South,
96                false => CardinalDirection::North,
97            },
98        }
99    }
100}
101
102impl DdmAngle for DdmLatitude {
103    fn get_direction(&self) -> CardinalDirection {
104        self.direction
105    }
106
107    fn get_degree_minutes(&self) -> DegreeMinutes {
108        self.ddm
109    }
110}
111
112impl PackArrayString for DdmLatitude {
113    fn pack_into<const SIZE: usize>(
114        &self,
115        s: &mut arrayvec::ArrayString<SIZE>,
116    ) -> Result<(), crate::errors::PackError> {
117        // Push DDM
118        self.ddm.pack_into(s)?;
119
120        // Push Direction
121        self.direction.pack_into(s)?;
122
123        Ok(())
124    }
125}
126
127/// Longitude in DDM
128#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
129pub struct DdmLongitude {
130    pub ddm: DegreeMinutes,
131    pub direction: CardinalDirection,
132}
133
134impl From<f32> for DdmLongitude {
135    fn from(longitude: f32) -> Self {
136        Self {
137            ddm: match longitude < 0.0 {
138                true => longitude * -1.0,
139                false => longitude,
140            }
141            .into(),
142            direction: match longitude < 0.0 {
143                true => CardinalDirection::West,
144                false => CardinalDirection::East,
145            },
146        }
147    }
148}
149
150impl DdmAngle for DdmLongitude {
151    fn get_direction(&self) -> CardinalDirection {
152        self.direction
153    }
154
155    fn get_degree_minutes(&self) -> DegreeMinutes {
156        self.ddm
157    }
158}
159
160impl PackArrayString for DdmLongitude {
161    fn pack_into<const SIZE: usize>(
162        &self,
163        s: &mut arrayvec::ArrayString<SIZE>,
164    ) -> Result<(), crate::errors::PackError> {
165        // Push DDM
166        s.try_push(('0' as u8 + (self.ddm.degrees / 100) as u8) as char)?;
167        self.ddm.pack_into(s)?;
168
169        // Push Direction
170        self.direction.pack_into(s)?;
171
172        Ok(())
173    }
174}