huginn/rest.py
class ObjectResource(Resource):
    """The ObjectResource is using an object and a marshmallow schema to return
    the representation of an object"""

    def __init__(self, obj, schema):
        """Create a new ObjectResource object

        Arguments:
        obj: the object to encode
        schema: the marshmallow schema object
        """
        self.obj = obj
        self.schema = schema

    def get(self):
        """return the object data when a GET request is executed"""
        result = self.schema.dump(self.obj)

        return result.data
huginn/fdm.py
class Position(object):
    """The Position class contains data about the position of the aircraft"""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec

    @property
    def latitude(self):
        """Returns the latitude in degrees"""
        return self.fdmexec.GetPropagate().GetLatitudeDeg()

    @property
    def longitude(self):
        """Returns the longitude in degrees"""
        return self.fdmexec.GetPropagate().GetLongitudeDeg()

    @property
    def altitude(self):
        """Returns the altitude in meters"""
        return self.fdmexec.GetPropagate().GetAltitudeASLmeters()

    @property
    def heading(self):
        """Returns the heading in degrees"""
        return degrees(self.fdmexec.GetPropagate().GetEuler(3))
huginn/rest.py
class ForcesResource(ObjectResource):
    """The ForcesResource object contains the forces that act on the
    aircraft"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.forces = Forces(fdmexec)
        self.forces_schema = ForcesSchema()

        super(ForcesResource, self).__init__(self.forces,
                                             self.forces_schema)
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/rest.py
class AtmosphereResource(ObjectResource):
    """The AtmosphereResource object return the fdm atmospheric data"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.atmosphere = Atmosphere(fdmexec)
        self.atmosphere_schema = AtmosphereShema()

        super(AtmosphereResource, self).__init__(self.atmosphere,
                                                 self.atmosphere_schema)
huginn/rest.py
class VerticalSpeedIndicatorResource(ObjectResource):
    """The VerticalSpeedIndicatorResource object returns the aircraft's heading
    indicator data"""

    def __init__(self, vertical_speed_indicator):
        """Create a new VerticalSpeedIndicatorResource object

        Arguments:
        vertical_speed_indicator: a VerticalSpeedIndicator object
        """
        self.vertical_speed_indicator = vertical_speed_indicator
        self.vertical_speed_indicator_schema = VerticalSpeedIndicatorSchema()

        super(VerticalSpeedIndicatorResource, self).__init__(
            self.vertical_speed_indicator,
            self.vertical_speed_indicator_schema
        )
huginn/rest.py
class PressureSensorResource(Resource):
    """The PressureSensorResource class returns the pressure sensor
    measurements"""

    def __init__(self, pressure_sensor):
        """Create a new PressureSensorResource object

        Arguments:
        pressure_sensor: a PressureSensor object
        """
        self.pressure_sensor = pressure_sensor

    def get(self):
        """Returns the pressure sensor data"""
        pressure_sensor_data = {
            "static_pressure": self.pressure_sensor.pressure,
        }

        return pressure_sensor_data
