flightdata 0.2.25__tar.gz → 0.2.27__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.
- {flightdata-0.2.25 → flightdata-0.2.27}/PKG-INFO +3 -2
- {flightdata-0.2.25 → flightdata-0.2.27}/pyproject.toml +5 -1
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/__init__.py +1 -2
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flight/flight.py +2 -1
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/origin.py +17 -27
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/scripts/flightline.py +53 -37
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/state.py +2 -1
- flightdata-0.2.27/test/data/script_tests/c6_on_0001.BIN +0 -0
- flightdata-0.2.27/test/data/script_tests/center_0003.bin +0 -0
- flightdata-0.2.27/test/data/script_tests/pilot_0004.BIN +0 -0
- flightdata-0.2.27/test/test_scripts.py +25 -0
- flightdata-0.2.25/src/flightdata/schemas/fcj.py +0 -174
- flightdata-0.2.25/test/test_state/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/.github/workflows/publish_pypi.yml +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/.gitignore +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/.vscode/launch.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/.vscode/settings.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/LICENSE +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/README.md +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/data/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/data/manual_F3A_F23_22_04_28_00000231.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/data/manual_F3A_P23_22_05_31_00000350.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/data/manual_F3A_P23_23_08_11_00000094.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/flight_dynamics/00000150.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/flight_dynamics/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/flight_dynamics/box.f3a +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/flight_dynamics/param_id.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/state_analysis/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/state_analysis/axes.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/examples/state_analysis/state_fill_plot.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/collection.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/constructs.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/labeling.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/numpy_encoder.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/base/table.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/bindata.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/coefficients.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/environment/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/environment/environment.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/environment/wind.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flight/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flight/ardupilot.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flight/fields.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flight/parameters.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/flow.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/model/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/model/aerodynamic.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/model/thrust.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/py.typed +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/src/flightdata/scripts/collect_logs.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/EmailedBox.f3a +0 -0
- {flightdata-0.2.25/src/flightdata/schemas → flightdata-0.2.27/test}/__init__.py +0 -0
- {flightdata-0.2.25/test → flightdata-0.2.27/test/base}/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/base/test_base_constructs.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/base/test_table.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/conftest.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/bin_parser_GPS.csv +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/bin_parser_POS.csv +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/make_inputs.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/manual_F3A_P23.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/p23.BIN +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/p23.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/p23_box.f3a +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/p23_fc.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/p23_flight.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/vtol_hover.bin +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/vtol_hover.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/data/web_bin_parse.json +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_bindata.py +0 -0
- {flightdata-0.2.25/test/base → flightdata-0.2.27/test/test_environment}/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_environment/test_environment.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_environment/test_environment_wind.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_fields.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_flight.py +0 -0
- {flightdata-0.2.25/test/test_environment → flightdata-0.2.27/test/test_model}/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_model/test_model_coefficients.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_model/test_model_flow.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_origin.py +0 -0
- {flightdata-0.2.25/test/test_model → flightdata-0.2.27/test/test_state}/__init__.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_state/test_state.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_state/test_state_builders.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_state/test_state_conversions.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/test/test_state/test_state_measurements.py +0 -0
- {flightdata-0.2.25 → flightdata-0.2.27}/uv.lock +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: flightdata
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.27
|
|
4
4
|
Summary: Python tools for handling flight data
|
|
5
5
|
Author-email: Thomas David <thomasdavid0@gmail.com>
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Requires-Python: >=3.12
|
|
7
8
|
Requires-Dist: numpy>=2.1.3
|
|
8
9
|
Requires-Dist: pandas>=2.2.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flightdata"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.27"
|
|
4
4
|
description = "Python tools for handling flight data"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Thomas David", email = "thomasdavid0@gmail.com" }]
|
|
@@ -11,6 +11,10 @@ dependencies = [
|
|
|
11
11
|
"pfc-geometry>=0.2.13",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
+
[project.scripts]
|
|
15
|
+
collect_logs = "flightdata.scripts.collect_logs:main"
|
|
16
|
+
flightline = "flightdata.scripts.flightline:main"
|
|
17
|
+
|
|
14
18
|
[build-system]
|
|
15
19
|
requires = ["hatchling"]
|
|
16
20
|
build-backend = "hatchling.build"
|
|
@@ -21,7 +21,8 @@ from time import time
|
|
|
21
21
|
from json import load, dump
|
|
22
22
|
from flightdata.base.numpy_encoder import NumpyEncoder
|
|
23
23
|
from .ardupilot import flightmodes
|
|
24
|
-
from flightdata import Origin
|
|
24
|
+
from flightdata import Origin
|
|
25
|
+
from schemas import fcj
|
|
25
26
|
from numbers import Number
|
|
26
27
|
from scipy.signal import filtfilt, butter
|
|
27
28
|
from datetime import datetime
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import geometry as g
|
|
2
3
|
import numpy as np
|
|
3
4
|
from json import load, dump
|
|
4
5
|
from typing import Self
|
|
5
|
-
import json_stream
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from dataclasses import dataclass
|
|
8
|
-
from
|
|
9
|
-
|
|
7
|
+
from schemas import fcj
|
|
8
|
+
|
|
10
9
|
|
|
11
10
|
@dataclass
|
|
12
11
|
class Origin(object):
|
|
@@ -141,35 +140,26 @@ class Origin(object):
|
|
|
141
140
|
),
|
|
142
141
|
float(parms.rotation)
|
|
143
142
|
)
|
|
144
|
-
|
|
143
|
+
|
|
145
144
|
def gps_to_point(self, gps: g.GPS) -> g.Point:
|
|
146
145
|
return self.rotation.transform_point(gps - self.pos)
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class FCJOrigin(BaseModel):
|
|
151
|
-
lat: float
|
|
152
|
-
lng: float
|
|
153
|
-
alt: float
|
|
154
|
-
heading: float
|
|
155
|
-
move_east: float=0
|
|
156
|
-
move_north: float=0
|
|
157
|
-
|
|
158
|
-
def origin(self):
|
|
159
|
-
"""Create a flightdata.Origin object from the FCJOrigin object."""
|
|
147
|
+
@staticmethod
|
|
148
|
+
def from_fcj_origin(fcj_origin: fcj.Origin):
|
|
160
149
|
return Origin(
|
|
161
150
|
"fcj",
|
|
162
|
-
g.GPS(
|
|
163
|
-
g.Point(
|
|
151
|
+
g.GPS(fcj_origin.lat, fcj_origin.lng, fcj_origin.alt).offset(
|
|
152
|
+
g.Point(fcj_origin.move_north, fcj_origin.move_east, 0)
|
|
164
153
|
),
|
|
165
|
-
np.radians(
|
|
154
|
+
np.radians(fcj_origin.heading),
|
|
166
155
|
)
|
|
167
156
|
|
|
168
|
-
def shift(self):
|
|
169
|
-
return self.origin().rotation.transform_point(
|
|
170
|
-
g.Point(self.move_east, -self.move_north, 0)
|
|
171
|
-
)
|
|
172
157
|
|
|
173
|
-
@
|
|
174
|
-
def
|
|
175
|
-
return
|
|
158
|
+
@property
|
|
159
|
+
def fcj_origin(self):
|
|
160
|
+
return fcj.Origin(
|
|
161
|
+
lat=self.lat,
|
|
162
|
+
lng=self.long,
|
|
163
|
+
alt=self.alt,
|
|
164
|
+
heading=np.degrees(self.heading),
|
|
165
|
+
)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from ast import In
|
|
2
|
+
import sys
|
|
1
3
|
from flightdata import Flight, Origin
|
|
2
4
|
from geometry import GPS
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
import argparse
|
|
5
|
-
|
|
7
|
+
import re
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def get_con_groups(log: Flight, channel: int):
|
|
@@ -14,64 +16,78 @@ def box_from_log(log: Flight, channel: int):
|
|
|
14
16
|
grps = get_con_groups(log, channel)
|
|
15
17
|
pilot = grps[0]
|
|
16
18
|
centre = grps[1]
|
|
17
|
-
# c6on = Flight(log.data.loc[log.data[f'rcin_c{channel}']>=1500])
|
|
18
|
-
# groups = (c6on.time_flight.diff() > 1).cumsum()
|
|
19
|
-
# pilot = Flight(c6on.data.loc[groups==0])
|
|
20
|
-
# centre = Flight(c6on.data.loc[groups==1])
|
|
21
19
|
|
|
22
20
|
return Origin.from_points("new", GPS(pilot.pos)[-1], GPS(centre.pos)[-1])
|
|
23
21
|
|
|
24
22
|
def box_from_logs(pilot: Flight, centre: Flight):
|
|
25
23
|
return Origin.from_points("new", GPS(*pilot.pos.iloc[-1]), GPS(*centre.pos.iloc[-1]))
|
|
26
24
|
|
|
25
|
+
re_logid = re.compile(r'[\d+]$')
|
|
26
|
+
def get_bin_from_number(folder: Path, number: str):
|
|
27
|
+
try:
|
|
28
|
+
return list(folder.glob(f"*{number}.BIN"))[0]
|
|
29
|
+
except IndexError:
|
|
30
|
+
return list(folder.glob(f"*{number}.bin"))[0]
|
|
27
31
|
|
|
28
|
-
def main():
|
|
29
|
-
parser = argparse.ArgumentParser(description='A tool for creating a flightline .f3a file from bin logs')
|
|
30
32
|
|
|
31
|
-
parser.add_argument('-l', '--logdir', default='', help='folder to look for logs in')
|
|
32
|
-
parser.add_argument('-p', '--pilot', default=None, help='flight log bin file to use, None for first')
|
|
33
|
-
parser.add_argument('-c', '--centre', default=None, help='centre position bin file to use if input==None')
|
|
34
|
-
parser.add_argument('-d', '--direction', default=None, help='heading of the box, if this is specified only pilot will be read')
|
|
35
|
-
parser.add_argument('-i', '--input', default=6, help='channel used to indicate pilot or centre postions (pwm>=1500), None for two files')
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
def create_flightline(args) -> argparse.Namespace:
|
|
35
|
+
logdir = Path(args.logdir)
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
plog = logdir / args.pilot
|
|
38
|
+
if not plog.exists():
|
|
39
|
+
if args.pilot is None:
|
|
40
|
+
plog=sorted(list(logdir.glob("*.BIN")))[0]
|
|
41
|
+
elif args.pilot.isdigit():
|
|
42
|
+
plog = get_bin_from_number(logdir, args.pilot)
|
|
43
|
+
else:
|
|
44
|
+
raise FileNotFoundError(f'Could not find pilot log file: {args.pilot}')
|
|
40
45
|
|
|
41
|
-
logs = sorted(list(Path(args.logdir).glob("*.BIN")))
|
|
42
|
-
logids = [int(log.stem) for log in logs]
|
|
43
|
-
|
|
44
|
-
if args.pilot in logs:
|
|
45
|
-
plog = args.pilot
|
|
46
|
-
elif args.pilot is None:
|
|
47
|
-
plog=logs[0]
|
|
48
|
-
elif args.pilot.isdigit():
|
|
49
|
-
plog = logs[logids.index(int(args.pilot))]
|
|
50
|
-
|
|
51
46
|
pilot = Flight.from_log(plog)
|
|
52
47
|
|
|
53
48
|
print(f'Pilot position log: {plog}')
|
|
54
49
|
|
|
55
|
-
if args.centre in logs:
|
|
56
|
-
clog = args.centre
|
|
57
|
-
elif args.centre is None:
|
|
58
|
-
clog=None
|
|
59
|
-
elif args.centre.isdigit():
|
|
60
|
-
clog = logs[logids.index(int(args.centre))]
|
|
61
|
-
if clog:
|
|
62
|
-
centre = Flight.from_log(clog)
|
|
63
|
-
print(f'Centre position log: {clog}')
|
|
64
|
-
|
|
65
50
|
if args.centre:
|
|
51
|
+
clog = logdir / args.centre
|
|
52
|
+
if not clog.exists():
|
|
53
|
+
if args.centre is None:
|
|
54
|
+
clog=None
|
|
55
|
+
elif args.centre.isdigit():
|
|
56
|
+
clog = get_bin_from_number(logdir, args.centre)
|
|
57
|
+
|
|
58
|
+
print(f'Centre position log: {clog}' if clog else "No centre position log")
|
|
59
|
+
centre = Flight.from_log(clog) if clog else None
|
|
60
|
+
|
|
66
61
|
box = Origin.from_points("new", GPS(*pilot.pos.iloc[-1]), GPS(*centre.pos.iloc[-1]))
|
|
67
62
|
else:
|
|
68
|
-
groups = get_con_groups(pilot, args.input)
|
|
69
63
|
if args.direction:
|
|
70
|
-
box = Origin("new", GPS(
|
|
64
|
+
box = Origin("new", GPS(plog.pos)[-1], float(args.direction))
|
|
71
65
|
else:
|
|
66
|
+
groups = get_con_groups(pilot, args.input)
|
|
72
67
|
box = Origin.from_points("new", GPS(groups[0].pos)[-1], GPS(groups[1].pos)[-1])
|
|
73
68
|
|
|
74
|
-
box
|
|
69
|
+
return box, plog.stem
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_args(args):
|
|
73
|
+
parser = argparse.ArgumentParser(description='A tool for creating a flightline .f3a file from bin logs')
|
|
74
|
+
|
|
75
|
+
parser.add_argument('-l', '--logdir', default='', help='folder to look for logs in')
|
|
76
|
+
parser.add_argument('-p', '--pilot', default=None, help='flight log bin file to use, None for first')
|
|
77
|
+
parser.add_argument('-c', '--centre', default=None, help='centre position bin file to use if input==None')
|
|
78
|
+
parser.add_argument('-d', '--direction', default=None, help='heading of the box, if this is specified only pilot will be read')
|
|
79
|
+
parser.add_argument('-i', '--input', default=6, help='channel used to indicate pilot or centre postions (pwm>=1500), None for two files')
|
|
80
|
+
|
|
81
|
+
args = parser.parse_args(args)
|
|
82
|
+
|
|
83
|
+
return args
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main():
|
|
87
|
+
args = parse_args(sys.argv[1:])
|
|
88
|
+
box, name = create_flightline(args)
|
|
89
|
+
box.to_f3a_zone(Path(args.logdir) / f'box_{name}.f3a')
|
|
90
|
+
|
|
75
91
|
|
|
76
92
|
if __name__ == '__main__':
|
|
77
93
|
main()
|
|
@@ -8,7 +8,8 @@ import numpy.typing as npt
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
|
|
10
10
|
import geometry as g
|
|
11
|
-
from flightdata import Constructs, Environment, Flight, Flow, Origin, SVar, Table
|
|
11
|
+
from flightdata import Constructs, Environment, Flight, Flow, Origin, SVar, Table
|
|
12
|
+
from schemas import fcj
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class State(Table):
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from flightdata.scripts import flightline as fl
|
|
2
|
+
import geometry as g
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pytest import approx
|
|
6
|
+
|
|
7
|
+
def test_get_bin_from_number():
|
|
8
|
+
bin = fl.get_bin_from_number(Path("test/data/script_tests"), 1)
|
|
9
|
+
assert bin.stem == "c6_on_0001"
|
|
10
|
+
|
|
11
|
+
bin = fl.get_bin_from_number(Path("test/data/script_tests"), 3)
|
|
12
|
+
assert bin.stem == "center_0003"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_flightline_two_bins():
|
|
17
|
+
box, name = fl.create_flightline(fl.parse_args(["-l", "test/data/script_tests", "-p", "pilot_0004.BIN", "-c", "3"]))
|
|
18
|
+
assert name == "pilot_0004"
|
|
19
|
+
assert isinstance(box.pos, g.GPS)
|
|
20
|
+
|
|
21
|
+
def test_flightline_c6on():
|
|
22
|
+
box, name = fl.create_flightline(fl.parse_args(["-l", "test/data/script_tests", "-p", "1"]))
|
|
23
|
+
assert name == "c6_on_0001"
|
|
24
|
+
|
|
25
|
+
assert np.degrees(box.heading) == approx(-34.44699010)
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from pydantic import BaseModel
|
|
3
|
-
import pandas as pd
|
|
4
|
-
import datetime
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class FCJ(BaseModel):
|
|
9
|
-
version: str
|
|
10
|
-
comments: str
|
|
11
|
-
name: str
|
|
12
|
-
view: View
|
|
13
|
-
parameters: Parameters
|
|
14
|
-
scored: bool
|
|
15
|
-
scores: list[float]
|
|
16
|
-
human_scores: list[HumanResult] = []
|
|
17
|
-
fcs_scores: list[Result] = []
|
|
18
|
-
mans: list[Man]
|
|
19
|
-
data: list[Data]
|
|
20
|
-
jhash: int | None = None
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def score_df(self):
|
|
24
|
-
return pd.concat(
|
|
25
|
-
{fcjr.fa_version: fcjr.to_df() for fcjr in self.fcs_scores},
|
|
26
|
-
axis=0,
|
|
27
|
-
names=["version", "manoeuvre", "difficulty", "truncate"],
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
def man_df(self):
|
|
31
|
-
return pd.DataFrame(
|
|
32
|
-
[man.__dict__ for man in self.mans[1:-1]],
|
|
33
|
-
index=pd.Index(range(len(self.mans[1:-1])), name="manoeuvre"),
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
def pfc_version_df(self):
|
|
37
|
-
sdf = self.score_df().loc[pd.IndexSlice[:, :, 3, False]]
|
|
38
|
-
return pd.concat(
|
|
39
|
-
[sdf, sdf.mul(self.man_df().k, axis=0)], axis=1, keys=["raw", "kfac"]
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def version_summary_df(self):
|
|
43
|
-
return self.pfc_version_df().groupby("version").kfac.sum()
|
|
44
|
-
|
|
45
|
-
def latest_version(self):
|
|
46
|
-
return max([fcjr.fa_version for fcjr in self.fcs_scores])
|
|
47
|
-
|
|
48
|
-
@property
|
|
49
|
-
def id(self):
|
|
50
|
-
return re.search(r"\d{8}", self.name)[0]
|
|
51
|
-
|
|
52
|
-
@property
|
|
53
|
-
def created(self):
|
|
54
|
-
try:
|
|
55
|
-
return datetime.datetime.strptime(
|
|
56
|
-
re.search(r"_\d{2}_\d{2}_\d{2}_", self.name)[0], "_%y_%m_%d_"
|
|
57
|
-
)
|
|
58
|
-
except Exception:
|
|
59
|
-
return None
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class View(BaseModel):
|
|
63
|
-
position: dict
|
|
64
|
-
target: dict
|
|
65
|
-
|
|
66
|
-
class Parameters(BaseModel):
|
|
67
|
-
rotation: float
|
|
68
|
-
start: int
|
|
69
|
-
stop: int
|
|
70
|
-
moveEast: float
|
|
71
|
-
moveNorth: float
|
|
72
|
-
wingspan: float
|
|
73
|
-
modelwingspan: float
|
|
74
|
-
elevate: float
|
|
75
|
-
originLat: float
|
|
76
|
-
originLng: float
|
|
77
|
-
originAlt: float
|
|
78
|
-
pilotLat: str | float
|
|
79
|
-
pilotLng: str | float
|
|
80
|
-
pilotAlt: str | float
|
|
81
|
-
centerLat: str | float
|
|
82
|
-
centerLng: str | float
|
|
83
|
-
centerAlt: str | float
|
|
84
|
-
schedule: list[str]
|
|
85
|
-
|
|
86
|
-
class HumanResult(BaseModel):
|
|
87
|
-
name: str
|
|
88
|
-
date: datetime.date
|
|
89
|
-
scores: list[float]
|
|
90
|
-
|
|
91
|
-
class Result(BaseModel):
|
|
92
|
-
fa_version: str
|
|
93
|
-
manresults: list[ManResult | None]
|
|
94
|
-
|
|
95
|
-
def to_df(self) -> pd.DataFrame:
|
|
96
|
-
return pd.concat(
|
|
97
|
-
{i: fcjmr.to_df() for i, fcjmr in enumerate(self.manresults[1:]) if fcjmr},
|
|
98
|
-
axis=0,
|
|
99
|
-
names=["manoeuvre", "difficulty", "truncate"],
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
class ManResult(BaseModel):
|
|
103
|
-
els: list[El]
|
|
104
|
-
results: list[Score] = []
|
|
105
|
-
|
|
106
|
-
def to_df(self) -> pd.DataFrame:
|
|
107
|
-
return pd.DataFrame(
|
|
108
|
-
data=[res.score.__dict__ for res in self.results],
|
|
109
|
-
index=pd.MultiIndex.from_frame(
|
|
110
|
-
pd.DataFrame([res.properties.__dict__ for res in self.results])
|
|
111
|
-
),
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
def get_score(self, props: ScoreProperties):
|
|
115
|
-
for r in self.results:
|
|
116
|
-
if r.properties == props:
|
|
117
|
-
return r.score
|
|
118
|
-
|
|
119
|
-
class El(BaseModel):
|
|
120
|
-
name: str
|
|
121
|
-
start: int
|
|
122
|
-
stop: int
|
|
123
|
-
|
|
124
|
-
class Score(BaseModel):
|
|
125
|
-
score: ScoreValues
|
|
126
|
-
properties: ScoreProperties
|
|
127
|
-
|
|
128
|
-
class ScoreValues(BaseModel):
|
|
129
|
-
intra: float
|
|
130
|
-
inter: float
|
|
131
|
-
positioning: float
|
|
132
|
-
total: float
|
|
133
|
-
|
|
134
|
-
class ScoreProperties(BaseModel):
|
|
135
|
-
difficulty: int=3
|
|
136
|
-
truncate: bool=False
|
|
137
|
-
|
|
138
|
-
def __eq__(self, other):
|
|
139
|
-
if not isinstance(other, ScoreProperties):
|
|
140
|
-
return False
|
|
141
|
-
return self.difficulty == other.difficulty and self.truncate == other.truncate
|
|
142
|
-
|
|
143
|
-
class Man(BaseModel):
|
|
144
|
-
name: str
|
|
145
|
-
k: float
|
|
146
|
-
id: str
|
|
147
|
-
sp: int
|
|
148
|
-
wd: float
|
|
149
|
-
start: int
|
|
150
|
-
stop: int
|
|
151
|
-
sel: bool
|
|
152
|
-
background: str
|
|
153
|
-
|
|
154
|
-
class Data(BaseModel):
|
|
155
|
-
VN: float = None
|
|
156
|
-
VE: float = None
|
|
157
|
-
VD: float = None
|
|
158
|
-
dPD: float = None #
|
|
159
|
-
r: float
|
|
160
|
-
p: float
|
|
161
|
-
yw: float
|
|
162
|
-
N: float
|
|
163
|
-
E: float
|
|
164
|
-
D: float
|
|
165
|
-
time: int
|
|
166
|
-
roll: float
|
|
167
|
-
pitch: float
|
|
168
|
-
yaw: float
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def get_scores(file: str) -> pd.DataFrame:
|
|
173
|
-
fcj = FCJ.model_validate_json(open(file, "r").read())
|
|
174
|
-
return fcj.pfc_version_df()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|