flightdata 0.2.3__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {flightdata-0.2.3 → flightdata-0.2.5}/PKG-INFO +1 -1
  2. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/base/table.py +28 -11
  3. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/flight/ardupilot.py +3 -0
  4. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/flight/fields.py +13 -3
  5. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/flight/flight.py +71 -36
  6. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/flow.py +1 -1
  7. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/state.py +11 -7
  8. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata.egg-info/PKG-INFO +1 -1
  9. {flightdata-0.2.3 → flightdata-0.2.5}/setup.cfg +1 -1
  10. {flightdata-0.2.3 → flightdata-0.2.5}/test/test_flight.py +2 -34
  11. {flightdata-0.2.3 → flightdata-0.2.5}/LICENSE +0 -0
  12. {flightdata-0.2.3 → flightdata-0.2.5}/README.md +0 -0
  13. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/__init__.py +0 -0
  14. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/base/__init__.py +0 -0
  15. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/base/collection.py +0 -0
  16. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/base/constructs.py +0 -0
  17. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/base/numpy_encoder.py +0 -0
  18. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/coefficients.py +0 -0
  19. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/environment/__init__.py +0 -0
  20. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/environment/environment.py +0 -0
  21. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/environment/wind.py +0 -0
  22. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/flight/__init__.py +0 -0
  23. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/model/__init__.py +0 -0
  24. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/model/aerodynamic.py +0 -0
  25. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/model/thrust.py +0 -0
  26. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata/origin.py +0 -0
  27. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata.egg-info/SOURCES.txt +0 -0
  28. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata.egg-info/dependency_links.txt +0 -0
  29. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata.egg-info/requires.txt +0 -0
  30. {flightdata-0.2.3 → flightdata-0.2.5}/flightdata.egg-info/top_level.txt +0 -0
  31. {flightdata-0.2.3 → flightdata-0.2.5}/setup.py +0 -0
  32. {flightdata-0.2.3 → flightdata-0.2.5}/test/test_fields.py +0 -0
  33. {flightdata-0.2.3 → flightdata-0.2.5}/test/test_origin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flightdata
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Module for handling UAV flight log data
5
5
  Home-page: https://github.com/PyFlightCoach/FlightData
6
6
  Author: Thomas David
@@ -63,7 +63,7 @@ class Table:
63
63
 
64
64
  @classmethod
65
65
  def from_dict(Cls, data):
66
- if ['data'] in data:
66
+ if 'data' in data:
67
67
  data = data['data']
68
68
  return Cls(pd.DataFrame.from_dict(data).set_index("t", drop=False))
69
69
 
@@ -119,7 +119,7 @@ class Table:
119
119
  yield self[ind]
120
120
 
121
121
  @classmethod
122
- def from_constructs(cls, *args,**kwargs):
122
+ def from_constructs(cls, *args,**kwargs) -> Self:
123
123
  kwargs = dict(
124
124
  **{list(cls.constructs.data.keys())[i]: arg for i, arg in enumerate(args)},
125
125
  **kwargs
@@ -130,7 +130,7 @@ class Table:
130
130
  x.to_pandas(
131
131
  columns=cls.constructs[key].keys,
132
132
  index=kwargs["time"].t
133
- ) for key, x in kwargs.items() if not x is None
133
+ ) for key, x in kwargs.items() if x is not None
134
134
  ],
135
135
  axis=1
136
136
  )
@@ -163,10 +163,7 @@ class Table:
163
163
  axis=0,
164
164
  ignore_index=True
165
165
  ).set_index("t", drop=False))
166
-
167
-
168
-
169
-
166
+
170
167
  def label(self, **kwargs) -> Self:
171
168
  return self.__class__(self.data.assign(**kwargs))
172
169
 
@@ -203,6 +200,7 @@ class Table:
203
200
  return 0
204
201
 
205
202
  def unique_labels(self, cols = None) -> pd.DataFrame:
203
+ '''TODO Fix This'''
206
204
  if cols is None:
