Source code for opengnc.mission_design.communications

"""
Link budget calculations and RF analysis tools.
"""

import numpy as np

# Speed of light in m/s
C = 299792458.0






[docs] def calculate_doppler_shift(f_tx_hz: float, r_ecef_rx: np.ndarray, v_ecef_rx: np.ndarray, r_ecef_tx: np.ndarray, v_ecef_tx: np.ndarray) -> dict[str, float]: """ Calculates the Doppler shift for a signal sent from TX to RX. Formula: delta_f = - f_tx * (v_rel / c) where v_rel = dot(r_rel, v_rel_vec) / |r_rel| r_rel = r_rx - r_tx v_rel_vec = v_rx - v_tx Args: f_tx_hz (float): Transmitter frequency [Hz]. r_ecef_rx (np.ndarray): Receiver position in ECEF [m], shape (3,). v_ecef_rx (np.ndarray): Receiver velocity in ECEF [m/s], shape (3,). r_ecef_tx (np.ndarray): Transmitter position in ECEF [m], shape (3,). v_ecef_tx (np.ndarray): Transmitter velocity in ECEF [m/s], shape (3,). Returns ------- dict: Containing: 'f_rx_hz' (float): Received frequency [Hz]. 'doppler_shift_hz' (float): Doppler shift [Hz]. """ r_rx = np.array(r_ecef_rx) v_rx = np.array(v_ecef_rx) r_tx = np.array(r_ecef_tx) v_tx = np.array(v_ecef_tx) if r_rx.shape != (3,) or v_rx.shape != (3,) or r_tx.shape != (3,) or v_tx.shape != (3,): raise ValueError("Positions and velocities must be vectors of length 3.") r_rel = r_rx - r_tx v_rel_vec = v_rx - v_tx distance = np.linalg.norm(r_rel) if distance == 0: return {"f_rx_hz": f_tx_hz, "doppler_shift_hz": 0.0} v_rel = np.dot(r_rel, v_rel_vec) / distance # range rate doppler_shift_hz = -f_tx_hz * (v_rel / C) f_rx_hz = f_tx_hz + doppler_shift_hz return {"f_rx_hz": f_rx_hz, "doppler_shift_hz": doppler_shift_hz}
[docs] def calculate_atmospheric_attenuation(elevation_deg: float, frequency_hz: float) -> float: """ Calculates atmospheric attenuation using a simplified cosecant model. Formula: L_atm = A_zenith / sin(elevation) where A_zenith is a base attenuation dependent on frequency band. Args: elevation_deg (float): Elevation angle [deg]. frequency_hz (float): Frequency [Hz]. Returns ------- float: Atmospheric attenuation [dB]. """ # Zenith attenuation by frequency band (ballpark values) if frequency_hz < 3e9: # S-band a_zenith = 0.03 elif frequency_hz < 10e9: # C/X-band a_zenith = 0.05 elif frequency_hz < 18e9: # Ku-band a_zenith = 0.15 else: # Ka-band and above a_zenith = 0.35 # Cosecant model valid for elevation > 5 deg; clamp to avoid division singularity elevation_rad = np.radians(max(elevation_deg, 5.0)) l_atm_db = a_zenith / np.sin(elevation_rad) return float(l_atm_db)