1 # Copyright 2023 highstreet technologies USA CORP.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
18 A Class representing a 3GPP new radio cell du (NrCellDu)
21 import xml.etree.ElementTree as ET
22 from typing import Any, cast
24 import network_generation.model.python.hexagon as Hexagon
25 from network_generation.model.python.geo_location import (
29 from network_generation.model.python.o_ran_node import IORanNode, ORanNode
30 from network_generation.model.python.o_ran_termination_point import (
33 from network_generation.model.python.point import Point
36 # Define the "INrCellDu" interface
37 class INrCellDu(IORanNode):
39 cellScaleFactorForHandoverArea: int
44 default_value: INrCellDu = cast(
50 "cellScaleFactorForHandoverArea": 0,
58 # Define an abstract O-RAN Node class
59 class NrCellDu(ORanNode):
62 data: dict[str, Any] = cast(dict[str, Any], default_value),
63 **kwargs: dict[str, Any]
65 cell_data: INrCellDu = self._to_cell_data(data)
66 super().__init__(cast(dict[str, Any], cell_data), **kwargs)
67 self._cell_angle: int = int(str(cell_data["cellAngle"]))
68 self._cell_scale_factor: int = int(
69 str(cell_data["cellScaleFactorForHandoverArea"])
71 self._maxReach: int = int(str(cell_data["maxReach"]))
72 self._azimuth: int = int(str(cell_data["azimuth"]))
74 def _to_cell_data(self, data: dict[str, Any]) -> INrCellDu:
75 result: INrCellDu = default_value
76 for key, key_type in INrCellDu.__annotations__.items():
78 result[key] = data[key] # type: ignore
82 def cell_angle(self) -> int:
83 return self._cell_angle
86 def cell_angle(self, value: int) -> None:
87 self._cell_angle = value
90 def cell_scale_factor(self) -> int:
91 return self._cell_scale_factor
93 @cell_scale_factor.setter
94 def cell_scale_factor(self, value: int) -> None:
95 self._cell_scale_factor = value
98 def maxReach(self) -> int:
102 def maxReach(self, value: int) -> None:
103 self._maxReach = value
106 def azimuth(self) -> int:
110 def azimuth(self, value: int) -> None:
111 self._azimuth = value
113 def termination_points(self) -> list[ORanTerminationPoint]:
114 result: list[ORanTerminationPoint] = super().termination_points()
115 result.append(ORanTerminationPoint({"id": self.name, "name": self.name}))
118 def to_topology_nodes(self) -> list[dict[str, Any]]:
119 # a cell is not a node it is a Termination Point
120 result: list[dict[str, Any]] = [] # super().to_topology_nodes()
123 def to_topology_links(self) -> list[dict[str, Any]]:
124 # as a cell is not a node, it does not have links
125 result: list[dict[str, Any]] = [] # super().to_topology_links()
128 def toKml(self) -> ET.Element:
129 placemark: ET.Element = ET.Element("Placemark")
130 name: ET.Element = ET.SubElement(placemark, "name")
131 name.text = self.name
132 style: ET.Element = ET.SubElement(placemark, "styleUrl")
133 style.text = "#" + self.__class__.__name__
134 multi_geometry: ET.Element = ET.SubElement(placemark, "MultiGeometry")
135 polygon: ET.Element = ET.SubElement(multi_geometry, "Polygon")
136 outer_boundary: ET.Element = ET.SubElement(polygon, "outerBoundaryIs")
137 linear_ring: ET.Element = ET.SubElement(outer_boundary, "LinearRing")
138 coordinates: ET.Element = ET.SubElement(linear_ring, "coordinates")
140 points: list[Point] = Hexagon.polygon_corners(self.layout, self.position)
142 self.parent.parent.parent.parent.parent.parent.geo_location.point_to_geo_location
144 geo_locations: list[GeoLocation] = list(map(method, points))
147 index: int = 1 + int(self._azimuth / self._cell_angle)
148 network_center: GeoLocation = (
149 self.parent.parent.parent.parent.parent.parent.geo_location
152 intersect1: Point = Point(
153 (points[(2 * index + 1) % 6].x + points[(2 * index + 2) % 6].x) / 2,
154 (points[(2 * index + 1) % 6].y + points[(2 * index + 2) % 6].y) / 2,
156 intersect_geo_location1: GeoLocation = network_center.point_to_geo_location(
160 intersect2: Point = Point(
161 (points[(2 * index + 3) % 6].x + points[(2 * index + 4) % 6].x) / 2,
162 (points[(2 * index + 3) % 6].y + points[(2 * index + 4) % 6].y) / 2,
164 intersect_geo_location2: GeoLocation = network_center.point_to_geo_location(
168 tower: GeoLocation = GeoLocation(cast(IGeoLocation, self.geo_location))
169 # TODO: Why a cast is required
171 cell_polygon: list[GeoLocation] = []
172 cell_polygon.append(tower)
173 cell_polygon.append(intersect_geo_location1)
174 cell_polygon.append(geo_locations[(2 * index + 2) % 6])
175 cell_polygon.append(geo_locations[(2 * index + 3) % 6])
176 cell_polygon.append(intersect_geo_location2)
178 cell_polygon.append(tower)
180 for gl in cell_polygon:
182 str("%.6f" % float(gl.longitude)),
183 str("%.6f" % float(gl.latitude)),
184 str("%.6f" % float(gl.aboveMeanSeaLevel)),
186 text.append(",".join(strs))
187 coordinates.text = " ".join(text)
189 if self.cell_scale_factor > 0:
190 scaled_polygon: ET.Element = ET.SubElement(multi_geometry, "Polygon")
191 scaled_outer_boundary: ET.Element = ET.SubElement(scaled_polygon, "outerBoundaryIs")
192 scaled_linear_ring: ET.Element = ET.SubElement(scaled_outer_boundary, "LinearRing")
193 scaled_coordinates: ET.Element = ET.SubElement(scaled_linear_ring, "coordinates")
195 arc: float = self.azimuth * math.pi / 180
196 meterToDegree: float = 2 * math.pi * GeoLocation().equatorialRadius / 360
197 translateX: float = (
199 * (self.cell_scale_factor / 100)
202 translateY: float = (
204 * (self.cell_scale_factor / 100)
207 centerX: float = self.layout.size.x * 0.5 * math.sin(arc)
208 centerY: float = self.layout.size.y * 0.5 * math.cos(arc)
209 cell_center : GeoLocation = GeoLocation(
211 "latitude": tower.latitude + centerY / meterToDegree,
212 "longitude": tower.longitude + centerX / meterToDegree,
213 "aboveMeanSeaLevel": tower.aboveMeanSeaLevel,
218 for gl in cell_polygon:
219 scale: float = 1 + self.cell_scale_factor / 100
220 lng_new: float = ( 1 * scale * (gl.longitude - cell_center.longitude) ) + cell_center.longitude
221 lat_new: float = ( 1 * scale * ( gl.latitude - cell_center.latitude ) ) + cell_center.latitude
222 scaled_strs: list[str] = [
223 str("%.6f" % float(lng_new)),
224 str("%.6f" % float(lat_new)),
225 str("%.6f" % float(gl.aboveMeanSeaLevel)),
227 text.append(",".join(scaled_strs))
229 scaled_coordinates.text = " ".join(text)
232 def toSvg(self) -> ET.Element:
233 return ET.Element("to-be-implemented")