from __future__ import annotations from dataclasses import dataclass from math import exp from app.config import get_settings @dataclass(frozen=True) class P833Coefficients: gamma_db_per_m: float max_attenuation_db: float WORLDCOVER_P833: dict[str, P833Coefficients] = { "tree_cover": P833Coefficients(gamma_db_per_m=0.20, max_attenuation_db=30.0), "mangroves": P833Coefficients(gamma_db_per_m=0.22, max_attenuation_db=32.0), "shrubland": P833Coefficients(gamma_db_per_m=0.10, max_attenuation_db=12.0), "grassland": P833Coefficients(gamma_db_per_m=0.05, max_attenuation_db=6.0), "cropland": P833Coefficients(gamma_db_per_m=0.04, max_attenuation_db=5.0), "herbaceous_wetland": P833Coefficients(gamma_db_per_m=0.08, max_attenuation_db=10.0), "unknown": P833Coefficients(gamma_db_per_m=0.15, max_attenuation_db=25.0), } def p833_coefficients_for_class(class_name: str) -> P833Coefficients: if class_name in WORLDCOVER_P833: return WORLDCOVER_P833[class_name] settings = get_settings() return P833Coefficients( gamma_db_per_m=settings.p833_gamma_db_per_m, max_attenuation_db=settings.p833_max_attenuation_db, ) def p833_attenuation( depth_m: float, freq_hz: float, forest_type: str, coefficients: P833Coefficients | None = None, ) -> float: if depth_m <= 0: return 0.0 if freq_hz <= 0: raise ValueError("freq_hz must be positive") coeff = coefficients or p833_coefficients_for_class(forest_type) if coeff.max_attenuation_db <= 0: raise ValueError("max_attenuation_db must be positive") return coeff.max_attenuation_db * ( 1 - exp(-(depth_m * coeff.gamma_db_per_m) / coeff.max_attenuation_db) )