207
205
  cols = self.label_cols
208
206
  return self.data.loc[:, cols].reset_index(drop=True).drop_duplicates().reset_index(drop=True)
@@ -319,14 +317,33 @@ class Table:
319
317
  labs = np.array(self.single_labels())
320
318
  return self.__class__(self.data[labs == lab])
321
319
 
322
- def split_labels(self) -> dict[str, Self]:
323
- '''split into multiple tables based on the labels'''
320
+ def split_labels(self, cols=None) -> dict[str, Self]:
321
+ '''Split into multiple tables based on the labels'''
324
322
  res = {}
325
- for l in self.unique_labels().iterrows():
326
- ld = l[1].to_dict()
323
+ for label in self.unique_labels(cols).iterrows():
324
+ ld = label[1].to_dict()
327
325
  res['_'.join(ld.values())] = self.get_label_subset(**ld)
328
326
  return res
329
327
 
328
+ def cumulative_labels(self, *cols) -> Self:
329
+ '''Return a string concatenation of the requested labels. append an indexer to the end
330
+ of the string for repeat descrete groups of the same label.'''
331
+ cols = self.label_cols if len(cols)==0 else cols
332
+ labs = self.data.loc[:, cols].stack().groupby(level=0).apply('_'.join)
333
+
334
+ changes = labs.shift() != labs
335
+ new_labels = labs.loc[changes]
336
+ uls = []
337
+ for i, nl in enumerate(new_labels):
338
+ uls.append(sum(new_labels.iloc[:i] == nl))
339
+
340
+ df = pd.DataFrame(labs).assign(indexer = np.array(uls)[changes.cumsum() - 1])
341
+ strdf = df.copy()
342
+ strdf['indexer'] = strdf['indexer'].astype(int).astype(str)
343
+ strdf = strdf.stack().groupby(level=0).apply('_'.join)
344
+ strdf.loc[df.indexer==0] = df[0]
345
+ return strdf.values
346
+
330
347
  @staticmethod
331
348
  def copy_labels(template: Self, flown: Self, path=None, min_len=0) -> Self:
332
349
  """Copy the labels from along the index warping path"""
@@ -1,3 +1,6 @@
1
+ from pymavlink.mavutil import mode_mapping_bynumber
2
+
3
+
1
4
 
2
5
 
