pyBADA 0.1.0__py3-none-any.whl

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 (99) hide show
  1. pyBADA/TCL.py +8731 -0
  2. pyBADA/__init__.py +0 -0
  3. pyBADA/aircraft/BADA3/DUMMY/BADA.GPF +113 -0
  4. pyBADA/aircraft/BADA3/DUMMY/BZJT__.APF +25 -0
  5. pyBADA/aircraft/BADA3/DUMMY/BZJT__.OPF +61 -0
  6. pyBADA/aircraft/BADA3/DUMMY/BZJT__.PTD +139 -0
  7. pyBADA/aircraft/BADA3/DUMMY/BZJT__.PTF +73 -0
  8. pyBADA/aircraft/BADA3/DUMMY/GA____.APF +25 -0
  9. pyBADA/aircraft/BADA3/DUMMY/GA____.OPF +61 -0
  10. pyBADA/aircraft/BADA3/DUMMY/GA____.PTD +71 -0
  11. pyBADA/aircraft/BADA3/DUMMY/GA____.PTF +39 -0
  12. pyBADA/aircraft/BADA3/DUMMY/J2H___.APF +25 -0
  13. pyBADA/aircraft/BADA3/DUMMY/J2H___.OPF +61 -0
  14. pyBADA/aircraft/BADA3/DUMMY/J2H___.PTD +131 -0
  15. pyBADA/aircraft/BADA3/DUMMY/J2H___.PTF +69 -0
  16. pyBADA/aircraft/BADA3/DUMMY/J2M___.APF +25 -0
  17. pyBADA/aircraft/BADA3/DUMMY/J2M___.OPF +61 -0
  18. pyBADA/aircraft/BADA3/DUMMY/J2M___.PTD +123 -0
  19. pyBADA/aircraft/BADA3/DUMMY/J2M___.PTF +65 -0
  20. pyBADA/aircraft/BADA3/DUMMY/J4H___.APF +25 -0
  21. pyBADA/aircraft/BADA3/DUMMY/J4H___.OPF +61 -0
  22. pyBADA/aircraft/BADA3/DUMMY/J4H___.PTD +139 -0
  23. pyBADA/aircraft/BADA3/DUMMY/J4H___.PTF +73 -0
  24. pyBADA/aircraft/BADA3/DUMMY/ReleaseSummary +35 -0
  25. pyBADA/aircraft/BADA3/DUMMY/SYNONYM.NEW +155 -0
  26. pyBADA/aircraft/BADA3/DUMMY/TP2M__.APF +25 -0
  27. pyBADA/aircraft/BADA3/DUMMY/TP2M__.OPF +61 -0
  28. pyBADA/aircraft/BADA3/DUMMY/TP2M__.PTD +99 -0
  29. pyBADA/aircraft/BADA3/DUMMY/TP2M__.PTF +53 -0
  30. pyBADA/aircraft/BADA4/DUMMY/ACM_BADA4.xsd +556 -0
  31. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST.ATF +106 -0
  32. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST.xml +244 -0
  33. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA+20.PTD +182 -0
  34. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA+20.PTF +41 -0
  35. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA.PTD +182 -0
  36. pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA.PTF +41 -0
  37. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP.ATF +191 -0
  38. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP.xml +540 -0
  39. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA+20.PTD +218 -0
  40. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA+20.PTF +49 -0
  41. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA.PTD +218 -0
  42. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA.PTF +49 -0
  43. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/LRC.dat +38 -0
  44. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MEC.dat +58 -0
  45. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MRC.dat +38 -0
  46. pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/OPTALT.dat +37 -0
  47. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN.ATF +204 -0
  48. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN.xml +648 -0
  49. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA+20.PTD +281 -0
  50. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA+20.PTF +63 -0
  51. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA.PTD +281 -0
  52. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA.PTF +63 -0
  53. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/ECON.OPT +37 -0
  54. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/LRC.OPT +38 -0
  55. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/MEC.OPT +58 -0
  56. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/MRC.OPT +38 -0
  57. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/OPTALT.OPT +37 -0
  58. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus.ATF +238 -0
  59. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus.xml +737 -0
  60. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA+20.PTD +281 -0
  61. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA+20.PTF +63 -0
  62. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA.PTD +281 -0
  63. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA.PTF +63 -0
  64. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/ECON.OPT +37 -0
  65. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/LRC.OPT +36 -0
  66. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/MEC.OPT +56 -0
  67. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/MRC.OPT +36 -0
  68. pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/OPTALT.OPT +37 -0
  69. pyBADA/aircraft/BADA4/DUMMY/GPF.xml +130 -0
  70. pyBADA/aircraft/BADA4/DUMMY/GPF_BADA4.xsd +75 -0
  71. pyBADA/aircraft/BADA4/DUMMY/aircraft_model_default.xml +311 -0
  72. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH.ATF +97 -0
  73. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH.xml +82 -0
  74. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH_ISA+20.PTD +632 -0
  75. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH_ISA+20.PTF +71 -0
  76. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH_ISA.PTD +632 -0
  77. pyBADA/aircraft/BADAH/DUMMY/DUMH/DUMH_ISA.PTF +71 -0
  78. pyBADA/aircraft/BADAH/DUMMY/DUMH/LRC.OPT +142 -0
  79. pyBADA/aircraft/BADAH/DUMMY/DUMH/MEC.OPT +142 -0
  80. pyBADA/aircraft/BADAH/DUMMY/DUMH/MRC.OPT +142 -0
  81. pyBADA/aircraft.py +414 -0
  82. pyBADA/atmosphere.py +345 -0
  83. pyBADA/bada3.py +4566 -0
  84. pyBADA/bada4.py +5327 -0
  85. pyBADA/badaE.py +3317 -0
  86. pyBADA/badaH.py +3632 -0
  87. pyBADA/configuration.py +98 -0
  88. pyBADA/constants.py +64 -0
  89. pyBADA/conversions.py +197 -0
  90. pyBADA/data/magneticDeclinationGridData.json +247067 -0
  91. pyBADA/flightTrajectory.py +929 -0
  92. pyBADA/geodesic.py +760 -0
  93. pyBADA/magnetic.py +119 -0
  94. pyBADA/trajectoryPrediction.py +175 -0
  95. pybada-0.1.0.dist-info/METADATA +57 -0
  96. pybada-0.1.0.dist-info/RECORD +99 -0
  97. pybada-0.1.0.dist-info/WHEEL +4 -0
  98. pybada-0.1.0.dist-info/licenses/AUTHORS +2 -0
  99. pybada-0.1.0.dist-info/licenses/LICENCE.txt +287 -0
