Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c4e83f1
Update 2108, fix p619 and p452. Also fix single_space_station
Jul 28, 2025
9ad3568
FIx input
Jul 28, 2025
5ea03b6
FIx parametersp452
Jul 28, 2025
35cbf9e
FIx parameters 619
Jul 28, 2025
a07efe4
FIx propagations 452
Jul 28, 2025
5e32e7a
Last commit
Jul 28, 2025
8a853e0
Fix percentage
Jul 29, 2025
e47b412
hotfix: Empty imt_system_antenna_gain_adjacent result in SimulationUp…
brunohcfaria Jul 30, 2025
2d4a01c
FIx one_end and both_end + some checks
Jul 30, 2025
1e8b7e1
update: Supressed divide by zero warnings when weights array
brunohcfaria Jul 30, 2025
aed7588
FIx P2108 check of mean_clutter_height
Jul 30, 2025
ac9f3c8
FIx: not passing mean_clutter_height and below_rooftop as parameters …
Jul 31, 2025
0105102
update(simulator): supports imt w/ MSS_DC topology using P.619
artistrea Jul 31, 2025
e683032
fix(simulator): propagation p619 earth station height from correct st…
artistrea Jul 31, 2025
57b08c1
Merge pull request #196 from Radio-Spectrum/feat/fix_2108_p619
brunohcfaria Aug 1, 2025
40afddc
refactor(simulation): DL IMT interferer better performance
artistrea Aug 5, 2025
8f8f9ab
Merge pull request #199 from Radio-Spectrum/refactor/imt_dl_interf_ma…
brunohcfaria Aug 5, 2025
61c421d
Merge pull request #198 from Radio-Spectrum/hotfix/empty_imt_system_a…
artistrea Aug 5, 2025
13d49d6
Merge pull request #197 from Radio-Spectrum/hotfix/supress_divide_by_…
artistrea Aug 5, 2025
3f60cdd
FIX(simulation): subarray not being used for IMT BS
artistrea Aug 7, 2025
bb3aade
fix(lint): params imt antenna missing docstring
artistrea Aug 7, 2025
f4a26bd
Merge pull request #205 from Radio-Spectrum/fix/imt_antenna_w_subarray
brunohcfaria Aug 7, 2025
f52fa03
FIX(simulation): imt bs beamsteering angles
artistrea Aug 8, 2025
eb3988b
requirements: update requirements.txt
araujoUnb Aug 8, 2025
b7f2f6e
Merge pull request #208 from Radio-Spectrum/fix/beamsteering_angles
brunohcfaria Aug 12, 2025
200a46e
feat(mss): add option for random time for sat position sampling from …
artistrea Aug 12, 2025
6a2058b
fix(orbit_predict): started fix for stochastic determination of orbit
artistrea Aug 13, 2025
cf473d4
feat: orbit time domain sampling
artistrea Aug 13, 2025
f34fe2b
fix: spectral mask always receives dBm values
artistrea Aug 15, 2025
b836f82
Merge pull request #210 from Radio-Spectrum/fix/spectral_mask_units
brunohcfaria Aug 18, 2025
ac6afc9
fix: Fixed an error in ParametersMssD2d where the P.619 parameters
brunohcfaria Aug 8, 2025
8a2a085
fix(propagation): Removed unusead P.619 parameters as they were unused.
brunohcfaria Aug 18, 2025
86973b0
Merge pull request #206 from Radio-Spectrum/fix/wrong_assigment_of_p6…
brunohcfaria Aug 19, 2025
3c2aba9
Merge pull request #209 from Radio-Spectrum/update/orbit_model_w_rand…
brunohcfaria Aug 20, 2025
9c0d02f
fix(antenna): Fixes nan values generated when angles were higher than
brunohcfaria Aug 21, 2025
9165826
Merge pull request #212 from Radio-Spectrum/fix/adjacent_mss_antenna_…
artistrea Aug 25, 2025
ea93648
feat(antenna): Implements the MSS UE antenna pattern provided for the…
brunohcfaria Aug 28, 2025
9a471c2
update(antenna): Now using a parabola fit to the HibleoX antenna patt…
brunohcfaria Aug 28, 2025
6d1ce46
update: antenna parameters and factory may create HibleoX UE antenna
artistrea Aug 29, 2025
31336d9
update(station_manager): in-place memory calculations for much better…
artistrea Sep 1, 2025
ffdab0a
fix: incorrect optimization implementation errors
artistrea Sep 1, 2025
e7ab2e4
feat: service grid random transformation as parameter option
artistrea Sep 3, 2025
b3d7b25
Merge pull request #213 from Radio-Spectrum/feat/antenna_hibleo_x_ue
brunohcfaria Sep 3, 2025
c326fc6
Merge branch 'development' into refactor/station_manager_calculations
brunohcfaria Sep 3, 2025
eb242f8
Merge pull request #215 from Radio-Spectrum/refactor/station_manager_…
brunohcfaria Sep 3, 2025
a8cd465
feat(topology): Added the TopologySingleBs azimuth as a parameter
brunohcfaria Aug 29, 2025
3c44b96
Merge pull request #218 from Radio-Spectrum/feat/single_bs_azimuth_as…
artistrea Sep 3, 2025
e2d9e35
update(station_factory): imt ue & bs antenna deduplication
artistrea Sep 2, 2025
1afde69
hotfix: antenna factory linting error
artistrea Sep 3, 2025
e48d0e8
Merge pull request #216 from Radio-Spectrum/update/deduplicating_unne…
brunohcfaria Sep 4, 2025
b25cf23
Update sharc/support/sharc_geom.py
brunohcfaria Sep 4, 2025
ac4395a
update(geom): Added missing parameter docstrings
brunohcfaria Sep 4, 2025
bd3dc2c
Merge branch 'development' into feat/random_grid_transform
brunohcfaria Sep 4, 2025
6bd918c
Merge pull request #217 from Radio-Spectrum/feat/random_grid_transform
brunohcfaria Sep 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions profile/P619/detailed_profile_p619.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,19 @@