huginn/rest.py
class OrientationResource(ObjectResource):
    """The OrientationResource object returns the orientations of the
    aircraft"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.orientation = Orientation(fdmexec)
        self.orientation_schema = OrientationSchema()

        super(OrientationResource, self).__init__(self.orientation,
                                                  self.orientation_schema)
huginn/unit_conversions.py
def convert_jsbsim_velocity(velocity):
    """Convert the velocity from the units used in JSBSim to meters/sec"""
    velocity = velocity * ur.feet_per_second

    velocity.ito(ur.meters_per_second)

    return velocity.magnitude
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/rest.py
class VelocitiesResource(ObjectResource):
    """The VelocitiesResource object returns the fdm velocities"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.velocities = Velocities(self.fdmexec)
        self.velocities_schema = VelocitiesSchema()

        super(VelocitiesResource, self).__init__(self.velocities,
                                                 self.velocities_schema)
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/fdm.py
class Accelerations(object):
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec

    @property
    def x(self):
        """Returns the acceleration along the x axis of the aircraft in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAuxiliary().GetPilotAccel(1)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def y(self):
        """Returns the acceleration along the y axis of the aircraft in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAuxiliary().GetPilotAccel(2)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def z(self):
        """Returns the acceleration along the z axis of the aircraft in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAuxiliary().GetPilotAccel(3)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def p_dot(self):
        """Returns the p value of the body axis angular acceleration in
        degress/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetPQRdot(1)

        return convert_jsbsim_angular_acceleration(acceleration)

    @property
    def q_dot(self):
        """Returns the q value of the body axis angular acceleration in
        degress/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetPQRdot(2)

        return convert_jsbsim_angular_acceleration(acceleration)

    @property
    def r_dot(self):
        """Returns the r value of the body axis angular acceleration in
        degress/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetPQRdot(3)

        return convert_jsbsim_angular_acceleration(acceleration)

    @property
    def u_dot(self):
        """Returns the u item of the the body axis acceleration in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetUVWdot(1)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def v_dot(self):
        """Returns the v item of the the body axis acceleration in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetUVWdot(2)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def w_dot(self):
        """Returns the w item of the the body axis acceleration in
        meters/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetUVWdot(3)

        return convert_jsbsim_acceleration(acceleration)

    @property
    def gravity(self):
        """Returns the acceleration of the gravity in meters/sec^2"""
        acceleration = self.fdmexec.GetAccelerations().GetGravAccelMagnitude()

        return convert_jsbsim_acceleration(acceleration)
huginn/rest.py
class WaypointResource(Resource):
    """The WaypointResource is used to ad, delete and update waypoints"""
    def __init__(self, db):
        self.db = db

    @marshal_with(request_models.waypoint)
    def get(self, name):
        """Get the waypoint's data"""
        Document = Query()

        waypoints = self.db.search(Document.type == "waypoints")[0]

        waypoint = waypoints["waypoints"].get(name)

        if waypoint is not None:
            return waypoint
        else:
            abort(404, error="waypoint doesn't exist", name=name)

    @marshal_with(request_models.waypoint)
    def post(self, name):
        """Add a new waypoint or update an existing waypoint's data"""
        waypoint = request_parsers.waypoint.parse_args()
        waypoint["name"] = name

        Document = Query()

        waypoints = self.db.search(Document.type == "waypoints")[0]

        waypoints["waypoints"][name] = waypoint

        self.db.update(waypoints, Document.type == "waypoints")

        return waypoint

    @marshal_with(request_models.waypoint)
    def delete(self, name):
        """Delete a waypoint"""
        Document = Query()

        waypoints = self.db.search(Document.type == "waypoints")[0]

        waypoint = waypoints["waypoints"].get(name)

        if waypoint is not None:
            del waypoints["waypoints"][name]
            return waypoint
        else:
            abort(404, error="waypoint doesn't exist", name=name)
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/rest.py
class FlightControlsResource(Resource):
    """The FlightControlsResource return the flight controls values"""

    def __init__(self, controls):
        """Create a new FlightControlsResource object

        Arguments:
        controls: A Controls object
        """
        self.controls = controls

    def get(self):
        """returns the flight controls values"""
        flight_controls_data = {
            "aileron": self.controls.aileron,
            "elevator": self.controls.elevator,
            "rudder": self.controls.rudder,
            "throttle": self.controls.throttle,
        }

        return flight_controls_data
huginn/rest.py
class EngineResource(Resource):
    """The EngineResource class returns the engine data"""
    def __init__(self, engine):
        """Create a new EngineResource object

        Arguments:
        engine: An Engine object
        """
        self.engine = engine

    def get(self):
        """Returns the engine data"""
        engine_data = {
            "thrust": self.engine.thrust,
            "throttle": self.engine.throttle,
        }

        return engine_data
huginn/rest.py
class PositionResource(ObjectResource):
    """The PositionResource object contain the aircraft position data"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.position = Position(fdmexec)
        self.position_schema = PositionSchema()

        super(PositionResource, self).__init__(
            self.position,
            self.position_schema
        )
huginn/fdm.py
class Forces(object):
    """The Forces objects contains the aerodynamics forces"""

    def __init__(self, fdmexec):
        self.fdmexec = fdmexec

    @property
    def x_body(self):
        """Return the force along the x axis in the body frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetForces(1)

        return convert_jsbsim_force(force)

    @property
    def y_body(self):
        """Return the force along the y axis in the body frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetForces(2)

        return convert_jsbsim_force(force)

    @property
    def z_body(self):
        """Return the force along the z axis in the body frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetForces(3)

        return convert_jsbsim_force(force)

    @property
    def x_wind(self):
        """Return the force along the x axis in the wind frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetvFw(1)

        return convert_jsbsim_force(force)

    @property
    def y_wind(self):
        """Return the force along the y axis in the wind frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetvFw(2)

        return convert_jsbsim_force(force)

    @property
    def z_wind(self):
        """Return the force along the z axis in the wind frame. The value
        is in Newtons"""
        force = self.fdmexec.GetAerodynamics().GetvFw(3)

        return convert_jsbsim_force(force)

    @property
    def x_total(self):
        """Return the total force along the x axis in the body frame. The
        value is in Newtons"""
        force = self.fdmexec.GetAccelerations().GetForces(1)

        return convert_jsbsim_force(force)

    @property
    def y_total(self):
        """Return the total force along the y axis in the body frame. The
        value is in Newtons"""
        force = self.fdmexec.GetAccelerations().GetForces(2)

        return convert_jsbsim_force(force)

    @property
    def z_total(self):
        """Return the total force along the z axis in the body frame. The
        value is in Newtons"""
        force = self.fdmexec.GetAccelerations().GetForces(3)

        return convert_jsbsim_force(force)
