Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Smit committed Feb 8, 2024
1 parent 4177086 commit 28ed261
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 159 deletions.
1 change: 0 additions & 1 deletion paseos/actors/actor_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from dotmap import DotMap
import pykep as pk
from skyfield.api import wgs84
import math

from .base_actor import BaseActor
from .spacecraft_actor import SpacecraftActor
Expand Down
60 changes: 32 additions & 28 deletions paseos/communication/link_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..actors.base_actor import BaseActor
import math


class LinkModel:
"""This class defines a link model, containing one transmitter and one receiver."""

Expand All @@ -18,27 +19,29 @@ class LinkModel:
_current_line_of_sight = False
_current_distance = 0
_current_elevation_angle = 0

def __init__(
self,
transmitter_actor: BaseActor,
transmitter_model: TransmitterModel,
receiver_actor: BaseActor,
receiver_model: ReceiverModel,
frequency: float
self,
transmitter_actor: BaseActor,
transmitter_model: TransmitterModel,
receiver_actor: BaseActor,
receiver_model: ReceiverModel,
frequency: float
) -> None:
"""Initializes the model.
Args:
transmitter_actor (BaseActor): The transmitter in this link.
receiver (ReceiverModel): The receiver in this link.
transmitter_actor (BaseActor): the transmitter actor in this link.
transmitter_model (TransmitterModel): the transmitter device model in this link.
receiver_actor (BaseActor): the receiver actor in this link.
receiver_model (ReceiverModel): the receiver device model.
"""
# assert isinstance(transmitter, TransmitterModel), "A transmitter is required for this link."
assert isinstance(receiver_model, ReceiverModel), "A receiver is required for this link."

