This commit is contained in:
gili8420 2026-08-14 00:01:49 +09:00
commit b43955e0d9
162 changed files with 15425 additions and 0 deletions

View file

@ -0,0 +1,88 @@
import { describe, it, expect } from 'vitest';
import { parseTelemetry, computeDeviations, distHaversine, parsePrediction } from '$domain';
import type { TelemetryPoint, PredictionStage } from '$domain';
const points: TelemetryPoint[] = [
{ latitude: 62.0, longitude: 129.0, altitude: 0, datetime: '2026-01-01T00:00:00Z', payload: '{}' },
{
latitude: 62.05,
longitude: 129.1,
altitude: 15000,
datetime: '2026-01-01T00:30:00Z',
payload: '{}'
}
];
describe('parseTelemetry', () => {
it('builds a flight path and takes the first sample as launch', () => {
const t = parseTelemetry(points);
expect(t.flight_path).toEqual([
[62.0, 129.0, 0],
[62.05, 129.1, 15000]
]);
expect(t.launch.latlng.lat).toBe(62.0);
expect(t.datapoints).toHaveLength(2);
});
it('throws on an empty series', () => {
expect(() => parseTelemetry([])).toThrow();
});
});
describe('distHaversine', () => {
it('is zero for identical points and ~343 km London→Paris', () => {
expect(distHaversine({ lat: 55, lng: 37 }, { lat: 55, lng: 37 })).toBe(0);
const d = distHaversine({ lat: 51.5074, lng: -0.1278 }, { lat: 48.8566, lng: 2.3522 });
expect(d).toBeGreaterThan(340);
expect(d).toBeLessThan(346);
});
});
describe('computeDeviations', () => {
// Prediction runs 00:00 → 01:30 along the same corridor as the telemetry.
const stages: PredictionStage[] = [
{
stage: 'ascent',
trajectory: [
{ altitude: 0, datetime: '2026-01-01T00:00:00Z', latitude: 62.0, longitude: 129.0 },
{ altitude: 30000, datetime: '2026-01-01T01:00:00Z', latitude: 62.1, longitude: 129.2 }
]
},
{
stage: 'descent',
trajectory: [
{ altitude: 30000, datetime: '2026-01-01T01:00:00Z', latitude: 62.1, longitude: 129.2 },
{ altitude: 0, datetime: '2026-01-01T01:30:00Z', latitude: 62.05, longitude: 129.4 }
]
}
];
const prediction = parsePrediction(stages);
it('matches each sample to the closest prediction point', () => {
const devs = computeDeviations(points, prediction);
expect(devs).toHaveLength(2);
// First sample sits exactly on the predicted launch point.
expect(devs[0].horizontal).toBeCloseTo(0, 6);
expect(devs[0].vertical).toBe(0);
// Second sample is offset from the prediction, both laterally and in altitude.
expect(devs[1].horizontal).toBeGreaterThan(0);
expect(devs[1].altActual).toBe(15000);
});
it('skips samples outside the prediction window', () => {
const outside: TelemetryPoint[] = [
{
latitude: 62,
longitude: 129,
altitude: 0,
datetime: '2025-12-31T00:00:00Z', // a day early
payload: '{}'
}
];
expect(computeDeviations(outside, prediction)).toHaveLength(0);
});
it('returns nothing without telemetry', () => {
expect(computeDeviations([], prediction)).toEqual([]);
});
});