#!/usr/bin/env python """ Run a jsbsim model as a child process. """ from __future__ import print_function import atexit import errno import fdpexpect import math import os import select import signal import socket import struct import sys import time import pexpect from pymavlink import fgFDM from .. pysim import util class control_state(object): def __init__(self): self.aileron = 0 self.elevator = 0 self.throttle = 0 self.rudder = 0 self.ground_height = 0 sitl_state = control_state() def interpret_address(addrstr): """Interpret a IP:port string.""" a = addrstr.split(':') a[1] = int(a[1]) return tuple(a) def jsb_set(variable, value): """Set a JSBSim variable.""" global jsb_console jsb_console.send('set %s %s\r\n' % (variable, value)) def setup_template(home): """Setup aircraft/Rascal/reset.xml .""" global opts v = home.split(',') if len(v) != 4: print("home should be lat,lng,alt,hdg - '%s'" % home) sys.exit(1) latitude = float(v[0]) longitude = float(v[1]) altitude = float(v[2]) heading = float(v[3]) sitl_state.ground_height = altitude template = os.path.join('aircraft', 'Rascal', 'reset_template.xml') reset = os.path.join('aircraft', 'Rascal', 'reset.xml') xml = open(template).read() % {'LATITUDE': str(latitude), 'LONGITUDE': str(longitude), 'HEADING': str(heading)} open(reset, mode='w').write(xml) print("Wrote %s" % reset) baseport = int(opts.simout.split(':')[1]) template = os.path.join('jsb_sim', 'fgout_template.xml') out = os.path.join('jsb_sim', 'fgout.xml') xml = open(template).read() % {'FGOUTPORT': str(baseport+3)} open(out, mode='w').write(xml) print("Wrote %s" % out) template = os.path.join('jsb_sim', 'rascal_test_template.xml') out = os.path.join('jsb_sim', 'rascal_test.xml') xml = open(template).read() % {'JSBCONSOLEPORT': str(baseport+4)} open(out, mode='w').write(xml) print("Wrote %s" % out) def process_sitl_input(buf): """Process control changes from SITL sim.""" control = list(struct.unpack('<14H', buf)) pwm = control[:11] (speed, direction, turbulance) = control[11:] global wind wind.speed = speed*0.01 wind.direction = direction*0.01 wind.turbulance = turbulance*0.01 aileron = (pwm[0]-1500)/500.0 elevator = (pwm[1]-1500)/500.0 throttle = (pwm[2]-1000)/1000.0 if opts.revthr: throttle = 1.0 - throttle rudder = (pwm[3]-1500)/500.0 if opts.elevon: # fake an elevon plane ch1 = aileron ch2 = elevator aileron = (ch2-ch1)/2.0 # the minus does away with the need for RC2_REVERSED=-1 elevator = -(ch2+ch1)/2.0 if opts.vtail: # fake an elevon plane ch1 = elevator ch2 = rudder # this matches VTAIL_OUTPUT==2 elevator = (ch2-ch1)/2.0 rudder = (ch2+ch1)/2.0 buf = '' if aileron != sitl_state.aileron: buf += 'set fcs/aileron-cmd-norm %s\n' % aileron sitl_state.aileron = aileron if elevator != sitl_state.elevator: buf += 'set fcs/elevator-cmd-norm %s\n' % elevator sitl_state.elevator = elevator if rudder != sitl_state.rudder: buf += 'set fcs/rudder-cmd-norm %s\n' % rudder sitl_state.rudder = rudder if throttle != sitl_state.throttle: buf += 'set fcs/throttle-cmd-norm %s\n' % throttle sitl_state.throttle = throttle buf += 'step\n' global jsb_console jsb_console.send(buf) def update_wind(wind): """Update wind simulation.""" (speed, direction) = wind.current() jsb_set('atmosphere/psiw-rad', math.radians(direction)) jsb_set('atmosphere/wind-mag-fps', speed/0.3048) def process_jsb_input(buf, simtime): """Process FG FDM input from JSBSim.""" global fdm, fg_out, sim_out fdm.parse(buf) if fg_out: try: agl = fdm.get('agl', units='meters') fdm.set('altitude', agl+sitl_state.ground_height, units='meters') fdm.set('rpm', sitl_state.throttle*1000) fg_out.send(fdm.pack()) except socket.error as e: if e.errno not in [errno.ECONNREFUSED]: raise timestamp = int(simtime*1.0e6) simbuf = struct.pack(' 0.1: update_wind(wind) last_wind_update = tnow if tnow - last_report > 3: print("FPS %u asl=%.1f agl=%.1f roll=%.1f pitch=%.1f a=(%.2f %.2f %.2f) AR=%.1f" % ( frame_count / (time.time() - last_report), fdm.get('altitude', units='meters'), fdm.get('agl', units='meters'), fdm.get('phi', units='degrees'), fdm.get('theta', units='degrees'), fdm.get('A_X_pilot', units='mpss'), fdm.get('A_Y_pilot', units='mpss'), fdm.get('A_Z_pilot', units='mpss'), achieved_rate)) frame_count = 0 last_report = time.time() if new_frame: now = time.time() if now < last_wall_time + scaled_frame_time: time.sleep(last_wall_time+scaled_frame_time - now) now = time.time() if now > last_wall_time and now - last_wall_time < 0.1: rate = 1.0/(now - last_wall_time) achieved_rate = (0.98*achieved_rate) + (0.02*rate) if achieved_rate < opts.rate*opts.speedup: scaled_frame_time *= 0.999 else: scaled_frame_time *= 1.001 last_wall_time = now def exit_handler(): """Exit the sim.""" print("running exit handler") signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) # JSBSim really doesn't like to die ... if getattr(jsb, 'pid', None) is not None: os.kill(jsb.pid, signal.SIGKILL) jsb_console.send('quit\n') jsb.close(force=True) util.pexpect_close_all() sys.exit(1) signal.signal(signal.SIGINT, exit_handler) signal.signal(signal.SIGTERM, exit_handler) try: main_loop() except Exception as ex: print(ex) exit_handler() raise