// 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 #include #include #include #include #include #include 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 RawValueMethodsData : public SGRawValue { 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::DefaultValue; } virtual bool setValue (VT value) { if (_setter) { (_obj.*_setter)(_data, value); return true; } else return false; } virtual SGRawValue *clone () const { return new RawValueMethodsData(_obj, _data, _getter, _setter); } private: C &_obj; DT _data; getter_t _getter; setter_t _setter; }; ///////////////////////////////////////////////////////////////////////////// // MK_VIII::Parameter /////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// template 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 *parameter) { if (parameter->ncd) unset(); else set(parameter->get()); } inline void set (const Parameter *parameter, double factor) { if (parameter->ncd) unset(); else set(parameter->get() * factor); } }; ///////////////////////////////////////////////////////////////////////////// // MK_VIII::Sample ////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// template 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 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 inline void tie (SGPropertyNode *node, const SGRawValue &raw_value) { node->tie(raw_value); tied_properties.push_back(node); } template inline void tie (SGPropertyNode *node, const char *relative_path, const SGRawValue &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 *agl, double abs_roll_deg, bool ap_engaged); static bool m6_t4_is_bank_angle (Parameter *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 uncorrected_barometric_altitude; // appendix E 6.2.1 Parameter barometric_altitude_rate; // appendix E 6.2.2 Parameter gps_altitude; // appendix E 6.2.4 Parameter gps_latitude; // appendix E 6.2.7 Parameter gps_longitude; // appendix E 6.2.8 Parameter gps_vertical_figure_of_merit; // appendix E 6.2.13 Parameter radio_altitude; // appendix E 6.2.29 Parameter glideslope_deviation; // appendix E 6.2.30 Parameter roll_angle; // appendix E 6.2.31 Parameter localizer_deviation; // appendix E 6.2.33 Parameter computed_airspeed; // appendix E 6.2.39 Parameter 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 barometric_altitude_rate; Parameter decision_height; Parameter geometric_altitude; Parameter glideslope_deviation_dots; Parameter gps_altitude; Parameter gps_latitude; Parameter gps_longitude; Parameter gps_vertical_figure_of_merit; Parameter localizer_deviation_dots; Parameter radio_altitude; Parameter roll_angle; Parameter 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 > 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 > > 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 *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 elements; vector::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 inline void tie (SGPropertyNode *node, const char *name, T *ptr) { player->mk->properties_handler.tie (node, (string("speaker/") + name).c_str(), RawValueMethodsData (*this, ptr, &MK_VIII::VoicePlayer::Speaker::get_property, &MK_VIII::VoicePlayer::Speaker::set_property)); } public: template inline void set_property (T *ptr, T value) { *ptr = value; update_configuration(); } template 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 samples; vector _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 inline void make_voice (Voice **voice, T1 e1) { make_voice(voice); append(*voice, e1); } template inline void make_voice (Voice **voice, T1 e1, T2 e2) { make_voice(voice, e1); append(*voice, e2); } template inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3) { make_voice(voice, e1, e2); append(*voice, e3); } template 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 last_ra; // last radio altitude Parameter 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 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 *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 last_radio_altitude; Parameter last_altitude_above_field; bool altitude_callouts_issued[n_altitude_callouts]; bool minimums_issued; bool above_field_issued; Timer runway_timer; Parameter 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 *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