Examples

Simple FDM loop

examples/simple_fdm.py
"""
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

examples/simple_ctrls.py
"""
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)

examples/simple_wing_level.py
"""
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

examples/simple_gui.py
"""
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

examples/simple_telnet.py
"""
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

examples/simple_http.py
"""
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
...