@@ -0,0 +1,929 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ pyBADA
4
+ Generic flight trajectory module
5
+ Developped @EUROCONTROL (EIH)
6
+ 2024
7
+ """
8
+
9
+ __author__ = "Henrich Glaser-Opitz"
10
+ __copyright__ = "Copyright 2024, EUROCONTROL (EIH)"
11
+ __license__ = "BADA Eurocontrol"
12
+ __version__ = "1.0.0"
13
+ __maintainer__ = "Henrich Glaser-Opitz"
14
+ __email__ = "henrich.glaser-opitz@eurocontrol.int"
15
+ __status__ = "Development"
16
+ __docformat__ = "reStructuredText"
17
+
18
+
19
+ import os
20
+ import datetime
21
+ import pandas as pd
22
+ import simplekml
23
+
24
+ from pyBADA import conversions as conv
25
+
26
+
27
+ class FlightTrajectory:
28
+ """This class implements the flight trajectory module and handles
29
+ all the operations on the flight trajectory
30
+
31
+ """
32
+
33
+ def __init__(self):
34
+ self.flightData = {}
35
+
36
+ def createFT(self):
37
+ """This function creates a flight trajectory and populate it with input data
38
+
39
+ :param AC: aircraft {BADA3/4/H/E}
40
+ :param Hp: altitude
41
+ :param TAS: True Air Speed (TAS)
42
+ :param CAS: Calibrated Air Speed (CAS)
43
+ :param M: Mach speed (M)
44
+ :param ROCD: Rate of Climb/Descent
45
+ :param FUEL: fuel consumption
46
+ :param P: Power
47
+ :param slope: trajectory slope
48
+ :param acc: acceleration
49
+ :param THR: thrust
50
+ :param config: aerodynamic configuration
51
+ :param HLid: High Lift Device - Level of deployement
52
+ :param LG: Landing Gear level of deployment
53
+ :param mass: aircraft mass
54
+ :param LAT: Geographical Latitude
55
+ :param LON: Geographical Longitude
56
+ :param HDG: aircraft heading
57
+ :param time: time flown
58
+ :param dist: distance flown
59
+ :param comment: comment describing the trajectory segment
60
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
61
+ :type Hp: float
62
+ :type TAS: float
63
+ :type CAS: float
64
+ :type M: float
65
+ :type ROCD: float
66
+ :type FUEL: float
67
+ :type P: float
68
+ :type slope: float
69
+ :type acc: float
70
+ :type THR: float
71
+ :type config: string
72
+ :type HLid: float
73
+ :type LG: string
74
+ :type mass: float
75
+ :type LAT: float
76
+ :type LON: float
77
+ :type HDG: float
78
+ :type time: float
79
+ :type dist: float
80
+ :type comment: string
81
+
82
+ :returns: aircraft flight trajectory
83
+ :rtype: dict{list[float]}.
84
+ """
85
+
86
+ # Define the empty DataFrame with columns
87
+ flightTrajectory = pd.DataFrame(
88
+ columns=[
89
+ "Hp",
90
+ "TAS",
91
+ "CAS",
92
+ "GS",
93
+ "M",
94
+ "ROCD",
95
+ "ESF",
96
+ "FUEL",
97
+ "FUELCONSUMED",
98
+ "Preq",
99
+ "Peng",
100
+ "Pav",
101
+ "slope",
102
+ "acc",
103
+ "THR",
104
+ "DRAG",
105
+ "config",
106
+ "HLid",
107
+ "LG",
108
+ "mass",
109
+ "LAT",
110
+ "LON",
111
+ "HDGTrue",
112
+ "HDGMagnetic",
113
+ "time",
114
+ "dist",
115
+ "comment",
116
+ "BankAngle",
117
+ "ROT",
118
+ ]
119
+ )
120
+ return flightTrajectory
121
+
122
+ @staticmethod
123
+ def createFlightTrajectoryDataframe(flight_data):
124
+ """This function creates a pandas dataframe from the trajectory data in form of a list of data,
125
+ and fill in the missing values with None, to ensure the same size columns
126
+
127
+ :param flight_data: trajectory data
128
+ :type flight_data: dict{list[float]}.
129
+
130
+ :returns: aircraft flight trajectory
131
+ :rtype: pandas dataframe.
132
+ """
133
+ # Find the maximum length of all lists in the flight data (ignore the Aircraft object)
134
+ max_length = max(
135
+ len(lst) if isinstance(lst, list) else 0
136
+ for key, lst in flight_data.items()
137
+ if key != "Aircraft"
138
+ )
139
+
140
+ # Function to pad lists with None to ensure all lists are of equal length
141
+ def pad_list(lst, max_length):
142
+ return lst + [None] * (max_length - len(lst))
143
+
144
+ # Pad each list to the same length
145
+ for key in flight_data:
146
+ flight_data[key] = (
147
+ pad_list(flight_data[key], max_length)
148
+ if isinstance(flight_data[key], list)
149
+ else [None] * max_length
150
+ )
151
+
152
+ # Convert the padded data to a DataFrame
153
+ flightTrajectory = pd.DataFrame(flight_data)
154
+
155
+ # Explode all columns that contain lists
156
+ columns_to_explode = [key for key in flight_data]
157
+
158
+ # Explode the DataFrame
159
+ flightTrajectory_exploded = flightTrajectory.explode(columns_to_explode)
160
+
161
+ return flightTrajectory_exploded
162
+
163
+ def getACList(self):
164
+ """This function return the list of aircraft in the flightTrajectory object
165
+
166
+ :returns: list of aircraft in the current flight trajectory object
167
+ :rtype: list[BadaAircraft].
168
+ """
169
+
170
+ return list(self.flightData.keys())
171
+
172
+ def addFT(self, AC, flightTrajectory):
173
+ """This function adds the flight trajectory based on the aircraft
174
+
175
+ .. note::this will overwrite the stored data for the same aircraft
176
+
177
+ :param AC: BadaAircraft {BADA3/4/H/E}
178
+ :param flightTrajectory: aircraft full flight trajectory
179
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
180
+ :type flightTrajectory: pandas dataframe.
181
+ """
182
+
183
+ self.flightData[AC] = flightTrajectory
184
+
185
+ def getFT(self, AC):
186
+ """This function returns the flight trajectory based on the aircraft
187
+
188
+ :param AC: BadaAircraft {BADA3/4/H/E}
189
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
190
+
191
+ :returns: aircraft flight trajectory
192
+ :rtype: pandas dataframe.
193
+ """
194
+
195
+ return self.flightData.get(AC)
196
+
197
+ def getAllValues(self, AC, parameter):
198
+ """This function returns the list of values corresponding to the aircarft trajectory
199
+ and defined parameter name
200
+
201
+ :param AC: BadaAircraft {BADA3/4/H/E}
202
+ :param parameter: name of the parameter to search for in the flight trajectory
203
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
204
+ :type parameter: string
205
+
206
+ :returns: value of a selected parameter for the whole trajectory
207
+ :rtype: list[float]
208
+ """
209
+
210
+ values = self.getFT(AC).get(parameter)
211
+
212
+ if values is not None:
213
+ return values.tolist()
214
+ else:
215
+ return []
216
+
217
+ def getFinalValue(self, AC, parameter):
218
+ """This function returns last value corresponding to the aircarft trajectory
219
+ and defined parameter name
220
+
221
+ :param AC: BadaAircraft {BADA3/4/H/E}
222
+ :param parameter: name of the parameter to search for in the flight trajectory
223
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
224
+ :type parameter: list[string]
225
+
226
+ :returns: final value in the list of a selected parameter for the whole trajectory
227
+ :rtype: float or string
228
+ """
229
+
230
+ if isinstance(parameter, list):
231
+ finalValueList = []
232
+ for param in parameter:
233
+ parameterValues = self.getAllValues(AC, param)
234
+
235
+ if not parameterValues:
236
+ finalValueList.append(None)
237
+ else:
238
+ finalValueList.append(parameterValues[-1])
239
+ return finalValueList
240
+
241
+ else:
242
+ parameterValues = self.getAllValues(AC, parameter)
243
+ if not parameterValues:
244
+ return None
245
+ else:
246
+ return self.getAllValues(AC, parameter)[-1]
247
+
248
+ def append(self, AC, flightTrajectoryToAppend):
249
+ """This function will append data of 2 consecutive flight trajectories and merge them in terms of time and distance
250
+ if the aircraft is not in the list, the trajectory will be added to the flightTrajectory object
251
+
252
+ :param AC: BadaAircraft {BADA3/4/H/E}
253
+ :param flightTrajectoryToAppend: second flight trajectory to combine with original one [dict]
254
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
255
+ :type flightTrajectoryToAppend: dict{list[float]}.
256
+ """
257
+
258
+ # retrieve the original trajectory
259
+ flightTrajectory = self.getFT(AC)
260
+
261
+ # Drop columns with all NaN values from both DataFrames before concatenating
262
+ if flightTrajectory is not None:
263
+ flightTrajectory = flightTrajectory.dropna(axis=1, how="all")
264
+
265
+ if flightTrajectoryToAppend is not None:
266
+ flightTrajectoryToAppend = flightTrajectoryToAppend.dropna(
267
+ axis=1, how="all"
268
+ )
269
+
270
+ # Make a deep copy of flightTrajectoryToAppend to avoid SettingWithCopyWarning
271
+ flightTrajectoryToAppend = flightTrajectoryToAppend.copy()
272
+
273
+ # Handle cumulative columns (time, distance, fuelConsumed)
274
+ cumulative_columns = ["time", "dist", "FUELCONSUMED"]
275
+ if flightTrajectory is not None and not flightTrajectory.empty:
276
+ # For cumulative columns, add the last value of the original to the subsequent values in the appended trajectory
277
+ for col in cumulative_columns:
278
+ if (
279
+ col in flightTrajectory.columns
280
+ and col in flightTrajectoryToAppend.columns
281
+ ):
282
+ last_value = flightTrajectory[col].iloc[
283
+ -1
284
+ ] # Get last value of original trajectory
285
+
286
+ # Ensure both columns are cast to float64 before performing the addition
287
+ flightTrajectoryToAppend[col] = flightTrajectoryToAppend[
288
+ col
289
+ ].astype(float)
290
+
291
+ # Perform the cumulative addition using .loc[]
292
+ flightTrajectoryToAppend.loc[:, col] = flightTrajectoryToAppend[
293
+ col
294
+ ] + float(last_value)
295
+
296
+ # Concatenating the two trajectory data
297
+ flightTrajectoryCombined = pd.concat(
298
+ [flightTrajectory, flightTrajectoryToAppend], ignore_index=True
299
+ )
300
+
301
+ # rewrite the original trajectory data
302
+ self.addFT(AC, flightTrajectoryCombined)
303
+
304
+ def cut(self, AC, parameter, threshold, direction="BELOW"):
305
+ """This function cuts data from aircraft flight trajectory based on input field name and value.
306
+
307
+ .. note::The value should be sorted to work as expected.
308
+
309
+ :param AC: BadaAircraft {BADA3/4/H/E}
310
+ :param parameter: name of the parameter to take into account
311
+ :param threshold: value of the parameter where the cut shall be performed []
312
+ :param direction: cut above or below set threshold value []
313
+ :type AC: {Bada3Aircraft, Bada4Aircraft, BadaEAircraft, BadaHAircraft}.
314
+ :type parameter: string.
315
+ :type threshold: float.
316
+ :type direction: string {BELOW/ABOVE}.
317
+ """
318
+
319
+ flightTrajectory = self.getFT(AC)
320
+
321
+ if direction == "ABOVE":
322
+ flightTrajectoryCut = flightTrajectory[
323
+ flightTrajectory[parameter] < threshold
324
+ ]
325
+ elif direction == "BELOW":
326
+ flightTrajectoryCut = flightTrajectory[
327
+ flightTrajectory[parameter] > threshold
328
+ ]
329
+
330
+ self.addFT(AC, flightTrajectoryCut)
331
+
332
+ def save2csv(self, saveToPath, separator=","):
333
+ """
334
+ This function saves the trajectory into a CSV file.
335
+
336
+ :param saveToPath: Path to directory where the file should be stored.
337
+ :param separator: Separator to be used in the CSV file (only applicable for CSV). Default is a comma (',').
338
+ :type saveToPath: string
339
+ :type separator: string
340
+ :returns: None
341
+ """
342
+
343
+ # Get the current time in a suitable format for filenames
344
+ currentTime = "_".join(
345
+ str(datetime.datetime.now()).split(".")[0].split(" ")
346
+ ).replace(":", "-")
347
+
348
+ # Create the full directory path
349
+ filepath = os.path.join(saveToPath, f"export_{currentTime}")
350
+
351
+ # Check if the directory exists, if not create it
352
+ if not os.path.exists(filepath):
353
+ os.makedirs(filepath)
354
+
355
+ # Loop through the aircraft list
356
+ for AC in self.getACList():
357
+ # Get the aircraft ID
358
+ AC_ID = str(id(AC))
359
+
360
+ # Flight Trajectory data
361
+ flightTrajectory = self.getFT(AC)
362
+
363
+ filename = os.path.join(filepath, f"{AC.acName}_ID{AC_ID}.csv")
364
+
365
+ # get custom header based on the BADA Family and some other calculation specificities
366
+ if AC.BADAFamily.BADA3:
367
+ if (
368
+ "LAT" in flightTrajectory.columns
369
+ and "LON" in flightTrajectory.columns
370
+ ):
371
+ customHeader = [
372
+ "Hp [ft]",
373
+ "TAS [kt]",
374
+ "CAS [kt]",
375
+ "GS [kt]",
376
+ "M [-]",
377
+ "acc [m/s^2]",
378
+ "ROCD [ft/min]",
379
+ "ESF []",
380
+ "FUEL [kg/s]",
381
+ "FUELCONSUMED [kg]",
382
+ "THR [N]",
383
+ " DRAG [N]",
384
+ "t [s]",
385
+ "d [NM]",
386
+ "slope [deg]",
387
+ "m [kg]",
388
+ "config",
389
+ "LAT [deg]",
390
+ "LON [deg]",
391
+ "HDG True [deg]",
392
+ "HDG Magnetic [deg]",
393
+ "bankAngle [deg]",
394
+ " ROT [deg/s]",
395
+ "COMMENT",
396
+ ]
397
+ else:
398
+ customHeader = [
399
+ "Hp [ft]",
400
+ "TAS [kt]",
401
+ "CAS [kt]",
402
+ "GS [kt]",
403
+ "M [-]",
404
+ "acc [m/s^2]",
405
+ "ROCD [ft/min]",
406
+ "ESF []",
407
+ "FUEL [kg/s]",
408
+ "FUELCONSUMED [kg]",
409
+ "THR [N]",
410
+ " DRAG [N]",
411
+ "t [s]",
412
+ "d [NM]",
413
+ "slope [deg]",
414
+ "m [kg]",
415
+ "config",
416
+ "bankAngle [deg]",
417
+ " ROT [deg/s]",
418
+ "COMMENT",
419
+ ]
420
+
421
+ elif AC.BADAFamily.BADA4:
422
+ if (
423
+ "LAT" in flightTrajectory.columns
424
+ and "LON" in flightTrajectory.columns
425
+ ):
426
+ customHeader = [
427
+ "Hp [ft]",
428
+ "TAS [kt]",
429
+ "CAS [kt]",
430
+ "GS [kt]",
431
+ "M [-]",
432
+ "acc [m/s^2]",
433
+ "ROCD [ft/min]",
434
+ "ESF []",
435
+ "FUEL [kg/s]",
436
+ "FUELCONSUMED [kg]",
437
+ "THR [N]",
438
+ " DRAG [N]",
439
+ "t [s]",
440
+ "d [NM]",
441
+ "slope [deg]",
442
+ "m [kg]",
443
+ "config",
444
+ "HLid",
445
+ "LG",
446
+ "LAT [deg]",
447
+ "LON [deg]",
448
+ "HDG True [deg]",
449
+ "HDG Magnetic [deg]",
450
+ "bankAngle [deg]",
451
+ " ROT [deg/s]",
452
+ "COMMENT",
453
+ ]
454
+ else:
455
+ customHeader = [
456
+ "Hp [ft]",
457
+ "TAS [kt]",
458
+ "CAS [kt]",
459
+ "GS [kt]",
460
+ "M [-]",
461
+ "acc [m/s^2]",
462
+ "ROCD [ft/min]",
463
+ "ESF []",
464
+ "FUEL [kg/s]",
465
+ "FUELCONSUMED [kg]",
466
+ "THR [N]",
467
+ " DRAG [N]",
468
+ "t [s]",
469
+ "d [NM]",
470
+ "slope [deg]",
471
+ "m [kg]",
472
+ "config",
473
+ "HLid",
474
+ "LG",
475
+ "bankAngle [deg]",
476
+ " ROT [deg/s]",
477
+ "COMMENT",
478
+ ]
479
+
480
+ elif AC.BADAFamily.BADAH:
481
+ if (
482
+ "LAT" in flightTrajectory.columns
483
+ and "LON" in flightTrajectory.columns
484
+ ):
485
+ customHeader = [
486
+ "Hp [ft]",
487
+ "TAS [kt]",
488
+ "CAS [kt]",
489
+ "GS [kt]",
490
+ "M [-]",
491
+ "acc [m/s^2]",
492
+ "ROCD [ft/min]",
493
+ "ESF []",
494
+ "FUEL [kg/s]",
495
+ "FUELCONSUMED [kg]",
496
+ "Peng [W]",
497
+ "Preq [W]",
498
+ "Pav [W]",
499
+ "t [s]",
500
+ "d [NM]",
501
+ "slope [deg]",
502
+ "m [kg]",
503
+ "LAT [deg]",
504
+ "LON [deg]",
505
+ "HDG True [deg]",
506
+ "HDG Magnetic [deg]",
507
+ "bankAngle [deg]",
508
+ " ROT [deg/s]",
509
+ "COMMENT",
510
+ ]
511
+ else:
512
+ customHeader = [
513
+ "Hp [ft]",
514
+ "TAS [kt]",
515
+ "CAS [kt]",
516
+ "GS [kt]",
517
+ "M [-]",
518
+ "acc [m/s^2]",
519
+ "ROCD [ft/min]",
520
+ "ESF []",
521
+ "FUEL [kg/s]",
522
+ "FUELCONSUMED [kg]",
523
+ "Peng [W]",
524
+ "Preq [W]",
525
+ "Pav [W]",
526
+ "t [s]",
527
+ "d [NM]",
528
+ "slope [deg]",
529
+ "m [kg]",
530
+ "bankAngle [deg]",
531
+ " ROT [deg/s]",
532
+ "COMMENT",
533
+ ]
534
+
535
+ elif AC.BADAFamily.BADAE:
536
+ if (
537
+ "LAT" in flightTrajectory.columns
538
+ and "LON" in flightTrajectory.columns
539
+ ):
540
+ customHeader = [
541
+ "Hp [ft]",
542
+ "TAS [kt]",
543
+ "CAS [kt]",
544
+ "GS [kt]",
545
+ "M [-]",
546
+ "acc [m/s^2]",
547
+ "ROCD [ft/min]",
548
+ "ESF []",
549
+ "Pmec [W]",
550
+ "Pelc [W]",
551
+ "Pbat, [W]",
552
+ "SOCr [%/h]",
553
+ "SOC [%]",
554
+ "Ibat [A]",
555
+ "Vbat [V];",
556
+ "Vgbat [V]",
557
+ "t [s]",
558
+ "d [NM]",
559
+ "slope [deg]",
560
+ "m [kg]",
561
+ "LAT [deg]",
562
+ "LON [deg]",
563
+ "HDG True [deg]",
564
+ "HDG Magnetic [deg]",
565
+ "bankAngle [deg]",
566
+ " ROT [deg/s]",
567
+ "COMMENT",
568
+ ]
569
+ else:
570
+ customHeader = [
571
+ "Hp [ft]",
572
+ "TAS [kt]",
573
+ "CAS [kt]",
574
+ "GS [kt]",
575
+ "M [-]",
576
+ "acc [m/s^2]",
577
+ "ROCD [ft/min]",
578
+ "ESF []",
579
+ "Pmec [W]",
580
+ "Pelc [W]",
581
+ "Pbat, [W]",
582
+ "SOCr [%/h]",
583
+ "SOC [%]",
584
+ "Ibat [A]",
585
+ "Vbat [V];",
586
+ "Vgbat [V]",
587
+ "t [s]",
588
+ "d [NM]",
589
+ "slope [deg]",
590
+ "m [kg]",
591
+ "bankAngle [deg]",
592
+ " ROT [deg/s]",
593
+ "COMMENT",
594
+ ]
595
+
596
+ # Save to CSV file with custom header and separator
597
+ flightTrajectory.to_csv(
598
+ filename, sep=separator, index=False, header=customHeader
599
+ )
600
+
601
+ def save2xlsx(self, saveToPath):
602
+ """
603
+ This function saves the trajectory into a Excel/xlsx file.
604
+
605
+ :param saveToPath: Path to directory where the file should be stored.
606
+ :param separator: Separator to be used in the CSV file (only applicable for CSV). Default is a comma (',').
607
+ :type saveToPath: string
608
+ :type separator: string
609
+ :returns: None
610
+ """
611
+
612
+ # Get the current time in a suitable format for filenames
613
+ currentTime = "_".join(
614
+ str(datetime.datetime.now()).split(".")[0].split(" ")
615
+ ).replace(":", "-")
616
+
617
+ # Create the full directory path
618
+ filepath = os.path.join(saveToPath, f"export_{currentTime}")
619
+
620
+ # Check if the directory exists, if not create it
621
+ if not os.path.exists(filepath):
622
+ os.makedirs(filepath)
623
+
624
+ # Loop through the aircraft list
625
+ for AC in self.getACList():
626
+ # Get the aircraft ID
627
+ AC_ID = str(id(AC))
628
+
629
+ # Flight Trajectory data
630
+ flightTrajectory = self.getFT(AC)
631
+
632
+ filename = os.path.join(filepath, f"{AC.acName}_ID{AC_ID}.xlsx")
633
+
634
+ # get custom header based on the BADA Family and some other calculation specificities
635
+ if AC.BADAFamily.BADA3:
636
+ if (
637
+ "LAT" in flightTrajectory.columns
638
+ and "LON" in flightTrajectory.columns
639
+ ):
640
+ customHeader = [
641
+ "Hp [ft]",
642
+ "TAS [kt]",
643
+ "CAS [kt]",
644
+ "GS [kt]",
645
+ "M [-]",
646
+ "acc [m/s^2]",
647
+ "ROCD [ft/min]",
648
+ "ESF []",
649
+ "FUEL [kg/s]",
650
+ "FUELCONSUMED [kg]",
651
+ "THR [N]",
652
+ " DRAG [N]",
653
+ "t [s]",
654
+ "d [NM]",
655
+ "slope [deg]",
656
+ "m [kg]",
657
+ "config",
658
+ "LAT [deg]",
659
+ "LON [deg]",
660
+ "HDG True [deg]",
661
+ "HDG Magnetic [deg]",
662
+ "bankAngle [deg]",
663
+ " ROT [deg/s]",
664
+ "COMMENT",
665
+ ]
666
+ else:
667
+ customHeader = [
668
+ "Hp [ft]",
669
+ "TAS [kt]",
670
+ "CAS [kt]",
671
+ "GS [kt]",
672
+ "M [-]",
673
+ "acc [m/s^2]",
674
+ "ROCD [ft/min]",
675
+ "ESF []",
676
+ "FUEL [kg/s]",
677
+ "FUELCONSUMED [kg]",
678
+ "THR [N]",
679
+ " DRAG [N]",
680
+ "t [s]",
681
+ "d [NM]",
682
+ "slope [deg]",
683
+ "m [kg]",
684
+ "config",
685
+ "bankAngle [deg]",
686
+ " ROT [deg/s]",
687
+ "COMMENT",
688
+ ]
689
+
690
+ elif AC.BADAFamily.BADA4:
691
+ if (
692
+ "LAT" in flightTrajectory.columns
693
+ and "LON" in flightTrajectory.columns
694
+ ):
695
+ customHeader = [
696
+ "Hp [ft]",
697
+ "TAS [kt]",
698
+ "CAS [kt]",
699
+ "GS [kt]",
700
+ "M [-]",
701
+ "acc [m/s^2]",
702
+ "ROCD [ft/min]",
703
+ "ESF []",
704
+ "FUEL [kg/s]",
705
+ "FUELCONSUMED [kg]",
706
+ "THR [N]",
707
+ " DRAG [N]",
708
+ "t [s]",
709
+ "d [NM]",
710
+ "slope [deg]",
711
+ "m [kg]",
712
+ "config",
713
+ "HLid",
714
+ "LG",
715
+ "LAT [deg]",
716
+ "LON [deg]",
717
+ "HDG True [deg]",
718
+ "HDG Magnetic [deg]",
719
+ "bankAngle [deg]",
720
+ " ROT [deg/s]",
721
+ "COMMENT",
722
+ ]
723
+ else:
724
+ customHeader = [
725
+ "Hp [ft]",
726
+ "TAS [kt]",
727
+ "CAS [kt]",
728
+ "GS [kt]",
729
+ "M [-]",
730
+ "acc [m/s^2]",
731
+ "ROCD [ft/min]",
732
+ "ESF []",
733
+ "FUEL [kg/s]",
734
+ "FUELCONSUMED [kg]",
735
+ "THR [N]",
736
+ " DRAG [N]",
737
+ "t [s]",
738
+ "d [NM]",
739
+ "slope [deg]",
740
+ "m [kg]",
741
+ "config",
742
+ "HLid",
743
+ "LG",
744
+ "bankAngle [deg]",
745
+ " ROT [deg/s]",
746
+ "COMMENT",
747
+ ]
748
+
749
+ elif AC.BADAFamily.BADAH:
750
+ if (
751
+ "LAT" in flightTrajectory.columns
752
+ and "LON" in flightTrajectory.columns
753
+ ):
754
+ customHeader = [
755
+ "Hp [ft]",
756
+ "TAS [kt]",
757
+ "CAS [kt]",
758
+ "GS [kt]",
759
+ "M [-]",
760
+ "acc [m/s^2]",
761
+ "ROCD [ft/min]",
762
+ "ESF []",
763
+ "FUEL [kg/s]",
764
+ "FUELCONSUMED [kg]",
765
+ "Peng [W]",
766
+ "Preq [W]",
767
+ "Pav [W]",
768
+ "t [s]",
769
+ "d [NM]",
770
+ "slope [deg]",
771
+ "m [kg]",
772
+ "LAT [deg]",
773
+ "LON [deg]",
774
+ "HDG True [deg]",
775
+ "HDG Magnetic [deg]",
776
+ "bankAngle [deg]",
777
+ " ROT [deg/s]",
778
+ "COMMENT",
779
+ ]
780
+ else:
781
+ customHeader = [
782
+ "Hp [ft]",
783
+ "TAS [kt]",
784
+ "CAS [kt]",
785
+ "GS [kt]",
786
+ "M [-]",
787
+ "acc [m/s^2]",
788
+ "ROCD [ft/min]",
789
+ "ESF []",
790
+ "FUEL [kg/s]",
791
+ "FUELCONSUMED [kg]",
792
+ "Peng [W]",
793
+ "Preq [W]",
794
+ "Pav [W]",
795
+ "t [s]",
796
+ "d [NM]",
797
+ "slope [deg]",
798
+ "m [kg]",
799
+ "bankAngle [deg]",
800
+ " ROT [deg/s]",
801
+ "COMMENT",
802
+ ]
803
+
804
+ elif AC.BADAFamily.BADAE:
805
+ if (
806
+ "LAT" in flightTrajectory.columns
807
+ and "LON" in flightTrajectory.columns
808
+ ):
809
+ customHeader = [
810
+ "Hp [ft]",
811
+ "TAS [kt]",
812
+ "CAS [kt]",
813
+ "GS [kt]",
814
+ "M [-]",
815
+ "acc [m/s^2]",
816
+ "ROCD [ft/min]",
817
+ "ESF []",
818
+ "Pmec [W]",
819
+ "Pelc [W]",
820
+ "Pbat, [W]",
821
+ "SOCr [%/h]",
822
+ "SOC [%]",
823
+ "Ibat [A]",
824
+ "Vbat [V];",
825
+ "Vgbat [V]",
826
+ "t [s]",
827
+ "d [NM]",
828
+ "slope [deg]",
829
+ "m [kg]",
830
+ "LAT [deg]",
831
+ "LON [deg]",
832
+ "HDG True [deg]",
833
+ "HDG Magnetic [deg]",
834
+ "bankAngle [deg]",
835
+ " ROT [deg/s]",
836
+ "COMMENT",
837
+ ]
838
+ else:
839
+ customHeader = [
840
+ "Hp [ft]",
841
+ "TAS [kt]",
842
+ "CAS [kt]",
843
+ "GS [kt]",
844
+ "M [-]",
845
+ "acc [m/s^2]",
846
+ "ROCD [ft/min]",
847
+ "ESF []",
848
+ "Pmec [W]",
849
+ "Pelc [W]",
850
+ "Pbat, [W]",
851
+ "SOCr [%/h]",
852
+ "SOC [%]",
853
+ "Ibat [A]",
854
+ "Vbat [V];",
855
+ "Vgbat [V]",
856
+ "t [s]",
857
+ "d [NM]",
858
+ "slope [deg]",
859
+ "m [kg]",
860
+ "bankAngle [deg]",
861
+ " ROT [deg/s]",
862
+ "COMMENT",
863
+ ]
864
+
865
+ # Save to xlsx file, since xlsx format doesn’t use a separator
866
+ with pd.ExcelWriter(filename, engine="xlsxwriter") as writer:
867
+ flightTrajectory.to_excel(writer, index=False, header=customHeader)
868
+
869
+ def save2kml(self, saveToPath):
870
+ """
871
+ This function saves the trajectory into a KML file.
872
+
873
+ :param saveToPath: Path to directory where the file should be stored.
874
+ :param separator: Separator to be used in the CSV file (only applicable for CSV). Default is a comma (',').
875
+ :type saveToPath: string
876
+ :type separator: string
877
+ :returns: None
878
+ """
879
+ # Create a KML object
880
+ kml = simplekml.Kml()
881
+
882
+ # Get the current time in a suitable format for filenames
883
+ currentTime = "_".join(
884
+ str(datetime.datetime.now()).split(".")[0].split(" ")
885
+ ).replace(":", "-")
886
+
887
+ # Create the full directory path
888
+ filepath = os.path.join(saveToPath, f"export_{currentTime}")
889
+
890
+ # Check if the directory exists, if not create it
891
+ if not os.path.exists(filepath):
892
+ os.makedirs(filepath)
893
+
894
+ # Loop through the aircraft list
895
+ for AC in self.getACList():
896
+ # Get the aircraft ID
897
+ AC_ID = str(id(AC))
898
+
899
+ # Flight Trajectory data
900
+ flightTrajectory = self.getFT(AC)
901
+
902
+ if not all(col in flightTrajectory.columns for col in ["LAT", "LON", "Hp"]):
903
+ print(f"Skipping {AC_ID}: Required columns (LAT, LON, Hp) are missing.")
904
+ continue
905
+
906
+ filename = os.path.join(filepath, f"{AC.acName}_ID{AC_ID}.kml")
907
+
908
+ # Create a LineString for each aircraft's trajectory
909
+ linestring = kml.newlinestring(name=f"{AC.acName} Trajectory")
910
+ linestring.coords = [
911
+ (row["LON"], row["LAT"], conv.ft2m(row["Hp"]))
912
+ for _, row in flightTrajectory.iterrows()
913
+ ]
914
+ linestring.altitudemode = (
915
+ simplekml.AltitudeMode.absolute
916
+ ) # Set altitude mode to absolute
917
+
918
+ # Customize the line style for altitude extrusion and color (Yellow)
919
+ linestring.style.linestyle.color = simplekml.Color.yellow # Yellow line
920
+ linestring.style.linestyle.width = 3 # Line width in pixels
921
+ linestring.extrude = 1 # Enable altitude extrusion
922
+
923
+ # Customize the fill color (extruded space) between the line and the ground
924
+ linestring.style.polystyle.color = simplekml.Color.changealpha(
925
+ "80", simplekml.Color.yellow
926
+ ) # 50% transparent yellow
927
+
928
+ # Save the KML file
929
+ kml.save(filename)