263 lines
5.5 KiB
Dart
263 lines
5.5 KiB
Dart
|
|
|
|
import 'sentence.dart';
|
|
import 'types.dart';
|
|
|
|
class Parser {
|
|
BaseSentence baseSentence;
|
|
String? _err;
|
|
|
|
Parser(this.baseSentence);
|
|
|
|
void assertType(String typ) {
|
|
if (baseSentence.type != typ) {
|
|
setErr('type', baseSentence.type);
|
|
}
|
|
}
|
|
|
|
String? err() {
|
|
return _err;
|
|
}
|
|
|
|
void setErr(String context, String value) {
|
|
_err ??= 'nmea: ${baseSentence.prefix()} invalid $context: $value';
|
|
}
|
|
|
|
String string(int i, String context) {
|
|
if (_err != null) {
|
|
return '';
|
|
}
|
|
if (i < 0 || i >= baseSentence.fields.length) {
|
|
setErr(context, 'index out of range');
|
|
return '';
|
|
}
|
|
return baseSentence.fields[i];
|
|
}
|
|
|
|
List<String> listString(int from, String context) {
|
|
if (_err != null) {
|
|
return [];
|
|
}
|
|
if (from < 0 || from >= baseSentence.fields.length) {
|
|
setErr(context, 'index out of range');
|
|
return [];
|
|
}
|
|
return baseSentence.fields.sublist(from);
|
|
}
|
|
|
|
String enumString(
|
|
int i,
|
|
String context,
|
|
List<String> options,
|
|
) {
|
|
String? s = string(i, context);
|
|
if (_err != null || s == '') {
|
|
return '';
|
|
}
|
|
if (options.contains(s)) {
|
|
return s;
|
|
} else {
|
|
setErr(context, s);
|
|
return '';
|
|
}
|
|
}
|
|
|
|
List<String> enumChars(int i, String context, List<String> options) {
|
|
String? s = string(i, context);
|
|
if (_err != null || s == '') {
|
|
return [];
|
|
}
|
|
List<String> strs = [];
|
|
for (int j = 0; j < s.length; j++) {
|
|
String rs = s[j];
|
|
if (options.contains(rs)) {
|
|
strs.add(rs);
|
|
}
|
|
}
|
|
if (strs.length != s.length) {
|
|
setErr(context, s);
|
|
return [];
|
|
}
|
|
return strs;
|
|
}
|
|
|
|
int hexInt64(int i, String context) {
|
|
String? s = string(i, context);
|
|
if (_err != null) {
|
|
return 0;
|
|
}
|
|
if (s == '') {
|
|
return 0;
|
|
}
|
|
int value = int.parse(s, radix: 16);
|
|
return value;
|
|
}
|
|
|
|
int int64(int i, String context) {
|
|
return nullInt64(i, context).value;
|
|
}
|
|
|
|
Int64 nullInt64(int i, String context) {
|
|
String? s = string(i, context);
|
|
if (_err != null) {
|
|
return Int64(0, false);
|
|
}
|
|
if (s == '') {
|
|
return Int64(0, false);
|
|
}
|
|
int? v = int.tryParse(s);
|
|
if (v == null) {
|
|
setErr(context, s);
|
|
return Int64(0, false);
|
|
}
|
|
return Int64(v, true);
|
|
}
|
|
|
|
double float64(int i, String context) {
|
|
return nullFloat64(i, context).value;
|
|
}
|
|
|
|
Float64 nullFloat64(int i, String context) {
|
|
String? s = string(i, context);
|
|
if (_err != null) {
|
|
return Float64(0, false);
|
|
}
|
|
if (s == '') {
|
|
return Float64(0, false);
|
|
}
|
|
double? v = double.tryParse(s);
|
|
if (v == null) {
|
|
setErr(context, s);
|
|
return Float64(0, false);
|
|
}
|
|
return Float64(v, true);
|
|
}
|
|
|
|
Time time(int i, String context) {
|
|
String? s = string(i, context);
|
|
if (_err != null) {
|
|
return Time(false, 0, 0, 0, 0);
|
|
}
|
|
try {
|
|
return parseTime(s);
|
|
} catch (e) {
|
|
setErr(context, s);
|
|
return Time(false, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
Date date(int i, String context) {
|
|
String? s = string(i, context);
|
|
if (_err != null) {
|
|
return Date(false, 0, 0, 0);
|
|
}
|
|
try {
|
|
return parseDate(s);
|
|
} catch (e) {
|
|
setErr(context, s);
|
|
return Date(false, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
double latLong(int i, int j, String context) {
|
|
String a = string(i, context);
|
|
String b = string(j, context);
|
|
if (_err != null) {
|
|
return 0;
|
|
}
|
|
String s = '$a $b';
|
|
double? v = parseLatLong(s);
|
|
if ((b == 'North' || b == 'South') && (v < -90.0 || v > 90.0)) {
|
|
setErr(context, 'latitude is not in range (-90, 90)');
|
|
return 0;
|
|
} else if ((b == 'West' || b == 'East') && (v < -180.0 || v > 180.0)) {
|
|
setErr(context, 'longitude is not in range (-180, 180)');
|
|
return 0;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
List<int> sixBitASCIIArmour(int i, int fillBits, String context) {
|
|
if (_err != null) {
|
|
return [];
|
|
}
|
|
if (fillBits < 0 || fillBits >= 6) {
|
|
setErr(context, 'fill bits');
|
|
return [];
|
|
}
|
|
|
|
List<int> payload = string(i, 'encoded payload').codeUnits;
|
|
int numBits = payload.length * 6 - fillBits;
|
|
|
|
if (numBits < 0) {
|
|
setErr(context, 'num bits');
|
|
return [];
|
|
}
|
|
|
|
List<int> result = List.filled(numBits, 0);
|
|
int resultIndex = 0;
|
|
|
|
for (int v in payload) {
|
|
if (v < 48 || v >= 120) {
|
|
setErr(context, 'data byte');
|
|
return [];
|
|
}
|
|
|
|
int d = v - 48;
|
|
if (d > 40) {
|
|
d -= 8;
|
|
}
|
|
|
|
for (int i = 5; i >= 0 && resultIndex < result.length; i--) {
|
|
result[resultIndex] = (d >> i) & 1;
|
|
resultIndex++;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class Time {
|
|
bool isValid;
|
|
int hour;
|
|
int minute;
|
|
int second;
|
|
int millisecond;
|
|
|
|
Time(this.isValid, this.hour, this.minute, this.second, this.millisecond);
|
|
}
|
|
|
|
Time parseTime(String s) {
|
|
if (s == "") {
|
|
return Time(false, 0, 0, 0, 0);
|
|
}
|
|
RegExp timeRe = RegExp(r"^\d{6}(\.\d*)?$");
|
|
if (!timeRe.hasMatch(s)) {
|
|
throw Exception("parse time: expected hhmmss.ss format, got '$s'");
|
|
}
|
|
int hour = int.parse(s.substring(0, 2));
|
|
int minute = int.parse(s.substring(2, 4));
|
|
double second = double.parse(s.substring(4));
|
|
|
|
int whole = second.floor(); // 获取整数部分
|
|
double frac = second - whole; // 获取小数部分
|
|
return Time(true, hour, minute, whole.toInt(), (frac * 1000).round());
|
|
}
|
|
|
|
mixin math {}
|
|
|
|
class Int64 {
|
|
int value;
|
|
bool valid;
|
|
|
|
Int64(this.value, this.valid);
|
|
}
|
|
|
|
class Float64 {
|
|
double value;
|
|
bool valid;
|
|
|
|
Float64(this.value, this.valid);
|
|
}
|