feat(export): AIS UDP NMEA forward and JSON export endpoints for AIS/ADS-B

AIS:
- New optional NMEA UDP forwarding via AIS-catcher's -u flag, configurable
  from the AIS sidebar (host + port). Lets OpenCPN and other NMEA tools
  receive live vessel data directly. All SDR builders updated.
- New GET /ais/vessels endpoint — clean JSON snapshot of tracked vessels
  for REST integration

ADS-B:
- New GET /adsb/aircraft endpoint — JSON snapshot of all tracked aircraft,
  with optional ?icao= and ?military=true filters. Response includes a
  reminder that port 30003 (SBS) is already available for tools like
  Virtual Radar Server and OpenCPN's AIS/target plugin.

Closes #90
This commit is contained in:
James Smith
2026-04-05 16:20:10 +01:00
parent 592e97719b
commit 0210791c69
9 changed files with 138 additions and 8 deletions
+6 -1
View File
@@ -161,7 +161,9 @@ class AirspyCommandBuilder(CommandBuilder):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS-catcher command for AIS vessel tracking with Airspy.
@@ -184,6 +186,9 @@ class AirspyCommandBuilder(CommandBuilder):
if bias_t:
cmd.extend(['-gr', 'biastee', '1'])
if udp_host and udp_port:
cmd.extend(['-u', udp_host, str(udp_port)])
return cmd
def build_iq_capture_command(
+5 -1
View File
@@ -165,7 +165,9 @@ class CommandBuilder(ABC):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS decoder command for vessel tracking.
@@ -175,6 +177,8 @@ class CommandBuilder(ABC):
gain: Gain in dB (None for auto)
bias_t: Enable bias-T power (for active antennas)
tcp_port: TCP port for JSON output server
udp_host: Optional host to forward NMEA 0183 sentences via UDP
udp_port: UDP port for NMEA forwarding (required if udp_host set)
Returns:
Command as list of strings for subprocess
+6 -1
View File
@@ -161,7 +161,9 @@ class HackRFCommandBuilder(CommandBuilder):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS-catcher command for AIS vessel tracking with HackRF.
@@ -184,6 +186,9 @@ class HackRFCommandBuilder(CommandBuilder):
if bias_t:
cmd.extend(['-gr', 'biastee', '1'])
if udp_host and udp_port:
cmd.extend(['-u', udp_host, str(udp_port)])
return cmd
def build_iq_capture_command(
+6 -1
View File
@@ -140,7 +140,9 @@ class LimeSDRCommandBuilder(CommandBuilder):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS-catcher command for AIS vessel tracking with LimeSDR.
@@ -161,6 +163,9 @@ class LimeSDRCommandBuilder(CommandBuilder):
if gain is not None and gain > 0:
cmd.extend(['-gr', 'tuner', str(int(gain))])
if udp_host and udp_port:
cmd.extend(['-u', udp_host, str(udp_port)])
return cmd
def build_iq_capture_command(
+6 -1
View File
@@ -281,7 +281,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS-catcher command for AIS vessel tracking.
@@ -308,6 +310,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
if bias_t:
cmd.extend(['-gr', 'BIASTEE', 'on'])
if udp_host and udp_port:
cmd.extend(['-u', udp_host, str(udp_port)])
return cmd
def build_iq_capture_command(
+6 -1
View File
@@ -139,7 +139,9 @@ class SDRPlayCommandBuilder(CommandBuilder):
device: SDRDevice,
gain: float | None = None,
bias_t: bool = False,
tcp_port: int = 10110
tcp_port: int = 10110,
udp_host: str | None = None,
udp_port: int | None = None,
) -> list[str]:
"""
Build AIS-catcher command for AIS vessel tracking with SDRPlay.
@@ -162,6 +164,9 @@ class SDRPlayCommandBuilder(CommandBuilder):
if bias_t:
cmd.extend(['-gr', 'biastee', '1'])
if udp_host and udp_port:
cmd.extend(['-u', udp_host, str(udp_port)])
return cmd
def build_iq_capture_command(