Creation of the magnetometer sensor - #1190
Conversation
…ire to have effect on different magnetometers, while ensuring efficiency and coherence with library
…s and magnetometer
…ignition wire type
…netometer creation of soft iron
…nction of the nose cone, intoduction of plate in inits modules,
…he radius of the nose_cone
…nd making it more modular.
… methods, and improvement of wire checking
…to represent also method's incorrect parameters, and some docstring improvements
There was a problem hiding this comment.
e and plates? what do those mean?
There was a problem hiding this comment.
In rockets, magnetometers face three magnetic distortions beyond the sensor's intrinsic limitations. These are
-
Soft iron distortion: caused by materials with higher magnetic permeability than vacuum. As a result, the magnetic field lines are forced to pass through these materials, altering the magnetic reading. To account for this magnetic distortion, I created the
Plateclass, which simulates a material sheet that is responsible for this bending of the magnetic field lines. -
Power interference: caused by wires in the avionics bay, which, due to current flow, generate a magnetic field, affecting the magnetic field reading. The power interference is, in turn, divided into communication interference, which comprises the magnetic field generated by wires with constant electron flow; and activation signal interference, which includes only the magnetic field generated by wires which fulfill some activation/pyrotechnic function (such as the one that activates the engine, or the one used to deploy the parachute). To account for this power interference, I created the
Wireclass. -
Hard iron distortion: caused by fixed components in the avionics bay, and it is simulated by a vector addition.
There is additional information about the formulation and usage of the Wire and Plate classes in the PR description and in their docstrings, let me know if you have more questions or if you would like me to clarify something.
# Conflicts: # rocketpy/rocket/rocket.py # tests/unit/sensors/test_sensor.py
Reviewing RocketPy-Team#1190 turned up problems that the test suite could not see, because the only coverage of the plate and wire distortion called Magnetometer.measure by hand with a real Rocket, which is not the object Flight hands it. Flight.__measure_sensors passed `rocket=self`, a Flight. The distortion branches reach for rocket.plates, rocket._communication_wires and rocket._ignition_wires, none of which a Flight has, so any flight of a magnetometer configured with plates or wires died with `AttributeError: 'Flight' object has no attribute 'plates'`. It now passes self.rocket, and a new integration test flies the wires-and-plates fixture so the hand-off itself is covered. The WMM epoch came from datetime.now() at construction, which made the same script produce a different geomagnetic field on different days, threw away the date the flight is actually simulated for, and would have silently extrapolated past the 2030 end of the WMM-2025 coefficients. It is resolved per measurement from Environment.datetime_date instead, warning when the Environment carries no date. _obtain_magnetic_field takes the epoch as an argument so it is a function of its inputs. Magnetometer, Wire and Plate each defined from_dict with no to_dict, so a save/load round trip silently rebuilt them with every distortion back at its default, and the encoder fell back to __dict__ for Wire and Plate, which holds caches keyed by tuples of floats that JSON cannot encode. Writing to_dict surfaced that _validate_soft_iron fell through both of its branches for a serialized matrix, leaving the sensor with no distortion matrix at all and raising no error; it now accepts nested sequences and rejects anything else. The distortion derived from the rocket was never reset between flights, so a second simulation with a different rocket, a moved sensor or an added plate silently reused the first one's matrix. Magnetometer._reset restores the configured baseline. In apply_soft_iron the accumulation sat inside the "not already cached" branch, so a plate whose matrix had been computed before contributed nothing to the total. Also: the plate dipole sum divided by zero without a word when the sensor sat on a plate point; the wire collinearity warning fired once per integration step; and the magnetometer applied temperature drift before noise while the other two inertial sensors do the reverse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ters The branch carried changes that have nothing to do with the magnetometer, which are reverted here so the feature can be reviewed on its own: the regenerated Monte Carlo notebook outputs (mrs.inputs.txt, mrs.outputs.txt), the project-wide napoleon_include_private_with_doc switch in docs/conf.py, the numericalunits import rename, and stray blank-line edits in motor.py, test_tools.py, test_defiance_rocket.py and barometer.py. The barometer's relative_position docstring type is kept, since that one is a correction. docs/requirements.txt asked for wcwidth>=0.8.0. No such release exists, the project has never gone past 0.2.x, so the docs install could not resolve; it goes back to the pinned wcwidth==0.2.13 the rest of that lock file uses. requirements.txt gets pywmm pinned to >=1.1.1 and its trailing newline back. rocket.py had grown past the 3050-line ceiling in .pylintrc. Rather than raise the ceiling, _define_3d_endpoints moves to wire.py as the module level define_wire_endpoints: it never touched self, and it is wire geometry. Also adds the CHANGELOG entries for the feature and for the accelerometer fix, and a test that states what a rotated accelerometer does instead of recomputing the implementation, so the direction of the frame change is actually pinned. ruff, ruff format and pylint are clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #1190 +/- ##
===========================================
- Coverage 91.41% 91.22% -0.20%
===========================================
Files 132 139 +7
Lines 18133 19117 +984
===========================================
+ Hits 16577 17440 +863
- Misses 1556 1677 +121 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
# Conflicts: # CHANGELOG.md
Pull request type
Checklist
black rocketpy/ tests/) has passed locallypytest tests -m slow --runslow) have passed locallyNew behavior
The backbone of this PR is the creation of the magnetometer sensor.
Unlike other sensors, the measured value (magnetic field) is not a result of the flight simulation, as a result I used the WMM (World Magnetic Model) to obtain the magnetic field, and use frame changes to adjust the magnetic field reading to the sensor position.
When it comes to the noise and distortions included we can divide them into:
Magnetic distortion:
To represent the magnetic distortions (hard iron distortion, soft iron distortion and power interference) I created auxiliary classes
PlateandWire. Their distortions are applied in the Body Axis Coordinate System frame. Moreover, print and plot methods to validate their geometric position and its characteristics have been included together with new methods to satisfy their physical requirements.Sensor Noise
The noise related to the apparatus limitations has been covered using the existing
InertialSensorandSensornoise model and included in the measured value when the magnetic field is in the sensor frame.Furthermore, the documentation has been improved to include the new features and testing of every new element has been performed successfully.
Breaking change
Additional information
This PR requires pywmm Python library to evaluate the World Magnetic Model (WMM). It has been added to requirements.txt.
A change in the accelerometer sensor has been made, due to existing coordinate transformation error.
I opted for a software architecture that raises error when input parameters are incorrect because this approach allows users to know the cause of the error rather than seeing the error in an inner layer of the code. The obvious disadvantage is that for personal/private usage of the library there is no room for other parameters if desired; nonetheless, I have accounted for these having more flexible arguments. This is illustrated with the
ignition_wire_type, which allows any type of strings, so that It can be used for HIL testing.One of the functions implemented that were required to ensure a physically accurate model was the
general_radiusmethod. This method belongs to the rocket and returns the radius of the rocket, without the fins, for any value along the longitudinal axis. Also methods for adding plates and wires to the rocket have been implementedadd_wireandadd_plate.The distortion created by wires is calculated using the Biot-Savart expression for finite wire, while the soft iron distortion modeled by the Plates is the sum of the distortions of the points that form the plate.
The current model supports plates that turns around the rocket, are bigger than the size of the rocket, have radius changes. All of these plate geometry characteristics are illustrated in the following image:
Other images that illustrate the creation of the plate are the following


