huginn/unit_conversions.py
def convert_jsbsim_acceleration(acceleration):
def convert_jsbsim_angular_acceleration(acceleration):
def convert_jsbsim_angular_velocity(velocity):
def convert_jsbsim_velocity(velocity):
def convert_jsbsim_pressure(pressure):
def convert_jsbsim_temperature(temperature):
def convert_jsbsim_density(density):
def convert_jsbsim_force(force):
huginn/configuration.py
SIMULATOR_CONTROL_PORT = 10500
WEB_SERVER_PORT = 8090
SENSORS_PORT = 10300
CONTROLS_PORT = 10301
FDM_CLIENT_ADDRESS = "127.0.0.1"
FDM_CLIENT_PORT = 10302
FDM_CLIENT_DT = 0.1
TELEMETRY_PORT = 10400
TELEMETRY_DT = 1.0
WEBSOCKET_HOST = "localhost"
WEBSOCKET_PORT = 8091
WEBSOCKET_UPDATE_RATE = 20.0
AIRCRAFT = "Rascal"
DT = 1.0/300.0
LATITUDE = 37.9232547
LONGITUDE = 23.921773
ALTITUDE = 300.0
AIRSPEED = 30.0
HEADING = 45.0
AVAILABLE_AIRCRAFT = ["Rascal", "easystar"]
DEFAULT_AICRAFT = "Rascal"
LOG_FILE = "huginn.log"
def get_data_path():
huginn/instruments.py
def pressure_altitude(sea_level_pressure, pressure, temperature):
    """Calculate the pressure altitude

    Arguments:
    sea_level_pressure: the pressure at sea level in Pascal
    pressure: the pressure at the current altitude in Pascal
    temperature: the temperature in Kelvin
    """
    return log(sea_level_pressure/pressure) * ((R * temperature) / (g * M))
huginn/instruments.py
class Altimeter(object):
    """The Altimeter class simulates the aircraft altimeter"""

    def __init__(self, fdmexec):
        """Create a new Altimeter object

        Arguments:
        fdmexec: A JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self._atmosphere = Atmosphere(fdmexec)
        self._pressure = 29.92130302799185 * ur.in_Hg

    @property
    def altitude(self):
        """Return the altitude in feet"""
        sea_level_pressure = self._pressure.to(ur.pascal)

        altitude = pressure_altitude(sea_level_pressure.magnitude,
                                     self._atmosphere.pressure,
                                     self._atmosphere.temperature)

        altitude = altitude * ur.meter
        altitude.ito(ur.foot)

        return altitude.magnitude

    @property
    def pressure(self):
        """Return the instrument's pressure setting in inHg"""
        return self._pressure.magnitude

    @pressure.setter
    def pressure(self, value):
        """Set the instrument's pressure setting

        Arguments:
        value: the pressure in inHg
        """
        self._pressure = value * ur.in_Hg
huginn/instruments.py
class HeadingIndicator(object):
    """The HeadingIndicator class simulates the heading indicator
    instrument"""

    def __init__(self, fdmexec):
        """Create a new HeadingIndicator object

        Arguments:
        fdmexec: a JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self._orientation = Orientation(fdmexec)

    @property
    def heading(self):
        """Return the heading in degrees"""
        return self._orientation.psi
huginn/instruments.py
class Instruments(object):
    """The Instruments class contains the instances of the aircraft's
    instruments"""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec
        self.gps = GPS(fdmexec)
        self.airspeed_indicator = AirspeedIndicator(fdmexec)
        self.altimeter = Altimeter(fdmexec)
        self.attitude_indicator = AttitudeIndicator(fdmexec)
        self.heading_indicator = HeadingIndicator(fdmexec)
        self.vertical_speed_indicator = VerticalSpeedIndicator(fdmexec)
huginn/constants.py
T0 = 288.15
M = 28.97
R = 8314.4598
huginn/instruments.py
class VerticalSpeedIndicator(object):
    """The VerticalSpeedIndicator simulates the aircraft's vertical speed
    indicator instrument"""

    def __init__(self, fdmexec):
        """Create a new VerticalSpeedIndicator object

        Arguments:
        fdmexec: a JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self._velocities = Velocities(fdmexec)

    @property
    def climb_rate(self):
        """Return the climb rate in feet per minutes"""
        climb_rate = self._velocities.climb_rate * ur.meters_per_second
        climb_rate.ito(ur.feet_per_minute)

        return climb_rate.magnitude
