@squawk
    Preparing search index...

    Module @squawk/adsb-feed

    squawk logo  @squawk/adsb-feed

    MIT License npm TypeScript

    Live ADS-B aircraft feed from a local dump1090-fa station, normalized into the shared Aircraft type and emitted as aircraft:new / aircraft:update / aircraft:lost events. Three sources are provided against the same station: polling its aircraft.json HTTP output, a persistent connection to its SBS/BaseStation output, or a persistent connection to its raw Beast binary output.

    Documentation

    Part of the @squawk aviation library suite. See all packages on npm.

    npm install @squawk/adsb-feed
    

    Polls dump1090-fa's aircraft.json endpoint on an interval. Works in both Node and the browser (subject to CORS - see below).

    import { createJsonAircraftFeed } from '@squawk/adsb-feed';

    const feed = createJsonAircraftFeed({
    url: 'http://192.168.1.50:8080/data/aircraft.json',
    });

    feed.addEventListener('aircraft:new', (event) => {
    console.log((event as CustomEvent).detail.aircraft);
    });
    feed.addEventListener('aircraft:lost', (event) => {
    console.log('lost', (event as CustomEvent).detail.icaoHex);
    });

    feed.start();
    // later: feed.stop();

    Connects to dump1090-fa's SBS/BaseStation TCP output. Lower latency than polling - a new line arrives the instant dump1090-fa decodes a message, rather than waiting for the next snapshot. Node-only; reconnects automatically if the connection drops.

    import { createSbsAircraftFeed } from '@squawk/adsb-feed';

    const feed = createSbsAircraftFeed({ host: '192.168.1.50' }); // port defaults to 30003

    feed.addEventListener('aircraft:update', (event) => {
    console.log((event as CustomEvent).detail.aircraft);
    });

    feed.start();

    Connects to dump1090-fa's Beast binary output (default port 30005) and decodes the raw Mode-S/ADS-B messages itself via @squawk/beast/@squawk/mode-s - unlike the JSON and SBS sources, dump1090-fa has not done any decode work on this output. Node-only; reconnects automatically if the connection drops.

    import { createBeastAircraftFeed } from '@squawk/adsb-feed';

    const feed = createBeastAircraftFeed({ host: '192.168.1.50' }); // port defaults to 30005

    feed.addEventListener('aircraft:update', (event) => {
    console.log((event as CustomEvent).detail.aircraft);
    });

    feed.start();

    Beast frames carry raw CPR-encoded positions rather than decoded coordinates. An airborne position resolves on its own once a paired even/odd frame has arrived (typically within a couple of seconds), but on-ground/surface position messages can only be decoded against a known-nearby reference position - there is no pair-only path for surface CPR. Pass receiverPosition (your station's own lat/lon) to enable surface position decoding and to speed up a new aircraft's first airborne fix; without it, surface aircraft are still tracked (squawk, callsign, ground speed, etc.) but carry no position:

    const feed = createBeastAircraftFeed({
    host: '192.168.1.50',
    receiverPosition: { lat: 40.6413, lon: -73.7781 },
    });

    DF0/4/5/16/20/21 replies (ACAS/TCAS and Mode-S surveillance replies) carry an ICAO address recovered from a CRC-XOR rather than a direct field, so this source only accepts them for aircraft already known from a squitter - an unmatched candidate address is dropped rather than risking a phantom or misattributed aircraft. Mode A/C replies are also dropped; they carry no ICAO address at all.

    All three factories return the same AircraftFeed shape, so switching sources for a given consumer is a one-line change - getAircraft, getAllAircraft, getPositionHistory, and the event names are identical either way.

    When the source comes from a CLI flag or a config value, createAircraftFeedForSource dispatches to the matching factory so there is one call site instead of a switch. Node-only, since the SBS and Beast sources are.

    import { createAircraftFeedForSource } from '@squawk/adsb-feed';
    import type { FeedSource } from '@squawk/adsb-feed';

    const source: FeedSource = 'beast'; // or 'json' / 'sbs', e.g. from a --source flag

    const feed = createAircraftFeedForSource({
    source,
    host: '192.168.1.50',
    receiverPosition: { lat: 40.6413, lon: -73.7781 },
    });

    feed.start();

    port defaults per source from the exported DEFAULT_PORT_BY_SOURCE (8080 json, 30003 sbs, 30005 beast), matching dump1090-fa's own defaults. For the json source the endpoint is dump1090-fa's standard http://<host>:<port>/data/aircraft.json, or url when given. Options that do not apply to the selected source (receiverPosition for anything but beast, url for anything but json, and so on) are ignored, so the same options object works whichever source is chosen.

    Import createJsonAircraftFeed from the /browser subpath. createSbsAircraftFeed, createBeastAircraftFeed, and createAircraftFeedForSource depend on Node's net module (raw TCP sockets have no browser API) and are not exported there.

    import { createJsonAircraftFeed } from '@squawk/adsb-feed/browser';
    

    dump1090-fa does not send CORS headers, so a browser fetching aircraft.json directly from another origin is blocked. Point url at a same-origin reverse proxy that forwards to your station - the same pattern @squawk/weather's /fetch layer documents for the Aviation Weather Center API.

    • createJsonAircraftFeed({ url, pollIntervalMs?, fetch?, staleAfterMs?, sweepIntervalMs?, positionHistoryRetention? }) - creates a feed backed by HTTP-polled aircraft.json.
    • createSbsAircraftFeed({ host, port?, reconnectDelayMs?, staleAfterMs?, sweepIntervalMs?, positionHistoryRetention? }) - creates a feed backed by a persistent SBS/BaseStation socket connection. Node-only.
    • createBeastAircraftFeed({ host, port?, reconnectDelayMs?, receiverPosition?, staleAfterMs?, sweepIntervalMs?, positionHistoryRetention? }) - creates a feed backed by a persistent Beast binary socket connection, decoding raw Mode-S/ADS-B messages itself. Node-only.
    • createAircraftFeedForSource({ source, host, port?, url?, pollIntervalMs?, fetch?, reconnectDelayMs?, receiverPosition?, staleAfterMs?, sweepIntervalMs?, positionHistoryRetention? }) - creates a feed for a FeedSource ('json' | 'sbs' | 'beast') chosen at runtime, dispatching to one of the three factories above. Node-only.
    • DEFAULT_PORT_BY_SOURCE - dump1090-fa's default port for each FeedSource.
    • isEmergencyAircraft(aircraft) - whether an Aircraft is in an emergency: an emergency squawk, a declared emergency state, or an active ACAS/TCAS Resolution Advisory. Any one is sufficient, since which of them a feed can see depends on its source (see Field population by source). Built from isEmergencySquawk(squawk) (7500, 7600, or 7700 - the EMERGENCY_SQUAWKS set) and isDeclaredEmergencyState(emergencyState) (any EmergencyState other than 'none' and 'reserved'), which are exported too. All are pure, and available from the browser entry point as well.

    All factories accept staleAfterMs (how long an aircraft may go without an update before aircraft:lost, default 60000) and sweepIntervalMs (how often the staleness sweep runs, default 1000). An aircraft is dropped on the first sweep after its window elapses, so aircraft:lost can fire up to one sweep interval late; the one-second default keeps that negligible, and the sweep is a single pass over the tracked aircraft, so it costs next to nothing. Lengthen it only if you would rather sweep less often.

    • feed.start() / feed.stop() - begin or end polling/connecting. stop() clears all tracked state.
    • feed.getAircraft(icaoHex) / feed.getAllAircraft() - current normalized Aircraft state.
    • feed.getPositionHistory(icaoHex) - retained position samples for one aircraft, oldest first.
    • feed.getConnectionState() - current connection state, 'connected' | 'reconnecting'.
    • Events (via addEventListener, read off CustomEvent.detail): aircraft:new and aircraft:update ({ aircraft: Aircraft }), aircraft:lost ({ icaoHex: string, lastAircraft: Aircraft }), connection:connect and connection:disconnect ({ state: 'connected' | 'reconnecting' }).

    All three sources expose the same connection lifecycle. For SBS and Beast, getConnectionState() and the connection:connect/connection:disconnect events reflect the underlying TCP socket's own connect/close lifecycle. The JSON source has no persistent connection to track, so it uses the most recent poll's outcome instead: 'connected' once a poll successfully parses, 'reconnecting' after a non-ok response or a network/parse failure.

    feed.addEventListener('connection:disconnect', () => {
    console.log('lost connection, reconnecting...');
    });
    console.log(feed.getConnectionState()); // 'connected' | 'reconnecting'

    Beyond the baseline fields all three sources populate, coverage differs:

    • JSON additionally populates category (from aircraft.json's category code), emergencyState (from its emergency field), and targetState (from its nav_* fields: nav_altitude_mcp or nav_altitude_fms for the selected altitude, preferring the MCP/FCU value when both are present, nav_heading, nav_qnh, and nav_modes for the autopilot modes and TCAS status, with the accuracy and integrity fields read from the entry's nac_p, nic_baro, and sil). The mode flags are undefined when nav_modes is absent, which dump1090-fa writes when the aircraft reports no mode status; tcasOperational is then false, since dump1090-fa only reports TCAS alongside the other modes. It has no equivalent for identActive/squawkAlert/resolutionAdvisory - aircraft.json carries no such fields.
    • SBS additionally populates identActive/squawkAlert (from the BaseStation SPI/Alert fields). It has no equivalent for category, emergencyState, resolutionAdvisory, or targetState - the BaseStation format carries none of them.
    • Beast populates all of the above - category (from type-code 1-4 identification messages), emergencyState, identActive and squawkAlert (from airborne position messages, which carry them twice a second, and from Mode-S surveillance replies), resolutionAdvisory (from either a DF16 reply or a type-code-28 subtype-2 broadcast), and targetState (from a type-code-29 message) - since it decodes the raw Mode-S/ADS-B messages itself rather than relying on dump1090-fa's own JSON/SBS summaries.

    Aircraft.origin / Aircraft.destination are never populated by any source - ADS-B data carries no flight-schedule information, so resolving a live aircraft's actual origin or destination needs a data source outside this package.

    browser