Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b4b51a4
ENH: add pressure to .measure params
MateusStano May 3, 2024
4c0fbf9
ENH: add InertialSensors and ScalarSensors
MateusStano May 3, 2024
6c4229d
ENH: add Barometer class
MateusStano May 3, 2024
9995904
BUG: fix drawing for scalar sensors
MateusStano May 3, 2024
354e681
ENH: barometer prints
MateusStano May 3, 2024
1f761c0
DOC: change docs for scalar sensors
MateusStano May 4, 2024
17feded
ENH: add barometer export data
MateusStano May 5, 2024
faf098a
BUG: fix scalars sensors prints
MateusStano May 5, 2024
960b1c3
TST: add barometers to tests
MateusStano May 5, 2024
f535e0f
DEV: update sensors testing
MateusStano May 5, 2024
00cf0c2
Merge remote-tracking branch 'origin/enh/sensors-impl' into enh/barom…
MateusStano May 21, 2024
1e36391
TST: merge tests
MateusStano May 22, 2024
f78e764
ENH: inherited export method
MateusStano May 22, 2024
fbaac53
TST: improve export data tests
MateusStano May 22, 2024
cdb54b1
TST: Refactor sensor tests and export method
MateusStano May 23, 2024
612176d
TST: fix fixture names
MateusStano May 23, 2024
0bd0f51
BUG: duplicate IntertialSensors
MateusStano May 24, 2024
332f3a9
TST: calisto_sensors to calisto_with_sensors
MateusStano Jun 3, 2024
b882579
MNT: isort
MateusStano Jun 3, 2024
5d5f9e9
MNT: remove type docs
MateusStano Jun 3, 2024
f913f86
ENH: move export_sensor_measured_data to tools.py
MateusStano Jun 3, 2024
3558eff
MNT: pylint fixes
MateusStano Jun 3, 2024
7e419b0
ENH: simplify sensors prints
MateusStano Jun 3, 2024
4e5ad4e
MNT: rename sensors classes
MateusStano Jun 3, 2024
4a2eb07
MNT: sensor.py rename
MateusStano Jun 3, 2024
642e1b3
DOC: improve inertialsensor and scalar sensor doc
MateusStano Jun 3, 2024
773ec59
MNT: sensors to sensor rename imports
MateusStano Jun 3, 2024
df02bb4
TST: format argument
MateusStano Jun 3, 2024
332b477
ENH: move generic_export_data back to Sensor class
MateusStano Jun 6, 2024
1d5e769
ENH: rename test files
MateusStano Jun 6, 2024
7f0fe70
ENH: change from celsius to kelvin
MateusStano Jun 13, 2024
0b779f2
TST: fix celsius to kelvin convertion
MateusStano Jun 13, 2024
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
416 changes: 346 additions & 70 deletions docs/notebooks/sensors_testing.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion rocketpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@
Tail,
TrapezoidalFins,
)
from .sensors import Accelerometer, Gyroscope, Sensors
from .sensors import Accelerometer, Barometer, Gyroscope
from .simulation import Flight
1 change: 1 addition & 0 deletions rocketpy/control/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def __init_controller_function(self, controller_function):
sig = signature(controller_function)
if len(sig.parameters) == 6:

# pylint: disable=unused-argument
def new_controller_function(
time,
sampling_rate,
Expand Down
31 changes: 16 additions & 15 deletions rocketpy/plots/rocket_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def draw(self, vis_args=None, plane="xz"):
self._draw_motor(last_radius, last_x, ax, vis_args)
self._draw_rail_buttons(ax, vis_args)
self._draw_center_of_mass_and_pressure(ax)
self._draw_sensor(ax, self.rocket.sensors, plane, vis_args)
self._draw_sensors(ax, self.rocket.sensors, plane, vis_args)

plt.title("Rocket Representation")
plt.xlim()
Expand Down Expand Up @@ -555,7 +555,7 @@ def _draw_center_of_mass_and_pressure(self, ax):
cp, 0, label="Static Center of Pressure", color="red", s=10, zorder=10
)