huginn/rest.py
class PitotTubeResource(Resource):
    """The PitotTubeResource returns the measurements from the pitot tube"""

    def __init__(self, pitot_tube):
        """Create a new PitotTubeResource object

        Arguments:
        pitot_tube: A PitotTube object
        """
        self.pitot_tube = pitot_tube

    def get(self):
        """Returns the pitot tube measurements"""
        pitot_tube_data = {
            "total_pressure": self.pitot_tube.pressure,
        }

        return pitot_tube_data
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/rest.py
class FDMResource(Resource):
    """The FDMResource will return the data from the flight dynamics model"""

    def __init__(self, fdm, aircraft):
        """Create a new FDMResource object

        Arguments:
        fdm: an FDM object
        aircraft: an aircraft object
        """
        self.fdm = fdm
        self.aircraft = aircraft

    def get(self):
        """Get the fdm data"""
        sensors = self.aircraft.sensors

        flight_data = {
            "time": self.fdm.fdmexec.GetSimTime(),
            "dt": self.fdm.fdmexec.GetDeltaT(),
            "latitude": self.fdm.position.latitude,
            "longitude": self.fdm.position.longitude,
            "altitude": self.fdm.position.altitude,
            "airspeed": self.fdm.velocities.true_airspeed,
            "heading": self.fdm.orientation.psi,
            "x_acceleration": self.fdm.accelerations.x,
            "y_acceleration": self.fdm.accelerations.y,
            "z_acceleration": self.fdm.accelerations.z,
            "roll_rate": self.fdm.velocities.p,
            "pitch_rate": self.fdm.velocities.q,
            "yaw_rate": self.fdm.velocities.r,
            "temperature": self.fdm.atmosphere.temperature,
            "static_pressure": self.fdm.atmosphere.pressure,
            "total_pressure": sensors.pitot_tube.true_pressure,
            "roll": self.fdm.orientation.phi,
            "pitch": self.fdm.orientation.theta,
            "climb_rate": self.fdm.velocities.climb_rate
        }

        return flight_data