huginn/instruments.py
class AirspeedIndicator(object):
    """The AirspeedIndicator class simulates the aircraft airspeed
    indicator"""

    def __init__(self, fdmexec):
        """Create a new AirspeedIndicator object

        Arguments:
        fdmexec: a JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self._atmosphere = Atmosphere(fdmexec)

    @property
    def airspeed(self):
        """Returns the airspeed in knots"""
        total_pressure = self.fdmexec.GetAuxiliary().GetTotalPressure()

        total_pressure = convert_jsbsim_pressure(total_pressure)

        return true_airspeed(total_pressure,
                             self._atmosphere.pressure,
                             self._atmosphere.temperature)
huginn/instruments.py
class GPS(object):
    """The GPS class simulates the aircraft's GPS system."""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec
        self._position = Position(fdmexec)
        self._velocities = Velocities(fdmexec)

    @property
    def latitude(self):
        """Returns the latitude in degrees"""
        return self._position.latitude

    @property
    def longitude(self):
        """Returns the longitude in degrees"""
        return self._position.longitude

    @property
    def altitude(self):
        """Returns the altitude in meters"""
        return self._position.altitude

    @property
    def airspeed(self):
        """Returns the airspeed in meters per second"""
        return self._velocities.true_airspeed

    @property
    def heading(self):
        """Returns the heading in degrees"""
        return self._position.heading
huginn/fdm.py
class Orientation(object):
    """The Orientation class contains data about the orientation of the
    aircraft"""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec

    @property
    def phi(self):
        """Return the phi euler angle angle in degrees"""
        return self.fdmexec.GetPropagate().GetEulerDeg(1)

    @property
    def theta(self):
        """Return the theta euler angle angle in degrees"""
        return self.fdmexec.GetPropagate().GetEulerDeg(2)

    @property
    def psi(self):
        """Return the psi euler angle angle in degrees"""
        return self.fdmexec.GetPropagate().GetEulerDeg(3)
huginn/fdm.py
class Atmosphere(object):
    """The Atmosphere contains the fdm data about the atmosphere"""

    def __init__(self, fdmexec):
        """Create a new Atmosphere object

        Arguments:
        fdmexec: a JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec

    @property
    def pressure(self):
        """Returns the pressure at the current altitude. The value will be in
        Pascal"""
        pressure = self.fdmexec.GetAtmosphere().GetPressure()

        return convert_jsbsim_pressure(pressure)

    @property
    def sea_level_pressure(self):
        """Returns the pressure at the sea level. The value will be in
        Pascal"""
        pressure = self.fdmexec.GetAtmosphere().GetPressureSL()

        return convert_jsbsim_pressure(pressure)

    @property
    def temperature(self):
        """Returns the temperature in kelvin at the current altitude"""
        temperature = self.fdmexec.GetAtmosphere().GetTemperature()

        return convert_jsbsim_temperature(temperature)

    @property
    def sea_level_temperature(self):
        """Returns the temperature in kelvin at the sea level"""
        temperature = self.fdmexec.GetAtmosphere().GetTemperatureSL()

        return convert_jsbsim_temperature(temperature)

    @property
    def density(self):
        """Returns the atmospheric density at the current altitude in
        kg/meters^3"""
        density = self.fdmexec.GetAtmosphere().GetDensity()

        return convert_jsbsim_density(density)

    @property
    def sea_level_density(self):
        """Returns the atmospheric density at sea level in kg/meters^3"""
        density = self.fdmexec.GetAtmosphere().GetDensitySL()

        return convert_jsbsim_density(density)
huginn/instruments.py
class AttitudeIndicator(object):
    """The AttitudeIndicator class simulates the attitude indicator
    instrument"""

    def __init__(self, fdmexec):
        """Create a new AttitudeIndicator object

        Arguments:
        fdmexec: a JSBSim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self._orientation = Orientation(fdmexec)

    @property
    def roll(self):
        """Return the roll angle ikn degrees"""
        return self._orientation.phi

    @property
    def pitch(self):
        """Return the pitch angle in degrees"""
        return self._orientation.theta
