Repository navigation
Releases
v1.2.0
Every answer is now checked against the traced curves. When nothing fits, score says so and lists the likely causes. This release also fixes clock and pairing bugs and makes the fit's error bars honest. The six-seed self-test gives the same errors and verdicts as v1.1.1.
Fixed
- Scoring dropped candidates the search had admitted. The search accepts an event-1 match up to the clock tolerance plus the event's CPA-time tolerance
t_tolaway. Scoring could shift time by only ±4 s and used its own clock tolerance (default 5 s), so candidates further out were dropped without a word. The time shift now spans at leastt_tol(capped at 30 s), andscoretakes the search's clock tolerance when--rec-start-tolis not given. Candidates rejected by the clock are counted, and an empty result explains itself. - Offset pairing lost the time term with large node ids. Node and time were packed into one float. With global grid ids (around 2.5·10¹⁴), the time difference rounded away, so pairs an hour off still matched. The key is now an exact integer (node rank × slot + milliseconds). It is checked against brute force for compact and global ids. Normal
searchruns used compact ids and were not affected. - Fit sigmas were too small for correlated residuals. The covariance is now inflated by the residuals' measured correlation time, max(win/hop, τ). In the self-test scenarios τ is 3–38 points, so sigmas grow by up to 2.7×. Straight synthetic passes are unchanged. The fit reports
resid_corr_time,sigma_inflation,n_effandcorr_v_d. detectstdout and its file looked like different schemas. Stdout now also namesevents_file. The docs say thatboxes.jsonis the event list and that the status is inboxes.diag.json.
New
- Trace check. Each hypothesis' model curve is compared with the traced points within ±60 s of each CPA, using a robust RMS against the event's own linear fit. It passes at ≤ max(4×, +1 Hz). Bright texture under a wrong curve (speech, music, wind) can lift a brightness score, but it cannot make the model reproduce the trace. With two events, failing hypotheses rank below the rest, and alternatives and margins are computed among those that pass.
- Verdict keywords, in order of precedence:
NO CONSISTENT SITE(two events, nothing passes;diagnosislists the causes);STRIP ONLY(tracks cross at < 25°);DEGENERATE(one event, three or more other aircraft within 2 %);AMBIGUOUS;unique within scored set.
result.jsongainsn_consistent,scoring(scored,rejected_clock,no_track,delta_max_s) anddiagnosis.best.trace_checkgives per-eventrms_hz,ref_hz,ratioandok. An f0 scale at its limit is flagged but not penalised.- Search stats report the following:
crossing_angle_deg(a log line says when every pair crosses at < 25°);flight_pairs(how many different aircraft explain one event);ground_elev_m;clock.
tracereportsbox_coverage. Below 50 % it adds a hint (--no-monotonefor a curve that rises before it falls, or a narrower box).fitaddsd_fit_min/d_fit_max(3 sigma) and a note when the search's distance window is much wider.
Docs (English and Chinese)
- One event needs a small area as well as a clock.
- The silence filter does little in sparse airspace.
- Sigma is statistical only: a speed change biases v by far more than sigma.
- What the distance window is.
- The DEM matters in relief.
- A new verification section covers what to do when no site is consistent: different places or a wrong file offset, an aircraft missing from ADS-B, a site outside the bbox, or a bad trace.
Measured on synthetic data
- Six self-test seeds give the same errors, verdicts and regions as v1.1.1.
- True two-event answers measure 0.3–1.8× the reference per event.
- Wrong aircraft pairs exceed 15× on at least one event. The true pair at a site 2–6 km away reaches 2.7–15×, so the check rules out wrong aircraft, not every wrong site; the brightness score still ranks those below the true site.
- With one event, wrong aircraft 30 km away can pass (1.3×). The check is reported there but does not reorder.
- Event 2 shifted by +300 s gives
NO CONSISTENT SITEwith a diagnosis. - One event with a 2 s clock and
t_tol= 20 s: all 492 candidates are scored, and the error is 23 m. - Real recordings were not part of these tests.
Downloads
| File | Language |
|---|---|
audio-flyover-geolocation-en.skill |
English |
audio-flyover-geolocation-zh-CN.skill |
简体中文 |
Reinstall over v1.1.1 the same way you installed it.
v1.1.1
Fixes for merged fly-overs, rising tones and very short files. The six-seed self-test reproduces the v1.1.0 numbers exactly.
Fixed
- Separate fly-overs were merged into one box. Overlapping 60 s windows chained neighbouring hits, so two passes a minute or two apart came out as one long candidate. Each group is now split again where its ridge loses support for more than 10 s of usable audio, and at turning points where the ridge rises again (one fly-over only falls in frequency). The flat approach and departure stay with their pass. Groups holding a single pass keep the box they had before.
- A rising test tone counted as a fly-over. Every event now has a frequency trend (
descending,flat) anddrop_pct. Ridges that rise are not returned;boxes.diag.jsonlists them underrejected. Because a strong tone hides what lies under it, its ridge is masked and the stretch is searched again, so a pass beneath it is still reported.auditcounts only falling ridges: a rising test tone is no longermedium, and a pass hidden under a tone makes the resultundeterminedrather thanlow. - Very short WAVs crashed
audit. Clips with 1–4 spectrogram frames (about 1–1.8 s) failed in transient gating, and files shorter than the 1 s analysis window raised an error.detectnow reportsinsufficientfor them.auditreturnsundetermined, with no PNG when there is nothing to draw, andspectrogramexits with code 2 and a message.
Changed
- Event fields
trend,drop_pct,warnings(andnotefor passes found under a tone).warningsflags a box whose ridge rises again or falls by more than 45 %: one propeller pass falls by 2v/(c+v), which is 36 % at 75 m/s. Such a box probably joins two aircraft or two harmonics, andauditcounts it as two. boxes.diag.jsongainsrejected. Theauditoutput gainsnotesandpng.detectlogs each event's trend and every set-aside ridge.
Measured on synthetic data
- Two passes 90–180 s apart: two events in 12 of 12 cases (v1.1.0: 4 of 12, only at 180 s).
- Passes 40–60 s apart that are audible at the same time often stay one box (6 of 8), but each of those boxes carries a warning.
- Single passes from 150 m to 2 km (144 clips) and 72 noise-only clips: the same detections as v1.1.0, with no warnings and no set-aside ridges.
- Real wind and traffic noise were not part of these tests.
Downloads
| File | Language |
|---|---|
audio-flyover-geolocation-en.skill |
English |
audio-flyover-geolocation-zh-CN.skill |
简体中文 |
Reinstall over v1.1.0 the same way you installed it.
v1.1.0
Fixes for short recordings and the privacy audit. Results on long recordings are unchanged: the six-seed self-test reproduces the v1.0.0 numbers exactly.
Fixed
- Short clips were never detected. With fewer than six usable 60 s windows (clips under about two minutes),
detectcompared each window with a median of itself and could not flag anything, however clear the curve. Such clips, and recordings that are tonal almost throughout, are now compared with a calibrated noise model: the same pipeline run on synthetic Gaussian noise for the same window length. Whitening makes that level independent of the noise's loudness and colour; it matched the background of all six self-test recordings within ±0.06 dB. auditreportedlowwhen it could not judge. An empty detection no longer means low risk. Insufficient evidence now givesundetermined, a noise-model check giveslow, limited check, and the output includes the detection status, its reasons and what alowrating covers.- Harmonic stacking was silently off in
detect. Over 40–400 Hz under a 600 Hz spectrogram only one harmonic fits for every candidate, so--harmonics 3acted as 1. The count used is now reported bydetectandtrace,detectdefaults to the honest value of 1, and a band above the spectrogram top keeps the fundamental instead of summing to zero. Per-candidate harmonic counts were tried and made both detection and tracing worse in the self-tests, so they were not adopted. - The last seconds of a recording were never examined. The final detection window now ends at the end of the recording.
Changed
detectwrites diagnostics to<out>.diag.json(statusok / limited / insufficient,reasons, baseline, harmonics used, hits shorter than--min-dur).boxes.jsonkeeps its format.- New
detectoptions:--window,--step,--null-db. - New regression tests (
tests/test_detection.py), run in CI.
Measured on synthetic data
- 73 noise-only clips (12–300 s, with mains hum and speech bursts): no false events.
- Passes within 1.5 km in 20–69 s clips: 48 of 48 detected; at 2 km, about half.
- Real wind and traffic noise were not part of these tests.
Downloads
| File | Language |
|---|---|
audio-flyover-geolocation-en.skill |
English |
audio-flyover-geolocation-zh-CN.skill |
简体中文 |
Reinstall over v1.0.0 the same way you installed it.
v1.0.0
First release of the audio fly-over geolocation skill: estimate where a recording was made from the Doppler curves of aircraft audible in the background, matched against ADS-B track history.
Downloads
| File | Language |
|---|---|
audio-flyover-geolocation-en.skill |
English |
audio-flyover-geolocation-zh-CN.skill |
简体中文 |
Both packages share the same tested Python pipeline (scripts/); only the instructions and references are translated.
Claude Code: unzip into ~/.claude/skills/ (or copy skills/<lang>/audio-flyover-geolocation/ from this repo), then pip install numpy scipy matplotlib.
Claude apps: upload the .skill file in the Skills section of settings, with code execution enabled.
What's in it
- Spectrogram → harmonic ridge tracing → Doppler fit (closest-approach time, speed, miss distance) with a split fit and clock-offset mask
- ADS-B ingest for OpenSky / ADS-B Exchange / CSV exports, corridor → CPA → timing → silence search funnel
- Joint scoring of two or more fly-overs with a shared clock offset, hill-climb refinement, a 0.9 confidence region and plateau / mirror / geometry warnings
- Blind synthetic self-test (
afg_cli.py selftest) and a report template
Self-test results (blind, synthetic)
| Seed | Two events | ±2 s, turning pass | ±2 s, straight pass |
|---|---|---|---|
| 7 | 24 m | 23 m | mirror (flagged) |
| 11 | 477 m (3° crossing, inside 849 m region) | 35 m | mirror (flagged) |
| 23 | 46 m | 79 m | 162 m |
| 42 | 31 m | 86 m | 206 m |
| 101 | 53 m | 48 m | 213 m |
| 2026 | 25 m | 96 m | 6 m |
With ±30 s clock uncertainty and one pass, the result is a 2.1–4.8 km strip that contains the truth in every seed.
Real recordings are harder than synthetic ones: wind, reflections, engine-tone drift and incomplete ADS-B coverage all widen the region. See the README's limitations and responsible-use sections before relying on a result.