Látható és használt műholdak számának meghatározása és megjelenítése
This commit is contained in:
parent
9347e22843
commit
497821bb41
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -3,5 +3,9 @@
|
|||||||
"cmake.sourceDirectory": "${workspaceFolder}/linux/flutter",
|
"cmake.sourceDirectory": "${workspaceFolder}/linux/flutter",
|
||||||
"editor.wordBasedSuggestions": "off",
|
"editor.wordBasedSuggestions": "off",
|
||||||
"editor.tabCompletion": "onlySnippets",
|
"editor.tabCompletion": "onlySnippets",
|
||||||
"editor.selectionHighlight": false
|
"editor.selectionHighlight": false,
|
||||||
|
"cSpell.words": [
|
||||||
|
"azimut",
|
||||||
|
"eleváció"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:nmea/nmea.dart';
|
import 'package:nmea/nmea.dart';
|
||||||
|
import 'package:terepi_seged/models/satellite_info.dart';
|
||||||
|
|
||||||
/// GSA — GNSS DOP és aktív műholdak
|
/// GSA — GNSS DOP és aktív műholdak
|
||||||
///
|
///
|
||||||
@ -67,6 +68,37 @@ class Gngsa extends TalkerSentence {
|
|||||||
_ => 'Unknown',
|
_ => 'Unknown',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int? get systemId {
|
||||||
|
if (fields.length <= 18) return null;
|
||||||
|
final rawSys = fields[18].split('*').first.trim();
|
||||||
|
if (rawSys.isEmpty) return null;
|
||||||
|
return int.tryParse(rawSys);
|
||||||
|
}
|
||||||
|
|
||||||
|
GnssConstellation get constellation {
|
||||||
|
switch (systemId) {
|
||||||
|
case 1:
|
||||||
|
return GnssConstellation.gps;
|
||||||
|
case 2:
|
||||||
|
return GnssConstellation.glonass;
|
||||||
|
case 3:
|
||||||
|
return GnssConstellation.galileo;
|
||||||
|
case 4:
|
||||||
|
return GnssConstellation.beidou;
|
||||||
|
case 5:
|
||||||
|
return GnssConstellation.qzss;
|
||||||
|
default:
|
||||||
|
return SatelliteInfo.constellationFromTalker(talkerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get constellationKey => constellation.name;
|
||||||
|
|
||||||
|
List<String> get activeSatelliteKeys =>
|
||||||
|
activeSatellitePrns.map((prn) => '$constellationKey:$prn').toList();
|
||||||
|
|
||||||
|
int get usedSatelliteCount => activeSatellitePrns.length;
|
||||||
|
|
||||||
double _parseDouble(String? s) {
|
double _parseDouble(String? s) {
|
||||||
if (s == null || s.isEmpty) return 0.0;
|
if (s == null || s.isEmpty) return 0.0;
|
||||||
return double.tryParse(s) ?? 0.0;
|
return double.tryParse(s) ?? 0.0;
|
||||||
|
|||||||
@ -29,21 +29,57 @@ class Gngsv extends TalkerSentence {
|
|||||||
/// Összes GSV mondat ezen rendszerhez ebben az epochban
|
/// Összes GSV mondat ezen rendszerhez ebben az epochban
|
||||||
int get totalMessages => int.tryParse(fields[1]) ?? 1;
|
int get totalMessages => int.tryParse(fields[1]) ?? 1;
|
||||||
|
|
||||||
/// Ezen mondat sorszáma (1-től indul)
|
/// Az aktuális mondat sorszáma
|
||||||
int get messageNumber => int.tryParse(fields[2]) ?? 1;
|
int get messageNumber => int.tryParse(fields[2]) ?? 1;
|
||||||
|
|
||||||
/// Összes látható műhold száma (az epochban, nem csak ebben a mondatban)
|
/// Összes látható műhold száma az adott stream-ben
|
||||||
int get totalSatellitesInView => int.tryParse(fields[3]) ?? 0;
|
int get totalSatellitesInView => int.tryParse(fields[3]) ?? 0;
|
||||||
|
|
||||||
/// Ez az utolsó mondat az adott rendszer GSV sorozatában?
|
/// Ez az utolsó mondat az adott rendszer GSV sorozatában?
|
||||||
bool get isLastMessage => messageNumber == totalMessages;
|
bool get isLastMessage => messageNumber == totalMessages;
|
||||||
|
|
||||||
|
String get talkerId => raw.length >= 3 ? raw.substring(1, 3) : 'GN';
|
||||||
|
|
||||||
|
GnssConstellation get constellation =>
|
||||||
|
SatelliteInfo.constellationFromTalker(talkerId);
|
||||||
|
|
||||||
|
String get systemName => switch (constellation) {
|
||||||
|
GnssConstellation.gps => 'GPS',
|
||||||
|
GnssConstellation.glonass => 'GLONASS',
|
||||||
|
GnssConstellation.galileo => 'Galileo',
|
||||||
|
GnssConstellation.beidou => 'BeiDou',
|
||||||
|
GnssConstellation.qzss => 'QZSS',
|
||||||
|
GnssConstellation.sbas => 'SBAS',
|
||||||
|
GnssConstellation.navic => 'NavIC',
|
||||||
|
GnssConstellation.mixed => 'Mixed',
|
||||||
|
GnssConstellation.unknown => 'Unknown',
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Opcionális NMEA Signal ID
|
||||||
|
///
|
||||||
|
/// Header: 4 mezo
|
||||||
|
/// Minden muhold: 4 mezo
|
||||||
|
/// Ha marad +1 mezo, az a signalId
|
||||||
|
int? get signalId {
|
||||||
|
final payloadCount = fields.length - 4;
|
||||||
|
if (payloadCount <= 0) return null;
|
||||||
|
|
||||||
|
final hasSignalId = payloadCount % 4 == 1;
|
||||||
|
if (!hasSignalId) return null;
|
||||||
|
|
||||||
|
final rawSignal = fields.last.split('*').first.trim();
|
||||||
|
if (rawSignal.isEmpty) return null;
|
||||||
|
return int.tryParse(rawSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Egy stream kulcsa: ugyanazon talker, ugyanazon signal
|
||||||
|
String get streamKey => '$talkerId:${signalId ?? -1}';
|
||||||
|
|
||||||
// ── Műholdak ebben a mondatban ───────────────────────────────────
|
// ── Műholdak ebben a mondatban ───────────────────────────────────
|
||||||
|
|
||||||
/// A mondatban szereplő műholdak listája (max 4)
|
/// A mondatban szereplő műholdak listája (max 4)
|
||||||
List<SatelliteInfo> get satellites {
|
List<SatelliteInfo> get satellites {
|
||||||
final sats = <SatelliteInfo>[];
|
final sats = <SatelliteInfo>[];
|
||||||
final system = systemName;
|
|
||||||
|
|
||||||
// Minden műhold 4 mezőt foglal: PRN, eleváció, azimut, SNR
|
// Minden műhold 4 mezőt foglal: PRN, eleváció, azimut, SNR
|
||||||
// fields[4]-től kezdődnek, fields[0] = mondattípus
|
// fields[4]-től kezdődnek, fields[0] = mondattípus
|
||||||
@ -53,13 +89,10 @@ class Gngsv extends TalkerSentence {
|
|||||||
// Ellenőrzés: van-e elég mező
|
// Ellenőrzés: van-e elég mező
|
||||||
if (base + 3 >= fields.length) break;
|
if (base + 3 >= fields.length) break;
|
||||||
|
|
||||||
final prn = int.tryParse(fields[base] ?? '');
|
final prn = int.tryParse(_field(base));
|
||||||
final elev = int.tryParse(fields[base + 1] ?? '') ?? 0;
|
final elev = int.tryParse(_field(base + 1)) ?? 0;
|
||||||
final az = int.tryParse(fields[base + 2] ?? '') ?? 0;
|
final az = int.tryParse(_field(base + 2)) ?? 0;
|
||||||
|
final snr = int.tryParse(_field(base + 3).split('*').first) ?? 0;
|
||||||
// SNR az utolsó mezőben checksum lehet (*XX)
|
|
||||||
final snrRaw = (fields[base + 3] ?? '').split('*').first;
|
|
||||||
final snr = int.tryParse(snrRaw) ?? 0;
|
|
||||||
|
|
||||||
if (prn != null && prn > 0) {
|
if (prn != null && prn > 0) {
|
||||||
sats.add(SatelliteInfo(
|
sats.add(SatelliteInfo(
|
||||||
@ -67,7 +100,8 @@ class Gngsv extends TalkerSentence {
|
|||||||
elevation: elev,
|
elevation: elev,
|
||||||
azimuth: az,
|
azimuth: az,
|
||||||
snr: snr,
|
snr: snr,
|
||||||
system: system,
|
constellation: constellation,
|
||||||
|
signalId: signalId,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,18 +109,8 @@ class Gngsv extends TalkerSentence {
|
|||||||
return sats;
|
return sats;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Rendszer azonosítás ───────────────────────────────────────────
|
String _field(int index) {
|
||||||
|
if (index < 0 || index >= fields.length) return '';
|
||||||
/// Talker azonosító a nyers mondatból
|
return fields[index].trim();
|
||||||
String get talkerId => raw.length >= 3 ? raw.substring(1, 3) : 'GN';
|
}
|
||||||
|
|
||||||
/// Rendszer neve a talker azonosítóból
|
|
||||||
String get systemName => switch (talkerId) {
|
|
||||||
'GP' => 'GPS',
|
|
||||||
'GL' => 'GLONASS',
|
|
||||||
'GA' => 'Galileo',
|
|
||||||
'GB' || 'BD' => 'BeiDou',
|
|
||||||
'GN' => 'Mixed',
|
|
||||||
_ => 'Unknown',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,17 @@
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
enum GnssConstellation {
|
||||||
|
gps,
|
||||||
|
glonass,
|
||||||
|
galileo,
|
||||||
|
beidou,
|
||||||
|
qzss,
|
||||||
|
sbas,
|
||||||
|
navic,
|
||||||
|
mixed,
|
||||||
|
unknown,
|
||||||
|
}
|
||||||
|
|
||||||
// ─── SatelliteInfo modell ──────────────────────────────────────────────────
|
// ─── SatelliteInfo modell ──────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Egyetlen látható műhold adatai — a skyplot és az SNR diagram alapja.
|
/// Egyetlen látható műhold adatai — a skyplot és az SNR diagram alapja.
|
||||||
@ -16,24 +28,49 @@ class SatelliteInfo {
|
|||||||
/// Jelerősség dB-Hz-ben (0–99, 0 = nem tracking)
|
/// Jelerősség dB-Hz-ben (0–99, 0 = nem tracking)
|
||||||
final int snr;
|
final int snr;
|
||||||
|
|
||||||
/// Rendszer neve: GPS, GLONASS, Galileo, BeiDou, Mixed
|
/// A műhold konstellációja
|
||||||
final String system;
|
final GnssConstellation constellation;
|
||||||
|
|
||||||
const SatelliteInfo({
|
// Opcionális: NMEA signal ID (pl. L1/L2/E1/...)
|
||||||
required this.prn,
|
final int? signalId;
|
||||||
|
|
||||||
|
const SatelliteInfo(
|
||||||
|
{required this.prn,
|
||||||
required this.elevation,
|
required this.elevation,
|
||||||
required this.azimuth,
|
required this.azimuth,
|
||||||
required this.snr,
|
required this.snr,
|
||||||
required this.system,
|
required this.constellation,
|
||||||
});
|
this.signalId});
|
||||||
|
|
||||||
|
String get system => switch (constellation) {
|
||||||
|
GnssConstellation.gps => 'GPS',
|
||||||
|
GnssConstellation.glonass => 'GLONASS',
|
||||||
|
GnssConstellation.galileo => 'Galileo',
|
||||||
|
GnssConstellation.beidou => 'BeiDou',
|
||||||
|
GnssConstellation.qzss => 'QZSS',
|
||||||
|
GnssConstellation.sbas => 'SBAS',
|
||||||
|
GnssConstellation.navic => 'NavIC',
|
||||||
|
GnssConstellation.mixed => 'Mixed',
|
||||||
|
GnssConstellation.unknown => 'Unknown',
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Egyedi kulcs ugyanazon konstellacio azonos muholdjahoz
|
||||||
|
String get satelliteKey => '${constellation.name}:$prn';
|
||||||
|
|
||||||
|
/// Egyedi kulcs ugyanazon signal streamhez
|
||||||
|
String get signalKey => '${constellation.name}:$prn:${signalId ?? -1}';
|
||||||
|
|
||||||
|
/// Human-readable sav/frekvencia becsles signalId alapjan
|
||||||
|
String get bandLabel => _bandLabel(constellation, signalId);
|
||||||
|
|
||||||
// ── Minősítési segédek ────────────────────────────────────────────
|
// ── Minősítési segédek ────────────────────────────────────────────
|
||||||
|
|
||||||
/// Erős jel — fixben megbízhatóan részt vesz
|
/// Erős jel — fixben megbízhatóan részt vesz
|
||||||
bool get isStrong => snr >= 40;
|
bool get isStrong => snr >= 40;
|
||||||
|
|
||||||
/// Használható jel — fixben részt vesz
|
/// Jelszint alapjan varhatoan hasznalhato jel.
|
||||||
bool get isUsed => snr >= 30;
|
/// A valodi "used in fix" allapotot a GSA mondatok alapjan allapitsuk meg.
|
||||||
|
bool get hasUsableSignal => snr >= 30;
|
||||||
|
|
||||||
/// Gyenge de látható jel
|
/// Gyenge de látható jel
|
||||||
bool get isWeak => snr > 0 && snr < 30;
|
bool get isWeak => snr > 0 && snr < 30;
|
||||||
@ -61,6 +98,128 @@ class SatelliteInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() {
|
||||||
'SatelliteInfo($system PRN$prn elev=$elevation° az=$azimuth° snr=$snr)';
|
final signal = signalId == null ? '-' : signalId.toString();
|
||||||
|
return 'SatelliteInfo($system PRN$prn elev=$elevation az=$azimuth snr=$snr signal=$signal band=$bandLabel)';
|
||||||
|
}
|
||||||
|
|
||||||
|
static GnssConstellation constellationFromTalker(String talkerId) {
|
||||||
|
switch (talkerId) {
|
||||||
|
case 'GP':
|
||||||
|
return GnssConstellation.gps;
|
||||||
|
case 'GL':
|
||||||
|
return GnssConstellation.glonass;
|
||||||
|
case 'GA':
|
||||||
|
return GnssConstellation.galileo;
|
||||||
|
case 'GB':
|
||||||
|
case 'BD':
|
||||||
|
return GnssConstellation.beidou;
|
||||||
|
case 'GQ':
|
||||||
|
case 'QZ':
|
||||||
|
return GnssConstellation.qzss;
|
||||||
|
case 'GI':
|
||||||
|
case 'IN':
|
||||||
|
return GnssConstellation.navic;
|
||||||
|
case 'GN':
|
||||||
|
return GnssConstellation.mixed;
|
||||||
|
default:
|
||||||
|
return GnssConstellation.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _bandLabel(GnssConstellation constellation, int? signalId) {
|
||||||
|
if (signalId == null) return 'Unknown';
|
||||||
|
|
||||||
|
switch (constellation) {
|
||||||
|
case GnssConstellation.gps:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'L1 C/A';
|
||||||
|
case 5:
|
||||||
|
return 'L2C';
|
||||||
|
case 6:
|
||||||
|
return 'L5';
|
||||||
|
default:
|
||||||
|
return 'GPS sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.glonass:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'G1';
|
||||||
|
case 3:
|
||||||
|
return 'G2';
|
||||||
|
default:
|
||||||
|
return 'GLO sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.galileo:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'E1';
|
||||||
|
case 6:
|
||||||
|
return 'E5a';
|
||||||
|
case 7:
|
||||||
|
return 'E5b';
|
||||||
|
case 8:
|
||||||
|
return 'E5 AltBOC';
|
||||||
|
default:
|
||||||
|
return 'GAL sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.beidou:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'B1I';
|
||||||
|
case 3:
|
||||||
|
return 'B2I';
|
||||||
|
case 5:
|
||||||
|
return 'B1C';
|
||||||
|
case 7:
|
||||||
|
return 'B2a';
|
||||||
|
default:
|
||||||
|
return 'BDS sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.qzss:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'L1 C/A';
|
||||||
|
case 4:
|
||||||
|
return 'L1S';
|
||||||
|
case 5:
|
||||||
|
return 'L2C';
|
||||||
|
case 6:
|
||||||
|
return 'L5';
|
||||||
|
default:
|
||||||
|
return 'QZSS sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.sbas:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'L1';
|
||||||
|
case 6:
|
||||||
|
return 'L5';
|
||||||
|
default:
|
||||||
|
return 'SBAS sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.navic:
|
||||||
|
switch (signalId) {
|
||||||
|
case 1:
|
||||||
|
return 'L5';
|
||||||
|
case 2:
|
||||||
|
return 'S';
|
||||||
|
default:
|
||||||
|
return 'NavIC sig $signalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
case GnssConstellation.mixed:
|
||||||
|
return 'Mixed sig $signalId';
|
||||||
|
|
||||||
|
case GnssConstellation.unknown:
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
// lib/services/gnss/gnss_service.dart
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:googleapis/privateca/v1.dart';
|
|
||||||
import 'package:nmea/nmea.dart';
|
import 'package:nmea/nmea.dart';
|
||||||
|
import 'package:terepi_seged/gnss_sentences/gngsa.dart';
|
||||||
|
import 'package:terepi_seged/gnss_sentences/gngsv.dart';
|
||||||
|
import 'package:terepi_seged/models/satellite_info.dart';
|
||||||
import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
|
import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
|
||||||
import 'package:terepi_seged/services/gnss/phone_gps_connection.dart';
|
import 'package:terepi_seged/services/gnss/phone_gps_connection.dart';
|
||||||
|
|
||||||
@ -25,6 +26,17 @@ class GnssService extends GetxService {
|
|||||||
final connectionState = GnssConnectionState.disconnected.obs;
|
final connectionState = GnssConnectionState.disconnected.obs;
|
||||||
final activeConnectionType = Rxn<GnssConnectionType>();
|
final activeConnectionType = Rxn<GnssConnectionType>();
|
||||||
|
|
||||||
|
final Map<String, List<SatelliteInfo>> _gsvBuffer = {};
|
||||||
|
final Map<String, DateTime> _gsvUpdatedAt = {};
|
||||||
|
static const Duration _gsvTtl = Duration(seconds: 4);
|
||||||
|
|
||||||
|
final Map<String, List<int>> _gsaPrnBuffer = {};
|
||||||
|
final Map<String, DateTime> _gsaUpdatedAt = {};
|
||||||
|
static const Duration _gsaTtl = Duration(seconds: 4);
|
||||||
|
|
||||||
|
final activePrns = <int>[].obs;
|
||||||
|
final activeSatelliteKeys = <String>[].obs;
|
||||||
|
|
||||||
// ── GGA adatok ────────────────────────────────────────────────────
|
// ── GGA adatok ────────────────────────────────────────────────────
|
||||||
final latitude = 0.0.obs;
|
final latitude = 0.0.obs;
|
||||||
final longitude = 0.0.obs;
|
final longitude = 0.0.obs;
|
||||||
@ -33,7 +45,11 @@ class GnssService extends GetxService {
|
|||||||
final gpsQuality = 0.obs;
|
final gpsQuality = 0.obs;
|
||||||
final utcFix = ''.obs;
|
final utcFix = ''.obs;
|
||||||
final satelliteCount = 0.obs;
|
final satelliteCount = 0.obs;
|
||||||
|
final gsaUsedSatelliteCount = 0.obs;
|
||||||
|
|
||||||
final hdop = 0.0.obs;
|
final hdop = 0.0.obs;
|
||||||
|
final pdop = 0.0.obs;
|
||||||
|
final vdop = 0.0.obs;
|
||||||
|
|
||||||
// Utolsó nyers GGA sor — NtripService küldi vissza a casternek
|
// Utolsó nyers GGA sor — NtripService küldi vissza a casternek
|
||||||
final lastGgaLine = ''.obs;
|
final lastGgaLine = ''.obs;
|
||||||
@ -46,12 +62,37 @@ class GnssService extends GetxService {
|
|||||||
// ── RMC adatok (dátum/idő) ────────────────────────────────────────
|
// ── RMC adatok (dátum/idő) ────────────────────────────────────────
|
||||||
final gpsDateTime = DateTime(2000).obs;
|
final gpsDateTime = DateTime(2000).obs;
|
||||||
|
|
||||||
|
final satellites = <SatelliteInfo>[].obs;
|
||||||
|
final fixType = 0.obs;
|
||||||
|
|
||||||
Timer? _reconnectTimer;
|
Timer? _reconnectTimer;
|
||||||
bool _isClosing = false;
|
bool _isClosing = false;
|
||||||
|
|
||||||
// Segédmező: van-e érvényes adat
|
// Segédmező: van-e érvényes adat
|
||||||
bool get hasValidData => gpsQuality.value > 0;
|
bool get hasValidData => gpsQuality.value > 0;
|
||||||
|
|
||||||
|
// Számított DOP minősítés (segéd getter-ek)
|
||||||
|
String get hdopRating => _dopRating(hdop.value);
|
||||||
|
String get vdopRating => _dopRating(vdop.value);
|
||||||
|
String get pdopRating => _dopRating(pdop.value);
|
||||||
|
|
||||||
|
String _dopRating(double dop) {
|
||||||
|
if (dop <= 0) return '–';
|
||||||
|
if (dop <= 1) return 'Ideális';
|
||||||
|
if (dop <= 2) return 'Kiváló';
|
||||||
|
if (dop <= 5) return 'Jó';
|
||||||
|
if (dop <= 10) return 'Közepes';
|
||||||
|
if (dop <= 20) return 'Gyenge';
|
||||||
|
return 'Rossz';
|
||||||
|
}
|
||||||
|
|
||||||
|
int get totalVisibleSatellites => satellites.length;
|
||||||
|
int get totalUsedSatellites => gsaUsedSatelliteCount.value;
|
||||||
|
bool isSatelliteUsed(int prn) => activePrns.contains(prn);
|
||||||
|
bool isSatelliteUsedSatellite(SatelliteInfo sat) {
|
||||||
|
return activeSatelliteKeys.contains(sat.satelliteKey);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Belső ─────────────────────────────────────────────────────────
|
// ── Belső ─────────────────────────────────────────────────────────
|
||||||
final NmeaDecoder _decoder = NmeaDecoder();
|
final NmeaDecoder _decoder = NmeaDecoder();
|
||||||
StreamSubscription? _nmeaSub;
|
StreamSubscription? _nmeaSub;
|
||||||
@ -74,7 +115,9 @@ class GnssService extends GetxService {
|
|||||||
_decoder
|
_decoder
|
||||||
..registerTalkerSentence('GGA', (l) => Gngga(raw: l))
|
..registerTalkerSentence('GGA', (l) => Gngga(raw: l))
|
||||||
..registerTalkerSentence('GST', (l) => Gngst(raw: l))
|
..registerTalkerSentence('GST', (l) => Gngst(raw: l))
|
||||||
..registerTalkerSentence('RMC', (l) => Gnrmc(raw: l));
|
..registerTalkerSentence('RMC', (l) => Gnrmc(raw: l))
|
||||||
|
..registerTalkerSentence('GSA', (l) => Gngsa(raw: l))
|
||||||
|
..registerTalkerSentence('GSV', (l) => Gngsv(raw: l));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Kapcsolódás ───────────────────────────────────────────────────
|
// ── Kapcsolódás ───────────────────────────────────────────────────
|
||||||
@ -189,6 +232,21 @@ class GnssService extends GetxService {
|
|||||||
_connection?.dispose();
|
_connection?.dispose();
|
||||||
_connection = null;
|
_connection = null;
|
||||||
connectionState.value = GnssConnectionState.disconnected;
|
connectionState.value = GnssConnectionState.disconnected;
|
||||||
|
|
||||||
|
_gsaPrnBuffer.clear();
|
||||||
|
_gsaUpdatedAt.clear();
|
||||||
|
_gsvBuffer.clear();
|
||||||
|
_gsvUpdatedAt.clear();
|
||||||
|
activePrns.clear();
|
||||||
|
activeSatelliteKeys.clear();
|
||||||
|
satellites.clear();
|
||||||
|
satelliteCount.value = 0;
|
||||||
|
gsaUsedSatelliteCount.value = 0;
|
||||||
|
pdop.value = 0;
|
||||||
|
hdop.value = 0;
|
||||||
|
vdop.value = 0;
|
||||||
|
fixType.value = 0;
|
||||||
|
lastGgaLine.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> disconnect() => _disconnect();
|
Future<void> disconnect() => _disconnect();
|
||||||
@ -213,6 +271,10 @@ class GnssService extends GetxService {
|
|||||||
_parseGst(line);
|
_parseGst(line);
|
||||||
} else if (sentenceType == 'RMC' && hasValidData) {
|
} else if (sentenceType == 'RMC' && hasValidData) {
|
||||||
_parseRmc(line);
|
_parseRmc(line);
|
||||||
|
} else if (sentenceType == 'GSA' && hasValidData) {
|
||||||
|
_parseGsa(line);
|
||||||
|
} else if (sentenceType == 'GSV' && hasValidData) {
|
||||||
|
_parseGsv(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,8 +290,9 @@ class GnssService extends GetxService {
|
|||||||
geoidSeparation.value = s.geoidSeparation;
|
geoidSeparation.value = s.geoidSeparation;
|
||||||
gpsQuality.value = s.gpsQualityIndicator;
|
gpsQuality.value = s.gpsQualityIndicator;
|
||||||
utcFix.value = s.utcOfPositionFix;
|
utcFix.value = s.utcOfPositionFix;
|
||||||
|
// A GGA altal riportalt, fixben hasznalt muholdszam.
|
||||||
satelliteCount.value = s.numberOfSvsInUse;
|
satelliteCount.value = s.numberOfSvsInUse;
|
||||||
hdop.value = s.hdop;
|
//hdop.value = s.hdop;
|
||||||
lastGgaLine.value = line;
|
lastGgaLine.value = line;
|
||||||
_utcTime = s.utcOfPositionFix;
|
_utcTime = s.utcOfPositionFix;
|
||||||
|
|
||||||
@ -275,6 +338,91 @@ class GnssService extends GetxService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _parseGsa(String line) {
|
||||||
|
try {
|
||||||
|
final s = _decoder.decode(line);
|
||||||
|
if (s == null || !s.valid || s is! Gngsa) return;
|
||||||
|
|
||||||
|
final key = s.constellationKey;
|
||||||
|
_gsaPrnBuffer[key] = s.activeSatellitePrns;
|
||||||
|
_gsaUpdatedAt[key] = DateTime.now().toUtc();
|
||||||
|
_purgeStaleGsa();
|
||||||
|
|
||||||
|
final satKeys = <String>{};
|
||||||
|
for (final entry in _gsaPrnBuffer.entries) {
|
||||||
|
for (final prn in entry.value) {
|
||||||
|
satKeys.add('${entry.key}:$prn');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeSatelliteKeys.assignAll(satKeys);
|
||||||
|
activePrns
|
||||||
|
.assignAll(satKeys.map((k) => int.parse(k.split(':').last)).toSet());
|
||||||
|
|
||||||
|
gsaUsedSatelliteCount.value = activeSatelliteKeys.length;
|
||||||
|
|
||||||
|
if (s.pdop > 0 && (pdop.value == 0 || s.pdop <= pdop.value)) {
|
||||||
|
pdop.value = s.pdop;
|
||||||
|
hdop.value = s.hdop;
|
||||||
|
vdop.value = s.vdop;
|
||||||
|
fixType.value = s.fixType;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('GSA parse error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _parseGsv(String line) {
|
||||||
|
try {
|
||||||
|
final s = _decoder.decode(line);
|
||||||
|
if (s == null || !s.valid || s is! Gngsv) return;
|
||||||
|
|
||||||
|
final streamKey = s.streamKey;
|
||||||
|
|
||||||
|
if (s.messageNumber == 1) {
|
||||||
|
_gsvBuffer[streamKey] = <SatelliteInfo>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
_gsvBuffer[streamKey] = [
|
||||||
|
...?_gsvBuffer[streamKey],
|
||||||
|
...s.satellites,
|
||||||
|
];
|
||||||
|
|
||||||
|
_gsvUpdatedAt[streamKey] = DateTime.now().toUtc();
|
||||||
|
_purgeStaleGsv();
|
||||||
|
|
||||||
|
if (s.isLastMessage) {
|
||||||
|
_updateSatelliteList();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('GSV parse error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Összes rendszer pufferéből összerakja a teljes műholdlistát.
|
||||||
|
/// Akkor hívódik, amikor egy rendszer utolsó GSV mondata megérkezett.
|
||||||
|
void _updateSatelliteList() {
|
||||||
|
_purgeStaleGsv();
|
||||||
|
|
||||||
|
final allSats = _gsvBuffer.values.expand((list) => list).toList();
|
||||||
|
if (allSats.isEmpty) return;
|
||||||
|
|
||||||
|
final bySatellite = <String, SatelliteInfo>{};
|
||||||
|
|
||||||
|
for (final sat in allSats) {
|
||||||
|
final key = sat.satelliteKey;
|
||||||
|
final prev = bySatellite[key];
|
||||||
|
|
||||||
|
// Ugyanaz a műhold tobb signal stream-ben is johet.
|
||||||
|
// A legerősebb SNR-es bejegyzest tartjuk meg.
|
||||||
|
if (prev == null || sat.snr > prev.snr) {
|
||||||
|
bySatellite[key] = sat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
satellites.assignAll(bySatellite.values);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() async {
|
void onClose() async {
|
||||||
_isClosing = true;
|
_isClosing = true;
|
||||||
@ -334,4 +482,32 @@ class GnssService extends GetxService {
|
|||||||
unawaited(reconnect());
|
unawaited(reconnect());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _purgeStaleGsa() {
|
||||||
|
final cutoff = DateTime.now().toUtc().subtract(_gsaTtl);
|
||||||
|
|
||||||
|
final staleKeys = _gsaUpdatedAt.entries
|
||||||
|
.where((e) => e.value.isBefore(cutoff))
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
for (final key in staleKeys) {
|
||||||
|
_gsaUpdatedAt.remove(key);
|
||||||
|
_gsaPrnBuffer.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _purgeStaleGsv() {
|
||||||
|
final cutoff = DateTime.now().toUtc().subtract(_gsvTtl);
|
||||||
|
|
||||||
|
final staleKeys = _gsvUpdatedAt.entries
|
||||||
|
.where((e) => e.value.isBefore(cutoff))
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
for (final key in staleKeys) {
|
||||||
|
_gsvUpdatedAt.remove(key);
|
||||||
|
_gsvBuffer.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:get/get_state_manager/get_state_manager.dart';
|
|||||||
import 'package:get/state_manager.dart';
|
import 'package:get/state_manager.dart';
|
||||||
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
|
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
|
||||||
import 'package:terepi_seged/pages/shell/presentations/controllers/shell_controller.dart';
|
import 'package:terepi_seged/pages/shell/presentations/controllers/shell_controller.dart';
|
||||||
|
import 'package:terepi_seged/services/gnss/gnss_service.dart';
|
||||||
import 'package:terepi_seged/services/ntrip_service.dart';
|
import 'package:terepi_seged/services/ntrip_service.dart';
|
||||||
import 'package:terepi_seged/widgets/gnss_status_chip.dart';
|
import 'package:terepi_seged/widgets/gnss_status_chip.dart';
|
||||||
import 'package:terepi_seged/widgets/map_mode_menu_anchor.dart';
|
import 'package:terepi_seged/widgets/map_mode_menu_anchor.dart';
|
||||||
@ -78,13 +79,12 @@ class ShellMapAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
children: [
|
children: [
|
||||||
GnssTextStatusChip(),
|
GnssTextStatusChip(),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Text('V:', style: TextStyle(fontSize: 12)),
|
const Icon(Icons.satellite_alt, size: 12),
|
||||||
SizedBox(width: 2),
|
SizedBox(width: 2),
|
||||||
Text(controller.verticalAccuracyText,
|
Text(
|
||||||
|
'${GnssService.to.totalVisibleSatellites}/${GnssService.to.totalUsedSatellites}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color:
|
|
||||||
_errorColor(controller.gpsAltitudeError.value),
|
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontFeatures: const [FontFeature.tabularFigures()]))
|
fontFeatures: const [FontFeature.tabularFigures()]))
|
||||||
])
|
])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user