3
6
  flightmodes = {
@@ -20,6 +20,9 @@ class Field:
20
20
  def col(self) -> str:
21
21
  return f'{self.column}_{self.i}' if self.i > 0 else self.column
22
22
 
23
+ def __repr__(self):
24
+ return f'{self.column}_{self.i}'
25
+
23
26
  class Fields:
24
27
  def __init__(self, data: Union[list[Field], dict[str: Field]]):
25
28
  if isinstance(data, list):
@@ -51,9 +54,8 @@ class Fields:
51
54
  else:
52
55
  return self.data[f'{group}_{col}'].instance(instance)
53
56
  except KeyError:
54
- raise AttributeError(f'Field {name} not found')
55
-
56
-
57
+ return None
58
+ #raise AttributeError(f'Field {name} not found')
57
59
 
58
60
  def get_fields(self, names: list[str]) -> list[Field]:
59
61
  _l = lambda v: [v] if isinstance(v, Field) else v
@@ -62,6 +64,8 @@ class Fields:
62
64
  def get_cols(self, names: list[str]) -> list[str]:
63
65
  return [f.col for f in self.get_fields(names)]
64
66
 
67
+ def __repr__(self) -> str:
68
+ return f'Fields({','.join(list(self.data.keys()))})'
65
69
 
66
70
  fields = Fields([
67
71
  Field('time_flight', 'time since the start of the flight, seconds'),
@@ -80,9 +84,15 @@ fields = Fields([
80
84
  Field('attitude_roll', 'roll angle, radians'),
81
85
  Field('attitude_pitch', 'pitch angle, radians'),
82
86
  Field('attitude_yaw', 'yaw angle, radians'),
87
+ Field('attdes_roll', 'desired roll angle, radians'),
88
+ Field('attdes_pitch', 'desired pitch angle, radians'),
89
+ Field('attdes_yaw', 'desired yaw angle, radians'),
83
90
  Field('axisrate_roll', 'roll rate, radians / second'),
84
91
  Field('axisrate_pitch', 'pitch rate, radians / second'),
85
92
  Field('axisrate_yaw', 'yaw rate, radians / second'),
93
+ Field('desrate_roll', 'roll rate, radians / second'),
94
+ Field('desrate_pitch', 'pitch rate, radians / second'),
95
+ Field('desrate_yaw', 'yaw rate, radians / second'),
86
96
  Field('battery_voltage', 'volts'),
87
97
  Field('battery_current', 'amps'),
88
98
  Field('battery_totalcurrent', 'Ah'),
@@ -9,6 +9,7 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9
9
  You should have received a copy of the GNU General Public License along with
10
10
  this program. If not, see <http://www.gnu.org/licenses/>.
11
11
  """
12
+ from __future__ import annotations
12
13
  from typing import Self, Union, IO
13
14
  import numpy as np
14
15
  import pandas as pd
@@ -29,7 +30,7 @@ from scipy.signal import butter, filtfilt
29
30
  class Flight:
30
31
  ardupilot_types = [
31
32
  'XKF1', 'XKF2', 'NKF1', 'NKF2',
32
- 'POS', 'ATT', 'ACC', 'GYRO', 'IMU',
33
+ 'POS', 'ATT', 'RATE', 'ACC', 'GYRO', 'IMU',
33
34
  'ARSP', 'GPS', 'RCIN', 'RCOU', 'BARO', 'MODE',
34
35
  'RPM', 'MAG', 'BAT', 'BAT2', 'VEL', 'ORGN', 'ESC', 'CURRENT']
35
36
 
@@ -43,6 +44,10 @@ class Flight:
43
44
 
44
45
  def __getattr__(self, name):
45
46
  cols = getattr(fields, name)
47
+ if cols is None:
48
+ cols = [f for f in self.data.columns if f.startswith(name)]
49
+ if len(cols) > 0:
50
+ return self.data[cols]
46
51
  try:
47
52
  if isinstance(cols, Field):
48
53
  return self.data[cols.col]
@@ -76,7 +81,15 @@ class Flight:
76
81
  .loc[sli].set_index("time_flight", drop=False),
77
82
  self.parameters, self.origin, self.primary_pos_source
78
83
  )
79
-
84
+
85
+ def slice_time_flight(self, sli):
86
+ return Flight(
87
+ self.data.reset_index(drop=True)
88
+ .set_index('time_flight', drop=False)
89
+ .loc[sli],
90
+ self.parameters, self.origin, self.primary_pos_source
91
+ )
92
+
80
93
  def copy(self, **kwargs):
81
94
  return Flight(
82
95
  kwargs['data'] if 'data' in kwargs else self.data.copy() ,
@@ -149,8 +162,6 @@ class Flight:
149
162
  Returns:
150
163
  list[Flight]: list of flights
151
164
  """
152
-
153
-
154
165
  modechanges = (self.flightmode_a.diff().fillna(value=0) != 0).astype(int).cumsum()
155
166
 
156
167
  flights = {flightmodes[m]: [] for m in self.flightmode_a.unique()}
@@ -185,17 +196,17 @@ class Flight:
185
196
  assert_almost_equal(self.origin.pos, other.origin.pos)
186
197
  assert self.origin.heading == other.origin.heading
187
198
  return True
188
- except:
199
+ except Exception as ex:
189
200
  return False
190
201
 
191
202
  @staticmethod
192
- def from_log(log:Union[Ardupilot, str]):
203
+ def from_log(log:Union[Ardupilot, str], extra_types: list[str] = None, **kwargs) -> Flight:
193
204
  """Constructor from an ardupilot bin file."""
194
-
195
- if isinstance(log, Path):
196
- log = str(log)
197
- if isinstance(log, str):
198
- parser = Ardupilot(log, types=Flight.ardupilot_types)
205
+
206
+ extra_types = [] if extra_types is None else extra_types
207
+
208
+ if isinstance(log, str) or isinstance(log, Path):
209
+ parser = Ardupilot(str(log), types=list(set(Flight.ardupilot_types + extra_types)))
199
210
  else:
200
211
  parser = log
201
212
 
@@ -219,6 +230,9 @@ class Flight:
219
230
  attitude_roll = np.radians(att.Roll),
220
231
  attitude_pitch = np.radians(att.Pitch),
221
232
  attitude_yaw = np.radians(att.Yaw),
233
+ attdes_roll = np.radians(att.DesRoll),
234
+ attdes_pitch = np.radians(att.DesPitch),
235
+ attdes_yaw = np.radians(att.DesYaw),
222
236
  ))
223
237
 
224
238
  if 'POS' in parser.dfs:
@@ -232,7 +246,7 @@ class Flight:
232
246
  else:
233
247
  ppsorce = 'position'
234
248
 
235
- if not ekf1 is None:
249
+ if ekf1 is not None:
236
250
  dfs = dfs + Flight.parse_instances(ekf1, dict(
237
251
  position_N='PN',
238
252
  position_E='PE',
@@ -242,28 +256,46 @@ class Flight:
242
256
  velocity_D='VD',
243
257
  ), 'C')
244
258
 
245
- if not ekf2 is None:
259
+ if ekf2 is not None:
246
260
  dfs = dfs + Flight.parse_instances(ekf2, {
247
261
  'wind_N': 'VWN',
248
262
  'wind_E': 'VWE',
249
263
  }, 'C')
264
+ if 'RATE' in parser.dfs:
265
+ dfs.append(Flight.build_cols(
266
+ time_actual = parser.rate.timestamp,
267
+ axisrate_roll = parser.rate.R,
268
+ axisrate_pitch = parser.rate.P,
269
+ axisrate_yaw = parser.rate.Y,
270
+ desrate_roll = parser.rate.RDes,
271
+ desrate_pitch = parser.rate.PDes,
272
+ desrate_yaw = parser.rate.YDes,
273
+ ))
250
274
 
251
275
  if 'IMU' in parser.dfs:
252
276
  imu = parser.IMU
253
277
  if 'I' in imu:
254
278
  imu = imu.loc[imu.I==0, :]
255
279
 
256
- if not ekf1 is None:
257
- imu = pd.merge_asof(imu, ekf1.loc[ekf1.C==0], on='timestamp', direction='nearest')
258
- imu['GyrX'] = imu.GyrX + np.radians(imu.GX) / 100
259
- imu['GyrY'] = imu.GyrY + np.radians(imu.GY) / 100
260
- imu['GyrZ'] = imu.GyrZ + np.radians(imu.GZ) / 100
261
-
262
- if not ekf2 is None:
263
- imu = pd.merge_asof(imu, ekf2.loc[ekf2.C==0], on='timestamp', direction='nearest')
264
- imu['AccX'] = imu.AccX + imu.AX / 100
265
- imu['AccY'] = imu.AccY + imu.AY / 100
266
- imu['AccZ'] = imu.AccZ + imu.AZ / 100
280
+ if ekf1 is not None:
281
+ if 'C' in ekf1.columns:
282
+ imu = pd.merge_asof(imu, ekf1.loc[ekf1.C==0], on='timestamp', direction='nearest')
283
+ else:
284
+ imu = pd.merge_asof(imu, ekf1, on='timestamp', direction='nearest')
285
+ if all([v in imu.columns for v in ['GX', 'GY', 'GZ']]):
286
+ imu['GyrX'] = imu.GyrX + np.radians(imu.GX) / 100
287
+ imu['GyrY'] = imu.GyrY + np.radians(imu.GY) / 100
288
+ imu['GyrZ'] = imu.GyrZ + np.radians(imu.GZ) / 100
289
+
290
+ if ekf2 is not None:
291
+ if 'C' in ekf2.columns:
292
+ imu = pd.merge_asof(imu, ekf2.loc[ekf2.C==0], on='timestamp', direction='nearest')
293
+ else:
294
+ imu = pd.merge_asof(imu, ekf2, on='timestamp', direction='nearest')
295
+ if all([v in imu.columns for v in ['AX', 'AY', 'AZ']]):
296
+ imu['AccX'] = imu.AccX + imu.AX / 100
297
+ imu['AccY'] = imu.AccY + imu.AY / 100
298
+ imu['AccZ'] = imu.AccZ + imu.AZ / 100
267
299
 
268
300
  dfs.append(Flight.build_cols(
269
301
  time_actual = imu.timestamp,
@@ -307,6 +339,7 @@ class Flight:
307
339
  ))
308
340
 
309
341
  if 'MODE' in parser.dfs:
342
+
310
343
  df = Flight.build_cols(
311
344
  time_actual = parser.MODE.timestamp,
312
345
  flightmode_a = parser.MODE.Mode,
@@ -337,20 +370,25 @@ class Flight:
337
370
  **{f'motor_rpm_{i}': parser.RPM[f'rpm{i}'] for i in range(2) if f'rpm{i}' in parser.RPM.columns},
338
371
  ))
339
372
 
340
-
373
+ for k, v in kwargs.items():
374
+ if k in parser.dfs:
375
+ dfs = dfs + Flight.parse_instances(parser.dfs[k], v)
341
376
 
342
- origin = Origin('ekf_origin', GPS(parser.ORGN.iloc[:,-3:]), 0)
343
-
344
377
  dfout = dfs[0]
345
-
378
+ dt = dfout.time_actual.diff().max()
346
379
  for df in dfs[1:]:
347
- dfout = pd.merge_asof(dfout, df, on='time_actual', direction='nearest')
380
+ dfout = pd.merge_asof(
381
+ dfout, df, on='time_actual', direction='nearest',
382
+ tolerance=min(max(dt, df.time_actual.diff().max()), 0.1)
383
+ )
348
384
 
349
- return Flight(dfout.set_index('time_flight', drop=False), parser.parms, origin, ppsorce)#.remove_time_flutter()
385
+ origin = Origin('ekf_origin', GPS(parser.ORGN.iloc[:,-3:]), 0)
386
+
387
+ return Flight(dfout.set_index('time_flight', drop=False), parser.parms, origin, ppsorce)
350
388
 
351
389
  @staticmethod
352
390
  def parse_instances(indf: pd.DataFrame, colmap:dict[str, str], instancecol='Instance'):
353
-
391
+ '''Where an instance column exists in an input df split the values into two columns'''
354
392
  instances = indf[instancecol].unique() if instancecol in indf.columns else [0]
355
393
  dfs = []
356
394
  for i in instances:
@@ -413,7 +451,6 @@ class Flight:
413
451
  ], axis=1).reset_index(drop=True).set_index('time_flight', drop=False)
