libits_client/reception/exchange/
reference_position.rs1use core::fmt;
10use std::f64::consts;
11
12use cheap_ruler::{CheapRuler, DistanceUnit};
13use navigation::Location;
14use serde::{Deserialize, Serialize};
15
16const EARTH_RADIUS: u32 = 6371000;
17const LG_MOD: u8 = 180;
19const COORDINATE_SIGNIFICANT_DIGIT: u8 = 7;
21
22const ALTITUDE_SIGNIFICANT_DIGIT: u8 = 3;
23
24#[derive(Clone, Default, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
25pub struct ReferencePosition {
26 pub latitude: i32,
27 pub longitude: i32,
28 pub altitude: i32,
29}
30
31impl ReferencePosition {
32 pub fn get_distance(&self, other: &ReferencePosition) -> u32 {
33 let longitude = get_coordinate(self.longitude) * consts::PI / LG_MOD as f64;
34 let latitude = get_coordinate(self.latitude) * consts::PI / LG_MOD as f64;
35 let other_longitude = get_coordinate(other.longitude) * consts::PI / LG_MOD as f64;
36 let other_latitude = get_coordinate(other.latitude) * consts::PI / LG_MOD as f64;
37
38 let longitude_distance = other_longitude - longitude;
39 let latitude_distance = other_latitude - latitude;
40
41 let a = (latitude_distance / 2.0).sin() * (latitude_distance / 2.0).sin()
43 + latitude.cos()
44 * other_latitude.cos()
45 * (longitude_distance / 2.0).sin()
46 * (longitude_distance / 2.0).sin();
47
48 let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
49
50 let distance = EARTH_RADIUS as f64 * c;
51 distance.round() as u32
52 }
53
54 pub fn get_heading(&self, other: &ReferencePosition) -> u16 {
55 let self_location = Location::new(
57 get_coordinate(self.latitude),
58 get_coordinate(self.longitude),
59 );
60 let other_location = Location::new(
61 get_coordinate(other.latitude),
62 get_coordinate(other.longitude),
63 );
64
65 let bearing = self_location.calc_bearing_to(&other_location);
66 (bearing * 10_f64).round() as u16
67 }
68
69 pub fn get_destination(&self, distance: f64, bearing: f64) -> Self {
70 let longitude_coordinate = get_coordinate(self.longitude);
71 let latitude_coordinate = get_coordinate(self.latitude);
72 let cr = CheapRuler::new(latitude_coordinate, DistanceUnit::Meters);
73 let p1 = (longitude_coordinate, latitude_coordinate).into();
74 let destination = cr.destination(&p1, distance, bearing);
75 ReferencePosition {
76 longitude: get_etsi_coordinate(destination.x()),
77 latitude: get_etsi_coordinate(destination.y()),
78 altitude: self.altitude,
79 }
80 }
81}
82
83impl fmt::Display for ReferencePosition {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(
86 f,
87 "lat {}/long {}/alt {}",
88 get_coordinate(self.latitude),
89 get_coordinate(self.longitude),
90 get_altitude(self.altitude),
91 )
92 }
93}
94
95fn get_coordinate(etsi_coordinate: i32) -> f64 {
96 let base: i32 = 10;
97 etsi_coordinate as f64 / base.pow(COORDINATE_SIGNIFICANT_DIGIT as u32) as f64
98}
99
100fn get_etsi_coordinate(coordinate: f64) -> i32 {
101 let base: i32 = 10;
102 (coordinate * base.pow(COORDINATE_SIGNIFICANT_DIGIT as u32) as f64) as i32
103}
104
105fn get_altitude(etsi_altitude: i32) -> f64 {
106 let base: i32 = 10;
107 etsi_altitude as f64 / base.pow(ALTITUDE_SIGNIFICANT_DIGIT as u32) as f64
108}
109
110#[cfg(test)]
111mod tests {
112 use navigation::Location;
113
114 use crate::reception::exchange::ReferencePosition;
115
116 fn teqmo_lane_merge_reference_postion() -> ReferencePosition {
117 ReferencePosition {
119 latitude: 486244870,
120 longitude: 22436370,
121 ..Default::default() }
123 }
124
125 fn teqmo_city_reference_postion() -> ReferencePosition {
126 ReferencePosition {
128 latitude: 486249990,
129 longitude: 22412116,
130 ..Default::default() }
132 }
133
134 #[test]
135 fn compute_100_meters_distance() {
136 let position = teqmo_lane_merge_reference_postion();
137 let other_position = ReferencePosition {
139 latitude: 486237420,
140 longitude: 22428750,
141 ..Default::default() };
143 assert_eq!(position.get_distance(&other_position), 100);
144 }
145
146 #[test]
147 fn compute_31_meters_distance() {
148 let position = teqmo_city_reference_postion();
150 let other_position = ReferencePosition {
152 latitude: 486252239,
153 longitude: 22409705,
154 ..Default::default() };
156 assert_eq!(position.get_distance(&other_position), 31);
157 }
158
159 #[test]
160 fn calc_bearing_montlhery1_to_montlhery2() {
161 let montlhery_1 = Location::new(48.6250323, 2.2412096);
162 let montlhery_2 = Location::new(48.6249755, 2.2412662);
163
164 assert_eq!(
165 "146.63",
166 format!("{:.2}", montlhery_1.calc_bearing_to(&montlhery_2))
167 );
168 assert_eq!(
169 "142.57",
170 format!(
171 "{:.2}",
172 montlhery_1.estimate_bearing_to(&montlhery_2, 69.0, 53.0)
173 )
174 );
175 }
176
177 #[test]
178 fn calc_bearing_montlhery3_to_montlhery4() {
179 let montlhery_3 = Location::new(48.6234734, 2.2397949);
180 let montlhery_4 = Location::new(48.6234641, 2.2398374);
181
182 assert_eq!(
183 "108.32",
184 format!("{:.2}", montlhery_3.calc_bearing_to(&montlhery_4))
185 );
186 assert_eq!(
187 "105.90",
188 format!(
189 "{:.2}",
190 montlhery_3.estimate_bearing_to(&montlhery_4, 69.0, 53.0)
191 )
192 );
193 }
194
195 #[test]
196 fn get_heading_montlhery3_to_montlhery4() {
197 let montlhery_3 = ReferencePosition {
198 latitude: 486234734,
199 longitude: 22397949,
200 altitude: 15710,
201 };
202 let montlhery_4 = ReferencePosition {
203 latitude: 486234641,
204 longitude: 22398374,
205 altitude: 15773,
206 };
207 assert_eq!(1083, montlhery_3.get_heading(&montlhery_4));
208 }
209
210 #[test]
211 fn calc_bearing_boulder_to_dia() {
212 let dia = Location::new(39.8617, -104.6731);
214
215 let boulder = Location::new(40.0274, -105.2519);
217
218 assert_eq!("110.48", format!("{:.*}", 2, boulder.calc_bearing_to(&dia)));
219 assert_eq!(
220 "110.44",
221 format!("{:.*}", 2, boulder.estimate_bearing_to(&dia, 69.0, 53.0))
222 );
223 }
224
225 #[test]
226 fn it_can_get_south_destination() {
227 let position = teqmo_lane_merge_reference_postion();
228 let other_position = ReferencePosition {
230 latitude: 486235877,
231 longitude: position.longitude,
232 ..Default::default() };
234 assert_eq!(position.get_destination(100.0, 180.0), other_position);
235 assert_eq!(position.get_destination(100.0, -180.0), other_position);
236 }
237
238 #[test]
239 fn it_can_get_north_destination() {
240 let position = teqmo_lane_merge_reference_postion();
241 let other_position = ReferencePosition {
243 latitude: 486253862,
244 longitude: position.longitude,
245 ..Default::default() };
247 assert_eq!(position.get_destination(100.0, 0.0), other_position);
248 assert_eq!(position.get_destination(100.0, 360.0), other_position);
249 }
250
251 #[test]
252 fn it_can_get_east_destination() {
253 let position = teqmo_lane_merge_reference_postion();
254 let other_position = ReferencePosition {
256 latitude: position.latitude,
257 longitude: 22449934,
258 ..Default::default() };
260 assert_eq!(position.get_destination(100.0, 90.0), other_position);
261 assert_eq!(position.get_destination(100.0, -270.0), other_position);
262 }
263
264 #[test]
265 fn it_can_get_west_destination() {
266 let position = teqmo_lane_merge_reference_postion();
267 let other_position = ReferencePosition {
269 latitude: position.latitude,
270 longitude: 22422805,
271 ..Default::default() };
273 assert_eq!(position.get_destination(100.0, 270.0), other_position);
274 assert_eq!(position.get_destination(100.0, -90.0), other_position);
275 }
276}