mirror of
https://github.com/smittix/intercept.git
synced 2026-07-20 15:28:11 -07:00
feat(acars): add message translator and ADS-B datalink integration
Add ACARS label translation, message classification, and field parsers so decoded messages show human-readable descriptions instead of raw label codes (H1, DF, _d, 5Z, etc.). Integrate translated ACARS messages into the ADS-B aircraft detail panel and add a live message feed to the standalone ACARS mode. - New utils/acars_translator.py with ~50 label codes, type classifier, and parsers for position reports, engine data, weather, and OOOI - Enrich messages at ingest in routes/acars.py with translation fields - Backfill translation in /adsb/aircraft/<icao>/messages endpoint - ADS-B dashboard: DATALINK MESSAGES section in aircraft detail panel with auto-refresh, color-coded type badges, and parsed field display - Standalone ACARS mode: scrollable live message feed (max 30 cards) - Fix default N. America ACARS frequencies to 131.550/130.025/129.125 - Unit tests covering all translator functions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+35
-24
@@ -21,7 +21,7 @@ import app as app_module
|
||||
from utils.logging import sensor_logger as logger
|
||||
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
||||
from utils.sdr import SDRFactory, SDRType
|
||||
from utils.sse import sse_stream_fanout
|
||||
from utils.sse import sse_stream_fanout
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.constants import (
|
||||
PROCESS_TERMINATE_TIMEOUT,
|
||||
@@ -33,10 +33,11 @@ from utils.process import register_process, unregister_process
|
||||
|
||||
acars_bp = Blueprint('acars', __name__, url_prefix='/acars')
|
||||
|
||||
# Default VHF ACARS frequencies (MHz) - common worldwide
|
||||
# Default VHF ACARS frequencies (MHz) - North America primary
|
||||
DEFAULT_ACARS_FREQUENCIES = [
|
||||
'131.725', # North America
|
||||
'131.825', # North America
|
||||
'131.550', # North America primary
|
||||
'130.025', # North America secondary
|
||||
'129.125', # North America tertiary
|
||||
]
|
||||
|
||||
# Message counter for statistics
|
||||
@@ -120,6 +121,16 @@ def stream_acars_output(process: subprocess.Popen, is_text_mode: bool = False) -
|
||||
data['type'] = 'acars'
|
||||
data['timestamp'] = datetime.utcnow().isoformat() + 'Z'
|
||||
|
||||
# Enrich with translated label and parsed fields
|
||||
try:
|
||||
from utils.acars_translator import translate_message
|
||||
translation = translate_message(data)
|
||||
data['label_description'] = translation['label_description']
|
||||
data['message_type'] = translation['message_type']
|
||||
data['parsed'] = translation['parsed']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Update stats
|
||||
acars_message_count += 1
|
||||
acars_last_message_time = time.time()
|
||||
@@ -411,25 +422,25 @@ def stop_acars() -> Response:
|
||||
return jsonify({'status': 'stopped'})
|
||||
|
||||
|
||||
@acars_bp.route('/stream')
|
||||
def stream_acars() -> Response:
|
||||
"""SSE stream for ACARS messages."""
|
||||
def _on_msg(msg: dict[str, Any]) -> None:
|
||||
process_event('acars', msg, msg.get('type'))
|
||||
|
||||
response = Response(
|
||||
sse_stream_fanout(
|
||||
source_queue=app_module.acars_queue,
|
||||
channel_key='acars',
|
||||
timeout=SSE_QUEUE_TIMEOUT,
|
||||
keepalive_interval=SSE_KEEPALIVE_INTERVAL,
|
||||
on_message=_on_msg,
|
||||
),
|
||||
mimetype='text/event-stream',
|
||||
)
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['X-Accel-Buffering'] = 'no'
|
||||
return response
|
||||
@acars_bp.route('/stream')
|
||||
def stream_acars() -> Response:
|
||||
"""SSE stream for ACARS messages."""
|
||||
def _on_msg(msg: dict[str, Any]) -> None:
|
||||
process_event('acars', msg, msg.get('type'))
|
||||
|
||||
response = Response(
|
||||
sse_stream_fanout(
|
||||
source_queue=app_module.acars_queue,
|
||||
channel_key='acars',
|
||||
timeout=SSE_QUEUE_TIMEOUT,
|
||||
keepalive_interval=SSE_KEEPALIVE_INTERVAL,
|
||||
on_message=_on_msg,
|
||||
),
|
||||
mimetype='text/event-stream',
|
||||
)
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['X-Accel-Buffering'] = 'no'
|
||||
return response
|
||||
|
||||
|
||||
@acars_bp.route('/frequencies')
|
||||
@@ -438,7 +449,7 @@ def get_frequencies() -> Response:
|
||||
return jsonify({
|
||||
'default': DEFAULT_ACARS_FREQUENCIES,
|
||||
'regions': {
|
||||
'north_america': ['131.725', '131.825'],
|
||||
'north_america': ['131.550', '130.025', '129.125'],
|
||||
'europe': ['131.525', '131.725', '131.550'],
|
||||
'asia_pacific': ['131.550', '131.450'],
|
||||
}
|
||||
|
||||
@@ -1189,4 +1189,17 @@ def get_aircraft_messages(icao: str):
|
||||
|
||||
from utils.flight_correlator import get_flight_correlator
|
||||
messages = get_flight_correlator().get_messages_for_aircraft(icao=icao.upper(), callsign=callsign)
|
||||
|
||||
# Backfill translation on messages missing label_description
|
||||
try:
|
||||
from utils.acars_translator import translate_message
|
||||
for msg in messages.get('acars', []):
|
||||
if not msg.get('label_description'):
|
||||
translation = translate_message(msg)
|
||||
msg['label_description'] = translation['label_description']
|
||||
msg['message_type'] = translation['message_type']
|
||||
msg['parsed'] = translation['parsed']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({'status': 'success', 'icao': icao.upper(), **messages})
|
||||
|
||||
Reference in New Issue
Block a user