414
452
  )
415
453
 
416
-
417
454
  def filter(self, b, a):
418
455
  dont_filter = [c for c in fields.get_cols(['time', 'flightmode', 'rcin', 'rcout']) if c in self.data.columns]
419
456
  unwrap_cols = [c for c in fields.get_cols(['attitude']) if c in self.data.columns]
@@ -432,11 +469,9 @@ class Flight:
432
469
  )
433
470
 
434
471
  def butter_filter(self, cutoff, order=5):
435
-
436
472
  ts = self.time_flight.to_numpy()
437
473
  N = len(self)
438
474
  T = (ts[-1] - ts[0]) / N
439
-
440
475
  fs = 1/T
441
-
442
- return self.filter(*butter(order, cutoff, fs=fs, btype='low', analog=False))
476
+ return self.filter(*butter(order, cutoff, fs=fs, btype='low', analog=False))
477
+
@@ -34,7 +34,7 @@ class Flow(Table):
34
34
  beta = np.arctan(stab_airspeed.y / stab_airspeed.x)
35
35
  beta[np.isnan(beta)] = 0.0
36
36
 
37
- with np.errstate(invalid='warn'):
37
+ with np.errstate(invalid='ignore'):
38
38
  q = 0.5 * env.rho * abs(airspeed)**2