# Constants
frequency_MHz = 2680.0
space_station_alt_m = 35786.0 * 1000 # Geostationary orbit altitude in meters
earth_station_alt_m = 1000.0
earth_station_lat_deg = -15.7801
earth_station_long_diff_deg = 0.0 # Not used in the loss calculation
season = "SUMMER"
apparent_elevation = np.arange(0, 90) # Elevation angles from 0 to 90 degrees
surf_water_vapour_density = 2.5

# Initialize the propagation model
random_number_gen = np.random.RandomState(101)
propagation = PropagationP619(random_number_gen=random_number_gen,
space_station_alt_m=space_station_alt_m,
earth_station_alt_m=earth_station_alt_m,
earth_station_lat_deg=earth_station_lat_deg,
earth_station_long_diff_deg=earth_station_long_diff_deg,
mean_clutter_height='low',
below_rooftop=0.0,
season=season)


Expand Down
5 changes: 2 additions & 3 deletions profile/P619/profile_with_and_without_lookuptable_p619.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

# Constants
frequency_MHz = 2680.0
space_station_alt_m = 35786.0 * 1000 # Geostationary orbit altitude in meters
earth_station_alt_m = 1000.0
earth_station_lat_deg = -15.7801
earth_station_long_diff_deg = 0.0 # Not used in the loss calculation
Expand All @@ -27,10 +26,10 @@
# Initialize the propagation model
random_number_gen = np.random.RandomState(101)
propagation = PropagationP619(random_number_gen=random_number_gen,
space_station_alt_m=space_station_alt_m,
earth_station_alt_m=earth_station_alt_m,
earth_station_lat_deg=earth_station_lat_deg,
earth_station_long_diff_deg=earth_station_long_diff_deg,
mean_clutter_height='low',
below_rooftop=0.0,
season=season)