huginn/simulator.py
class Simulator(object):
    """The Simulator class is used to perform the simulation of an aircraft"""

    def __init__(self, fdmexec):
        """Constructor for the Simulator object

        Arguments:
        fdmexec: A flight dynamics model
        """
        self.aircraft = Aircraft(fdmexec)
        self.fdmexec = fdmexec
        self.fdm = FDM(fdmexec)
        self.trim_mode = TRIM_MODE_FULL
        self._crashed = False
        self.start_paused = False

    @property
    def crashed(self):
        """Returns True if the aircraft has crashed"""
        return self._crashed

    @property
    def dt(self):
        """The simulation time step"""
        return self.fdmexec.GetDeltaT()

    @property
    def simulation_time(self):
        """The current simulation time"""
        return self.fdmexec.GetSimTime()

    def pause(self):
        """Pause the simulator"""
        self.fdmexec.Hold()

    def resume(self):
        """Resume the simulation"""
        if self.crashed:
            logger.debug("Not resuming simulation because the aircraft has "
                         "crashed")
            return

        if self.fdmexec.IntegrationSuspended():
            self.fdmexec.ResumeIntegration()

        self.fdmexec.Resume()

    def is_paused(self):
        """Check if the simulator is paused"""
        return self.fdmexec.Holding()

    def reset(self):
        """Reset the simulation"""
        logger.debug("Reseting the aircraft")
        self._crashed = False

        self.pause()

        self.aircraft.controls.aileron = 0.0
        self.aircraft.controls.elevator = 0.0
        self.aircraft.controls.rudder = 0.0
        self.aircraft.controls.throttle = 0.0

        self.fdmexec.ResetToInitialConditions(0)

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

        logger.debug("starting the aircraft's engines")
        self.aircraft.start_engines()

        trim_result = self.aircraft.trim(self.trim_mode)
        if not trim_result:
            logger.warning("Failed to trim the aircraft")

            # reset the controls because the trim operation might have messed
            # them up
            self.aircraft.controls.aileron = 0.0
            self.aircraft.controls.elevator = 0.0
            self.aircraft.controls.rudder = 0.0
            self.aircraft.controls.throttle = 0.0

        if not self.step():
            logger.error("Failed to execute initial run")
            return False

        logger.debug("Engine thrust after simulation reset %f",
                     self.aircraft.engine.thrust)

        if not self.start_paused:
            self.resume()

        return True

    def step(self):
        """Run the simulation one time"""
        if not self.crashed and self.fdm.position.altitude < 0.0:
            logger.debug("Aircraft has crashed. Pausing simulator")
            self.pause()
            self._crashed = True
            return True

        if self.crashed:
            logger.debug("Not executing simulation step because aircraft has "
                         "crashed")
            return True

        was_paused = self.is_paused()

        if was_paused:
            self.resume()

        try:
            self.fdmexec.ProcessMessage()
            self.fdmexec.CheckIncrementalHold()
            run_result = self.fdmexec.Run()
        except:
            raise SimulationError()

        if run_result:
            if was_paused:
                self.pause()

            return True
        else:
            if was_paused:
                self.pause()

            logger.error("The simulator has failed to run")
            return False

    def run_for(self, time_to_run):
        """Run the simulation for the given time in seconds

        Arguments:
        time_to_run: the time in seconds that the simulator will run
        """
        if time_to_run < 0.0:
            logger.error("Invalid simulator run time length %f",
                         time_to_run)

            return False

        start_time = self.fdmexec.GetSimTime()
        end_time = start_time + time_to_run

        while self.fdmexec.GetSimTime() <= end_time:
            result = self.step()

            if not result:
                return False

        return True

    def run(self):
        """Run the simulation"""
        if not self.fdmexec.Holding():
            result = self.step()

            return result

        return True

    def set_aircraft_controls(self, aileron, elevator, rudder, throttle):
        """Update the aircraft controls"""
        self.aircraft.controls.aileron = aileron
        self.aircraft.controls.elevator = elevator
        self.aircraft.controls.rudder = rudder
        self.aircraft.controls.throttle = throttle

    def print_simulator_state(self):
        """Show the current state of the simulation"""
        print("Simulation state")
        print("================")
        print("Time: %f seconds" % self.simulation_time)
        print("DT: %f seconds" % self.dt)
        print("Running: %s" % (not self.is_paused()))
        print("")
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/schemas.py
class AccelerationsSchema(Schema):
    x = fields.Float()
    y = fields.Float()
    z = fields.Float()
    p_dot = fields.Float()
    q_dot = fields.Float()
    r_dot = fields.Float()
    u_dot = fields.Float()
    v_dot = fields.Float()
    w_dot = fields.Float()
    gravity = fields.Float()
huginn/rest.py
class AirspeedIndicatorResource(ObjectResource):
    """The AirspeedIndicatorResource object returns the aircraft's true
    airspeed"""

    def __init__(self, airspeed_indicator):
        """Create a new AirspeedindicatorResource object

        Arguments:
        airspeed_indicator: an AirspeedIndicator object
        """
        self.airspeed_indicator = airspeed_indicator
        self.airspeed_indicator_schema = AirspeedIndicatorSchema()

        super(AirspeedIndicatorResource, self).__init__(
            self.airspeed_indicator,
            self.airspeed_indicator_schema
        )
huginn/rest.py
class AltimeterResource(ObjectResource):
    """The AltimeterResource object returns the aircraft's altimeter data"""

    def __init__(self, altimeter):
        """Create a new AltimeterResource object

        Arguments:
        altimeter: an Altimeter object
        """
        self.altimeter = altimeter
        self.altimeter_schema = AltimeterSchema()

        super(AltimeterResource, self).__init__(
            self.altimeter,
            self.altimeter_schema
        )