huginn/fdm.py
class Velocities(object):
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec

    @property
    def p(self):
        """Return the p item of the body angular rates in degrees/sec"""
        velocity = self.fdmexec.GetPropagate().GetPQR(1)

        return convert_jsbsim_angular_velocity(velocity)

    @property
    def q(self):
        """Return the q item of the body angular rates in degrees/sec"""
        velocity = self.fdmexec.GetPropagate().GetPQR(2)

        return convert_jsbsim_angular_velocity(velocity)

    @property
    def r(self):
        """Return the r item of the body angular rates in degrees/sec"""
        velocity = self.fdmexec.GetPropagate().GetPQR(3)

        return convert_jsbsim_angular_velocity(velocity)

    @property
    def true_airspeed(self):
        """Return the true airspeed in meters/second"""
        airspeed = self.fdmexec.GetAuxiliary().GetVtrueFPS()

        return convert_jsbsim_velocity(airspeed)

    @property
    def climb_rate(self):
        """Return the vertical velocity in meters/seconds"""
        # climb_rate = self.fdmexec.GetPropertyValue("velocities/v-down-fps")
        climb_rate = -self.fdmexec.GetPropagate().GetVel(3)

        return convert_jsbsim_velocity(climb_rate)

    @property
    def u(self):
        """Returns the u item of the body frame velocity vector in
        meters/sec"""
        velocity = self.fdmexec.GetPropagate().GetUVW(1)

        return convert_jsbsim_velocity(velocity)

    @property
    def v(self):
        """Returns the v item of the body frame velocity vector in
        meters/sec"""
        velocity = self.fdmexec.GetPropagate().GetUVW(2)

        return convert_jsbsim_velocity(velocity)

    @property
    def w(self):
        """Returns the w item of the body frame velocity vector in
        meters/sec"""
        velocity = self.fdmexec.GetPropagate().GetUVW(3)

        return convert_jsbsim_velocity(velocity)

    @property
    def calibrated_airspeed(self):
        """Returns the calibrated airspeed in meters/sec"""
        airspeed = self.fdmexec.GetAuxiliary().GetVcalibratedFPS()

        return convert_jsbsim_velocity(airspeed)

    @property
    def equivalent_airspeed(self):
        """Returns the equivalent airspeed in meters/sec"""
        airspeed = self.fdmexec.GetAuxiliary().GetVequivalentFPS()

        return convert_jsbsim_velocity(airspeed)

    @property
    def ground_speed(self):
        """Returns the ground speed in meters/sec"""
        airspeed = self.fdmexec.GetAuxiliary().GetVground()

        return convert_jsbsim_velocity(airspeed)
