Added observers for tracking data

This commit is contained in:
dario 2023-12-13 11:18:23 +01:00
parent 22d6280123
commit b331e9b27a
7 changed files with 240 additions and 167 deletions

File diff suppressed because one or more lines are too long

View File

@ -131,7 +131,7 @@ class Dataset(Advanceable):
ArrayLike: The current transformation matrix from local to body-fixed coords. ArrayLike: The current transformation matrix from local to body-fixed coords.
""" """
# Get the rotation in the local coordinate system. # Get the rotation in the local coordinate system.
rots = self.__fetch_values(['pitch_l', 'yaw_l', 'roll_l']) rots = self.fetch_values(['pitch_l', 'yaw_l', 'roll_l'])
pitch_l, yaw_l, roll_l = rots[0], rots[1], rots[2] pitch_l, yaw_l, roll_l = rots[0], rots[1], rots[2]
return self.T1(roll_l) @ self.T2(pitch_l - math.pi/2) @ self.T1(-yaw_l) return self.T1(roll_l) @ self.T2(pitch_l - math.pi/2) @ self.T1(-yaw_l)
@ -141,8 +141,8 @@ class Dataset(Advanceable):
Returns: Returns:
ArrayLike: The current transformation matrix from global to local coords. ArrayLike: The current transformation matrix from global to local coords.
""" """
decl = self.__fetch_value('declination') decl = self.fetch_value('declination')
long = self.__fetch_value('longitude') long = self.fetch_value('longitude')
t0 = self.__df['time'].iloc[0] t0 = self.__df['time'].iloc[0]
omega_E = (2*math.pi) / (24*60*60) omega_E = (2*math.pi) / (24*60*60)
@ -189,7 +189,7 @@ class Dataset(Advanceable):
""" """
return self.get_mach_number() > 1 return self.get_mach_number() > 1
def __fetch_value(self, name: str) -> float: def fetch_value(self, name: str) -> float:
"""Get a specific value from the dataframe. """Get a specific value from the dataframe.
Args: Args:
@ -212,7 +212,7 @@ class Dataset(Advanceable):
# Interpolate linearly between the two data points. # Interpolate linearly between the two data points.
return (1 - alpha) * self.__df[name].iloc[self.__idx] + alpha * self.__df[name].iloc[self.__idx + 1] return (1 - alpha) * self.__df[name].iloc[self.__idx] + alpha * self.__df[name].iloc[self.__idx + 1]
def __fetch_values(self, names: List[str]) -> np.array: def fetch_values(self, names: List[str]) -> np.array:
"""Get specific values from the dataframe. """Get specific values from the dataframe.
Args: Args:
@ -221,14 +221,14 @@ class Dataset(Advanceable):
Returns: Returns:
np.array: Returns a numpy array containing the requested values in the same order as in the input list. np.array: Returns a numpy array containing the requested values in the same order as in the input list.
""" """
return np.asarray([self.__fetch_value(name) for name in names]) return np.asarray([self.fetch_value(name) for name in names])
def get_velocity(self) -> float: def get_velocity(self) -> float:
""" """
Returns: Returns:
np.array: Returns the velocity at the current time of the simulation. np.array: Returns the velocity at the current time of the simulation.
""" """
return self.__fetch_value('velocity') return self.fetch_value('velocity')
def get_acceleration(self, frame='FL') -> ArrayLike: def get_acceleration(self, frame='FL') -> ArrayLike:
"""_summary_ """_summary_
@ -239,7 +239,7 @@ class Dataset(Advanceable):
Returns: Returns:
ArrayLike: _description_ ArrayLike: _description_
""" """
acc = self.__fetch_values(['ax', 'ay', 'az']) acc = self.fetch_values(['ax', 'ay', 'az'])
if frame == 'B': if frame == 'B':
return self.launch_rail_to_body() @ acc return self.launch_rail_to_body() @ acc
@ -251,7 +251,7 @@ class Dataset(Advanceable):
Returns: Returns:
ArrayLike: Gets the derivatives in angular velocity across all axes of the rocket. ArrayLike: Gets the derivatives in angular velocity across all axes of the rocket.
""" """
return self.__fetch_values(['omega_X', 'omega_Y', 'omega_Z']) return self.fetch_values(['omega_X', 'omega_Y', 'omega_Z'])
def get_velocity(self, frame='FL') -> ArrayLike: def get_velocity(self, frame='FL') -> ArrayLike:
""" """
@ -262,7 +262,7 @@ class Dataset(Advanceable):
ArrayLike: _description_ ArrayLike: _description_
""" """
vel = self.__fetch_values(['vx', 'vy', 'vz']) vel = self.fetch_values(['vx', 'vy', 'vz'])
if frame == 'B': if frame == 'B':
return self.launch_rail_to_body() @ vel return self.launch_rail_to_body() @ vel
@ -274,63 +274,63 @@ class Dataset(Advanceable):
Returns: Returns:
float: Returns the mach number at the current time of the simulation. float: Returns the mach number at the current time of the simulation.
""" """
return self.__fetch_value('mach') return self.fetch_value('mach')
def get_speed_of_sound(self) -> float: def get_speed_of_sound(self) -> float:
""" """
Returns: Returns:
float: Returns the speed of sound at the current time of the simulation. float: Returns the speed of sound at the current time of the simulation.
""" """
return self.__fetch_value('speedofsound') return self.fetch_value('speedofsound')
def get_rotation_rates(self) -> np.array: def get_rotation_rates(self) -> np.array:
""" """
Returns: Returns:
np.array: Returns the rotation rates at the current time of the simulation. np.array: Returns the rotation rates at the current time of the simulation.
""" """
return self.__fetch_values(['OMEGA_X', 'OMEGA_Y', 'OMEGA_Z']) return self.fetch_values(['OMEGA_X', 'OMEGA_Y', 'OMEGA_Z'])
def get_rotation(self) -> np.array: def get_rotation(self) -> np.array:
""" """
Returns: Returns:
np.array: _description_ np.array: _description_
""" """
return self.__fetch_values(['pitch_l', 'yaw_l', 'roll_l']) return self.fetch_values(['pitch_l', 'yaw_l', 'roll_l'])
def get_temperature(self) -> float: def get_temperature(self) -> float:
""" """
Returns: Returns:
np.array: Returns the temperature at the current time of the simulation. np.array: Returns the temperature at the current time of the simulation.
""" """
return self.__fetch_value('temperature') return self.fetch_value('temperature')
def get_pressure(self) -> float: def get_pressure(self) -> float:
""" """
Returns: Returns:
np.array: Returns the pressure at the current time of the simulation. np.array: Returns the pressure at the current time of the simulation.
""" """
return self.__fetch_value('pressure') return self.fetch_value('pressure')
def get_thrust(self) -> float: def get_thrust(self) -> float:
""" """
Returns: Returns:
float: Returns the thrust value for the current time of the simulation. float: Returns the thrust value for the current time of the simulation.
""" """
return self.__fetch_value('thrust') return self.fetch_value('thrust')
def get_drag(self) -> float: def get_drag(self) -> float:
""" """
Returns: Returns:
float: Returns the drag value for the current time of the simulation. float: Returns the drag value for the current time of the simulation.
""" """
return self.__fetch_value('drag') return self.fetch_value('drag')
def get_mass(self) -> float: def get_mass(self) -> float:
""" """
Returns: Returns:
float: Returns the mass value for the current time of the simulation. float: Returns the mass value for the current time of the simulation.
""" """
return self.__fetch_value('mass') return self.fetch_value('mass')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -0,0 +1,2 @@
from spatz.sensors import Sensor
from spatz.observer.observer import Observer

