302 lines
9.4 KiB
Dart
302 lines
9.4 KiB
Dart
|
|
import 'dart:async';
|
||
|
|
import 'dart:convert';
|
||
|
|
import 'dart:io';
|
||
|
|
import 'dart:typed_data';
|
||
|
|
|
||
|
|
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||
|
|
import 'package:get/get.dart';
|
||
|
|
import 'package:intl/intl.dart';
|
||
|
|
import 'package:nmea/nmea.dart';
|
||
|
|
import 'package:terepi_seged/eov/convert_coordinate.dart';
|
||
|
|
import 'package:terepi_seged/eov/eov.dart';
|
||
|
|
import 'package:terepi_seged/gnss_sentences/gngga.dart';
|
||
|
|
import 'package:terepi_seged/gnss_sentences/gngst.dart';
|
||
|
|
import 'package:terepi_seged/gnss_sentences/gnrmc.dart';
|
||
|
|
|
||
|
|
class RtcmTestController extends GetxController {
|
||
|
|
String gpsAddress = "E8:31:CD:14:8B:B2";
|
||
|
|
String gpsName = "TiGNSS Rover-8BB2";
|
||
|
|
Rx<bool> gpsIsConnected = false.obs;
|
||
|
|
RxBool ntripIsConnected = false.obs;
|
||
|
|
RxInt ntripDataPacketNumbers = 0.obs;
|
||
|
|
String _messageBuffer = "";
|
||
|
|
late BluetoothConnection connection;
|
||
|
|
late Socket socket;
|
||
|
|
late StreamSubscription socketStreamSubscription;
|
||
|
|
RxInt gpsReceivedData = 0.obs;
|
||
|
|
RxBool hasGpsValidData = false.obs;
|
||
|
|
DateTime lastGpsRefreshTime = DateTime.now();
|
||
|
|
String utcOfPositionFix = "";
|
||
|
|
String utcDateOfPositionFix = "";
|
||
|
|
RxInt ntripReceivedData = 0.obs;
|
||
|
|
final NmeaDecoder nmeaDecoder = NmeaDecoder();
|
||
|
|
|
||
|
|
NumberFormat formatEov = NumberFormat("##0,000.0", "hu-HU");
|
||
|
|
NumberFormat formatWgs84Sec = NumberFormat('00.000', 'hu-HU');
|
||
|
|
|
||
|
|
RxDouble gpsLatitude = 0.0.obs;
|
||
|
|
RxString gpsLatitudeDirection = "".obs;
|
||
|
|
RxDouble gpsLongitude = 0.0.obs;
|
||
|
|
RxString gpsLongitudeDirection = "".obs;
|
||
|
|
RxDouble gpsAltitude = 0.0.obs;
|
||
|
|
RxInt gpsQuality = 0.obs;
|
||
|
|
RxDouble gpsLatitudeError = 0.0.obs;
|
||
|
|
RxDouble gpsLongitudeError = 0.0.obs;
|
||
|
|
RxDouble gpsAltitudeError = 0.0.obs;
|
||
|
|
Rx<DateTime> gpsDateTime = DateTime(2000).obs;
|
||
|
|
Rx<Eov> eov = Eov(0, 0).obs;
|
||
|
|
RxInt latDegree = 0.obs;
|
||
|
|
RxInt latMin = 0.obs;
|
||
|
|
RxDouble latSec = 0.0.obs;
|
||
|
|
RxInt longDegree = 0.obs;
|
||
|
|
RxInt longMin = 0.obs;
|
||
|
|
RxDouble longSec = 0.0.obs;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void onInit() {
|
||
|
|
super.onInit();
|
||
|
|
//Loading, Success, Error handle with 1 line of code
|
||
|
|
nmeaDecoder
|
||
|
|
..registerTalkerSentence("GGA", (line) => Gngga(raw: line))
|
||
|
|
..registerTalkerSentence("RMC", (line) => Gnrmc(raw: line))
|
||
|
|
..registerTalkerSentence("GST", (line) => Gngst(raw: line));
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void onClose() {
|
||
|
|
super.onClose();
|
||
|
|
FlutterBluetoothSerial.instance.setPairingRequestHandler(null);
|
||
|
|
if (gpsIsConnected.value) {
|
||
|
|
connection.close();
|
||
|
|
// connection = null;
|
||
|
|
print("BluetoothTestController dispose ....");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void connectToGps() {
|
||
|
|
if (gpsIsConnected == false) {
|
||
|
|
BluetoothConnection.toAddress(gpsAddress).then((value) {
|
||
|
|
connection = value;
|
||
|
|
gpsIsConnected.value = true;
|
||
|
|
print("GPS is connected ...");
|
||
|
|
|
||
|
|
connection.input!.listen(_onDataReceived);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void disconnectFromGps() {
|
||
|
|
if (gpsIsConnected.value) {
|
||
|
|
connection.close();
|
||
|
|
gpsIsConnected.value = false;
|
||
|
|
print("GPS is disconnected ....");
|
||
|
|
}
|
||
|
|
if (ntripIsConnected.value) {
|
||
|
|
disconnectFromNtripServer();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void connectToNtripServer() async {
|
||
|
|
socket = await Socket.connect(InternetAddress('3.23.52.207'), 2101,
|
||
|
|
timeout: const Duration(seconds: 5));
|
||
|
|
socket.asBroadcastStream;
|
||
|
|
ntripIsConnected.value = true;
|
||
|
|
|
||
|
|
socket.encoding = ascii;
|
||
|
|
print("Connected to ntrip server ....");
|
||
|
|
|
||
|
|
String header = "GET /KOVARIK HTTP/1.1\r\n";
|
||
|
|
header += "User-Agent: SharpGps iter.dk\r\n";
|
||
|
|
header += "Accept: */*\r\nConnection: close\r\n";
|
||
|
|
header += "Authorization: Basic ${_toBase64("info@mail.app-dev.hu:")}\r\n";
|
||
|
|
header += "Host:rtk2go.com:2101\r\n";
|
||
|
|
header += "Ntrip-Vesrsion:Ntrip/2.0\r\n";
|
||
|
|
header += "\r\n";
|
||
|
|
|
||
|
|
// listen for responses from the server
|
||
|
|
socketStreamSubscription = socket.listen(
|
||
|
|
// handle data from the server
|
||
|
|
(Uint8List data) {
|
||
|
|
// "ICY 200 OK" - first response
|
||
|
|
final serverResponse = String.fromCharCodes(data);
|
||
|
|
ntripReceivedData.value = data.length;
|
||
|
|
ntripDataPacketNumbers.value++;
|
||
|
|
if (gpsIsConnected.value) {
|
||
|
|
if (data.length > 12) {
|
||
|
|
connection.output.add(data);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
print('Server: $ntripReceivedData');
|
||
|
|
},
|
||
|
|
|
||
|
|
// handle errors
|
||
|
|
onError: (error) {
|
||
|
|
print(error);
|
||
|
|
socket.destroy();
|
||
|
|
},
|
||
|
|
|
||
|
|
// handle server ending connection
|
||
|
|
onDone: () async {
|
||
|
|
print('Server left.');
|
||
|
|
socketStreamSubscription.cancel();
|
||
|
|
await socket.flush();
|
||
|
|
ntripIsConnected.value = false;
|
||
|
|
ntripReceivedData.value = 0;
|
||
|
|
socket.destroy();
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
socket.add(_convertStringToUint8List(header));
|
||
|
|
}
|
||
|
|
|
||
|
|
void disconnectFromNtripServer() async {
|
||
|
|
socketStreamSubscription.cancel();
|
||
|
|
await socket.flush();
|
||
|
|
socket.close();
|
||
|
|
socket.destroy();
|
||
|
|
ntripReceivedData.value = 0;
|
||
|
|
ntripIsConnected.value = false;
|
||
|
|
print("Disconnect from ntrip server....");
|
||
|
|
}
|
||
|
|
|
||
|
|
void _onDataReceived(Uint8List data) {
|
||
|
|
String sentence = "";
|
||
|
|
|
||
|
|
print("Bluetooth received -> ${data.length} byte(s)");
|
||
|
|
String dataString = String.fromCharCodes(data);
|
||
|
|
int index = data.indexOf(13);
|
||
|
|
|
||
|
|
if (~index != 0) {
|
||
|
|
sentence = _messageBuffer + dataString.substring(0, index);
|
||
|
|
_messageBuffer = dataString.substring(index);
|
||
|
|
} else {
|
||
|
|
_messageBuffer = _messageBuffer + dataString;
|
||
|
|
}
|
||
|
|
|
||
|
|
// print("Message ($count): $sentence");
|
||
|
|
_processGnssMessage(sentence);
|
||
|
|
}
|
||
|
|
|
||
|
|
String _toBase64(String str) {
|
||
|
|
final bytes = ascii.encode(str);
|
||
|
|
final base64Str = base64.encode(bytes);
|
||
|
|
|
||
|
|
return base64Str;
|
||
|
|
}
|
||
|
|
|
||
|
|
Uint8List _convertStringToUint8List(String str) {
|
||
|
|
final List<int> codeUnits = str.codeUnits;
|
||
|
|
final Uint8List unit8List = Uint8List.fromList(codeUnits);
|
||
|
|
return unit8List;
|
||
|
|
}
|
||
|
|
|
||
|
|
void _processGnssMessage(String message) {
|
||
|
|
LineSplitter lineSplitter = const LineSplitter();
|
||
|
|
List<String> lines = lineSplitter.convert(message);
|
||
|
|
|
||
|
|
if (lines.isEmpty) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (String line in lines) {
|
||
|
|
if (line.trim().isEmpty) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (line.startsWith("\$GNGGA")) {
|
||
|
|
final sentence = nmeaDecoder.decode(line);
|
||
|
|
if (sentence!.valid && sentence is Gngga) {
|
||
|
|
hasGpsValidData.value = sentence.gpsQualityIndicator > 0;
|
||
|
|
if (hasGpsValidData.value) {
|
||
|
|
if (DateTime.now().difference(lastGpsRefreshTime).inSeconds >= 2) {
|
||
|
|
lastGpsRefreshTime = DateTime.now();
|
||
|
|
utcOfPositionFix = sentence.utcOfPositionFix;
|
||
|
|
gpsLatitude.value = sentence.latitude;
|
||
|
|
gpsLatitudeDirection.value = sentence.latitudeDirection;
|
||
|
|
gpsLongitude.value = sentence.longitude;
|
||
|
|
gpsLongitudeDirection.value = sentence.longitudeDirection;
|
||
|
|
gpsAltitude.value = sentence.altitudeAboveMeanSeaLevel;
|
||
|
|
gpsQuality.value = sentence.gpsQualityIndicator;
|
||
|
|
eov.value = ConvertCoordinate.ConvertWgsToEov(
|
||
|
|
gpsLatitude.value, gpsLongitude.value);
|
||
|
|
latDegree.value = ConvertCoordinate.toDegree(gpsLatitude.value);
|
||
|
|
latMin.value = ConvertCoordinate.toMinute(gpsLatitude.value);
|
||
|
|
latSec.value = ConvertCoordinate.toSecond(gpsLatitude.value);
|
||
|
|
longDegree.value = ConvertCoordinate.toDegree(gpsLongitude.value);
|
||
|
|
longMin.value = ConvertCoordinate.toMinute(gpsLongitude.value);
|
||
|
|
longSec.value = ConvertCoordinate.toSecond(gpsLongitude.value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (line.startsWith("\$GNGST") && hasGpsValidData.value) {
|
||
|
|
final sentence = nmeaDecoder.decode(line);
|
||
|
|
if (sentence!.valid && sentence is Gngst) {
|
||
|
|
gpsLatitudeError.value = sentence.latitudeError;
|
||
|
|
gpsLongitudeError.value = sentence.longitudeError;
|
||
|
|
gpsAltitudeError.value = sentence.heightError;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (line.startsWith("\$GNRMC") && hasGpsValidData.value) {
|
||
|
|
final sentence = nmeaDecoder.decode(line);
|
||
|
|
if (sentence!.valid && sentence is Gnrmc) {
|
||
|
|
utcDateOfPositionFix = sentence.date;
|
||
|
|
if (utcDateOfPositionFix.isNotEmpty && utcOfPositionFix.isNotEmpty) {
|
||
|
|
gpsDateTime.value = DateTime(
|
||
|
|
2000 +
|
||
|
|
int.parse(
|
||
|
|
"${utcDateOfPositionFix[4]}${utcDateOfPositionFix[5]}"),
|
||
|
|
int.parse(
|
||
|
|
"${utcDateOfPositionFix[2]}${utcDateOfPositionFix[3]}"),
|
||
|
|
int.parse(
|
||
|
|
"${utcDateOfPositionFix[0]}${utcDateOfPositionFix[1]}"),
|
||
|
|
int.parse("${utcOfPositionFix[0]}${utcOfPositionFix[1]}"),
|
||
|
|
int.parse("${utcOfPositionFix[2]}${utcOfPositionFix[3]}"),
|
||
|
|
int.parse("${utcOfPositionFix[4]}${utcOfPositionFix[5]}"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
String getGpsQualityIndicator({required int quality}) {
|
||
|
|
String qualityStr = "Invalid";
|
||
|
|
switch (quality) {
|
||
|
|
case 0:
|
||
|
|
{
|
||
|
|
qualityStr = "Invalid";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
{
|
||
|
|
qualityStr = "Standard GPS (2D/3D)";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
{
|
||
|
|
qualityStr = "Differential GPS";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
{
|
||
|
|
qualityStr = "RTK Fix";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
{
|
||
|
|
qualityStr = "RTK Float";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 6:
|
||
|
|
{
|
||
|
|
qualityStr = "Estimated (DR) Fix";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
{
|
||
|
|
qualityStr = "Invalid (-1)";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return qualityStr;
|
||
|
|
}
|
||
|
|
}
|