huginn/rest.py
class AccelerationsResource(ObjectResource):
    """The AccelerationsResource object returns the fdm accelerations"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.accelerations = Accelerations(self.fdmexec)
        self.acceleration_schema = AccelerationsSchema()

        super(AccelerationsResource, self).__init__(self.accelerations,
                                                    self.acceleration_schema)
huginn/rest.py
class HeadingIndicatorResource(ObjectResource):
    """The HeadingIndicatorResource object returns the aircraft's heading
    indicator data"""

    def __init__(self, heading_indicator):
        """Create a new HeadingIndicatorResource object

        Arguments:
        heading_indicator: a HeadingIndicator object
        """
        self.heading_indicator = heading_indicator
        self.heading_indicator_schema = HeadingIndicatorSchema()

        super(HeadingIndicatorResource, self).__init__(
            self.heading_indicator,
            self.heading_indicator_schema
        )
huginn/fdm.py
class InitialCondition(object):
    """The InitialCondition class gets/sets the simulator initial conditions"""

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

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

    @property
    def latitude(self):
        """Get the starting position latitude in degrees"""
        return self.fdmexec.GetIC().GetLatitudeDegIC()

    @latitude.setter
    def latitude(self, value):
        """Set the starting position latitude

        Arguments:
        value: the latitude in degrees
        """
        self.fdmexec.GetIC().SetLatitudeDegIC(value)

    @property
    def longitude(self):
        """Get the starting position longitude in degrees"""
        return self.fdmexec.GetIC().GetLongitudeDegIC()

    @longitude.setter
    def longitude(self, value):
        """Set the starting position longitude

        Arguments:
        value: the longitude in degrees
        """

        self.fdmexec.GetIC().SetLongitudeDegIC(value)

    @property
    def altitude(self):
        """Get the altitude in meters"""
        altitude = self.fdmexec.GetIC().GetAltitudeASLFtIC() * ur.foot

        altitude.ito(ur.meter)

        return altitude.magnitude

    @altitude.setter
    def altitude(self, value):
        """Set the starting altitude

        Arguments:
        value: the altitude in meters
        """
        altitude = value * ur.meter

        altitude.ito(ur.foot)

        self.fdmexec.GetIC().SetAltitudeASLFtIC(altitude.magnitude)

    @property
    def heading(self):
        """Get the heading in degrees"""
        return self.fdmexec.GetIC().GetPsiDegIC()

    @heading.setter
    def heading(self, value):
        """Set the heading

        Arguments:
        value: the heading in degrees
        """
        self.fdmexec.GetIC().SetPsiDegIC(value)

    @property
    def airspeed(self):
        """Get the airspeed in meters/second"""
        airspeed = self.fdmexec.GetIC().GetVtrueKtsIC() * ur.knot

        airspeed.ito(ur.meters_per_second)

        return airspeed.magnitude

    @airspeed.setter
    def airspeed(self, value):
        """Set the airspeed

        Arguments:
        value: the airspeed in meters/second
        """
        airspeed = value * ur.meters_per_second

        airspeed.ito(ur.knot)

        self.fdmexec.GetIC().SetVtrueKtsIC(airspeed.magnitude)
huginn/rest.py
class AttitudeIndicatorResource(ObjectResource):
    """The AttitudeIndicatorResource object returns the aircraft's attitude
    indicator data"""

    def __init__(self, attitude_indicator):
        """Create a new AttitudeIndicatorResource object

        Arguments:
        attitude_indicator: an AttitudeIndicator object
        """
        self.attitude_indicator = attitude_indicator
        self.attitude_indicator_schema = AttitudeIndicatorSchema()

        super(AttitudeIndicatorResource, self).__init__(
            self.attitude_indicator,
            self.attitude_indicator_schema
        )
huginn/fdm.py
class FDM(object):
    """The FDM object is a wrapper around the JSBSim objects that contains the
    values of the flight dynamics model."""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec
        self.accelerations = Accelerations(fdmexec)
        self.velocities = Velocities(fdmexec)
        self.position = Position(fdmexec)
        self.orientation = Orientation(fdmexec)
        self.atmosphere = Atmosphere(fdmexec)
        self.forces = Forces(fdmexec)
        self.initial_condition = InitialCondition(fdmexec)
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/rest.py
class SimulatorControlResource(Resource):
    """The SimulatorControlResource provides the endpoint that is used to
    control the simulator"""

    def __init__(self, simulator):
        """Create a new SimulatorControlResource object

        Arguments:
        simulator: a Simulator object
        """
        self.simulator = simulator

    def get(self):
        """Returns the simulator state"""
        simulator_state = {
            "time": self.simulator.simulation_time,
            "dt": self.simulator.dt,
            "running": not self.simulator.is_paused(),
            "crashed": self.simulator.crashed
        }

        return simulator_state

    def execute_command(self, command, params=None):
        """Execute the simulator command

        Arguments:
        command: the command to execute
        params: the parameters of the command
        """
        if command == "reset":
            self.simulator.reset()

            response = {"result": "ok",
                        "command": command}
        elif command == "pause":
            self.simulator.pause()

            response = {"result": "ok",
                        "command": command}
        elif command == "resume":
            self.simulator.resume()

            response = {"result": "ok",
                        "command": command}
        elif command == "step":
            self.simulator.step()

            response = {"result": "ok",
                        "command": command}
        elif command == "run_for":
            if params:
                time_to_run = params["time_to_run"]
            else:
                time_to_run = None

            if not time_to_run:
                response = {"error": "no time to run provided",
                            "command": command}
            else:
                self.simulator.run_for(time_to_run)

                response = {"result": "ok",
                            "command": "run_for"}
        elif command == "start_paused":
            self.simulator.start_paused = True

            response = {"result": "ok",
                        "command": "start_paused"}
        elif command == "start_running":
            self.simulator.start_paused = False

            response = {"result": "ok",
                        "command": "start_running"}
        else:
            response = {"error": "unknown command",
                        "command": command}

        return response

    def post(self):
        """Execute a command on the simulator"""
        parser = reqparse.RequestParser()

        parser.add_argument("command", type=str, required=True)
        parser.add_argument("time_to_run", type=float)
        parser.add_argument("paused", type=bool)

        args = parser.parse_args()

        command = args.command

        params = {}

        if args.time_to_run:
            params["time_to_run"] = args.time_to_run

        if args.paused:
            params["paused"] = args.paused

        return self.execute_command(command, params)
huginn/aircraft.py
class Aircraft(object):
    """The Aircraft class is a wrapper around jsbsim that contains data about
    the aircraft state."""
    def __init__(self, fdmexec):
        self.fdmexec = fdmexec
        self.type = "Rascal"
        self.sensors = Sensors(fdmexec)
        self.instruments = Instruments(fdmexec)
        self.engine = Engine(fdmexec)
        self.controls = Controls(fdmexec)

    def start_engines(self):
        """Start the aircraft engines"""
        for i in range(self.fdmexec.GetPropulsion().GetNumEngines()):
            logger.debug("Starting engine %d", i)
            self.fdmexec.GetPropulsion().GetEngine(i).SetRunning(1)

    def trim(self, mode=TRIM_MODE_FULL):
        """Trim the aircraft"""
        logger.debug("Executing trim with mode = %d", mode)

        try:
            self.fdmexec.DoTrim(mode)
        except:
            logger.warning("Failed to trim the aircraft in mode %d", mode)
            return False

        return True

    def print_aircraft_state(self):
        """Print the aircraft state"""
        print("Aircraft state")
        print("")
        print("Position")
        print("========")
        print("Latitude: %f degrees" % self.instruments.gps.latitude)
        print("Longitude: %f degrees" % self.instruments.gps.longitude)
        print("Altitude: %f meters" % self.instruments.gps.altitude)
        print("Airspeed: %f meters/second" % self.instruments.gps.airspeed)
        print("Heading: %f degrees" % self.instruments.gps.heading)
        print("")
        print("Orientation")
        print("===========")

        roll = self.sensors.inertial_navigation_system.roll
        print("Roll: %f degrees" % roll)

        pitch = self.sensors.inertial_navigation_system.pitch
        print("Pitch: %f degrees" % pitch)
        print("")
        print("Engines")
        print("=======")
        print("Thrust: %f Newtons" % self.engine.thrust)
        print("")
        print("Controls")
        print("========")
        print("Aileron: %f" % self.controls.aileron)
        print("Elevator: %f" % self.controls.elevator)
        print("Rudder: %f" % self.controls.rudder)
        print("Throttle: %f" % self.controls.throttle)
        print("")
huginn/rest.py
class AircraftResource(Resource):
    """The AircraftResource returns infomration about the aircraft"""

    def __init__(self, aircraft):
        """Create a new AircraftResource object

        Arguments:
        aircraft: an Aircraft object
        """
        self.aircraft = aircraft

    def get(self):
        """Get the aircraft information"""
        return {"type": self.aircraft.type}
huginn/rest.py
class WaypointsResource(Resource):
    """The WaypointsResource is used to retrieve all the available waypoints"""
    def __init__(self, db):
        self.db = db

    @marshal_with(request_models.waypoint)
    def get(self):
        """Get a list of the available waypoints"""
        Document = Query()

        waypoints = self.db.search(Document.type == "waypoints")[0]

        return waypoints["waypoints"].values()
huginn/rest.py
class GPSResource(Resource):
    """The GPSResource returns the gps data"""

    def __init__(self, gps):
        """Create a new GPSResource object

        Arguments:
        gps: A GPS object
        """
        self.gps = gps

    def get(self):
        """Return the gps data"""
        gps_data = {
            "latitude": self.gps.latitude,
            "longitude": self.gps.longitude,
            "altitude": self.gps.altitude,
            "airspeed": self.gps.airspeed,
            "heading": self.gps.heading
        }

        return gps_data
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/rest.py
class AccelerometerResource(Resource):
    """The AccelerometerResource returns the accelerometer measurements"""

    def __init__(self, accelerometer):
        """Create a new AccelerometerResource object

        Arguments:
        accelerometer: an Accelerometer object
        """
        self.accelerometer = accelerometer

    def get(self):
        """Returns the accelerometer measurements"""
        accelerometer_data = {
            "x": self.accelerometer.x,
            "y": self.accelerometer.y,
            "z": self.accelerometer.z,
        }

        return accelerometer_data
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/rest.py
class GyroscopeResource(Resource):
    """The GyroscopeResource returns the data from the gyroscope sensor"""

    def __init__(self, gyroscope):
        """Create a new GyroscopeResource object

        Arguments:
        gyroscope: A Gyroscope object
        """
        self.gyroscope = gyroscope

    def get(self):
        """Returns the gyroscope measurements"""
        gyroscope_data = {
            "roll_rate": self.gyroscope.roll_rate,
            "pitch_rate": self.gyroscope.pitch_rate,
            "yaw_rate": self.gyroscope.yaw_rate
        }

        return gyroscope_data
huginn/rest.py
class ThermometerResource(Resource):
    """The ThermometerResource class contains the measurements from the
    temperature sensor"""

    def __init__(self, thermometer):
        """Create a new ThermometerResource object

        Arguments:
        thermometer: a Thermometer object
        """
        self.thermometer = thermometer

    def get(self):
        """Returns the thermometer measurements"""
        thermometer_data = {
            "temperature": self.thermometer.temperature,
        }

        return thermometer_data
huginn/rest.py
class InertialNavigationSystemResource(Resource):
    """The InertialNavigationSystemResource returns the measurements from the
    inertial navigation system"""

    def __init__(self, inertial_navigation_system):
        """Create a new InertialNavigationSystemResource object

        Arguments:
        inertial_navigation_system: An InertialNavigationSystem object
        """
        self.inertial_navigation_system = inertial_navigation_system

    def get(self):
        """Returns the ins measurements"""
        inertial_navigation_system_data = {
            "latitude": self.inertial_navigation_system.latitude,
            "longitude": self.inertial_navigation_system.longitude,
            "altitude": self.inertial_navigation_system.altitude,
            "airspeed": self.inertial_navigation_system.airspeed,
            "heading": self.inertial_navigation_system.heading,
            "roll": self.inertial_navigation_system.roll,
            "pitch": self.inertial_navigation_system.pitch,
        }

        return inertial_navigation_system_data
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/rest.py
class InitialConditionResource(ObjectResource):
    """The InitialConditionResource object contain the simulator initial
    conditions"""

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

        Arguments:
        fdmexec: a jsbsim FGFDMExec object
        """
        self.fdmexec = fdmexec
        self.initial_condition = InitialCondition(fdmexec)
        self.initial_condition_schema = InitialConditionSchema()

        super(InitialConditionResource, self).__init__(
            self.initial_condition,
            self.initial_condition_schema
        )

    def update_initial_conditions(self, initial_condition):
        """Update the simulator initial conditions

        Arguments:
        initial_conditions: a dictionary with the initial conditions of the
            simulator
        """
        if "latitude" in initial_condition:
            self.initial_condition.latitude = initial_condition["latitude"]

        if "longitude" in initial_condition:
            self.initial_condition.longitude = initial_condition["longitude"]

        if "airspeed" in initial_condition:
            self.initial_condition.airspeed = initial_condition["airspeed"]

        if "altitude" in initial_condition:
            self.initial_condition.altitude = initial_condition["altitude"]

        if "heading" in initial_condition:
            self.initial_condition.heading = initial_condition["heading"]

    def post(self):
        """Update the simulator condition when a POST request is executed"""
        data = request.json

        ic_schema = InitialConditionSchema()

        initial_condition = ic_schema.load(data)

        self.update_initial_conditions(initial_condition.data)

        return {"result": "ok"}