View File

@ -0,0 +1,33 @@
from typing import Any, List, Dict, AnyStr
from numpy.typing import ArrayLike
from spatz.dataset import Dataset
from spatz.logger import Logger
from spatz.transforms import Transform
class Observer:
def __init__(self, dataset: Dataset, logger: Logger, attributes: List[str]):
self._dataset = dataset
self._logger = logger
self.__attrs = attributes
def _get_name(self) -> AnyStr:
return 'general'
def set_dataset(self, dataset: Dataset):
self._dataset = dataset
def set_logger(self, logger: Logger):
self._logger = logger
def _log(self, name: AnyStr, value: Any):
self._logger.write(name, value, self._get_name())
def __call__(self) -> ArrayLike:
data = self._dataset.fetch_values(self.__attrs)
for attrib, value in zip(self.__attrs, data):
self._log(attrib, value)
return data

View File

@ -22,6 +22,9 @@ class Sensor:
def set_logger(self, logger: Logger): def set_logger(self, logger: Logger):
self._logger = logger self._logger = logger
def set_transforms(self, transforms: List[Transform]):
self._transforms = transforms
def _log(self, name: AnyStr, value: Any): def _log(self, name: AnyStr, value: Any):
self._logger.write(name, value, self._get_name()) self._logger.write(name, value, self._get_name())

View File

@ -8,6 +8,7 @@ from spatz.sensors import Sensor
from spatz.dataset import Dataset from spatz.dataset import Dataset
from spatz.logger import Logger from spatz.logger import Logger
from spatz.sensors import Sensor from spatz.sensors import Sensor
from spatz.observer import Observer
class UniformTimeSteps: class UniformTimeSteps:
@ -89,11 +90,34 @@ class Simulation:
return self return self
def add_sensor(self, sensor, *args, **kwargs) -> Sensor: def add_sensor(self, sensor, *args, **kwargs) -> Sensor:
"""Register a new sensor for this simulation. A registered sensor can be called like a function and returns
the current measurements. The class' constructor arguments have to be given aswell.
Args:
sensor (_type_): A subclass of the abstract Sensor class.
Returns:
Sensor: Returns an object of the provided sensor subclass.
"""
assert issubclass(sensor, Sensor), "Expected a subclass of Sensor." assert issubclass(sensor, Sensor), "Expected a subclass of Sensor."
self.__sensors.append(sensor(self.__dataset, self.__logger, *args, **kwargs)) self.__sensors.append(sensor(self.__dataset, self.__logger, *args, **kwargs))
return self.__sensors[-1] return self.__sensors[-1]
def add_observer(self, attributes: List[str]) -> Observer:
"""Register a new observer for this simulation observing the provided attributes.
Args:
attributes (List[str]): A list of strings describing the attributes to observe.
Returns:
Observer: An observer object which can be called like a function to obtain the desired data.
"""
assert len(attributes) != 0, "Observed attributes list must be nonempty."
self.__sensors.append(Observer(self.__dataset, self.__logger, attributes))
return self.__sensors[-1]

View File

@ -152,6 +152,9 @@ def preprocess_file(path):
df = compute_omegas(df) df = compute_omegas(df)
df = preprocess_rotations(df) df = preprocess_rotations(df)
# Set the altitude to meters
df['altitude'] *= 1000
renaming = { renaming = {
'sonic_velocity': 'speedofsound', 'sonic_velocity': 'speedofsound',
'Time': 'time', 'Time': 'time',