2026-05-28 13:24:16 +02:00
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
2026-06-06 23:16:03 +02:00
|
|
|
|
enum GnssConstellation {
|
|
|
|
|
|
gps,
|
|
|
|
|
|
glonass,
|
|
|
|
|
|
galileo,
|
|
|
|
|
|
beidou,
|
|
|
|
|
|
qzss,
|
|
|
|
|
|
sbas,
|
|
|
|
|
|
navic,
|
|
|
|
|
|
mixed,
|
|
|
|
|
|
unknown,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 13:24:16 +02:00
|
|
|
|
// ─── SatelliteInfo modell ──────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/// Egyetlen látható műhold adatai — a skyplot és az SNR diagram alapja.
|
|
|
|
|
|
class SatelliteInfo {
|
|
|
|
|
|
/// Műhold PRN/SVID azonosítója
|
|
|
|
|
|
final int prn;
|
|
|
|
|
|
|
|
|
|
|
|
/// Eleváció fokban (0–90°, 90 = zenit)
|
|
|
|
|
|
final int elevation;
|
|
|
|
|
|
|
|
|
|
|
|
/// Azimut fokban (0–359°, 0/360 = É, 90 = K)
|
|
|
|
|
|
final int azimuth;
|
|
|
|
|
|
|
|
|
|
|
|
/// Jelerősség dB-Hz-ben (0–99, 0 = nem tracking)
|
|
|
|
|
|
final int snr;
|
|
|
|
|
|
|
2026-06-06 23:16:03 +02:00
|
|
|
|
/// A műhold konstellációja
|
|
|
|
|
|
final GnssConstellation constellation;
|
|
|
|
|
|
|
|
|
|
|
|
// Opcionális: NMEA signal ID (pl. L1/L2/E1/...)
|
|
|
|
|
|
final int? signalId;
|
|
|
|
|
|
|
|
|
|
|
|
const SatelliteInfo(
|
|
|
|
|
|
{required this.prn,
|
|
|
|
|
|
required this.elevation,
|
|
|
|
|
|
required this.azimuth,
|
|
|
|
|
|
required this.snr,
|
|
|
|
|
|
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);
|
2026-05-28 13:24:16 +02:00
|
|
|
|
|
|
|
|
|
|
// ── Minősítési segédek ────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/// Erős jel — fixben megbízhatóan részt vesz
|
|
|
|
|
|
bool get isStrong => snr >= 40;
|
|
|
|
|
|
|
2026-06-06 23:16:03 +02:00
|
|
|
|
/// Jelszint alapjan varhatoan hasznalhato jel.
|
|
|
|
|
|
/// A valodi "used in fix" allapotot a GSA mondatok alapjan allapitsuk meg.
|
|
|
|
|
|
bool get hasUsableSignal => snr >= 30;
|
2026-05-28 13:24:16 +02:00
|
|
|
|
|
|
|
|
|
|
/// Gyenge de látható jel
|
|
|
|
|
|
bool get isWeak => snr > 0 && snr < 30;
|
|
|
|
|
|
|
|
|
|
|
|
/// Egyáltalán látható-e (snr > 0)
|
|
|
|
|
|
bool get isVisible => snr > 0;
|
|
|
|
|
|
|
|
|
|
|
|
// ── Polár koordináta számítás a skyplot CustomPainter-hez ────────
|
|
|
|
|
|
|
|
|
|
|
|
/// Normalizált sugár (0.0 = zenit, 1.0 = horizont)
|
|
|
|
|
|
double get polarRadius => 1.0 - (elevation / 90.0);
|
|
|
|
|
|
|
|
|
|
|
|
/// Szög radiánban a polár koordinátához
|
|
|
|
|
|
/// (azimut → matematikai szög: É=felső, K=jobb)
|
|
|
|
|
|
double get polarAngleRad => (azimuth - 90) * math.pi / 180.0;
|
|
|
|
|
|
|
|
|
|
|
|
/// Polár koordináta pixel pozíciójának kiszámítása
|
|
|
|
|
|
/// [center] a kör középpontja, [radius] a horizont kör sugara
|
|
|
|
|
|
({double dx, double dy}) toOffset(
|
|
|
|
|
|
double centerX, double centerY, double radius) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
dx: centerX + radius * polarRadius * math.cos(polarAngleRad),
|
|
|
|
|
|
dy: centerY + radius * polarRadius * math.sin(polarAngleRad),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
2026-06-06 23:16:03 +02:00
|
|
|
|
String toString() {
|
|
|
|
|
|
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';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-28 13:24:16 +02:00
|
|
|
|
}
|