///|
pub(all) struct NmeaChecksum {
value : Int
}
///|
fn checksum_error(code : NmeaErrorCode, message : String) -> NmeaError {
NmeaError::from_diagnostic(
Diagnostic::new(code, Error, message, SourceRef::sentence(0)),
)
}
///|
/// Returns the XOR of all ASCII code units in a sentence payload.
pub fn NmeaChecksum::calculate(payload : String) -> Int {
let mut value = 0
for index = 0; index < payload.length(); index = index + 1 {
value = value ^ payload[index].to_int()
}
value
}
///|
pub fn NmeaChecksum::hex(value : Int) -> String {
let digits = "0123456789ABCDEF"
let high = match digits[(value >> 4) & 0xF].to_char() {
Some(character) => character.to_string()
None => "?"
}
let low = match digits[value & 0xF].to_char() {
Some(character) => character.to_string()
None => "?"
}
high + low
}
///|
pub fn NmeaChecksum::verify(payload : String, expected : String) -> Bool {
if expected.length() != 2 {
return false
}
let mut normalized = ""
for index = 0; index < expected.length(); index = index + 1 {
let code = expected[index].to_int()
let upper = if code >= 97 && code <= 102 { code - 32 } else { code }
let character = match upper.to_uint16().to_char() {
Some(value) => value
None => ' '
}
normalized = normalized + character.to_string()
}
NmeaChecksum::hex(NmeaChecksum::calculate(payload)) == normalized
}
///|
fn parse_checksum_hex(value : String) -> Result[Int, NmeaError] {
if value.length() != 2 {
return Err(
checksum_error(
ChecksumMalformed,
"checksum must contain two hexadecimal digits",
),
)
}
let mut result = 0
for index = 0; index < 2; index = index + 1 {
let code = value[index].to_int()
let digit = if code >= 48 && code <= 57 {
code - 48
} else if code >= 65 && code <= 70 {
code - 55
} else if code >= 97 && code <= 102 {
code - 87
} else {
return Err(
checksum_error(
ChecksumMalformed,
"checksum contains a non-hexadecimal digit",
),
)
}
result = result * 16 + digit
}
Ok(result)
}
///|
fn checksum_value(value : String) -> Result[Int, NmeaError] {
parse_checksum_hex(value)
}