logger.debug("Initializing link model.")
self.c = 299792458
self.wavelength = self.c / frequency # in m
self.wavelength = self.c / frequency # in m
self.transmitter_actor = transmitter_actor
self.transmitter = transmitter_model
self.receiver_actor = receiver_actor
Expand All @@ -47,30 +50,31 @@ def __init__(
self._line_of_sight_history = []
self._distance_history = []
self._elevation_angle_history = []
def get_path_loss(self, slant_range):

def get_path_loss(self, slant_range: float) -> float:
"""Gets the path loss (free space loss) for a link.
Args:
slant_range (int): The slant range of the link, in meters
slant_range (float): The slant range of the link, in meters
Returns:
The path loss (free space loss) in dB
"""
assert slant_range > 0, "Slant range needs to be higher than 0 meters"

return 20 * math.log10(4 * math.pi * slant_range / self.wavelength)
return 20 * math.log10(4 * math.pi * slant_range / self.wavelength)

def set_bitrate(self, bitrate: float):
def set_bitrate(self, bitrate: float) -> None:
"""Sets the bitrate of this link for a certain epoch.
Args:
bitrate (float): The bitrate of this link, in bps
"""
self._current_bitrate = bitrate

def set_line_of_sight(self, state: bool):
"""Sets the line of sight of this link for a certain epoch, if there is a line of sight, the transmitter is set to active.

def set_line_of_sight(self, state: bool) -> None:
"""Sets the line of sight of this link for a certain epoch,
if there is a line of sight, the transmitter is set to active.
Args:
state (bool): The current line of sight state
Expand All @@ -81,41 +85,41 @@ def set_line_of_sight(self, state: bool):
else:
self.transmitter.set_active(False)

def set_distance(self, distance: float):
def set_distance(self, distance: float) -> None:
"""Sets the distance of this link for a certain epoch.
Args:
distance (float): The slant range of the link, in meters
"""
self._current_distance = distance
def set_elevation_angle(self, angle: float):

def set_elevation_angle(self, angle: float) -> None:
"""Sets the elevation angle of this link for a certain epoch.
Args:
angle (float): The elevation angle, in degrees
"""
self._current_elevation_angle = angle
def save_state(self):

def save_state(self) -> None:
"""Saves the state of this link."""
self._bitrate_history.append(self._current_bitrate)
self._line_of_sight_history.append(self._current_line_of_sight)
self._distance_history.append(self._current_distance)
self._elevation_angle_history.append(self._current_elevation_angle)

@property
def bitrate_history(self):
return self._bitrate_history

@property
def line_of_sight_history(self):
return self._line_of_sight_history

@property
def distance_history(self):
return self._distance_history

@property
def elevation_angle_history(self):
return self._elevation_angle_history
return self._elevation_angle_history
49 changes: 26 additions & 23 deletions paseos/communication/optical_link_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
from ..actors.base_actor import BaseActor
import math


class OpticalLinkModel(LinkModel):
"""This class defines an ptical link model, containing one transmitter and one receiver."""
"""This class defines an optical link model, containing one transmitter and one receiver."""

def __init__(
self,
transmitter_actor: BaseActor,
transmitter_device_name: str,
receiver_actor: BaseActor,
receiver_device_name: str
self,
transmitter_actor: BaseActor,
transmitter_device_name: str,
receiver_actor: BaseActor,
receiver_device_name: str
) -> None:
"""Initializes the model.
Expand All @@ -25,44 +26,46 @@ def __init__(
receiver_actor (BaseActor): the receiver in this link.
receiver_device_name (str): the name of the receiver device.
"""
self.wavelength = 1550E-9 # in m
self.wavelength = 1550E-9 # in m

# Get the transmitter and receiver models from the actor
transmitter = transmitter_actor.get_transmitter(transmitter_device_name)
receiver = receiver_actor.get_receiver(receiver_device_name)

super().__init__(transmitter_actor, transmitter, receiver_actor, receiver, frequency=299792458/self.wavelength)

assert isinstance(transmitter, OpticalTransmitterModel), "An optical transmitter is required for this optical link."
super().__init__(transmitter_actor, transmitter, receiver_actor, receiver,
frequency=299792458 / self.wavelength)

assert isinstance(transmitter, OpticalTransmitterModel), ("An optical transmitter is required "
"for this optical link.")
assert isinstance(receiver, OpticalReceiverModel), "An optical receiver is required for this optical link."

logger.debug("Initializing optical link model.")
self.required_BER = 10E-3

self.modulation_scheme = "OOK"
self.required_s_n_margin = 3 # in dB
self.required_s_n_margin = 3 # in dB

self.receiver.set_gain(self.wavelength)
self.transmitter.set_gain()
def get_path_loss(self, slant_range):

def get_path_loss(self, slant_range: float) -> float:
"""Gets the path loss (free space loss) for a link.
Args:
slant_range (int): The slant range of the link, in meters
slant_range (float): The slant range of the link, in meters
Returns:
The path loss (free space loss) in dB
"""
assert slant_range > 0, "Slant range needs to be higher than 0 meters"

return 20 * math.log10(4 * math.pi * slant_range / self.wavelength)
def get_bitrate(self, slant_range, min_elevation_angle):
return 20 * math.log10(4 * math.pi * slant_range / self.wavelength)

def get_bitrate(self, slant_range: float, min_elevation_angle: float) -> float:
"""Gets the bitrate for a link based on current slant range and minimum elevation angle.
Args:
slant_range (int): The slant range of the link, in meters
slant_range (float): The slant range of the link, in meters
min_elevation_angle (float): The minimum elevation angle for this receiver, in degrees
Returns:
Expand All @@ -76,11 +79,11 @@ def get_bitrate(self, slant_range, min_elevation_angle):
self.signal_at_receiver = self.transmitter.EIRP - self.total_channel_loss

self.received_signal_power_with_gain = self.signal_at_receiver + self.receiver.antenna_gain - self.receiver.line_losses
self.received_signal_power_with_margin = self.received_signal_power_with_gain - self.required_s_n_margin #dBm
self.received_signal_power_with_margin = 10**(self.received_signal_power_with_margin / 10) * 1E-3 #nW
self.received_signal_power_with_margin = self.received_signal_power_with_gain - self.required_s_n_margin # dBm
self.received_signal_power_with_margin = 10 ** (self.received_signal_power_with_margin / 10) * 1E-3 # nW
bitrate = self.received_signal_power_with_margin / 250 * 1550E-9 / 6.626E-34 / self.c

if bitrate < 0:
bitrate = 0
return bitrate

return bitrate
21 changes: 12 additions & 9 deletions paseos/communication/optical_receiver_model.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
from loguru import logger
from .receiver_model import ReceiverModel
from ..utils.gain_calc import calc_optical_gain_from_wavelength_diameter


class OpticalReceiverModel(ReceiverModel):
"""This class defines an optical receiver model."""

def __init__(
self,
line_losses: int,
antenna_diameter: int = 0,
antenna_gain: int = 0
self,
line_losses: float,
antenna_diameter: float = 0,
antenna_gain: float = 0
) -> None:
"""Initializes the model.
Args:
line_losses (int): The line losses of the receiver, in dB.
antenna_diameter (int): The diameter of the antenna, in m. Either this or the gain needs to be given.
antenna_gain (int): The gain of the antenna, either this or the diameter needs to be given so that gain can be determined.
line_losses (float): The line losses of the receiver, in dB.
antenna_diameter (float): The diameter of the antenna, in m. Either this or the gain needs to be given.
antenna_gain (float): The gain of the antenna, either this or the diameter needs to be given so that
gain can be determined.
"""

super().__init__(line_losses, antenna_diameter, antenna_gain)
logger.debug("Initializing optical receiver model.")

def set_gain(self, wavelength):
def set_gain(self, wavelength: float = 0) -> None:
"""Sets gain for a receiver, based on the given gain, or antenna diameter and wavelength.
Args:
wavelength (int): The wavelength of the link, in meters
"""
if self.antenna_gain == 0:
self.antenna_gain = calc_optical_gain_from_wavelength_diameter(wavelength, self.antenna_diameter, 1)
self.antenna_gain = calc_optical_gain_from_wavelength_diameter(wavelength, self.antenna_diameter, 1)
29 changes: 13 additions & 16 deletions paseos/communication/optical_transmitter_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,34 @@ class OpticalTransmitterModel(TransmitterModel):

def __init__(
self,
input_power: int,
input_power: float,
power_efficiency: float,
antenna_efficiency: float,
line_losses: int,
point_losses: int,
antenna_gain: int = 0,
antenna_diameter: int = 0,
fwhm: int = 0
line_losses: float,
point_losses: float,
antenna_gain: float = 0,
antenna_diameter: float = 0,
fwhm: float = 0
) -> None:
"""Initializes the model.
Args:
input_power (int): Input power into the signal amplifier, in W.
input_power (float): Input power into the signal amplifier, in W.
power_efficiency (float): The power efficiency of the signal amplifier, determines the output power.
antenna_efficiency (float): The efficiency of the antenna.
line_losses (int): The line losses of the transmitter, in dB.
point_losses (int): The pointing losses of the transmitter, in dB.
antenna_gain (int): The gain of the antenna, either this or the diameter needs to be given so that gain
line_losses (float): The line losses of the transmitter, in dB.
point_losses (float): The pointing losses of the transmitter, in dB.
antenna_gain (float): The gain of the antenna, either this or the diameter needs to be given so that gain
can be determined.
antenna_diameter (int): The diameter of the antenna, in m. Either this or the gain needs to be given.
fwhm (int): full width at half maximum, in radians.
antenna_diameter (float): The diameter of the antenna, in m. Either this or the gain needs to be given.
fwhm (float): full width at half maximum, in radians.
"""

logger.debug("Initializing optical transmitter model.")
super().__init__(input_power, power_efficiency, antenna_efficiency, line_losses, point_losses, antenna_gain,
antenna_diameter)
assert antenna_gain > 0 or antenna_diameter > 0 or fwhm > 0, ("Antenna gain or antenna diameter or "
"FWHM needs to be higher than 0.")
# assert (
# antenna_diameter > 0 and antenna_gain > 0 and fwhm > 0), ("Only set one of antenna gain, "
# "antenna diameter, and FWHM not multiple.")
assert sum(param != 0 for param in (antenna_diameter, antenna_gain,
fwhm)) <= 1, ("Only set one of antenna gain, "
"antenna diameter, and FWHM not multiple.")
Expand All @@ -49,7 +46,7 @@ def __init__(
self.output_power = 10 * math.log10(input_power * power_efficiency * 1000) # dBm
self.FWHM = fwhm

def set_gain(self):
def set_gain(self) -> None:
"""Sets gain for a transmitter, based on the given gain, or antenna diameter and wavelength.
"""
if self.antenna_gain == 0:
Expand Down
10 changes: 5 additions & 5 deletions paseos/communication/radio_link_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ def __init__(
self.receiver.set_gain(self.wavelength)
self.transmitter.set_gain(self.wavelength)

def get_path_loss(self, slant_range) -> float:
def get_path_loss(self, slant_range: float) -> float:
"""Gets the path loss (free space loss) for a link.
Args:
slant_range (int): The slant range of the link, in meters
slant_range (float): The slant range of the link, in meters
Returns:
The path loss (free space loss) in dB
Expand All @@ -67,7 +67,7 @@ def get_path_loss(self, slant_range) -> float:

return 20 * math.log10(4 * math.pi * slant_range / self.wavelength)

def get_max_atmospheric_loss(self, min_elevation_angle) -> float:
def get_max_atmospheric_loss(self, min_elevation_angle: float) -> float:
"""Gets the maximal atmospheric loss for a link.
Args:
Expand All @@ -80,11 +80,11 @@ def get_max_atmospheric_loss(self, min_elevation_angle) -> float:

return self.zenith_atmospheric_attenuation / math.sin(min_elevation_angle * math.pi / 180)

def get_bitrate(self, slant_range, min_elevation_angle) -> float:
def get_bitrate(self, slant_range: float, min_elevation_angle: float) -> float:
"""Gets the bitrate for a link based on current slant range and minimum elevation angle.
Args:
slant_range (int): The slant range of the link, in meters
slant_range (float): The slant range of the link, in meters
min_elevation_angle (float): The minimum elevation angle for this receiver, in degrees
Returns:
Expand Down
Loading

0 comments on commit 28ed261

Please sign in to comment.