Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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: 6 additions & 0 deletions sharc/antenna/antenna_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ def create_antenna(
azimuth,
elevation
)
case "Antenna System 4":
# Handled in station_factory.py since it requires two antennas
raise NotImplementedError(
"Antenna System 4 requires two antenna instances and "
"should be created in station_factory.py"
)
case _:
raise ValueError(
f"Antenna factory does not support pattern {
Expand Down
41 changes: 25 additions & 16 deletions sharc/antenna/antenna_s1528.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,18 @@ def __init__(self, param: ParametersAntennaS1528):
# the system design
self.l_s = param.antenna_l_s

# far-out side-lobe level [dBi]
if param.far_out_side_lobe is None:
self.l_f = 0
else:
self.l_f = param.far_out_side_lobe

# for elliptical antennas, this is the ratio major axis/minor axis
# we assume circular antennas, so z = 1
self.z = 1

# far-out side-lobe level [dBi]
self.l_f = 0
if param.major_minor_axis_ratio is None:
self.z = 1
else:
self.z = param.major_minor_axis_ratio

# back-lobe level
self.l_b = np.maximum(
Expand All @@ -190,19 +196,22 @@ def __init__(self, param: ParametersAntennaS1528):
# one-half the 3 dB beamwidth in the plane of interest
self.psi_b = param.antenna_3_dB_bw / 2

if self.l_s == -15:
self.a = 2.58 * math.sqrt(1 - 1.4 * math.log10(self.z))
elif self.l_s == -20:
self.a = 2.58 * math.sqrt(1 - 1.0 * math.log10(self.z))
elif self.l_s == -25:
self.a = 2.58 * math.sqrt(1 - 0.6 * math.log10(self.z))
elif self.l_s == -30:
self.a = 2.58 * math.sqrt(1 - 0.4 * math.log10(self.z))
if math.isclose(self.z, 1.0):
self.a = 2.58
else:
sys.stderr.write(
"ERROR\nInvalid AntennaS1528 L_s parameter: " + str(self.l_s),
)
sys.exit(1)
if self.l_s == -15:
self.a = 2.58 * math.sqrt(1 - 1.4 * math.log10(self.z))
elif self.l_s == -20:
self.a = 2.58 * math.sqrt(1 - 1.0 * math.log10(self.z))
elif self.l_s == -25:
self.a = 2.58 * math.sqrt(1 - 0.6 * math.log10(self.z))
elif self.l_s == -30:
self.a = 2.58 * math.sqrt(1 - 0.4 * math.log10(self.z))
else:
sys.stderr.write(
f"ERROR\nInvalid AntennaS1528 L_s parameter {self.l_s} for z={self.z}"
)
sys.exit(1)

self.b = 6.32
self.alpha = 1.5
Expand Down
8 changes: 8 additions & 0 deletions sharc/parameters/antenna/parameters_antenna_s1528.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class ParametersAntennaS1528(ParametersBase):
l_r: float = None
l_t: float = None

# Recommends 1.2 only
# For elliptical antennas, this is the ratio major axis/minor axis
# we assume circular antennas, so z = 1
major_minor_axis_ratio: float = None

# Far-out side-lobe level
far_out_side_lobe: float = None

def load_parameters_from_file(self, config_file: str):
"""Load the parameters from file an run a sanity check.

Expand Down
48 changes: 48 additions & 0 deletions sharc/parameters/antenna/parameters_antenna_system4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Implementation of ParametersAntennaSystem4 class
# The System 4 antenna was defined in WP4C Working Document 4C/356 from October 2025
# The antenna is based on S.1528 recommends 1.2
# It defines two sets of parameters for high elevation and low elevation beams
from dataclasses import dataclass, field

from sharc.parameters.parameters_base import ParametersBase
from sharc.parameters.antenna.parameters_antenna_s1528 import ParametersAntennaS1528


@dataclass
class ParametersAntennaSystem4(ParametersBase):
"""Dataclass containing the Antenna System 4 parameters for the simulator.
"""
section_name: str = "Antenna System 4"

# Parameters for high elevation beams
antenna_parameters_high: ParametersAntennaS1528 = field(
# we don't care about frequency and bandwidth here. Just to make validation work.
default_factory=lambda: ParametersAntennaS1528(
frequency=-1,
bandwidth=-1
)
)

# Parameters for low elevation beams
antenna_parameters_low: ParametersAntennaS1528 = field(
# we don't care about frequency and bandwidth here. Just to make validation work.
default_factory=lambda: ParametersAntennaS1528(
frequency=-1,
bandwidth=-1
)
)

def load_parameters_from_file(self, config_file: str):
"""Load the parameters from file an run a sanity check.

Parameters
----------
file_name : str
the path to the configuration file

Raises
------
ValueError
if a parameter is not valid
"""
super().load_parameters_from_file(config_file)
6 changes: 6 additions & 0 deletions sharc/parameters/imt/parameters_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ class ParametersSatelliteWithServiceGrid(ParametersTerrestrialGrid):

beam_radius: float = None

# [deg]
minimum_service_angle: float = 5.0

def validate(self, ctx):
"""Validates instance parameters.
Parameters
Expand All @@ -353,6 +356,9 @@ def validate(self, ctx):
)
self.cell_radius = self.beam_radius

if self.minimum_service_angle < 0. or self.minimum_service_angle > 90:
raise ValueError(f"{ctx}.minimum_service_angle should be in [0, 90]")

if not isinstance(
self.eligible_sats_margin_from_border,
float) and not isinstance(
Expand Down
13 changes: 11 additions & 2 deletions sharc/parameters/parameters_antenna.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from sharc.parameters.antenna.parameters_antenna_s672 import ParametersAntennaS672
from sharc.parameters.antenna.parameters_antenna_with_freq import ParametersAntennaWithFreq
from sharc.parameters.imt.parameters_antenna_imt import ParametersAntennaImt
from sharc.parameters.antenna.parameters_antenna_system4 import ParametersAntennaSystem4

from dataclasses import dataclass, field
import typing
Expand All @@ -29,7 +30,8 @@ class ParametersAntenna(ParametersBase):
"ITU-R-S.1528-Taylor",
"ITU-R-S.1528-Section1.2",
"ITU-R-S.1528-LEO",
"MSS Adjacent"]
"MSS Adjacent",
"Antenna System 4"]

