Преглед изворни кода

Calculate altitudes in LOC records

The RFC specifies that altitudes in LOC records are offset by 100,000m, which is now taken into account when displaying them.
Benjamin Sago пре 4 година
родитељ
комит
d5692e8d6e
2 измењених фајлова са 68 додато и 5 уклоњено
  1. 67 4
      dns/src/record/loc.rs
  2. 1 1
      src/output.rs

+ 67 - 4
dns/src/record/loc.rs

@@ -36,7 +36,7 @@ pub struct LOC {
 
     /// The altitude of the centre of the sphere, measured in centimetres
     /// above a base of 100,000 metres below the GPS reference spheroid.
-    pub altitude: u32,
+    pub altitude: Altitude,
 }
 
 /// A measure of size, in centimetres, represented by a base and an exponent.
@@ -56,6 +56,13 @@ pub struct Position {
     direction: Direction,
 }
 
+/// A position on the vertical axis.
+#[derive(PartialEq, Debug, Copy, Clone)]
+pub struct Altitude {
+    metres: i64,
+    centimetres: i64,
+}
+
 /// One of the directions a position could be in, relative to the equator or
 /// prime meridian.
 #[derive(PartialEq, Debug, Copy, Clone)]
@@ -105,8 +112,9 @@ impl Wire for LOC {
         let longitude = Position::from_u32(longitude_num, false);
         trace!("Parsed longitude -> {:?} ({:?})", longitude_num, longitude);
 
-        let altitude = c.read_u32::<BigEndian>()?;
-        trace!("Parsed altitude -> {:?}", altitude);
+        let altitude_num = c.read_u32::<BigEndian>()?;
+        let altitude = Altitude::from_u32(altitude_num);
+        trace!("Parsed altitude -> {:?} ({:})", altitude_num, altitude);
 
         Ok(Self {
             size, horizontal_precision, vertical_precision, latitude, longitude, altitude,
@@ -170,6 +178,17 @@ impl Position {
     }
 }
 
+impl Altitude {
+    fn from_u32(input: u32) -> Self {
+        let mut input = i64::from(input);
+        input -= 100_000_00;  // 100,000m
+        let metres = input / 100;
+        let centimetres = input % 100;
+        Self { metres, centimetres }
+    }
+}
+
+
 impl fmt::Display for Size {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}e{}", self.base, self.power_of_ten)
@@ -203,6 +222,19 @@ impl fmt::Display for Direction {
     }
 }
 
+impl fmt::Display for Altitude {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Usually there's a space between the number and the unit, but
+        // spaces are already used to delimit segments in the record summary
+        if self.centimetres == 0 {
+            write!(f, "{}m", self.metres)
+        }
+        else {
+            write!(f, "{}.{:02}m", self.metres, self.centimetres)
+        }
+    }
+}
+
 
 #[cfg(test)]
 mod test {
@@ -228,7 +260,7 @@ mod test {
                        vertical_precision: 0,
                        latitude:  Position::from_u32(0x_8b_0d_2c_8c, true),
                        longitude: Position::from_u32(0x_7f_f8_fc_a5, false),
-                       altitude:  0x_00_98_96_80,
+                       altitude:  Altitude::from_u32(0x_00_98_96_80),
                    });
     }
 
@@ -427,3 +459,34 @@ mod position_test {
                    None);
     }
 }
+
+
+#[cfg(test)]
+mod altitude_test {
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn base_level() {
+        assert_eq!(Altitude::from_u32(10000000).to_string(),
+                   String::from("0m"));
+    }
+
+    #[test]
+    fn up_high() {
+        assert_eq!(Altitude::from_u32(20000000).to_string(),
+                   String::from("100000m"));
+    }
+
+    #[test]
+    fn down_low() {
+        assert_eq!(Altitude::from_u32(0).to_string(),
+                   String::from("-100000m"));
+    }
+
+    #[test]
+    fn with_decimal() {
+        assert_eq!(Altitude::from_u32(50505050).to_string(),
+                   String::from("405050.50m"));
+    }
+}

+ 1 - 1
src/output.rs

@@ -401,7 +401,7 @@ fn json_record(record: &Record) -> JsonValue {
                 "point": {
                     "latitude": rec.latitude.map(|e| e.to_string()),
                     "longitude": rec.longitude.map(|e| e.to_string()),
-                    "altitude": rec.altitude,
+                    "altitude": rec.altitude.to_string(),
                 },
             })
         }