@mytec: 1.4iter ready for testing
This commit is contained in:
128
backend/app/services/materials_service.py
Normal file
128
backend/app/services/materials_service.py
Normal file
@@ -0,0 +1,128 @@
|
||||
import math
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class BuildingMaterial(Enum):
|
||||
"""Building materials with RF properties"""
|
||||
CONCRETE = "concrete"
|
||||
BRICK = "brick"
|
||||
GLASS = "glass"
|
||||
WOOD = "wood"
|
||||
METAL = "metal"
|
||||
STONE = "stone"
|
||||
PLASTER = "plaster"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
# ITU-R P.2040 based attenuation (dB per wall at 1-3 GHz)
|
||||
MATERIAL_LOSS = {
|
||||
BuildingMaterial.CONCRETE: 15.0,
|
||||
BuildingMaterial.BRICK: 10.0,
|
||||
BuildingMaterial.GLASS: 3.0,
|
||||
BuildingMaterial.WOOD: 5.0,
|
||||
BuildingMaterial.METAL: 25.0, # Or full reflection
|
||||
BuildingMaterial.STONE: 12.0,
|
||||
BuildingMaterial.PLASTER: 4.0,
|
||||
BuildingMaterial.UNKNOWN: 10.0, # Default assumption
|
||||
}
|
||||
|
||||
# Reflection coefficient (0-1, portion of signal reflected)
|
||||
MATERIAL_REFLECTION = {
|
||||
BuildingMaterial.CONCRETE: 0.6,
|
||||
BuildingMaterial.BRICK: 0.5,
|
||||
BuildingMaterial.GLASS: 0.3,
|
||||
BuildingMaterial.WOOD: 0.2,
|
||||
BuildingMaterial.METAL: 0.9,
|
||||
BuildingMaterial.STONE: 0.55,
|
||||
BuildingMaterial.PLASTER: 0.3,
|
||||
BuildingMaterial.UNKNOWN: 0.4,
|
||||
}
|
||||
|
||||
|
||||
class MaterialsService:
|
||||
"""Building material detection and RF properties"""
|
||||
|
||||
# OSM building:material tag mapping
|
||||
OSM_MATERIAL_MAP = {
|
||||
"concrete": BuildingMaterial.CONCRETE,
|
||||
"brick": BuildingMaterial.BRICK,
|
||||
"glass": BuildingMaterial.GLASS,
|
||||
"wood": BuildingMaterial.WOOD,
|
||||
"metal": BuildingMaterial.METAL,
|
||||
"steel": BuildingMaterial.METAL,
|
||||
"stone": BuildingMaterial.STONE,
|
||||
"plaster": BuildingMaterial.PLASTER,
|
||||
"cement_block": BuildingMaterial.CONCRETE,
|
||||
"timber": BuildingMaterial.WOOD,
|
||||
}
|
||||
|
||||
# Fallback by building type
|
||||
BUILDING_TYPE_MATERIAL = {
|
||||
"industrial": BuildingMaterial.METAL,
|
||||
"warehouse": BuildingMaterial.METAL,
|
||||
"garage": BuildingMaterial.METAL,
|
||||
"shed": BuildingMaterial.WOOD,
|
||||
"house": BuildingMaterial.BRICK,
|
||||
"residential": BuildingMaterial.CONCRETE,
|
||||
"apartments": BuildingMaterial.CONCRETE,
|
||||
"commercial": BuildingMaterial.GLASS, # Often glass facades
|
||||
"office": BuildingMaterial.GLASS,
|
||||
"retail": BuildingMaterial.GLASS,
|
||||
"church": BuildingMaterial.STONE,
|
||||
"cathedral": BuildingMaterial.STONE,
|
||||
"school": BuildingMaterial.BRICK,
|
||||
"hospital": BuildingMaterial.CONCRETE,
|
||||
"university": BuildingMaterial.CONCRETE,
|
||||
}
|
||||
|
||||
def detect_material(self, building_tags: dict) -> BuildingMaterial:
|
||||
"""Detect building material from OSM tags"""
|
||||
|
||||
# Direct material tag
|
||||
if "building:material" in building_tags:
|
||||
material_str = building_tags["building:material"].lower()
|
||||
if material_str in self.OSM_MATERIAL_MAP:
|
||||
return self.OSM_MATERIAL_MAP[material_str]
|
||||
|
||||
# Facade material (often more relevant for RF)
|
||||
if "building:facade:material" in building_tags:
|
||||
material_str = building_tags["building:facade:material"].lower()
|
||||
if material_str in self.OSM_MATERIAL_MAP:
|
||||
return self.OSM_MATERIAL_MAP[material_str]
|
||||
|
||||
# Fallback by building type
|
||||
building_type = building_tags.get("building", "yes").lower()
|
||||
if building_type in self.BUILDING_TYPE_MATERIAL:
|
||||
return self.BUILDING_TYPE_MATERIAL[building_type]
|
||||
|
||||
return BuildingMaterial.UNKNOWN
|
||||
|
||||
def get_penetration_loss(self, material: BuildingMaterial, frequency_mhz: float = 1800) -> float:
|
||||
"""
|
||||
Get RF penetration loss through wall
|
||||
|
||||
Frequency correction: +2dB per octave above 1GHz
|
||||
"""
|
||||
base_loss = MATERIAL_LOSS[material]
|
||||
|
||||
# Frequency correction (simplified)
|
||||
freq_factor = max(0, (frequency_mhz - 1000) / 1000) * 2
|
||||
|
||||
return base_loss + freq_factor
|
||||
|
||||
def get_reflection_coefficient(self, material: BuildingMaterial) -> float:
|
||||
"""Get reflection coefficient (0-1)"""
|
||||
return MATERIAL_REFLECTION[material]
|
||||
|
||||
def get_reflection_loss(self, material: BuildingMaterial) -> float:
|
||||
"""Get loss due to reflection (dB)"""
|
||||
coeff = MATERIAL_REFLECTION[material]
|
||||
if coeff <= 0:
|
||||
return 30.0 # Effectively no reflection
|
||||
|
||||
# Reflection loss in dB = -10 * log10(coefficient)
|
||||
return -10 * math.log10(coeff)
|
||||
|
||||
|
||||
materials_service = MaterialsService()
|
||||
Reference in New Issue
Block a user