@mearman/doppler 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import { $ as computeSegmentCpa, $t as QuietIntervalSchema, A as synthesizeMultiFlyoverAudio, At as ConstellationEntry, B as SpectrogramOptions, Bt as GeoPoint3D, C as formatConstellationKey, Ct as define, D as SyntheticAudioConfig, Dt as AircraftCategorySchema, E as encodeWavPcm16, Et as AircraftCategory, F as extractDopplerObservations, Ft as DopplerObservationSchema, G as downmixToMono, Gt as LinearFlightSegmentSchema, H as computeSingleSpectrogramFrame, Ht as HypothesisMatch, I as ComputedSingleFrame, It as DopplerRidgePoint, J as fft, Jt as LocationHypothesis, K as ComplexSpectrum, Kt as ListeningEpisode, L as DopplerSpectrogram, Lt as DopplerRidgePointSchema, M as DechirpScoreResult, Mt as DopplerDataset, N as scoreByEmissionDechirping, Nt as DopplerDatasetSchema, O as detectEnfSignature, Ot as AsymptoticKinematics, P as RidgeTrackerOptions, Pt as DopplerObservation, Q as SynthesizeCurveOptions, Qt as QuietInterval, R as SingleFrameOptions, Rt as EnfSignature, S as expandConstellationQueryKeys, St as getOwn, T as decodeWavBytes, Tt as AdsbPingSchema, U as decimateAudioStream, Ut as HypothesisMatchSchema, V as computeDopplerSpectrogram, Vt as GeoPoint3DSchema, W as designLowPassFirKernel, Wt as LinearFlightSegment, X as simplifyTrajectory4D, Xt as LocationSession, Y as TraceMetadata, Yt as LocationHypothesisSchema, Z as RetardedAcousticPoint, Zt as LocationSessionSchema, _ as unmatchedObservationLogPenalty, _t as geoToLocalEnu, a as BUNDLED_SEGMENTS, at as DEFAULT_SOUND_SPEED_MPS, b as QUERY_BUCKET_SPAN, bt as slantDistanceMeters, c as DopplerSessionEngine, ct as DEFAULT_ELLIPSE_SEGMENTS, d as ObservationSegmentPair, dt as HALF_TURN_DEG, en as SearchConstraints, et as solveRetardedDelaySec, f as RefineCoordinateOptions, ft as HorizontalEllipse, g as refineObserverCoordinateGaussNewton, gt as geoToEcef, h as fisherToConfidenceEllipse95, ht as encodeSpatialCell, i as BUNDLED_GROUND_TRUTH, it as AtmosphereState, j as DechirpOptions, jt as ConstellationEntrySchema, k as estimateTempFromCricketChirpsCelsius, kt as AsymptoticKinematicsSchema, l as SessionEngineOptions, lt as DEFAULT_SPATIAL_RESOLUTION, m as SLANT_SIGMA_FLOOR_METERS, mt as ellipseOutline, n as BUNDLED_DATASET, nn as Vector3D, nt as DopplerCurveMeasurement, o as generateBundledReferenceAudio, ot as effectiveRaySoundSpeedMps, p as RefinedLocationResult, pt as decodeSpatialCellCenter, q as computeHannPowerSpectrumDb, qt as ListeningEpisodeSchema, r as BUNDLED_DEM_GRID, rn as Vector3DSchema, rt as extractAsymptoticKinematics, s as generateBundledReferenceWavBytes, st as speedOfSoundAtTempMps, t as BUNDLED_CONSTELLATIONS, tn as SearchConstraintsSchema, tt as synthesizeRetardedDopplerCurve, u as MAX_ALONG_TRACK_WIND_MPS, ut as FULL_TURN_DEG, v as BuildConstellationOptions, vt as interpolateTerrainElevationMeters, w as matchesSearchConstraints, wt as AdsbPing, x as buildConstellationIndex, xt as recordOf, y as CandidateConstraintTarget, yt as localEnuToGeo, z as SpectrogramFrame, zt as EnfSignatureSchema } from "./index-TOQF-n8K.js";
2
+ export { AdsbPing, AdsbPingSchema, AircraftCategory, AircraftCategorySchema, AsymptoticKinematics, AsymptoticKinematicsSchema, AtmosphereState, BUNDLED_CONSTELLATIONS, BUNDLED_DATASET, BUNDLED_DEM_GRID, BUNDLED_GROUND_TRUTH, BUNDLED_SEGMENTS, BuildConstellationOptions, CandidateConstraintTarget, ComplexSpectrum, ComputedSingleFrame, ConstellationEntry, ConstellationEntrySchema, DEFAULT_ELLIPSE_SEGMENTS, DEFAULT_SOUND_SPEED_MPS, DEFAULT_SPATIAL_RESOLUTION, DechirpOptions, DechirpScoreResult, DopplerCurveMeasurement, DopplerDataset, DopplerDatasetSchema, DopplerObservation, DopplerObservationSchema, DopplerRidgePoint, DopplerRidgePointSchema, DopplerSessionEngine, DopplerSpectrogram, EnfSignature, EnfSignatureSchema, FULL_TURN_DEG, GeoPoint3D, GeoPoint3DSchema, HALF_TURN_DEG, HorizontalEllipse, HypothesisMatch, HypothesisMatchSchema, LinearFlightSegment, LinearFlightSegmentSchema, ListeningEpisode, ListeningEpisodeSchema, LocationHypothesis, LocationHypothesisSchema, LocationSession, LocationSessionSchema, MAX_ALONG_TRACK_WIND_MPS, ObservationSegmentPair, QUERY_BUCKET_SPAN, QuietInterval, QuietIntervalSchema, RefineCoordinateOptions, RefinedLocationResult, RetardedAcousticPoint, RidgeTrackerOptions, SLANT_SIGMA_FLOOR_METERS, SearchConstraints, SearchConstraintsSchema, SessionEngineOptions, SingleFrameOptions, SpectrogramFrame, SpectrogramOptions, SynthesizeCurveOptions, SyntheticAudioConfig, TraceMetadata, Vector3D, Vector3DSchema, buildConstellationIndex, computeDopplerSpectrogram, computeHannPowerSpectrumDb, computeSegmentCpa, computeSingleSpectrogramFrame, decimateAudioStream, decodeSpatialCellCenter, decodeWavBytes, define, designLowPassFirKernel, detectEnfSignature, downmixToMono, effectiveRaySoundSpeedMps, ellipseOutline, encodeSpatialCell, encodeWavPcm16, estimateTempFromCricketChirpsCelsius, expandConstellationQueryKeys, extractAsymptoticKinematics, extractDopplerObservations, fft, fisherToConfidenceEllipse95, formatConstellationKey, generateBundledReferenceAudio, generateBundledReferenceWavBytes, geoToEcef, geoToLocalEnu, getOwn, interpolateTerrainElevationMeters, localEnuToGeo, matchesSearchConstraints, recordOf, refineObserverCoordinateGaussNewton, scoreByEmissionDechirping, simplifyTrajectory4D, slantDistanceMeters, solveRetardedDelaySec, speedOfSoundAtTempMps, synthesizeMultiFlyoverAudio, synthesizeRetardedDopplerCurve, unmatchedObservationLogPenalty };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { tt as AdsbPingSchema, nt as AircraftCategorySchema, rt as AsymptoticKinematicsSchema, t as BUNDLED_CONSTELLATIONS, n as BUNDLED_DATASET, r as BUNDLED_DEM_GRID, i as BUNDLED_GROUND_TRUTH, a as BUNDLED_SEGMENTS, it as ConstellationEntrySchema, V as DEFAULT_ELLIPSE_SEGMENTS, R as DEFAULT_SOUND_SPEED_MPS, H as DEFAULT_SPATIAL_RESOLUTION, at as DopplerDatasetSchema, ot as DopplerObservationSchema, st as DopplerRidgePointSchema, c as DopplerSessionEngine, ct as EnfSignatureSchema, U as FULL_TURN_DEG, lt as GeoPoint3DSchema, W as HALF_TURN_DEG, ut as HypothesisMatchSchema, dt as LinearFlightSegmentSchema, ft as ListeningEpisodeSchema, pt as LocationHypothesisSchema, mt as LocationSessionSchema, l as MAX_ALONG_TRACK_WIND_MPS, m as QUERY_BUCKET_SPAN, ht as QuietIntervalSchema, u as SLANT_SIGMA_FLOOR_METERS, gt as SearchConstraintsSchema, _t as Vector3DSchema, h as buildConstellationIndex, E as computeDopplerSpectrogram, j as computeHannPowerSpectrumDb, P as computeSegmentCpa, D as computeSingleSpectrogramFrame, O as decimateAudioStream, G as decodeSpatialCellCenter, y as decodeWavBytes, vt as define, k as designLowPassFirKernel, x as detectEnfSignature, A as downmixToMono, z as effectiveRaySoundSpeedMps, K as ellipseOutline, q as encodeSpatialCell, b as encodeWavPcm16, S as estimateTempFromCricketChirpsCelsius, g as expandConstellationQueryKeys, L as extractAsymptoticKinematics, T as extractDopplerObservations, M as fft, d as fisherToConfidenceEllipse95, _ as formatConstellationKey, o as generateBundledReferenceAudio, s as generateBundledReferenceWavBytes, J as geoToEcef, Y as geoToLocalEnu, et as getOwn, X as interpolateTerrainElevationMeters, Z as localEnuToGeo, v as matchesSearchConstraints, $ as recordOf, f as refineObserverCoordinateGaussNewton, w as scoreByEmissionDechirping, N as simplifyTrajectory4D, Q as slantDistanceMeters, F as solveRetardedDelaySec, B as speedOfSoundAtTempMps, C as synthesizeMultiFlyoverAudio, I as synthesizeRetardedDopplerCurve, p as unmatchedObservationLogPenalty } from "./src-C4UucgwP.js";
@@ -0,0 +1,4 @@
1
+ // src/browser/mic-capture-protocol.ts
2
+ /** Name under which the microphone capture processor registers itself in the audio worklet scope, and which the main thread passes to `AudioWorkletNode`. */
3
+ const MIC_CAPTURE_PROCESSOR_NAME = "doppler-mic-capture";
4
+ export { MIC_CAPTURE_PROCESSOR_NAME as t };
@@ -0,0 +1,128 @@
1
+ import { $ as computeSegmentCpa, $t as QuietIntervalSchema, A as synthesizeMultiFlyoverAudio, At as ConstellationEntry, B as SpectrogramOptions, Bt as GeoPoint3D, C as formatConstellationKey, Ct as define, D as SyntheticAudioConfig, Dt as AircraftCategorySchema, E as encodeWavPcm16, Et as AircraftCategory, F as extractDopplerObservations, Ft as DopplerObservationSchema, G as downmixToMono, Gt as LinearFlightSegmentSchema, H as computeSingleSpectrogramFrame, Ht as HypothesisMatch, I as ComputedSingleFrame, It as DopplerRidgePoint, J as fft, Jt as LocationHypothesis, K as ComplexSpectrum, Kt as ListeningEpisode, L as DopplerSpectrogram, Lt as DopplerRidgePointSchema, M as DechirpScoreResult, Mt as DopplerDataset, N as scoreByEmissionDechirping, Nt as DopplerDatasetSchema, O as detectEnfSignature, Ot as AsymptoticKinematics, P as RidgeTrackerOptions, Pt as DopplerObservation, Q as SynthesizeCurveOptions, Qt as QuietInterval, R as SingleFrameOptions, Rt as EnfSignature, S as expandConstellationQueryKeys, St as getOwn, T as decodeWavBytes, Tt as AdsbPingSchema, U as decimateAudioStream, Ut as HypothesisMatchSchema, V as computeDopplerSpectrogram, Vt as GeoPoint3DSchema, W as designLowPassFirKernel, Wt as LinearFlightSegment, X as simplifyTrajectory4D, Xt as LocationSession, Y as TraceMetadata, Yt as LocationHypothesisSchema, Z as RetardedAcousticPoint, Zt as LocationSessionSchema, _ as unmatchedObservationLogPenalty, _t as geoToLocalEnu, a as BUNDLED_SEGMENTS, at as DEFAULT_SOUND_SPEED_MPS, b as QUERY_BUCKET_SPAN, bt as slantDistanceMeters, c as DopplerSessionEngine, ct as DEFAULT_ELLIPSE_SEGMENTS, d as ObservationSegmentPair, dt as HALF_TURN_DEG, en as SearchConstraints, et as solveRetardedDelaySec, f as RefineCoordinateOptions, ft as HorizontalEllipse, g as refineObserverCoordinateGaussNewton, gt as geoToEcef, h as fisherToConfidenceEllipse95, ht as encodeSpatialCell, i as BUNDLED_GROUND_TRUTH, it as AtmosphereState, j as DechirpOptions, jt as ConstellationEntrySchema, k as estimateTempFromCricketChirpsCelsius, kt as AsymptoticKinematicsSchema, l as SessionEngineOptions, lt as DEFAULT_SPATIAL_RESOLUTION, m as SLANT_SIGMA_FLOOR_METERS, mt as ellipseOutline, n as BUNDLED_DATASET, nn as Vector3D, nt as DopplerCurveMeasurement, o as generateBundledReferenceAudio, ot as effectiveRaySoundSpeedMps, p as RefinedLocationResult, pt as decodeSpatialCellCenter, q as computeHannPowerSpectrumDb, qt as ListeningEpisodeSchema, r as BUNDLED_DEM_GRID, rn as Vector3DSchema, rt as extractAsymptoticKinematics, s as generateBundledReferenceWavBytes, st as speedOfSoundAtTempMps, t as BUNDLED_CONSTELLATIONS, tn as SearchConstraintsSchema, tt as synthesizeRetardedDopplerCurve, u as MAX_ALONG_TRACK_WIND_MPS, ut as FULL_TURN_DEG, v as BuildConstellationOptions, vt as interpolateTerrainElevationMeters, w as matchesSearchConstraints, wt as AdsbPing, x as buildConstellationIndex, xt as recordOf, y as CandidateConstraintTarget, yt as localEnuToGeo, z as SpectrogramFrame, zt as EnfSignatureSchema } from "../index-TOQF-n8K.js";
2
+ import { z } from "zod";
3
+ /**
4
+ * Raw trace format from `adsb.lol` or `tar1090` JSON exports.
5
+ */
6
+ export declare const RawAdsbTraceFileSchema: z.ZodObject<{
7
+ icao: z.ZodString;
8
+ r: z.ZodOptional<z.ZodString>;
9
+ t: z.ZodOptional<z.ZodString>;
10
+ desc: z.ZodOptional<z.ZodString>;
11
+ timestamp: z.ZodOptional<z.ZodNumber>;
12
+ trace: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"ground">, z.ZodNull]>], z.ZodUnknown>>;
13
+ }, z.core.$strip> & {
14
+ readonly is: (value: unknown) => value is {
15
+ icao: string;
16
+ trace: [number, number, number, number | "ground" | null, ...unknown[]][];
17
+ r?: string | undefined;
18
+ t?: string | undefined;
19
+ desc?: string | undefined;
20
+ timestamp?: number | undefined;
21
+ };
22
+ };
23
+ export type RawAdsbTraceFile = z.infer<typeof RawAdsbTraceFileSchema>;
24
+ /** A latitude and longitude rectangle. */
25
+ export interface BoundingBox {
26
+ readonly minLat: number;
27
+ readonly minLon: number;
28
+ readonly maxLat: number;
29
+ readonly maxLon: number;
30
+ }
31
+ /** Options for turning raw traces into segments. */
32
+ export interface AdsbTraceOptions {
33
+ /** Pings outside the box end the current run and are dropped, so memory and work follow the region rather than the whole archive. A flight audible from inside the box but flying outside it is lost, so the box should include the acoustic range (about 10 km) as margin. */
34
+ readonly bbox?: Readonly<BoundingBox>;
35
+ /** Largest 3D deviation, in metres, of a dropped ping from its segment. Defaults to half the solver's slant-distance sigma floor, so compression error cannot dominate the solver's own uncertainty. */
36
+ readonly toleranceMeters?: number;
37
+ }
38
+ /**
39
+ * Classifies an ICAO aircraft type code into a propulsion category.
40
+ * @param typeCode - 4-character ICAO aircraft type designator (e.g. `C172`, `B738`, `DA42`, `EC35`).
41
+ * @returns Propulsion category.
42
+ */
43
+ export declare function classifyAircraftCategory(typeCode?: string): AircraftCategory;
44
+ /**
45
+ * Converts a raw `adsb.lol` / `tar1090` trace object into compressed 4D piecewise-linear segments.
46
+ * Jet categories are discarded. A ping on the ground, with no altitude, or above 20,000 ft ends the current run, and so does a gap of more than two minutes, so a segment never spans a landing, a climb to cruise or a loss of coverage.
47
+ * @param trace - Raw ADS-B trace record.
48
+ * @param dateIso - ISO calendar date (`YYYY-MM-DD`).
49
+ * @param baseEpochSec - Epoch seconds that the trace's point offsets are counted from.
50
+ * @param options - Region filter and compression tolerance.
51
+ * @returns Array of linear flight segments.
52
+ */
53
+ export declare function processRawAdsbTrace(trace: Readonly<RawAdsbTraceFile>, dateIso: string, baseEpochSec: number, options?: Readonly<AdsbTraceOptions>): LinearFlightSegment[];
54
+ /** Options for reading ADS-B input. */
55
+ export interface AdsbInputOptions extends AdsbTraceOptions {
56
+ /** Calendar date (`YYYY-MM-DD`) for raw traces that carry no `timestamp`; their offsets count from midnight UTC of this date. */
57
+ readonly dateIso?: string;
58
+ }
59
+ /**
60
+ * Converts parsed ADS-B JSON into segments: ready-made segments (an array, or an object with `segments`), a raw trace, or an array of raw traces.
61
+ * @param parsed - The parsed JSON value.
62
+ * @param options - Date for raw traces without a `timestamp`.
63
+ * @returns The segments it holds.
64
+ * @throws When the value matches none of the accepted shapes, or a raw trace has neither a `timestamp` nor a date option.
65
+ */
66
+ export declare function segmentsFromAdsbJson(parsed: unknown, options: Readonly<AdsbInputOptions>): LinearFlightSegment[];
67
+ /**
68
+ * Reads flight segments from ADS-B JSON on disk: a file or every `.json` file of a directory (not recursive). Each file may hold ready-made segments (an array, or an object with `segments`), a raw `adsb.lol` / `tar1090` trace, or an array of raw traces.
69
+ * @param inputPath - A JSON file or a directory of them.
70
+ * @param options - Date for raw traces without a `timestamp`.
71
+ * @returns Segments from every file, in file-name order.
72
+ * @throws When a file is not valid JSON or matches none of the accepted shapes, naming the file.
73
+ */
74
+ export declare function readAdsbSegments(inputPath: string, options?: Readonly<AdsbInputOptions>): LinearFlightSegment[];
75
+ /**
76
+ * Loads and parses a DopplerDataset JSON file from disk.
77
+ * @param filePath - Path to `.json` dataset file.
78
+ * @returns Parsed `DopplerDataset`.
79
+ */
80
+ export declare function loadDatasetFile(filePath: string): DopplerDataset;
81
+ /**
82
+ * Serializes and saves a DopplerDataset to disk with built constellation index. The file is compact JSON written in batches, because a day of real traffic is hundreds of thousands of segments and one `JSON.stringify` of that overflows the string length limit.
83
+ * @param filePath - Output path.
84
+ * @param segments - Array of linear flight segments.
85
+ * @param demGrid - Optional DEM elevation grid points.
86
+ * @returns The saved `DopplerDataset`.
87
+ */
88
+ export declare function saveDatasetBundle(filePath: string, segments: readonly LinearFlightSegment[], demGrid?: readonly GeoPoint3D[]): DopplerDataset;
89
+ /**
90
+ * Reads flight segments from a tar stream of `adsb.lol` `globe_history` traces, one aircraft at a time so memory holds a single trace and the segments kept.
91
+ * A day is published as `.tar.aa` and `.tar.ab` parts that concatenate to one tar, so the two parts can be piped in one after the other.
92
+ * @param source - The tar bytes.
93
+ * @param options - Region filter and compression tolerance.
94
+ * @returns Segments from every trace file in the archive.
95
+ * @throws When a trace file is not valid JSON of the trace shape, naming the file.
96
+ */
97
+ export declare function readAdsbArchiveSegments(source: Readonly<AsyncIterable<Uint8Array>>, options?: Readonly<AdsbInputOptions>): Promise<LinearFlightSegment[]>;
98
+ /** A regular file read from a tar stream. */
99
+ export interface TarFile {
100
+ readonly name: string;
101
+ readonly data: Buffer;
102
+ }
103
+ /**
104
+ * Reads the regular files of a tar stream one at a time. Entries for which `wanted` is false are skipped without being buffered, which matters for archives that mix small files with very large ones.
105
+ * Entry data is held in memory, so one entry must fit in memory. GNU long names (`L`) are honoured; other extension headers (`K`, pax `x` and `g`) are skipped, and directories, links and devices are ignored.
106
+ * @param source - The tar bytes, for example a file or standard input stream.
107
+ * @param wanted - Chooses by name which entries are read.
108
+ * @returns A generator of each wanted regular file.
109
+ * @throws When the stream ends inside a header or an entry.
110
+ */
111
+ export declare function readTarFiles(source: Readonly<AsyncIterable<Uint8Array>>, wanted: (name: string) => boolean): AsyncGenerator<TarFile>;
112
+ /**
113
+ * Reads and decodes a WAV audio file from disk into mono `Float32Array` samples and sample rate.
114
+ * @param filePath - Path to `.wav` file.
115
+ * @returns Decoded mono audio samples and sample rate in Hz.
116
+ */
117
+ export declare function readWavFile(filePath: string): {
118
+ readonly samples: Float32Array;
119
+ readonly sampleRateHz: number;
120
+ };
121
+ /**
122
+ * Encodes and writes a mono `Float32Array` audio buffer to disk as a 16-bit PCM WAV file.
123
+ * @param filePath - Output path.
124
+ * @param samples - Audio samples in range `[-1, 1]`.
125
+ * @param sampleRateHz - Audio sample rate in Hz.
126
+ */
127
+ export declare function writeWavFile(filePath: string, samples: Readonly<Float32Array>, sampleRateHz: number): void;
128
+ export { AdsbPing, AdsbPingSchema, AircraftCategory, AircraftCategorySchema, AsymptoticKinematics, AsymptoticKinematicsSchema, AtmosphereState, BUNDLED_CONSTELLATIONS, BUNDLED_DATASET, BUNDLED_DEM_GRID, BUNDLED_GROUND_TRUTH, BUNDLED_SEGMENTS, BuildConstellationOptions, CandidateConstraintTarget, ComplexSpectrum, ComputedSingleFrame, ConstellationEntry, ConstellationEntrySchema, DEFAULT_ELLIPSE_SEGMENTS, DEFAULT_SOUND_SPEED_MPS, DEFAULT_SPATIAL_RESOLUTION, DechirpOptions, DechirpScoreResult, DopplerCurveMeasurement, DopplerDataset, DopplerDatasetSchema, DopplerObservation, DopplerObservationSchema, DopplerRidgePoint, DopplerRidgePointSchema, DopplerSessionEngine, DopplerSpectrogram, EnfSignature, EnfSignatureSchema, FULL_TURN_DEG, GeoPoint3D, GeoPoint3DSchema, HALF_TURN_DEG, HorizontalEllipse, HypothesisMatch, HypothesisMatchSchema, LinearFlightSegment, LinearFlightSegmentSchema, ListeningEpisode, ListeningEpisodeSchema, LocationHypothesis, LocationHypothesisSchema, LocationSession, LocationSessionSchema, MAX_ALONG_TRACK_WIND_MPS, ObservationSegmentPair, QUERY_BUCKET_SPAN, QuietInterval, QuietIntervalSchema, RefineCoordinateOptions, RefinedLocationResult, RetardedAcousticPoint, RidgeTrackerOptions, SLANT_SIGMA_FLOOR_METERS, SearchConstraints, SearchConstraintsSchema, SessionEngineOptions, SingleFrameOptions, SpectrogramFrame, SpectrogramOptions, SynthesizeCurveOptions, SyntheticAudioConfig, TraceMetadata, Vector3D, Vector3DSchema, buildConstellationIndex, computeDopplerSpectrogram, computeHannPowerSpectrumDb, computeSegmentCpa, computeSingleSpectrogramFrame, decimateAudioStream, decodeSpatialCellCenter, decodeWavBytes, define, designLowPassFirKernel, detectEnfSignature, downmixToMono, effectiveRaySoundSpeedMps, ellipseOutline, encodeSpatialCell, encodeWavPcm16, estimateTempFromCricketChirpsCelsius, expandConstellationQueryKeys, extractAsymptoticKinematics, extractDopplerObservations, fft, fisherToConfidenceEllipse95, formatConstellationKey, generateBundledReferenceAudio, generateBundledReferenceWavBytes, geoToEcef, geoToLocalEnu, getOwn, interpolateTerrainElevationMeters, localEnuToGeo, matchesSearchConstraints, recordOf, refineObserverCoordinateGaussNewton, scoreByEmissionDechirping, simplifyTrajectory4D, slantDistanceMeters, solveRetardedDelaySec, speedOfSoundAtTempMps, synthesizeMultiFlyoverAudio, synthesizeRetardedDopplerCurve, unmatchedObservationLogPenalty };
@@ -0,0 +1,478 @@
1
+ import { $ as recordOf, A as downmixToMono, B as speedOfSoundAtTempMps, C as synthesizeMultiFlyoverAudio, D as computeSingleSpectrogramFrame, E as computeDopplerSpectrogram, F as solveRetardedDelaySec, G as decodeSpatialCellCenter, H as DEFAULT_SPATIAL_RESOLUTION, I as synthesizeRetardedDopplerCurve, J as geoToEcef, K as ellipseOutline, L as extractAsymptoticKinematics, M as fft, N as simplifyTrajectory4D, O as decimateAudioStream, P as computeSegmentCpa, Q as slantDistanceMeters, R as DEFAULT_SOUND_SPEED_MPS, S as estimateTempFromCricketChirpsCelsius, T as extractDopplerObservations, U as FULL_TURN_DEG, V as DEFAULT_ELLIPSE_SEGMENTS, W as HALF_TURN_DEG, X as interpolateTerrainElevationMeters, Y as geoToLocalEnu, Z as localEnuToGeo, _ as formatConstellationKey, _t as Vector3DSchema, a as BUNDLED_SEGMENTS, at as DopplerDatasetSchema, b as encodeWavPcm16, c as DopplerSessionEngine, ct as EnfSignatureSchema, d as fisherToConfidenceEllipse95, dt as LinearFlightSegmentSchema, et as getOwn, f as refineObserverCoordinateGaussNewton, ft as ListeningEpisodeSchema, g as expandConstellationQueryKeys, gt as SearchConstraintsSchema, h as buildConstellationIndex, ht as QuietIntervalSchema, i as BUNDLED_GROUND_TRUTH, it as ConstellationEntrySchema, j as computeHannPowerSpectrumDb, k as designLowPassFirKernel, l as MAX_ALONG_TRACK_WIND_MPS, lt as GeoPoint3DSchema, m as QUERY_BUCKET_SPAN, mt as LocationSessionSchema, n as BUNDLED_DATASET, nt as AircraftCategorySchema, o as generateBundledReferenceAudio, ot as DopplerObservationSchema, p as unmatchedObservationLogPenalty, pt as LocationHypothesisSchema, q as encodeSpatialCell, r as BUNDLED_DEM_GRID, rt as AsymptoticKinematicsSchema, s as generateBundledReferenceWavBytes, st as DopplerRidgePointSchema, t as BUNDLED_CONSTELLATIONS, tt as AdsbPingSchema, u as SLANT_SIGMA_FLOOR_METERS, ut as HypothesisMatchSchema, v as matchesSearchConstraints, vt as define, w as scoreByEmissionDechirping, x as detectEnfSignature, y as decodeWavBytes, z as effectiveRaySoundSpeedMps } from "../src-C4UucgwP.js";
2
+ import { t as MS_PER_SECOND } from "../time-DWqWWgPL.js";
3
+ import { z } from "zod";
4
+ import { gunzipSync } from "node:zlib";
5
+ import { closeSync, openSync, readFileSync, readdirSync, statSync, writeFileSync, writeSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ // src/node/stream-adsb.ts
8
+ /**
9
+ * One trace sample as `adsb.lol` and `tar1090` write it: seconds since the trace's `timestamp`, latitude, longitude and barometric altitude in feet, which is the string `ground` on the ground and null when unreported. Later fields (ground speed, track, flags) are ignored.
10
+ */
11
+ const RawTracePoint = z.tuple([
12
+ z.number(),
13
+ z.number(),
14
+ z.number(),
15
+ z.union([
16
+ z.number(),
17
+ z.literal("ground"),
18
+ z.null()
19
+ ])
20
+ ]).rest(z.unknown());
21
+ /**
22
+ * Raw trace format from `adsb.lol` or `tar1090` JSON exports.
23
+ */
24
+ const RawAdsbTraceFileSchema = define(z.object({
25
+ icao: z.string().min(1),
26
+ r: z.string().optional(),
27
+ t: z.string().optional(),
28
+ desc: z.string().optional(),
29
+ /** Epoch seconds that each point's offset is counted from. */
30
+ timestamp: z.number().optional(),
31
+ trace: z.array(RawTracePoint)
32
+ }));
33
+ /** Altitude above which a ping is cruise traffic that the microphone is not expected to hear: 20,000 ft. */
34
+ const MAX_AUDIBLE_ALTITUDE_METERS = 6096;
35
+ /**
36
+ * Ground speed, in m/s (about 195 knots), above which a segment of an aircraft whose type is not classified is dropped. Light propeller aircraft and helicopters seldom exceed it (about 2 percent of their segments in a sample of real traces), whereas most unclassified types are airliners that fly faster than this below 20,000 ft (about 56 percent of theirs) and have no narrowband propeller tone to match.
37
+ */
38
+ const MAX_UNCLASSIFIED_SPEED_MPS = 100;
39
+ /** Characters of the ISO 8601 calendar date (`YYYY-MM-DD`) at the start of a `Date.toISOString()` string. */
40
+ const ISO_DATE_LENGTH = 10;
41
+ /** Metres in a foot. */
42
+ const METERS_PER_FOOT = .3048;
43
+ /**
44
+ * Longest gap between pings that is still bridged by a straight line. Across a longer gap the aircraft can have turned, landed or left coverage, and at light-aircraft speeds (about 70 m/s) a two-minute gap is already about 8 km, comparable to the acoustic range, so the run is split instead.
45
+ */
46
+ const MAX_PING_GAP_SEC = 120;
47
+ function inBox(lat, lon, box) {
48
+ return lat >= box.minLat && lat <= box.maxLat && lon >= box.minLon && lon <= box.maxLon;
49
+ }
50
+ /**
51
+ * Classifies an ICAO aircraft type code into a propulsion category.
52
+ * @param typeCode - 4-character ICAO aircraft type designator (e.g. `C172`, `B738`, `DA42`, `EC35`).
53
+ * @returns Propulsion category.
54
+ */
55
+ function classifyAircraftCategory(typeCode) {
56
+ if (typeCode === void 0 || typeCode === "") return "unknown";
57
+ const code = typeCode.toUpperCase().trim();
58
+ if ([
59
+ "R22",
60
+ "R44",
61
+ "R66",
62
+ "EC20",
63
+ "EC30",
64
+ "EC35",
65
+ "EC45",
66
+ "B06",
67
+ "B206",
68
+ "B407",
69
+ "AS50",
70
+ "A109",
71
+ "S76",
72
+ "H125",
73
+ "H135",
74
+ "H145",
75
+ "G2CA"
76
+ ].includes(code)) return "helicopter";
77
+ if ([
78
+ "C150",
79
+ "C152",
80
+ "C172",
81
+ "C182",
82
+ "C206",
83
+ "C210",
84
+ "PA28",
85
+ "PA32",
86
+ "PA34",
87
+ "PA44",
88
+ "SR20",
89
+ "SR22",
90
+ "DA20",
91
+ "DA40",
92
+ "DA42",
93
+ "P28A",
94
+ "AA5",
95
+ "BE36",
96
+ "BE58",
97
+ "M20T",
98
+ "RV7",
99
+ "RV8",
100
+ "RV10"
101
+ ].includes(code)) return "piston";
102
+ if ([
103
+ "B350",
104
+ "BE20",
105
+ "PC12",
106
+ "TBM7",
107
+ "TBM8",
108
+ "TBM9",
109
+ "DH8D",
110
+ "AT72",
111
+ "AT76",
112
+ "AT45",
113
+ "C208",
114
+ "SW4",
115
+ "D228"
116
+ ].includes(code)) return "turboprop";
117
+ if ([
118
+ "A319",
119
+ "A320",
120
+ "A321",
121
+ "A332",
122
+ "A333",
123
+ "A359",
124
+ "B737",
125
+ "B738",
126
+ "B739",
127
+ "B772",
128
+ "B77W",
129
+ "B788",
130
+ "B789",
131
+ "E190",
132
+ "E195",
133
+ "CRJ7",
134
+ "CRJ9",
135
+ "C56X",
136
+ "C680",
137
+ "GL5T",
138
+ "GLEX",
139
+ "FA7X",
140
+ "CL30"
141
+ ].includes(code)) return "jet";
142
+ return "unknown";
143
+ }
144
+ /**
145
+ * Converts a raw `adsb.lol` / `tar1090` trace object into compressed 4D piecewise-linear segments.
146
+ * Jet categories are discarded. A ping on the ground, with no altitude, or above 20,000 ft ends the current run, and so does a gap of more than two minutes, so a segment never spans a landing, a climb to cruise or a loss of coverage.
147
+ * @param trace - Raw ADS-B trace record.
148
+ * @param dateIso - ISO calendar date (`YYYY-MM-DD`).
149
+ * @param baseEpochSec - Epoch seconds that the trace's point offsets are counted from.
150
+ * @param options - Region filter and compression tolerance.
151
+ * @returns Array of linear flight segments.
152
+ */
153
+ function processRawAdsbTrace(trace, dateIso, baseEpochSec, options = {}) {
154
+ const typeCode = trace.t ?? "unknown";
155
+ const category = classifyAircraftCategory(typeCode);
156
+ if (category === "jet") return [];
157
+ const runs = [];
158
+ let run = [];
159
+ const endRun = () => {
160
+ if (run.length > 0) {
161
+ runs.push(run);
162
+ run = [];
163
+ }
164
+ };
165
+ for (const [offsetSec, lat, lon, altFeet] of trace.trace) {
166
+ const altMeters = typeof altFeet === "number" ? altFeet * METERS_PER_FOOT : void 0;
167
+ const { bbox } = options;
168
+ if (altMeters === void 0 || altMeters > MAX_AUDIBLE_ALTITUDE_METERS || bbox !== void 0 && !inBox(lat, lon, bbox)) {
169
+ endRun();
170
+ continue;
171
+ }
172
+ const epochSec = baseEpochSec + offsetSec;
173
+ const previous = run[run.length - 1];
174
+ if (previous !== void 0 && epochSec - previous.epochSec > MAX_PING_GAP_SEC) endRun();
175
+ run.push({
176
+ epochSec,
177
+ lat,
178
+ lon,
179
+ altMeters
180
+ });
181
+ }
182
+ endRun();
183
+ return runs.flatMap((pings) => simplifyTrajectory4D(pings, {
184
+ flightId: trace.r ?? trace.icao,
185
+ icao24: trace.icao,
186
+ typeCode,
187
+ category,
188
+ dateIso
189
+ }, options.toleranceMeters ?? 20)).filter((segment) => category !== "unknown" || segment.groundspeedMps <= MAX_UNCLASSIFIED_SPEED_MPS);
190
+ }
191
+ /**
192
+ * Converts parsed ADS-B JSON into segments: ready-made segments (an array, or an object with `segments`), a raw trace, or an array of raw traces.
193
+ * @param parsed - The parsed JSON value.
194
+ * @param options - Date for raw traces without a `timestamp`.
195
+ * @returns The segments it holds.
196
+ * @throws When the value matches none of the accepted shapes, or a raw trace has neither a `timestamp` nor a date option.
197
+ */
198
+ function segmentsFromAdsbJson(parsed, options) {
199
+ if (Array.isArray(parsed)) return parsed.flatMap((element) => segmentsFromAdsbJson(element, options));
200
+ if (typeof parsed === "object" && parsed !== null && "segments" in parsed) return z.array(LinearFlightSegmentSchema).parse(parsed.segments);
201
+ if (LinearFlightSegmentSchema.is(parsed)) return [parsed];
202
+ const trace = RawAdsbTraceFileSchema.parse(parsed);
203
+ if (trace.timestamp !== void 0) return processRawAdsbTrace(trace, new Date(trace.timestamp * MS_PER_SECOND).toISOString().slice(0, ISO_DATE_LENGTH), trace.timestamp, options);
204
+ if (options.dateIso === void 0) throw new Error(`trace ${trace.icao} has no timestamp, so its date is unknown; pass the date`);
205
+ return processRawAdsbTrace(trace, options.dateIso, Date.parse(`${options.dateIso}T00:00:00Z`) / MS_PER_SECOND, options);
206
+ }
207
+ /**
208
+ * Reads flight segments from ADS-B JSON on disk: a file or every `.json` file of a directory (not recursive). Each file may hold ready-made segments (an array, or an object with `segments`), a raw `adsb.lol` / `tar1090` trace, or an array of raw traces.
209
+ * @param inputPath - A JSON file or a directory of them.
210
+ * @param options - Date for raw traces without a `timestamp`.
211
+ * @returns Segments from every file, in file-name order.
212
+ * @throws When a file is not valid JSON or matches none of the accepted shapes, naming the file.
213
+ */
214
+ function readAdsbSegments(inputPath, options = {}) {
215
+ return (statSync(inputPath).isDirectory() ? readdirSync(inputPath).filter((name) => name.endsWith(".json")).sort().map((name) => join(inputPath, name)) : [inputPath]).flatMap((file) => {
216
+ try {
217
+ return segmentsFromAdsbJson(JSON.parse(readFileSync(file, "utf8")), options);
218
+ } catch (error) {
219
+ throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
220
+ }
221
+ });
222
+ }
223
+ /**
224
+ * Loads and parses a DopplerDataset JSON file from disk.
225
+ * @param filePath - Path to `.json` dataset file.
226
+ * @returns Parsed `DopplerDataset`.
227
+ */
228
+ function loadDatasetFile(filePath) {
229
+ const raw = readFileSync(filePath, "utf8");
230
+ const parsed = JSON.parse(raw);
231
+ return DopplerDatasetSchema.parse(parsed);
232
+ }
233
+ /** Items serialised per write, so no single string approaches the engine's string length limit however large the dataset. */
234
+ const WRITE_BATCH_ITEMS = 5e3;
235
+ function writeJsonArray(fd, items) {
236
+ writeSync(fd, "[");
237
+ for (let start = 0; start < items.length; start += WRITE_BATCH_ITEMS) {
238
+ const batch = items.slice(start, start + WRITE_BATCH_ITEMS).map((item) => JSON.stringify(item)).join(",");
239
+ writeSync(fd, start === 0 ? batch : `,${batch}`);
240
+ }
241
+ writeSync(fd, "]");
242
+ }
243
+ /**
244
+ * Serializes and saves a DopplerDataset to disk with built constellation index. The file is compact JSON written in batches, because a day of real traffic is hundreds of thousands of segments and one `JSON.stringify` of that overflows the string length limit.
245
+ * @param filePath - Output path.
246
+ * @param segments - Array of linear flight segments.
247
+ * @param demGrid - Optional DEM elevation grid points.
248
+ * @returns The saved `DopplerDataset`.
249
+ */
250
+ function saveDatasetBundle(filePath, segments, demGrid = []) {
251
+ const constellations = buildConstellationIndex(segments, { demGrid });
252
+ const dataset = {
253
+ version: "1.0.0",
254
+ generatedAtIso: (/* @__PURE__ */ new Date()).toISOString(),
255
+ segments,
256
+ constellations,
257
+ elevationGrid: demGrid
258
+ };
259
+ const fd = openSync(filePath, "w");
260
+ try {
261
+ writeSync(fd, `{"version":${JSON.stringify(dataset.version)},"generatedAtIso":${JSON.stringify(dataset.generatedAtIso)},"segments":`);
262
+ writeJsonArray(fd, segments);
263
+ writeSync(fd, ",\"constellations\":");
264
+ writeJsonArray(fd, constellations);
265
+ writeSync(fd, ",\"elevationGrid\":");
266
+ writeJsonArray(fd, demGrid);
267
+ writeSync(fd, "}");
268
+ } finally {
269
+ closeSync(fd);
270
+ }
271
+ return dataset;
272
+ }
273
+ // src/node/tar-stream.ts
274
+ /** Bytes in a tar block: headers and the padding of entry data are multiples of it. */
275
+ const TAR_BLOCK_BYTES = 512;
276
+ /** Offsets and lengths of the header fields this reader needs (POSIX ustar and GNU tar agree on them). */
277
+ const NAME_FIELD = {
278
+ offset: 0,
279
+ length: 100
280
+ };
281
+ const SIZE_FIELD = {
282
+ offset: 124,
283
+ length: 12
284
+ };
285
+ const TYPE_FIELD_OFFSET = 156;
286
+ const PREFIX_FIELD = {
287
+ offset: 345,
288
+ length: 155
289
+ };
290
+ /** Tar writes sizes as octal text. */
291
+ const OCTAL_RADIX = 8;
292
+ /** Words for the parser state in a truncation error. */
293
+ const STATE_LABELS = {
294
+ header: "header",
295
+ longName: "long name",
296
+ data: "entry",
297
+ skip: "entry"
298
+ };
299
+ /**
300
+ * A push-driven tar parser: it is fed chunks as they arrive and holds only the bytes of the current header or entry plus the unconsumed remainder of the current chunk.
301
+ * It is a synchronous state machine, so the only suspension point of a reader is the `for await` over the source.
302
+ */
303
+ var TarParser = class {
304
+ wanted;
305
+ state = { kind: "header" };
306
+ /** Bytes still needed to finish the current state. */
307
+ need = TAR_BLOCK_BYTES;
308
+ /** Bytes the current state asked for in total, for the truncation error. */
309
+ total = TAR_BLOCK_BYTES;
310
+ /** Collected bytes of the current header, long name or file; skipped bytes are not kept. */
311
+ collected = [];
312
+ longName;
313
+ ended = false;
314
+ constructor(wanted) {
315
+ this.wanted = wanted;
316
+ }
317
+ /** True once an end-of-archive block has been read: later bytes are ignored. */
318
+ get isEnded() {
319
+ return this.ended;
320
+ }
321
+ begin(state, length) {
322
+ this.state = state;
323
+ this.need = length;
324
+ this.total = length;
325
+ this.collected = [];
326
+ }
327
+ /**
328
+ * Feeds one chunk of the stream.
329
+ * @param chunk - The next bytes of the tar stream.
330
+ * @returns The files completed by these bytes.
331
+ * @throws When a header has a size that is not an octal number.
332
+ */
333
+ *push(chunk) {
334
+ let rest = chunk;
335
+ while (!this.ended) {
336
+ if (this.need === 0) {
337
+ yield* this.complete();
338
+ continue;
339
+ }
340
+ if (rest.length === 0) return;
341
+ const taken = rest.subarray(0, this.need);
342
+ if (this.state.kind !== "skip") this.collected.push(taken);
343
+ this.need -= taken.length;
344
+ rest = rest.subarray(taken.length);
345
+ }
346
+ }
347
+ /**
348
+ * Checks that the stream ended on an entry boundary.
349
+ * @throws When the stream ended inside a header, a long name or an entry.
350
+ */
351
+ finish() {
352
+ const onBoundary = this.state.kind === "header" && this.need === this.total;
353
+ if (!this.ended && !onBoundary) throw new Error(`Tar stream ended ${String(this.need)} bytes short of a ${String(this.total)}-byte ${STATE_LABELS[this.state.kind]}`);
354
+ }
355
+ *complete() {
356
+ const state = this.state;
357
+ const bytes = Buffer.concat(this.collected);
358
+ switch (state.kind) {
359
+ case "header":
360
+ this.readHeader(bytes);
361
+ break;
362
+ case "longName":
363
+ this.longName = field(bytes, {
364
+ offset: 0,
365
+ length: bytes.length
366
+ });
367
+ this.begin({ kind: "skip" }, state.padding);
368
+ break;
369
+ case "data":
370
+ this.begin({
371
+ kind: "skip",
372
+ file: {
373
+ name: state.name,
374
+ data: bytes
375
+ }
376
+ }, state.padding);
377
+ break;
378
+ case "skip":
379
+ this.begin({ kind: "header" }, TAR_BLOCK_BYTES);
380
+ if (state.file !== void 0) yield state.file;
381
+ }
382
+ }
383
+ readHeader(header) {
384
+ if (header.every((byte) => byte === 0)) {
385
+ this.ended = true;
386
+ return;
387
+ }
388
+ const size = Number.parseInt(field(header, SIZE_FIELD).trim() || "0", OCTAL_RADIX);
389
+ if (!Number.isFinite(size)) throw new Error("Tar header has a size that is not an octal number");
390
+ const padding = (TAR_BLOCK_BYTES - size % TAR_BLOCK_BYTES) % TAR_BLOCK_BYTES;
391
+ const type = String.fromCharCode(header[TYPE_FIELD_OFFSET] ?? 0);
392
+ if (type === "L") {
393
+ this.begin({
394
+ kind: "longName",
395
+ padding
396
+ }, size);
397
+ return;
398
+ }
399
+ const prefix = field(header, PREFIX_FIELD);
400
+ const name = this.longName ?? (prefix === "" ? field(header, NAME_FIELD) : `${prefix}/${field(header, NAME_FIELD)}`);
401
+ this.longName = void 0;
402
+ if ((type === "0" || type === "\0") && this.wanted(name)) this.begin({
403
+ kind: "data",
404
+ name,
405
+ padding
406
+ }, size);
407
+ else this.begin({ kind: "skip" }, size + padding);
408
+ }
409
+ };
410
+ function field(header, spec) {
411
+ const raw = header.subarray(spec.offset, spec.offset + spec.length);
412
+ const end = raw.indexOf(0);
413
+ return raw.subarray(0, end === -1 ? raw.length : end).toString("utf8");
414
+ }
415
+ /**
416
+ * Reads the regular files of a tar stream one at a time. Entries for which `wanted` is false are skipped without being buffered, which matters for archives that mix small files with very large ones.
417
+ * Entry data is held in memory, so one entry must fit in memory. GNU long names (`L`) are honoured; other extension headers (`K`, pax `x` and `g`) are skipped, and directories, links and devices are ignored.
418
+ * @param source - The tar bytes, for example a file or standard input stream.
419
+ * @param wanted - Chooses by name which entries are read.
420
+ * @returns A generator of each wanted regular file.
421
+ * @throws When the stream ends inside a header or an entry.
422
+ */
423
+ async function* readTarFiles(source, wanted) {
424
+ const parser = new TarParser(wanted);
425
+ for await (const chunk of source) {
426
+ yield* parser.push(Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength));
427
+ if (parser.isEnded) return;
428
+ }
429
+ parser.finish();
430
+ }
431
+ // src/node/adsb-archive.ts
432
+ /** First two bytes of a gzip stream. `readsb` history files carry a `.json` name but are usually gzip-compressed. */
433
+ const GZIP_MAGIC = [31, 139];
434
+ /** Matches the per-aircraft trace files of an `adsb.lol` `globe_history` archive, for example `./traces/3a/trace_full_3a1b2c.json`. */
435
+ const TRACE_FILE = /(^|\/)trace_full_[0-9a-f~]+\.json$/;
436
+ /**
437
+ * Reads flight segments from a tar stream of `adsb.lol` `globe_history` traces, one aircraft at a time so memory holds a single trace and the segments kept.
438
+ * A day is published as `.tar.aa` and `.tar.ab` parts that concatenate to one tar, so the two parts can be piped in one after the other.
439
+ * @param source - The tar bytes.
440
+ * @param options - Region filter and compression tolerance.
441
+ * @returns Segments from every trace file in the archive.
442
+ * @throws When a trace file is not valid JSON of the trace shape, naming the file.
443
+ */
444
+ async function readAdsbArchiveSegments(source, options = {}) {
445
+ const segments = [];
446
+ for await (const file of readTarFiles(source, (name) => TRACE_FILE.test(name))) {
447
+ const text = (GZIP_MAGIC.every((byte, index) => file.data[index] === byte) ? gunzipSync(file.data) : file.data).toString("utf8");
448
+ let found;
449
+ try {
450
+ found = segmentsFromAdsbJson(JSON.parse(text), options);
451
+ } catch (error) {
452
+ throw new Error(`${file.name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
453
+ }
454
+ segments.push(...found);
455
+ }
456
+ return segments;
457
+ }
458
+ // src/node/wav-file.ts
459
+ /**
460
+ * Reads and decodes a WAV audio file from disk into mono `Float32Array` samples and sample rate.
461
+ * @param filePath - Path to `.wav` file.
462
+ * @returns Decoded mono audio samples and sample rate in Hz.
463
+ */
464
+ function readWavFile(filePath) {
465
+ const bytes = new Uint8Array(readFileSync(filePath));
466
+ return decodeWavBytes(bytes);
467
+ }
468
+ /**
469
+ * Encodes and writes a mono `Float32Array` audio buffer to disk as a 16-bit PCM WAV file.
470
+ * @param filePath - Output path.
471
+ * @param samples - Audio samples in range `[-1, 1]`.
472
+ * @param sampleRateHz - Audio sample rate in Hz.
473
+ */
474
+ function writeWavFile(filePath, samples, sampleRateHz) {
475
+ const bytes = encodeWavPcm16(samples, sampleRateHz);
476
+ writeFileSync(filePath, bytes);
477
+ }
478
+ export { AdsbPingSchema, AircraftCategorySchema, AsymptoticKinematicsSchema, BUNDLED_CONSTELLATIONS, BUNDLED_DATASET, BUNDLED_DEM_GRID, BUNDLED_GROUND_TRUTH, BUNDLED_SEGMENTS, ConstellationEntrySchema, DEFAULT_ELLIPSE_SEGMENTS, DEFAULT_SOUND_SPEED_MPS, DEFAULT_SPATIAL_RESOLUTION, DopplerDatasetSchema, DopplerObservationSchema, DopplerRidgePointSchema, DopplerSessionEngine, EnfSignatureSchema, FULL_TURN_DEG, GeoPoint3DSchema, HALF_TURN_DEG, HypothesisMatchSchema, LinearFlightSegmentSchema, ListeningEpisodeSchema, LocationHypothesisSchema, LocationSessionSchema, MAX_ALONG_TRACK_WIND_MPS, QUERY_BUCKET_SPAN, QuietIntervalSchema, RawAdsbTraceFileSchema, SLANT_SIGMA_FLOOR_METERS, SearchConstraintsSchema, Vector3DSchema, buildConstellationIndex, classifyAircraftCategory, computeDopplerSpectrogram, computeHannPowerSpectrumDb, computeSegmentCpa, computeSingleSpectrogramFrame, decimateAudioStream, decodeSpatialCellCenter, decodeWavBytes, define, designLowPassFirKernel, detectEnfSignature, downmixToMono, effectiveRaySoundSpeedMps, ellipseOutline, encodeSpatialCell, encodeWavPcm16, estimateTempFromCricketChirpsCelsius, expandConstellationQueryKeys, extractAsymptoticKinematics, extractDopplerObservations, fft, fisherToConfidenceEllipse95, formatConstellationKey, generateBundledReferenceAudio, generateBundledReferenceWavBytes, geoToEcef, geoToLocalEnu, getOwn, interpolateTerrainElevationMeters, loadDatasetFile, localEnuToGeo, matchesSearchConstraints, processRawAdsbTrace, readAdsbArchiveSegments, readAdsbSegments, readTarFiles, readWavFile, recordOf, refineObserverCoordinateGaussNewton, saveDatasetBundle, scoreByEmissionDechirping, segmentsFromAdsbJson, simplifyTrajectory4D, slantDistanceMeters, solveRetardedDelaySec, speedOfSoundAtTempMps, synthesizeMultiFlyoverAudio, synthesizeRetardedDopplerCurve, unmatchedObservationLogPenalty, writeWavFile };