1
0
Fork 0
flightgear/src/Instrumentation/mk_viii.hxx
2009-01-10 00:07:01 +01:00

1649 lines
45 KiB
C++
Executable file

// mk_viii.cxx -- Honeywell MK VIII EGPWS emulation
//
// Written by Jean-Yves Lefort, started September 2005.
//
// Copyright (C) 2005, 2006 Jean-Yves Lefort - jylefort@FreeBSD.org
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef __INSTRUMENTS_MK_VIII_HXX
#define __INSTRUMENTS_MK_VIII_HXX
#include <assert.h>
#include <vector>
#include <deque>
#include <map>
#include <simgear/props/props.hxx>
#include <simgear/sound/sample_openal.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
using std::vector;
using std::deque;
using std::map;
#include "Airports/runways.hxx"
#include "Airports/simple.hxx"
#include "Main/globals.hxx"
///////////////////////////////////////////////////////////////////////////////
// MK_VIII ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class MK_VIII : public SGSubsystem
{
// keep in sync with Mode6Handler::altitude_callout_definitions[]
static const int n_altitude_callouts = 11;
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::RawValueMethodsData /////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
template <class C, class VT, class DT>
class RawValueMethodsData : public SGRawValue<VT>
{
public:
typedef VT (C::*getter_t) (DT) const;
typedef void (C::*setter_t) (DT, VT);
RawValueMethodsData (C &obj, DT data, getter_t getter = 0, setter_t setter = 0)
: _obj(obj), _data(data), _getter(getter), _setter(setter) {}
virtual VT getValue () const
{
if (_getter)
return (_obj.*_getter)(_data);
else
return SGRawValue<VT>::DefaultValue;
}
virtual bool setValue (VT value)
{
if (_setter)
{
(_obj.*_setter)(_data, value);
return true;
}
else
return false;
}
virtual SGRawValue<VT> *clone () const
{
return new RawValueMethodsData<C,VT,DT>(_obj, _data, _getter, _setter);
}
private:
C &_obj;
DT _data;
getter_t _getter;
setter_t _setter;
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Parameter ///////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
template <class T>
class Parameter
{
T _value;
public:
bool ncd;
inline Parameter ()
: _value(0), ncd(true) {}
inline T get () const { assert(! ncd); return _value; }
inline T *get_pointer () { return &_value; }
inline void set (T value) { ncd = false; _value = value; }
inline void unset () { ncd = true; }
inline void set (const Parameter<T> *parameter)
{
if (parameter->ncd)
unset();
else
set(parameter->get());
}
inline void set (const Parameter<double> *parameter, double factor)
{
if (parameter->ncd)
unset();
else
set(parameter->get() * factor);
}
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Sample //////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
template <class T>
class Sample
{
public:
double timestamp;
T value;
inline Sample (T _value)
: timestamp(globals->get_sim_time_sec()), value(_value) {}
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Timer ///////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Timer
{
double start_time;
public:
bool running;
inline Timer ()
: running(false) {}
inline void start () { running = true; start_time = globals->get_sim_time_sec(); }
inline void stop () { running = false; }
inline double elapsed () const { assert(running); return globals->get_sim_time_sec() - start_time; }
inline double start_or_elapsed ()
{
if (running)
return elapsed();
else
{
start();
return 0;
}
}
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::PropertiesHandler ///////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class PropertiesHandler
{
MK_VIII *mk;
vector<SGPropertyNode_ptr> tied_properties;
public:
struct
{
SGPropertyNode_ptr ai_caged;
SGPropertyNode_ptr ai_roll;
SGPropertyNode_ptr ai_serviceable;
SGPropertyNode_ptr altimeter_altitude;
SGPropertyNode_ptr altimeter_serviceable;
SGPropertyNode_ptr altitude;
SGPropertyNode_ptr altitude_agl;
SGPropertyNode_ptr asi_serviceable;
SGPropertyNode_ptr asi_speed;
SGPropertyNode_ptr autopilot_heading_lock;
SGPropertyNode_ptr flaps;
SGPropertyNode_ptr gear_down;
SGPropertyNode_ptr latitude;
SGPropertyNode_ptr longitude;
SGPropertyNode_ptr nav0_cdi_serviceable;
SGPropertyNode_ptr nav0_gs_distance;
SGPropertyNode_ptr nav0_gs_needle_deflection;
SGPropertyNode_ptr nav0_gs_serviceable;
SGPropertyNode_ptr nav0_has_gs;
SGPropertyNode_ptr nav0_heading_needle_deflection;
SGPropertyNode_ptr nav0_in_range;
SGPropertyNode_ptr nav0_nav_loc;
SGPropertyNode_ptr nav0_serviceable;
SGPropertyNode_ptr power;
SGPropertyNode_ptr replay_state;
SGPropertyNode_ptr vs;
} external_properties;
inline PropertiesHandler (MK_VIII *device)
: mk(device) {}
template <class T>
inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
{
node->tie(raw_value);
tied_properties.push_back(node);
}
template <class T>
inline void tie (SGPropertyNode *node,
const char *relative_path,
const SGRawValue<T> &raw_value)
{
tie(node->getNode(relative_path, true), raw_value);
}
PropertiesHandler() {};
void init ();
void unbind ();
};
public:
PropertiesHandler properties_handler;
private:
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::PowerHandler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class PowerHandler
{
MK_VIII *mk;
bool serviceable;
bool powered;
Timer power_loss_timer;
Timer abnormal_timer;
Timer low_surge_timer;
Timer high_surge_timer;
Timer very_high_surge_timer;
bool handle_abnormal_voltage (bool abnormal,
Timer *timer,
double max_duration);
void power_on ();
void power_off ();
public:
inline PowerHandler (MK_VIII *device)
: mk(device), serviceable(false), powered(false) {}
void bind (SGPropertyNode *node);
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::SystemHandler ///////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class SystemHandler
{
MK_VIII *mk;
double boot_delay;
Timer boot_timer;
int last_replay_state;
Timer reposition_timer;
public:
typedef enum
{
STATE_OFF,
STATE_BOOTING,
STATE_ON,
STATE_REPOSITION
} State;
State state;
inline SystemHandler (MK_VIII *device)
: mk(device), state(STATE_OFF) {}
void power_on ();
void power_off ();
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::ConfigurationModule /////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class ConfigurationModule
{
public:
// keep in sync with IOHandler::present_status()
typedef enum
{
CATEGORY_AIRCRAFT_MODE_TYPE_SELECT,
CATEGORY_AIR_DATA_INPUT_SELECT,
CATEGORY_POSITION_INPUT_SELECT,
CATEGORY_ALTITUDE_CALLOUTS,
CATEGORY_AUDIO_MENU_SELECT,
CATEGORY_TERRAIN_DISPLAY_SELECT,
CATEGORY_OPTIONS_SELECT_GROUP_1,
CATEGORY_RADIO_ALTITUDE_INPUT_SELECT,
CATEGORY_NAVIGATION_INPUT_SELECT,
CATEGORY_ATTITUDE_INPUT_SELECT,
CATEGORY_HEADING_INPUT_SELECT,
CATEGORY_WINDSHEAR_INPUT_SELECT,
CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT,
CATEGORY_AUDIO_OUTPUT_LEVEL,
CATEGORY_UNDEFINED_INPUT_SELECT_1,
CATEGORY_UNDEFINED_INPUT_SELECT_2,
CATEGORY_UNDEFINED_INPUT_SELECT_3,
N_CATEGORIES
} Category;
typedef enum
{
STATE_OK,
STATE_INVALID_DATABASE,
STATE_INVALID_AIRCRAFT_TYPE
} State;
State state;
int effective_categories[N_CATEGORIES];
ConfigurationModule (MK_VIII *device);
void boot ();
void bind (SGPropertyNode *node);
private:
MK_VIII *mk;
int categories[N_CATEGORIES];
bool read_aircraft_mode_type_select (int value);
bool read_air_data_input_select (int value);
bool read_position_input_select (int value);
bool read_altitude_callouts (int value);
bool read_audio_menu_select (int value);
bool read_terrain_display_select (int value);
bool read_options_select_group_1 (int value);
bool read_radio_altitude_input_select (int value);
bool read_navigation_input_select (int value);
bool read_attitude_input_select (int value);
bool read_heading_input_select (int value);
bool read_windshear_input_select (int value);
bool read_input_output_discrete_type_select (int value);
bool read_audio_output_level (int value);
bool read_undefined_input_select (int value);
static bool m6_t2_is_bank_angle (Parameter<double> *agl,
double abs_roll_deg,
bool ap_engaged);
static bool m6_t4_is_bank_angle (Parameter<double> *agl,
double abs_roll_deg,
bool ap_engaged);
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::FaultHandler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class FaultHandler
{
enum
{
INOP_GPWS = 1 << 0,
INOP_TAD = 1 << 1
};
MK_VIII *mk;
static const unsigned int fault_inops[];
bool has_faults (unsigned int inop);
public:
// keep in sync with IOHandler::present_status()
typedef enum
{
FAULT_ALL_MODES_INHIBIT,
FAULT_GEAR_SWITCH,
FAULT_FLAPS_SWITCH,
FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID,
FAULT_SELF_TEST_INVALID,
FAULT_GLIDESLOPE_CANCEL_INVALID,
FAULT_STEEP_APPROACH_INVALID,
FAULT_GPWS_INHIBIT,
FAULT_TA_TCF_INHIBIT,
FAULT_MODES14_INPUTS_INVALID,
FAULT_MODE5_INPUTS_INVALID,
FAULT_MODE6_INPUTS_INVALID,
FAULT_BANK_ANGLE_INPUTS_INVALID,
FAULT_TCF_INPUTS_INVALID,
N_FAULTS
} Fault;
bool faults[N_FAULTS];
inline FaultHandler (MK_VIII *device)
: mk(device) {}
void boot ();
void set_fault (Fault fault);
void unset_fault (Fault fault);
bool has_faults () const;
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::IOHandler ///////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
public:
class IOHandler
{
public:
enum Lamp
{
LAMP_NONE,
LAMP_GLIDESLOPE,
LAMP_CAUTION,
LAMP_WARNING
};
struct LampConfiguration
{
bool format2;
bool flashing;
};
struct FaultsConfiguration
{
double max_flaps_down_airspeed;
double max_gear_down_airspeed;
};
struct _s_Conf
{
const LampConfiguration *lamp;
const FaultsConfiguration *faults;
bool flap_reversal;
bool steep_approach_enabled;
bool gpws_inhibit_enabled;
bool momentary_flap_override_enabled;
bool alternate_steep_approach;
bool use_internal_gps;
bool localizer_enabled;
} conf;
struct _s_input_feeders
{
struct _s_discretes
{
bool landing_gear;
bool landing_flaps;
bool glideslope_inhibit;
bool decision_height;
bool autopilot_engaged;
} discretes;
struct _s_arinc429
{
bool uncorrected_barometric_altitude;
bool barometric_altitude_rate;
bool radio_altitude;
bool glideslope_deviation;
bool roll_angle;
bool localizer_deviation;
bool computed_airspeed;
bool decision_height;
} arinc429;
} input_feeders;
struct _s_inputs
{
struct _s_discretes
{
bool landing_gear; // appendix E 6.6.2, 3.15.1.4
bool landing_flaps; // appendix E 6.6.4, 3.15.1.2
bool momentary_flap_override; // appendix E 6.6.6, 3.15.1.6
bool self_test; // appendix E 6.6.7, 3.15.1.10
bool glideslope_inhibit; // appendix E 6.6.11, 3.15.1.1
bool glideslope_cancel; // appendix E 6.6.13, 3.15.1.5
bool decision_height; // appendix E 6.6.14, 3.10.2
bool mode6_low_volume; // appendix E 6.6.15, 3.15.1.7
bool audio_inhibit; // appendix E 6.6.16, 3.15.1.3
bool ta_tcf_inhibit; // appendix E 6.6.20, 3.15.1.9
bool autopilot_engaged; // appendix E 6.6.21, 3.15.1.8
bool steep_approach; // appendix E 6.6.25, 3.15.1.11
bool gpws_inhibit; // appendix E 6.6.27, 3.15.1.12
} discretes;
struct _s_arinc429
{
Parameter<double> uncorrected_barometric_altitude; // appendix E 6.2.1
Parameter<double> barometric_altitude_rate; // appendix E 6.2.2
Parameter<double> gps_altitude; // appendix E 6.2.4
Parameter<double> gps_latitude; // appendix E 6.2.7
Parameter<double> gps_longitude; // appendix E 6.2.8
Parameter<double> gps_vertical_figure_of_merit; // appendix E 6.2.13
Parameter<double> radio_altitude; // appendix E 6.2.29
Parameter<double> glideslope_deviation; // appendix E 6.2.30
Parameter<double> roll_angle; // appendix E 6.2.31
Parameter<double> localizer_deviation; // appendix E 6.2.33
Parameter<double> computed_airspeed; // appendix E 6.2.39
Parameter<double> decision_height; // appendix E 6.2.41
} arinc429;
} inputs;
struct Outputs
{
struct _s_discretes
{
bool gpws_warning; // appendix E 7.4.1, 3.15.2.5
bool gpws_alert; // appendix E 7.4.1, 3.15.2.6
bool audio_on; // appendix E 7.4.2, 3.15.2.10
bool gpws_inop; // appendix E 7.4.3, 3.15.2.3
bool tad_inop; // appendix E 7.4.3, 3.15.2.4
bool flap_override; // appendix E 7.4.5, 3.15.2.8
bool glideslope_cancel; // appendix E 7.4.6, 3.15.2.7
bool steep_approach; // appendix E 7.4.12, 3.15.2.9
} discretes;
struct _s_arinc429
{
int egpws_alert_discrete_1; // appendix E 7.1.1.1
int egpwc_logic_discretes; // appendix E 7.1.1.2
int mode6_callouts_discrete_1; // appendix E 7.1.1.3
int mode6_callouts_discrete_2; // appendix E 7.1.1.4
int egpws_alert_discrete_2; // appendix E 7.1.1.5
int egpwc_alert_discrete_3; // appendix E 7.1.1.6
} arinc429;
};
Outputs outputs;
struct _s_data
{
Parameter<double> barometric_altitude_rate;
Parameter<double> decision_height;
Parameter<double> geometric_altitude;
Parameter<double> glideslope_deviation_dots;
Parameter<double> gps_altitude;
Parameter<double> gps_latitude;
Parameter<double> gps_longitude;
Parameter<double> gps_vertical_figure_of_merit;
Parameter<double> localizer_deviation_dots;
Parameter<double> radio_altitude;
Parameter<double> roll_angle;
Parameter<double> terrain_clearance;
} data;
IOHandler (MK_VIII *device);
void boot ();
void post_boot ();
void power_off ();
void enter_ground ();
void enter_takeoff ();
void update_inputs ();
void update_input_faults ();
void update_alternate_discrete_input (bool *ptr);
void update_internal_latches ();
void update_egpws_alert_discrete_1 ();
void update_egpwc_logic_discretes ();
void update_mode6_callouts_discrete_1 ();
void update_mode6_callouts_discrete_2 ();
void update_egpws_alert_discrete_2 ();
void update_egpwc_alert_discrete_3 ();
void update_outputs ();
void update_lamps ();
void set_lamp (Lamp lamp);
bool gpws_inhibit () const;
bool real_flaps_down () const;
bool flaps_down () const;
bool flap_override () const;
bool steep_approach () const;
bool momentary_steep_approach_enabled () const;
void bind (SGPropertyNode *node);
MK_VIII *mk;
private:
///////////////////////////////////////////////////////////////////////////
// MK_VIII::IOHandler::TerrainClearanceFilter /////////////////////////////
///////////////////////////////////////////////////////////////////////////
class TerrainClearanceFilter
{
typedef deque< Sample<double> > samples_type;
samples_type samples;
double value;
public:
inline TerrainClearanceFilter ()
: value(0) {}
double update (double agl);
void reset ();
};
///////////////////////////////////////////////////////////////////////////
// MK_VIII::IOHandler (continued) /////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
TerrainClearanceFilter terrain_clearance_filter;
Lamp _lamp;
Timer lamp_timer;
Timer audio_inhibit_fault_timer;
Timer landing_gear_fault_timer;
Timer flaps_down_fault_timer;
Timer momentary_flap_override_fault_timer;
Timer self_test_fault_timer;
Timer glideslope_cancel_fault_timer;
Timer steep_approach_fault_timer;
Timer gpws_inhibit_fault_timer;
Timer ta_tcf_inhibit_fault_timer;
bool last_landing_gear;
bool last_real_flaps_down;
typedef deque< Sample< Parameter<double> > > altitude_samples_type;
altitude_samples_type altitude_samples;
struct
{
bool glideslope_cancel;
} power_saved;
void update_terrain_clearance ();
void reset_terrain_clearance ();
void handle_input_fault (bool test, FaultHandler::Fault fault);
void handle_input_fault (bool test,
Timer *timer,
double max_duration,
FaultHandler::Fault fault);
void tie_input (SGPropertyNode *node,
const char *name,
bool *input,
bool *feed = NULL);
void tie_input (SGPropertyNode *node,
const char *name,
Parameter<double> *input,
bool *feed = NULL);
void tie_output (SGPropertyNode *node,
const char *name,
bool *output);
void tie_output (SGPropertyNode *node,
const char *name,
int *output);
public:
bool get_discrete_input (bool *ptr) const;
void set_discrete_input (bool *ptr, bool value);
void present_status ();
void present_status_section (const char *name);
void present_status_item (const char *name, const char *value = NULL);
void present_status_subitem (const char *name);
bool get_present_status () const;
void set_present_status (bool value);
bool *get_lamp_output (Lamp lamp);
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer /////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class VoicePlayer
{
public:
///////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Voice ////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
class Voice
{
public:
/////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Voice::Element ////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
class Element
{
public:
bool silence;
virtual inline void play (double volume) {}
virtual inline void stop () {}
virtual bool is_playing () = 0;
virtual inline void set_volume (double volume) {}
};
/////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Voice::SampleElement ///////////////////////////
/////////////////////////////////////////////////////////////////////////
class SampleElement : public Element
{
SGSoundSample *_sample;
double _volume;
public:
inline SampleElement (SGSoundSample *sample, double volume = 1.0)
: _sample(sample), _volume(volume) { silence = false; }
virtual inline void play (double volume) { if (_sample) { set_volume(volume); _sample->play_once(); } }
virtual inline void stop () { if (_sample) _sample->stop(); }
virtual inline bool is_playing () { return _sample ? _sample->is_playing() : false; }
virtual inline void set_volume (double volume) { if (_sample) _sample->set_volume(volume * _volume); }
};
/////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Voice::SilenceElement //////////////////////////
/////////////////////////////////////////////////////////////////////////
class SilenceElement : public Element
{
double _duration;
double start_time;
public:
inline SilenceElement (double duration)
: _duration(duration) { silence = true; }
virtual inline void play (double volume) { start_time = globals->get_sim_time_sec(); }
virtual inline bool is_playing () { return globals->get_sim_time_sec() - start_time < _duration; }
};
/////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Voice (continued) //////////////////////////////
/////////////////////////////////////////////////////////////////////////
Element *element;
inline Voice (VoicePlayer *_player)
: player(_player), volume(1.0), element(NULL) {}
~Voice ();
inline void append (Element *_element) { elements.push_back(_element); }
void play ();
void stop (bool now);
void set_volume (double _volume);
void volume_changed ();
void update ();
private:
VoicePlayer *player;
double volume;
vector<Element *> elements;
vector<Element *>::iterator iter;
inline double get_volume () const { return player->volume * player->speaker.volume * volume; }
};
///////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
struct
{
double volume;
} conf;
double volume;
Voice *voice;
Voice *next_voice;
struct
{
Voice *application_data_base_failed;
Voice *bank_angle;
Voice *bank_angle_bank_angle;
Voice *bank_angle_bank_angle_3;
Voice *bank_angle_inop;
Voice *bank_angle_pause_bank_angle;
Voice *bank_angle_pause_bank_angle_3;
Voice *callouts_inop;
Voice *configuration_type_invalid;
Voice *dont_sink;
Voice *dont_sink_pause_dont_sink;
Voice *five_hundred_above;
Voice *glideslope;
Voice *glideslope_inop;
Voice *gpws_inop;
Voice *hard_glideslope;
Voice *minimums;
Voice *minimums_minimums;
Voice *pull_up;
Voice *sink_rate;
Voice *sink_rate_pause_sink_rate;
Voice *soft_glideslope;
Voice *terrain;
Voice *terrain_pause_terrain;
Voice *too_low_flaps;
Voice *too_low_gear;
Voice *too_low_terrain;
Voice *altitude_callouts[n_altitude_callouts];
} voices;
inline VoicePlayer (MK_VIII *device)
: mk(device), speaker(this), voice(NULL), next_voice(NULL) {}
~VoicePlayer ();
void init ();
enum
{
PLAY_NOW = 1 << 0,
PLAY_LOOPED = 1 << 1
};
void play (Voice *_voice, unsigned int flags = 0);
enum
{
STOP_NOW = 1 << 0
};
void stop (unsigned int flags = 0);
void set_volume (double _volume);
void update ();
inline void bind (SGPropertyNode *node) { speaker.bind(node); }
public:
///////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer::Speaker //////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
class Speaker
{
VoicePlayer *player;
double pitch;
float position[3];
float orientation[3];
float inner_cone;
float outer_cone;
float outer_gain;
float reference_dist;
float max_dist;
template <class T>
inline void tie (SGPropertyNode *node, const char *name, T *ptr)
{
player->mk->properties_handler.tie
(node, (string("speaker/") + name).c_str(),
RawValueMethodsData<MK_VIII::VoicePlayer::Speaker,T,T*>
(*this, ptr,
&MK_VIII::VoicePlayer::Speaker::get_property,
&MK_VIII::VoicePlayer::Speaker::set_property));
}
public:
template <class T>
inline void set_property (T *ptr, T value) { *ptr = value; update_configuration(); }
template <class T>
inline T get_property (T *ptr) const { return *ptr; }
double volume;
inline Speaker (VoicePlayer *_player)
: player(_player),
volume(1),
pitch(1),
inner_cone(360),
outer_cone(360),
outer_gain(0),
reference_dist(3),
max_dist(10)
{
position[0] = 0; position[1] = 0; position[2] = 0;
orientation[0] = 0; orientation[1] = 0; orientation[2] = 0;
}
void bind (SGPropertyNode *node);
void update_configuration ();
};
private:
///////////////////////////////////////////////////////////////////////////
// MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
MK_VIII *mk;
Speaker speaker;
map<string, SGSoundSample *> samples;
vector<Voice *> _voices;
bool looped;
bool next_looped;
SGSoundSample *get_sample (const char *name);
inline void append (Voice *voice, Voice::Element *element) { voice->append(element); }
inline void append (Voice *voice, const char *sample_name) { voice->append(new Voice::SampleElement(get_sample(sample_name))); }
inline void append (Voice *voice, double silence) { voice->append(new Voice::SilenceElement(silence)); }
inline void make_voice (Voice **voice) { *voice = new Voice(this); _voices.push_back(*voice); }
template <class T1>
inline void make_voice (Voice **voice, T1 e1) { make_voice(voice); append(*voice, e1); }
template <class T1, class T2>
inline void make_voice (Voice **voice, T1 e1, T2 e2) { make_voice(voice, e1); append(*voice, e2); }
template <class T1, class T2, class T3>
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3) { make_voice(voice, e1, e2); append(*voice, e3); }
template <class T1, class T2, class T3, class T4>
inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3, T4 e4) { make_voice(voice, e1, e2, e3); append(*voice, e4); }
};
private:
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::SelfTestHandler /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class SelfTestHandler
{
MK_VIII *mk;
typedef enum
{
CANCEL_NONE,
CANCEL_SHORT,
CANCEL_LONG
} Cancel;
enum
{
ACTION_SLEEP = 1 << 0,
ACTION_VOICE = 1 << 1,
ACTION_DISCRETE_ON_OFF = 1 << 2,
ACTION_DONE = 1 << 3
};
typedef struct
{
unsigned int flags;
double sleep_duration;
bool *discrete;
} Action;
Cancel cancel;
Action action;
int current;
bool button_pressed;
double button_press_timestamp;
IOHandler::Outputs saved_outputs;
double sleep_start;
bool _was_here (int position);
Action sleep (double duration);
Action play (VoicePlayer::Voice *voice);
Action discrete_on (bool *discrete, double duration);
Action discrete_on_off (bool *discrete, double duration);
Action discrete_on_off (bool *discrete, VoicePlayer::Voice *voice);
Action done ();
Action run ();
void start ();
void stop ();
void shutdown ();
public:
typedef enum
{
STATE_NONE,
STATE_START,
STATE_RUNNING
} State;
State state;
inline SelfTestHandler (MK_VIII *device)
: mk(device), state(STATE_NONE), button_pressed(false) {}
inline void power_off () { stop(); }
inline void set_inop () { stop(); }
void handle_button_event (bool value);
bool update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::AlertHandler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class AlertHandler
{
MK_VIII *mk;
unsigned int old_alerts;
unsigned int voice_alerts;
unsigned int repeated_alerts;
VoicePlayer::Voice *altitude_callout_voice;
void reset ();
inline bool has_alerts (unsigned int test) const { return (alerts & test) != 0; }
inline bool has_old_alerts (unsigned int test) const { return (old_alerts & test) != 0; }
inline bool must_play_voice (unsigned int test) const { return ! has_old_alerts(test) || (repeated_alerts & test) != 0; }
bool select_voice_alerts (unsigned int test);
public:
enum
{
ALERT_MODE1_PULL_UP = 1 << 0,
ALERT_MODE1_SINK_RATE = 1 << 1,
ALERT_MODE2A_PREFACE = 1 << 2,
ALERT_MODE2B_PREFACE = 1 << 3,
ALERT_MODE2A = 1 << 4,
ALERT_MODE2B = 1 << 5,
ALERT_MODE2B_LANDING_MODE = 1 << 6,
ALERT_MODE2A_ALTITUDE_GAIN = 1 << 7,
ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING = 1 << 8,
ALERT_MODE3 = 1 << 9,
ALERT_MODE4_TOO_LOW_FLAPS = 1 << 10,
ALERT_MODE4_TOO_LOW_GEAR = 1 << 11,
ALERT_MODE4AB_TOO_LOW_TERRAIN = 1 << 12,
ALERT_MODE4C_TOO_LOW_TERRAIN = 1 << 13,
ALERT_MODE5_SOFT = 1 << 14,
ALERT_MODE5_HARD = 1 << 15,
ALERT_MODE6_MINIMUMS = 1 << 16,
ALERT_MODE6_ALTITUDE_CALLOUT = 1 << 17,
ALERT_MODE6_LOW_BANK_ANGLE_1 = 1 << 18,
ALERT_MODE6_HIGH_BANK_ANGLE_1 = 1 << 19,
ALERT_MODE6_LOW_BANK_ANGLE_2 = 1 << 20,
ALERT_MODE6_HIGH_BANK_ANGLE_2 = 1 << 21,
ALERT_MODE6_LOW_BANK_ANGLE_3 = 1 << 22,
ALERT_MODE6_HIGH_BANK_ANGLE_3 = 1 << 23,
ALERT_TCF_TOO_LOW_TERRAIN = 1 << 24
};
enum
{
ALERT_FLAG_REPEAT = 1 << 0
};
unsigned int alerts;
inline AlertHandler (MK_VIII *device)
: mk(device) {}
void boot ();
void reposition ();
void update ();
void set_alerts (unsigned int _alerts,
unsigned int flags = 0,
VoicePlayer::Voice *_altitude_callout_voice = NULL);
void unset_alerts (unsigned int _alerts);
inline void repeat_alert (unsigned int alert) { set_alerts(alert, ALERT_FLAG_REPEAT); }
inline void set_altitude_callout_alert (VoicePlayer::Voice *voice) { set_alerts(ALERT_MODE6_ALTITUDE_CALLOUT, 0, voice); }
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::StateHandler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class StateHandler
{
MK_VIII *mk;
Timer potentially_airborne_timer;
void update_ground ();
void enter_ground ();
void leave_ground ();
void update_takeoff ();
void enter_takeoff ();
void leave_takeoff ();
public:
bool ground;
bool takeoff;
inline StateHandler (MK_VIII *device)
: mk(device), ground(true), takeoff(true) {}
void post_reposition ();
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode1Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode1Handler
{
MK_VIII *mk;
Timer pull_up_timer;
Timer sink_rate_timer;
double sink_rate_tti; // time-to-impact in minutes
double get_pull_up_bias ();
bool is_pull_up ();
double get_sink_rate_bias ();
bool is_sink_rate ();
double get_sink_rate_tti ();
void update_pull_up ();
void update_sink_rate ();
public:
typedef struct
{
bool flap_override_bias;
int min_agl;
double (*pull_up_min_agl1) (double vs);
int pull_up_max_agl1;
double (*pull_up_min_agl2) (double vs);
int pull_up_max_agl2;
} EnvelopesConfiguration;
struct
{
const EnvelopesConfiguration *envelopes;
} conf;
inline Mode1Handler (MK_VIII *device)
: mk(device) {}
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode2Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode2Handler
{
///////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode2Handler::ClosureRateFilter ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
class ClosureRateFilter
{
/////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode2Handler::ClosureRateFilter::PassFilter /////////////////
/////////////////////////////////////////////////////////////////////////
class PassFilter
{
double a0;
double a1;
double b1;
double last_input;
double last_output;
public:
inline PassFilter (double _a0, double _a1, double _b1)
: a0(_a0), a1(_a1), b1(_b1) {}
inline double filter (double input)
{
last_output = a0 * input + a1 * last_input + b1 * last_output;
last_input = input;
return last_output;
}
inline void reset ()
{
last_input = 0;
last_output = 0;
}
};
/////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode2Handler::ClosureRateFilter (continued) /////////////////
/////////////////////////////////////////////////////////////////////////
MK_VIII *mk;
Timer timer;
Parameter<double> last_ra; // last radio altitude
Parameter<double> last_ba; // last barometric altitude
PassFilter ra_filter; // radio altitude rate filter
PassFilter ba_filter; // barometric altitude rate filter
double limit_radio_altitude_rate (double r);
public:
Parameter<double> output;
inline ClosureRateFilter (MK_VIII *device)
: mk(device),
ra_filter(0.05, 0, 0.95), // low-pass filter
ba_filter(0.93, -0.93, 0.86) {} // high-pass-filter
void init ();
void update ();
};
///////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode2Handler (continued) //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
MK_VIII *mk;
ClosureRateFilter closure_rate_filter;
Timer takeoff_timer;
Timer pull_up_timer;
double a_start_time;
Timer a_altitude_gain_timer;
double a_altitude_gain_alt;
void check_pull_up (unsigned int preface_alert, unsigned int alert);
bool b_conditions ();
bool is_a ();
bool is_b ();
void update_a ();
void update_b ();
public:
typedef struct
{
int airspeed1;
int airspeed2;
} Configuration;
const Configuration *conf;
inline Mode2Handler (MK_VIII *device)
: mk(device), closure_rate_filter(device) {}
void boot ();
void power_off ();
void leave_ground ();
void enter_takeoff ();
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode3Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode3Handler
{
MK_VIII *mk;
bool armed;
bool has_descent_alt;
double descent_alt;
double bias;
double max_alt_loss (double _bias);
double get_bias (double initial_bias, double alt_loss);
bool is (double *alt_loss);
public:
typedef struct
{
int min_agl;
int (*max_agl) (bool flap_override);
double (*max_alt_loss) (bool flap_override, double agl);
} Configuration;
const Configuration *conf;
inline Mode3Handler (MK_VIII *device)
: mk(device), armed(false), has_descent_alt(false) {}
void enter_takeoff ();
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode4Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode4Handler
{
public:
typedef struct
{
int airspeed1;
int airspeed2;
int min_agl1;
double (*min_agl2) (double airspeed);
int min_agl3;
} EnvelopesConfiguration;
typedef struct
{
const EnvelopesConfiguration *ac;
const EnvelopesConfiguration *b;
} ModesConfiguration;
struct
{
VoicePlayer::Voice *voice_too_low_gear;
const ModesConfiguration *modes;
} conf;
inline Mode4Handler (MK_VIII *device)
: mk(device) {}
double get_upper_agl (const EnvelopesConfiguration *c);
void update ();
private:
MK_VIII *mk;
double ab_bias;
double ab_expanded_bias;
double c_bias;
const EnvelopesConfiguration *get_ab_envelope ();
double get_bias (double initial_bias, double min_agl);
void handle_alert (unsigned int alert, double min_agl, double *bias);
void update_ab ();
void update_ab_expanded ();
void update_c ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode5Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode5Handler
{
MK_VIII *mk;
Timer hard_timer;
Timer soft_timer;
double soft_bias;
bool is_hard ();
bool is_soft (double bias);
double get_soft_bias (double initial_bias);
void update_hard (bool is);
void update_soft (bool is);
public:
inline Mode5Handler (MK_VIII *device)
: mk(device) {}
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::Mode6Handler ////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Mode6Handler
{
public:
// keep in sync with altitude_callout_definitions[]
typedef enum
{
ALTITUDE_CALLOUT_1000,
ALTITUDE_CALLOUT_500,
ALTITUDE_CALLOUT_400,
ALTITUDE_CALLOUT_300,
ALTITUDE_CALLOUT_200,
ALTITUDE_CALLOUT_100,
ALTITUDE_CALLOUT_50,
ALTITUDE_CALLOUT_40,
ALTITUDE_CALLOUT_30,
ALTITUDE_CALLOUT_20,
ALTITUDE_CALLOUT_10
} AltitudeCallout;
typedef bool (*BankAnglePredicate) (Parameter<double> *agl,
double abs_roll_deg,
bool ap_engaged);
struct
{
bool minimums_enabled;
bool smart_500_enabled;
VoicePlayer::Voice *above_field_voice;
bool altitude_callouts_enabled[n_altitude_callouts];
bool bank_angle_enabled;
BankAnglePredicate is_bank_angle;
} conf;
static const int altitude_callout_definitions[];
inline Mode6Handler (MK_VIII *device)
: mk(device) {}
void boot ();
void power_off ();
void enter_takeoff ();
void leave_takeoff ();
void set_volume (double volume);
bool altitude_callouts_enabled ();
void update ();
private:
MK_VIII *mk;
bool last_decision_height;
Parameter<double> last_radio_altitude;
Parameter<double> last_altitude_above_field;
bool altitude_callouts_issued[n_altitude_callouts];
bool minimums_issued;
bool above_field_issued;
Timer runway_timer;
Parameter<bool> has_runway;
struct
{
double elevation; // elevation in feet
} runway;
void reset_minimums ();
void reset_altitude_callouts ();
bool is_playing_altitude_callout ();
bool is_near_minimums (double callout);
bool is_outside_band (double elevation, double callout);
bool inhibit_smart_500 ();
void update_minimums ();
void update_altitude_callouts ();
bool test_runway (const FGRunway *_runway);
bool test_airport (const FGAirport *airport);
void update_runway ();
void get_altitude_above_field (Parameter<double> *parameter);
void update_above_field_callout ();
bool is_bank_angle (double abs_roll_angle, double bias);
bool is_high_bank_angle ();
unsigned int get_bank_angle_alerts ();
void update_bank_angle ();
class AirportFilter : public FGAirport::AirportFilter
{
public:
AirportFilter(Mode6Handler *s)
: self(s) {}
virtual bool passAirport(FGAirport *a) const;
virtual FGPositioned::Type maxType() const {
return FGPositioned::AIRPORT;
}
private:
Mode6Handler* self;
};
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII::TCFHandler //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class TCFHandler
{
typedef struct
{
double latitude; // latitude in degrees
double longitude; // longitude in degrees
} Position;
typedef struct
{
Position position; // position of threshold
double heading; // runway heading
} RunwayEdge;
MK_VIII *mk;
static const double k;
Timer runway_timer;
bool has_runway;
struct
{
Position center; // center point
double elevation; // elevation in feet
double half_length; // runway half length, in nautical miles
RunwayEdge edges[2]; // runway threshold and end
Position bias_area[4]; // vertices of the bias area
} runway;
double bias;
double *reference;
double initial_value;
double get_azimuth_difference (double from_lat,
double from_lon,
double to_lat,
double to_lon,
double to_heading);
double get_azimuth_difference (const FGRunway *_runway);
FGRunway* select_runway (const FGAirport *airport);
void update_runway ();
void get_bias_area_edges (Position *edge,
double reciprocal,
double half_width_m,
Position *bias_edge1,
Position *bias_edge2);
bool is_inside_edge_triangle (RunwayEdge *edge);
bool is_inside_bias_area ();
bool is_tcf ();
bool is_rfcf ();
class AirportFilter : public FGAirport::AirportFilter
{
public:
AirportFilter(MK_VIII *device)
: mk(device) {}
virtual bool passAirport(FGAirport *a) const;
virtual FGPositioned::Type maxType() const {
return FGPositioned::AIRPORT;
}
private:
MK_VIII* mk;
};
public:
struct
{
bool enabled;
} conf;
inline TCFHandler (MK_VIII *device)
: mk(device) {}
void update ();
};
/////////////////////////////////////////////////////////////////////////////
// MK_VIII (continued) //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
string name;
int num;
PowerHandler power_handler;
SystemHandler system_handler;
ConfigurationModule configuration_module;
FaultHandler fault_handler;
IOHandler io_handler;
VoicePlayer voice_player;
SelfTestHandler self_test_handler;
AlertHandler alert_handler;
StateHandler state_handler;
Mode1Handler mode1_handler;
Mode2Handler mode2_handler;
Mode3Handler mode3_handler;
Mode4Handler mode4_handler;
Mode5Handler mode5_handler;
Mode6Handler mode6_handler;
TCFHandler tcf_handler;
struct
{
int runway_database;
} conf;
public:
MK_VIII (SGPropertyNode *node);
virtual void init ();
virtual void bind ();
virtual void unbind ();
virtual void update (double dt);
};
#endif // __INSTRUMENTS_MK_VIII_HXX