Focusing on code efficiency, the calculation of the magnetic distortion (by the wire, and the plate) is calculated once, when it is needed, and then stored in a dictionary with keys the position for which it has been calculated and value the magnetic field or soft iron distortion matrix computed.
Aiming to have a coherent naming style and better readability, almost all the variables/attributes that start with _ are Vector instances.
Plots of all plates and wires attached to the rocket have also been implemented in
_RocketPlot(draw_wires,draw_plates). Due to code repetition in this class, a function_rocket_shape_plotthat creates the outline of the rocket has been added.Example of
draw_wiresSupporting features were included in
sensor.py,nose_cone.py,tail.py,components.py....The user defined coordinate system orientation and the center of dry mass are required for the changes in coordinate systems in
WireandPlateand the rocket instance to which they are attached is necessary for the corresponding plotting associated classes. As a result, the_rocket_belongingmethod is implemented inWireandPlateand called when added to the rocket withadd_wireadd_plate. I am aware that cross referencing is not the best software practice, and it reduces the conceptual understanding of the inner library; however, I find it the best solution for this necessity.All plotting methods have been implemented using Matplotlib
The test "tests/unit/environment/test_environment_analysis.py::test_values" failed during local test, yet it is an atmospheric test unrelated to the new features. Outputs of Monte Carlo testing
.../monte_carlo_analysis/monte_carlo_analysis_outputs/mrs.inputs.txtand.../monte_carlo_analysis/monte_carlo_analysis_outputs/mrs.outputs.txthave been automatically included in the current version.