# Profile with lookup table
Expand Down
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ mpmath==1.3.0
multipledispatch==1.0.0
nest-asyncio==1.6.0
numpy==1.26.4
obspy==1.4.0
#obspy==1.4.0
overloading==0.5.0
packaging==23.2
pandas==2.2.1
pandas==2.3.1
parso==0.8.4
pep8==1.7.1
pexpect==4.9.0
pillow==10.2.0
pillow==11.3.0
pip-system-certs==4.0
platformdirs==4.2.0
plotly==5.23.0
Expand All @@ -80,9 +80,9 @@ PyYAML==6.0.1
pyzmq==26.0.3
requests==2.31.0
ruff==0.6.9
scipy==1.12.0
scipy==1.16.1
setuptools==69.1.1
shapely==2.0.3
shapely==2.1.1
six==1.16.0
snowballstemmer==2.2.0
SQLAlchemy==2.0.29
Expand Down
19 changes: 8 additions & 11 deletions sharc/antenna/antenna_beamforming_imt.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
from sharc.antenna.antenna_element_imt_const import AntennaElementImtConst
from sharc.antenna.antenna_subarray_imt import AntennaSubarrayIMT
from sharc.antenna.antenna import Antenna
from sharc.support.named_tuples import AntennaPar
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt, ParametersAntennaSubarrayImt
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt


class AntennaBeamformingImt(Antenna):
Expand Down Expand Up @@ -45,19 +44,17 @@ class AntennaBeamformingImt(Antenna):

def __init__(
self,
par: AntennaPar,
par: ParametersAntennaImt,
azimuth: float,
elevation: float,
subarray: ParametersAntennaSubarrayImt = ParametersAntennaSubarrayImt(
is_enabled=False)):
elevation: float):
"""
Constructs an AntennaBeamformingImt object.
Does not receive angles in local coordinate system.
Elevation taken with x axis as reference.

Parameters
---------
param (AntennaPar): antenna IMT parameters
param (ParametersAntennaImt): antenna IMT parameters
azimuth (float): antenna's physical azimuth inclination
elevation (float): antenna's physical elevation inclination
referenced in the x axis
Expand All @@ -78,12 +75,12 @@ def __init__(
par.element_pattern} not supported", )
sys.exit(1)

if subarray.is_enabled:
if par.subarray.is_enabled:
self.subarray = AntennaSubarrayIMT(
element=self.element,
eletrical_downtilt=subarray.eletrical_downtilt,
n_rows=subarray.n_rows,
element_vert_spacing=subarray.element_vert_spacing
eletrical_downtilt=par.subarray.eletrical_downtilt,
n_rows=par.subarray.n_rows,
element_vert_spacing=par.subarray.element_vert_spacing
)

self.azimuth = azimuth
Expand Down
4 changes: 2 additions & 2 deletions sharc/antenna/antenna_element_imt_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""


from sharc.support.named_tuples import AntennaPar
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt
import numpy as np


Expand All @@ -20,7 +20,7 @@ class AntennaElementImtConst(object):

"""

def __init__(self, par: AntennaPar):
def __init__(self, par: ParametersAntennaImt):
"""
Constructs an AntennaElementImt object.

Expand Down
4 changes: 2 additions & 2 deletions sharc/antenna/antenna_element_imt_f1336.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np
import sys

from sharc.support.named_tuples import AntennaPar
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt


class AntennaElementImtF1336(object):
Expand All @@ -23,7 +23,7 @@ class AntennaElementImtF1336(object):
phi_3db (float): horizontal 3dB beamwidth of single element [degrees]
"""

def __init__(self, par: AntennaPar):
def __init__(self, par: ParametersAntennaImt):
"""
Constructs an AntennaElementImt object.

Expand Down
4 changes: 2 additions & 2 deletions sharc/antenna/antenna_element_imt_m2101.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import numpy as np

from sharc.support.named_tuples import AntennaPar
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt


class AntennaElementImtM2101(object):
Expand All @@ -23,7 +23,7 @@ class AntennaElementImtM2101(object):
sla_v (float): element vertical sidelobe attenuation
"""

