///|
pub(all) enum RmcStatus {
Active
Void
} derive(Eq, Debug)
///|
pub(all) struct RmcRecord {
talker_value : String
time_value : UtcTime
status_value : RmcStatus
latitude_value : Coordinate?
longitude_value : Coordinate?
speed_knots_value : ExactDecimal?
course_value : ExactDecimal?
date_value : UtcDate
magnetic_variation_value : ExactDecimal?
magnetic_direction_value : String?
mode_value : String?
} derive(Eq, Debug)
///|
fn rmc_error(message : String) -> NmeaError {
NmeaError::from_diagnostic(
Diagnostic::new(SentenceInvalid, Error, message, SourceRef::sentence(0)),
)
}
///|
fn optional_rmc_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_rmc(frame : NmeaFrame) -> Result[RmcRecord, NmeaError] {
if frame.formatter() != "RMC" {
return Err(rmc_error("frame formatter is not RMC"))
}
let cursor = FieldCursor::new(frame.fields())
let time_text = match cursor.required(0) {
Ok(value) => value
Err(error) => return Err(error)
}
let time = match UtcTime::parse(time_text, SourceRef::field(0, 0)) {
Ok(value) => value
Err(error) => return Err(error)
}
let status_text = match cursor.required(1) {
Ok(value) => value
Err(error) => return Err(error)
}
let status = if status_text == "A" {
Active
} else if status_text == "V" {
Void
} else {
return Err(rmc_error("RMC status must be A or V"))
}
let date_text = match cursor.required(8) {
Ok(value) => value
Err(error) => return Err(error)
}
let date = match UtcDate::parse_rmc(date_text, SourceRef::field(0, 8)) {
Ok(value) => value
Err(error) => return Err(error)
}
let latitude_text = match cursor.optional(2) {
Ok(value) => value
Err(error) => return Err(error)
}
let latitude_hemi = match cursor.optional(3) {
Ok(value) => value
Err(error) => return Err(error)
}
let longitude_text = match cursor.optional(4) {
Ok(value) => value
Err(error) => return Err(error)
}
let longitude_hemi = match cursor.optional(5) {
Ok(value) => value
Err(error) => return Err(error)
}
let (latitude, longitude) = if status is Void {
(None, None)
} else {
let lat_text = match latitude_text {
Some(value) => value
None => return Err(rmc_error("active RMC is missing latitude"))
}
let lat_hemi = match latitude_hemi {
Some(value) => value
None => return Err(rmc_error("active RMC is missing latitude hemisphere"))
}
let lon_text = match longitude_text {
Some(value) => value
None => return Err(rmc_error("active RMC is missing longitude"))
}
let lon_hemi = match longitude_hemi {
Some(value) => value
None =>
return Err(rmc_error("active RMC is missing longitude hemisphere"))
}
let lat = match parse_latitude(lat_text, lat_hemi, SourceRef::field(0, 2)) {
Ok(value) => value
Err(error) => return Err(error)
}
let lon = match
parse_longitude(lon_text, lon_hemi, SourceRef::field(0, 4)) {
Ok(value) => value
Err(error) => return Err(error)
}
(Some(lat), Some(lon))
}
let speed = match optional_rmc_decimal(cursor, 6) {
Ok(value) => value
Err(error) => return Err(error)
}
let course = match optional_rmc_decimal(cursor, 7) {
Ok(value) => value
Err(error) => return Err(error)
}
let magnetic = match optional_rmc_decimal(cursor, 9) {
Ok(value) => value
Err(error) => return Err(error)
}
let magnetic_direction = match cursor.optional(10) {
Ok(value) => value
Err(error) => return Err(error)
}
if magnetic is Some(_) &&
magnetic_direction != Some("E") &&
magnetic_direction != Some("W") {
return Err(rmc_error("RMC magnetic variation requires E or W direction"))
}
let mode = match cursor.optional(11) {
Ok(value) => value
Err(error) => return Err(error)
}
Ok({
talker_value: frame.talker(),
time_value: time,
status_value: status,
latitude_value: latitude,
longitude_value: longitude,
speed_knots_value: speed,
course_value: course,
date_value: date,
magnetic_variation_value: magnetic,
magnetic_direction_value: magnetic_direction,
mode_value: mode,
})
}
///|
pub fn RmcRecord::status(self : RmcRecord) -> RmcStatus {
self.status_value
}
///|
pub fn RmcRecord::time(self : RmcRecord) -> UtcTime {
self.time_value
}
///|
pub fn RmcRecord::date(self : RmcRecord) -> UtcDate {
self.date_value
}
///|
pub fn RmcRecord::latitude(self : RmcRecord) -> Coordinate? {
self.latitude_value
}
///|
pub fn RmcRecord::longitude(self : RmcRecord) -> Coordinate? {
self.longitude_value
}
///|
pub fn RmcRecord::speed_knots(self : RmcRecord) -> ExactDecimal? {
self.speed_knots_value
}
///|
pub fn RmcRecord::course(self : RmcRecord) -> ExactDecimal? {
self.course_value
}
///|
pub fn RmcRecord::magnetic_variation(self : RmcRecord) -> ExactDecimal? {
self.magnetic_variation_value
}
///|
fn rmc_optional_decimal_text(value : ExactDecimal?) -> String {
match value {
Some(decimal) => decimal.to_string()
None => ""
}
}
///|
fn rmc_optional_text(value : String?) -> String {
match value {
Some(text) => text
None => ""
}
}
///|
pub fn RmcRecord::encode(self : RmcRecord) -> Result[String, NmeaError] {
let latitude = match self.latitude_value {
Some(value) => coordinate_wire_text(value)
None => ""
}
let latitude_hemi = 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_hemi = match self.longitude_value {
Some(value) => if value.hemisphere() == 69 { "E" } else { "W" }
None => ""
}
let status = if self.status_value is Active { "A" } else { "V" }
let frame = NmeaFrame::new(36, self.talker_value, "RMC", [
utc_time_wire(self.time_value),
status,
latitude,
latitude_hemi,
longitude,
longitude_hemi,
rmc_optional_decimal_text(self.speed_knots_value),
rmc_optional_decimal_text(self.course_value),
utc_date_rmc_wire(self.date_value),
rmc_optional_decimal_text(self.magnetic_variation_value),
rmc_optional_text(self.magnetic_direction_value),
rmc_optional_text(self.mode_value),
])
Ok(frame.encode())
}