import math class IndoorService: """ITU-R P.2109 building entry loss model""" # Building Entry Loss (BEL) by construction type at 2 GHz # Format: (median_loss_dB, std_dev_dB) BUILDING_TYPES = { "none": (0.0, 0.0), # Outdoor only "light": (8.0, 4.0), # Wood frame, large windows "medium": (15.0, 6.0), # Brick, standard windows "heavy": (22.0, 8.0), # Concrete, small windows "basement": (30.0, 10.0), # Underground "vehicle": (6.0, 3.0), # Inside car "train": (20.0, 5.0), # Inside train } # Frequency correction factor (dB per octave above 2 GHz) FREQ_CORRECTION = 2.5 def calculate_indoor_loss( self, frequency_mhz: float, building_type: str = "medium", floor_number: int = 0, depth_m: float = 0.0, ) -> float: """ Calculate building entry/indoor penetration loss Args: frequency_mhz: Frequency in MHz building_type: Type of building construction floor_number: Floor number (0=ground, negative=basement) depth_m: Distance from exterior wall in meters Returns: Loss in dB """ if building_type == "none": return 0.0 base_loss, _ = self.BUILDING_TYPES.get(building_type, (15.0, 6.0)) # Frequency correction freq_ghz = frequency_mhz / 1000 if freq_ghz > 2.0: octaves = math.log2(freq_ghz / 2.0) freq_correction = self.FREQ_CORRECTION * octaves else: freq_correction = 0.0 # Floor correction (higher floors = less loss due to better angle) if floor_number > 0: floor_correction = -1.5 * min(floor_number, 10) elif floor_number < 0: # Basement - additional loss floor_correction = 5.0 * abs(floor_number) else: floor_correction = 0.0 # Depth correction (signal attenuates inside building) # Approximately 0.5 dB per meter for first 10m depth_correction = 0.5 * min(depth_m, 20) total_loss = base_loss + freq_correction + floor_correction + depth_correction return max(0.0, min(total_loss, 50.0)) # Clamp 0-50 dB def calculate_outdoor_to_indoor_coverage( self, outdoor_rsrp: float, building_type: str, frequency_mhz: float, ) -> float: """Calculate expected indoor RSRP from outdoor signal""" indoor_loss = self.calculate_indoor_loss(frequency_mhz, building_type) return outdoor_rsrp - indoor_loss indoor_service = IndoorService()