103 lines
2.9 KiB
Python
103 lines
2.9 KiB
Python
import math
|
|
|
|
|
|
class WeatherService:
|
|
"""ITU-R P.838 rain attenuation model"""
|
|
|
|
# ITU-R P.838-3 coefficients for horizontal polarization
|
|
# Format: (frequency_GHz, k, alpha)
|
|
RAIN_COEFFICIENTS = {
|
|
0.7: (0.0000387, 0.912),
|
|
1.0: (0.0000887, 0.949),
|
|
1.8: (0.000292, 1.021),
|
|
2.1: (0.000425, 1.052),
|
|
2.6: (0.000683, 1.091),
|
|
3.5: (0.00138, 1.149),
|
|
5.0: (0.00361, 1.206),
|
|
10.0: (0.0245, 1.200),
|
|
20.0: (0.0906, 1.099),
|
|
30.0: (0.175, 1.021),
|
|
}
|
|
|
|
def calculate_rain_attenuation(
|
|
self,
|
|
frequency_mhz: float,
|
|
distance_km: float,
|
|
rain_rate: float, # mm/h
|
|
) -> float:
|
|
"""
|
|
Calculate rain attenuation in dB
|
|
|
|
Args:
|
|
frequency_mhz: Frequency in MHz
|
|
distance_km: Path length in km
|
|
rain_rate: Rain rate in mm/h (0=none, 5=light, 25=moderate, 50=heavy)
|
|
|
|
Returns:
|
|
Attenuation in dB
|
|
"""
|
|
if rain_rate <= 0:
|
|
return 0.0
|
|
|
|
freq_ghz = frequency_mhz / 1000
|
|
|
|
# Get interpolated coefficients
|
|
k, alpha = self._get_coefficients(freq_ghz)
|
|
|
|
# Specific attenuation (dB/km)
|
|
gamma_r = k * (rain_rate ** alpha)
|
|
|
|
# Effective path length reduction for longer paths
|
|
# Rain cells are typically 2-5 km
|
|
if distance_km > 2:
|
|
reduction_factor = 1 / (1 + distance_km / 35)
|
|
effective_distance = distance_km * reduction_factor
|
|
else:
|
|
effective_distance = distance_km
|
|
|
|
attenuation = gamma_r * effective_distance
|
|
|
|
return min(attenuation, 30.0) # Cap at 30 dB
|
|
|
|
def _get_coefficients(self, freq_ghz: float) -> tuple[float, float]:
|
|
"""Interpolate rain coefficients for frequency"""
|
|
freqs = sorted(self.RAIN_COEFFICIENTS.keys())
|
|
|
|
# Find surrounding frequencies
|
|
if freq_ghz <= freqs[0]:
|
|
return self.RAIN_COEFFICIENTS[freqs[0]]
|
|
if freq_ghz >= freqs[-1]:
|
|
return self.RAIN_COEFFICIENTS[freqs[-1]]
|
|
|
|
for i in range(len(freqs) - 1):
|
|
if freqs[i] <= freq_ghz <= freqs[i + 1]:
|
|
f1, f2 = freqs[i], freqs[i + 1]
|
|
k1, a1 = self.RAIN_COEFFICIENTS[f1]
|
|
k2, a2 = self.RAIN_COEFFICIENTS[f2]
|
|
|
|
# Linear interpolation
|
|
t = (freq_ghz - f1) / (f2 - f1)
|
|
k = k1 + t * (k2 - k1)
|
|
alpha = a1 + t * (a2 - a1)
|
|
|
|
return k, alpha
|
|
|
|
return self.RAIN_COEFFICIENTS[freqs[0]]
|
|
|
|
@staticmethod
|
|
def rain_rate_from_description(description: str) -> float:
|
|
"""Convert rain description to rate"""
|
|
rates = {
|
|
"none": 0.0,
|
|
"drizzle": 2.5,
|
|
"light": 5.0,
|
|
"moderate": 12.5,
|
|
"heavy": 25.0,
|
|
"very_heavy": 50.0,
|
|
"extreme": 100.0,
|
|
}
|
|
return rates.get(description.lower(), 0.0)
|
|
|
|
|
|
weather_service = WeatherService()
|