Examples¶
Simple FDM loop¶
"""
Simple Flight Dynamics Model (FDM) example that makes the altitude increase and the plane roll in the air.
"""
import time
from flightgear_python.fg_if import FDMConnection
def fdm_callback(fdm_data, event_pipe):
if event_pipe.child_poll():
phi_rad_child, = event_pipe.child_recv() # unpack tuple
# set only the data that we need to
fdm_data['phi_rad'] = phi_rad_child # we can force our own values
fdm_data.alt_m = fdm_data.alt_m + 0.5 # or just make a relative change
return fdm_data # return the whole structure
"""
Start FlightGear with `--native-fdm=socket,out,30,localhost,5501,udp --native-fdm=socket,in,30,localhost,5502,udp`
(you probably also want `--fdm=null` and `--max-fps=30` to stop the simulation fighting with
these external commands)
"""
if __name__ == '__main__': # NOTE: This is REQUIRED on Windows!
fdm_conn = FDMConnection(fdm_version=24) # May need to change version from 24
fdm_event_pipe = fdm_conn.connect_rx('localhost', 5501, fdm_callback)
fdm_conn.connect_tx('localhost', 5502)
fdm_conn.start() # Start the FDM RX/TX loop
phi_rad_parent = 0.0
while True:
phi_rad_parent += 0.1
# could also do `fdm_conn.event_pipe.parent_send` so you just need to pass around `fdm_conn`
fdm_event_pipe.parent_send((phi_rad_parent,)) # send tuple
time.sleep(0.5)
Simple Controls loop¶
"""
Simple Controls example that sweeps the control surfaces and periodically puts the gear up/down
"""
import time
import math
from flightgear_python.fg_if import CtrlsConnection
gear_down_child_state = True
def ctrls_callback(ctrls_data, event_pipe):
global gear_down_child_state
if event_pipe.child_poll():
gear_down_child, = event_pipe.child_recv() # unpack tuple
# TODO: FG sometimes ignores "once" updates? i.e. if we set `ctrls_data.gear_handle`
# the next callback will still have the old value of `ctrls_data.gear_handle`, not
# the one that we set. To fix this we can just keep our own state of what the value
# should be and set it every time. I still need to figure out a clean way to fix
# this on the backend
gear_down_child_state = gear_down_child
ctrls_data.gear_handle = 'down' if gear_down_child_state else 'up'
# set only the data that we need to
ctrls_data.aileron = math.sin(time.time())
ctrls_data.elevator = math.sin(time.time())
ctrls_data.rudder = math.sin(time.time())
ctrls_data.throttle[0] = (math.sin(time.time()) / 2) + 0.5
ctrls_data.throttle[1] = (-math.sin(time.time()) / 2) + 0.5
return ctrls_data # return the whole structure
"""
Start FlightGear with `--native-ctrls=socket,out,30,localhost,5503,udp --native-ctrls=socket,in,30,localhost,5504,udp`
"""
if __name__ == '__main__': # NOTE: This is REQUIRED on Windows!
ctrls_conn = CtrlsConnection(ctrls_version=27)
ctrls_event_pipe = ctrls_conn.connect_rx('localhost', 5503, ctrls_callback)
ctrls_conn.connect_tx('localhost', 5504)
ctrls_conn.start() # Start the Ctrls RX/TX loop
gear_down_parent = True
time.sleep(2)
while True:
# could also do `ctrls_conn.event_pipe.parent_send` so you just need to pass around `ctrls_conn`
ctrls_event_pipe.parent_send((gear_down_parent,)) # send tuple
gear_down_parent = not gear_down_parent # Flip gear state
time.sleep(10)
Simple Wing Leveler¶
Here we connect a FDM interface and a Controls interface together in order
to set the wings to 5 degrees clockwise. (change roll_deg_setpoint
to
0.0
to actually level the wings)
"""
Simple example where we connect to both the FDM and Controls interface to create closed loop control.
We're using a simple Proportional ('P') controller that reads the plane roll angle and controls the ailerons.
"""
import time
import math
from flightgear_python.fg_if import FDMConnection, CtrlsConnection
child_aileron_state = 0.0
def ctrls_callback(ctrls_data, event_pipe):
global child_aileron_state
if event_pipe.child_poll():
child_aileron_req, = event_pipe.child_recv() # Unpack tuple from parent
# TODO: FG sometimes ignores "once" updates? i.e. if we set `ctrls_data.aileron`
# the next callback will still have the old value of `ctrls_data.aileron`, not
# the one that we set. To fix this we can just keep our own state of what the value
# should be and set it every time. I still need to figure out a clean way to fix
# this on the backend
child_aileron_state = child_aileron_req
ctrls_data.aileron = child_aileron_state # from -1..1
return ctrls_data
def fdm_callback(fdm_data, event_pipe):
roll_deg = math.degrees(fdm_data.phi_rad)
event_pipe.child_send((roll_deg,)) # Send tuple to parent
"""
Start FlightGear with:
`--native-fdm=socket,out,30,localhost,5501,udp --native-ctrls=socket,out,30,localhost,5503,udp --native-ctrls=socket,in,30,localhost,5504,udp`
"""
if __name__ == '__main__': # NOTE: This is REQUIRED on Windows!
ctrls_conn = CtrlsConnection(ctrls_version=27)
ctrls_event_pipe = ctrls_conn.connect_rx('localhost', 5503, ctrls_callback)
ctrls_conn.connect_tx('localhost', 5504)
fdm_conn = FDMConnection(fdm_version=24) # May need to change version from 24
fdm_event_pipe = fdm_conn.connect_rx('localhost', 5501, fdm_callback)
ctrls_conn.start() # Start the Ctrls RX/TX loop
fdm_conn.start() # Start the FDM RX loop
# We'll just use a simple 'P' controller
roll_Kp = 0.1
roll_deg_setpoint = 5.0 # Let's say the setpoint is 5deg clockwise
while True:
if fdm_event_pipe.parent_poll(): # Only update controller when we get FDM updates
parent_roll_deg, = fdm_event_pipe.parent_recv() # Unpack tuple from FDM
roll_error = roll_deg_setpoint - parent_roll_deg # Calculate error
# Calculate the aileron request (technically should clamp to -1..1 but it doesn't really matter)
parent_aileron_req = roll_error * roll_Kp
ctrls_event_pipe.parent_send((parent_aileron_req,)) # Send tuple to Ctrls
time.sleep(0.01) # Faster than 30Hz but still sleeping a bit
Simple GUI loop¶
"""
Simple GUI example that prints the latitude and longitude of the aircraft
"""
import time
import math
from flightgear_python.fg_if import GuiConnection
def gui_callback(gui_data, event_pipe):
lat_deg = math.degrees(gui_data['lat_rad'])
lon_deg = math.degrees(gui_data['lon_rad'])
child_data = (lat_deg, lon_deg)
event_pipe.child_send(child_data)
"""
Start FlightGear with `--native-gui=socket,out,30,localhost,5504,udp --native-gui=socket,in,30,localhost,5505,udp`
"""
if __name__ == '__main__': # NOTE: This is REQUIRED on Windows!
gui_conn = GuiConnection(gui_version=8)
gui_event_pipe = gui_conn.connect_rx('localhost', 5504, gui_callback)
# Note: I couldn't get FlightGear to do anything with the returned data on this interface
# I think it's just ignoring everything. Technically you can send data back though.
# gui_conn.connect_tx('localhost', 5505)
gui_conn.start() # Start the GUI RX loop
while True:
# could also do `gui_conn.event_pipe.parent_recv` so you just need to pass around `gui_conn`
pipe_data = gui_event_pipe.parent_recv() # receive tuple
lat_deg, lon_deg = pipe_data
print(f'Lat: {lat_deg:.6f} Lon: {lon_deg:.6f}')
time.sleep(0.01)
Simple telnet (properties) interface¶
"""
Simple telnet example that makes the altitude increase.
"""
import time
from pprint import pprint
from flightgear_python.fg_if import TelnetConnection
"""
Start FlightGear with `--telnet=socket,bi,60,localhost,5500,tcp`
"""
telnet_conn = TelnetConnection('localhost', 5500)
telnet_conn.connect() # Make an actual connection
telnet_props = telnet_conn.list_props('/', recurse_limit=0)
pprint(telnet_props) # List the top-level properties, no recursion
while True:
alt_ft = telnet_conn.get_prop('/position/altitude-ft')
print(f'Altitude: {alt_ft:.1f}ft')
telnet_conn.set_prop('/position/altitude-ft', alt_ft + 20.0)
time.sleep(0.1)
sample output
{'directories': ['/sim',
'/position',
'/orientation',
'/autopilot',
'/velocities',
'/controls',
'/environment',
'/instrumentation',
'/local-weather',
'/accelerations',
'/devices',
'/input',
'/systems',
'/logging',
'/nasal',
'/scenery',
'/earthview',
'/fdm',
'/engines',
'/payload',
'/aircraft',
'/consumables',
'/gear',
'/rotors',
'/limits',
'/save',
'/command',
'/canvas',
'/surface-positions',
'/ai',
'/rendering',
'/ephemeris',
'/pax',
'/io',
'/hazards',
'/Interior',
'/_debug'],
'properties': {'/models': ''}}
Altitude: 4757.3ft
Altitude: 4782.7ft
Altitude: 4807.4ft
...
Simple HTTP (properties) interface¶
"""
Simple http example that makes the altitude increase.
"""
import time
from pprint import pprint
from flightgear_python.fg_if import HTTPConnection
"""
Start FlightGear with `--httpd=5050`
"""
http_conn = HTTPConnection('localhost', 5050)
pprint(http_conn.list_props('/', recurse_limit=0)) # List the top-level properties, no recursion
while True:
alt_ft = http_conn.get_prop('/position/altitude-ft')
print(f'Altitude: {alt_ft:.1f}ft')
http_conn.set_prop('/position/altitude-ft', alt_ft + 20.0)
time.sleep(0.1)
sample output
{'directories': ['/sim',
'/position',
'/orientation',
'/autopilot',
'/velocities',
'/controls',
'/environment',
'/instrumentation',
'/local-weather',
'/accelerations',
'/devices',
'/input',
'/systems',
'/logging',
'/nasal',
'/scenery',
'/earthview',
'/fdm',
'/engines',
'/payload',
'/aircraft',
'/consumables',
'/gear',
'/rotors',
'/limits',
'/save',
'/command',
'/canvas',
'/surface-positions',
'/ai',
'/rendering',
'/ephemeris',
'/pax',
'/io',
'/hazards',
'/Interior',
'/_debug'],
'properties': {'/models': ''}}
Altitude: 4757.3ft
Altitude: 4782.7ft
Altitude: 4807.4ft
...