def __init__(self, par: AntennaPar):
def __init__(self, par: ParametersAntennaImt):
"""
Constructs an AntennaElementImt object.

Expand Down
38 changes: 38 additions & 0 deletions sharc/antenna/antenna_factory.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

"""Antenna factory module for creating antenna instances based on parameters."""
from sharc.parameters.parameters_antenna import ParametersAntenna
from sharc.antenna.antenna import Antenna

from sharc.antenna.antenna_mss_adjacent import AntennaMSSAdjacent
from sharc.antenna.antenna_omni import AntennaOmni
from sharc.antenna.antenna_mss_hibleo_x_ue import AntennaMssHibleoXUe
from sharc.antenna.antenna_f699 import AntennaF699
from sharc.antenna.antenna_s465 import AntennaS465
from sharc.antenna.antenna_rra7_3 import AntennaReg_RR_A7_3
Expand All @@ -13,6 +15,8 @@
from sharc.antenna.antenna_s1528 import AntennaS1528, AntennaS1528Leo, AntennaS1528Taylor
from sharc.antenna.antenna_beamforming_imt import AntennaBeamformingImt

import numpy as np


class AntennaFactory():
"""Factory class for creating antenna instances based on pattern parameters."""
Expand All @@ -27,6 +31,8 @@ def create_antenna(
match antenna_params.pattern:
case "OMNI":
return AntennaOmni(antenna_params.gain)
case "HibleoX":
return AntennaMssHibleoXUe(antenna_params.hibleo_x.frequency)
case "ITU-R F.699":
return AntennaF699(antenna_params.itu_r_f_699)
case "ITU-R-S.1528-Taylor":
Expand Down Expand Up @@ -58,3 +64,35 @@ def create_antenna(
raise ValueError(
f"Antenna factory does not support pattern {
antenna_params.pattern}")

@staticmethod
def create_n_antennas(
antenna_params: ParametersAntenna,
azimuth: np.ndarray | float,
elevation: np.ndarray | float,
n_stations: int,
):
"""
Creates many antennas based on passed parameters.
If antenna does not require each object to have different state,
only a single antenna object will be created, and every position
in the array will point to it.
This is much more performant.
"""
antennas = np.empty((n_stations,), dtype=Antenna)
assert n_stations == len(azimuth)
assert n_stations == len(elevation)

if antenna_params.pattern == "ARRAY":
for i in range(n_stations):
antennas[i] = AntennaFactory.create_antenna(
antenna_params, azimuth[i], elevation[i],
)
else:
# some antennas don't need azimuth and elevation at all
# this makes it much faster
antennas[:] = AntennaFactory.create_antenna(
antenna_params, None, None,
)

return antennas
5 changes: 3 additions & 2 deletions sharc/antenna/antenna_mss_adjacent.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ def calculate_gain(self, *args, **kwargs) -> np.array:
np.array
Calculated antenna gain values.
"""
theta = np.absolute(kwargs["off_axis_angle_vec"])
theta_rad = np.deg2rad(np.absolute(kwargs["off_axis_angle_vec"]))
theta_rad = np.minimum(theta_rad, np.pi / 2 - 1e-4)
return 20 * np.log10(self.frequency / 2e3) + 10 * \
np.log10(np.cos(np.deg2rad(theta)))
np.log10(np.cos(theta_rad))


if __name__ == '__main__':
Expand Down
128 changes: 128 additions & 0 deletions sharc/antenna/antenna_mss_hibleo_x_ue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@

# -*- coding: utf-8 -*-
"""Antenna model for HibleoX MSS UE in the 2.5GHz frequency band."""
from sharc.antenna.antenna import Antenna

import numpy as np

