Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
88cd9a2
ENH: class GNSS
MateusStano May 28, 2024
686e665
ENH: gnss prints
MateusStano May 28, 2024
a5c8513
ENH: add params to measure call
MateusStano May 28, 2024
7c0e507
ENH: export data
MateusStano May 28, 2024
ae682cb
ENH: pass env in .measure
MateusStano May 28, 2024
575148a
DOC: gnss attribute docs
MateusStano May 28, 2024
da74430
MNT: units in prints
MateusStano May 28, 2024
c62f475
Merge branch 'enh/sensors-impl' into enh/gps
MateusStano Jul 2, 2024
0f25181
MNT: fix merge errors
MateusStano Jul 8, 2024
0bc461d
TST: add gnss tests
MateusStano Jul 8, 2024
3b46ae2
TST: fix time nodes tests
MateusStano Jul 8, 2024
dd4d63c
DEV: add gnss to sensors testing notebook
MateusStano Jul 8, 2024
137c6ec
Merge branch 'enh/sensors-impl' into enh/gps
MateusStano Jul 8, 2024
f926472
DEV: fix merge errors
MateusStano Jul 8, 2024
d9458a1
MNT: lint and isort
MateusStano Jul 8, 2024
0e1c58e
MNT: black notebooks
MateusStano Jul 12, 2024
0744b79
TST: fix test by inversion of rotation
MateusStano Jul 12, 2024
e6fdaeb
TST: lower rel tolerances and delete duplicated tests
MateusStano Jul 12, 2024
6f741df
MNT: minor fixes
MateusStano Aug 15, 2024
9ce718e
ENH: export sensor data by sensor name
MateusStano Aug 20, 2024
a28e7bd
MNT: use inverted_haversine in GNSS
MateusStano Aug 20, 2024
887e0a9
BUG: bearing unit in inverted_haversine
MateusStano Aug 20, 2024
dc43f27
MNT: pylint
MateusStano Aug 23, 2024
2295609
TST: add test for export with sensor name
MateusStano Aug 23, 2024
015f5ea
MNT: rename `GNSS` class to `GnssReceiver`
Gui-FernandesBR Sep 8, 2024
26859e6
MNT: fix lint
Gui-FernandesBR Sep 8, 2024
6265f05
MNT: rename gnss file
Gui-FernandesBR Sep 8, 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
259 changes: 159 additions & 100 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,7 +37,7 @@
Tail,
TrapezoidalFins,
)
from .sensors import Accelerometer, Barometer, Gyroscope
from .sensors import Accelerometer, Barometer, GnssReceiver, Gyroscope
from .simulation import Flight, MonteCarlo
from .stochastic import (
StochasticEllipticalFins,
Expand Down
13 changes: 5 additions & 8 deletions rocketpy/plots/rocket_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import matplotlib.pyplot as plt
import numpy as np

from rocketpy.mathutils.vector_matrix import Vector
from rocketpy.motors import EmptyMotor, HybridMotor, LiquidMotor, SolidMotor
from rocketpy.rocket.aero_surface import Fins, NoseCone, Tail

Expand Down Expand Up @@ -166,8 +165,6 @@ def thrust_to_weight(self):
lower=0, upper=self.rocket.motor.burn_out_time
)

return None

def draw(self, vis_args=None, plane="xz"):
"""Draws the rocket in a matplotlib figure.

Expand Down Expand Up @@ -204,7 +201,7 @@ def draw(self, vis_args=None, plane="xz"):
"line_width": 1.0,
}

fig, ax = plt.subplots(figsize=(8, 6), facecolor=vis_args["background"])
_, ax = plt.subplots(figsize=(8, 6), facecolor=vis_args["background"])
ax.set_aspect("equal")
ax.grid(True, linestyle="--", linewidth=0.5)

Expand All @@ -217,7 +214,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_sensors(ax, self.rocket.sensors, plane, vis_args)
self._draw_sensors(ax, self.rocket.sensors, plane)

plt.title("Rocket Representation")
plt.xlim()
Expand Down Expand Up @@ -386,7 +383,7 @@ def _draw_motor(self, last_radius, last_x, ax, vis_args):
)

# Get motor patches translated to the correct position
motor_patches = self._generate_motor_patches(total_csys, ax, vis_args)
motor_patches = self._generate_motor_patches(total_csys, ax)

# Draw patches
if not isinstance(self.rocket.motor, EmptyMotor):
Expand All @@ -407,7 +404,7 @@ def _draw_motor(self, last_radius, last_x, ax, vis_args):
self._draw_nozzle_tube(last_radius, last_x, nozzle_position, ax, vis_args)

def _generate_motor_patches(
self, total_csys, ax, vis_args
self, total_csys, ax
): # pylint: disable=unused-argument
"""Generates motor patches for drawing"""
motor_patches = []
Expand Down Expand Up @@ -554,7 +551,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_sensors(self, ax, sensors, plane, vis_args):
def _draw_sensors(self, ax, sensors, plane):
"""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
26 changes: 15 additions & 11 deletions rocketpy/prints/sensors_prints.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@ def identity(self):
self._print_aligned("Name:", self.sensor.name)
self._print_aligned("Type:", self.sensor.__class__.__name__)

