Subscribe to my newsletter
X plane 11 flight sim offers telemetry data through UDP sockets. Having fun with it.
1. X plane settings
there’s nice way to output data file on disk as well.
2. python script
#kernel gpu_spark
#https://www.x-plane.com/kb/data-set-output-table/
#spark and delta
import findspark
findspark.init()
findspark.find()
import pyspark
from pyspark import SparkContext, SparkConf
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *
#conf = pyspark.SparkConf().setAppName('appName').setMaster('local')
#sc = pyspark.SparkContext(conf=conf)
#spark = SparkSession(sc)
spark = pyspark.sql.SparkSession.builder.appName("MyApp") \
.config("spark.jars.packages", "io.delta:delta-core_2.12:0.7.0") \
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
.config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
.getOrCreate()
from delta.tables import *
import pandas as pd
pd.set_option('display.max_columns', None)
pd.set_option("max_rows", 20)
import numpy as np
import matplotlib.pyplot as plt
# from IPython.display import IFrame
# IFrame(src='https://www.x-plane.com/kb/data-set-output-table/', width=900, height=600)
#how to run jupyter from a remote server
#https://www.digitalocean.com/community/tutorials/how-to-install-run-connect-to-jupyter-notebook-on-remote-server
pdf = pd.read_csv('../data/20201002_cessna172.txt', sep='|')
#clean column names
def clean_dataframe_column_names(df):
new_column_names = []
for col in df.columns:
new_col = col.lstrip().rstrip().lower().replace(" ", "_"). \
replace("__", "").replace(",", "_").replace("__", "_").replace(",", ""). \
replace(".", "").replace("*", "").replace("-", "_").replace("0/1", "zero_or_one"). \
replace("/", "_per_").replace("#", "number")#strip beginning spaces, makes lowercase, add underscpre
new_column_names.append(new_col)
column_names_mapping = dict(zip(df.columns,new_column_names))
df.rename(columns = column_names_mapping, inplace = True)
return df,column_names_mapping
pdf,column_names_mapping = clean_dataframe_column_names(pdf)
column_names_mapping
'''
{' f-act,_/sec ': 'f_act__per_sec',
' f-sim,_/sec ': 'f_sim__per_sec',
' frame,_time ': 'frame_time',
' __cpu,_time ': 'cpu_time',
' _gpu_,time_ ': '_gpu_time_',
' _grnd,ratio ': '_grnd_ratio',
' _flit,ratio ': '_flit_ratio',
' _real,_time ': '_real_time',
' _totl,_time ': '_totl_time',
' missn,_time ': 'missn_time',
' timer,_time ': 'timer_time',
' _zulu,_time ': '_zulu_time',
' local,_time ': 'local_time',
' hobbs,_time ': 'hobbs_time',
' __USE,puffs ': 'use_puffs',
' __TOT,puffs ': 'tot_puffs',
' __VIS,_tris ': 'vis_tris',
' _Vind,_kias ': '_vind_kias',
' _Vind,_keas ': '_vind_keas',
' Vtrue,_ktas ': 'vtrue_ktas',
' Vtrue,_ktgs ': 'vtrue_ktgs',
' _Vind,__mph ': '_vind_mph',
' Vtrue,mphas ': 'vtrue_mphas'}
'''
%matplotlib inline
fig = plt.figure()
ax = plt.axes(projection='3d')
# Data for a three-dimensional line
xline = pdf.lat_deg
yline = pdf.lon_deg
zline = pdf.alt_ftagl
ax.plot3D(xline, yline, zline, 'gray')
# Data for three-dimensional scattered points
# zdata = 15 * np.random.random(100)
# xdata = np.sin(zdata) + 0.1 * np.random.randn(100)
# ydata = np.cos(zdata) + 0.1 * np.random.randn(100)
# ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='Greens');
#https://towardsdatascience.com/easy-steps-to-plot-geographic-data-on-a-map-python-11217859a2db
BBox = pdf.lon_deg.min(), pdf.lon_deg.max(), pdf.lat_deg.min(), pdf.lat_deg.max()
BBox
#output
(-122.32378, -122.28126999999999, 47.43139, 47.58338)
#https://stackoverflow.com/questions/38507069/how-to-make-a-flight-path-projection-if-possible-in-python
f = open('../data/20201002_cessna172.kml', 'w')
#Writing the kml file.
f.write("<?xml version='1.0' encoding='UTF-8'?>\n")
f.write("<kml xmlns='http://earth.google.com/kml/2.2'>\n")
f.write("<Document>\n")
f.write("<Placemark>\n")
f.write(" <name>flight</name>\n")
f.write(" <LineString>\n")
f.write(" <extrude>1</extrude>\n")
f.write(" <altitudeMode>absolute</altitudeMode>\n")
f.write(" <coordinates>\n")
for i in range(0,len(pdf['alt_ind']),10): #Here I skip some data
f.write(" "+str(pdf['lon_deg'][i]) + ","+ str(pdf['lat_deg'][i]) + "," + str(pdf['alt_ind'][i]) +"\n")
f.write(" </coordinates>\n")
f.write(" </LineString>\n")
f.write("</Placemark>\n")
f.write("</Document>")
f.write("</kml>\n")
f.close()
pdf[['alt_ftmsl','alt_ftagl']].plot.line()
UDP_PORT = 40
import socket
import struct
def DecodeDataMessage(message):
# Message consists of 4 byte type and 8 times a 4byte float value.
# Write the results in a python dict.
values = {}
typelen = 4
#type = int.from_bytes(message[0:typelen], byteorder='little')
data = message[typelen:]
dataFLOATS = struct.unpack("<ffffffff",data)
type = 'custom'
if type == 3:
values["speed"]=dataFLOATS[0]
elif type == 17:
values["pitch"]=dataFLOATS[0]
values["roll"]=dataFLOATS[1]
values["heading"]=dataFLOATS[2]
values["heading2"]=dataFLOATS[3]
elif type == 20:
values["latitude"]=dataFLOATS[0]
values["longitude"]=dataFLOATS[1]
values["altitude MSL"]=dataFLOATS[2]
values["altitude AGL"]=dataFLOATS[3]
values["altitude 2"]=dataFLOATS[4]
values["altitude 3"]=dataFLOATS[5]
elif type == 'custom':
values["real_time"]=dataFLOATS[0]
values["total_time"]=dataFLOATS[1]
values["mission_time"]=dataFLOATS[2]
values["timer_time"]=dataFLOATS[3]
values["zulu_utc_time"]=str(datetime.datetime.strftime(datetime.date.today(), format = "%Y%m%d")) + datetime.datetime.strftime(datetime.datetime.strptime(str(datetime.timedelta(seconds = dataFLOATS[4]*3600)),'%H:%M:%S.%f'), format='%H%M%S')
values["local_time"]=dataFLOATS[5]
values["hobbs_time"]=dataFLOATS[6]
values["pitch"]=dataFLOATS[7]
values["roll"]=dataFLOATS[8]
values["heading"]=dataFLOATS[9]
values["heading2"]=dataFLOATS[10]
values["latitude"]=dataFLOATS[11]
values["longitude"]=dataFLOATS[12]
values["altitude_MSL"]=dataFLOATS[13]
values["altitude_AGL"]=dataFLOATS[14]
values["on_runway"]=dataFLOATS[15]
values["altitude_2_msl"]=dataFLOATS[16]
values["latitude_origin"]=dataFLOATS[17]
values["longitude_origin"]=dataFLOATS[18]
else:
print(" Type ", type, " not implemented: ",dataFLOATS)
return values
def DecodePacket(data):
# Packet consists of 5 byte header and multiple messages.
valuesout = {}
headerlen = 5
header = data[0:headerlen]
messages = data[headerlen:]
if(header==b'DATA*'):
# Divide into 36 byte messages
messagelen = 36
for i in range(0,int((len(messages))/messagelen)):
message = messages[(i*messagelen) : ((i+1)*messagelen)]
values = DecodeDataMessage(message)
valuesout.update( values )
else:
print("Packet type not implemented. ")
print(" Header: ", header)
print(" Data: ", messages)
return valuesout
def main():
# Open a Socket on UDP Port 49000
UDP_IP = "127.0.0.1"
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
# Receive a packet
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
# Decode the packet. Result is a python dict (like a map in C) with values from X-Plane.
# Example:
# {'latitude': 47.72798156738281, 'longitude': 12.434000015258789,
# 'altitude MSL': 1822.67, 'altitude AGL': 0.17, 'speed': 4.11,
# 'roll': 1.05, 'pitch': -4.38, 'heading': 275.43, 'heading2': 271.84}
#print(data)
values = DecodePacket(data)
print(values)
#print()
# if __name__ == '__main__':
# main()
Have fun!