Question: from unittest import TestCase, main
from tinydb import TinyDB, Query
from tinydb.storages import MemoryStorage
from huginn.rest import (FDMResource, AccelerometerResource, GyroscopeResource,
                         GPSResource, ThermometerResource, PitotTubeResource,
                         PressureSensorResource, EngineResource,
                         InertialNavigationSystemResource, AircraftResource,
                         FlightControlsResource, SimulatorControlResource,
                         ObjectResource, AccelerationsResource,
                         VelocitiesResource, OrientationResource,
                         AtmosphereResource, ForcesResource,
                         InitialConditionResource, PositionResource,
                         AirspeedIndicatorResource, AltimeterResource,
                         AttitudeIndicatorResource, HeadingIndicatorResource,
                         VerticalSpeedIndicatorResource, WaypointResource,
                         WaypointsResource)
from huginn import configuration
from huginn.fdm import (FDMBuilder, Accelerations, Velocities, Orientation,
                        Atmosphere, Forces, InitialCondition, Position)
from huginn.fdm import FDM
from huginn.aircraft import Aircraft
from huginn.simulator import Simulator
from huginn.schemas import AccelerationsSchema
from huginn.unit_conversions import convert_jsbsim_velocity
from huginn.instruments import (AirspeedIndicator, Altimeter,
                                AttitudeIndicator, HeadingIndicator,
                                VerticalSpeedIndicator)








