""" Okumura-Hata empirical propagation model. Valid for: - Frequency: 150-1500 MHz - Distance: 1-20 km - TX height: 30-200 m - RX height: 1-10 m Reference: Hata (1980), "Empirical Formula for Propagation Loss in Land Mobile Radio Services" """ import math from app.propagation.base import PropagationModel, PropagationInput, PropagationOutput class OkumuraHataModel(PropagationModel): @property def name(self) -> str: return "Okumura-Hata" @property def frequency_range(self) -> tuple: return (150, 1500) @property def distance_range(self) -> tuple: return (100, 20000) # Extended to 100m minimum for practical use def calculate(self, input: PropagationInput) -> PropagationOutput: f = input.frequency_mhz d = max(input.distance_m / 1000, 0.1) # km, min 100m hb = max(input.tx_height_m, 1.0) hm = max(input.rx_height_m, 1.0) # Mobile antenna height correction factor if input.environment == "urban" and f >= 400: # Large city a_hm = 3.2 * (math.log10(11.75 * hm) ** 2) - 4.97 else: # Medium/small city a_hm = (1.1 * math.log10(f) - 0.7) * hm - (1.56 * math.log10(f) - 0.8) # Basic path loss (urban) L_urban = ( 69.55 + 26.16 * math.log10(f) - 13.82 * math.log10(hb) - a_hm + (44.9 - 6.55 * math.log10(hb)) * math.log10(d) ) # Environment correction if input.environment == "suburban": L = L_urban - 2 * (math.log10(f / 28) ** 2) - 5.4 elif input.environment == "rural": L = L_urban - 4.78 * (math.log10(f) ** 2) + 18.33 * math.log10(f) - 35.94 elif input.environment == "open": L = L_urban - 4.78 * (math.log10(f) ** 2) + 18.33 * math.log10(f) - 40.94 else: L = L_urban return PropagationOutput( path_loss_db=L, model_name=self.name, is_los=False, breakdown={ "basic_loss": L_urban, "environment_correction": L - L_urban, "antenna_correction": a_hm, }, )