gnssview_old/plugins/gnss/lib/gnss.dart

381 lines
10 KiB
Dart
Raw Normal View History

2024-07-31 19:04:11 +08:00
library gnss;
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
import 'dart:async';
import "package:libserialport/libserialport.dart";
import "nmea/sentence.dart";
import 'nmea/gga.dart';
import 'nmea/rmc.dart';
import 'nmea/vtg.dart';
import 'nmea/gsa.dart';
import 'nmea/gsv.dart';
import 'nmea/heading.dart';
import 'nmea/bestpos.dart';
import "rtk_base.dart";
class LocationData {
double latitude = 0; //纬度
double longitude = 0; //经度
double speed = 0; //速度
double baseLength = 0; //基线长度(两天线距离)
double heading = 0; //
double pitch = 0; //俯仰角
double altitude = 0; //海拔
double adop = 0; //高程定位精度
double hdop = 0; //水平定位精度
double vdop = 0; //垂直定位精度
double course = 0; //航向
double diffAge = 0; //差分数据年龄
int numberSv = 0; //可见卫星数
int numberSa = 0; //使用卫星数
String fixQuality = ""; //定位质量(字符串)
int quality = 0; //定位质量(数字)
@override
String toString() {
return "LocationData{latitude=$latitude,longitude=$longitude,speed=$speed,baseLength=$baseLength,heading=$heading,pitch=$pitch,altitude=$altitude,adop=$adop,hdop=$hdop,vdop=$vdop,course=$course,diffAge=$diffAge,numberSv=$numberSv,numberSa=$numberSa,fixQuality=$fixQuality,quality=$quality}";
}
}
const um982Init = [
"version\r\n",
"UNLOGALL\r\n",
"GPRMC 1\r\n", //0.2
"GPGGA 1\r\n",
"GPVTG 1\r\n",
"LOG HEADINGA ONTIME 0.2\r\n",
"LOG BESTPOSA ONTIME 0.2\r\n",
"GPGSV 1\r\n",
"GPGSA 1\r\n",
"SAVECONFIG\r\n"
];
class SignalGPS {
int prn;
double elevation;
double azimuth;
int snrL1;
int snrL2;
int snrL5;
SignalGPS(
{required this.prn,
required this.elevation,
required this.azimuth,
required this.snrL1,
required this.snrL2,
required this.snrL5});
}
class SignalBDS {
int prn;
double elevation;
double azimuth;
int snrB1I;
int snrB2I;
int snrB1C;
int snrB2a;
int snrB3I;
SignalBDS(
{required this.prn,
required this.elevation,
required this.azimuth,
required this.snrB1I,
required this.snrB2I,
required this.snrB1C,
required this.snrB2a,
required this.snrB3I});
}
class SignalGLO {
int prn;
double elevation;
double azimuth;
int snrR1;
int snrR2;
SignalGLO(
{required this.prn,
required this.elevation,
required this.azimuth,
required this.snrR1,
required this.snrR2});
}
class SignalGAL {
int prn;
double elevation;
double azimuth;
int snrE1;
int snrE5a;
int snrE5b;
SignalGAL(
{required this.prn,
required this.elevation,
required this.azimuth,
required this.snrE1,
required this.snrE5a,
required this.snrE5b});
}
class SignalData {
List<SignalGPS> signalsGPS = [];
List<SignalBDS> signalsBDS = [];
List<SignalGLO> signalsGLO = [];
List<SignalGAL> signalsALS = [];
}
class Gnss {
SerialPort? _serialPort;
SerialPortReader? _serialPortReader;
Socket? _socket; // tcp
RtkBase? rtkBase;
LineSplitter lineSplitter = LineSplitter();
SentenceParser sentence = SentenceParser({}, null, null, null);
var locationStreamController = StreamController<LocationData>.broadcast();
var signalStreamController = StreamController<SignalData>.broadcast();
Stream<LocationData> get locationStream => locationStreamController.stream;
Stream<SignalData> get signalStream => signalStreamController.stream;
final LocationData _locationData = LocationData();
final SignalData _signalData = SignalData();
SignalData get signalData => _signalData;
bool locationUpdate = false;
bool signalUpdate = false;
bool _isTCP = false;
String _port = "";
int _baudrate = 0;
String _host = "";
int _portTcp = 0;
Gnss({port = "", baudrate = 115200, host = "", portTCP = 0, isTCP = false}) {
_port = port;
_baudrate = baudrate;
_host = host;
_portTcp = portTCP;
_isTCP = isTCP;
}
start() {
if (_isTCP) {
_connectTcp();
} else {
_connectSerialPort();
}
rtkBase ??= RtkBase();
rtkBase!.connect();
rtkBase!.rtcmStream.listen((rtcmData) {
if (_serialPort != null) {
_serialPort!.write(Uint8List.fromList(rtcmData));
}
});
}
void dispose() {
if (_isTCP) {
if (_socket != null) {
_socket!.close();
_socket = null;
}
} else {
if (_serialPort != null) {
_serialPortReader?.close();
_serialPort!.close();
_serialPort = null;
}
}
}
_connectSerialPort() {
_serialPort = SerialPort(_port);
if (!_serialPort!.openReadWrite()) {
throw Exception(SerialPort.lastError);
}
var config = _serialPort!.config;
config.baudRate = _baudrate;
config.bits = 8;
config.parity = 0;
config.stopBits = 1;
config.setFlowControl(0);
_serialPort!.config = config;
_serialPortReader = SerialPortReader(_serialPort!);
_serialPortReader!.stream.listen(_onData);
if (_serialPort != null) {
int i = 0;
_serialPort!.write(Uint8List.fromList(um982Init[i].codeUnits));
Timer.periodic(const Duration(milliseconds: 100), (timer) {
if (_serialPort != null) {
_serialPort!.write(Uint8List.fromList(um982Init[i].codeUnits));
}
i++;
if (i == um982Init.length - 1) {
timer.cancel();
}
});
}
}
void _connectTcp() {
if (_host == "" || _portTcp == 0) {
return;
}
log("尝试连接$_host:$_port");
Socket.connect(_host, _portTcp).then((socket) {
_socket = socket;
log("连接成功");
socket.listen(_onData);
socket.handleError((error) {
log("error=$error");
});
socket.timeout(const Duration(seconds: 15), onTimeout: (sink) {
log("timeout");
socket.destroy();
_reconnect();
});
}).onError((error, stackTrace) {
log("error=$error");
_socket = null;
_reconnect();
});
}
void _reconnect() {
log("尝试重新连接...");
Future.delayed(const Duration(seconds: 5), () {
_connectTcp();
});
}
_onData(Uint8List data) {
var lines = lineSplitter.pushData(data);
for (int i = 0; i < lines.length; i++) {
try {
var s = sentence.parse(lines[i]);
switch (s.type) {
case TypeBESTPOSA:
var bestpos = BESTPOSA.newBESTPOSA(s);
// log("bestposa=${bestposa.lat},${bestposa.lon}");
_locationData.latitude = bestpos.lat;
_locationData.longitude = bestpos.lon;
_locationData.altitude = bestpos.hgt;
_locationData.adop = bestpos.adop;
_locationData.hdop = bestpos.hdop;
_locationData.vdop = bestpos.vdop;
_locationData.diffAge = bestpos.diffAge;
_locationData.fixQuality = bestpos.fixQuality;
// _locationData.pdop = bestpos.pdop;
break;
case TypeHEADINGA:
var h = HEADINGA.newHEADINGA(s);
// log("heading=${h.heading}");
_locationData.baseLength = h.length;
_locationData.heading = h.heading;
_locationData.pitch = h.pitch;
_locationData.numberSv = h.numberSv;
_locationData.numberSa = h.numberSa;
locationUpdate = true;
break;
case TypeRMC:
var rmc = RMC.newRMC(s);
_locationData.course = rmc.course;
// log("rmc=${rmc.latitude},${rmc.longitude},${rmc.speed}");
break;
case TypeGGA:
var gga = GGA.newGGA(s);
if (rtkBase != null) {
rtkBase!.sendGGA(s.raw);
}
// log("gga=${gga.latitude},${gga.longitude}");
_locationData.quality = gga.quality;
break;
case TypeVTG:
// 速度
var vtg = VTG.newVTG(s);
// log("vtg=${vtg.groundSpeedKnots}");
_locationData.speed = vtg.groundSpeedKnots;
break;
case TypeGSA:
// 已使用
var gsa = GSA.newGSA(s);
// log("gsa=${gsa.pdop}");
// SV 可用
break;
case TypeGSV:
// 可见
var gsv = GSV.newGSV(s);
// log("gsv=${gsv.numberSVsInView}");
signalUpdate = true;
break;
default:
log('Unhandled sentence type: ${s.type}');
}
} catch (e) {
log("error=$e");
}
}
if (locationUpdate) {
locationStreamController.add(_locationData);
locationUpdate = false;
}
if (signalUpdate) {
signalStreamController.add(_signalData);
signalUpdate = false;
}
}
}
class LineSplitter {
pushData(Uint8List data) {
List<String> sentences = [];
if (endIndex + data.length > dataBuf.length) {
endIndex = 0;
sentenceStart = -1;
}
dataBuf.setAll(endIndex, data);
endIndex = endIndex += data.length;
int index = 0;
while (index < endIndex) {
if (sentenceStart < 0) {
if (startDelimiter.contains(dataBuf[index]) == false) {
index++;
} else {
sentenceStart = index;
seekIndex = index + 1;
}
} else {
for (; seekIndex < endIndex; seekIndex++) {
if (dataBuf[seekIndex] == 0x0a) {
sentences
.add(String.fromCharCodes(dataBuf, sentenceStart, seekIndex));
sentenceStart = -1;
index = seekIndex + 1;
break;
}
}
if (seekIndex == endIndex) {
break;
}
}
}
// log("index=$index,endIndex=$endIndex, data.length=${data.length}");
if (index == endIndex) {
endIndex = 0;
} else if (index > 0) {
dataBuf.setAll(0, dataBuf.sublist(index, endIndex));
sentenceStart = -1;
endIndex -= index;
}
return sentences;
}
int endIndex = 0;
int seekIndex = 0;
int sentenceStart = -1;
Uint8List dataBuf = Uint8List(4096);
static const startDelimiter = [0x24, 0x23]; //$23 #23
}