import 'package:flutter_test/flutter_test.dart'; import 'package:dmc_app/domain/waypoint/flat_waypoint_list.dart'; import 'package:dmc_app/domain/wind/wind_math.dart'; void main() { group('interpolateAngle', () { test('nimmt den kuerzeren Weg ueber die 0/360-Grenze', () { expect(interpolateAngle(350, 10, 0.5), closeTo(0, 0.001)); }); test('interpoliert linear ohne Grenzuebergang', () { expect(interpolateAngle(10, 30, 0.5), closeTo(20, 0.001)); }); }); group('interpolateWindAtHeight', () { const levels = [ WindLevel(heightAmslM: 100, speedMs: 4, dirFromDeg: 200), WindLevel(heightAmslM: 300, speedMs: 8, dirFromDeg: 220), ]; test('klemmt unterhalb des tiefsten Stuetzpunkts', () { final result = interpolateWindAtHeight(levels, 50); expect(result!.speedMs, 4); expect(result.dirFromDeg, 200); }); test('klemmt oberhalb des hoechsten Stuetzpunkts', () { final result = interpolateWindAtHeight(levels, 500); expect(result!.speedMs, 8); expect(result.dirFromDeg, 220); }); test('interpoliert linear zwischen zwei Stuetzpunkten', () { final result = interpolateWindAtHeight(levels, 200); expect(result!.speedMs, closeTo(6, 0.001)); expect(result.dirFromDeg, closeTo(210, 0.001)); }); test('liefert null ohne Stuetzpunkte', () { expect(interpolateWindAtHeight(const [], 100), isNull); }); }); group('bearingBetweenDeg', () { test('nach Norden ergibt ~0 Grad', () { expect(bearingBetweenDeg(0, 0, 1, 0), closeTo(0, 0.5)); }); test('nach Osten ergibt ~90 Grad', () { expect(bearingBetweenDeg(0, 0, 0, 1), closeTo(90, 0.5)); }); }); group('headTailwindComponent', () { test('Wind von hinten ergibt vollen positiven Rueckenwind', () { // Kurs 0 (Nord), Wind weht aus Sueden (180) - also von hinten. expect(headTailwindComponent(0, 10, 180), closeTo(10, 0.001)); }); test('Wind von vorne ergibt vollen negativen Gegenwind', () { // Kurs 0 (Nord), Wind weht aus Norden (0) - also von vorne. expect(headTailwindComponent(0, 10, 0), closeTo(-10, 0.001)); }); test('Seitenwind ergibt ~0 Laengskomponente', () { expect(headTailwindComponent(0, 10, 90), closeTo(0, 0.001)); }); }); group('computeLegWindComponent / computeLegWind / computeGroundSpeeds', () { const a = Waypoint( lat: 0, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60, ); const bWithWind = Waypoint( lat: 1, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60, windSpeedMs: 10, windDirFromDeg: 180, ); const bWithoutWind = Waypoint( lat: 1, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60, ); test('null solange am Zielwegpunkt kein Wind geladen ist', () { expect(computeLegWindComponent(a, bWithoutWind), isNull); }); test('Rueckenwind auf dem Leg erhoeht die Bodengeschwindigkeit', () { final component = computeLegWindComponent(a, bWithWind); expect(component, closeTo(10, 0.001)); final groundSpeeds = computeGroundSpeeds([a, bWithWind]); expect(groundSpeeds.single, closeTo(25, 0.001)); }); test('computeLegWind liefert eine Komponente pro Leg', () { final legWind = computeLegWind([a, bWithWind, bWithoutWind]); expect(legWind, hasLength(2)); expect(legWind[0], isNotNull); expect(legWind[1], isNull); }); }); group('computeLegWindBad', () { const start = Waypoint(lat: 0, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60); const tailwindTarget = Waypoint( lat: 1, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60, windSpeedMs: 10, windDirFromDeg: 180, ); const noWindTarget = Waypoint(lat: 1, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60); test('true, wenn die resultierende Bodengeschwindigkeit auf <= 1 m/s sinkt', () { // Bearing a->b ist 0 (Nord); Wind aus Nord (windDirFromDeg 0) weht nach // Sueden -> reiner Gegenwind, component = -windSpeedMs. const headwindTarget = Waypoint( lat: 1, lon: 0, altitudeM: 60, speedMs: 3, catchRadiusM: 60, windSpeedMs: 10, windDirFromDeg: 0, ); final bad = computeLegWindBad([start, headwindTarget], 25); expect(bad.single, isTrue); }); test('true, wenn die rohe Windgeschwindigkeit die maximale Fluggeschwindigkeit uebersteigt', () { // Reiner Rueckenwind - Bodengeschwindigkeit (15+10=25) ist hoch genug, // aber der Wind selbst (10 m/s) ist staerker als die Drohne (hier: max // 8 m/s) fliegen kann. final bad = computeLegWindBad([start, tailwindTarget], 8); expect(bad.single, isTrue); }); test('false bei moderatem Rueckenwind innerhalb der Grenzen', () { final bad = computeLegWindBad([start, tailwindTarget], 30); expect(bad.single, isFalse); }); test('false (nicht unbekannt), solange fuer das Leg kein Wind geladen ist', () { final bad = computeLegWindBad([start, noWindTarget], 25); expect(bad.single, isFalse); }); }); }