pyBADA 0.1.0__py3-none-any.whl → 0.1.1__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.
- pyBADA/TCL.py +1573 -797
- pyBADA/aircraft/BADA4/DUMMY/ACM_BADA4.xsd +3 -1
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST.ATF +33 -33
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST.xml +3 -1
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA+20.PTD +30 -30
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA+20.PTF +14 -14
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA.PTD +30 -30
- pyBADA/aircraft/BADA4/DUMMY/Dummy-PST/Dummy-PST_ISA.PTF +14 -14
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP.ATF +143 -143
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP.xml +83 -81
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA+20.PTD +153 -153
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA+20.PTF +21 -21
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA.PTD +153 -153
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/Dummy-TBP_ISA.PTF +21 -21
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/LRC.OPT +36 -0
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MEC.OPT +56 -0
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MRC.OPT +36 -0
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/{OPTALT.dat → OPTALT.OPT} +5 -5
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN.ATF +158 -158
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN.xml +123 -121
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA+20.PTD +216 -216
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA+20.PTF +28 -28
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA.PTD +216 -216
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/Dummy-TWIN_ISA.PTF +28 -28
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/ECON.OPT +25 -25
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/LRC.OPT +22 -24
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/MEC.OPT +42 -44
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/MRC.OPT +22 -24
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN/OPTALT.OPT +20 -20
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus.ATF +181 -181
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus.xml +190 -188
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA+20.PTD +216 -216
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA+20.PTF +25 -25
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA.PTD +204 -204
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/Dummy-TWIN-plus_ISA.PTF +25 -25
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/ECON.OPT +25 -25
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/LRC.OPT +22 -22
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/MEC.OPT +42 -42
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/MRC.OPT +22 -22
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TWIN-plus/OPTALT.OPT +20 -20
- pyBADA/aircraft.py +180 -145
- pyBADA/atmosphere.py +117 -87
- pyBADA/bada3.py +1233 -621
- pyBADA/bada4.py +1537 -799
- pyBADA/badaH.py +1103 -508
- pyBADA/configuration.py +82 -24
- pyBADA/constants.py +0 -9
- pyBADA/conversions.py +0 -10
- pyBADA/flightTrajectory.py +154 -151
- pyBADA/geodesic.py +278 -179
- pyBADA/magnetic.py +54 -16
- pyBADA/trajectoryPrediction.py +27 -26
- {pybada-0.1.0.dist-info → pybada-0.1.1.dist-info}/METADATA +22 -7
- {pybada-0.1.0.dist-info → pybada-0.1.1.dist-info}/RECORD +57 -59
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/LRC.dat +0 -38
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MEC.dat +0 -58
- pyBADA/aircraft/BADA4/DUMMY/Dummy-TBP/MRC.dat +0 -38
- pyBADA/aircraft/BADA4/DUMMY/aircraft_model_default.xml +0 -311
- pyBADA/badaE.py +0 -3317
- {pybada-0.1.0.dist-info → pybada-0.1.1.dist-info}/WHEEL +0 -0
- {pybada-0.1.0.dist-info → pybada-0.1.1.dist-info}/licenses/AUTHORS +0 -0
- {pybada-0.1.0.dist-info → pybada-0.1.1.dist-info}/licenses/LICENCE.txt +0 -0
pyBADA/TCL.py
CHANGED
|
@@ -6,15 +6,6 @@ Developped @EUROCONTROL (EIH)
|
|
|
6
6
|
2024
|
|
7
7
|
"""
|
|
8
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
9
|
import os
|
|
19
10
|
import numpy as np
|
|
20
11
|
from pyBADA.geodesic import Vincenty as vincenty
|
|
@@ -69,64 +60,97 @@ def constantSpeedLevel(
|
|
|
69
60
|
magneticDeclinationGrid=None,
|
|
70
61
|
**kwargs,
|
|
71
62
|
):
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
:param
|
|
82
|
-
:param
|
|
83
|
-
:param
|
|
84
|
-
:param
|
|
85
|
-
:param
|
|
86
|
-
:param
|
|
87
|
-
:param
|
|
88
|
-
:param
|
|
89
|
-
:param
|
|
90
|
-
:param
|
|
91
|
-
:param
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
:param
|
|
96
|
-
:param
|
|
97
|
-
:param
|
|
98
|
-
:param
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
:
|
|
103
|
-
:
|
|
104
|
-
:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
63
|
+
"""
|
|
64
|
+
Calculates the time, fuel consumption, and other parameters for a level flight
|
|
65
|
+
segment at a constant speed for a given aircraft in the BADA model. It supports step-climb,
|
|
66
|
+
constant heading (true or magnetic), and turns.
|
|
67
|
+
|
|
68
|
+
The function handles different BADA families (BADA3, BADA4, BADAH, BADAE), and computes various
|
|
69
|
+
parameters such as altitude, speed, fuel consumption, power, heading, and mass based on aircraft
|
|
70
|
+
performance data.
|
|
71
|
+
|
|
72
|
+
:param AC: Aircraft object {BADA3/4/H/E}
|
|
73
|
+
:param lengthType: Specifies if the length of the flight segment is based on 'distance' [NM] or 'time' [s].
|
|
74
|
+
:param length: The length of the flight segment. Distance [NM] or Time [s] depending on lengthType.
|
|
75
|
+
:param speedType: Specifies the type of speed to follow {M, CAS, TAS}.
|
|
76
|
+
:param v: The target speed in [kt] for CAS/TAS or [-] for MACH.
|
|
77
|
+
:param Hp_init: Initial pressure altitude at the start of the flight segment [ft].
|
|
78
|
+
:param m_init: Initial mass of the aircraft [kg].
|
|
79
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
80
|
+
:param maxRFL: Maximum cruise altitude limit [ft]. Default is infinity.
|
|
81
|
+
:param wS: Wind speed component along the longitudinal axis (positive for headwind, negative for tailwind) [kt]. Default is 0.0.
|
|
82
|
+
:param turnMetrics: Dictionary containing turn parameters:
|
|
83
|
+
- rateOfTurn [deg/s]
|
|
84
|
+
- bankAngle [deg]
|
|
85
|
+
- directionOfTurn {LEFT/RIGHT}. Default is no turn (straight flight).
|
|
86
|
+
:param stepClimb: Boolean to enable or disable step climb during the cruise segment. Default is False.
|
|
87
|
+
:param Lat: Geographical latitude of the starting point [deg].
|
|
88
|
+
:param Lon: Geographical longitude of the starting point [deg].
|
|
89
|
+
:param initialHeading: Dictionary defining the initial heading and its type:
|
|
90
|
+
- magnetic: Magnetic heading [deg].
|
|
91
|
+
- true: True heading [deg].
|
|
92
|
+
- constantHeading: Whether to fly along a constant heading (loxodrome). Default is None.
|
|
93
|
+
:param flightPhase: Defines the phase of flight, e.g., "Cruise", "Climb", "Descent". Default is "Cruise".
|
|
94
|
+
:param magneticDeclinationGrid: Optional magnetic declination grid to correct headings. Default is None.
|
|
95
|
+
:param kwargs: Additional optional parameters:
|
|
96
|
+
- mass_const: Boolean. If True, keeps the aircraft mass constant during the segment. Default is False.
|
|
97
|
+
- SOC_init: Initial battery state of charge for electric aircraft [%]. Default is 100 for BADAE.
|
|
98
|
+
- speedBrakes: Dictionary defining whether speed brakes are deployed and their drag coefficient {deployed: False, value: 0.03}.
|
|
99
|
+
- ROCD_min: Minimum rate of climb/descent threshold to identify service ceiling [ft/min]. Default varies by aircraft type.
|
|
100
|
+
- config: Default aerodynamic configuration. Values: TO, IC, CR, AP, LD.
|
|
101
|
+
- HpStep: Altitude step for step climb [ft]. Default is 2000 ft.
|
|
102
|
+
- m_iter: Number of iterations for mass integration. Default is 1 for BADAE, 2 for others.
|
|
103
|
+
- step_length: The step length for each iteration in [NM] or [s], depending on the lengthType. Default is 100 NM for BADA3/4 and 10 NM for BADAH/BADAE.
|
|
104
|
+
|
|
105
|
+
:returns: A pandas DataFrame containing the flight trajectory with columns such as:
|
|
106
|
+
- Hp: Altitude [ft]
|
|
107
|
+
- TAS: True Air Speed [kt]
|
|
108
|
+
- CAS: Calibrated Air Speed [kt]
|
|
109
|
+
- GS: Ground Speed [kt]
|
|
110
|
+
- M: Mach number [-]
|
|
111
|
+
- ROCD: Rate of Climb/Descent [ft/min]
|
|
112
|
+
- ESF: Energy Share Factor[-]
|
|
113
|
+
- FUEL: Fuel flow [kg/s]
|
|
114
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
115
|
+
- THR: Thrust force [N]
|
|
116
|
+
- DRAG: Drag force [N]
|
|
117
|
+
- time: Elapsed time [s]
|
|
118
|
+
- dist: Distance flown [NM]
|
|
119
|
+
- slope: Trajectory slope [deg]
|
|
120
|
+
- mass: Aircraft mass [kg]
|
|
121
|
+
- config: Aerodynamic configuration
|
|
122
|
+
- LAT: Latitude [deg]
|
|
123
|
+
- LON: Longitude [deg]
|
|
124
|
+
- HDGTrue: True heading [deg]
|
|
125
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
126
|
+
- BankAngle: Bank angle [deg]
|
|
127
|
+
- ROT: Rate of turn [deg/s]
|
|
128
|
+
- For BADAH:
|
|
129
|
+
- Preq: Required power [W]
|
|
130
|
+
- Peng: Generated power [W]
|
|
131
|
+
- Pav: Available power [W]
|
|
132
|
+
- For BADAE (electric aircraft):
|
|
133
|
+
- Pmec: Mechanical power [W]
|
|
134
|
+
- Pelc: Electrical power [W]
|
|
135
|
+
- Pbat: Power supplied by the battery [W]
|
|
136
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
137
|
+
- SOC: Battery state of charge [%]
|
|
138
|
+
- Ibat: Battery current [A]
|
|
139
|
+
- Vbat: Battery voltage [V]
|
|
140
|
+
- Vgbat: Ground battery voltage [V]
|
|
141
|
+
- Comment: Comments for each segment
|
|
142
|
+
:rtype: pandas.DataFrame
|
|
143
|
+
|
|
144
|
+
This function works by iteratively calculating the flight trajectory for a given segment of the flight,
|
|
145
|
+
taking into account the specified flight conditions, and updating the aircraft’s state (altitude, speed, fuel, etc.)
|
|
146
|
+
at each step of the iteration. The trajectory is returned as a DataFrame containing all relevant flight parameters.
|
|
147
|
+
|
|
148
|
+
Key considerations:
|
|
149
|
+
- Magnetic declination can be corrected based on the provided grid.
|
|
150
|
+
- Supports handling turns and constant heading navigation.
|
|
151
|
+
- Step climbs are optionally allowed if enabled.
|
|
152
|
+
- Takes into account wind speed and flight phase-specific configurations.
|
|
153
|
+
- Power and fuel consumption are calculated differently for each BADA family (BADAH and BADAE handle power instead of fuel in different ways).
|
|
130
154
|
"""
|
|
131
155
|
|
|
132
156
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -177,7 +201,9 @@ def constantSpeedLevel(
|
|
|
177
201
|
|
|
178
202
|
# speed brakes application
|
|
179
203
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
180
|
-
speedBrakes = kwargs.get(
|
|
204
|
+
speedBrakes = kwargs.get(
|
|
205
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
206
|
+
)
|
|
181
207
|
|
|
182
208
|
# optional parameter - iteration step length based on the type of aircraft
|
|
183
209
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
@@ -191,9 +217,13 @@ def constantSpeedLevel(
|
|
|
191
217
|
|
|
192
218
|
# weight iteration constant
|
|
193
219
|
if AC.BADAFamily.BADAE:
|
|
194
|
-
m_iter = kwargs.get(
|
|
220
|
+
m_iter = kwargs.get(
|
|
221
|
+
"m_iter", 1
|
|
222
|
+
) # number of iterations for integration loop[-]
|
|
195
223
|
else:
|
|
196
|
-
m_iter = kwargs.get(
|
|
224
|
+
m_iter = kwargs.get(
|
|
225
|
+
"m_iter", 2
|
|
226
|
+
) # number of iterations for integration loop[-]
|
|
197
227
|
|
|
198
228
|
# comment line describing type of trajectory calculation
|
|
199
229
|
if flightPhase != "Cruise":
|
|
@@ -317,7 +347,9 @@ def constantSpeedLevel(
|
|
|
317
347
|
|
|
318
348
|
# atmosphere properties
|
|
319
349
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
320
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
350
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
351
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
352
|
+
)
|
|
321
353
|
# aircraft speed
|
|
322
354
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
323
355
|
v=v, speedType=speedType, theta=theta, delta=delta, sigma=sigma
|
|
@@ -327,10 +359,14 @@ def constantSpeedLevel(
|
|
|
327
359
|
if turnFlight:
|
|
328
360
|
if turnMetrics["bankAngle"] != 0.0:
|
|
329
361
|
# bankAngle is defined
|
|
330
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
362
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
363
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
364
|
+
)
|
|
331
365
|
else:
|
|
332
366
|
# rateOfTurn is defined
|
|
333
|
-
bankAngle = AC.bankAngle(
|
|
367
|
+
bankAngle = AC.bankAngle(
|
|
368
|
+
rateOfTurn=rateOfTurn, v=TAS_i
|
|
369
|
+
) # [degrees]
|
|
334
370
|
|
|
335
371
|
if lengthType == "distance":
|
|
336
372
|
# step time is: distance differantial divided by ground speed
|
|
@@ -359,7 +395,9 @@ def constantSpeedLevel(
|
|
|
359
395
|
# BADAH or BADAE
|
|
360
396
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
361
397
|
# compute Power required for level flight
|
|
362
|
-
Preq_i = AC.Preq(
|
|
398
|
+
Preq_i = AC.Preq(
|
|
399
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
400
|
+
)
|
|
363
401
|
Peng_i = Preq_i
|
|
364
402
|
if AC.BADAFamily.BADAH:
|
|
365
403
|
Pav_i = AC.Pav(
|
|
@@ -372,7 +410,8 @@ def constantSpeedLevel(
|
|
|
372
410
|
|
|
373
411
|
if Pav_i < Preq_i:
|
|
374
412
|
warnings.warn(
|
|
375
|
-
"Power Available is lower than Power Required",
|
|
413
|
+
"Power Available is lower than Power Required",
|
|
414
|
+
UserWarning,
|
|
376
415
|
)
|
|
377
416
|
|
|
378
417
|
# BADAH
|
|
@@ -390,7 +429,9 @@ def constantSpeedLevel(
|
|
|
390
429
|
Pelc_i = Preq_i / AC.eta
|
|
391
430
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC_i)
|
|
392
431
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC_i)
|
|
393
|
-
Vgbat_i =
|
|
432
|
+
Vgbat_i = (
|
|
433
|
+
AC.Vocbat(SOC=SOC_i) - AC.R0bat(SOC=SOC_i) * Ibat_i
|
|
434
|
+
)
|
|
394
435
|
|
|
395
436
|
# BADA4
|
|
396
437
|
elif AC.BADAFamily.BADA4:
|
|
@@ -414,12 +455,16 @@ def constantSpeedLevel(
|
|
|
414
455
|
currentConfig=config_i,
|
|
415
456
|
)
|
|
416
457
|
|
|
417
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
458
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
459
|
+
config=config_i
|
|
460
|
+
)
|
|
418
461
|
|
|
419
462
|
# compute lift coefficient
|
|
420
463
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
421
464
|
# compute drag coefficient
|
|
422
|
-
CD = AC.CD(
|
|
465
|
+
CD = AC.CD(
|
|
466
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
467
|
+
)
|
|
423
468
|
# compute drag force
|
|
424
469
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
425
470
|
# compute thrust force and fuel flow
|
|
@@ -702,7 +747,9 @@ def constantSpeedLevel(
|
|
|
702
747
|
# determine atmosphere properties at upper cruise altitude
|
|
703
748
|
nextHp = min(Hp_i + HpStep, maxRFL)
|
|
704
749
|
H_m = conv.ft2m(nextHp) # altitude [m]
|
|
705
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
750
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
751
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
752
|
+
)
|
|
706
753
|
|
|
707
754
|
# aircraft speed at upper cruise altitude
|
|
708
755
|
[M_up, CAS_up, TAS_up] = atm.convertSpeed(
|
|
@@ -771,19 +818,31 @@ def constantSpeedLevel(
|
|
|
771
818
|
currentConfig=config_i,
|
|
772
819
|
)
|
|
773
820
|
|
|
774
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
821
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
822
|
+
config=config_i
|
|
823
|
+
)
|
|
775
824
|
|
|
776
825
|
# compute lift coefficient
|
|
777
826
|
CL = AC.CL(M=M_up, delta=delta, mass=mass_i, nz=nz)
|
|
778
827
|
# compute drag coefficient
|
|
779
|
-
CD = AC.CD(
|
|
828
|
+
CD = AC.CD(
|
|
829
|
+
M=M_up,
|
|
830
|
+
CL=CL,
|
|
831
|
+
HLid=HLid_i,
|
|
832
|
+
LG=LG_i,
|
|
833
|
+
speedBrakes=speedBrakes,
|
|
834
|
+
)
|
|
780
835
|
# compute drag force
|
|
781
836
|
Drag = AC.D(M=M_up, delta=delta, CD=CD)
|
|
782
837
|
# compute thrust force and fuel flow
|
|
783
838
|
THR_up = Drag
|
|
784
839
|
CT = AC.CT(Thrust=THR_up, delta=delta)
|
|
785
840
|
FUEL_up = AC.ff(
|
|
786
|
-
CT=CT,
|
|
841
|
+
CT=CT,
|
|
842
|
+
delta=delta,
|
|
843
|
+
theta=theta,
|
|
844
|
+
M=M_up,
|
|
845
|
+
DeltaTemp=DeltaTemp,
|
|
787
846
|
) # [kg/s]
|
|
788
847
|
|
|
789
848
|
# Compare specific range at current and upper cruise altitudes
|
|
@@ -808,7 +867,10 @@ def constantSpeedLevel(
|
|
|
808
867
|
|
|
809
868
|
flightEvolution = "const" + speedType
|
|
810
869
|
ESF_i = AC.esf(
|
|
811
|
-
h=H_m,
|
|
870
|
+
h=H_m,
|
|
871
|
+
flightEvolution=flightEvolution,
|
|
872
|
+
M=M_up,
|
|
873
|
+
DeltaTemp=DeltaTemp,
|
|
812
874
|
)
|
|
813
875
|
temp_const = (theta * const.temp_0) / (
|
|
814
876
|
theta * const.temp_0 - DeltaTemp
|
|
@@ -894,7 +956,9 @@ def constantSpeedLevel(
|
|
|
894
956
|
ROT.extend(flightTrajectory_CL["ROT"])
|
|
895
957
|
|
|
896
958
|
comment_CL = flightTrajectory_CL["comment"]
|
|
897
|
-
Comment.extend(
|
|
959
|
+
Comment.extend(
|
|
960
|
+
[com + "_stepClimb" for com in comment_CL]
|
|
961
|
+
)
|
|
898
962
|
|
|
899
963
|
# BADA4
|
|
900
964
|
if AC.BADAFamily.BADA4:
|
|
@@ -916,7 +980,9 @@ def constantSpeedLevel(
|
|
|
916
980
|
if Lat and Lon and magneticHeading:
|
|
917
981
|
LAT.extend(flightTrajectory_CL["LAT"])
|
|
918
982
|
LON.extend(flightTrajectory_CL["LON"])
|
|
919
|
-
HDGMagnetic.extend(
|
|
983
|
+
HDGMagnetic.extend(
|
|
984
|
+
flightTrajectory_CL["HDGMagnetic"]
|
|
985
|
+
)
|
|
920
986
|
HDGTrue.extend(flightTrajectory_CL["HDGTrue"])
|
|
921
987
|
|
|
922
988
|
# Compute cruise fuel at upper altitude
|
|
@@ -936,18 +1002,20 @@ def constantSpeedLevel(
|
|
|
936
1002
|
|
|
937
1003
|
# ensure continuity of configuration change within the segment
|
|
938
1004
|
if config:
|
|
939
|
-
config_i = (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
currentConfig=config_i,
|
|
944
|
-
)
|
|
1005
|
+
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
1006
|
+
phase=flightPhase,
|
|
1007
|
+
previousConfig=config[-1],
|
|
1008
|
+
currentConfig=config_i,
|
|
945
1009
|
)
|
|
946
1010
|
|
|
947
1011
|
# compute lift coefficient
|
|
948
|
-
CL = AC.CL(
|
|
1012
|
+
CL = AC.CL(
|
|
1013
|
+
tas=TAS_up, sigma=sigma, mass=mass[-1], nz=nz
|
|
1014
|
+
)
|
|
949
1015
|
# compute drag coefficient
|
|
950
|
-
CD = AC.CD(
|
|
1016
|
+
CD = AC.CD(
|
|
1017
|
+
CL=CL, config=config_i, speedBrakes=speedBrakes
|
|
1018
|
+
)
|
|
951
1019
|
# compute drag force
|
|
952
1020
|
Drag = AC.D(tas=TAS_up, sigma=sigma, CD=CD)
|
|
953
1021
|
# compute thrust force and fuel flow
|
|
@@ -977,12 +1045,10 @@ def constantSpeedLevel(
|
|
|
977
1045
|
|
|
978
1046
|
# ensure continuity of configuration change within the segment
|
|
979
1047
|
if config:
|
|
980
|
-
config_i = (
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
currentConfig=config_i,
|
|
985
|
-
)
|
|
1048
|
+
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
1049
|
+
phase=flightPhase,
|
|
1050
|
+
previousConfig=config[-1],
|
|
1051
|
+
currentConfig=config_i,
|
|
986
1052
|
)
|
|
987
1053
|
|
|
988
1054
|
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
@@ -990,7 +1056,9 @@ def constantSpeedLevel(
|
|
|
990
1056
|
)
|
|
991
1057
|
|
|
992
1058
|
# compute lift coefficient
|
|
993
|
-
CL = AC.CL(
|
|
1059
|
+
CL = AC.CL(
|
|
1060
|
+
M=M_up, delta=delta, mass=mass[-1], nz=nz
|
|
1061
|
+
)
|
|
994
1062
|
# compute drag coefficient
|
|
995
1063
|
CD = AC.CD(
|
|
996
1064
|
M=M_up,
|
|
@@ -1121,58 +1189,89 @@ def constantSpeedROCD(
|
|
|
1121
1189
|
magneticDeclinationGrid=None,
|
|
1122
1190
|
**kwargs,
|
|
1123
1191
|
):
|
|
1124
|
-
"""
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
:param
|
|
1134
|
-
:param
|
|
1135
|
-
:param
|
|
1136
|
-
:param
|
|
1137
|
-
:param
|
|
1138
|
-
:param
|
|
1139
|
-
:param
|
|
1140
|
-
:param
|
|
1141
|
-
:param
|
|
1142
|
-
:param
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
:param
|
|
1147
|
-
:param
|
|
1148
|
-
:
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
:
|
|
1153
|
-
:
|
|
1154
|
-
:
|
|
1155
|
-
:
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
:
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1192
|
+
"""
|
|
1193
|
+
Computes the time, fuel consumption, and other parameters required for an aircraft to climb or descend
|
|
1194
|
+
from a given initial altitude (Hp_init) to a final altitude (Hp_final) at a constant speed and rate of climb/descent (ROCD).
|
|
1195
|
+
|
|
1196
|
+
The function handles multiple BADA families (BADA3, BADA4, BADAH, BADAE), computing various parameters
|
|
1197
|
+
such as altitude, speed, fuel consumption, power, heading, and mass based on the aircraft's performance
|
|
1198
|
+
characteristics. The function supports turn performance, optional heading (true or magnetic), and
|
|
1199
|
+
handling mass changes during the flight.
|
|
1200
|
+
|
|
1201
|
+
:param AC: Aircraft object {BADA3/4/H/E}
|
|
1202
|
+
:param speedType: Type of speed to maintain during the flight {M, CAS, TAS}.
|
|
1203
|
+
:param v: Speed to maintain during the flight - [kt] CAS/TAS or [-] MACH.
|
|
1204
|
+
:param Hp_init: Initial pressure altitude at the start of the segment [ft].
|
|
1205
|
+
:param Hp_final: Final pressure altitude at the end of the segment [ft].
|
|
1206
|
+
:param ROCDtarget: Target rate of climb/descent [ft/min].
|
|
1207
|
+
:param m_init: Initial aircraft mass at the start of the segment [kg].
|
|
1208
|
+
:param DeltaTemp: Deviation from standard ISA temperature [K].
|
|
1209
|
+
:param wS: Wind speed component along the longitudinal axis [kt]. Positive values for headwind, negative for tailwind. Default is 0.0.
|
|
1210
|
+
:param turnMetrics: Dictionary defining turn parameters:
|
|
1211
|
+
- rateOfTurn [deg/s]
|
|
1212
|
+
- bankAngle [deg]
|
|
1213
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
1214
|
+
:param Lat: Geographical latitude of the starting point [deg]. Default is None.
|
|
1215
|
+
:param Lon: Geographical longitude of the starting point [deg]. Default is None.
|
|
1216
|
+
:param initialHeading: Dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
1217
|
+
- magnetic: Magnetic heading [deg].
|
|
1218
|
+
- true: True heading [deg].
|
|
1219
|
+
- constantHeading: Whether to fly along a constant heading (loxodrome). Default is None.
|
|
1220
|
+
:param reducedPower: Boolean specifying whether to apply reduced power during the climb. Default is None.
|
|
1221
|
+
:param directionOfTurn: Direction of the turn {LEFT, RIGHT}. Default is None.
|
|
1222
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declinations used to correct headings. Default is None.
|
|
1223
|
+
:param kwargs: Additional optional parameters:
|
|
1224
|
+
- Hp_step: Altitude step size [ft]. Default is 1000 for BADA3/4, 500 for BADAH/BADAE.
|
|
1225
|
+
- SOC_init: Initial state of charge for electric aircraft [%]. Default is 100 for BADAE.
|
|
1226
|
+
- speedBrakes: Dictionary specifying whether speed brakes are deployed and their drag coefficient {deployed: False, value: 0.03}.
|
|
1227
|
+
- ROCD_min: Minimum ROCD to identify the service ceiling [ft/min]. Default varies by aircraft type.
|
|
1228
|
+
- config: Default aerodynamic configuration. Values: TO, IC, CR, AP, LD. Default is None.
|
|
1229
|
+
- mass_const: Boolean specifying whether to keep the mass constant during the segment. Default is False.
|
|
1230
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 5.
|
|
1231
|
+
|
|
1232
|
+
:returns: A pandas DataFrame containing the flight trajectory, including parameters like:
|
|
1233
|
+
- Hp: Altitude [ft]
|
|
1234
|
+
- TAS: True Air Speed [kt]
|
|
1235
|
+
- CAS: Calibrated Air Speed [kt]
|
|
1236
|
+
- GS: Ground Speed [kt]
|
|
1237
|
+
- M: Mach number [-]
|
|
1238
|
+
- ROCD: Rate of Climb/Descent [ft/min]
|
|
1239
|
+
- ESF: Energy Share Factor [-]
|
|
1240
|
+
- FUEL: Fuel flow [kg/s]
|
|
1241
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
1242
|
+
- THR: Thrust force [N]
|
|
1243
|
+
- DRAG: Drag force [N]
|
|
1244
|
+
- time: Elapsed time [s]
|
|
1245
|
+
- dist: Distance flown [NM]
|
|
1246
|
+
- slope: Trajectory slope [deg]
|
|
1247
|
+
- mass: Aircraft mass [kg]
|
|
1248
|
+
- config: Aerodynamic configuration
|
|
1249
|
+
- LAT: Latitude [deg]
|
|
1250
|
+
- LON: Longitude [deg]
|
|
1251
|
+
- HDGTrue: True heading [deg]
|
|
1252
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
1253
|
+
- BankAngle: Bank angle [deg]
|
|
1254
|
+
- ROT: Rate of turn [deg/s]
|
|
1255
|
+
- For BADAH:
|
|
1256
|
+
- Preq: Required power [W]
|
|
1257
|
+
- Peng: Generated power [W]
|
|
1258
|
+
- Pav: Available power [W]
|
|
1259
|
+
- For BADAE (electric aircraft):
|
|
1260
|
+
- Pmec: Mechanical power [W]
|
|
1261
|
+
- Pelc: Electrical power [W]
|
|
1262
|
+
- Pbat: Power supplied by the battery [W]
|
|
1263
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
1264
|
+
- SOC: Battery state of charge [%]
|
|
1265
|
+
- Ibat: Battery current [A]
|
|
1266
|
+
- Vbat: Battery voltage [V]
|
|
1267
|
+
- Vgbat: Ground battery voltage [V]
|
|
1268
|
+
- Comment: Comments for each segment
|
|
1269
|
+
:rtype: pandas.DataFrame
|
|
1270
|
+
|
|
1271
|
+
Notes:
|
|
1272
|
+
- The function iteratively calculates flight parameters for each altitude step, adjusting fuel, power, and mass.
|
|
1273
|
+
- Magnetic heading and true heading can be adjusted using the magnetic declination grid if provided.
|
|
1274
|
+
- The function supports turns, and constant or changing headings based on input parameters.
|
|
1176
1275
|
"""
|
|
1177
1276
|
|
|
1178
1277
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -1223,7 +1322,9 @@ def constantSpeedROCD(
|
|
|
1223
1322
|
|
|
1224
1323
|
# speed brakes application
|
|
1225
1324
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
1226
|
-
speedBrakes = kwargs.get(
|
|
1325
|
+
speedBrakes = kwargs.get(
|
|
1326
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
1327
|
+
)
|
|
1227
1328
|
|
|
1228
1329
|
# optional parameter - iteration step for altitude loop
|
|
1229
1330
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
@@ -1271,7 +1372,9 @@ def constantSpeedROCD(
|
|
|
1271
1372
|
constHeadingStr = ""
|
|
1272
1373
|
|
|
1273
1374
|
# comment line describing type of trajectory calculation
|
|
1274
|
-
comment =
|
|
1375
|
+
comment = (
|
|
1376
|
+
phase + turnComment + "_const_ROCD_" + speedType + constHeadingStr
|
|
1377
|
+
)
|
|
1275
1378
|
|
|
1276
1379
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
1277
1380
|
comment = comment + "_" + headingToFly + "_Heading"
|
|
@@ -1292,7 +1395,9 @@ def constantSpeedROCD(
|
|
|
1292
1395
|
)
|
|
1293
1396
|
|
|
1294
1397
|
# weight iteration constant
|
|
1295
|
-
m_iter = kwargs.get(
|
|
1398
|
+
m_iter = kwargs.get(
|
|
1399
|
+
"m_iter", 5
|
|
1400
|
+
) # number of iterations for integration loop[-]
|
|
1296
1401
|
|
|
1297
1402
|
# convert ROCD to IS units
|
|
1298
1403
|
ROCDisu = conv.ft2m(ROCDtarget) / 60
|
|
@@ -1361,8 +1466,12 @@ def constantSpeedROCD(
|
|
|
1361
1466
|
|
|
1362
1467
|
# atmosphere properties
|
|
1363
1468
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
1364
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
1365
|
-
|
|
1469
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
1470
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
1471
|
+
)
|
|
1472
|
+
temp_const = (theta * const.temp_0) / (
|
|
1473
|
+
theta * const.temp_0 - DeltaTemp
|
|
1474
|
+
)
|
|
1366
1475
|
|
|
1367
1476
|
# aircraft speed
|
|
1368
1477
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -1371,16 +1480,23 @@ def constantSpeedROCD(
|
|
|
1371
1480
|
|
|
1372
1481
|
# compute Energy Share Factor (ESF)
|
|
1373
1482
|
ESF_i = AC.esf(
|
|
1374
|
-
h=H_m,
|
|
1483
|
+
h=H_m,
|
|
1484
|
+
M=M_i,
|
|
1485
|
+
DeltaTemp=DeltaTemp,
|
|
1486
|
+
flightEvolution=("const" + speedType),
|
|
1375
1487
|
)
|
|
1376
1488
|
|
|
1377
1489
|
if turnFlight:
|
|
1378
1490
|
if turnMetrics["bankAngle"] != 0.0:
|
|
1379
1491
|
# bankAngle is defined
|
|
1380
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
1492
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
1493
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
1494
|
+
)
|
|
1381
1495
|
else:
|
|
1382
1496
|
# rateOfTurn is defined
|
|
1383
|
-
bankAngle = AC.bankAngle(
|
|
1497
|
+
bankAngle = AC.bankAngle(
|
|
1498
|
+
rateOfTurn=rateOfTurn, v=TAS_i
|
|
1499
|
+
) # [degrees]
|
|
1384
1500
|
|
|
1385
1501
|
# Load factor
|
|
1386
1502
|
nz = 1 / cos(radians(bankAngle))
|
|
@@ -1394,7 +1510,9 @@ def constantSpeedROCD(
|
|
|
1394
1510
|
# BADAH or BADAE
|
|
1395
1511
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
1396
1512
|
# compute Power required for level flight
|
|
1397
|
-
Preq_i = AC.Preq(
|
|
1513
|
+
Preq_i = AC.Preq(
|
|
1514
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
1515
|
+
)
|
|
1398
1516
|
# Compute power required for target ROCD
|
|
1399
1517
|
Preq_target_i = AC.Peng_target(
|
|
1400
1518
|
temp=theta * const.temp_0,
|
|
@@ -1422,20 +1540,27 @@ def constantSpeedROCD(
|
|
|
1422
1540
|
# ensure continuity of configuration change within the segment
|
|
1423
1541
|
if config:
|
|
1424
1542
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
1425
|
-
phase=phase,
|
|
1543
|
+
phase=phase,
|
|
1544
|
+
previousConfig=config[-1],
|
|
1545
|
+
currentConfig=config_i,
|
|
1426
1546
|
)
|
|
1427
1547
|
|
|
1428
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
1548
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
1549
|
+
config=config_i
|
|
1550
|
+
)
|
|
1429
1551
|
|
|
1430
1552
|
# compute lift coefficient
|
|
1431
1553
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
1432
1554
|
# compute drag coefficient
|
|
1433
|
-
CD = AC.CD(
|
|
1555
|
+
CD = AC.CD(
|
|
1556
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
1557
|
+
)
|
|
1434
1558
|
# compute drag force
|
|
1435
1559
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
1436
1560
|
# compute thrust force
|
|
1437
1561
|
THR_i = (
|
|
1438
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
1562
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
1563
|
+
+ Drag
|
|
1439
1564
|
) # [N]
|
|
1440
1565
|
|
|
1441
1566
|
# BADA3
|
|
@@ -1468,7 +1593,8 @@ def constantSpeedROCD(
|
|
|
1468
1593
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
1469
1594
|
# compute thrust force
|
|
1470
1595
|
THR_i = (
|
|
1471
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
1596
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
1597
|
+
+ Drag
|
|
1472
1598
|
) # [N]
|
|
1473
1599
|
|
|
1474
1600
|
# check that required thrust/power fits in the avialable thrust/power envelope,
|
|
@@ -1567,21 +1693,39 @@ def constantSpeedROCD(
|
|
|
1567
1693
|
Pelc_i = Preq_target_i / AC.eta
|
|
1568
1694
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
1569
1695
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
1570
|
-
Vgbat_i =
|
|
1696
|
+
Vgbat_i = (
|
|
1697
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
1698
|
+
)
|
|
1571
1699
|
|
|
1572
1700
|
# BADA4
|
|
1573
1701
|
elif AC.BADAFamily.BADA4:
|
|
1574
1702
|
THR_min = AC.Thrust(
|
|
1575
|
-
rating="LIDL",
|
|
1703
|
+
rating="LIDL",
|
|
1704
|
+
delta=delta,
|
|
1705
|
+
theta=theta,
|
|
1706
|
+
M=M_i,
|
|
1707
|
+
DeltaTemp=DeltaTemp,
|
|
1576
1708
|
) # IDLE Thrust
|
|
1577
1709
|
FUEL_min = AC.ff(
|
|
1578
|
-
rating="LIDL",
|
|
1710
|
+
rating="LIDL",
|
|
1711
|
+
delta=delta,
|
|
1712
|
+
theta=theta,
|
|
1713
|
+
M=M_i,
|
|
1714
|
+
DeltaTemp=DeltaTemp,
|
|
1579
1715
|
) # IDLE Fuel Flow
|
|
1580
1716
|
THR_max = AC.Thrust(
|
|
1581
|
-
rating="MCMB",
|
|
1717
|
+
rating="MCMB",
|
|
1718
|
+
delta=delta,
|
|
1719
|
+
theta=theta,
|
|
1720
|
+
M=M_i,
|
|
1721
|
+
DeltaTemp=DeltaTemp,
|
|
1582
1722
|
) # MCMB Thrust
|
|
1583
1723
|
FUEL_max = AC.ff(
|
|
1584
|
-
rating="MCMB",
|
|
1724
|
+
rating="MCMB",
|
|
1725
|
+
delta=delta,
|
|
1726
|
+
theta=theta,
|
|
1727
|
+
M=M_i,
|
|
1728
|
+
DeltaTemp=DeltaTemp,
|
|
1585
1729
|
) # MCMB Fuel Flow
|
|
1586
1730
|
if THR_i < THR_min:
|
|
1587
1731
|
THR_i = THR_min
|
|
@@ -1612,14 +1756,22 @@ def constantSpeedROCD(
|
|
|
1612
1756
|
else:
|
|
1613
1757
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
1614
1758
|
FUEL_i = AC.ff(
|
|
1615
|
-
CT=CT,
|
|
1759
|
+
CT=CT,
|
|
1760
|
+
delta=delta,
|
|
1761
|
+
theta=theta,
|
|
1762
|
+
M=M_i,
|
|
1763
|
+
DeltaTemp=DeltaTemp,
|
|
1616
1764
|
) # [kg/s]
|
|
1617
1765
|
ROCD_i = ROCDtarget
|
|
1618
1766
|
|
|
1619
1767
|
# BADA3
|
|
1620
1768
|
elif AC.BADAFamily.BADA3:
|
|
1621
1769
|
THR_min = AC.Thrust(
|
|
1622
|
-
rating="LIDL",
|
|
1770
|
+
rating="LIDL",
|
|
1771
|
+
v=TAS_i,
|
|
1772
|
+
h=H_m,
|
|
1773
|
+
config="CR",
|
|
1774
|
+
DeltaTemp=DeltaTemp,
|
|
1623
1775
|
) # IDLE Thrust
|
|
1624
1776
|
FUEL_min = AC.ff(
|
|
1625
1777
|
flightPhase="Descent",
|
|
@@ -1630,7 +1782,11 @@ def constantSpeedROCD(
|
|
|
1630
1782
|
adapted=False,
|
|
1631
1783
|
) # IDLE Fuel Flow
|
|
1632
1784
|
THR_max = AC.Thrust(
|
|
1633
|
-
rating="MCMB",
|
|
1785
|
+
rating="MCMB",
|
|
1786
|
+
v=TAS_i,
|
|
1787
|
+
h=H_m,
|
|
1788
|
+
DeltaTemp=DeltaTemp,
|
|
1789
|
+
config="CR",
|
|
1634
1790
|
) # MCMB Thrust
|
|
1635
1791
|
FUEL_max = AC.ff(
|
|
1636
1792
|
flightPhase="Climb",
|
|
@@ -1779,10 +1935,14 @@ def constantSpeedROCD(
|
|
|
1779
1935
|
gamma_i = 90 * np.sign(ROCD_i)
|
|
1780
1936
|
else:
|
|
1781
1937
|
if AC.BADAFamily.BADAE:
|
|
1782
|
-
gamma_i = degrees(
|
|
1938
|
+
gamma_i = degrees(
|
|
1939
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
1940
|
+
)
|
|
1783
1941
|
else:
|
|
1784
1942
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
1785
|
-
gamma_i = degrees(
|
|
1943
|
+
gamma_i = degrees(
|
|
1944
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
1945
|
+
)
|
|
1786
1946
|
|
|
1787
1947
|
Slope.append(gamma_i)
|
|
1788
1948
|
|
|
@@ -1815,7 +1975,9 @@ def constantSpeedROCD(
|
|
|
1815
1975
|
if turnFlight:
|
|
1816
1976
|
step_distance = conv.m2nm(
|
|
1817
1977
|
turn.distance(
|
|
1818
|
-
rateOfTurn=rateOfTurn,
|
|
1978
|
+
rateOfTurn=rateOfTurn,
|
|
1979
|
+
TAS=TAS_i,
|
|
1980
|
+
timeOfTurn=step_time,
|
|
1819
1981
|
)
|
|
1820
1982
|
) # arcLength during the turn [NM]
|
|
1821
1983
|
else:
|
|
@@ -2035,58 +2197,84 @@ def constantSpeedROCD_time(
|
|
|
2035
2197
|
magneticDeclinationGrid=None,
|
|
2036
2198
|
**kwargs,
|
|
2037
2199
|
):
|
|
2038
|
-
"""
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
:param
|
|
2048
|
-
:param
|
|
2049
|
-
:param
|
|
2050
|
-
:param
|
|
2051
|
-
:param
|
|
2052
|
-
:param
|
|
2053
|
-
:param
|
|
2054
|
-
:param
|
|
2055
|
-
:param
|
|
2056
|
-
:param
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
:param
|
|
2061
|
-
:param
|
|
2062
|
-
:
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
:
|
|
2067
|
-
:
|
|
2068
|
-
:
|
|
2069
|
-
:
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
:
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2200
|
+
"""
|
|
2201
|
+
Computes the time, fuel consumption, and performance parameters required for an aircraft to
|
|
2202
|
+
perform a climb or descent based on a set amount of time, while maintaining a constant speed and constant
|
|
2203
|
+
rate of climb/descent (ROCD).
|
|
2204
|
+
|
|
2205
|
+
The function supports various BADA families (BADA3, BADA4, BADAH, BADAE), with different handling for mass changes,
|
|
2206
|
+
aerodynamic configurations, and energy consumption. It calculates parameters such as fuel consumption, power
|
|
2207
|
+
requirements, speed, heading, and altitude changes over the specified duration.
|
|
2208
|
+
|
|
2209
|
+
:param AC: Aircraft object {BADA3/4/H/E}
|
|
2210
|
+
:param length: The length of the segment to fly in time [s].
|
|
2211
|
+
:param speedType: Type of speed to maintain during the flight {M, CAS, TAS}.
|
|
2212
|
+
:param v: Speed to follow - [kt] CAS/TAS or [-] MACH.
|
|
2213
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
2214
|
+
:param ROCDtarget: Rate of climb or descent [ft/min].
|
|
2215
|
+
:param m_init: Initial aircraft mass at the start of the segment [kg].
|
|
2216
|
+
:param DeltaTemp: Deviation from standard ISA temperature [K].
|
|
2217
|
+
:param wS: Wind speed component along the longitudinal axis [kt]. Default is 0.0.
|
|
2218
|
+
:param turnMetrics: Dictionary defining turn parameters:
|
|
2219
|
+
- rateOfTurn [deg/s]
|
|
2220
|
+
- bankAngle [deg]
|
|
2221
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
2222
|
+
:param Lat: Geographical latitude at the start [deg]. Default is None.
|
|
2223
|
+
:param Lon: Geographical longitude at the start [deg]. Default is None.
|
|
2224
|
+
:param initialHeading: Dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
2225
|
+
- magnetic: Magnetic heading [deg].
|
|
2226
|
+
- true: True heading [deg].
|
|
2227
|
+
- constantHeading: Whether to fly along a constant heading (loxodrome). Default is None.
|
|
2228
|
+
:param reducedPower: Boolean specifying whether to apply reduced power during the climb. Default is None.
|
|
2229
|
+
:param directionOfTurn: Direction of the turn {LEFT, RIGHT}. Default is None.
|
|
2230
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declinations used to correct headings. Default is None.
|
|
2231
|
+
:param kwargs: Additional optional parameters:
|
|
2232
|
+
- step_length: Step size in seconds for time iteration. Default is 1 second.
|
|
2233
|
+
- SOC_init: Initial state of charge for electric aircraft [%]. Default is 100 for BADAE.
|
|
2234
|
+
- speedBrakes: Dictionary specifying whether speed brakes are deployed and their drag coefficient {deployed: False, value: 0.03}.
|
|
2235
|
+
- ROCD_min: Minimum ROCD to identify the service ceiling [ft/min]. Default varies by aircraft type.
|
|
2236
|
+
- config: Default aerodynamic configuration. Values: TO, IC, CR, AP, LD. Default is None.
|
|
2237
|
+
- mass_const: Boolean specifying whether to keep the mass constant during the segment. Default is False.
|
|
2238
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 5.
|
|
2239
|
+
|
|
2240
|
+
:returns: A pandas DataFrame containing the flight trajectory, including parameters like:
|
|
2241
|
+
- Hp: Altitude [ft]
|
|
2242
|
+
- TAS: True Air Speed [kt]
|
|
2243
|
+
- CAS: Calibrated Air Speed [kt]
|
|
2244
|
+
- GS: Ground Speed [kt]
|
|
2245
|
+
- M: Mach number [-]
|
|
2246
|
+
- ROCD: Rate of Climb/Descent [ft/min]
|
|
2247
|
+
- ESF: Energy Share Factor [-]
|
|
2248
|
+
- FUEL: Fuel flow [kg/s]
|
|
2249
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
2250
|
+
- THR: Thrust force [N]
|
|
2251
|
+
- DRAG: Drag force [N]
|
|
2252
|
+
- time: Elapsed time [s]
|
|
2253
|
+
- dist: Distance flown [NM]
|
|
2254
|
+
- slope: Trajectory slope [deg]
|
|
2255
|
+
- mass: Aircraft mass [kg]
|
|
2256
|
+
- config: Aerodynamic configuration
|
|
2257
|
+
- LAT: Latitude [deg]
|
|
2258
|
+
- LON: Longitude [deg]
|
|
2259
|
+
- HDGTrue: True heading [deg]
|
|
2260
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
2261
|
+
- BankAngle: Bank angle [deg]
|
|
2262
|
+
- ROT: Rate of turn [deg/s]
|
|
2263
|
+
- For BADAH:
|
|
2264
|
+
- Preq: Required power [W]
|
|
2265
|
+
- Peng: Generated power [W]
|
|
2266
|
+
- Pav: Available power [W]
|
|
2267
|
+
- For BADAE (electric aircraft):
|
|
2268
|
+
- Pmec: Mechanical power [W]
|
|
2269
|
+
- Pelc: Electrical power [W]
|
|
2270
|
+
- Pbat: Power supplied by the battery [W]
|
|
2271
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
2272
|
+
- SOC: Battery state of charge [%]
|
|
2273
|
+
- Ibat: Battery current [A]
|
|
2274
|
+
- Vbat: Battery voltage [V]
|
|
2275
|
+
- Vgbat: Ground battery voltage [V]
|
|
2276
|
+
- Comment: Comments for each segment.
|
|
2277
|
+
:rtype: pandas.DataFrame
|
|
2090
2278
|
"""
|
|
2091
2279
|
|
|
2092
2280
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -2137,7 +2325,9 @@ def constantSpeedROCD_time(
|
|
|
2137
2325
|
|
|
2138
2326
|
# speed brakes application
|
|
2139
2327
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
2140
|
-
speedBrakes = kwargs.get(
|
|
2328
|
+
speedBrakes = kwargs.get(
|
|
2329
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
2330
|
+
)
|
|
2141
2331
|
|
|
2142
2332
|
# step size in [s]
|
|
2143
2333
|
step_length = kwargs.get("step_length", 1)
|
|
@@ -2170,7 +2360,9 @@ def constantSpeedROCD_time(
|
|
|
2170
2360
|
constHeadingStr = ""
|
|
2171
2361
|
|
|
2172
2362
|
# comment line describing type of trajectory calculation
|
|
2173
|
-
comment =
|
|
2363
|
+
comment = (
|
|
2364
|
+
phase + turnComment + "_const_ROCD_" + speedType + constHeadingStr
|
|
2365
|
+
)
|
|
2174
2366
|
|
|
2175
2367
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
2176
2368
|
comment = comment + "_" + headingToFly + "_Heading"
|
|
@@ -2191,7 +2383,9 @@ def constantSpeedROCD_time(
|
|
|
2191
2383
|
)
|
|
2192
2384
|
|
|
2193
2385
|
# weight iteration constant
|
|
2194
|
-
m_iter = kwargs.get(
|
|
2386
|
+
m_iter = kwargs.get(
|
|
2387
|
+
"m_iter", 5
|
|
2388
|
+
) # number of iterations for integration loop[-]
|
|
2195
2389
|
|
|
2196
2390
|
# convert ROCD to IS units
|
|
2197
2391
|
ROCDisu = conv.ft2m(ROCDtarget) / 60
|
|
@@ -2269,8 +2463,12 @@ def constantSpeedROCD_time(
|
|
|
2269
2463
|
for _ in itertools.repeat(None, m_iter):
|
|
2270
2464
|
# atmosphere properties
|
|
2271
2465
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
2272
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
2273
|
-
|
|
2466
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
2467
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
2468
|
+
)
|
|
2469
|
+
temp_const = (theta * const.temp_0) / (
|
|
2470
|
+
theta * const.temp_0 - DeltaTemp
|
|
2471
|
+
)
|
|
2274
2472
|
|
|
2275
2473
|
# aircraft speed
|
|
2276
2474
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -2279,13 +2477,18 @@ def constantSpeedROCD_time(
|
|
|
2279
2477
|
|
|
2280
2478
|
# compute Energy Share Factor (ESF)
|
|
2281
2479
|
ESF_i = AC.esf(
|
|
2282
|
-
h=H_m,
|
|
2480
|
+
h=H_m,
|
|
2481
|
+
M=M_i,
|
|
2482
|
+
DeltaTemp=DeltaTemp,
|
|
2483
|
+
flightEvolution=("const" + speedType),
|
|
2283
2484
|
)
|
|
2284
2485
|
|
|
2285
2486
|
if turnFlight:
|
|
2286
2487
|
if turnMetrics["bankAngle"] != 0.0:
|
|
2287
2488
|
# bankAngle is defined
|
|
2288
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
2489
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
2490
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
2491
|
+
)
|
|
2289
2492
|
else:
|
|
2290
2493
|
# rateOfTurn is defined
|
|
2291
2494
|
bankAngle = AC.bankAngle(
|
|
@@ -2300,7 +2503,9 @@ def constantSpeedROCD_time(
|
|
|
2300
2503
|
# BADAH or BADAE
|
|
2301
2504
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
2302
2505
|
# compute Power required for level flight
|
|
2303
|
-
Preq_i = AC.Preq(
|
|
2506
|
+
Preq_i = AC.Preq(
|
|
2507
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
2508
|
+
)
|
|
2304
2509
|
# Compute power required for target ROCD
|
|
2305
2510
|
Preq_target_i = AC.Peng_target(
|
|
2306
2511
|
temp=theta * const.temp_0,
|
|
@@ -2328,20 +2533,27 @@ def constantSpeedROCD_time(
|
|
|
2328
2533
|
# ensure continuity of configuration change within the segment
|
|
2329
2534
|
if config:
|
|
2330
2535
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
2331
|
-
phase=phase,
|
|
2536
|
+
phase=phase,
|
|
2537
|
+
previousConfig=config[-1],
|
|
2538
|
+
currentConfig=config_i,
|
|
2332
2539
|
)
|
|
2333
2540
|
|
|
2334
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
2541
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
2542
|
+
config=config_i
|
|
2543
|
+
)
|
|
2335
2544
|
|
|
2336
2545
|
# compute lift coefficient
|
|
2337
2546
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
2338
2547
|
# compute drag coefficient
|
|
2339
|
-
CD = AC.CD(
|
|
2548
|
+
CD = AC.CD(
|
|
2549
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
2550
|
+
)
|
|
2340
2551
|
# compute drag force
|
|
2341
2552
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
2342
2553
|
# compute thrust force
|
|
2343
2554
|
THR_i = (
|
|
2344
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
2555
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
2556
|
+
+ Drag
|
|
2345
2557
|
) # [N]
|
|
2346
2558
|
|
|
2347
2559
|
# BADA3
|
|
@@ -2374,7 +2586,8 @@ def constantSpeedROCD_time(
|
|
|
2374
2586
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
2375
2587
|
# compute thrust force
|
|
2376
2588
|
THR_i = (
|
|
2377
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
2589
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
2590
|
+
+ Drag
|
|
2378
2591
|
) # [N]
|
|
2379
2592
|
|
|
2380
2593
|
# check that required thrust/power fits in the avialable thrust/power envelope,
|
|
@@ -2473,21 +2686,39 @@ def constantSpeedROCD_time(
|
|
|
2473
2686
|
Pelc_i = Preq_target_i / AC.eta
|
|
2474
2687
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
2475
2688
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
2476
|
-
Vgbat_i =
|
|
2689
|
+
Vgbat_i = (
|
|
2690
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
2691
|
+
)
|
|
2477
2692
|
|
|
2478
2693
|
# BADA4
|
|
2479
2694
|
elif AC.BADAFamily.BADA4:
|
|
2480
2695
|
THR_min = AC.Thrust(
|
|
2481
|
-
rating="LIDL",
|
|
2696
|
+
rating="LIDL",
|
|
2697
|
+
delta=delta,
|
|
2698
|
+
theta=theta,
|
|
2699
|
+
M=M_i,
|
|
2700
|
+
DeltaTemp=DeltaTemp,
|
|
2482
2701
|
) # IDLE Thrust
|
|
2483
2702
|
FUEL_min = AC.ff(
|
|
2484
|
-
rating="LIDL",
|
|
2703
|
+
rating="LIDL",
|
|
2704
|
+
delta=delta,
|
|
2705
|
+
theta=theta,
|
|
2706
|
+
M=M_i,
|
|
2707
|
+
DeltaTemp=DeltaTemp,
|
|
2485
2708
|
) # IDLE Fuel Flow
|
|
2486
2709
|
THR_max = AC.Thrust(
|
|
2487
|
-
rating="MCMB",
|
|
2710
|
+
rating="MCMB",
|
|
2711
|
+
delta=delta,
|
|
2712
|
+
theta=theta,
|
|
2713
|
+
M=M_i,
|
|
2714
|
+
DeltaTemp=DeltaTemp,
|
|
2488
2715
|
) # MCMB Thrust
|
|
2489
2716
|
FUEL_max = AC.ff(
|
|
2490
|
-
rating="MCMB",
|
|
2717
|
+
rating="MCMB",
|
|
2718
|
+
delta=delta,
|
|
2719
|
+
theta=theta,
|
|
2720
|
+
M=M_i,
|
|
2721
|
+
DeltaTemp=DeltaTemp,
|
|
2491
2722
|
) # MCMB Fuel Flow
|
|
2492
2723
|
if THR_i < THR_min:
|
|
2493
2724
|
THR_i = THR_min
|
|
@@ -2518,14 +2749,22 @@ def constantSpeedROCD_time(
|
|
|
2518
2749
|
else:
|
|
2519
2750
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
2520
2751
|
FUEL_i = AC.ff(
|
|
2521
|
-
CT=CT,
|
|
2752
|
+
CT=CT,
|
|
2753
|
+
delta=delta,
|
|
2754
|
+
theta=theta,
|
|
2755
|
+
M=M_i,
|
|
2756
|
+
DeltaTemp=DeltaTemp,
|
|
2522
2757
|
) # [kg/s]
|
|
2523
2758
|
ROCD_i = ROCDtarget
|
|
2524
2759
|
|
|
2525
2760
|
# BADA3
|
|
2526
2761
|
elif AC.BADAFamily.BADA3:
|
|
2527
2762
|
THR_min = AC.Thrust(
|
|
2528
|
-
rating="LIDL",
|
|
2763
|
+
rating="LIDL",
|
|
2764
|
+
v=TAS_i,
|
|
2765
|
+
h=H_m,
|
|
2766
|
+
config="CR",
|
|
2767
|
+
DeltaTemp=DeltaTemp,
|
|
2529
2768
|
) # IDLE Thrust
|
|
2530
2769
|
FUEL_min = AC.ff(
|
|
2531
2770
|
flightPhase="Descent",
|
|
@@ -2536,7 +2775,11 @@ def constantSpeedROCD_time(
|
|
|
2536
2775
|
adapted=False,
|
|
2537
2776
|
) # IDLE Fuel Flow
|
|
2538
2777
|
THR_max = AC.Thrust(
|
|
2539
|
-
rating="MCMB",
|
|
2778
|
+
rating="MCMB",
|
|
2779
|
+
v=TAS_i,
|
|
2780
|
+
h=H_m,
|
|
2781
|
+
DeltaTemp=DeltaTemp,
|
|
2782
|
+
config="CR",
|
|
2540
2783
|
) # MCMB Thrust
|
|
2541
2784
|
FUEL_max = AC.ff(
|
|
2542
2785
|
flightPhase="Climb",
|
|
@@ -2688,12 +2931,18 @@ def constantSpeedROCD_time(
|
|
|
2688
2931
|
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
2689
2932
|
h=conv.ft2m(Hp_i), DeltaTemp=DeltaTemp
|
|
2690
2933
|
)
|
|
2691
|
-
temp_const = (theta * const.temp_0) / (
|
|
2934
|
+
temp_const = (theta * const.temp_0) / (
|
|
2935
|
+
theta * const.temp_0 - DeltaTemp
|
|
2936
|
+
)
|
|
2692
2937
|
if AC.BADAFamily.BADAE:
|
|
2693
|
-
gamma_i = degrees(
|
|
2938
|
+
gamma_i = degrees(
|
|
2939
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
2940
|
+
)
|
|
2694
2941
|
else:
|
|
2695
2942
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
2696
|
-
gamma_i = degrees(
|
|
2943
|
+
gamma_i = degrees(
|
|
2944
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
2945
|
+
)
|
|
2697
2946
|
|
|
2698
2947
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
2699
2948
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -2730,7 +2979,9 @@ def constantSpeedROCD_time(
|
|
|
2730
2979
|
if turnFlight:
|
|
2731
2980
|
step_distance = conv.m2nm(
|
|
2732
2981
|
turn.distance(
|
|
2733
|
-
rateOfTurn=rateOfTurn,
|
|
2982
|
+
rateOfTurn=rateOfTurn,
|
|
2983
|
+
TAS=TAS_i,
|
|
2984
|
+
timeOfTurn=step_time,
|
|
2734
2985
|
)
|
|
2735
2986
|
) # arcLength during the turn [NM]
|
|
2736
2987
|
else:
|
|
@@ -2932,58 +3183,83 @@ def constantSpeedSlope(
|
|
|
2932
3183
|
magneticDeclinationGrid=None,
|
|
2933
3184
|
**kwargs,
|
|
2934
3185
|
):
|
|
2935
|
-
"""
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
:param
|
|
2944
|
-
:param
|
|
2945
|
-
:param
|
|
2946
|
-
:param
|
|
2947
|
-
:param
|
|
2948
|
-
:param
|
|
2949
|
-
:param
|
|
2950
|
-
:param
|
|
2951
|
-
:param
|
|
2952
|
-
:param
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
:param
|
|
2957
|
-
:param
|
|
2958
|
-
:param
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
:
|
|
2963
|
-
:
|
|
2964
|
-
:
|
|
2965
|
-
:
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
:
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3186
|
+
"""
|
|
3187
|
+
Calculates time, fuel consumption, and other parameters required for an aircraft to perform a climb or descent
|
|
3188
|
+
from an initial altitude (Hp_init) to a final altitude (Hp_final) while maintaining a constant speed and a constant slope.
|
|
3189
|
+
|
|
3190
|
+
It uses different models for BADA (Base of Aircraft Data) families (BADA3, BADA4, BADAH, BADAE) to compute key flight parameters
|
|
3191
|
+
such as energy consumption, rate of climb/descent (ROCD), fuel burn, mass changes, and power requirements. The function also supports
|
|
3192
|
+
complex flight dynamics including turns, heading changes, and wind influences.
|
|
3193
|
+
|
|
3194
|
+
:param AC: Aircraft object {BADA3/4/H/E}.
|
|
3195
|
+
:param speedType: Type of speed to maintain during the flight {M, CAS, TAS}.
|
|
3196
|
+
:param v: Speed to follow - [kt] CAS/TAS or [-] MACH.
|
|
3197
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
3198
|
+
:param Hp_final: Final pressure altitude [ft].
|
|
3199
|
+
:param slopetarget: Target slope (trajectory angle) to be maintained during climb/descent [deg].
|
|
3200
|
+
:param m_init: Initial mass of the aircraft at the start of the segment [kg].
|
|
3201
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
3202
|
+
:param wS: Wind speed component along the longitudinal axis (affects ground speed) [kt]. Default is 0.0.
|
|
3203
|
+
:param turnMetrics: A dictionary defining the turn parameters:
|
|
3204
|
+
- rateOfTurn [deg/s]
|
|
3205
|
+
- bankAngle [deg]
|
|
3206
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
3207
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
3208
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
3209
|
+
:param initialHeading: A dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
3210
|
+
- magnetic: Magnetic heading [deg].
|
|
3211
|
+
- true: True heading [deg].
|
|
3212
|
+
- constantHeading: Whether to maintain a constant heading. Default is None.
|
|
3213
|
+
:param reducedPower: Boolean specifying if reduced power is applied during the climb. Default is None.
|
|
3214
|
+
:param directionOfTurn: Direction of the turn {LEFT, RIGHT}. Default is None.
|
|
3215
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declination used to correct magnetic heading. Default is None.
|
|
3216
|
+
:param kwargs: Additional optional parameters:
|
|
3217
|
+
- Hp_step: Step size in altitude for the iterative calculation [ft]. Default is 1000 ft for BADA3/BADA4, 500 ft for BADAH/BADAE.
|
|
3218
|
+
- SOC_init: Initial battery state of charge for electric aircraft (BADAE) [%]. Default is 100.
|
|
3219
|
+
- speedBrakes: A dictionary specifying whether speed brakes are deployed and the additional drag coefficient {deployed: False, value: 0.03}.
|
|
3220
|
+
- ROCD_min: Minimum rate of climb/descent used to determine service ceiling [ft/min]. Default varies based on aircraft type.
|
|
3221
|
+
- config: Default aerodynamic configuration (TO, IC, CR, AP, LD). Default is None.
|
|
3222
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight segment. Default is False.
|
|
3223
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 5.
|
|
3224
|
+
|
|
3225
|
+
:returns: A pandas DataFrame containing flight trajectory data with the following columns:
|
|
3226
|
+
- Hp: Pressure altitude [ft]
|
|
3227
|
+
- TAS: True Air Speed [kt]
|
|
3228
|
+
- CAS: Calibrated Air Speed [kt]
|
|
3229
|
+
- GS: Ground Speed [kt]
|
|
3230
|
+
- M: Mach number [-]
|
|
3231
|
+
- ROCD: Rate of climb/descent [ft/min]
|
|
3232
|
+
- ESF: Energy Share Factor [-]
|
|
3233
|
+
- FUEL: Fuel flow [kg/s]
|
|
3234
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
3235
|
+
- THR: Thrust force [N]
|
|
3236
|
+
- DRAG: Drag force [N]
|
|
3237
|
+
- time: Elapsed time [s]
|
|
3238
|
+
- dist: Distance flown [NM]
|
|
3239
|
+
- slope: Flight trajectory slope (angle) [deg]
|
|
3240
|
+
- mass: Aircraft mass [kg]
|
|
3241
|
+
- config: Aerodynamic configuration
|
|
3242
|
+
- LAT: Latitude [deg]
|
|
3243
|
+
- LON: Longitude [deg]
|
|
3244
|
+
- HDGTrue: True heading [deg]
|
|
3245
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
3246
|
+
- BankAngle: Bank angle during the turn [deg]
|
|
3247
|
+
- ROT: Rate of turn [deg/s]
|
|
3248
|
+
- Comment: Comments describing the flight segment
|
|
3249
|
+
- For BADAH:
|
|
3250
|
+
- Preq: Required power for level flight [W]
|
|
3251
|
+
- Peng: Generated power [W]
|
|
3252
|
+
- Pav: Available power [W]
|
|
3253
|
+
- For BADAE (electric aircraft):
|
|
3254
|
+
- Pmec: Mechanical power [W]
|
|
3255
|
+
- Pelc: Electrical power [W]
|
|
3256
|
+
- Pbat: Battery power [W]
|
|
3257
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
3258
|
+
- SOC: Battery state of charge [%]
|
|
3259
|
+
- Ibat: Battery current [A]
|
|
3260
|
+
- Vbat: Battery voltage [V]
|
|
3261
|
+
- Vgbat: Ground battery voltage [V]
|
|
3262
|
+
:rtype: pandas.DataFrame
|
|
2987
3263
|
"""
|
|
2988
3264
|
|
|
2989
3265
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -3034,7 +3310,9 @@ def constantSpeedSlope(
|
|
|
3034
3310
|
|
|
3035
3311
|
# speed brakes application
|
|
3036
3312
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
3037
|
-
speedBrakes = kwargs.get(
|
|
3313
|
+
speedBrakes = kwargs.get(
|
|
3314
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
3315
|
+
)
|
|
3038
3316
|
|
|
3039
3317
|
# optional parameter - iteration step for altitude loop
|
|
3040
3318
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
@@ -3079,7 +3357,9 @@ def constantSpeedSlope(
|
|
|
3079
3357
|
constHeadingStr = ""
|
|
3080
3358
|
|
|
3081
3359
|
# comment line describing type of trajectory calculation
|
|
3082
|
-
comment =
|
|
3360
|
+
comment = (
|
|
3361
|
+
phase + turnComment + "_const_Slope_" + speedType + constHeadingStr
|
|
3362
|
+
)
|
|
3083
3363
|
|
|
3084
3364
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
3085
3365
|
comment = comment + "_" + headingToFly + "_Heading"
|
|
@@ -3100,7 +3380,9 @@ def constantSpeedSlope(
|
|
|
3100
3380
|
)
|
|
3101
3381
|
|
|
3102
3382
|
# weight iteration constant
|
|
3103
|
-
m_iter = kwargs.get(
|
|
3383
|
+
m_iter = kwargs.get(
|
|
3384
|
+
"m_iter", 5
|
|
3385
|
+
) # number of iterations for integration loop[-]
|
|
3104
3386
|
|
|
3105
3387
|
# initialize output parameters
|
|
3106
3388
|
Hp = []
|
|
@@ -3166,8 +3448,12 @@ def constantSpeedSlope(
|
|
|
3166
3448
|
|
|
3167
3449
|
# atmosphere properties
|
|
3168
3450
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
3169
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
3170
|
-
|
|
3451
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
3452
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
3453
|
+
)
|
|
3454
|
+
temp_const = (theta * const.temp_0) / (
|
|
3455
|
+
theta * const.temp_0 - DeltaTemp
|
|
3456
|
+
)
|
|
3171
3457
|
|
|
3172
3458
|
# aircraft speed
|
|
3173
3459
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -3177,17 +3463,24 @@ def constantSpeedSlope(
|
|
|
3177
3463
|
if turnFlight:
|
|
3178
3464
|
if turnMetrics["bankAngle"] != 0.0:
|
|
3179
3465
|
# bankAngle is defined
|
|
3180
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
3466
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
3467
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
3468
|
+
)
|
|
3181
3469
|
else:
|
|
3182
3470
|
# rateOfTurn is defined
|
|
3183
|
-
bankAngle = AC.bankAngle(
|
|
3471
|
+
bankAngle = AC.bankAngle(
|
|
3472
|
+
rateOfTurn=rateOfTurn, v=TAS_i
|
|
3473
|
+
) # [degrees]
|
|
3184
3474
|
|
|
3185
3475
|
# Load factor
|
|
3186
3476
|
nz = 1 / cos(radians(bankAngle))
|
|
3187
3477
|
|
|
3188
3478
|
# compute Energy Share Factor (ESF)
|
|
3189
3479
|
ESF_i = AC.esf(
|
|
3190
|
-
h=H_m,
|
|
3480
|
+
h=H_m,
|
|
3481
|
+
M=M_i,
|
|
3482
|
+
DeltaTemp=DeltaTemp,
|
|
3483
|
+
flightEvolution=("const" + speedType),
|
|
3191
3484
|
)
|
|
3192
3485
|
|
|
3193
3486
|
if AC.BADAFamily.BADAE:
|
|
@@ -3205,7 +3498,9 @@ def constantSpeedSlope(
|
|
|
3205
3498
|
# BADAH or BADAE
|
|
3206
3499
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
3207
3500
|
# compute Power required for level flight
|
|
3208
|
-
Preq_i = AC.Preq(
|
|
3501
|
+
Preq_i = AC.Preq(
|
|
3502
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
3503
|
+
)
|
|
3209
3504
|
# Compute power required for target ROCD
|
|
3210
3505
|
Preq_target_i = AC.Peng_target(
|
|
3211
3506
|
temp=theta * const.temp_0,
|
|
@@ -3233,20 +3528,27 @@ def constantSpeedSlope(
|
|
|
3233
3528
|
# ensure continuity of configuration change within the segment
|
|
3234
3529
|
if config:
|
|
3235
3530
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
3236
|
-
phase=phase,
|
|
3531
|
+
phase=phase,
|
|
3532
|
+
previousConfig=config[-1],
|
|
3533
|
+
currentConfig=config_i,
|
|
3237
3534
|
)
|
|
3238
3535
|
|
|
3239
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
3536
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
3537
|
+
config=config_i
|
|
3538
|
+
)
|
|
3240
3539
|
|
|
3241
3540
|
# compute lift coefficient
|
|
3242
3541
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
3243
3542
|
# compute drag coefficient
|
|
3244
|
-
CD = AC.CD(
|
|
3543
|
+
CD = AC.CD(
|
|
3544
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
3545
|
+
)
|
|
3245
3546
|
# compute drag force
|
|
3246
3547
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
3247
3548
|
# compute thrust force
|
|
3248
3549
|
THR_i = (
|
|
3249
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
3550
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
3551
|
+
+ Drag
|
|
3250
3552
|
) # [N]
|
|
3251
3553
|
|
|
3252
3554
|
# BADA3
|
|
@@ -3279,7 +3581,8 @@ def constantSpeedSlope(
|
|
|
3279
3581
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
3280
3582
|
# compute thrust force
|
|
3281
3583
|
THR_i = (
|
|
3282
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
3584
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
3585
|
+
+ Drag
|
|
3283
3586
|
) # [N]
|
|
3284
3587
|
|
|
3285
3588
|
# check that required thrust/power fits in the avialable thrust/power envelope,
|
|
@@ -3378,21 +3681,39 @@ def constantSpeedSlope(
|
|
|
3378
3681
|
Pelc_i = Preq_target_i / AC.eta
|
|
3379
3682
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
3380
3683
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
3381
|
-
Vgbat_i =
|
|
3684
|
+
Vgbat_i = (
|
|
3685
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
3686
|
+
)
|
|
3382
3687
|
|
|
3383
3688
|
# BADA4
|
|
3384
3689
|
elif AC.BADAFamily.BADA4:
|
|
3385
3690
|
THR_min = AC.Thrust(
|
|
3386
|
-
rating="LIDL",
|
|
3691
|
+
rating="LIDL",
|
|
3692
|
+
delta=delta,
|
|
3693
|
+
theta=theta,
|
|
3694
|
+
M=M_i,
|
|
3695
|
+
DeltaTemp=DeltaTemp,
|
|
3387
3696
|
) # IDLE Thrust
|
|
3388
3697
|
FUEL_min = AC.ff(
|
|
3389
|
-
rating="LIDL",
|
|
3698
|
+
rating="LIDL",
|
|
3699
|
+
delta=delta,
|
|
3700
|
+
theta=theta,
|
|
3701
|
+
M=M_i,
|
|
3702
|
+
DeltaTemp=DeltaTemp,
|
|
3390
3703
|
) # IDLE Fuel Flow
|
|
3391
3704
|
THR_max = AC.Thrust(
|
|
3392
|
-
rating="MCMB",
|
|
3705
|
+
rating="MCMB",
|
|
3706
|
+
delta=delta,
|
|
3707
|
+
theta=theta,
|
|
3708
|
+
M=M_i,
|
|
3709
|
+
DeltaTemp=DeltaTemp,
|
|
3393
3710
|
) # MCMB Thrust
|
|
3394
3711
|
FUEL_max = AC.ff(
|
|
3395
|
-
rating="MCMB",
|
|
3712
|
+
rating="MCMB",
|
|
3713
|
+
delta=delta,
|
|
3714
|
+
theta=theta,
|
|
3715
|
+
M=M_i,
|
|
3716
|
+
DeltaTemp=DeltaTemp,
|
|
3396
3717
|
) # MCMB Fuel Flow
|
|
3397
3718
|
|
|
3398
3719
|
if THR_i < THR_min:
|
|
@@ -3424,14 +3745,22 @@ def constantSpeedSlope(
|
|
|
3424
3745
|
else:
|
|
3425
3746
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
3426
3747
|
FUEL_i = AC.ff(
|
|
3427
|
-
CT=CT,
|
|
3748
|
+
CT=CT,
|
|
3749
|
+
delta=delta,
|
|
3750
|
+
theta=theta,
|
|
3751
|
+
M=M_i,
|
|
3752
|
+
DeltaTemp=DeltaTemp,
|
|
3428
3753
|
) # [kg/s]
|
|
3429
3754
|
ROCD_i = conv.m2ft(ROCDisu) * 60
|
|
3430
3755
|
|
|
3431
3756
|
# BADA3
|
|
3432
3757
|
elif AC.BADAFamily.BADA3:
|
|
3433
3758
|
THR_min = AC.Thrust(
|
|
3434
|
-
rating="LIDL",
|
|
3759
|
+
rating="LIDL",
|
|
3760
|
+
v=TAS_i,
|
|
3761
|
+
h=H_m,
|
|
3762
|
+
config="CR",
|
|
3763
|
+
DeltaTemp=DeltaTemp,
|
|
3435
3764
|
) # IDLE Thrust
|
|
3436
3765
|
FUEL_min = AC.ff(
|
|
3437
3766
|
flightPhase="Descent",
|
|
@@ -3442,7 +3771,11 @@ def constantSpeedSlope(
|
|
|
3442
3771
|
adapted=False,
|
|
3443
3772
|
) # IDLE Fuel Flow
|
|
3444
3773
|
THR_max = AC.Thrust(
|
|
3445
|
-
rating="MCMB",
|
|
3774
|
+
rating="MCMB",
|
|
3775
|
+
v=TAS_i,
|
|
3776
|
+
h=H_m,
|
|
3777
|
+
DeltaTemp=DeltaTemp,
|
|
3778
|
+
config="CR",
|
|
3446
3779
|
) # MCMB Thrust
|
|
3447
3780
|
FUEL_max = AC.ff(
|
|
3448
3781
|
flightPhase="Climb",
|
|
@@ -3590,10 +3923,14 @@ def constantSpeedSlope(
|
|
|
3590
3923
|
gamma_i = 90 * np.sign(ROCD_i)
|
|
3591
3924
|
else:
|
|
3592
3925
|
if AC.BADAFamily.BADAE:
|
|
3593
|
-
gamma_i = degrees(
|
|
3926
|
+
gamma_i = degrees(
|
|
3927
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
3928
|
+
)
|
|
3594
3929
|
else:
|
|
3595
3930
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
3596
|
-
gamma_i = degrees(
|
|
3931
|
+
gamma_i = degrees(
|
|
3932
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
3933
|
+
)
|
|
3597
3934
|
|
|
3598
3935
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
3599
3936
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -3629,7 +3966,9 @@ def constantSpeedSlope(
|
|
|
3629
3966
|
if turnFlight:
|
|
3630
3967
|
step_distance = conv.m2nm(
|
|
3631
3968
|
turn.distance(
|
|
3632
|
-
rateOfTurn=rateOfTurn,
|
|
3969
|
+
rateOfTurn=rateOfTurn,
|
|
3970
|
+
TAS=TAS_i,
|
|
3971
|
+
timeOfTurn=step_time,
|
|
3633
3972
|
)
|
|
3634
3973
|
) # arcLength during the turn [NM]
|
|
3635
3974
|
else:
|
|
@@ -3844,59 +4183,82 @@ def constantSpeedSlope_time(
|
|
|
3844
4183
|
magneticDeclinationGrid=None,
|
|
3845
4184
|
**kwargs,
|
|
3846
4185
|
):
|
|
3847
|
-
"""
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
:param
|
|
3853
|
-
:param
|
|
3854
|
-
:param
|
|
3855
|
-
:param
|
|
3856
|
-
:param
|
|
3857
|
-
:param
|
|
3858
|
-
:param
|
|
3859
|
-
:param
|
|
3860
|
-
:param
|
|
3861
|
-
:param
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
:param Lat:
|
|
3866
|
-
:param Lon:
|
|
3867
|
-
:param initialHeading:
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
:param reducedPower:
|
|
3872
|
-
:
|
|
3873
|
-
:
|
|
3874
|
-
:
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
:
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
4186
|
+
"""
|
|
4187
|
+
Computes the time, fuel, and trajectory parameters required by an aircraft to climb or descend at constant speed and slope over a given duration.
|
|
4188
|
+
|
|
4189
|
+
This function models a trajectory with constant speed (either CAS, TAS, or Mach) and a specified slope (degrees). The aircraft's dynamics are modeled based on BADA family data (BADA3, BADA4, BADAH, BADAE), supporting various aircraft types including electric models. It also accounts for turns, heading changes, and wind effects.
|
|
4190
|
+
|
|
4191
|
+
:param AC: Aircraft object {BADA3, BADA4, BADAH, BADAE}.
|
|
4192
|
+
:param length: Total duration of the segment [s].
|
|
4193
|
+
:param speedType: Speed type to follow during the trajectory {M, CAS, TAS}.
|
|
4194
|
+
:param v: Speed to follow (in knots for CAS/TAS or as a Mach number) [kt] or [-] for Mach.
|
|
4195
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
4196
|
+
:param slopetarget: Desired slope (trajectory angle) to follow [deg].
|
|
4197
|
+
:param m_init: Initial aircraft mass [kg].
|
|
4198
|
+
:param DeltaTemp: Deviation from standard ISA temperature [K].
|
|
4199
|
+
:param wS: Longitudinal wind speed component (affects ground speed) [kt]. Default is 0.0.
|
|
4200
|
+
:param turnMetrics: A dictionary defining the turn parameters:
|
|
4201
|
+
- rateOfTurn [deg/s]
|
|
4202
|
+
- bankAngle [deg]
|
|
4203
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
4204
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
4205
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
4206
|
+
:param initialHeading: A dictionary specifying the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
4207
|
+
- magnetic: Magnetic heading [deg].
|
|
4208
|
+
- true: True heading [deg].
|
|
4209
|
+
- constantHeading: Whether to maintain a constant heading [True/False].
|
|
4210
|
+
:param reducedPower: Boolean specifying if reduced power is applied during climb/descent. Default is None.
|
|
4211
|
+
:param directionOfTurn: Direction of turn {LEFT, RIGHT}. Default is None.
|
|
4212
|
+
:param magneticDeclinationGrid: Optional magnetic declination grid for correcting magnetic heading. Default is None.
|
|
4213
|
+
:param kwargs: Additional optional parameters:
|
|
4214
|
+
- step_length: Step length for trajectory calculation [s]. Default is 1 second.
|
|
4215
|
+
- Hp_step: Altitude step size for calculations [ft]. Default is 1000 ft for BADA3/BADA4, 500 ft for BADAH/BADAE.
|
|
4216
|
+
- SOC_init: Initial battery state of charge (for electric aircraft) [%]. Default is 100.
|
|
4217
|
+
- config: Default aerodynamic configuration {TO, IC, CR, AP, LD}. If not provided, configuration is calculated automatically.
|
|
4218
|
+
- speedBrakes: Dictionary specifying if speed brakes are deployed and additional drag coefficient {deployed: False, value: 0.03}.
|
|
4219
|
+
- ROCD_min: Minimum Rate of Climb/Descent to determine service ceiling [ft/min]. Defaults depend on aircraft type and engine.
|
|
4220
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight. Default is False.
|
|
4221
|
+
- m_iter: Number of iterations for mass integration. Default is 5.
|
|
4222
|
+
|
|
4223
|
+
:returns: A pandas DataFrame containing the flight trajectory data with the following columns:
|
|
4224
|
+
- Hp: Pressure altitude [ft]
|
|
4225
|
+
- TAS: True Air Speed [kt]
|
|
4226
|
+
- CAS: Calibrated Air Speed [kt]
|
|
4227
|
+
- GS: Ground Speed [kt]
|
|
4228
|
+
- M: Mach number [-]
|
|
4229
|
+
- ROCD: Rate of Climb/Descent [ft/min]
|
|
4230
|
+
- ESF: Energy Share Factor [-]
|
|
4231
|
+
- FUEL: Fuel flow [kg/s]
|
|
4232
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
4233
|
+
- THR: Thrust force [N]
|
|
4234
|
+
- DRAG: Drag force [N]
|
|
4235
|
+
- time: Elapsed time [s]
|
|
4236
|
+
- dist: Distance flown [NM]
|
|
4237
|
+
- slope: Trajectory slope (angle) [deg]
|
|
4238
|
+
- mass: Aircraft mass [kg]
|
|
4239
|
+
- config: Aerodynamic configuration
|
|
4240
|
+
- LAT: Latitude [deg]
|
|
4241
|
+
- LON: Longitude [deg]
|
|
4242
|
+
- HDGTrue: True heading [deg]
|
|
4243
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
4244
|
+
- BankAngle: Bank angle during turns [deg]
|
|
4245
|
+
- ROT: Rate of turn [deg/s]
|
|
4246
|
+
- Comment: Descriptive comments about the trajectory segment.
|
|
4247
|
+
- For BADAH:
|
|
4248
|
+
- Preq: Power required for level flight [W]
|
|
4249
|
+
- Peng: Generated power [W]
|
|
4250
|
+
- Pav: Available power [W]
|
|
4251
|
+
- For BADAE (electric aircraft):
|
|
4252
|
+
- Pmec: Mechanical power [W]
|
|
4253
|
+
- Pelc: Electrical power [W]
|
|
4254
|
+
- Pbat: Battery power [W]
|
|
4255
|
+
- SOCr: Battery state of charge rate of depletion [%/h]
|
|
4256
|
+
- SOC: Battery state of charge [%]
|
|
4257
|
+
- Ibat: Battery current [A]
|
|
4258
|
+
- Vbat: Battery voltage [V]
|
|
4259
|
+
- Vgbat: Ground battery voltage [V]
|
|
4260
|
+
|
|
4261
|
+
:rtype: pandas.DataFrame
|
|
3900
4262
|
"""
|
|
3901
4263
|
|
|
3902
4264
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -3947,7 +4309,9 @@ def constantSpeedSlope_time(
|
|
|
3947
4309
|
|
|
3948
4310
|
# speed brakes application
|
|
3949
4311
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
3950
|
-
speedBrakes = kwargs.get(
|
|
4312
|
+
speedBrakes = kwargs.get(
|
|
4313
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
4314
|
+
)
|
|
3951
4315
|
|
|
3952
4316
|
# step size in [s]
|
|
3953
4317
|
step_length = kwargs.get("step_length", 1)
|
|
@@ -3980,7 +4344,9 @@ def constantSpeedSlope_time(
|
|
|
3980
4344
|
constHeadingStr = ""
|
|
3981
4345
|
|
|
3982
4346
|
# comment line describing type of trajectory calculation
|
|
3983
|
-
comment =
|
|
4347
|
+
comment = (
|
|
4348
|
+
phase + turnComment + "_const_Slope_" + speedType + constHeadingStr
|
|
4349
|
+
)
|
|
3984
4350
|
|
|
3985
4351
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
3986
4352
|
comment = comment + "_" + headingToFly + "_Heading"
|
|
@@ -4001,7 +4367,9 @@ def constantSpeedSlope_time(
|
|
|
4001
4367
|
)
|
|
4002
4368
|
|
|
4003
4369
|
# weight iteration constant
|
|
4004
|
-
m_iter = kwargs.get(
|
|
4370
|
+
m_iter = kwargs.get(
|
|
4371
|
+
"m_iter", 5
|
|
4372
|
+
) # number of iterations for integration loop[-]
|
|
4005
4373
|
|
|
4006
4374
|
# initialize output parameters
|
|
4007
4375
|
Hp = [Hp_init]
|
|
@@ -4075,8 +4443,12 @@ def constantSpeedSlope_time(
|
|
|
4075
4443
|
for _ in itertools.repeat(None, m_iter):
|
|
4076
4444
|
# atmosphere properties
|
|
4077
4445
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
4078
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
4079
|
-
|
|
4446
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
4447
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
4448
|
+
)
|
|
4449
|
+
temp_const = (theta * const.temp_0) / (
|
|
4450
|
+
theta * const.temp_0 - DeltaTemp
|
|
4451
|
+
)
|
|
4080
4452
|
|
|
4081
4453
|
# aircraft speed
|
|
4082
4454
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -4086,7 +4458,9 @@ def constantSpeedSlope_time(
|
|
|
4086
4458
|
if turnFlight:
|
|
4087
4459
|
if turnMetrics["bankAngle"] != 0.0:
|
|
4088
4460
|
# bankAngle is defined
|
|
4089
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
4461
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
4462
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
4463
|
+
)
|
|
4090
4464
|
else:
|
|
4091
4465
|
# rateOfTurn is defined
|
|
4092
4466
|
bankAngle = AC.bankAngle(
|
|
@@ -4098,7 +4472,10 @@ def constantSpeedSlope_time(
|
|
|
4098
4472
|
|
|
4099
4473
|
# compute Energy Share Factor (ESF)
|
|
4100
4474
|
ESF_i = AC.esf(
|
|
4101
|
-
h=H_m,
|
|
4475
|
+
h=H_m,
|
|
4476
|
+
M=M_i,
|
|
4477
|
+
DeltaTemp=DeltaTemp,
|
|
4478
|
+
flightEvolution=("const" + speedType),
|
|
4102
4479
|
)
|
|
4103
4480
|
|
|
4104
4481
|
step_time = length_loop - time[-1]
|
|
@@ -4106,14 +4483,20 @@ def constantSpeedSlope_time(
|
|
|
4106
4483
|
# Compute required ROCD
|
|
4107
4484
|
if AC.BADAFamily.BADAE:
|
|
4108
4485
|
# special case for BADAE, in future it may apply also for BADAH
|
|
4109
|
-
ROCDisu =
|
|
4486
|
+
ROCDisu = (
|
|
4487
|
+
tan(conv.deg2rad(slopetarget)) * TAS_i * (1 / temp_const)
|
|
4488
|
+
)
|
|
4110
4489
|
else:
|
|
4111
|
-
ROCDisu =
|
|
4490
|
+
ROCDisu = (
|
|
4491
|
+
sin(conv.deg2rad(slopetarget)) * TAS_i * (1 / temp_const)
|
|
4492
|
+
)
|
|
4112
4493
|
|
|
4113
4494
|
# BADAH or BADAE
|
|
4114
4495
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
4115
4496
|
# compute Power required for level flight
|
|
4116
|
-
Preq_i = AC.Preq(
|
|
4497
|
+
Preq_i = AC.Preq(
|
|
4498
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
4499
|
+
)
|
|
4117
4500
|
# Compute power required for target ROCD
|
|
4118
4501
|
Preq_target_i = AC.Peng_target(
|
|
4119
4502
|
temp=theta * const.temp_0,
|
|
@@ -4141,20 +4524,27 @@ def constantSpeedSlope_time(
|
|
|
4141
4524
|
# ensure continuity of configuration change within the segment
|
|
4142
4525
|
if config:
|
|
4143
4526
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
4144
|
-
phase=phase,
|
|
4527
|
+
phase=phase,
|
|
4528
|
+
previousConfig=config[-1],
|
|
4529
|
+
currentConfig=config_i,
|
|
4145
4530
|
)
|
|
4146
4531
|
|
|
4147
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
4532
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
4533
|
+
config=config_i
|
|
4534
|
+
)
|
|
4148
4535
|
|
|
4149
4536
|
# compute lift coefficient
|
|
4150
4537
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
4151
4538
|
# compute drag coefficient
|
|
4152
|
-
CD = AC.CD(
|
|
4539
|
+
CD = AC.CD(
|
|
4540
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
4541
|
+
)
|
|
4153
4542
|
# compute drag force
|
|
4154
4543
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
4155
4544
|
# compute thrust force
|
|
4156
4545
|
THR_i = (
|
|
4157
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
4546
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
4547
|
+
+ Drag
|
|
4158
4548
|
) # [N]
|
|
4159
4549
|
|
|
4160
4550
|
# BADA3
|
|
@@ -4187,7 +4577,8 @@ def constantSpeedSlope_time(
|
|
|
4187
4577
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
4188
4578
|
# compute thrust force
|
|
4189
4579
|
THR_i = (
|
|
4190
|
-
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
4580
|
+
ROCDisu * mass_i * const.g * temp_const / (TAS_i * ESF_i)
|
|
4581
|
+
+ Drag
|
|
4191
4582
|
) # [N]
|
|
4192
4583
|
|
|
4193
4584
|
# check that required thrust/power fits in the avialable thrust/power envelope,
|
|
@@ -4286,21 +4677,39 @@ def constantSpeedSlope_time(
|
|
|
4286
4677
|
Pelc_i = Preq_target_i / AC.eta
|
|
4287
4678
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
4288
4679
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
4289
|
-
Vgbat_i =
|
|
4680
|
+
Vgbat_i = (
|
|
4681
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
4682
|
+
)
|
|
4290
4683
|
|
|
4291
4684
|
# BADA4
|
|
4292
4685
|
elif AC.BADAFamily.BADA4:
|
|
4293
4686
|
THR_min = AC.Thrust(
|
|
4294
|
-
rating="LIDL",
|
|
4687
|
+
rating="LIDL",
|
|
4688
|
+
delta=delta,
|
|
4689
|
+
theta=theta,
|
|
4690
|
+
M=M_i,
|
|
4691
|
+
DeltaTemp=DeltaTemp,
|
|
4295
4692
|
) # IDLE Thrust
|
|
4296
4693
|
FUEL_min = AC.ff(
|
|
4297
|
-
rating="LIDL",
|
|
4694
|
+
rating="LIDL",
|
|
4695
|
+
delta=delta,
|
|
4696
|
+
theta=theta,
|
|
4697
|
+
M=M_i,
|
|
4698
|
+
DeltaTemp=DeltaTemp,
|
|
4298
4699
|
) # IDLE Fuel Flow
|
|
4299
4700
|
THR_max = AC.Thrust(
|
|
4300
|
-
rating="MCMB",
|
|
4701
|
+
rating="MCMB",
|
|
4702
|
+
delta=delta,
|
|
4703
|
+
theta=theta,
|
|
4704
|
+
M=M_i,
|
|
4705
|
+
DeltaTemp=DeltaTemp,
|
|
4301
4706
|
) # MCMB Thrust
|
|
4302
4707
|
FUEL_max = AC.ff(
|
|
4303
|
-
rating="MCMB",
|
|
4708
|
+
rating="MCMB",
|
|
4709
|
+
delta=delta,
|
|
4710
|
+
theta=theta,
|
|
4711
|
+
M=M_i,
|
|
4712
|
+
DeltaTemp=DeltaTemp,
|
|
4304
4713
|
) # MCMB Fuel Flow
|
|
4305
4714
|
|
|
4306
4715
|
if THR_i < THR_min:
|
|
@@ -4332,14 +4741,22 @@ def constantSpeedSlope_time(
|
|
|
4332
4741
|
else:
|
|
4333
4742
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
4334
4743
|
FUEL_i = AC.ff(
|
|
4335
|
-
CT=CT,
|
|
4744
|
+
CT=CT,
|
|
4745
|
+
delta=delta,
|
|
4746
|
+
theta=theta,
|
|
4747
|
+
M=M_i,
|
|
4748
|
+
DeltaTemp=DeltaTemp,
|
|
4336
4749
|
) # [kg/s]
|
|
4337
4750
|
ROCD_i = conv.m2ft(ROCDisu) * 60
|
|
4338
4751
|
|
|
4339
4752
|
# BADA3
|
|
4340
4753
|
elif AC.BADAFamily.BADA3:
|
|
4341
4754
|
THR_min = AC.Thrust(
|
|
4342
|
-
rating="LIDL",
|
|
4755
|
+
rating="LIDL",
|
|
4756
|
+
v=TAS_i,
|
|
4757
|
+
h=H_m,
|
|
4758
|
+
config="CR",
|
|
4759
|
+
DeltaTemp=DeltaTemp,
|
|
4343
4760
|
) # IDLE Thrust
|
|
4344
4761
|
FUEL_min = AC.ff(
|
|
4345
4762
|
flightPhase="Descent",
|
|
@@ -4350,7 +4767,11 @@ def constantSpeedSlope_time(
|
|
|
4350
4767
|
adapted=False,
|
|
4351
4768
|
) # IDLE Fuel Flow
|
|
4352
4769
|
THR_max = AC.Thrust(
|
|
4353
|
-
rating="MCMB",
|
|
4770
|
+
rating="MCMB",
|
|
4771
|
+
v=TAS_i,
|
|
4772
|
+
h=H_m,
|
|
4773
|
+
DeltaTemp=DeltaTemp,
|
|
4774
|
+
config="CR",
|
|
4354
4775
|
) # MCMB Thrust
|
|
4355
4776
|
FUEL_max = AC.ff(
|
|
4356
4777
|
flightPhase="Climb",
|
|
@@ -4502,12 +4923,18 @@ def constantSpeedSlope_time(
|
|
|
4502
4923
|
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
4503
4924
|
h=conv.ft2m(Hp_i), DeltaTemp=DeltaTemp
|
|
4504
4925
|
)
|
|
4505
|
-
temp_const = (theta * const.temp_0) / (
|
|
4926
|
+
temp_const = (theta * const.temp_0) / (
|
|
4927
|
+
theta * const.temp_0 - DeltaTemp
|
|
4928
|
+
)
|
|
4506
4929
|
if AC.BADAFamily.BADAE:
|
|
4507
|
-
gamma_i = degrees(
|
|
4930
|
+
gamma_i = degrees(
|
|
4931
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
4932
|
+
)
|
|
4508
4933
|
else:
|
|
4509
4934
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
4510
|
-
gamma_i = degrees(
|
|
4935
|
+
gamma_i = degrees(
|
|
4936
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
4937
|
+
)
|
|
4511
4938
|
|
|
4512
4939
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
4513
4940
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -4544,7 +4971,9 @@ def constantSpeedSlope_time(
|
|
|
4544
4971
|
if turnFlight:
|
|
4545
4972
|
step_distance = conv.m2nm(
|
|
4546
4973
|
turn.distance(
|
|
4547
|
-
rateOfTurn=rateOfTurn,
|
|
4974
|
+
rateOfTurn=rateOfTurn,
|
|
4975
|
+
TAS=TAS_i,
|
|
4976
|
+
timeOfTurn=step_time,
|
|
4548
4977
|
)
|
|
4549
4978
|
) # arcLength during the turn [NM]
|
|
4550
4979
|
else:
|
|
@@ -4747,58 +5176,83 @@ def constantSpeedRating(
|
|
|
4747
5176
|
initRating=None,
|
|
4748
5177
|
**kwargs,
|
|
4749
5178
|
):
|
|
4750
|
-
"""
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
:param
|
|
4759
|
-
:param
|
|
4760
|
-
:param
|
|
4761
|
-
:param
|
|
4762
|
-
:param
|
|
4763
|
-
:param
|
|
4764
|
-
:param
|
|
4765
|
-
:param
|
|
4766
|
-
:param
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
:param
|
|
4771
|
-
:param
|
|
4772
|
-
:param
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
:
|
|
4777
|
-
:
|
|
4778
|
-
:
|
|
4779
|
-
:
|
|
4780
|
-
:
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
:
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
5179
|
+
"""
|
|
5180
|
+
Calculates time, fuel consumption, and other parameters required for an aircraft to perform a climb or descent
|
|
5181
|
+
from an initial altitude (Hp_init) to a final altitude (Hp_final) while maintaining a constant speed and engine rating.
|
|
5182
|
+
|
|
5183
|
+
It uses different models for BADA (Base of Aircraft Data) families (BADA3, BADA4, BADAH, BADAE) to compute key flight parameters
|
|
5184
|
+
such as fuel burn, rate of climb/descent (ROCD), thrust, drag, and energy requirements. The function also supports
|
|
5185
|
+
complex flight dynamics including turns, heading changes, and wind influences.
|
|
5186
|
+
|
|
5187
|
+
:param AC: Aircraft object {BADA3/4/H/E}.
|
|
5188
|
+
:param speedType: Type of speed to maintain during the flight {M (Mach), CAS, TAS}.
|
|
5189
|
+
:param v: Speed to follow - [kt] CAS/TAS or [-] MACH.
|
|
5190
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
5191
|
+
:param Hp_final: Final pressure altitude [ft].
|
|
5192
|
+
:param m_init: Initial mass of the aircraft at the start of the segment [kg].
|
|
5193
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
5194
|
+
:param wS: Wind speed component along the longitudinal axis (affects ground speed) [kt]. Default is 0.0.
|
|
5195
|
+
:param turnMetrics: A dictionary defining the turn parameters:
|
|
5196
|
+
- rateOfTurn [deg/s]
|
|
5197
|
+
- bankAngle [deg]
|
|
5198
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
5199
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
5200
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
5201
|
+
:param initialHeading: A dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
5202
|
+
- magnetic: Magnetic heading [deg].
|
|
5203
|
+
- true: True heading [deg].
|
|
5204
|
+
- constantHeading: Whether to maintain a constant heading. Default is None.
|
|
5205
|
+
:param reducedPower: Boolean specifying if reduced power is applied during the climb. Default is None.
|
|
5206
|
+
:param directionOfTurn: Direction of the turn {LEFT, RIGHT}. Default is None.
|
|
5207
|
+
:param expedite: Boolean flag to expedite climb/descent. Default is False.
|
|
5208
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declination used to correct magnetic heading. Default is None.
|
|
5209
|
+
:param kwargs: Additional optional parameters:
|
|
5210
|
+
- Hp_step: Step size in altitude for the iterative calculation [ft]. Default is 1000 ft for BADA3/BADA4, 500 ft for BADAH/BADAE.
|
|
5211
|
+
- SOC_init: Initial battery state of charge for electric aircraft (BADAE) [%]. Default is 100.
|
|
5212
|
+
- speedBrakes: A dictionary specifying whether speed brakes are deployed and the additional drag coefficient {deployed: False, value: 0.03}.
|
|
5213
|
+
- ROCD_min: Minimum rate of climb/descent used to determine service ceiling [ft/min]. Default varies based on aircraft type.
|
|
5214
|
+
- config: Default aerodynamic configuration (TO, IC, CR, AP, LD). Default is None.
|
|
5215
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight segment. Default is False.
|
|
5216
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 5.
|
|
5217
|
+
|
|
5218
|
+
:returns: A pandas DataFrame containing flight trajectory data with the following columns:
|
|
5219
|
+
- Hp: Pressure altitude [ft]
|
|
5220
|
+
- TAS: True Air Speed [kt]
|
|
5221
|
+
- CAS: Calibrated Air Speed [kt]
|
|
5222
|
+
- GS: Ground Speed [kt]
|
|
5223
|
+
- M: Mach number [-]
|
|
5224
|
+
- ROCD: Rate of climb/descent [ft/min]
|
|
5225
|
+
- ESF: Energy Share Factor [-]
|
|
5226
|
+
- FUEL: Fuel flow [kg/s]
|
|
5227
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
5228
|
+
- THR: Thrust force [N]
|
|
5229
|
+
- DRAG: Drag force [N]
|
|
5230
|
+
- time: Elapsed time [s]
|
|
5231
|
+
- dist: Distance flown [NM]
|
|
5232
|
+
- slope: Flight trajectory slope (angle) [deg]
|
|
5233
|
+
- mass: Aircraft mass [kg]
|
|
5234
|
+
- config: Aerodynamic configuration
|
|
5235
|
+
- LAT: Latitude [deg]
|
|
5236
|
+
- LON: Longitude [deg]
|
|
5237
|
+
- HDGTrue: True heading [deg]
|
|
5238
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
5239
|
+
- BankAngle: Bank angle during the turn [deg]
|
|
5240
|
+
- ROT: Rate of turn [deg/s]
|
|
5241
|
+
- Comment: Comments describing the flight segment
|
|
5242
|
+
- For BADAH:
|
|
5243
|
+
- Preq: Required power for level flight [W]
|
|
5244
|
+
- Peng: Generated power [W]
|
|
5245
|
+
- Pav: Available power [W]
|
|
5246
|
+
- For BADAE (electric aircraft):
|
|
5247
|
+
- Pmec: Mechanical power [W]
|
|
5248
|
+
- Pelc: Electrical power [W]
|
|
5249
|
+
- Pbat: Battery power [W]
|
|
5250
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
5251
|
+
- SOC: Battery state of charge [%]
|
|
5252
|
+
- Ibat: Battery current [A]
|
|
5253
|
+
- Vbat: Battery voltage [V]
|
|
5254
|
+
- Vgbat: Ground battery voltage [V]
|
|
5255
|
+
:rtype: pandas.DataFrame
|
|
4802
5256
|
"""
|
|
4803
5257
|
|
|
4804
5258
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -4849,7 +5303,9 @@ def constantSpeedRating(
|
|
|
4849
5303
|
|
|
4850
5304
|
# speed brakes application
|
|
4851
5305
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
4852
|
-
speedBrakes = kwargs.get(
|
|
5306
|
+
speedBrakes = kwargs.get(
|
|
5307
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
5308
|
+
)
|
|
4853
5309
|
|
|
4854
5310
|
# optional parameter - iteration step for altitude loop
|
|
4855
5311
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
@@ -4908,7 +5364,13 @@ def constantSpeedRating(
|
|
|
4908
5364
|
|
|
4909
5365
|
# comment line describing type of trajectory calculation
|
|
4910
5366
|
comment = (
|
|
4911
|
-
phase
|
|
5367
|
+
phase
|
|
5368
|
+
+ turnComment
|
|
5369
|
+
+ "_const_"
|
|
5370
|
+
+ speedType
|
|
5371
|
+
+ "_"
|
|
5372
|
+
+ rating
|
|
5373
|
+
+ constHeadingStr
|
|
4912
5374
|
)
|
|
4913
5375
|
|
|
4914
5376
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
@@ -4933,7 +5395,9 @@ def constantSpeedRating(
|
|
|
4933
5395
|
)
|
|
4934
5396
|
|
|
4935
5397
|
# weight iteration constant
|
|
4936
|
-
m_iter = kwargs.get(
|
|
5398
|
+
m_iter = kwargs.get(
|
|
5399
|
+
"m_iter", 5
|
|
5400
|
+
) # number of iterations for integration loop[-]
|
|
4937
5401
|
|
|
4938
5402
|
# The thrust_fuel method for BADA 3 models applies the cruise fuel correction
|
|
4939
5403
|
# whenever the thrust is adapted, instead of only in cruise: this correction
|
|
@@ -5004,8 +5468,12 @@ def constantSpeedRating(
|
|
|
5004
5468
|
## atmosphere, speeds, thrust. fuel flow, ESF
|
|
5005
5469
|
# atmosphere properties
|
|
5006
5470
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
5007
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
5008
|
-
|
|
5471
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
5472
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
5473
|
+
)
|
|
5474
|
+
temp_const = (theta * const.temp_0) / (
|
|
5475
|
+
theta * const.temp_0 - DeltaTemp
|
|
5476
|
+
)
|
|
5009
5477
|
|
|
5010
5478
|
# aircraft speed
|
|
5011
5479
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -5015,17 +5483,24 @@ def constantSpeedRating(
|
|
|
5015
5483
|
if turnFlight:
|
|
5016
5484
|
if turnMetrics["bankAngle"] != 0.0:
|
|
5017
5485
|
# bankAngle is defined
|
|
5018
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
5486
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
5487
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
5488
|
+
)
|
|
5019
5489
|
else:
|
|
5020
5490
|
# rateOfTurn is defined
|
|
5021
|
-
bankAngle = AC.bankAngle(
|
|
5491
|
+
bankAngle = AC.bankAngle(
|
|
5492
|
+
rateOfTurn=rateOfTurn, v=TAS_i
|
|
5493
|
+
) # [degrees]
|
|
5022
5494
|
|
|
5023
5495
|
# Load factor
|
|
5024
5496
|
nz = 1 / cos(radians(bankAngle))
|
|
5025
5497
|
|
|
5026
5498
|
# compute Energy Share Factor (ESF)
|
|
5027
5499
|
ESF_i = AC.esf(
|
|
5028
|
-
h=H_m,
|
|
5500
|
+
h=H_m,
|
|
5501
|
+
M=M_i,
|
|
5502
|
+
DeltaTemp=DeltaTemp,
|
|
5503
|
+
flightEvolution=("const" + speedType),
|
|
5029
5504
|
)
|
|
5030
5505
|
|
|
5031
5506
|
mass_i = mass[-1]
|
|
@@ -5034,7 +5509,9 @@ def constantSpeedRating(
|
|
|
5034
5509
|
if AC.BADAFamily.BADAH:
|
|
5035
5510
|
# compute available power
|
|
5036
5511
|
if rating == "UNKNOWN":
|
|
5037
|
-
Preq_target_i =
|
|
5512
|
+
Preq_target_i = (
|
|
5513
|
+
0.1 * AC.P0
|
|
5514
|
+
) # No minimum power model: assume 10% torque
|
|
5038
5515
|
else:
|
|
5039
5516
|
Preq_target_i = AC.Pav(rating=rating, theta=theta, delta=delta)
|
|
5040
5517
|
|
|
@@ -5048,7 +5525,9 @@ def constantSpeedRating(
|
|
|
5048
5525
|
elif AC.BADAFamily.BADAE:
|
|
5049
5526
|
# compute available power
|
|
5050
5527
|
if rating == "UNKNOWN":
|
|
5051
|
-
Preq_target_i =
|
|
5528
|
+
Preq_target_i = (
|
|
5529
|
+
0.1 * AC.P0
|
|
5530
|
+
) # No minimum power model: assume 10% torque
|
|
5052
5531
|
else:
|
|
5053
5532
|
Preq_target_i = AC.Pav(rating=rating, SOC=SOC[-1])
|
|
5054
5533
|
|
|
@@ -5067,10 +5546,16 @@ def constantSpeedRating(
|
|
|
5067
5546
|
elif AC.BADAFamily.BADA4:
|
|
5068
5547
|
# compute thrust force and fuel flow
|
|
5069
5548
|
THR_i = AC.Thrust(
|
|
5070
|
-
rating=rating,
|
|
5549
|
+
rating=rating,
|
|
5550
|
+
delta=delta,
|
|
5551
|
+
theta=theta,
|
|
5552
|
+
M=M_i,
|
|
5553
|
+
DeltaTemp=DeltaTemp,
|
|
5071
5554
|
) # [N]
|
|
5072
5555
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
5073
|
-
FUEL_i = AC.ff(
|
|
5556
|
+
FUEL_i = AC.ff(
|
|
5557
|
+
CT=CT, delta=delta, theta=theta, M=M_i, DeltaTemp=DeltaTemp
|
|
5558
|
+
)
|
|
5074
5559
|
|
|
5075
5560
|
# BADA3
|
|
5076
5561
|
elif AC.BADAFamily.BADA3:
|
|
@@ -5089,12 +5574,18 @@ def constantSpeedRating(
|
|
|
5089
5574
|
# ensure continuity of configuration change within the segment
|
|
5090
5575
|
if config:
|
|
5091
5576
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
5092
|
-
phase=phase,
|
|
5577
|
+
phase=phase,
|
|
5578
|
+
previousConfig=config[-1],
|
|
5579
|
+
currentConfig=config_i,
|
|
5093
5580
|
)
|
|
5094
5581
|
|
|
5095
5582
|
# compute thrust force and fuel flow
|
|
5096
5583
|
THR_i = AC.Thrust(
|
|
5097
|
-
rating=rating,
|
|
5584
|
+
rating=rating,
|
|
5585
|
+
v=TAS_i,
|
|
5586
|
+
h=H_m,
|
|
5587
|
+
config=config_i,
|
|
5588
|
+
DeltaTemp=DeltaTemp,
|
|
5098
5589
|
)
|
|
5099
5590
|
FUEL_i = AC.ff(
|
|
5100
5591
|
flightPhase=phase,
|
|
@@ -5122,7 +5613,9 @@ def constantSpeedRating(
|
|
|
5122
5613
|
# BADAH or BADAE
|
|
5123
5614
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
5124
5615
|
# compute Power required
|
|
5125
|
-
Preq_i = AC.Preq(
|
|
5616
|
+
Preq_i = AC.Preq(
|
|
5617
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
5618
|
+
)
|
|
5126
5619
|
# compute ROCD
|
|
5127
5620
|
ROCD_i = (
|
|
5128
5621
|
conv.m2ft(
|
|
@@ -5155,15 +5648,21 @@ def constantSpeedRating(
|
|
|
5155
5648
|
# ensure continuity of configuration change within the segment
|
|
5156
5649
|
if config:
|
|
5157
5650
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
5158
|
-
phase=phase,
|
|
5651
|
+
phase=phase,
|
|
5652
|
+
previousConfig=config[-1],
|
|
5653
|
+
currentConfig=config_i,
|
|
5159
5654
|
)
|
|
5160
5655
|
|
|
5161
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
5656
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
5657
|
+
config=config_i
|
|
5658
|
+
)
|
|
5162
5659
|
|
|
5163
5660
|
# compute lift coefficient
|
|
5164
5661
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
5165
5662
|
# compute drag coefficient
|
|
5166
|
-
CD = AC.CD(
|
|
5663
|
+
CD = AC.CD(
|
|
5664
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
5665
|
+
)
|
|
5167
5666
|
# compute drag force
|
|
5168
5667
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
5169
5668
|
# compute ROCD
|
|
@@ -5204,7 +5703,10 @@ def constantSpeedRating(
|
|
|
5204
5703
|
CL = AC.CL(tas=TAS_i, sigma=sigma, mass=mass_i, nz=nz)
|
|
5205
5704
|
# compute drag coefficient
|
|
5206
5705
|
CD = AC.CD(
|
|
5207
|
-
CL=CL,
|
|
5706
|
+
CL=CL,
|
|
5707
|
+
config=config_i,
|
|
5708
|
+
expedite=expedite,
|
|
5709
|
+
speedBrakes=speedBrakes,
|
|
5208
5710
|
)
|
|
5209
5711
|
# compute drag force
|
|
5210
5712
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
@@ -5327,10 +5829,14 @@ def constantSpeedRating(
|
|
|
5327
5829
|
gamma_i = 90 * np.sign(ROCD_i)
|
|
5328
5830
|
else:
|
|
5329
5831
|
if AC.BADAFamily.BADAE:
|
|
5330
|
-
gamma_i = degrees(
|
|
5832
|
+
gamma_i = degrees(
|
|
5833
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
5834
|
+
)
|
|
5331
5835
|
else:
|
|
5332
5836
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
5333
|
-
gamma_i = degrees(
|
|
5837
|
+
gamma_i = degrees(
|
|
5838
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
5839
|
+
)
|
|
5334
5840
|
|
|
5335
5841
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
5336
5842
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -5366,7 +5872,9 @@ def constantSpeedRating(
|
|
|
5366
5872
|
if turnFlight:
|
|
5367
5873
|
step_distance = conv.m2nm(
|
|
5368
5874
|
turn.distance(
|
|
5369
|
-
rateOfTurn=rateOfTurn,
|
|
5875
|
+
rateOfTurn=rateOfTurn,
|
|
5876
|
+
TAS=TAS_i,
|
|
5877
|
+
timeOfTurn=step_time,
|
|
5370
5878
|
)
|
|
5371
5879
|
) # arcLength during the turn [NM]
|
|
5372
5880
|
else:
|
|
@@ -5583,60 +6091,85 @@ def constantSpeedRating_time(
|
|
|
5583
6091
|
initRating=None,
|
|
5584
6092
|
**kwargs,
|
|
5585
6093
|
):
|
|
5586
|
-
"""
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
:param
|
|
5595
|
-
:param
|
|
5596
|
-
:param
|
|
5597
|
-
:param
|
|
5598
|
-
:param
|
|
5599
|
-
:param
|
|
5600
|
-
:param
|
|
5601
|
-
:param
|
|
5602
|
-
:param
|
|
5603
|
-
:param
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
:param
|
|
5608
|
-
:param
|
|
5609
|
-
:param
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
:
|
|
5614
|
-
:
|
|
5615
|
-
:
|
|
5616
|
-
:
|
|
5617
|
-
:
|
|
5618
|
-
:
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
:
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
6094
|
+
"""
|
|
6095
|
+
Calculates the time, fuel consumption, and other flight parameters required for an aircraft to perform
|
|
6096
|
+
a climb or descent at constant speed and engine rating for a specified duration.
|
|
6097
|
+
|
|
6098
|
+
It uses different models for BADA (Base of Aircraft Data) families (BADA3, BADA4, BADAH, BADAE) to compute key parameters
|
|
6099
|
+
such as rate of climb/descent (ROCD), thrust, drag, fuel burn, and power requirements. The function also supports complex
|
|
6100
|
+
flight dynamics, including turns, heading changes, and the effect of wind.
|
|
6101
|
+
|
|
6102
|
+
:param AC: Aircraft object {BADA3/4/H/E}.
|
|
6103
|
+
:param length: Duration of the flight segment [s].
|
|
6104
|
+
:param speedType: Type of speed to maintain during the flight {M, CAS, TAS}.
|
|
6105
|
+
:param v: Speed to follow - [kt] CAS/TAS or [-] MACH.
|
|
6106
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
6107
|
+
:param phase: Phase of flight (Climb or Descent).
|
|
6108
|
+
:param m_init: Initial mass of the aircraft at the start of the segment [kg].
|
|
6109
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
6110
|
+
:param wS: Wind speed component along the longitudinal axis (affects ground speed) [kt]. Default is 0.0.
|
|
6111
|
+
:param turnMetrics: A dictionary defining the turn parameters:
|
|
6112
|
+
- rateOfTurn [deg/s]
|
|
6113
|
+
- bankAngle [deg]
|
|
6114
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
6115
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
6116
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
6117
|
+
:param initialHeading: A dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
6118
|
+
- magnetic: Magnetic heading [deg].
|
|
6119
|
+
- true: True heading [deg].
|
|
6120
|
+
- constantHeading: Whether to maintain a constant heading. Default is None.
|
|
6121
|
+
:param reducedPower: Boolean specifying if reduced power is applied during the climb. Default is None.
|
|
6122
|
+
:param directionOfTurn: Direction of the turn {LEFT, RIGHT}. Default is None.
|
|
6123
|
+
:param expedite: Boolean flag to expedite the climb/descent. Default is False.
|
|
6124
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declination used to correct magnetic heading. Default is None.
|
|
6125
|
+
:param initRating: Initial engine rating settings. Default is None.
|
|
6126
|
+
:param kwargs: Additional optional parameters:
|
|
6127
|
+
- step_length: Step size in time for the iterative calculation [s]. Default is 1 s.
|
|
6128
|
+
- SOC_init: Initial battery state of charge for electric aircraft (BADAE) [%]. Default is 100.
|
|
6129
|
+
- speedBrakes: A dictionary specifying whether speed brakes are deployed and the additional drag coefficient {deployed: False, value: 0.03}.
|
|
6130
|
+
- ROCD_min: Minimum rate of climb/descent to determine service ceiling [ft/min]. Default varies by aircraft type.
|
|
6131
|
+
- config: Default aerodynamic configuration (TO, IC, CR, AP, LD). Default is None.
|
|
6132
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight segment. Default is False.
|
|
6133
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 5.
|
|
6134
|
+
|
|
6135
|
+
:returns: A pandas DataFrame containing flight trajectory data with the following columns:
|
|
6136
|
+
- Hp: Pressure altitude [ft]
|
|
6137
|
+
- TAS: True Air Speed [kt]
|
|
6138
|
+
- CAS: Calibrated Air Speed [kt]
|
|
6139
|
+
- GS: Ground Speed [kt]
|
|
6140
|
+
- M: Mach number [-]
|
|
6141
|
+
- ROCD: Rate of climb/descent [ft/min]
|
|
6142
|
+
- ESF: Energy Share Factor [-]
|
|
6143
|
+
- FUEL: Fuel flow [kg/s]
|
|
6144
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
6145
|
+
- THR: Thrust force [N]
|
|
6146
|
+
- DRAG: Drag force [N]
|
|
6147
|
+
- time: Elapsed time [s]
|
|
6148
|
+
- dist: Distance flown [NM]
|
|
6149
|
+
- slope: Flight trajectory slope (angle) [deg]
|
|
6150
|
+
- mass: Aircraft mass [kg]
|
|
6151
|
+
- config: Aerodynamic configuration
|
|
6152
|
+
- LAT: Latitude [deg]
|
|
6153
|
+
- LON: Longitude [deg]
|
|
6154
|
+
- HDGTrue: True heading [deg]
|
|
6155
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
6156
|
+
- BankAngle: Bank angle during the turn [deg]
|
|
6157
|
+
- ROT: Rate of turn [deg/s]
|
|
6158
|
+
- Comment: Comments describing the flight segment
|
|
6159
|
+
- For BADAH:
|
|
6160
|
+
- Preq: Required power for level flight [W]
|
|
6161
|
+
- Peng: Generated power [W]
|
|
6162
|
+
- Pav: Available power [W]
|
|
6163
|
+
- For BADAE (electric aircraft):
|
|
6164
|
+
- Pmec: Mechanical power [W]
|
|
6165
|
+
- Pelc: Electrical power [W]
|
|
6166
|
+
- Pbat: Battery power [W]
|
|
6167
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
6168
|
+
- SOC: Battery state of charge [%]
|
|
6169
|
+
- Ibat: Battery current [A]
|
|
6170
|
+
- Vbat: Battery voltage [V]
|
|
6171
|
+
- Vgbat: Ground battery voltage [V]
|
|
6172
|
+
:rtype: pandas.DataFrame
|
|
5640
6173
|
"""
|
|
5641
6174
|
|
|
5642
6175
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -5687,7 +6220,9 @@ def constantSpeedRating_time(
|
|
|
5687
6220
|
|
|
5688
6221
|
# speed brakes application
|
|
5689
6222
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
5690
|
-
speedBrakes = kwargs.get(
|
|
6223
|
+
speedBrakes = kwargs.get(
|
|
6224
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
6225
|
+
)
|
|
5691
6226
|
|
|
5692
6227
|
# step size in [s]
|
|
5693
6228
|
step_length = kwargs.get("step_length", 1)
|
|
@@ -5722,7 +6257,9 @@ def constantSpeedRating_time(
|
|
|
5722
6257
|
else:
|
|
5723
6258
|
rating = "LIDL"
|
|
5724
6259
|
else:
|
|
5725
|
-
raise Exception(
|
|
6260
|
+
raise Exception(
|
|
6261
|
+
"Phase definition is wrong! It should be Climb or Descent"
|
|
6262
|
+
)
|
|
5726
6263
|
else:
|
|
5727
6264
|
rating = initRating
|
|
5728
6265
|
|
|
@@ -5738,7 +6275,13 @@ def constantSpeedRating_time(
|
|
|
5738
6275
|
|
|
5739
6276
|
# comment line describing type of trajectory calculation
|
|
5740
6277
|
comment = (
|
|
5741
|
-
phase
|
|
6278
|
+
phase
|
|
6279
|
+
+ turnComment
|
|
6280
|
+
+ "_const_"
|
|
6281
|
+
+ speedType
|
|
6282
|
+
+ "_"
|
|
6283
|
+
+ rating
|
|
6284
|
+
+ constHeadingStr
|
|
5742
6285
|
)
|
|
5743
6286
|
|
|
5744
6287
|
if Lat and Lon and (magneticHeading or trueHeading):
|
|
@@ -5763,7 +6306,9 @@ def constantSpeedRating_time(
|
|
|
5763
6306
|
)
|
|
5764
6307
|
|
|
5765
6308
|
# weight iteration constant
|
|
5766
|
-
m_iter = kwargs.get(
|
|
6309
|
+
m_iter = kwargs.get(
|
|
6310
|
+
"m_iter", 5
|
|
6311
|
+
) # number of iterations for integration loop[-]
|
|
5767
6312
|
|
|
5768
6313
|
# The thrust_fuel method for BADA 3 models applies the cruise fuel correction
|
|
5769
6314
|
# whenever the thrust is adapted, instead of only in cruise: this correction
|
|
@@ -5843,8 +6388,12 @@ def constantSpeedRating_time(
|
|
|
5843
6388
|
for _ in itertools.repeat(None, m_iter):
|
|
5844
6389
|
# atmosphere properties
|
|
5845
6390
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
5846
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
5847
|
-
|
|
6391
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
6392
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
6393
|
+
)
|
|
6394
|
+
temp_const = (theta * const.temp_0) / (
|
|
6395
|
+
theta * const.temp_0 - DeltaTemp
|
|
6396
|
+
)
|
|
5848
6397
|
|
|
5849
6398
|
# aircraft speed
|
|
5850
6399
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
@@ -5854,7 +6403,9 @@ def constantSpeedRating_time(
|
|
|
5854
6403
|
if turnFlight:
|
|
5855
6404
|
if turnMetrics["bankAngle"] != 0.0:
|
|
5856
6405
|
# bankAngle is defined
|
|
5857
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
6406
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
6407
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
6408
|
+
)
|
|
5858
6409
|
else:
|
|
5859
6410
|
# rateOfTurn is defined
|
|
5860
6411
|
bankAngle = AC.bankAngle(
|
|
@@ -5866,7 +6417,10 @@ def constantSpeedRating_time(
|
|
|
5866
6417
|
|
|
5867
6418
|
# compute Energy Share Factor (ESF)
|
|
5868
6419
|
ESF_i = AC.esf(
|
|
5869
|
-
h=H_m,
|
|
6420
|
+
h=H_m,
|
|
6421
|
+
M=M_i,
|
|
6422
|
+
DeltaTemp=DeltaTemp,
|
|
6423
|
+
flightEvolution=("const" + speedType),
|
|
5870
6424
|
)
|
|
5871
6425
|
|
|
5872
6426
|
step_time = length_loop - time[-1]
|
|
@@ -5879,7 +6433,9 @@ def constantSpeedRating_time(
|
|
|
5879
6433
|
0.1 * AC.P0
|
|
5880
6434
|
) # No minimum power model: assume 10% torque
|
|
5881
6435
|
else:
|
|
5882
|
-
Preq_target_i = AC.Pav(
|
|
6436
|
+
Preq_target_i = AC.Pav(
|
|
6437
|
+
rating=rating, theta=theta, delta=delta
|
|
6438
|
+
)
|
|
5883
6439
|
|
|
5884
6440
|
Pav_i = AC.Pav(rating="MTKF", theta=theta, delta=delta)
|
|
5885
6441
|
|
|
@@ -5904,13 +6460,19 @@ def constantSpeedRating_time(
|
|
|
5904
6460
|
Pelc_i = Preq_target_i / AC.eta
|
|
5905
6461
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
5906
6462
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
5907
|
-
Vgbat_i =
|
|
6463
|
+
Vgbat_i = (
|
|
6464
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
6465
|
+
)
|
|
5908
6466
|
|
|
5909
6467
|
# BADA4
|
|
5910
6468
|
elif AC.BADAFamily.BADA4:
|
|
5911
6469
|
# compute thrust force and fuel flow
|
|
5912
6470
|
THR_i = AC.Thrust(
|
|
5913
|
-
rating=rating,
|
|
6471
|
+
rating=rating,
|
|
6472
|
+
delta=delta,
|
|
6473
|
+
theta=theta,
|
|
6474
|
+
M=M_i,
|
|
6475
|
+
DeltaTemp=DeltaTemp,
|
|
5914
6476
|
) # [N]
|
|
5915
6477
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
5916
6478
|
FUEL_i = AC.ff(
|
|
@@ -5941,7 +6503,11 @@ def constantSpeedRating_time(
|
|
|
5941
6503
|
|
|
5942
6504
|
# compute thrust force and fuel flow
|
|
5943
6505
|
THR_i = AC.Thrust(
|
|
5944
|
-
rating=rating,
|
|
6506
|
+
rating=rating,
|
|
6507
|
+
v=TAS_i,
|
|
6508
|
+
h=H_m,
|
|
6509
|
+
config=config_i,
|
|
6510
|
+
DeltaTemp=DeltaTemp,
|
|
5945
6511
|
)
|
|
5946
6512
|
FUEL_i = AC.ff(
|
|
5947
6513
|
flightPhase=phase,
|
|
@@ -5955,7 +6521,9 @@ def constantSpeedRating_time(
|
|
|
5955
6521
|
# BADAH or BADAE
|
|
5956
6522
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
5957
6523
|
# compute Power required
|
|
5958
|
-
Preq_i = AC.Preq(
|
|
6524
|
+
Preq_i = AC.Preq(
|
|
6525
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
6526
|
+
)
|
|
5959
6527
|
# compute ROCD
|
|
5960
6528
|
ROCD_i = (
|
|
5961
6529
|
conv.m2ft(
|
|
@@ -5988,15 +6556,21 @@ def constantSpeedRating_time(
|
|
|
5988
6556
|
# ensure continuity of configuration change within the segment
|
|
5989
6557
|
if config:
|
|
5990
6558
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
5991
|
-
phase=phase,
|
|
6559
|
+
phase=phase,
|
|
6560
|
+
previousConfig=config[-1],
|
|
6561
|
+
currentConfig=config_i,
|
|
5992
6562
|
)
|
|
5993
6563
|
|
|
5994
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
6564
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
6565
|
+
config=config_i
|
|
6566
|
+
)
|
|
5995
6567
|
|
|
5996
6568
|
# compute lift coefficient
|
|
5997
6569
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
5998
6570
|
# compute drag coefficient
|
|
5999
|
-
CD = AC.CD(
|
|
6571
|
+
CD = AC.CD(
|
|
6572
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
6573
|
+
)
|
|
6000
6574
|
# compute drag force
|
|
6001
6575
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
6002
6576
|
|
|
@@ -6037,7 +6611,10 @@ def constantSpeedRating_time(
|
|
|
6037
6611
|
CL = AC.CL(tas=TAS_i, sigma=sigma, mass=mass_i, nz=nz)
|
|
6038
6612
|
# compute drag coefficient
|
|
6039
6613
|
CD = AC.CD(
|
|
6040
|
-
CL=CL,
|
|
6614
|
+
CL=CL,
|
|
6615
|
+
config=config_i,
|
|
6616
|
+
expedite=expedite,
|
|
6617
|
+
speedBrakes=speedBrakes,
|
|
6041
6618
|
)
|
|
6042
6619
|
# compute drag force
|
|
6043
6620
|
Drag = AC.D(tas=TAS_i, sigma=sigma, CD=CD)
|
|
@@ -6167,12 +6744,18 @@ def constantSpeedRating_time(
|
|
|
6167
6744
|
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
6168
6745
|
h=conv.ft2m(Hp_i), DeltaTemp=DeltaTemp
|
|
6169
6746
|
)
|
|
6170
|
-
temp_const = (theta * const.temp_0) / (
|
|
6747
|
+
temp_const = (theta * const.temp_0) / (
|
|
6748
|
+
theta * const.temp_0 - DeltaTemp
|
|
6749
|
+
)
|
|
6171
6750
|
if AC.BADAFamily.BADAE:
|
|
6172
|
-
gamma_i = degrees(
|
|
6751
|
+
gamma_i = degrees(
|
|
6752
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
6753
|
+
)
|
|
6173
6754
|
else:
|
|
6174
6755
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
6175
|
-
gamma_i = degrees(
|
|
6756
|
+
gamma_i = degrees(
|
|
6757
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
6758
|
+
)
|
|
6176
6759
|
|
|
6177
6760
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
6178
6761
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -6209,7 +6792,9 @@ def constantSpeedRating_time(
|
|
|
6209
6792
|
if turnFlight:
|
|
6210
6793
|
step_distance = conv.m2nm(
|
|
6211
6794
|
turn.distance(
|
|
6212
|
-
rateOfTurn=rateOfTurn,
|
|
6795
|
+
rateOfTurn=rateOfTurn,
|
|
6796
|
+
TAS=TAS_i,
|
|
6797
|
+
timeOfTurn=step_time,
|
|
6213
6798
|
)
|
|
6214
6799
|
) # arcLength during the turn [NM]
|
|
6215
6800
|
else:
|
|
@@ -6411,77 +6996,94 @@ def accDec(
|
|
|
6411
6996
|
magneticDeclinationGrid=None,
|
|
6412
6997
|
**kwargs,
|
|
6413
6998
|
):
|
|
6414
|
-
"""
|
|
6999
|
+
"""
|
|
7000
|
+
Calculates the time, fuel consumption, and other key flight parameters required for an aircraft
|
|
7001
|
+
to perform an acceleration or deceleration from an initial speed (v_init) to a final speed (v_final)
|
|
7002
|
+
during the climb, cruise, or descent phases of flight.
|
|
7003
|
+
|
|
7004
|
+
The flight parameters are calculated using different models for the BADA (Base of Aircraft Data) families (BADA3, BADA4, BADAH, BADAE).
|
|
7005
|
+
The function can also accommodate different control laws, vertical evolution phases, wind conditions, and complex flight dynamics like turns.
|
|
6415
7006
|
|
|
6416
7007
|
.. note::
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
:param AC:
|
|
6426
|
-
:param speedType:
|
|
6427
|
-
:param v_init:
|
|
6428
|
-
:param v_final:
|
|
6429
|
-
:param phase:
|
|
6430
|
-
:param control:
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
:param
|
|
6436
|
-
:param
|
|
6437
|
-
:param
|
|
6438
|
-
:param
|
|
6439
|
-
:param
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
:param
|
|
6444
|
-
:param
|
|
6445
|
-
:param
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
:param
|
|
6450
|
-
:param
|
|
6451
|
-
:param
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
:
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
7008
|
+
The control law used during the segment depends on the targets provided in the input parameter 'control':
|
|
7009
|
+
- ROCD/slope+ESF: Law based on ROCD/slope + ESF
|
|
7010
|
+
- ROCD/slope+acc: Law based on ROCD/slope + acceleration
|
|
7011
|
+
- ROCD/slope only: Law based on rating + ROCD/slope
|
|
7012
|
+
- ESF only: Law based on rating + ESF
|
|
7013
|
+
- acc only: Law based on rating + acceleration
|
|
7014
|
+
- Neither: Law is rating + default ESF
|
|
7015
|
+
|
|
7016
|
+
:param AC: Aircraft object {BADA3/4/H/E}.
|
|
7017
|
+
:param speedType: Type of speed being followed {M, CAS, TAS}.
|
|
7018
|
+
:param v_init: Initial speed [kt] (CAS/TAS) or [-] MACH.
|
|
7019
|
+
:param v_final: Final speed [kt] (CAS/TAS) or [-] MACH.
|
|
7020
|
+
:param phase: Vertical evolution phase {Climb, Descent, Cruise}.
|
|
7021
|
+
:param control: A dictionary containing the following targets:
|
|
7022
|
+
- ROCDtarget: Rate of climb/descent to be followed [ft/min].
|
|
7023
|
+
- slopetarget: Slope (flight path angle) to be followed [deg].
|
|
7024
|
+
- acctarget: Acceleration to be followed [m/s^2].
|
|
7025
|
+
- ESFtarget: Energy Share Factor to be followed [-].
|
|
7026
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
7027
|
+
:param m_init: Initial aircraft mass [kg].
|
|
7028
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
7029
|
+
:param wS: Wind speed component along the longitudinal axis (affects ground speed) [kt]. Default is 0.0.
|
|
7030
|
+
:param turnMetrics: A dictionary defining turn parameters:
|
|
7031
|
+
- rateOfTurn [deg/s]
|
|
7032
|
+
- bankAngle [deg]
|
|
7033
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
7034
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
7035
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
7036
|
+
:param initialHeading: A dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
7037
|
+
- magnetic: Magnetic heading [deg].
|
|
7038
|
+
- true: True heading [deg].
|
|
7039
|
+
- constantHeading: Whether to maintain a constant heading. Default is None.
|
|
7040
|
+
:param reducedPower: Boolean specifying if reduced power is applied during the climb. Default is None.
|
|
7041
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declination used to correct magnetic heading. Default is None.
|
|
7042
|
+
:param kwargs: Additional optional parameters:
|
|
7043
|
+
- speed_step: Speed step size for the iterative calculation [-] for M, [kt] for TAS/CAS. Default is 0.01 Mach, 5 kt for TAS/CAS.
|
|
7044
|
+
- SOC_init: Initial battery state of charge for electric aircraft (BADAE) [%]. Default is 100.
|
|
7045
|
+
- config: Default aerodynamic configuration (TO, IC, CR, AP, LD). Default is None.
|
|
7046
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight segment. Default is False.
|
|
7047
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 10 for BADA3/4/H, 5 for BADAE.
|
|
7048
|
+
:returns: A pandas DataFrame containing flight trajectory data with the following columns:
|
|
7049
|
+
- Hp: Pressure altitude [ft]
|
|
7050
|
+
- TAS: True Air Speed [kt]
|
|
7051
|
+
- CAS: Calibrated Air Speed [kt]
|
|
7052
|
+
- GS: Ground Speed [kt]
|
|
7053
|
+
- M: Mach number [-]
|
|
7054
|
+
- acc: Acceleration rate [m/s^2]
|
|
7055
|
+
- ROCD: Rate of climb/descent [ft/min]
|
|
7056
|
+
- ESF: Energy Share Factor [-]
|
|
7057
|
+
- FUEL: Fuel flow [kg/s]
|
|
7058
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
7059
|
+
- THR: Thrust force [N]
|
|
7060
|
+
- DRAG: Drag force [N]
|
|
7061
|
+
- time: Elapsed time [s]
|
|
7062
|
+
- dist: Distance flown [NM]
|
|
7063
|
+
- slope: Flight trajectory slope (angle) [deg]
|
|
7064
|
+
- mass: Aircraft mass [kg]
|
|
7065
|
+
- config: Aerodynamic configuration
|
|
7066
|
+
- LAT: Latitude [deg]
|
|
7067
|
+
- LON: Longitude [deg]
|
|
7068
|
+
- HDGTrue: True heading [deg]
|
|
7069
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
7070
|
+
- BankAngle: Bank angle during the turn [deg]
|
|
7071
|
+
- ROT: Rate of turn [deg/s]
|
|
7072
|
+
- Comment: Comments describing the flight segment
|
|
7073
|
+
- For BADAH:
|
|
7074
|
+
- Preq: Required power for level flight [W]
|
|
7075
|
+
- Peng: Generated power [W]
|
|
7076
|
+
- Pav: Available power [W]
|
|
7077
|
+
- For BADAE (electric aircraft):
|
|
7078
|
+
- Pmec: Mechanical power [W]
|
|
7079
|
+
- Pelc: Electrical power [W]
|
|
7080
|
+
- Pbat: Battery power [W]
|
|
7081
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
7082
|
+
- SOC: Battery state of charge [%]
|
|
7083
|
+
- Ibat: Battery current [A]
|
|
7084
|
+
- Vbat: Battery voltage [V]
|
|
7085
|
+
- Vgbat: Ground battery voltage [V]
|
|
7086
|
+
:rtype: pandas.DataFrame
|
|
6485
7087
|
"""
|
|
6486
7088
|
|
|
6487
7089
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -6532,7 +7134,9 @@ def accDec(
|
|
|
6532
7134
|
|
|
6533
7135
|
# speed brakes application
|
|
6534
7136
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
6535
|
-
speedBrakes = kwargs.get(
|
|
7137
|
+
speedBrakes = kwargs.get(
|
|
7138
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
7139
|
+
)
|
|
6536
7140
|
|
|
6537
7141
|
# iteratin step of speed loop
|
|
6538
7142
|
if speedType == "M":
|
|
@@ -6543,7 +7147,9 @@ def accDec(
|
|
|
6543
7147
|
# number of iteration of mass/altitude loop
|
|
6544
7148
|
# BADAE
|
|
6545
7149
|
if AC.BADAFamily.BADAE:
|
|
6546
|
-
m_iter = kwargs.get(
|
|
7150
|
+
m_iter = kwargs.get(
|
|
7151
|
+
"m_iter", 5
|
|
7152
|
+
) # number of iterations for integration loop[-]
|
|
6547
7153
|
# BADA3 or BADA4 or BADAH
|
|
6548
7154
|
else:
|
|
6549
7155
|
m_iter = kwargs.get(
|
|
@@ -6700,9 +7306,9 @@ def accDec(
|
|
|
6700
7306
|
maxRating = checkArgument("maxRating", **kwargs)
|
|
6701
7307
|
|
|
6702
7308
|
# Determine engine rating
|
|
6703
|
-
if (
|
|
6704
|
-
control.
|
|
6705
|
-
):
|
|
7309
|
+
if (
|
|
7310
|
+
control.ROCDtarget is not None or control.slopetarget is not None
|
|
7311
|
+
) and (control.ESFtarget is not None or control.acctarget is not None):
|
|
6706
7312
|
rating = None
|
|
6707
7313
|
else:
|
|
6708
7314
|
if phase == "Climb" or (phase == "Cruise" and speedEvol == "acc"):
|
|
@@ -6806,18 +7412,28 @@ def accDec(
|
|
|
6806
7412
|
for _ in itertools.repeat(None, m_iter):
|
|
6807
7413
|
# atmosphere properties
|
|
6808
7414
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
6809
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
6810
|
-
|
|
7415
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
7416
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
7417
|
+
)
|
|
7418
|
+
temp_const = (theta * const.temp_0) / (
|
|
7419
|
+
theta * const.temp_0 - DeltaTemp
|
|
7420
|
+
)
|
|
6811
7421
|
|
|
6812
7422
|
# aircraft speed
|
|
6813
7423
|
[M_i, CAS_i, TAS_i] = atm.convertSpeed(
|
|
6814
|
-
v=v_i,
|
|
7424
|
+
v=v_i,
|
|
7425
|
+
speedType=speedType,
|
|
7426
|
+
theta=theta,
|
|
7427
|
+
delta=delta,
|
|
7428
|
+
sigma=sigma,
|
|
6815
7429
|
)
|
|
6816
7430
|
|
|
6817
7431
|
if turnFlight:
|
|
6818
7432
|
if turnMetrics["bankAngle"] != 0.0:
|
|
6819
7433
|
# bankAngle is defined
|
|
6820
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
7434
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
7435
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
7436
|
+
)
|
|
6821
7437
|
else:
|
|
6822
7438
|
# rateOfTurn is defined
|
|
6823
7439
|
bankAngle = AC.bankAngle(
|
|
@@ -6845,7 +7461,9 @@ def accDec(
|
|
|
6845
7461
|
# BADAH or BADAE
|
|
6846
7462
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
6847
7463
|
# compute Power required
|
|
6848
|
-
Preq_i = AC.Preq(
|
|
7464
|
+
Preq_i = AC.Preq(
|
|
7465
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
7466
|
+
)
|
|
6849
7467
|
|
|
6850
7468
|
# compute engine power
|
|
6851
7469
|
if rating is None:
|
|
@@ -6863,11 +7481,17 @@ def accDec(
|
|
|
6863
7481
|
|
|
6864
7482
|
# Check that required thrust/power fits in the available thrust/power envelope,
|
|
6865
7483
|
# recompute ROCD if necessary and compute fuel coefficient accordingly
|
|
6866
|
-
Pmin =
|
|
7484
|
+
Pmin = (
|
|
7485
|
+
0.1 * AC.P0
|
|
7486
|
+
) # No minimum power model: assume 10% torque
|
|
6867
7487
|
|
|
6868
7488
|
if AC.BADAFamily.BADAH:
|
|
6869
|
-
Pmax = AC.Pav(
|
|
6870
|
-
|
|
7489
|
+
Pmax = AC.Pav(
|
|
7490
|
+
rating=maxRating, theta=theta, delta=delta
|
|
7491
|
+
)
|
|
7492
|
+
Pav_i = AC.Pav(
|
|
7493
|
+
rating=maxRating, theta=theta, delta=delta
|
|
7494
|
+
)
|
|
6871
7495
|
elif AC.BADAFamily.BADAE:
|
|
6872
7496
|
Pmax = AC.Pav(rating=maxRating, SOC=SOC[-1])
|
|
6873
7497
|
Pav_i = AC.Pav(rating=maxRating, SOC=SOC[-1])
|
|
@@ -6892,7 +7516,11 @@ def accDec(
|
|
|
6892
7516
|
elif control.acctarget is not None:
|
|
6893
7517
|
ROCD_i = (
|
|
6894
7518
|
conv.m2ft(
|
|
6895
|
-
(
|
|
7519
|
+
(
|
|
7520
|
+
P_i
|
|
7521
|
+
- mass_i * TAS_i * control.acctarget
|
|
7522
|
+
- Preq_i
|
|
7523
|
+
)
|
|
6896
7524
|
/ (mass_i * const.g * temp_const)
|
|
6897
7525
|
)
|
|
6898
7526
|
* 60
|
|
@@ -6918,7 +7546,11 @@ def accDec(
|
|
|
6918
7546
|
elif control.acctarget is not None:
|
|
6919
7547
|
ROCD_i = (
|
|
6920
7548
|
conv.m2ft(
|
|
6921
|
-
(
|
|
7549
|
+
(
|
|
7550
|
+
P_i
|
|
7551
|
+
- mass_i * TAS_i * control.acctarget
|
|
7552
|
+
- Preq_i
|
|
7553
|
+
)
|
|
6922
7554
|
/ (mass_i * const.g * temp_const)
|
|
6923
7555
|
)
|
|
6924
7556
|
* 60
|
|
@@ -6929,12 +7561,20 @@ def accDec(
|
|
|
6929
7561
|
else:
|
|
6930
7562
|
# Compute available power
|
|
6931
7563
|
if rating == "UNKNOWN":
|
|
6932
|
-
P_i =
|
|
6933
|
-
|
|
7564
|
+
P_i = (
|
|
7565
|
+
0.1 * AC.P0
|
|
7566
|
+
) # No minimum power model: assume 10% torque
|
|
7567
|
+
Pav_i = AC.Pav(
|
|
7568
|
+
rating=maxRating, theta=theta, delta=delta
|
|
7569
|
+
)
|
|
6934
7570
|
else:
|
|
6935
7571
|
if AC.BADAFamily.BADAH:
|
|
6936
|
-
P_i = AC.Pav(
|
|
6937
|
-
|
|
7572
|
+
P_i = AC.Pav(
|
|
7573
|
+
rating=rating, theta=theta, delta=delta
|
|
7574
|
+
)
|
|
7575
|
+
Pav_i = AC.Pav(
|
|
7576
|
+
rating=rating, theta=theta, delta=delta
|
|
7577
|
+
)
|
|
6938
7578
|
elif AC.BADAFamily.BADAE:
|
|
6939
7579
|
P_i = AC.Pav(rating=rating, SOC=SOC[-1])
|
|
6940
7580
|
Pav_i = AC.Pav(rating=rating, SOC=SOC[-1])
|
|
@@ -6957,7 +7597,9 @@ def accDec(
|
|
|
6957
7597
|
Pelc_i = P_i / AC.eta
|
|
6958
7598
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
6959
7599
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
6960
|
-
Vgbat_i =
|
|
7600
|
+
Vgbat_i = (
|
|
7601
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
7602
|
+
)
|
|
6961
7603
|
|
|
6962
7604
|
# BADA4
|
|
6963
7605
|
elif AC.BADAFamily.BADA4:
|
|
@@ -6976,15 +7618,21 @@ def accDec(
|
|
|
6976
7618
|
# ensure continuity of configuration change within the segment
|
|
6977
7619
|
if config:
|
|
6978
7620
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
6979
|
-
phase=phase,
|
|
7621
|
+
phase=phase,
|
|
7622
|
+
previousConfig=config[-1],
|
|
7623
|
+
currentConfig=config_i,
|
|
6980
7624
|
)
|
|
6981
7625
|
|
|
6982
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
7626
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
7627
|
+
config=config_i
|
|
7628
|
+
)
|
|
6983
7629
|
|
|
6984
7630
|
# compute lift coefficient
|
|
6985
7631
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
6986
7632
|
# compute drag coefficient
|
|
6987
|
-
CD = AC.CD(
|
|
7633
|
+
CD = AC.CD(
|
|
7634
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
7635
|
+
)
|
|
6988
7636
|
# compute drag force
|
|
6989
7637
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
6990
7638
|
|
|
@@ -7044,7 +7692,11 @@ def accDec(
|
|
|
7044
7692
|
else:
|
|
7045
7693
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
7046
7694
|
FUEL_i = AC.ff(
|
|
7047
|
-
CT=CT,
|
|
7695
|
+
CT=CT,
|
|
7696
|
+
delta=delta,
|
|
7697
|
+
theta=theta,
|
|
7698
|
+
M=M_i,
|
|
7699
|
+
DeltaTemp=DeltaTemp,
|
|
7048
7700
|
)
|
|
7049
7701
|
else:
|
|
7050
7702
|
THR_i = AC.Thrust(
|
|
@@ -7056,7 +7708,11 @@ def accDec(
|
|
|
7056
7708
|
) # [N]
|
|
7057
7709
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
7058
7710
|
FUEL_i = AC.ff(
|
|
7059
|
-
CT=CT,
|
|
7711
|
+
CT=CT,
|
|
7712
|
+
delta=delta,
|
|
7713
|
+
theta=theta,
|
|
7714
|
+
M=M_i,
|
|
7715
|
+
DeltaTemp=DeltaTemp,
|
|
7060
7716
|
)
|
|
7061
7717
|
|
|
7062
7718
|
# compute excess power
|
|
@@ -7111,7 +7767,11 @@ def accDec(
|
|
|
7111
7767
|
# recompute ROCD if necessary and compute fuel flow accordingly
|
|
7112
7768
|
|
|
7113
7769
|
THR_min = AC.Thrust(
|
|
7114
|
-
rating="LIDL",
|
|
7770
|
+
rating="LIDL",
|
|
7771
|
+
v=TAS_i,
|
|
7772
|
+
h=H_m,
|
|
7773
|
+
config="CR",
|
|
7774
|
+
DeltaTemp=DeltaTemp,
|
|
7115
7775
|
) # IDLE Thrust
|
|
7116
7776
|
FUEL_min = AC.ff(
|
|
7117
7777
|
flightPhase="Descent",
|
|
@@ -7122,7 +7782,11 @@ def accDec(
|
|
|
7122
7782
|
adapted=False,
|
|
7123
7783
|
) # IDLE Fuel Flow
|
|
7124
7784
|
THR_max = AC.Thrust(
|
|
7125
|
-
rating="MCMB",
|
|
7785
|
+
rating="MCMB",
|
|
7786
|
+
v=TAS_i,
|
|
7787
|
+
h=H_m,
|
|
7788
|
+
config="CR",
|
|
7789
|
+
DeltaTemp=DeltaTemp,
|
|
7126
7790
|
) # MCMB Thrust
|
|
7127
7791
|
FUEL_max = AC.ff(
|
|
7128
7792
|
flightPhase="Climb",
|
|
@@ -7141,7 +7805,11 @@ def accDec(
|
|
|
7141
7805
|
FUEL_i = FUEL_max
|
|
7142
7806
|
else:
|
|
7143
7807
|
FUEL_i = AC.ff(
|
|
7144
|
-
v=TAS_i,
|
|
7808
|
+
v=TAS_i,
|
|
7809
|
+
h=H_m,
|
|
7810
|
+
T=THR_i,
|
|
7811
|
+
config=config_i,
|
|
7812
|
+
adapted=True,
|
|
7145
7813
|
)
|
|
7146
7814
|
else:
|
|
7147
7815
|
THR_i = AC.Thrust(
|
|
@@ -7206,7 +7874,10 @@ def accDec(
|
|
|
7206
7874
|
dhdtisu = PC_i / (mass_i * const.g) # [m/s]
|
|
7207
7875
|
ROCDisu = dhdtisu * 1 / temp_const # [m/s]
|
|
7208
7876
|
ROCD_i = conv.m2ft(ROCDisu) * 60 # [ft/min]
|
|
7209
|
-
elif
|
|
7877
|
+
elif (
|
|
7878
|
+
control.slopetarget is not None
|
|
7879
|
+
or control.ROCDtarget is not None
|
|
7880
|
+
):
|
|
7210
7881
|
dhdtisu = dh_dt_i # [m/s]
|
|
7211
7882
|
ROCDisu = dh_dt_i * 1 / temp_const # [m/s]
|
|
7212
7883
|
ROCD_i = conv.m2ft(ROCDisu) * 60 # [ft/min]
|
|
@@ -7309,7 +7980,10 @@ def accDec(
|
|
|
7309
7980
|
# BADAH or BADAE
|
|
7310
7981
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
7311
7982
|
check.append(
|
|
7312
|
-
P_i
|
|
7983
|
+
P_i
|
|
7984
|
+
- Preq_i
|
|
7985
|
+
- mass_i * const.g * dhdtisu
|
|
7986
|
+
- mass_i * TAS_i * dVdtisu_i
|
|
7313
7987
|
)
|
|
7314
7988
|
|
|
7315
7989
|
# BADA3 or BADA4
|
|
@@ -7327,12 +8001,18 @@ def accDec(
|
|
|
7327
8001
|
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
7328
8002
|
h=conv.ft2m(Hp_i), DeltaTemp=DeltaTemp
|
|
7329
8003
|
)
|
|
7330
|
-
temp_const = (theta * const.temp_0) / (
|
|
8004
|
+
temp_const = (theta * const.temp_0) / (
|
|
8005
|
+
theta * const.temp_0 - DeltaTemp
|
|
8006
|
+
)
|
|
7331
8007
|
if AC.BADAFamily.BADAE:
|
|
7332
|
-
gamma_i = degrees(
|
|
8008
|
+
gamma_i = degrees(
|
|
8009
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
8010
|
+
)
|
|
7333
8011
|
else:
|
|
7334
8012
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
7335
|
-
gamma_i = degrees(
|
|
8013
|
+
gamma_i = degrees(
|
|
8014
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
8015
|
+
)
|
|
7336
8016
|
|
|
7337
8017
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
7338
8018
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -7427,14 +8107,16 @@ def accDec(
|
|
|
7427
8107
|
|
|
7428
8108
|
else:
|
|
7429
8109
|
# calculate the turn
|
|
7430
|
-
(Lat_i, Lon_i, HDGTrue_i) =
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
8110
|
+
(Lat_i, Lon_i, HDGTrue_i) = (
|
|
8111
|
+
turn.destinationPoint_finalBearing(
|
|
8112
|
+
LAT_init=LAT[-1],
|
|
8113
|
+
LON_init=LON[-1],
|
|
8114
|
+
bearingInit=HDGTrue[-1],
|
|
8115
|
+
TAS=TAS_i,
|
|
8116
|
+
rateOfTurn=rateOfTurn,
|
|
8117
|
+
timeOfTurn=step_time,
|
|
8118
|
+
directionOfTurn=directionOfTurn,
|
|
8119
|
+
)
|
|
7438
8120
|
)
|
|
7439
8121
|
|
|
7440
8122
|
if magneticDeclinationGrid is not None:
|
|
@@ -7472,14 +8154,16 @@ def accDec(
|
|
|
7472
8154
|
|
|
7473
8155
|
else:
|
|
7474
8156
|
# calculate the turn
|
|
7475
|
-
(Lat_i, Lon_i, HDGTrue_i) =
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
8157
|
+
(Lat_i, Lon_i, HDGTrue_i) = (
|
|
8158
|
+
turn.destinationPoint_finalBearing(
|
|
8159
|
+
LAT_init=LAT[-1],
|
|
8160
|
+
LON_init=LON[-1],
|
|
8161
|
+
bearingInit=HDGTrue[-1],
|
|
8162
|
+
TAS=TAS_i,
|
|
8163
|
+
rateOfTurn=rateOfTurn,
|
|
8164
|
+
timeOfTurn=step_time,
|
|
8165
|
+
directionOfTurn=directionOfTurn,
|
|
8166
|
+
)
|
|
7483
8167
|
)
|
|
7484
8168
|
|
|
7485
8169
|
if magneticDeclinationGrid is not None:
|
|
@@ -7580,79 +8264,95 @@ def accDec_time(
|
|
|
7580
8264
|
magneticDeclinationGrid=None,
|
|
7581
8265
|
**kwargs,
|
|
7582
8266
|
):
|
|
7583
|
-
"""
|
|
8267
|
+
"""
|
|
8268
|
+
Calculates the time, fuel consumption, and other key flight parameters required for an aircraft
|
|
8269
|
+
to perform an acceleration or deceleration from an initial speed (v_init) over a set period of time
|
|
8270
|
+
during the climb, cruise, or descent phases of flight.
|
|
8271
|
+
|
|
8272
|
+
The flight parameters are calculated using different models for the BADA (Base of Aircraft Data) families (BADA3, BADA4, BADAH, BADAE).
|
|
8273
|
+
The function can also accommodate different control laws, vertical evolution phases, wind conditions, and complex flight dynamics like turns.
|
|
7584
8274
|
|
|
7585
8275
|
.. note::
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
:param AC:
|
|
7595
|
-
:param
|
|
7596
|
-
:param
|
|
7597
|
-
:param
|
|
7598
|
-
:param
|
|
7599
|
-
:param
|
|
7600
|
-
:param
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
:param
|
|
7607
|
-
:param
|
|
7608
|
-
:param
|
|
7609
|
-
:param
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
:param
|
|
7614
|
-
:param
|
|
7615
|
-
:param
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
:param
|
|
7620
|
-
:param
|
|
7621
|
-
:param
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
:
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
8276
|
+
The control law used during the segment depends on the targets provided in the input parameter 'control':
|
|
8277
|
+
- ROCD/slope+ESF: Law based on ROCD/slope + ESF
|
|
8278
|
+
- ROCD/slope+acc: Law based on ROCD/slope + acceleration
|
|
8279
|
+
- ROCD/slope only: Law based on rating + ROCD/slope
|
|
8280
|
+
- ESF only: Law based on rating + ESF
|
|
8281
|
+
- acc only: Law based on rating + acceleration
|
|
8282
|
+
- Neither: Law is rating + default ESF
|
|
8283
|
+
|
|
8284
|
+
:param AC: Aircraft object {BADA3/4/H/E}.
|
|
8285
|
+
:param length: Total duration of the flight segment [s].
|
|
8286
|
+
:param speedType: Type of speed being followed {M, CAS, TAS}.
|
|
8287
|
+
:param v_init: Initial speed [kt] (CAS/TAS) or [-] MACH.
|
|
8288
|
+
:param speedEvol: Evolution of speed {acc, dec} (acceleration or deceleration).
|
|
8289
|
+
:param phase: Vertical evolution phase {Climb, Descent, Cruise}.
|
|
8290
|
+
:param control: A dictionary containing the following targets:
|
|
8291
|
+
- ROCDtarget: Rate of climb/descent to be followed [ft/min].
|
|
8292
|
+
- slopetarget: Slope (flight path angle) to be followed [deg].
|
|
8293
|
+
- acctarget: Acceleration to be followed [m/s^2].
|
|
8294
|
+
- ESFtarget: Energy Share Factor to be followed [-].
|
|
8295
|
+
:param Hp_init: Initial pressure altitude [ft].
|
|
8296
|
+
:param m_init: Initial aircraft mass [kg].
|
|
8297
|
+
:param DeltaTemp: Deviation from the standard ISA temperature [K].
|
|
8298
|
+
:param wS: Wind speed component along the longitudinal axis (affects ground speed) [kt]. Default is 0.0.
|
|
8299
|
+
:param turnMetrics: A dictionary defining turn parameters:
|
|
8300
|
+
- rateOfTurn [deg/s]
|
|
8301
|
+
- bankAngle [deg]
|
|
8302
|
+
- directionOfTurn {LEFT/RIGHT}. Default is straight flight.
|
|
8303
|
+
:param Lat: Initial latitude [deg]. Default is None.
|
|
8304
|
+
:param Lon: Initial longitude [deg]. Default is None.
|
|
8305
|
+
:param initialHeading: A dictionary defining the initial heading (magnetic or true) and whether to fly a constant heading:
|
|
8306
|
+
- magnetic: Magnetic heading [deg].
|
|
8307
|
+
- true: True heading [deg].
|
|
8308
|
+
- constantHeading: Whether to maintain a constant heading. Default is None.
|
|
8309
|
+
:param reducedPower: Boolean specifying if reduced power is applied during the climb. Default is None.
|
|
8310
|
+
:param magneticDeclinationGrid: Optional grid of magnetic declination used to correct magnetic heading. Default is None.
|
|
8311
|
+
:param kwargs: Additional optional parameters:
|
|
8312
|
+
- step_length: Length of each time step in the calculation [s]. Default is 1 second.
|
|
8313
|
+
- SOC_init: Initial battery state of charge for electric aircraft (BADAE) [%]. Default is 100.
|
|
8314
|
+
- config: Default aerodynamic configuration (TO, IC, CR, AP, LD). Default is None.
|
|
8315
|
+
- mass_const: Boolean indicating whether mass remains constant during the flight segment. Default is False.
|
|
8316
|
+
- m_iter: Number of iterations for the mass integration loop. Default is 10 for BADA3/4/H, 5 for BADAE.
|
|
8317
|
+
:returns: A pandas DataFrame containing flight trajectory data with the following columns:
|
|
8318
|
+
- Hp: Pressure altitude [ft]
|
|
8319
|
+
- TAS: True Air Speed [kt]
|
|
8320
|
+
- CAS: Calibrated Air Speed [kt]
|
|
8321
|
+
- GS: Ground Speed [kt]
|
|
8322
|
+
- M: Mach number [-]
|
|
8323
|
+
- acc: Acceleration rate [m/s^2]
|
|
8324
|
+
- ROCD: Rate of climb/descent [ft/min]
|
|
8325
|
+
- ESF: Energy Share Factor [-]
|
|
8326
|
+
- FUEL: Fuel flow [kg/s]
|
|
8327
|
+
- FUELCONSUMED: Total fuel consumed [kg]
|
|
8328
|
+
- THR: Thrust force [N]
|
|
8329
|
+
- DRAG: Drag force [N]
|
|
8330
|
+
- time: Elapsed time [s]
|
|
8331
|
+
- dist: Distance flown [NM]
|
|
8332
|
+
- slope: Flight trajectory slope (angle) [deg]
|
|
8333
|
+
- mass: Aircraft mass [kg]
|
|
8334
|
+
- config: Aerodynamic configuration
|
|
8335
|
+
- LAT: Latitude [deg]
|
|
8336
|
+
- LON: Longitude [deg]
|
|
8337
|
+
- HDGTrue: True heading [deg]
|
|
8338
|
+
- HDGMagnetic: Magnetic heading [deg]
|
|
8339
|
+
- BankAngle: Bank angle during the turn [deg]
|
|
8340
|
+
- ROT: Rate of turn [deg/s]
|
|
8341
|
+
- Comment: Comments describing the flight segment
|
|
8342
|
+
- For BADAH:
|
|
8343
|
+
- Preq: Required power for level flight [W]
|
|
8344
|
+
- Peng: Generated power [W]
|
|
8345
|
+
- Pav: Available power [W]
|
|
8346
|
+
- For BADAE (electric aircraft):
|
|
8347
|
+
- Pmec: Mechanical power [W]
|
|
8348
|
+
- Pelc: Electrical power [W]
|
|
8349
|
+
- Pbat: Battery power [W]
|
|
8350
|
+
- SOCr: Rate of battery state of charge depletion [%/h]
|
|
8351
|
+
- SOC: Battery state of charge [%]
|
|
8352
|
+
- Ibat: Battery current [A]
|
|
8353
|
+
- Vbat: Battery voltage [V]
|
|
8354
|
+
- Vgbat: Ground battery voltage [V]
|
|
8355
|
+
:rtype: pandas.DataFrame
|
|
7656
8356
|
"""
|
|
7657
8357
|
|
|
7658
8358
|
rateOfTurn = turnMetrics["rateOfTurn"]
|
|
@@ -7703,7 +8403,9 @@ def accDec_time(
|
|
|
7703
8403
|
|
|
7704
8404
|
# speed brakes application
|
|
7705
8405
|
if AC.BADAFamily.BADA3 or AC.BADAFamily.BADA4:
|
|
7706
|
-
speedBrakes = kwargs.get(
|
|
8406
|
+
speedBrakes = kwargs.get(
|
|
8407
|
+
"speedBrakes", {"deployed": False, "value": 0.03}
|
|
8408
|
+
)
|
|
7707
8409
|
|
|
7708
8410
|
# step size in [s]
|
|
7709
8411
|
step_length = kwargs.get("step_length", 1)
|
|
@@ -7711,7 +8413,9 @@ def accDec_time(
|
|
|
7711
8413
|
# number of iteration of mass/altitude loop
|
|
7712
8414
|
# BADAE
|
|
7713
8415
|
if AC.BADAFamily.BADAE:
|
|
7714
|
-
m_iter = kwargs.get(
|
|
8416
|
+
m_iter = kwargs.get(
|
|
8417
|
+
"m_iter", 5
|
|
8418
|
+
) # number of iterations for integration loop[-]
|
|
7715
8419
|
# BADA3 or BADA4 or BADAH
|
|
7716
8420
|
else:
|
|
7717
8421
|
m_iter = kwargs.get(
|
|
@@ -7861,9 +8565,9 @@ def accDec_time(
|
|
|
7861
8565
|
maxRating = checkArgument("maxRating", **kwargs)
|
|
7862
8566
|
|
|
7863
8567
|
# Determine engine rating
|
|
7864
|
-
if (
|
|
7865
|
-
control.
|
|
7866
|
-
):
|
|
8568
|
+
if (
|
|
8569
|
+
control.ROCDtarget is not None or control.slopetarget is not None
|
|
8570
|
+
) and (control.ESFtarget is not None or control.acctarget is not None):
|
|
7867
8571
|
rating = None
|
|
7868
8572
|
else:
|
|
7869
8573
|
if phase == "Climb" or (phase == "Cruise" and speedEvol == "acc"):
|
|
@@ -7981,8 +8685,12 @@ def accDec_time(
|
|
|
7981
8685
|
for _ in itertools.repeat(None, m_iter):
|
|
7982
8686
|
# atmosphere properties
|
|
7983
8687
|
H_m = conv.ft2m(Hp_i) # altitude [m]
|
|
7984
|
-
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
7985
|
-
|
|
8688
|
+
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
8689
|
+
h=H_m, DeltaTemp=DeltaTemp
|
|
8690
|
+
)
|
|
8691
|
+
temp_const = (theta * const.temp_0) / (
|
|
8692
|
+
theta * const.temp_0 - DeltaTemp
|
|
8693
|
+
)
|
|
7986
8694
|
|
|
7987
8695
|
step_time = length_loop - time[-1]
|
|
7988
8696
|
|
|
@@ -7994,7 +8702,9 @@ def accDec_time(
|
|
|
7994
8702
|
if turnFlight:
|
|
7995
8703
|
if turnMetrics["bankAngle"] != 0.0:
|
|
7996
8704
|
# bankAngle is defined
|
|
7997
|
-
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
8705
|
+
rateOfTurn = AC.rateOfTurn_bankAngle(
|
|
8706
|
+
TAS=TAS_i, bankAngle=bankAngle
|
|
8707
|
+
)
|
|
7998
8708
|
else:
|
|
7999
8709
|
# rateOfTurn is defined
|
|
8000
8710
|
bankAngle = AC.bankAngle(
|
|
@@ -8021,7 +8731,9 @@ def accDec_time(
|
|
|
8021
8731
|
# BADAH or BADAE
|
|
8022
8732
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
8023
8733
|
# compute Power required
|
|
8024
|
-
Preq_i = AC.Preq(
|
|
8734
|
+
Preq_i = AC.Preq(
|
|
8735
|
+
sigma=sigma, tas=TAS_i, mass=mass_i, phi=bankAngle
|
|
8736
|
+
)
|
|
8025
8737
|
|
|
8026
8738
|
# compute engine power
|
|
8027
8739
|
if rating is None:
|
|
@@ -8039,11 +8751,17 @@ def accDec_time(
|
|
|
8039
8751
|
|
|
8040
8752
|
# Check that required thrust/power fits in the available thrust/power envelope,
|
|
8041
8753
|
# recompute ROCD if necessary and compute fuel coefficient accordingly
|
|
8042
|
-
Pmin =
|
|
8754
|
+
Pmin = (
|
|
8755
|
+
0.1 * AC.P0
|
|
8756
|
+
) # No minimum power model: assume 10% torque
|
|
8043
8757
|
|
|
8044
8758
|
if AC.BADAFamily.BADAH:
|
|
8045
|
-
Pmax = AC.Pav(
|
|
8046
|
-
|
|
8759
|
+
Pmax = AC.Pav(
|
|
8760
|
+
rating=maxRating, theta=theta, delta=delta
|
|
8761
|
+
)
|
|
8762
|
+
Pav_i = AC.Pav(
|
|
8763
|
+
rating=maxRating, theta=theta, delta=delta
|
|
8764
|
+
)
|
|
8047
8765
|
elif AC.BADAFamily.BADAE:
|
|
8048
8766
|
Pmax = AC.Pav(rating=maxRating, SOC=SOC[-1])
|
|
8049
8767
|
Pav_i = AC.Pav(rating=maxRating, SOC=SOC[-1])
|
|
@@ -8068,7 +8786,11 @@ def accDec_time(
|
|
|
8068
8786
|
elif control.acctarget is not None:
|
|
8069
8787
|
ROCD_i = (
|
|
8070
8788
|
conv.m2ft(
|
|
8071
|
-
(
|
|
8789
|
+
(
|
|
8790
|
+
P_i
|
|
8791
|
+
- mass_i * TAS_i * control.acctarget
|
|
8792
|
+
- Preq_i
|
|
8793
|
+
)
|
|
8072
8794
|
/ (mass_i * const.g * temp_const)
|
|
8073
8795
|
)
|
|
8074
8796
|
* 60
|
|
@@ -8093,7 +8815,11 @@ def accDec_time(
|
|
|
8093
8815
|
elif control.acctarget is not None:
|
|
8094
8816
|
ROCD_i = (
|
|
8095
8817
|
conv.m2ft(
|
|
8096
|
-
(
|
|
8818
|
+
(
|
|
8819
|
+
P_i
|
|
8820
|
+
- mass_i * TAS_i * control.acctarget
|
|
8821
|
+
- Preq_i
|
|
8822
|
+
)
|
|
8097
8823
|
/ (mass_i * const.g * temp_const)
|
|
8098
8824
|
)
|
|
8099
8825
|
* 60
|
|
@@ -8104,12 +8830,20 @@ def accDec_time(
|
|
|
8104
8830
|
else:
|
|
8105
8831
|
# Compute available power
|
|
8106
8832
|
if rating == "UNKNOWN":
|
|
8107
|
-
P_i =
|
|
8108
|
-
|
|
8833
|
+
P_i = (
|
|
8834
|
+
0.1 * AC.P0
|
|
8835
|
+
) # No minimum power model: assume 10% torque
|
|
8836
|
+
Pav_i = AC.Pav(
|
|
8837
|
+
rating=maxRating, theta=theta, delta=delta
|
|
8838
|
+
)
|
|
8109
8839
|
else:
|
|
8110
8840
|
if AC.BADAFamily.BADAH:
|
|
8111
|
-
P_i = AC.Pav(
|
|
8112
|
-
|
|
8841
|
+
P_i = AC.Pav(
|
|
8842
|
+
rating=rating, theta=theta, delta=delta
|
|
8843
|
+
)
|
|
8844
|
+
Pav_i = AC.Pav(
|
|
8845
|
+
rating=rating, theta=theta, delta=delta
|
|
8846
|
+
)
|
|
8113
8847
|
elif AC.BADAFamily.BADAE:
|
|
8114
8848
|
P_i = AC.Pav(rating=rating, SOC=SOC[-1])
|
|
8115
8849
|
Pav_i = AC.Pav(rating=rating, SOC=SOC[-1])
|
|
@@ -8132,7 +8866,9 @@ def accDec_time(
|
|
|
8132
8866
|
Pelc_i = P_i / AC.eta
|
|
8133
8867
|
Ibat_i = AC.Ibat(P=Pelc_i, SOC=SOC[-1])
|
|
8134
8868
|
Vbat_i = AC.Vbat(I=Ibat_i, SOC=SOC[-1])
|
|
8135
|
-
Vgbat_i =
|
|
8869
|
+
Vgbat_i = (
|
|
8870
|
+
AC.Vocbat(SOC=SOC[-1]) - AC.R0bat(SOC=SOC[-1]) * Ibat_i
|
|
8871
|
+
)
|
|
8136
8872
|
|
|
8137
8873
|
# BADA4
|
|
8138
8874
|
elif AC.BADAFamily.BADA4:
|
|
@@ -8151,15 +8887,21 @@ def accDec_time(
|
|
|
8151
8887
|
# ensure continuity of configuration change within the segment
|
|
8152
8888
|
if config:
|
|
8153
8889
|
config_i = AC.flightEnvelope.checkConfigurationContinuity(
|
|
8154
|
-
phase=phase,
|
|
8890
|
+
phase=phase,
|
|
8891
|
+
previousConfig=config[-1],
|
|
8892
|
+
currentConfig=config_i,
|
|
8155
8893
|
)
|
|
8156
8894
|
|
|
8157
|
-
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
8895
|
+
[HLid_i, LG_i] = AC.flightEnvelope.getAeroConfig(
|
|
8896
|
+
config=config_i
|
|
8897
|
+
)
|
|
8158
8898
|
|
|
8159
8899
|
# compute lift coefficient
|
|
8160
8900
|
CL = AC.CL(M=M_i, delta=delta, mass=mass_i, nz=nz)
|
|
8161
8901
|
# compute drag coefficient
|
|
8162
|
-
CD = AC.CD(
|
|
8902
|
+
CD = AC.CD(
|
|
8903
|
+
M=M_i, CL=CL, HLid=HLid_i, LG=LG_i, speedBrakes=speedBrakes
|
|
8904
|
+
)
|
|
8163
8905
|
# compute drag force
|
|
8164
8906
|
Drag = AC.D(M=M_i, delta=delta, CD=CD)
|
|
8165
8907
|
|
|
@@ -8219,7 +8961,11 @@ def accDec_time(
|
|
|
8219
8961
|
else:
|
|
8220
8962
|
CT = AC.CT(Thrust=THR_i, delta=delta)
|
|
8221
8963
|
FUEL_i = AC.ff(
|
|
8222
|
-
CT=CT,
|
|
8964
|
+
CT=CT,
|
|
8965
|
+
delta=delta,
|
|
8966
|
+
theta=theta,
|
|
8967
|
+
M=M_i,
|
|
8968
|
+
DeltaTemp=DeltaTemp,
|
|
8223
8969
|
)
|
|
8224
8970
|
else:
|
|
8225
8971
|
THR_i = AC.Thrust(
|
|
@@ -8293,7 +9039,11 @@ def accDec_time(
|
|
|
8293
9039
|
# recompute ROCD if necessary and compute fuel flow accordingly
|
|
8294
9040
|
|
|
8295
9041
|
THR_min = AC.Thrust(
|
|
8296
|
-
rating="LIDL",
|
|
9042
|
+
rating="LIDL",
|
|
9043
|
+
v=TAS_i,
|
|
9044
|
+
h=H_m,
|
|
9045
|
+
config="CR",
|
|
9046
|
+
DeltaTemp=DeltaTemp,
|
|
8297
9047
|
) # IDLE Thrust
|
|
8298
9048
|
FUEL_min = AC.ff(
|
|
8299
9049
|
flightPhase="Descent",
|
|
@@ -8304,7 +9054,11 @@ def accDec_time(
|
|
|
8304
9054
|
adapted=False,
|
|
8305
9055
|
) # IDLE Fuel Flow
|
|
8306
9056
|
THR_max = AC.Thrust(
|
|
8307
|
-
rating="MCMB",
|
|
9057
|
+
rating="MCMB",
|
|
9058
|
+
v=TAS_i,
|
|
9059
|
+
h=H_m,
|
|
9060
|
+
config="CR",
|
|
9061
|
+
DeltaTemp=DeltaTemp,
|
|
8308
9062
|
) # MCMB Thrust
|
|
8309
9063
|
FUEL_max = AC.ff(
|
|
8310
9064
|
flightPhase="Climb",
|
|
@@ -8323,7 +9077,11 @@ def accDec_time(
|
|
|
8323
9077
|
FUEL_i = FUEL_max
|
|
8324
9078
|
else:
|
|
8325
9079
|
FUEL_i = AC.ff(
|
|
8326
|
-
v=TAS_i,
|
|
9080
|
+
v=TAS_i,
|
|
9081
|
+
h=H_m,
|
|
9082
|
+
T=THR_i,
|
|
9083
|
+
config=config_i,
|
|
9084
|
+
adapted=True,
|
|
8327
9085
|
)
|
|
8328
9086
|
else:
|
|
8329
9087
|
THR_i = AC.Thrust(
|
|
@@ -8386,7 +9144,10 @@ def accDec_time(
|
|
|
8386
9144
|
dhdtisu = PC_i / (mass_i * const.g) # [m/s]
|
|
8387
9145
|
ROCDisu = dhdtisu * 1 / temp_const # [m/s]
|
|
8388
9146
|
ROCD_i = conv.m2ft(ROCDisu) * 60 # [ft/min]
|
|
8389
|
-
elif
|
|
9147
|
+
elif (
|
|
9148
|
+
control.slopetarget is not None
|
|
9149
|
+
or control.ROCDtarget is not None
|
|
9150
|
+
):
|
|
8390
9151
|
dhdtisu = dh_dt_i # [m/s]
|
|
8391
9152
|
ROCDisu = dh_dt_i * 1 / temp_const # [m/s]
|
|
8392
9153
|
ROCD_i = conv.m2ft(ROCDisu) * 60 # [ft/min]
|
|
@@ -8487,7 +9248,10 @@ def accDec_time(
|
|
|
8487
9248
|
# BADAH or BADAE
|
|
8488
9249
|
if AC.BADAFamily.BADAH or AC.BADAFamily.BADAE:
|
|
8489
9250
|
check.append(
|
|
8490
|
-
P_i
|
|
9251
|
+
P_i
|
|
9252
|
+
- Preq_i
|
|
9253
|
+
- mass_i * const.g * dhdtisu
|
|
9254
|
+
- mass_i * TAS_i * dVdtisu_i
|
|
8491
9255
|
)
|
|
8492
9256
|
|
|
8493
9257
|
# BADA3 or BADA4
|
|
@@ -8505,12 +9269,18 @@ def accDec_time(
|
|
|
8505
9269
|
[theta, delta, sigma] = atm.atmosphereProperties(
|
|
8506
9270
|
h=conv.ft2m(Hp_i), DeltaTemp=DeltaTemp
|
|
8507
9271
|
)
|
|
8508
|
-
temp_const = (theta * const.temp_0) / (
|
|
9272
|
+
temp_const = (theta * const.temp_0) / (
|
|
9273
|
+
theta * const.temp_0 - DeltaTemp
|
|
9274
|
+
)
|
|
8509
9275
|
if AC.BADAFamily.BADAE:
|
|
8510
|
-
gamma_i = degrees(
|
|
9276
|
+
gamma_i = degrees(
|
|
9277
|
+
atan(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
9278
|
+
)
|
|
8511
9279
|
else:
|
|
8512
9280
|
# using SIN assumes the TAS to be in the direction of the aircraft axis, not ground plane. Which means, this should be mathematically the correct equation for all the aircraft
|
|
8513
|
-
gamma_i = degrees(
|
|
9281
|
+
gamma_i = degrees(
|
|
9282
|
+
asin(conv.ft2m(ROCD_i) * temp_const / 60 / TAS_i)
|
|
9283
|
+
)
|
|
8514
9284
|
|
|
8515
9285
|
# ground speed can be calcualted as TAS projected on the ground minus wind
|
|
8516
9286
|
GS_i = cos(radians(gamma_i)) * TAS_i - wS
|
|
@@ -8521,7 +9291,9 @@ def accDec_time(
|
|
|
8521
9291
|
ROT.append(rateOfTurn)
|
|
8522
9292
|
|
|
8523
9293
|
# integrated data
|
|
8524
|
-
if
|
|
9294
|
+
if (
|
|
9295
|
+
length_loop != 0
|
|
9296
|
+
): # exclude first point: initial t/d/m already known
|
|
8525
9297
|
if AC.BADAFamily.BADAE:
|
|
8526
9298
|
SOC.append(SOC_i)
|
|
8527
9299
|
|
|
@@ -8609,14 +9381,16 @@ def accDec_time(
|
|
|
8609
9381
|
|
|
8610
9382
|
else:
|
|
8611
9383
|
# calculate the turn
|
|
8612
|
-
(Lat_i, Lon_i, HDGTrue_i) =
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
9384
|
+
(Lat_i, Lon_i, HDGTrue_i) = (
|
|
9385
|
+
turn.destinationPoint_finalBearing(
|
|
9386
|
+
LAT_init=LAT[-1],
|
|
9387
|
+
LON_init=LON[-1],
|
|
9388
|
+
bearingInit=HDGTrue[-1],
|
|
9389
|
+
TAS=TAS_i,
|
|
9390
|
+
rateOfTurn=rateOfTurn,
|
|
9391
|
+
timeOfTurn=step_time,
|
|
9392
|
+
directionOfTurn=directionOfTurn,
|
|
9393
|
+
)
|
|
8620
9394
|
)
|
|
8621
9395
|
|
|
8622
9396
|
if magneticDeclinationGrid is not None:
|
|
@@ -8654,14 +9428,16 @@ def accDec_time(
|
|
|
8654
9428
|
|
|
8655
9429
|
else:
|
|
8656
9430
|
# calculate the turn
|
|
8657
|
-
(Lat_i, Lon_i, HDGTrue_i) =
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
9431
|
+
(Lat_i, Lon_i, HDGTrue_i) = (
|
|
9432
|
+
turn.destinationPoint_finalBearing(
|
|
9433
|
+
LAT_init=LAT[-1],
|
|
9434
|
+
LON_init=LON[-1],
|
|
9435
|
+
bearingInit=HDGTrue[-1],
|
|
9436
|
+
TAS=TAS_i,
|
|
9437
|
+
rateOfTurn=rateOfTurn,
|
|
9438
|
+
timeOfTurn=step_time,
|
|
9439
|
+
directionOfTurn=directionOfTurn,
|
|
9440
|
+
)
|
|
8665
9441
|
)
|
|
8666
9442
|
|
|
8667
9443
|
if magneticDeclinationGrid is not None:
|