# chosen antenna radiation pattern
pattern: typing.Literal["OMNI",
Expand All @@ -44,7 +46,8 @@ class ParametersAntenna(ParametersBase):
"ITU-R-S.1528-Taylor",
"ITU-R-S.1528-Section1.2",
"ITU-R-S.1528-LEO",
"MSS Adjacent"] = None
"MSS Adjacent",
"Antenna System 4"] = None

# antenna gain [dBi]
gain: float = None
Expand Down Expand Up @@ -94,6 +97,10 @@ class ParametersAntenna(ParametersBase):
default_factory=ParametersAntennaS672,
)

antenna_system_4: ParametersAntennaSystem4 = field(
default_factory=ParametersAntennaSystem4,
)

def set_external_parameters(self, **kwargs):
"""
Set external parameters for all sub-parameters of the antenna.
Expand Down Expand Up @@ -198,6 +205,8 @@ def validate(self, ctx):
self.itu_r_s_672.validate(f"{ctx}.itu_r_s_672")
case "MSS Adjacent":
self.mss_adjacent.validate(f"{ctx}.mss_adjacent")
case "Antenna System 4":
self.antenna_system_4.validate(f"{ctx}.antenna_system_4")
case _:
raise NotImplementedError(
"ParametersAntenna.validate does not implement this antenna validation!", )
3 changes: 3 additions & 0 deletions sharc/propagation/atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ def get_reference_atmosphere_p835(
"""

h_km = altitude / 1000
season = season.lower()
if season not in ['winter', 'summer']:
raise ValueError(f"Invalid season name f{season}.")

if latitude <= 22:
# low latitude
Expand Down
11 changes: 11 additions & 0 deletions sharc/station_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,7 @@ def generate_mss_d2d(
z = mss_d2d_values["sat_z"]
elev = mss_d2d_values["sat_antenna_elev"]
azim = mss_d2d_values["sat_antenna_azim"]
beams_ground_elev = mss_d2d_values["beams_ground_elev"]
mss_d2d.geom.set_global_coords(
x, y, z,
azim, elev,
Expand Down Expand Up @@ -1783,11 +1784,21 @@ def generate_mss_d2d(
antenna_pattern = AntennaS1528Taylor(params.antenna.itu_r_s_1528)
elif params.antenna.pattern == "MSS Adjacent":
antenna_pattern = AntennaMSSAdjacent(params.frequency)
elif params.antenna.pattern == "Antenna System 4":
antenna_pattern_high = AntennaS1528(params.antenna.antenna_system_4.antenna_parameters_high)
antenna_pattern_low = AntennaS1528(params.antenna.antenna_system_4.antenna_parameters_low)

else:
raise ValueError(
f"generate_mss_ss: Invalid antenna type: {params.antenna.pattern}")

for i in range(mss_d2d.num_stations):
if params.antenna.pattern == "Satellite Beamforming":
if beams_ground_elev[i] >= 50:
antenna_pattern = antenna_pattern_high
else:
antenna_pattern = antenna_pattern_low

mss_d2d.antenna[i] = antenna_pattern

return mss_d2d # Return the configured StationManager
Expand Down
Loading