///|
pub(all) struct SatelliteObservation {
satellite_id_value : String
elevation_degrees_value : Int?
azimuth_degrees_value : Int?
snr_value : Int?
} derive(Eq, Debug)
///|
pub(all) struct GsvRecord {
talker_value : String
total_messages_value : Int
message_number_value : Int
total_satellites_value : Int
observations_value : Array[SatelliteObservation]
signal_id_value : String?
} derive(Eq, Debug)
///|
fn gsv_error(message : String) -> NmeaError {
NmeaError::from_diagnostic(
Diagnostic::new(SentenceInvalid, Error, message, SourceRef::sentence(0)),
)
}
///|
fn gsv_optional_integer(
cursor : FieldCursor,
index : Int,
min : Int,
max : Int,
) -> Result[Int?, NmeaError] {
match cursor.optional(index) {
Ok(None) => Ok(None)
Ok(Some(_)) =>
match cursor.integer(index, min~, max~) {
Ok(value) => Ok(Some(value))
Err(error) => Err(error)
}
Err(error) => Err(error)
}
}
///|
pub fn parse_gsv(frame : NmeaFrame) -> Result[GsvRecord, NmeaError] {
if frame.formatter() != "GSV" {
return Err(gsv_error("frame formatter is not GSV"))
}
let cursor = FieldCursor::new(frame.fields())
let total_messages = match cursor.integer(0, min=1, max=99) {
Ok(value) => value
Err(error) => return Err(error)
}
let message_number = match cursor.integer(1, min=1, max=99) {
Ok(value) => value
Err(error) => return Err(error)
}
if message_number > total_messages {
return Err(gsv_error("GSV message number exceeds total messages"))
}
let total_satellites = match cursor.integer(2, min=0, max=999) {
Ok(value) => value
Err(error) => return Err(error)
}
let remainder = cursor.length() - 3
if remainder < 0 || (remainder % 4 != 0 && remainder % 4 != 1) {
return Err(gsv_error("GSV satellite fields are incomplete"))
}
let signal_id = if remainder % 4 == 1 {
match cursor.optional(cursor.length() - 1) {
Ok(value) => value
Err(error) => return Err(error)
}
} else {
None
}
let group_count = remainder / 4
let observations : Array[SatelliteObservation] = []
for group = 0; group < group_count; group = group + 1 {
let base = 3 + group * 4
let satellite_id = match cursor.required(base) {
Ok(value) => value
Err(error) => return Err(error)
}
let elevation = match gsv_optional_integer(cursor, base + 1, 0, 90) {
Ok(value) => value
Err(error) => return Err(error)
}
let azimuth = match gsv_optional_integer(cursor, base + 2, 0, 359) {
Ok(value) => value
Err(error) => return Err(error)
}
let snr = match gsv_optional_integer(cursor, base + 3, 0, 99) {
Ok(value) => value
Err(error) => return Err(error)
}
observations.push({
satellite_id_value: satellite_id,
elevation_degrees_value: elevation,
azimuth_degrees_value: azimuth,
snr_value: snr,
})
}
Ok({
talker_value: frame.talker(),
total_messages_value: total_messages,
message_number_value: message_number,
total_satellites_value: total_satellites,
observations_value: observations,
signal_id_value: signal_id,
})
}
///|
pub fn SatelliteObservation::satellite_id(
self : SatelliteObservation,
) -> String {
self.satellite_id_value
}
///|
pub fn SatelliteObservation::elevation_degrees(
self : SatelliteObservation,
) -> Int? {
self.elevation_degrees_value
}
///|
pub fn SatelliteObservation::azimuth_degrees(
self : SatelliteObservation,
) -> Int? {
self.azimuth_degrees_value
}
///|
pub fn SatelliteObservation::snr(self : SatelliteObservation) -> Int? {
self.snr_value
}
///|
pub fn GsvRecord::total_messages(self : GsvRecord) -> Int {
self.total_messages_value
}
///|
pub fn GsvRecord::message_number(self : GsvRecord) -> Int {
self.message_number_value
}
///|
pub fn GsvRecord::total_satellites(self : GsvRecord) -> Int {
self.total_satellites_value
}
///|
pub fn GsvRecord::observations(self : GsvRecord) -> Array[SatelliteObservation] {
self.observations_value.copy()
}
///|
pub fn GsvRecord::signal_id(self : GsvRecord) -> String? {
self.signal_id_value
}
///|
fn gsv_integer_text(value : Int?) -> String {
match value {
Some(integer) => integer.to_string()
None => ""
}
}
///|
pub fn GsvRecord::encode(self : GsvRecord) -> Result[String, NmeaError] {
if self.observations_value.length() > 4 {
return Err(gsv_error("GSV cannot encode more than four observations"))
}
let fields = [
self.total_messages_value.to_string(),
self.message_number_value.to_string(),
self.total_satellites_value.to_string(),
]
for observation in self.observations_value {
fields.push(observation.satellite_id_value)
fields.push(gsv_integer_text(observation.elevation_degrees_value))
fields.push(gsv_integer_text(observation.azimuth_degrees_value))
fields.push(gsv_integer_text(observation.snr_value))
}
match self.signal_id_value {
Some(signal_id) => fields.push(signal_id)
None => ()
}
Ok(NmeaFrame::new(36, self.talker_value, "GSV", fields).encode())
}