# The filing does not provide an antenna model, but a set of points.
# We interpolate these points to create a continuous function.
# The points are taken from the filing.
data_str = """
x, y
0, 2.7
10, 2.6566311598597308
20, 2.431925804835606
30, 2.150602473172147
40, 1.6546431302238513
50, 0.9929370896722656
60, 0.0904942389326866
70, -0.9031468347087825
80, -2.386785139873968
90, -3.679459224299312
"""

HALF_BEAM_WIDTH_DEG = 64.0


class AntennaMssHibleoXUe(Antenna):
"""
Implements the MSS UE antenna pattern provided for the HIBLEO-X system in it's filings.

This is provided as a reference antenna pattern in document Document 4C/166-E - 30 September 2024.
This is valid only for frequencies around 2.45 GHz.
"""

def __init__(self, frequency_MHz: float):
"""
Initialize the AntennaMSSAdjacent class.

Parameters
----------
frequency_MHz : float
Frequency in MHz for the antenna model.
"""
super().__init__()
if frequency_MHz < 2483 or frequency_MHz > 2500:
raise ValueError("This antenna model is only valid for frequencies around 2.45 GHz")
self.frequency = frequency_MHz

# Parse the data string to extract x and y values
lines = data_str.strip().split('\n')[1:]
self.pattern_theta_deg = np.array([float(line.split(',')[0]) for line in lines])
self.patten_gain_dbi = np.array([float(line.split(',')[1]) for line in lines])

def calculate_gain(self, *args, **kwargs) -> np.array:
"""
Calculate the antenna gain for the given off-axis angles.

Parameters
----------
*args : tuple
Positional arguments (not used).
**kwargs : dict
Keyword arguments, expects 'off_axis_angle_vec' in degrees as input.

Returns
-------
np.array
Calculated antenna gain values.
"""
theta_deg = kwargs["off_axis_angle_vec"]
return -2.5226404 * (np.deg2rad(theta_deg) ** 2) + 2.7


if __name__ == '__main__':
import plotly.graph_objects as go
from scipy.optimize import curve_fit

# Compare the fitted parabolic function to the provided points

mss_ue_antenna = AntennaMssHibleoXUe(2483)

# Define the custom parabolic function without the linear term
def poly_no_linear(x, a):
"""
Polynomial function of the form ax^2 + c (no linear term).
"""
return a * x**2 + np.max(mss_ue_antenna.patten_gain_dbi)

# Fit the data using curve_fit
# initial_guess = [1, 1] # Optional: provide an initial guess for the parameters
params, covariance = curve_fit(
poly_no_linear,
np.deg2rad(mss_ue_antenna.pattern_theta_deg),
mss_ue_antenna.patten_gain_dbi
)

print("Fitting parabolic function (no linear term) to the provided points:")
print(f"Fitted parameters:\na={params}")
print(f"Covariance matrix:\n{covariance}")

off_axis_vec_deg = np.linspace(-90, 90, 100)
gains = mss_ue_antenna.calculate_gain(off_axis_angle_vec=off_axis_vec_deg)

gains_interp = np.interp(
np.abs(off_axis_vec_deg),
mss_ue_antenna.pattern_theta_deg,
mss_ue_antenna.patten_gain_dbi
)

fig = go.Figure(data=go.Scatter(x=off_axis_vec_deg, y=gains, mode='markers+lines', name='Parabolic Fit pattern'))
fig.update_layout(
title='Hibleo-X UE pattern from filing',
xaxis_title='off-axis angle (degrees)',
yaxis_title='Gain (dBi)',
template="plotly_white"
)
fig.update_yaxes(
linewidth=1,
linecolor='black',
mirror=True,
ticks='inside',
showline=True,
gridcolor="#DCDCDC",
gridwidth=1.5,
)
fig.add_trace(go.Scatter(x=off_axis_vec_deg, y=gains_interp, mode='markers', name='Interpolated'))
fig.show()
Loading