def orientation(self):
"""Prints the orientation of the sensor."""
print("\nOrientation:\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 quantization(self):
"""Prints the quantization of the sensor."""
print("\nQuantization:\n")
Expand Down Expand Up @@ -115,3 +104,18 @@ def noise(self):
"Acceleration Sensitivity:",
f"{self.sensor.acceleration_sensitivity} rad/s/g",
)


class _GnssReceiverPrints(_SensorPrints):
"""Class that contains all GnssReceiver prints."""

def accuracy(self):
"""Prints the accuracy of the sensor."""
print("\nAccuracy:\n")
self._print_aligned("Position Accuracy:", f"{self.sensor.position_accuracy} m")
self._print_aligned("Altitude Accuracy:", f"{self.sensor.altitude_accuracy} m")

def all(self):
"""Prints all information of the sensor."""
self.identity()
self.accuracy()
1 change: 1 addition & 0 deletions rocketpy/rocket/parachute.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def __evaluate_trigger_function(self, trigger):
"""This is used to set the triggerfunc attribute that will be used to
interact with the Flight class.
"""
# pylint: disable=unused-argument, function-redefined
# The parachute is deployed by a custom function
if callable(trigger):
# work around for having added sensors to parachute triggers
Expand Down
3 changes: 1 addition & 2 deletions rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

from rocketpy.control.controller import _Controller
from rocketpy.mathutils.function import Function
from rocketpy.mathutils.vector_matrix import Vector, Matrix
from rocketpy.mathutils.vector_matrix import Matrix
from rocketpy.mathutils.vector_matrix import Matrix, Vector
from rocketpy.motors.motor import EmptyMotor
from rocketpy.plots.rocket_plots import _RocketPlots
from rocketpy.prints.rocket_prints import _RocketPrints
Expand Down
1 change: 1 addition & 0 deletions rocketpy/sensors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .accelerometer import Accelerometer
from .barometer import Barometer
from .gnss_receiver import GnssReceiver
from .gyroscope import Gyroscope
from .sensor import InertialSensor, ScalarSensor, Sensor
10 changes: 5 additions & 5 deletions rocketpy/sensors/accelerometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from ..prints.sensors_prints import _InertialSensorPrints
from ..sensors.sensor import InertialSensor

# pylint: disable=too-many-arguments


class Accelerometer(InertialSensor):
"""Class for the accelerometer sensor
Expand Down Expand Up @@ -208,15 +210,13 @@ def measure(self, time, **kwargs):
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.
- environment : Environment
Environment object containing the atmospheric conditions.
"""
u = kwargs["u"]
u_dot = kwargs["u_dot"]
relative_position = kwargs["relative_position"]
gravity = kwargs["gravity"]
gravity = kwargs["environment"].gravity.get_value_opt(u[3])

