1
+ """Unit tests for FlightDataExporter class.
2
+
3
+ This module tests the data export functionality of the FlightDataExporter class,
4
+ which exports flight simulation data to various formats (CSV, JSON, KML).
5
+ """
6
+
1
7
import json
8
+
9
+ import numpy as np
10
+
2
11
from rocketpy .simulation import FlightDataExporter
3
12
4
13
5
- def test_export_data_writes_csv_header (flight_calisto , tmp_path ):
6
- """Expect: direct exporter writes a CSV with a header containing 'Time (s)'."""
7
- out = tmp_path / "out.csv"
8
- FlightDataExporter (flight_calisto ).export_data (str (out ))
9
- text = out .read_text (encoding = "utf-8" )
10
- assert "Time (s)" in text
14
+ def test_export_pressures_writes_csv_rows (flight_calisto_robust , tmp_path ):
15
+ """Test that export_pressures writes CSV rows with pressure data.
11
16
17
+ Validates that the exported file contains multiple data rows in CSV format
18
+ with 2-3 columns (time and pressure values).
12
19
13
- def test_export_pressures_writes_rows (flight_calisto_robust , tmp_path ):
14
- """Expect: direct exporter writes a pressure file with time-first CSV rows."""
15
- out = tmp_path / "p.csv"
20
+ Parameters
21
+ ----------
22
+ flight_calisto_robust : rocketpy.Flight
23
+ Flight object with parachutes configured.
24
+ tmp_path : pathlib.Path
25
+ Pytest fixture for temporary directories.
26
+ """
27
+ out = tmp_path / "pressures.csv"
16
28
FlightDataExporter (flight_calisto_robust ).export_pressures (str (out ), time_step = 0.2 )
17
29
lines = out .read_text (encoding = "utf-8" ).strip ().splitlines ()
18
30
assert len (lines ) > 5
19
- # basic CSV shape “ t, value”
31
+ # Basic CSV shape " t, value" or "t, clean, noisy"
20
32
parts = lines [0 ].split ("," )
21
33
assert len (parts ) in (2 , 3 )
22
34
23
35
24
- def test_export_sensor_data_writes_json_when_sensor_data_present (
25
- flight_calisto , tmp_path , monkeypatch
26
- ):
27
- """Expect: exporter writes JSON mapping sensor.name -> data when sensor_data is present."""
36
+ def test_export_sensor_data_writes_json (flight_calisto , tmp_path , monkeypatch ):
37
+ """Test that export_sensor_data writes JSON with sensor data.
38
+
39
+ Validates that sensor data is exported as JSON with sensor names as keys
40
+ and measurement arrays as values.
41
+
42
+ Parameters
43
+ ----------
44
+ flight_calisto : rocketpy.Flight
45
+ Flight object to be tested.
46
+ tmp_path : pathlib.Path
47
+ Pytest fixture for temporary directories.
48
+ monkeypatch : pytest.MonkeyPatch
49
+ Pytest fixture for modifying attributes.
50
+ """
28
51
29
52
class DummySensor :
30
53
def __init__ (self , name ):
@@ -40,3 +63,130 @@ def __init__(self, name):
40
63
41
64
data = json .loads (out .read_text (encoding = "utf-8" ))
42
65
assert data ["DummySensor" ] == [1.0 , 2.0 , 3.0 ]
66
+
67
+
68
+ def test_export_data_default_variables (flight_calisto , tmp_path ):
69
+ """Test export_data with default variables (full solution matrix).
70
+
71
+ Validates that all state variables are exported correctly when no specific
72
+ variables are requested: position (x, y, z), velocity (vx, vy, vz),
73
+ quaternions (e0, e1, e2, e3), and angular velocities (w1, w2, w3).
74
+
75
+ Parameters
76
+ ----------
77
+ flight_calisto : rocketpy.Flight
78
+ Flight object to be tested.
79
+ tmp_path : pathlib.Path
80
+ Pytest fixture for temporary directories.
81
+ """
82
+ file_name = tmp_path / "flight_data.csv"
83
+ FlightDataExporter (flight_calisto ).export_data (str (file_name ))
84
+
85
+ test_data = np .loadtxt (file_name , delimiter = "," , skiprows = 1 )
86
+
87
+ # Verify time column
88
+ assert np .allclose (flight_calisto .x [:, 0 ], test_data [:, 0 ], atol = 1e-5 )
89
+
90
+ # Verify position
91
+ assert np .allclose (flight_calisto .x [:, 1 ], test_data [:, 1 ], atol = 1e-5 )
92
+ assert np .allclose (flight_calisto .y [:, 1 ], test_data [:, 2 ], atol = 1e-5 )
93
+ assert np .allclose (flight_calisto .z [:, 1 ], test_data [:, 3 ], atol = 1e-5 )
94
+
95
+ # Verify velocity
96
+ assert np .allclose (flight_calisto .vx [:, 1 ], test_data [:, 4 ], atol = 1e-5 )
97
+ assert np .allclose (flight_calisto .vy [:, 1 ], test_data [:, 5 ], atol = 1e-5 )
98
+ assert np .allclose (flight_calisto .vz [:, 1 ], test_data [:, 6 ], atol = 1e-5 )
99
+
100
+ # Verify quaternions
101
+ assert np .allclose (flight_calisto .e0 [:, 1 ], test_data [:, 7 ], atol = 1e-5 )
102
+ assert np .allclose (flight_calisto .e1 [:, 1 ], test_data [:, 8 ], atol = 1e-5 )
103
+ assert np .allclose (flight_calisto .e2 [:, 1 ], test_data [:, 9 ], atol = 1e-5 )
104
+ assert np .allclose (flight_calisto .e3 [:, 1 ], test_data [:, 10 ], atol = 1e-5 )
105
+
106
+ # Verify angular velocities
107
+ assert np .allclose (flight_calisto .w1 [:, 1 ], test_data [:, 11 ], atol = 1e-5 )
108
+ assert np .allclose (flight_calisto .w2 [:, 1 ], test_data [:, 12 ], atol = 1e-5 )
109
+ assert np .allclose (flight_calisto .w3 [:, 1 ], test_data [:, 13 ], atol = 1e-5 )
110
+
111
+
112
+ def test_export_data_custom_variables_and_time_step (flight_calisto , tmp_path ):
113
+ """Test export_data with custom variables and time step.
114
+
115
+ Validates that specific variables can be exported with custom time intervals,
116
+ including derived quantities like angle of attack.
117
+
118
+ Parameters
119
+ ----------
120
+ flight_calisto : rocketpy.Flight
121
+ Flight object to be tested.
122
+ tmp_path : pathlib.Path
123
+ Pytest fixture for temporary directories.
124
+ """
125
+ file_name = tmp_path / "custom_flight_data.csv"
126
+ time_step = 0.1
127
+
128
+ FlightDataExporter (flight_calisto ).export_data (
129
+ str (file_name ),
130
+ "z" ,
131
+ "vz" ,
132
+ "e1" ,
133
+ "w3" ,
134
+ "angle_of_attack" ,
135
+ time_step = time_step ,
136
+ )
137
+
138
+ test_data = np .loadtxt (file_name , delimiter = "," , skiprows = 1 )
139
+ time_points = np .arange (flight_calisto .t_initial , flight_calisto .t_final , time_step )
140
+
141
+ # Verify time column
142
+ assert np .allclose (time_points , test_data [:, 0 ], atol = 1e-5 )
143
+
144
+ # Verify custom variables
145
+ assert np .allclose (flight_calisto .z (time_points ), test_data [:, 1 ], atol = 1e-5 )
146
+ assert np .allclose (flight_calisto .vz (time_points ), test_data [:, 2 ], atol = 1e-5 )
147
+ assert np .allclose (flight_calisto .e1 (time_points ), test_data [:, 3 ], atol = 1e-5 )
148
+ assert np .allclose (flight_calisto .w3 (time_points ), test_data [:, 4 ], atol = 1e-5 )
149
+ assert np .allclose (
150
+ flight_calisto .angle_of_attack (time_points ), test_data [:, 5 ], atol = 1e-5
151
+ )
152
+
153
+
154
+ def test_export_kml_trajectory (flight_calisto_robust , tmp_path ):
155
+ """Test export_kml creates valid KML file with trajectory coordinates.
156
+
157
+ Validates that the KML export contains correct latitude, longitude, and
158
+ altitude coordinates for the flight trajectory in absolute altitude mode.
159
+
160
+ Parameters
161
+ ----------
162
+ flight_calisto_robust : rocketpy.Flight
163
+ Flight object to be tested.
164
+ tmp_path : pathlib.Path
165
+ Pytest fixture for temporary directories.
166
+ """
167
+ file_name = tmp_path / "trajectory.kml"
168
+ FlightDataExporter (flight_calisto_robust ).export_kml (
169
+ str (file_name ), time_step = None , extrude = True , altitude_mode = "absolute"
170
+ )
171
+
172
+ # Parse KML coordinates
173
+ with open (file_name , "r" ) as kml_file :
174
+ for row in kml_file :
175
+ if row .strip ().startswith ("<coordinates>" ):
176
+ coords_str = (
177
+ row .strip ()
178
+ .replace ("<coordinates>" , "" )
179
+ .replace ("</coordinates>" , "" )
180
+ )
181
+ coords_list = coords_str .strip ().split (" " )
182
+
183
+ # Extract lon, lat, z from coordinates
184
+ parsed_coords = [c .split ("," ) for c in coords_list ]
185
+ lon = [float (point [0 ]) for point in parsed_coords ]
186
+ lat = [float (point [1 ]) for point in parsed_coords ]
187
+ z = [float (point [2 ]) for point in parsed_coords ]
188
+
189
+ # Verify coordinates match flight data
190
+ assert np .allclose (flight_calisto_robust .latitude [:, 1 ], lat , atol = 1e-3 )
191
+ assert np .allclose (flight_calisto_robust .longitude [:, 1 ], lon , atol = 1e-3 )
192
+ assert np .allclose (flight_calisto_robust .z [:, 1 ], z , atol = 1e-3 )
0 commit comments