From a38098e3b754bb9ab9c50632175faf198a71e99f Mon Sep 17 00:00:00 2001 From: ztimson Date: Thu, 25 Jun 2026 17:11:18 -0400 Subject: [PATCH] ADSB updates --- server/data/aircraft.db | Bin 73048064 -> 73048064 bytes server/src/adsb.mjs | 81 ++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/server/data/aircraft.db b/server/data/aircraft.db index c817ec7ef0b9d0e5730f439faf0c731deec4d338..d050ed65c857a7adbdd297bf223320cf56012a3f 100644 GIT binary patch delta 5771 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 diff --git a/server/src/adsb.mjs b/server/src/adsb.mjs index 9376234..b0bcca7 100644 --- a/server/src/adsb.mjs +++ b/server/src/adsb.mjs @@ -13,56 +13,72 @@ const SHAPES_PATH = resolve(DATA, 'shapes.json'); const history = new Map(); const MAX_HISTORY = 500; -const CSV_URL = 'https://s3.opensky-network.org/data-samples/metadata/aircraft-database-complete-2024-06.csv'; -const MIL_RANGES_URL = ':8080/db-3.14.1707/ranges.js'; +const CSV_URL = 'https://s3.opensky-network.org/data-samples/metadata/aircraft-database-complete-2024-06.csv'; +const MIL_RANGES_URL = ':8080/db-3.14.1708/ranges.js'; const MILITARY_OPERATORS = ['air force', 'army', 'navy', 'marine', 'coast guard', 'military', 'defence', 'defense', 'luftwaffe', 'RAF', 'USAF', 'USN', 'USMC']; const CARGO_OPERATORS = ['fedex', 'ups', 'dhl', 'cargo', 'freight', 'logistic', 'atlas air', 'kalitta', 'air freight']; const PASSENGER_OPERATORS = ['airlines', 'airways', 'air ', 'jet', 'easyjet', 'ryanair', 'delta', 'united', 'american', 'southwest', 'lufthansa', 'emirates', 'british']; const MILITARY_CLASSES = ['H1M', 'H2M', 'L1M', 'L2M', 'L4M', 'A0']; +const milRanges = []; let db; +async function syncMilitaryRanges() { + const { ADSB_URL } = cfg(); + try { + const res = await fetch(ADSB_URL + MIL_RANGES_URL); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const { military } = await res.json(); + + db.exec('DELETE FROM military_ranges'); + const insert = db.prepare('INSERT INTO military_ranges (start, end) VALUES (?, ?)'); + const insertMany = db.transaction(ranges => { + for (const [s, e] of ranges) insert.run(s.toUpperCase(), e.toUpperCase()); + }); + + insertMany(military); + milRanges.length = 0; + milRanges.push(...military.map(([s, e]) => [s.toUpperCase(), e.toUpperCase()])); + } catch (e) { + const rows = db.prepare('SELECT start, end FROM military_ranges').all(); + milRanges.length = 0; + milRanges.push(...rows.map(r => [r.start, r.end])); + } +} + export async function initAircraftDb() { if (!fs.existsSync(DATA)) fs.mkdirSync(DATA, { recursive: true }); const missing = !fs.existsSync(DB_PATH); - if(missing) console.log(`✈️ Building database`); + if (missing) console.log(`✈️ Building database`); db = new Database(DB_PATH); db.exec(` CREATE TABLE IF NOT EXISTS aircraft ( - icao24 TEXT PRIMARY KEY, - built TEXT, - categoryDescription TEXT, - country TEXT, - engines TEXT, - class TEXT, - manufacturer TEXT, - model TEXT, - modes TEXT, - operator TEXT, - operatorCallsign TEXT, - owner TEXT, - registration TEXT, - serialNumber TEXT, - aircraft TEXT + icao24 TEXT PRIMARY KEY, + built TEXT, + categoryDescription TEXT, + country TEXT, + engines TEXT, + class TEXT, + manufacturer TEXT, + model TEXT, + modes TEXT, + operator TEXT, + operatorCallsign TEXT, + owner TEXT, + registration TEXT, + serialNumber TEXT, + aircraft TEXT ); CREATE TABLE IF NOT EXISTS military_ranges ( - start TEXT NOT NULL, - end TEXT NOT NULL + start TEXT NOT NULL, + end TEXT NOT NULL ); - `); + `); - // Always refresh military ranges on startup - const { ADSB_URL } = cfg(); - const res = await fetch(ADSB_URL + MIL_RANGES_URL); - if (!res.ok) throw new Error(`Failed to fetch military ranges: HTTP ${res.status}`); - const { military } = await res.json(); - db.exec('DELETE FROM military_ranges'); - const insertRange = db.prepare('INSERT INTO military_ranges (start, end) VALUES (?, ?)'); - const insertRanges = db.transaction(ranges => { for (const [s, e] of ranges) insertRange.run(s.toUpperCase(), e.toUpperCase()); }); - insertRanges(military); + await syncMilitaryRanges(); if (missing) { const res = await fetch(CSV_URL); @@ -77,7 +93,7 @@ export async function initAircraftDb() { @class, @manufacturer, @model, @modes, @operator, @operatorCallsign, @owner, @registration, @serialNumber, @aircraft ) - `); + `); const insertMany = db.transaction(rows => { for (const row of rows) insert.run(row); @@ -112,8 +128,7 @@ export async function initAircraftDb() { function isIcaoInMilitaryRange(icao) { if (!icao) return false; const hex = icao.toUpperCase(); - const match = db.prepare('SELECT 1 FROM military_ranges WHERE start <= ? AND end >= ? LIMIT 1').get(hex, hex); - return !!match; + return milRanges.some(([s, e]) => hex >= s && hex <= e); } function wordMatch(text, keyword) {