def _draw_sensor(self, ax, sensors, plane, vis_args):
def _draw_sensors(self, ax, sensors, plane, vis_args):
"""Draw the sensor as a small thick line at the position of the sensor,
with a vector pointing in the direction normal of the sensor. Get the
normal vector from the sensor orientation matrix."""
Expand Down Expand Up @@ -591,19 +591,20 @@ def _draw_sensor(self, ax, sensors, plane, vis_args):
zorder=10,
label=sensor.name,
)
ax.quiver(
x_pos,
y_pos,
normal_x,
normal_y,
color=colors[(i + 1) % len(colors)],
scale_units="xy",
angles="xy",
minshaft=2,
headwidth=2,
headlength=4,
zorder=10,
)
if abs(sensor.normal_vector) != 0:
ax.quiver(
x_pos,
y_pos,
normal_x,
normal_y,
color=colors[(i + 1) % len(colors)],
scale_units="xy",
angles="xy",
minshaft=2,
headwidth=2,
headlength=4,
zorder=10,
)

def all(self):
"""Prints out all graphs available about the Rocket. It simply calls
Expand Down
57 changes: 32 additions & 25 deletions rocketpy/prints/sensors_prints.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod
from abc import ABC


class _SensorsPrints(ABC):
class _SensorPrints(ABC):
def __init__(self, sensor):
self.sensor = sensor
self.units = sensor.units
Expand Down Expand Up @@ -32,14 +32,14 @@ def quantization(self):
print("\nQuantization:\n")
self._print_aligned(
"Measurement Range:",
f"{self.sensor.measurement_range[0]} to {self.sensor.measurement_range[1]} ({self.units})",
f"{self.sensor.measurement_range[0]} "
+ f"to {self.sensor.measurement_range[1]} ({self.units})",
)
self._print_aligned("Resolution:", f"{self.sensor.resolution} {self.units}/LSB")

@abstractmethod
def noise(self):
"""Prints the noise of the sensor."""
pass
self._general_noise()

def _general_noise(self):
"""Prints the noise of the sensor."""
Expand All @@ -62,45 +62,52 @@ def _general_noise(self):
"Constant Bias:", f"{self.sensor.constant_bias} {self.units}"
)
self._print_aligned(
"Operating Temperature:", f"{self.sensor.operating_temperature} °C"
)
self._print_aligned(
"Temperature Bias:", f"{self.sensor.temperature_bias} {self.units}/°C"
"Operating Temperature:", f"{self.sensor.operating_temperature} K"
)
self._print_aligned(
"Temperature Scale Factor:", f"{self.sensor.temperature_scale_factor} %/°C"
"Temperature Bias:", f"{self.sensor.temperature_bias} {self.units}/K"
)
self._print_aligned(
"Cross Axis Sensitivity:", f"{self.sensor.cross_axis_sensitivity} %"
"Temperature Scale Factor:", f"{self.sensor.temperature_scale_factor} %/K"
)

def all(self):
"""Prints all information of the sensor."""
self.identity()
self.orientation()
self.quantization()
self.noise()


class _AccelerometerPrints(_SensorsPrints):
"""Class that contains all accelerometer prints."""
class _InertialSensorPrints(_SensorPrints):

def __init__(self, accelerometer):
"""Initialize the class."""
super().__init__(accelerometer)
def orientation(self):
"""Prints the orientation of the sensor."""
print("\nOrientation of the Sensor:\n")
self._print_aligned("Orientation:", self.sensor.orientation)
self._print_aligned("Normal Vector:", self.sensor.normal_vector)
print("Rotation Matrix:")
for row in self.sensor.rotation_matrix:
value = " ".join(f"{val:.2f}" for val in row)
value = [float(val) for val in value.split()]
self._print_aligned("", value)

def noise(self):
"""Prints the noise of the sensor."""
self._general_noise()
def _general_noise(self):
super()._general_noise()
self._print_aligned(
"Cross Axis Sensitivity:", f"{self.sensor.cross_axis_sensitivity} %"
)

def all(self):
"""Prints all information of the sensor."""
self.identity()
self.orientation()
self.quantization()
self.noise()


class _GyroscopePrints(_SensorsPrints):
class _GyroscopePrints(_InertialSensorPrints):
"""Class that contains all gyroscope prints."""

def __init__(self, gyroscope):
"""Initialize the class."""
super().__init__(gyroscope)

def noise(self):
"""Prints the noise of the sensor."""
self._general_noise()
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def __init__(
self.thrust_eccentricity_y = 0
self.thrust_eccentricity_x = 0

# Parachute, Aerodynamic, Buttons, Controllers, Sensors data initialization
# Parachute, Aerodynamic, Buttons, Controllers, Sensor data initialization
self.parachutes = []
self._controllers = []
self.air_brakes = []
Expand Down
3 changes: 2 additions & 1 deletion rocketpy/sensors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .accelerometer import Accelerometer
from .barometer import Barometer
from .gyroscope import Gyroscope
from .sensors import Sensors
from .sensor import InertialSensor, ScalarSensor, Sensor
119 changes: 44 additions & 75 deletions rocketpy/sensors/accelerometer.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import json

import numpy as np

from ..mathutils.vector_matrix import Matrix, Vector
from ..prints.sensors_prints import _AccelerometerPrints
from ..sensors.sensors import Sensors
from ..prints.sensors_prints import _InertialSensorPrints
from ..sensors.sensor import InertialSensor


class Accelerometer(Sensors):
class Accelerometer(InertialSensor):
"""Class for the accelerometer sensor

