Files
rfcp/backend/app/services/weather_service.py

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()