How to estimate the fuel burnt by an aircraft?
The traffic library integrates the OpenAP aircraft performance and emission model.
We can demonstrate its use with an example flight extracted from a real flight data recorder of an Airbus A320-216 aircraft. Data is anonymised, so there is no geographical information about the trajectory and timestamps are just wrong.
About anonymisation
I may consider adding extra features to this dataset if it could help validate or illustrate a use case with the library. The bottom line is that anonymisation (tail number) should not be obviously broken.
If you find a way to break the anonymisation of this particular flight, good for you: nobody will confirm or deny your claim.
You may manage to find the city-pair associated with that flight: if you reconstruct the whole trajectory with a reasonable process, let’s write a tutorial page to illustrate it.
from traffic.data.samples import fuelflow_a320
There are enough features (and some more) in the provided data to estimate the fuel flow with OpenAP and compare the result with real data.
fuelflow_a320.data[['timestamp', 'altitude', 'groundspeed', 'CAS', 'vertical_acceleration', 'weight', 'fuelflow']]
timestamp | altitude | groundspeed | CAS | vertical_acceleration | weight | fuelflow | |
---|---|---|---|---|---|---|---|
602 | 2011-07-23 13:23:09+00:00 | 232 | 169 | 164.875 | 1.195312 | 69454.064722 | 7625.792472 |
603 | 2011-07-23 13:23:10+00:00 | 264 | 169 | 165.000 | 1.121094 | 69454.064722 | 7642.121792 |
604 | 2011-07-23 13:23:11+00:00 | 296 | 169 | 165.125 | 1.121094 | 69454.064722 | 7643.936161 |
605 | 2011-07-23 13:23:12+00:00 | 330 | 169 | 164.625 | 1.035156 | 69454.064722 | 7634.864316 |
606 | 2011-07-23 13:23:13+00:00 | 364 | 169 | 162.625 | 0.964844 | 69444.992874 | 7629.421210 |
... | ... | ... | ... | ... | ... | ... | ... |
12405 | 2011-07-23 16:39:52+00:00 | 156 | 135 | 128.125 | 0.976562 | 60926.528040 | 720.304452 |
12406 | 2011-07-23 16:39:53+00:00 | 156 | 134 | 126.875 | 1.015625 | 60926.528040 | 733.005034 |
12407 | 2011-07-23 16:39:54+00:00 | 164 | 132 | 124.375 | 0.929688 | 60926.528040 | 912.627555 |
12408 | 2011-07-23 16:39:55+00:00 | 172 | 130 | 123.750 | 1.187500 | 60917.456192 | 1043.262115 |
12409 | 2011-07-23 16:39:56+00:00 | 170 | 127 | 120.875 | 1.140625 | 60908.384344 | 1491.411233 |
11808 rows × 7 columns
User interface
The most direct use of the API, is with the fuelflow()
method:
- Flight.fuelflow(initial_mass=None, typecode=None, engine=None)
Estimates the fuel flow with OpenAP.
The OpenAP model is based on the aircraft type (actually, the most probable engine type) and on three features commonly available in ADS-B data:
altitude (in ft),
vertical rate (in ft/min), and
speed (in kts), in order of priority,
TAS
(true air speed),CAS
(computed air speed, used to compute TAS) andgroundspeed
, if no air speed is available.
- Parameters:
initial_mass (
Union
[None
,str
,float
]) – by default (None), 90% of the maximum take-off weight. You can also pass a value to initialise the mass. Wheninitial_mass > 1
, the mass is in kg. Wheninitial_mass <= 1
, it represents the fraction of the maximum take-off weight.typecode (
Optional
[str
]) – by default (None), use the typecode column if available, the provided aircraft database to infer the typecode based on theicao24
. Ignored if the engine parameter is not None.engine (
Optional
[str
]) – by default (None), use the default engine associated with the aircraft type.
- Return type:
- Returns:
the same instance enriched with three extra features: the mass, the fuel flow (in kg/s) and the total burnt fuel (in kg).
In this dataset:
- the vertical rate is not available as it is not directly measured on aircraft. Here, we consider the most simple approach and derive it from the altitude.In practice, the vertical acceleration is used to filter the vertical rate signal. It has been made available in this dataset (as a g-force)
the fuel flow (the ground truth) is provided in kg/h, we convert it in kg/s for compatibility reason with the output of the OpenAP interface.
f = fuelflow_a320.assign(
# the vertical_rate is not present in the data
vertical_rate=lambda df: df.altitude.diff().fillna(0) * 60,
# convert to kg/s
fuelflow=lambda df: df.fuelflow / 3600,
)
Sources of uncertainty
Let’s analyse the results produced with the following runs.
import altair as alt
alt.data_transformers.disable_max_rows()
def plot_flow(flight):
return flight.chart().encode(
alt.X(
"utchoursminutesseconds(timestamp)",
axis=alt.Axis(title=None, format="%H:%M"),
),
alt.Y("fuelflow", axis=alt.Axis(title="fuel flow (in kg/s)")),
alt.Color("legend", title=None),
)
def chart_flow(*flights):
return (
alt.layer(*(plot_flow(flight) for flight in flights))
.properties(width=600, height=250)
.configure_axis(
labelFontSize=14, titleFontSize=16,
titleAngle=0, titleY=-12, titleAnchor="start",
)
.configure_legend(
orient="bottom", columns=1,
labelFontSize=14, symbolSize=400, symbolStrokeWidth=3,
)
)
Default parameters
The default approach considers the default engine (which is the correct one for this particular aircraft), assumes the initial mass of the aircraft to be 90% of the initial take-off mass, and computes the TAS based on the available CAS.
The typecode="A320"
must be passed as a parameter because the icao24
parameter is not provided in this example.
resampled = f.resample("5s")
openap = resampled.fuelflow(typecode="A320")
chart_flow(
openap.assign(legend="OpenAP estimation"),
resampled.assign(legend="Real fuelflow")
)