huginn/fdm.py
class FDMBuilder(object):
    """The FDMBuilder creates the flight dynamics model object that will be
    used by the simulator"""
    def __init__(self, data_path):
        self.data_path = data_path
        self.dt = configuration.DT

        self.aircraft = configuration.AIRCRAFT

        self.latitude = configuration.LATITUDE
        self.longitude = configuration.LONGITUDE
        self.altitude = configuration.ALTITUDE
        self.airspeed = configuration.AIRSPEED
        self.heading = configuration.HEADING

    def create_fdm(self):
        """Create the flight dynamics model"""

        fdmexec = FGFDMExec()

        logger.debug("Using jsbsim data at %s", self.data_path)

        aircraft_data_path = self.data_path
        engine_data_path = path.join(self.data_path, "Engines")
        systems_data_path = path.join(self.data_path, "Systems")

        logger.debug("Using aircraft data at %s", aircraft_data_path)
        logger.debug("Using engine data at %s", engine_data_path)
        logger.debug("Using systems data at %s", systems_data_path)

        fdmexec.SetRootDir(self.data_path)
        fdmexec.SetAircraftPath("")
        fdmexec.SetEnginePath(engine_data_path)
        fdmexec.SetSystemsPath(systems_data_path)

        logger.debug("JSBSim dt is %f", self.dt)
        fdmexec.Setdt(self.dt)

        logger.debug("Loading '%s' aircraft model", self.aircraft)
        fdmexec.LoadModel(self.aircraft)

        altitude = self.altitude * ur.meter
        altitude.ito(ur.foot)

        altitude_in_feet = altitude.magnitude

        airspeed = self.airspeed * ur.meters_per_second
        airspeed.ito(ur.knot)

        airspeed_in_knots = airspeed.magnitude

        logger.debug("Initial latitude: %f degrees", self.latitude)
        logger.debug("Initial longitude: %f degrees", self.longitude)
        logger.debug("Initial altitude: %f meters", self.altitude)
        logger.debug("Initial airspeed: %f meters/second", self.airspeed)
        logger.debug("Initial heading: %f degrees", self.heading)

        fdmexec.GetIC().SetLatitudeDegIC(self.latitude)
        fdmexec.GetIC().SetLongitudeDegIC(self.longitude)
        fdmexec.GetIC().SetAltitudeASLFtIC(altitude_in_feet)
        fdmexec.GetIC().SetPsiDegIC(self.heading)
        fdmexec.GetIC().SetVtrueKtsIC(airspeed_in_knots)

        if not fdmexec.RunIC():
            logger.error("Failed to run initial condition")
            return None

        return fdmexec
huginn/instruments.py
def true_airspeed(total_pressure, static_pressure, temperature):
    """Calculate the true airspeed

    Arguments:
    total_pressure: the total pressure in Pascal
    static_pressure: the static pressure in Pascal
    temperature: the temperature in kelvin

    returns the airspeed in knots
    """
    impact_pressure = total_pressure - static_pressure

    t_t0 = temperature / T0
    q_p = impact_pressure / static_pressure

    return a0 * sqrt(5.0 * (math.pow(q_p + 1.0, 2.0/7.0) - 1.0) * t_t0)


Question: import math
from unittest import TestCase
from huginn import configuration
from huginn.fdm import FDMBuilder, Atmosphere, Orientation, Velocities
from huginn.instruments import (Instruments, GPS, true_airspeed,
                                AirspeedIndicator, pressure_altitude,
                                Altimeter, AttitudeIndicator, HeadingIndicator,
                                VerticalSpeedIndicator)
from huginn.unit_conversions import convert_jsbsim_velocity, convert_jsbsim_pressure, ur
from huginn.constants import p0




class GPSTests(TestCase):
    def test_gps(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdm_builder.aircraft = "Rascal"
        fdmexec = fdm_builder.create_fdm()

        gps = GPS(fdmexec)

        self.assertAlmostEqual(gps.latitude, fdmexec.GetPropagate().GetLatitudeDeg())
        self.assertAlmostEqual(gps.longitude, fdmexec.GetPropagate().GetLongitudeDeg())
        self.assertAlmostEqual(gps.altitude, fdmexec.GetPropagate().GetAltitudeASLmeters())

Answer:         self.assertAlmostEqual(gps.airspeed, convert_jsbsim_velocity(fdmexec.GetAuxiliary().GetVtrueFPS()))