I ran into this while driving the IMU/GNSS sensors in a seeded run, and wanted to check whether it's a known limitation before proposing anything.
Current behaviour
The measurement noise for the sensors (white noise and random-walk bias drift in Sensor/InertialSensor/ScalarSensor, and the position/altitude accuracy in GnssReceiver) is drawn from the process-global NumPy RNG: rocketpy/sensors/sensor.py lines 556 and 561 (InertialSensor.apply_noise) and 766/774 (ScalarSensor.apply_noise), and rocketpy/sensors/gnss_receiver.py lines 93-95 (GnssReceiver.measure).
None of the sensor constructors take a seed, so there's no way to make the noise reproducible without reseeding the global numpy.random state by hand. That has two consequences. First, a simulation that's otherwise reproducible still produces different sensor measurements on every run; the rest of the stochastic stack already seeds explicitly (StochasticModel builds np.random.default_rng(seed) and MonteCarlo derives its streams from a SeedSequence), so the sensors are the one place that escapes it. Second, under multiprocess the child workers inherit a copy of the global RNG state, so sensor noise can come out correlated (or seed-independent) across parallel runs.
Minimal example
import numpy as np
from rocketpy.mathutils.vector_matrix import Vector
from rocketpy import Accelerometer
acc = Accelerometer(sampling_rate=100, noise_density=0.1, noise_variance=1.0)
print(acc.apply_noise(Vector([0, 0, 0]))) # changes every run; no `seed` to pin it
The only way to make this repeatable today is np.random.seed(...) on the global state, which also perturbs every other consumer of the global RNG and isn't safe across processes.
Desired behaviour
Each sensor draws its noise from its own numpy.random.Generator, seeded deterministically, so the noise is reproducible for a given seed and independent of the global RNG. This matches what StochasticModel and MonteCarlo already do.
Proposed solution
Add a seed=None argument to the sensor constructors, store self._rng = np.random.default_rng(seed), and replace the np.random.normal(...) calls with self._rng.normal(...). seed=None keeps the current "random by default" behaviour, but per instance, so it no longer reaches into the global RNG. Reproducibility then lives at construction, since a freshly-seeded sensor replays the same sequence. That matches how StochasticModel and MonteCarlo are already seeded (seed once, draw sequentially).
I've prototyped this with unit tests (reproducible from a seed, decorrelated across sensors, independent of the global RNG, and unchanged for the zero-noise ideal case) and would be glad to open a PR if this direction sounds reasonable. Happy to discuss the API first, for example whether the seed should live on the sensor or be threaded down from add_sensor/the rocket.
I ran into this while driving the IMU/GNSS sensors in a seeded run, and wanted to check whether it's a known limitation before proposing anything.
Current behaviour
The measurement noise for the sensors (white noise and random-walk bias drift in
Sensor/InertialSensor/ScalarSensor, and the position/altitude accuracy inGnssReceiver) is drawn from the process-global NumPy RNG:rocketpy/sensors/sensor.pylines 556 and 561 (InertialSensor.apply_noise) and 766/774 (ScalarSensor.apply_noise), androcketpy/sensors/gnss_receiver.pylines 93-95 (GnssReceiver.measure).None of the sensor constructors take a
seed, so there's no way to make the noise reproducible without reseeding the globalnumpy.randomstate by hand. That has two consequences. First, a simulation that's otherwise reproducible still produces different sensor measurements on every run; the rest of the stochastic stack already seeds explicitly (StochasticModelbuildsnp.random.default_rng(seed)andMonteCarloderives its streams from aSeedSequence), so the sensors are the one place that escapes it. Second, undermultiprocessthe child workers inherit a copy of the global RNG state, so sensor noise can come out correlated (or seed-independent) across parallel runs.Minimal example
The only way to make this repeatable today is
np.random.seed(...)on the global state, which also perturbs every other consumer of the global RNG and isn't safe across processes.Desired behaviour
Each sensor draws its noise from its own
numpy.random.Generator, seeded deterministically, so the noise is reproducible for a given seed and independent of the global RNG. This matches whatStochasticModelandMonteCarloalready do.Proposed solution
Add a
seed=Noneargument to the sensor constructors, storeself._rng = np.random.default_rng(seed), and replace thenp.random.normal(...)calls withself._rng.normal(...).seed=Nonekeeps the current "random by default" behaviour, but per instance, so it no longer reaches into the global RNG. Reproducibility then lives at construction, since a freshly-seeded sensor replays the same sequence. That matches howStochasticModelandMonteCarloare already seeded (seed once, draw sequentially).I've prototyped this with unit tests (reproducible from a seed, decorrelated across sensors, independent of the global RNG, and unchanged for the zero-noise ideal case) and would be glad to open a PR if this direction sounds reasonable. Happy to discuss the API first, for example whether the seed should live on the sensor or be threaded down from
add_sensor/the rocket.