class FDMResourceTests(TestCase):
    def test_get_fdm_data(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdmexec = fdm_builder.create_fdm()

        aircraft = Aircraft(fdmexec)
        fdm = FDM(fdmexec)
        
        resource = FDMResource(fdm, aircraft)

        fdm_data = resource.get()

        self.assertAlmostEqual(fdm.fdmexec.GetSimTime(), fdm_data["time"], 3)
        self.assertAlmostEqual(fdm.fdmexec.GetDeltaT(), fdm_data["dt"], 3)
        self.assertAlmostEqual(fdm.position.latitude, fdm_data["latitude"], 3)
        self.assertAlmostEqual(fdm.position.longitude, fdm_data["longitude"], 3)
        self.assertAlmostEqual(fdm.position.altitude, fdm_data["altitude"], 3)
        self.assertAlmostEqual(fdm.velocities.true_airspeed, fdm_data["airspeed"], 3)
        self.assertAlmostEqual(fdm.position.heading, fdm_data["heading"], 3)
        self.assertAlmostEqual(fdm.accelerations.x, fdm_data["x_acceleration"], 3)
        self.assertAlmostEqual(fdm.accelerations.y, fdm_data["y_acceleration"], 3)
        self.assertAlmostEqual(fdm.accelerations.z, fdm_data["z_acceleration"], 3)
        self.assertAlmostEqual(fdm.velocities.p, fdm_data["roll_rate"], 3)
        self.assertAlmostEqual(fdm.velocities.q, fdm_data["pitch_rate"], 3)
        self.assertAlmostEqual(fdm.velocities.r, fdm_data["yaw_rate"], 3)
        self.assertAlmostEqual(fdm.atmosphere.temperature, fdm_data["temperature"], 3)
        self.assertAlmostEqual(fdm.atmosphere.pressure, fdm_data["static_pressure"], 3)
        self.assertAlmostEqual(aircraft.sensors.pitot_tube.true_pressure, fdm_data["total_pressure"], 3)
        self.assertAlmostEqual(fdm.orientation.phi, fdm_data["roll"], 3)
        self.assertAlmostEqual(fdm.orientation.theta, fdm_data["pitch"], 3)

        climb_rate = convert_jsbsim_velocity(-fdmexec.GetPropagate().GetVel(3))
        
        self.assertAlmostEqual(climb_rate, fdm_data["climb_rate"], 3)

class AccelerometerResourceTests(TestCase):
    def test_get_accelerometer_data(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdmexec = fdm_builder.create_fdm()

        aircraft = Aircraft(fdmexec)

        resource = AccelerometerResource(aircraft.sensors.accelerometer)

        accelerometer_data = resource.get()

        self.assertAlmostEqual(aircraft.sensors.accelerometer.x, accelerometer_data["x"], 3)
        self.assertAlmostEqual(aircraft.sensors.accelerometer.y, accelerometer_data["y"], 3)
        self.assertAlmostEqual(aircraft.sensors.accelerometer.z, accelerometer_data["z"], 3)

class GyroscopeResourceTests(TestCase):
    def test_get_gyroscope_data(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdmexec = fdm_builder.create_fdm()

        aircraft = Aircraft(fdmexec)

        resource = GyroscopeResource(aircraft.sensors.gyroscope)

        gyroscope_data = resource.get()

        self.assertAlmostEqual(aircraft.sensors.gyroscope.roll_rate, gyroscope_data["roll_rate"], 3)
        self.assertAlmostEqual(aircraft.sensors.gyroscope.pitch_rate, gyroscope_data["pitch_rate"], 3)
        self.assertAlmostEqual(aircraft.sensors.gyroscope.yaw_rate, gyroscope_data["yaw_rate"], 3)

class GPSResourceTests(TestCase):
    def test_get_gps_data(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdmexec = fdm_builder.create_fdm()

        aircraft = Aircraft(fdmexec)

        resource = GPSResource(aircraft.instruments.gps)

        gps_data = resource.get()

        self.assertAlmostEqual(aircraft.instruments.gps.latitude, gps_data["latitude"], 3)
        self.assertAlmostEqual(aircraft.instruments.gps.longitude, gps_data["longitude"], 3)
        self.assertAlmostEqual(aircraft.instruments.gps.altitude, gps_data["altitude"], 3)
        self.assertAlmostEqual(aircraft.instruments.gps.airspeed, gps_data["airspeed"], 3)
        self.assertAlmostEqual(aircraft.instruments.gps.heading, gps_data["heading"], 3)

class ThermometerResourceTests(TestCase):
    def test_get_thermometer_data(self):
        huginn_data_path = configuration.get_data_path()

        fdm_builder = FDMBuilder(huginn_data_path)
        fdmexec = fdm_builder.create_fdm()

        aircraft = Aircraft(fdmexec)


Answer:         resource = ThermometerResource(aircraft.sensors.thermometer)