39
39
  q[np.isnan(q)] = 0.0
40
40
 
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
  from typing import Union, List, Tuple, Self
3
- import warnings
4
3
  from pathlib import Path
5
4
  import numpy as np
6
5
  import pandas as pd
@@ -29,10 +28,10 @@ class State(Table):
29
28
  return g.Transformation(-self.pos, self.att.inverse())
30
29
 
31
30
  @staticmethod
32
- def from_transform(transform: g.Transformation=None, **kwargs):
31
+ def from_transform(transform: g.Transformation=None, **kwargs) -> State:
33
32
  if transform is None:
34
33
  transform = g.Transformation()
35
- if not "time" in kwargs:
34
+ if "time" not in kwargs:
36
35
  kwargs["time"] = g.Time.from_t(np.linspace(0, State._construct_freq*len(transform), len(transform)))
37
36
  return State.from_constructs(pos=transform.p, att=transform.q, **kwargs)
38
37
 
@@ -64,8 +63,8 @@ class State(Table):
64
63
  att = st.att.body_rotate(rvel * time.t)
65
64
  pos = g.Point.concatenate([
66
65
  g.P0(),
67
- (att.transform_point(vel)).cumsum()[:-1]
68
- ]) * time.dt + st.pos
66
+ (att.transform_point(vel) * time.dt).cumsum()[:-1]
67
+ ]) + st.pos
69
68
  return State.from_constructs(time,pos, att, vel, rvel)
