Skip to content
Merged
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
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
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
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 {season}.")

if latitude <= 22:
# low latitude
Expand Down
459 changes: 233 additions & 226 deletions sharc/topology/topology_imt_mss_dc.py

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions tests/parameters/parameters_for_testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ imt:
# if positive, makes border smaller by x km
# if negative, makes border bigger by x km
eligible_sats_margin_from_border: -2.1
minimum_service_angle: 10.1
sat_is_active_if:
# for a satellite to be active, it needs to respect ALL conditions
conditions:
Expand Down Expand Up @@ -998,6 +999,9 @@ single_space_station:
# Antenna diameter [m]
# if no diameter is passed, a diameter will be assumed according to document
diameter: 2.12
itu_r_s_1528:
major_minor_axis_ratio: 1.0
far_out_side_lobe: -25
###########################################################################
# Selected channel model
channel_model: "P619"
Expand Down Expand Up @@ -1096,6 +1100,7 @@ mss_d2d:
# if positive, makes border smaller by x km
# if negative, makes border bigger by x km
eligible_sats_margin_from_border: -2.1
minimum_service_angle: 12.1
sat_is_active_if:
# for a satellite to be active, it needs to respect ALL conditions
conditions:
Expand Down
18 changes: 18 additions & 0 deletions tests/parameters/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ def test_parameters_imt(self):
0.11)
self.assertEqual(
self.parameters.imt.topology.mss_dc.beam_positioning.service_grid.eligible_sats_margin_from_border, -2.1)
self.assertEqual(
self.parameters.imt.topology.mss_dc.beam_positioning.service_grid.minimum_service_angle, 10.1)
self.assertEqual(len(
self.parameters.imt.topology.mss_dc.beam_positioning.service_grid.grid_in_zone.from_countries.country_names), 2)
self.assertEqual(
Expand Down Expand Up @@ -709,6 +711,8 @@ def test_parametes_mss_d2d(self):
0.11)
self.assertEqual(
self.parameters.mss_d2d.beam_positioning.service_grid.eligible_sats_margin_from_border, -2.1)
self.assertEqual(
self.parameters.mss_d2d.beam_positioning.service_grid.minimum_service_angle, 12.1)
self.assertEqual(
len(self.parameters.mss_d2d.beam_positioning.service_grid.grid_in_zone.from_countries.country_names), 2)
self.assertEqual(
Expand Down Expand Up @@ -871,6 +875,20 @@ def test_parameters_single_space_station(self):
self.parameters.single_space_station.antenna.gain,
)

self.assertEqual(
self.parameters.single_space_station.antenna.itu_r_s_1528.antenna_gain,
self.parameters.single_space_station.antenna.gain,
)
self.assertEqual(
self.parameters.single_space_station.antenna.itu_r_s_1528.major_minor_axis_ratio,
1.0
)

self.assertEqual(
self.parameters.single_space_station.antenna.itu_r_s_1528.far_out_side_lobe,
-25
)

self.assertEqual(
self.parameters.single_space_station.param_p619.earth_station_alt_m,
self.parameters.single_space_station.geometry.es_altitude,
Expand Down
2 changes: 2 additions & 0 deletions tests/test_antenna_s1528.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def setUp(self):
param.antenna_gain = 39
param.antenna_pattern = "ITU-R S.1528-0"
param.antenna_3_dB_bw = 2
param.major_minor_axis_ratio = 1
param.far_out_side_lobe = 0

param.antenna_l_s = -20
self.antenna20 = AntennaS1528(param)
Expand Down
46 changes: 46 additions & 0 deletions tests/test_topology_imt_mss_dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from sharc.station_manager import StationManager
from sharc.parameters.parameters_orbit import ParametersOrbit
from sharc.support.sharc_geom import CoordinateSystem, lla2ecef
from sharc.satellite.utils.sat_utils import calc_elevation


class TestTopologyImtMssDc(unittest.TestCase):
Expand Down Expand Up @@ -156,6 +157,51 @@ def test_visible_satellites(self):
# oblateness
npt.assert_array_less(min_elevation_angle, xy_plane_elevations)

def test_minimum_service_angle(self):
"""Test minimum visibility angle for service grid service."""
orbit = ParametersOrbit(
n_planes=1,
sats_per_plane=1,
phasing_deg=3.9,
long_asc_deg=18.0,
inclination_deg=54.5,
perigee_alt_km=525,
apogee_alt_km=525,
)
self.coordinate_system.set_reference(0.0, 0.0, 0)

self.params.orbits = [orbit]
self.params.beam_positioning.type = "SERVICE_GRID"
self.params.beam_positioning.service_grid.grid_in_zone.type = "CIRCLE"
self.params.beam_positioning.service_grid.grid_in_zone.circle.center_lat = 0.0
self.params.beam_positioning.service_grid.grid_in_zone.circle.center_lon = 0.0
self.params.beam_positioning.service_grid.grid_in_zone.circle.radius_km = 10 * 111.0
self.params.validate("")

self.imt_mss_dc_topology = TopologyImtMssDc(
self.params, self.coordinate_system)

n_previous_selected = np.inf
for a in [5, 50, 80]:
self.imt_mss_dc_topology.orbit_params.beam_positioning.service_grid.minimum_service_angle = a
self.imt_mss_dc_topology.calculate_coordinates(
random_number_gen=np.random.RandomState(8))

lon_lat_grid = self.params.beam_positioning.service_grid.lon_lat_grid
elev_from_bs = calc_elevation(
lon_lat_grid[1],
self.imt_mss_dc_topology.lat[0],
lon_lat_grid[0],
self.imt_mss_dc_topology.lon[0],
sat_height=self.imt_mss_dc_topology.height[0],
es_height=0.0,
)
n_selected = np.sum(elev_from_bs >= a)
self.assertLess(n_selected, n_previous_selected)
self.assertLess(n_selected, len(elev_from_bs))
self.assertEqual(n_selected, self.imt_mss_dc_topology.num_base_stations)
n_previous_selected = n_selected


if __name__ == '__main__':
unittest.main()