Attributes
----------
consider_gravity : bool
Whether the sensor considers the effect of gravity on the acceleration.
prints : _AccelerometerPrints
prints : _InertialSensorPrints
Object that contains the print functions for the sensor.
sampling_rate : float
Sample rate of the sensor in Hz.
Expand All @@ -35,11 +33,11 @@ class Accelerometer(Sensors):
constant_bias : float, list
The constant bias of the sensor in m/s^2.
operating_temperature : float
The operating temperature of the sensor in degrees Celsius.
The operating temperature of the sensor in Kelvin.
temperature_bias : float, list
The temperature bias of the sensor in m/s^2/°C.
The temperature bias of the sensor in m/s^2/K.
temperature_scale_factor : float, list
The temperature scale factor of the sensor in %/°C.
The temperature scale factor of the sensor in %/K.
cross_axis_sensitivity : float
The cross axis sensitivity of the sensor in percentage.
name : str
Expand Down Expand Up @@ -145,15 +143,16 @@ def __init__(
is applied to all axes. The values of each axis can be set
individually by passing a list of length 3.
operating_temperature : float, optional
The operating temperature of the sensor in degrees Celsius. At 25°C,
the temperature bias and scale factor are 0. Default is 25.
The operating temperature of the sensor in Kelvin.
At 298.15 K (25 °C), the sensor is assumed to operate ideally, no
temperature related noise is applied. Default is 298.15.
temperature_bias : float, list, optional
The temperature bias of the sensor in m/s^2/°C. Default is 0,
The temperature bias of the sensor in m/s^2/K. Default is 0,
meaning no temperature bias is applied. If a float or int is given,
the same temperature bias is applied to all axes. The values of each
axis can be set individually by passing a list of length 3.
temperature_scale_factor : float, list, optional
The temperature scale factor of the sensor in %/°C. Default is 0,
The temperature scale factor of the sensor in %/K. Default is 0,
meaning no temperature scale factor is applied. If a float or int is
given, the same temperature scale factor is applied to all axes. The
values of each axis can be set individually by passing a list of
Expand Down Expand Up @@ -192,29 +191,38 @@ def __init__(
name=name,
)
self.consider_gravity = consider_gravity
self.prints = _AccelerometerPrints(self)
self.prints = _InertialSensorPrints(self)

def measure(self, t, u, u_dot, relative_position, gravity, *args):
def measure(self, time, **kwargs):
"""Measure the acceleration of the rocket

Parameters
----------
t : float
Current time
u : list
State vector of the rocket
u_dot : list
Derivative of the state vector of the rocket
relative_position : Vector
Position of the sensor relative to the rocket cdm
gravity : float
Acceleration due to gravity
time : float
Current time in seconds.
kwargs : dict
Keyword arguments dictionary containing the following keys:
- u : np.array
State vector of the rocket.
- u_dot : np.array
Derivative of the state vector of the rocket.
- relative_position : np.array
Position of the sensor relative to the rocket center of mass.
- gravity : float
Gravitational acceleration in m/s^2.
- pressure : Function
Atmospheric pressure profile as a function of altitude in Pa.
"""
u = kwargs["u"]
u_dot = kwargs["u_dot"]
relative_position = kwargs["relative_position"]
gravity = kwargs["gravity"]

# Linear acceleration of rocket cdm in inertial frame
gravity = (
Vector([0, 0, -gravity]) if self.consider_gravity else Vector([0, 0, 0])
)
a_I = Vector(u_dot[3:6]) + gravity
inertial_acceleration = Vector(u_dot[3:6]) + gravity

# Vector from rocket cdm to sensor in rocket frame
r = relative_position
Expand All @@ -225,7 +233,7 @@ def measure(self, t, u, u_dot, relative_position, gravity, *args):

# Measured acceleration at sensor position in inertial frame
A = (
a_I
inertial_acceleration
+ Vector.cross(omega_dot, r)
+ Vector.cross(omega, Vector.cross(omega, r))
)
Expand All @@ -241,64 +249,25 @@ def measure(self, t, u, u_dot, relative_position, gravity, *args):
A = self.quantize(A)

self.measurement = tuple([*A])
self._save_data((t, *A))
self._save_data((time, *A))

def export_measured_data(self, filename, format="csv"):
"""
Export the measured values to a file
def export_measured_data(self, filename, file_format="csv"):
"""Export the measured values to a file

Parameters
----------
filename : str
Name of the file to export the values to
format : str
file_format : str
Format of the file to export the values to. Options are "csv" and
"json". Default is "csv".

Returns
-------
None
"""
if format.lower() not in ["json", "csv"]:
raise ValueError("Invalid format")
if format.lower() == "csv":
# if sensor has been added multiple times to the simulated rocket
if isinstance(self.measured_data[0], list):
print("Data saved to", end=" ")
for i, data in enumerate(self.measured_data):
with open(filename + f"_{i+1}", "w") as f:
f.write("t,ax,ay,az\n")
for t, ax, ay, az in data:
f.write(f"{t},{ax},{ay},{az}\n")
print(filename + f"_{i+1},", end=" ")
else:
with open(filename, "w") as f:
f.write("t,ax,ay,az\n")
for t, ax, ay, az in self.measured_data:
f.write(f"{t},{ax},{ay},{az}\n")
print(f"Data saved to {filename}")
return
if format.lower() == "json":
if isinstance(self.measured_data[0], list):
print("Data saved to", end=" ")
for i, data in enumerate(self.measured_data):
dict = {"t": [], "ax": [], "ay": [], "az": []}
for t, ax, ay, az in data:
dict["t"].append(t)
dict["ax"].append(ax)
dict["ay"].append(ay)
dict["az"].append(az)
with open(filename + f"_{i+1}", "w") as f:
json.dump(dict, f)
print(filename + f"_{i+1},", end=" ")
else:
dict = {"t": [], "ax": [], "ay": [], "az": []}
for t, ax, ay, az in self.measured_data:
dict["t"].append(t)
dict["ax"].append(ax)
dict["ay"].append(ay)
dict["az"].append(az)
with open(filename, "w") as f:
json.dump(dict, f)
print(f"Data saved to {filename}")
return
self._generic_export_measured_data(
filename=filename,
file_format=file_format,
data_labels=("t", "ax", "ay", "az"),
)
Loading