70
69
 
71
70
 
@@ -424,9 +423,14 @@ class State(Table):
424
423
  def direction(self):
425
424
  """returns 1 for going right, -1 for going left"""
426
425
  return np.sign(self.att.transform_point(g.Point(1, 0, 0)).x)
427
-
426
+
427
+ def cross_direction(self):
428
+ """returns 1 for going out, -1 for coming in"""
429
+ return np.sign(self.att.transform_point(g.Point(1, 0, 0)).y)
430
+
431
+
428
432
  def inverted(self):
429
- return np.sign(self.att.transform_point(g.Point(0, 0, 1)).z) > 0
433
+ return self.att.transform_point(g.Point(0, 0, 1)).z > 0
430
434
 
431
435
  def upright(self):
432
436
  return not self.inverted()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flightdata
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Module for handling UAV flight log data
5
5
  Home-page: https://github.com/PyFlightCoach/FlightData
6
6
  Author: Thomas David
@@ -5,7 +5,7 @@ author_email = thomasdavid0@gmail.com
5
5
  description = Module for handling UAV flight log data
6
6
  long_description = file: README.md
7
7
  long_description_content_type = text/markdown
8
- version = 0.2.3
8
+ version = 0.2.5
9
9
  url = https://github.com/PyFlightCoach/FlightData
10
10
 
11
11
  [options]
@@ -1,13 +1,10 @@
1
1
  from flightdata import Flight, Origin
2
- import os
3
2
  from io import open
4
- from json import load, dumps, loads
3
+ from json import load
5
4
  from pytest import fixture, approx, mark
6
5
  import numpy as np
7
6
  import pandas as pd
8
7
  from ardupilot_log_reader import Ardupilot
9
- from geometry import GPS
10
- from geometry.testing import assert_almost_equal
11
8
 
12
9
 
13
10
  @fixture(scope='session')
@@ -23,7 +20,6 @@ def fl():
23
20
  def fcj():
24
21
  return Flight.from_fc_json('test/test_inputs/00000137.json')
25
22
 
26
-
27
23
  def test_duration(fl):
