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 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 gpsDateTime = DateTime(2000).obs; Rx 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 codeUnits = str.codeUnits; final Uint8List unit8List = Uint8List.fromList(codeUnits); return unit8List; } void _processGnssMessage(String message) { LineSplitter lineSplitter = const LineSplitter(); List 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; } }