# Linear acceleration of rocket cdm in inertial frame
gravity = (
Expand Down
10 changes: 3 additions & 7 deletions rocketpy/sensors/barometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,12 @@ def measure(self, time, **kwargs):
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.
- elevation : float
Elevation of the launch site in meters.
- environment : Environment
Environment object containing the atmospheric conditions.
"""
u = kwargs["u"]
relative_position = kwargs["relative_position"]
pressure = kwargs["pressure"]
pressure = kwargs["environment"].pressure

# Calculate the altitude of the sensor
relative_altitude = (Matrix.transformation(u[6:10]) @ relative_position).z
Expand Down
125 changes: 125 additions & 0 deletions rocketpy/sensors/gnss_receiver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import math

import numpy as np

from rocketpy.tools import inverted_haversine

from ..mathutils.vector_matrix import Matrix, Vector
from ..prints.sensors_prints import _GnssReceiverPrints
from .sensor import ScalarSensor


class GnssReceiver(ScalarSensor):
"""Class for the GNSS Receiver sensor.

Attributes
----------
prints : _GnssReceiverPrints
Object that contains the print functions for the sensor.
sampling_rate : float
Sample rate of the sensor in Hz.
position_accuracy : float
Accuracy of the sensor interpreted as the standard deviation of the
position in meters.
altitude_accuracy : float
Accuracy of the sensor interpreted as the standard deviation of the
position in meters.
name : str
The name of the sensor.
measurement : tuple
The measurement of the sensor.
measured_data : list
The stored measured data of the sensor.
"""

units = "°, m"

def __init__(
self,
sampling_rate,
position_accuracy=0,
altitude_accuracy=0,
name="GnssReceiver",
):
"""Initialize the Gnss Receiver sensor.

Parameters
----------
sampling_rate : float
Sample rate of the sensor in Hz.
position_accuracy : float
Accuracy of the sensor interpreted as the standard deviation of the
position in meters. Default is 0.
altitude_accuracy : float
Accuracy of the sensor interpreted as the standard deviation of the
position in meters. Default is 0.
name : str
The name of the sensor. Default is "GnssReceiver".
"""
super().__init__(sampling_rate=sampling_rate, name=name)
self.position_accuracy = position_accuracy
self.altitude_accuracy = altitude_accuracy

self.prints = _GnssReceiverPrints(self)

def measure(self, time, **kwargs):
"""Measure the position of the rocket in latitude, longitude and
altitude.

Parameters
----------
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.
- environment : Environment
Environment object containing the atmospheric conditions.
"""
u = kwargs["u"]
relative_position = kwargs["relative_position"]
lat, lon = kwargs["environment"].latitude, kwargs["environment"].longitude
earth_radius = kwargs["environment"].earth_radius

# Get from state u and add relative position
x, y, z = (Matrix.transformation(u[6:10]) @ relative_position) + Vector(u[0:3])
# Apply accuracy to the position
x = np.random.normal(x, self.position_accuracy)
y = np.random.normal(y, self.position_accuracy)
altitude = np.random.normal(z, self.altitude_accuracy)

# Convert x and y to latitude and longitude
drift = (x**2 + y**2) ** 0.5
bearing = (2 * math.pi - math.atan2(-x, y)) * (180 / math.pi)

# Applies the haversine equation to find final lat/lon coordinates
latitude, longitude = inverted_haversine(lat, lon, drift, bearing, earth_radius)

self.measurement = (latitude, longitude, altitude)
self._save_data((time, *self.measurement))

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
file_format : str
Format of the file to export the values to. Options are "csv" and
"json". Default is "csv".

Returns
-------
None
"""
self._generic_export_measured_data(
filename=filename,
file_format=file_format,
data_labels=("t", "latitude", "longitude", "altitude"),
)
8 changes: 4 additions & 4 deletions rocketpy/sensors/gyroscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from ..prints.sensors_prints import _GyroscopePrints
from ..sensors.sensor import InertialSensor

# pylint: disable=too-many-arguments


class Gyroscope(InertialSensor):
"""Class for the gyroscope sensor
Expand Down Expand Up @@ -210,10 +212,8 @@ def measure(self, time, **kwargs):
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.
- environment : Environment
Environment object containing the atmospheric conditions.
"""
u = kwargs["u"]
u_dot = kwargs["u_dot"]
Expand Down
8 changes: 2 additions & 6 deletions rocketpy/sensors/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rocketpy.mathutils.vector_matrix import Matrix, Vector


# pylint: disable=too-many-statements
class Sensor(ABC):
"""Abstract class for sensors

Expand Down Expand Up @@ -183,27 +184,22 @@ def _save_data_multiple(self, data):
@abstractmethod
def measure(self, time, **kwargs):
"""Measure the sensor data at a given time"""
pass

@abstractmethod
def quantize(self, value):
"""Quantize the sensor measurement"""
pass

@abstractmethod
def apply_noise(self, value):
"""Add noise to the sensor measurement"""
pass

@abstractmethod
def apply_temperature_drift(self, value):
"""Apply temperature drift to the sensor measurement"""
pass

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

def _generic_export_measured_data(self, filename, file_format, data_labels):
"""Export the measured values to a file given the data labels of each
Expand Down Expand Up @@ -572,7 +568,7 @@ def apply_temperature_drift(self, value):


class ScalarSensor(Sensor):
"""Model of a scalar sensor (barometer, GPS, etc.). Scalar sensors are used
"""Model of a scalar sensor (e.g. Barometer). Scalar sensors are used
to measure a single scalar value. The measurements are not affected by the
sensor's orientation in the rocket.

Expand Down
Loading