///|
pub(all) enum GgaFixQuality {
InvalidFix
GpsFix
DgpsFix
PpsFix
RtkFixed
RtkFloat
EstimatedFix
ManualFix
SimulationFix
UnknownFix(Int)
} derive(Eq, Debug)
///|
pub(all) struct GgaRecord {
talker_value : String
time_value : UtcTime
latitude_value : Coordinate?
longitude_value : Coordinate?
quality_value : GgaFixQuality
satellites_value : Int
hdop_value : ExactDecimal?
altitude_value : ExactDecimal?
geoid_separation_value : ExactDecimal?
dgps_age_value : ExactDecimal?
dgps_station_value : String?
} derive(Eq, Debug)
///|
fn gga_error(message : String) -> NmeaError {
NmeaError::from_diagnostic(
Diagnostic::new(SentenceInvalid, Error, message, SourceRef::sentence(0)),
)
}
///|
fn gga_quality(value : Int) -> GgaFixQuality {
match value {
0 => InvalidFix
1 => GpsFix
2 => DgpsFix
3 => PpsFix
4 => RtkFixed
5 => RtkFloat
6 => EstimatedFix
7 => ManualFix
8 => SimulationFix
other => UnknownFix(other)
}
}
///|
fn optional_gga_decimal(
cursor : FieldCursor,
index : Int,
) -> Result[ExactDecimal?, NmeaError] {
match cursor.optional(index) {
Ok(None) => Ok(None)
Ok(Some(_)) =>
match cursor.decimal(index) {
Ok(value) => Ok(Some(value))
Err(error) => Err(error)
}
Err(error) => Err(error)
}
}
///|
pub fn parse_gga(frame : NmeaFrame) -> Result[GgaRecord, NmeaError] {
if frame.formatter() != "GGA" {
return Err(gga_error("frame formatter is not GGA"))
}
let cursor = FieldCursor::new(frame.fields())
let time = match cursor.required(0) {
Ok(value) => UtcTime::parse(value, SourceRef::field(0, 0))
Err(error) => Err(error)
}
let time = match time {
Ok(value) => value
Err(error) => return Err(error)
}
let quality_value = match cursor.integer(5, min=0, max=255) {
Ok(value) => value
Err(error) => return Err(error)
}
let satellites = match cursor.integer(6, min=0, max=255) {
Ok(value) => value
Err(error) => return Err(error)
}
let quality = gga_quality(quality_value)
let latitude_text = match cursor.optional(1) {
Ok(value) => value
Err(error) => return Err(error)
}
let latitude_hemisphere = match cursor.optional(2) {
Ok(value) => value
Err(error) => return Err(error)
}
let longitude_text = match cursor.optional(3) {
Ok(value) => value
Err(error) => return Err(error)
}
let longitude_hemisphere = match cursor.optional(4) {
Ok(value) => value
Err(error) => return Err(error)
}
let (latitude, longitude) = if quality_value == 0 {
(None, None)
} else {
let latitude_text = match latitude_text {
Some(value) => value
None => return Err(gga_error("valid GGA fix is missing latitude"))
}
let latitude_hemisphere = match latitude_hemisphere {
Some(value) => value
None =>
return Err(gga_error("valid GGA fix is missing latitude hemisphere"))
}
let longitude_text = match longitude_text {
Some(value) => value
None => return Err(gga_error("valid GGA fix is missing longitude"))
}
let longitude_hemisphere = match longitude_hemisphere {
Some(value) => value
None =>
return Err(gga_error("valid GGA fix is missing longitude hemisphere"))
}
let latitude = match
parse_latitude(latitude_text, latitude_hemisphere, SourceRef::field(0, 1)) {
Ok(value) => value
Err(error) => return Err(error)
}
let longitude = match
parse_longitude(
longitude_text,
longitude_hemisphere,
SourceRef::field(0, 3),
) {
Ok(value) => value
Err(error) => return Err(error)
}
(Some(latitude), Some(longitude))
}
let hdop = match optional_gga_decimal(cursor, 7) {
Ok(value) => value
Err(error) => return Err(error)
}
let altitude = match optional_gga_decimal(cursor, 8) {
Ok(value) => value
Err(error) => return Err(error)
}
let geoid_separation = match optional_gga_decimal(cursor, 10) {
Ok(value) => value
Err(error) => return Err(error)
}
let dgps_age = match optional_gga_decimal(cursor, 12) {
Ok(value) => value
Err(error) => return Err(error)
}
let dgps_station = match cursor.optional(13) {
Ok(value) => value
Err(error) => return Err(error)
}
Ok({
talker_value: frame.talker(),
time_value: time,
latitude_value: latitude,
longitude_value: longitude,
quality_value: quality,
satellites_value: satellites,
hdop_value: hdop,
altitude_value: altitude,
geoid_separation_value: geoid_separation,
dgps_age_value: dgps_age,
dgps_station_value: dgps_station,
})
}
///|
pub fn GgaRecord::time(self : GgaRecord) -> UtcTime {
self.time_value
}
///|
pub fn GgaRecord::latitude(self : GgaRecord) -> Coordinate? {
self.latitude_value
}
///|
pub fn GgaRecord::longitude(self : GgaRecord) -> Coordinate? {
self.longitude_value
}
///|
pub fn GgaRecord::quality(self : GgaRecord) -> GgaFixQuality {
self.quality_value
}
///|
pub fn GgaRecord::satellites(self : GgaRecord) -> Int {
self.satellites_value
}
///|
pub fn GgaRecord::hdop(self : GgaRecord) -> ExactDecimal? {
self.hdop_value
}
///|
pub fn GgaRecord::altitude(self : GgaRecord) -> ExactDecimal? {
self.altitude_value
}
///|
pub fn GgaRecord::geoid_separation(self : GgaRecord) -> ExactDecimal? {
self.geoid_separation_value
}
///|
fn gga_decimal_text(value : ExactDecimal?) -> String {
match value {
Some(value) => value.to_string()
None => ""
}
}
///|
pub fn GgaRecord::encode(self : GgaRecord) -> Result[String, NmeaError] {
let latitude = match self.latitude_value {
Some(value) => value.wire().to_string()
None => ""
}
let latitude_hemisphere = match self.latitude_value {
Some(value) => if value.hemisphere() == 78 { "N" } else { "S" }
None => ""
}
let longitude = match self.longitude_value {
Some(value) => coordinate_wire_text(value)
None => ""
}
let longitude_hemisphere = match self.longitude_value {
Some(value) => if value.hemisphere() == 69 { "E" } else { "W" }
None => ""
}
let quality = match self.quality_value {
InvalidFix => 0
GpsFix => 1
DgpsFix => 2
PpsFix => 3
RtkFixed => 4
RtkFloat => 5
EstimatedFix => 6
ManualFix => 7
SimulationFix => 8
UnknownFix(value) => value
}
let frame = NmeaFrame::new(36, self.talker_value, "GGA", [
pad2(self.time_value.hour()) +
pad2(self.time_value.minute()) +
self.time_value.second_wire_value.to_string(),
latitude,
latitude_hemisphere,
longitude,
longitude_hemisphere,
quality.to_string(),
self.satellites_value.to_string(),
gga_decimal_text(self.hdop_value),
gga_decimal_text(self.altitude_value),
"M",
gga_decimal_text(self.geoid_separation_value),
"M",
gga_decimal_text(self.dgps_age_value),
match self.dgps_station_value {
Some(value) => value
None => ""
},
])
Ok(frame.encode())
}