SXXXXXXX_PyBusMonitor1553/pybusmonitor1553/core/controller.py

148 lines
6.0 KiB
Python

from ..lib1553 import messages as msgs
from ..lib1553.constants import (
TargetHistory, RadarMode, AltitudeBlock,
RWSSubmode, GMSubmode, RangeScale, BarScan, AzimuthScan,
StandbyStatus, DesignationControl
)
import logging
logger = logging.getLogger(__name__)
class RadarController:
"""
High-level controller for the Radar state.
FUTURE EXTENSION POINTS:
- add validate_command(msg, field, value) for ICD range checking
- add queue_command(msg) for rate-limited transmission
- add apply_config(profile) to replace hardcoded defaults
- add state change callbacks for GUI synchronization
Holds the instances of 1553 messages (A-Transmit / B-Receive).
Acts as the interface for the GUI or Scripts to modify radar behavior.
"""
def __init__(self):
# --- TX Messages (BC -> RT) ---
# These are the commands we send to the Radar
self.msg_a1 = msgs.MsgA1() # Settings
self.msg_a2 = msgs.MsgA2() # Commands
self.msg_a3 = msgs.MsgA3() # Graphics
self.msg_a4 = msgs.MsgA4() # Nav Data
self.msg_a5 = msgs.MsgA5() # INU Data
self.msg_a7 = msgs.MsgA7() # Data Link 1
self.msg_a8 = msgs.MsgA8() # Data Link 2
# --- RX Messages (RT -> BC) ---
# These are placeholders/requests we send to get data back
self.msg_b1 = msgs.MsgB1() # TWS 1-2
self.msg_b2 = msgs.MsgB2() # TWS 3-5
self.msg_b3 = msgs.MsgB3() # TWS 6-8
self.msg_b4 = msgs.MsgB4() # SPT
self.msg_b5 = msgs.MsgB5() # Tracked Target
self.msg_b6 = msgs.MsgB6() # Settings Tell-back
self.msg_b7 = msgs.MsgB7() # Status Tell-back
self.msg_b8 = msgs.MsgB8() # BIT Report
# NOTE: Defaults are NOT applied automatically
# Call apply_defaults() explicitly when needed (e.g., when RUN button is pressed)
def apply_defaults(self):
"""
Sets the default values for the radar commands.
Based on legacy initialization logic from working test_1553.py.
"""
logger.info("Applying default radar configuration...")
# --- A1: Settings ---
# Legacy: settings.set_history_level(TARGET_HISTORY_LEVEL_04)
self.msg_a1.target_history = TargetHistory.LEVEL_4
# Legacy: rdr_symbology_intensity = 127
self.msg_a1.symbol_intensity = 127
# Additional safe defaults for A1
self.msg_a1.altitude_block = AltitudeBlock.NORMAL
self.msg_a1.beacon_delay = 0.0
# Frequency agility and other A1 fields initialized to 0 by default (safe)
# --- A2: Operation Command ---
# Legacy: set_master_mode(RWS)
self.msg_a2.master_mode = RadarMode.RWS
# CRITICAL: Ensure STANDBY is OFF to allow radar operation
self.msg_a2.standby_cmd = StandbyStatus.OFF
# Legacy: set_gm_submode(0) -> RBM
self.msg_a2.gm_submode = GMSubmode.RBM
# Ensure other modes are in a known state
self.msg_a2.rws_submode = RWSSubmode.NAM
# Set reasonable scan parameters for RWS mode
self.msg_a2.range_scale = RangeScale.NM_20 # 20 NM default
self.msg_a2.bar_scan = BarScan.BAR_1 # 1 bar
self.msg_a2.azimuth_scan = AzimuthScan.DEG_60 # 60 deg scan
# Designation control = NOT_VALID (no designation active)
self.msg_a2.designation_ctrl = DesignationControl.NOT_VALID
# --- A3: Graphic ---
# Legacy: just instantiated (all zeros)
# A3 controls symbology display. 0 usually means nothing displayed initially.
# Can be configured via GUI later if needed.
# --- A4: Nav Data ---
# Legacy: validity_and_slew.raw = 0
# CRITICAL: validity bits = 0 means data is VALID (inverse logic!)
# Set all validity flags to 0 (data valid)
self.msg_a4.nav_data_invalid = 0
self.msg_a4.attitude_data_invalid = 0
self.msg_a4.baro_inertial_alt_invalid = 0
self.msg_a4.corr_baro_alt_invalid = 0
self.msg_a4.radalt_invalid = 0
self.msg_a4.spoi_alt_invalid = 0
self.msg_a4.spoi_pos_invalid = 0
self.msg_a4.tas_invalid = 0
self.msg_a4.cas_invalid = 0
self.msg_a4.ppos_invalid = 0
self.msg_a4.cursor_rates_invalid = 0
# Provide some dummy nav data (radar needs valid nav context)
self.msg_a4.baro_inertial_alt = 10000.0 # 10,000 ft
self.msg_a4.tas = 300.0 # 300 knots
self.msg_a4.cas = 250 # 250 knots
self.msg_a4.true_heading = 0.0 # North
# --- A5: INU Data ---
# Legacy: timetag.raw = 0
self.msg_a5.time_tag = 0.0
# Note: velocity_x/y/z are read-only properties (32-bit fields)
# They're calculated from underlying words, initialized to 0 by default
# To set them, we'd need to write directly to _data[2-7]
# For now, leave as 0 (default initialization is fine)
# Angular rates (zero = stable platform) - these ARE settable
self.msg_a5.pitch_rate = 0.0
self.msg_a5.roll_rate = 0.0
self.msg_a5.yaw_rate = 0.0
logger.info("Radar defaults applied:")
logger.info(f" → Master Mode: {self.msg_a2.master_mode.name}")
logger.info(f" → Standby: {self.msg_a2.standby_cmd.name}")
logger.info(f" → Range Scale: {self.msg_a2.range_scale.name}")
logger.info(f" → Nav Data Valid: YES (all validity flags = 0)")
logger.info(f" → Altitude: {self.msg_a4.baro_inertial_alt} ft")
logger.info(f" → TAS: {self.msg_a4.tas} kts")
def set_master_mode(self, mode: RadarMode):
"""API to change the Radar Master Mode."""
self.msg_a2.master_mode = mode
logger.info(f"Master Mode set to: {mode.name}")
def update_navigation_data(self, alt=None, tas=None):
"""Example API to update navigation data."""
if alt is not None:
self.msg_a4.baro_inertial_alt = alt
if tas is not None:
self.msg_a4.tas = tas