/// Ein Drohnen-Profil mit Flugleistungsdaten (Architektur-Doku 3.5/7.3): /// FC-Typ ist bewusst NICHT Teil dieses MVP-Profils (nur iNAV/MSP im /// Scope, siehe CLAUDE.md) - anders als im HTML-Demonstrator, der bereits /// auf MAVLink vorbereitet. class DroneProfile { const DroneProfile({ this.id, required this.name, required this.minSpeed, required this.cruiseSpeed, required this.maxSpeed, required this.minTurnRadius, required this.loiterRadius, required this.catchRadius, required this.maxClimbRate, required this.maxDescentRate, required this.maxRangeM, required this.maxEnduranceMin, required this.maxAltitudeM, this.gpsSatGreenMin = defaultGpsSatGreenMin, this.gpsSatRedMin = defaultGpsSatRedMin, this.hdopGreenMax = defaultHdopGreenMax, this.hdopRedMax = defaultHdopRedMax, this.currentGreenMaxA = defaultCurrentGreenMaxA, this.currentRedMaxA = defaultCurrentRedMaxA, this.batteryCellCount = defaultBatteryCellCount, this.batteryVoltageGreenMinPerCellV = defaultBatteryVoltageGreenMinPerCellV, this.batteryVoltageRedMinPerCellV = defaultBatteryVoltageRedMinPerCellV, this.rxQualityGreenMin = defaultRxQualityGreenMin, this.rxQualityRedMin = defaultRxQualityRedMin, this.rxSnrGreenMinDb = defaultRxSnrGreenMinDb, this.rxSnrRedMinDb = defaultRxSnrRedMinDb, }); /// Null fuer ein noch nicht gespeichertes (neu angelegtes) Profil. final int? id; final String name; final double minSpeed; final double cruiseSpeed; final double maxSpeed; final double minTurnRadius; final double loiterRadius; final double catchRadius; final double maxClimbRate; final double maxDescentRate; final double maxRangeM; final double maxEnduranceMin; final double maxAltitudeM; /// Ampel-Schwellwerte fuer die farbcodierten Telemetriefelder im /// Drohnen-Status-Menue (Doku: "Schwellwerte fuer die Ampel-Kodierung im /// Drohnenprofil anlegen", siehe domain/telemetry/telemetry_field_status.dart) /// - je Feld eine Gruen- und eine Rot-Schwelle, dazwischen gilt gelb. /// *Max/*GreenMax bei Feldern, bei denen ein NIEDRIGER Wert gut ist (HDOP), /// *Min/*GreenMin bei Feldern, bei denen ein HOHER Wert gut ist (alle /// anderen). Default-Werte als statische Konstanten (statt nur inline im /// Konstruktor), damit sie auch als Fallback beim Import aelterer, noch /// ohne diese Felder exportierter Profile dienen koennen (siehe /// drone_share_codec.dart). final double gpsSatGreenMin; final double gpsSatRedMin; final double hdopGreenMax; final double hdopRedMax; final double currentGreenMaxA; final double currentRedMaxA; /// Zellenzahl des LiPo-Akkus (z.B. 4 fuer "4S") - bestimmt zusammen mit /// den beiden *PerCellV-Schwellwerten die tatsaechliche Alarmspannung des /// Packs (siehe [batteryVoltageGreenMinV]/[batteryVoltageRedMinV]). Vorher /// war die Alarmspannung als feste Pack-Gesamtspannung hinterlegt - bei /// einem Wechsel der Zellenzahl (z.B. 4S statt 6S) waeren die alten /// Schwellwerte dann fuer den neuen Pack sinnlos gewesen, ohne dass beim /// Bearbeiten des Profils ein Hinweis darauf bestanden haette. final int batteryCellCount; /// Alarm-Schwellwerte PRO ZELLE (LiPo-Standardwerte als Default: 3.4V /// gruen/"noch sicher", 3.2V rot/"jetzt landen") - siehe /// [batteryVoltageGreenMinV]/[batteryVoltageRedMinV] fuer die daraus /// abgeleitete tatsaechliche Pack-Alarmspannung. final double batteryVoltageGreenMinPerCellV; final double batteryVoltageRedMinPerCellV; final double rxQualityGreenMin; final double rxQualityRedMin; final double rxSnrGreenMinDb; final double rxSnrRedMinDb; static const defaultGpsSatGreenMin = 8.0; static const defaultGpsSatRedMin = 6.0; static const defaultHdopGreenMax = 2.0; static const defaultHdopRedMax = 5.0; static const defaultCurrentGreenMaxA = 20.0; static const defaultCurrentRedMaxA = 30.0; static const defaultBatteryCellCount = 4; static const defaultBatteryVoltageGreenMinPerCellV = 3.4; static const defaultBatteryVoltageRedMinPerCellV = 3.2; static const defaultRxQualityGreenMin = 70.0; static const defaultRxQualityRedMin = 50.0; static const defaultRxSnrGreenMinDb = 0.0; static const defaultRxSnrRedMinDb = -10.0; /// Tatsaechliche Pack-Alarmspannung, aus Zellenzahl x Pro-Zelle-Schwelle /// berechnet - [telemetry_field_status.dart] vergleicht damit die vom FC /// gemeldete Gesamtspannung, kennt selbst aber keine Zellenzahl. double get batteryVoltageGreenMinV => batteryCellCount * batteryVoltageGreenMinPerCellV; double get batteryVoltageRedMinV => batteryCellCount * batteryVoltageRedMinPerCellV; /// T1 Ranger (Heewing) - Standardprofil (Doku 3.5), analog DEFAULT_DRONE /// im HTML-Demonstrator. static const _t1RangerName = 'T1 Ranger'; factory DroneProfile.t1Ranger() => const DroneProfile( name: _t1RangerName, minSpeed: 13, cruiseSpeed: 15, maxSpeed: 25, minTurnRadius: 35, loiterRadius: 100, catchRadius: 60, maxClimbRate: 5, maxDescentRate: 6, maxRangeM: 15000, maxEnduranceMin: 45, maxAltitudeM: 2000, batteryCellCount: 4, ); /// Einzeilige Zusammenfassung fuer die Profilliste (Doku 3.5), analog /// droneSummary() im HTML-Demonstrator. String get summary { final rangeKm = (maxRangeM / 100).round() / 10; return '$minSpeed–$cruiseSpeed–$maxSpeed m/s · R${minTurnRadius.round()}m · ' '↑${maxClimbRate.round()}/↓${maxDescentRate.round()} m/s · ${rangeKm}km · ' '${maxEnduranceMin.round()}min · ceil ${maxAltitudeM.round()}m'; } DroneProfile copyWith({ int? id, String? name, double? minSpeed, double? cruiseSpeed, double? maxSpeed, double? minTurnRadius, double? loiterRadius, double? catchRadius, double? maxClimbRate, double? maxDescentRate, double? maxRangeM, double? maxEnduranceMin, double? maxAltitudeM, double? gpsSatGreenMin, double? gpsSatRedMin, double? hdopGreenMax, double? hdopRedMax, double? currentGreenMaxA, double? currentRedMaxA, int? batteryCellCount, double? batteryVoltageGreenMinPerCellV, double? batteryVoltageRedMinPerCellV, double? rxQualityGreenMin, double? rxQualityRedMin, double? rxSnrGreenMinDb, double? rxSnrRedMinDb, }) { return DroneProfile( id: id ?? this.id, name: name ?? this.name, minSpeed: minSpeed ?? this.minSpeed, cruiseSpeed: cruiseSpeed ?? this.cruiseSpeed, maxSpeed: maxSpeed ?? this.maxSpeed, minTurnRadius: minTurnRadius ?? this.minTurnRadius, loiterRadius: loiterRadius ?? this.loiterRadius, catchRadius: catchRadius ?? this.catchRadius, maxClimbRate: maxClimbRate ?? this.maxClimbRate, maxDescentRate: maxDescentRate ?? this.maxDescentRate, maxRangeM: maxRangeM ?? this.maxRangeM, maxEnduranceMin: maxEnduranceMin ?? this.maxEnduranceMin, maxAltitudeM: maxAltitudeM ?? this.maxAltitudeM, gpsSatGreenMin: gpsSatGreenMin ?? this.gpsSatGreenMin, gpsSatRedMin: gpsSatRedMin ?? this.gpsSatRedMin, hdopGreenMax: hdopGreenMax ?? this.hdopGreenMax, hdopRedMax: hdopRedMax ?? this.hdopRedMax, currentGreenMaxA: currentGreenMaxA ?? this.currentGreenMaxA, currentRedMaxA: currentRedMaxA ?? this.currentRedMaxA, batteryCellCount: batteryCellCount ?? this.batteryCellCount, batteryVoltageGreenMinPerCellV: batteryVoltageGreenMinPerCellV ?? this.batteryVoltageGreenMinPerCellV, batteryVoltageRedMinPerCellV: batteryVoltageRedMinPerCellV ?? this.batteryVoltageRedMinPerCellV, rxQualityGreenMin: rxQualityGreenMin ?? this.rxQualityGreenMin, rxQualityRedMin: rxQualityRedMin ?? this.rxQualityRedMin, rxSnrGreenMinDb: rxSnrGreenMinDb ?? this.rxSnrGreenMinDb, rxSnrRedMinDb: rxSnrRedMinDb ?? this.rxSnrRedMinDb, ); } }