28
24
  assert fl.duration == approx(685, rel=1e-3)
29
25
 
@@ -31,7 +27,6 @@ def test_slice(fl):
31
27
  short_flight = fl[100:200]
32
28
  assert short_flight.duration == approx(100, 0.01)
33
29
 
34
-
35
30
  def test_to_from_dict(fl):
36
31
  data = fl.to_dict()
37
32
  fl2 = Flight.from_dict(data)
@@ -43,7 +38,6 @@ def test_from_fc_json(fcj):
43
38
  assert fcj.duration > 200
44
39
  assert fcj.position_D.max() < -10
45
40
 
46
-
47
41
  @mark.skip
48
42
  def test_unique_identifier():
49
43
  with open("test/test_inputs/manual_F3A_P21_21_09_24_00000052.json", "r") as f:
@@ -54,27 +48,18 @@ def test_unique_identifier():
54
48
 
55
49
  assert flight1.unique_identifier() == flight2.unique_identifier()
56
50
 
57
-
58
51
  @mark.skip
59
52
  def test_baro(fl):
60
53
  press = fl.air_pressure
61
- temp = fl.air_temperature
62
54
  assert press.iloc[0,0] < 120000
63
55
  assert press.iloc[0,0] > 90000
64
56
 
65
-
66
- @mark.skip
67
- def test_ekfv2(fl):
68
- pass
69
-
70
-
71
57
  def test_flying_only(fl: Flight):
72
58
  flt = fl.flying_only()
73
59
  assert isinstance(flt, Flight)
74
60
  assert flt.duration < fl.duration
75
61
  assert flt[0].gps_altitude > 5
76
62
 
77
-
78
63
  def test_slice_raw_t(fl: Flight):
79
64
  sli = fl.slice_raw_t(slice(100, None, None))
80
65
  assert isinstance(sli, Flight)
@@ -86,7 +71,7 @@ def test_origin(fl: Flight):
86
71
 
87
72
  @fixture(scope='session')
88
73
  def vtol_hover():
89
- return Flight.from_log('test/data/vtol_hover.bin')
74
+ return Flight.from_json('test/data/vtol_hover.json')
90
75
 
91
76
  def test_flightmode_split(vtol_hover: Flight):
92
77
  smodes = vtol_hover.split_modes()
@@ -94,8 +79,6 @@ def test_flightmode_split(vtol_hover: Flight):
94
79
  assert isinstance(smodes['QHOVER'], list)
95
80
  assert isinstance(smodes['QHOVER'][0], Flight)
96
81
 
97
-
98
-
99
82
  def _fft(col: pd.Series):
100
83
  from scipy.fft import fft, fftfreq
101
84
  ts = col.index
@@ -107,7 +90,6 @@ def _fft(col: pd.Series):
107
90
 
108
91
  return xf, 2.0/N * np.abs(yf[0:N//2])
109
92
 
110
-
111
93
  def test_butter_filter(fl: Flight):
112
94
  filtered = fl.butter_filter(1,5)
113
95
 
@@ -119,17 +101,3 @@ def test_butter_filter(fl: Flight):
119
101
  def test_remove_time_flutter(fl: Flight):
120
102
  flf = fl.remove_time_flutter()
121
103
  assert np.gradient(np.gradient(flf.data.index)) == approx(0)
122
-
123
-
124
- #import plotly.graph_objects as go
125
-
126
- #fig = go.Figure()
127
- #fig.add_trace(go.Scatter(x=fl.time_flight, y=fl.acceleration_x, name='original'))
128
- #fig.add_trace(go.Scatter(x=filtered.time_flight, y=filtered.acceleration_x, name='filtered'))
129
- #fig.show()
130
- #
131
- #
132
- #fig = go.Figure()
133
- #fig.add_trace(go.Scatter(x=x, y=y, name='original'))
134
- #fig.add_trace(go.Scatter(x=xf, y=yf, name='filtered'))
135
- #fig.show()
File without changes
File without changes
File without changes