1 // mk_viii.cxx -- Honeywell MK VIII EGPWS emulation
3 // Written by Jean-Yves Lefort, started September 2005.
5 // Copyright (C) 2005, 2006 Jean-Yves Lefort - jylefort@FreeBSD.org
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 ///////////////////////////////////////////////////////////////////////////////
25 // [PILOT] Honeywell International Inc., "MK VI and MK VIII,
26 // Enhanced Ground Proximity Warning System (EGPWS),
27 // Pilot's Guide", May 2004
29 // http://www.egpws.com/engineering_support/documents/pilot_guides/060-4314-000.pdf
31 // [SPEC] Honeywell International Inc., "Product Specification
32 // for the MK VI and MK VIII Enhanced Ground Proximity
33 // Warning System (EGPWS)", June 2004
35 // http://www.egpws.com/engineering_support/documents/specs/965-1180-601.pdf
37 // [INSTALL] Honeywell Inc., "MK VI, MK VIII, Enhanced Ground
38 // Proximity Warning System (Class A TAWS), Installation
39 // Design Guide", July 2003
41 // http://www.egpws.com/engineering_support/documents/install/060-4314-150.pdf
45 // [1] [SPEC] does not specify the "must be airborne"
46 // condition; we use it to make sure that the alert
47 // will not trigger when on the ground, since it would
51 # pragma warning( disable: 4355 )
66 #include <simgear/constants.h>
67 #include <simgear/sg_inlines.h>
68 #include <simgear/debug/logstream.hxx>
69 #include <simgear/math/sg_geodesy.hxx>
70 #include <simgear/math/sg_random.h>
71 #include <simgear/misc/sg_path.hxx>
72 #include <simgear/sound/soundmgr_openal.hxx>
73 #include <simgear/structure/exception.hxx>
77 #include <Airports/runways.hxx>
78 #include <Airports/simple.hxx>
80 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
81 # include <Include/version.h>
83 # include <Include/no_version.h>
86 #include <Main/fg_props.hxx>
87 #include <Main/globals.hxx>
88 #include "instrument_mgr.hxx"
89 #include "mk_viii.hxx"
91 ///////////////////////////////////////////////////////////////////////////////
92 // constants //////////////////////////////////////////////////////////////////
93 ///////////////////////////////////////////////////////////////////////////////
95 #define GLIDESLOPE_DOTS_TO_DDM 0.0875 // [INSTALL] 6.2.30
96 #define GLIDESLOPE_DDM_TO_DOTS (1 / GLIDESLOPE_DOTS_TO_DDM)
98 #define LOCALIZER_DOTS_TO_DDM 0.0775 // [INSTALL] 6.2.33
99 #define LOCALIZER_DDM_TO_DOTS (1 / LOCALIZER_DOTS_TO_DDM)
101 ///////////////////////////////////////////////////////////////////////////////
102 // helpers ////////////////////////////////////////////////////////////////////
103 ///////////////////////////////////////////////////////////////////////////////
105 #define assert_not_reached() assert(false)
106 #define n_elements(_array) (sizeof(_array) / sizeof((_array)[0]))
107 #define test_bits(_bits, _test) (((_bits) & (_test)) != 0)
109 #define mk_node(_name) (mk->properties_handler.external_properties._name)
111 #define mk_dinput_feed(_name) (mk->io_handler.input_feeders.discretes._name)
112 #define mk_dinput(_name) (mk->io_handler.inputs.discretes._name)
113 #define mk_ainput_feed(_name) (mk->io_handler.input_feeders.arinc429._name)
114 #define mk_ainput(_name) (mk->io_handler.inputs.arinc429._name)
115 #define mk_doutput(_name) (mk->io_handler.outputs.discretes._name)
116 #define mk_aoutput(_name) (mk->io_handler.outputs.arinc429._name)
117 #define mk_data(_name) (mk->io_handler.data._name)
119 #define mk_voice(_name) (mk->voice_player.voices._name)
120 #define mk_altitude_voice(_i) (mk->voice_player.voices.altitude_callouts[(_i)])
122 #define mk_alert(_name) (AlertHandler::ALERT_ ## _name)
123 #define mk_alert_flag(_name) (AlertHandler::ALERT_FLAG_ ## _name)
124 #define mk_set_alerts (mk->alert_handler.set_alerts)
125 #define mk_unset_alerts (mk->alert_handler.unset_alerts)
126 #define mk_repeat_alert (mk->alert_handler.repeat_alert)
127 #define mk_test_alert(_name) (mk_test_alerts(mk_alert(_name)))
128 #define mk_test_alerts(_test) (test_bits(mk->alert_handler.alerts, (_test)))
130 const double MK_VIII::TCFHandler::k = 0.25;
133 modify_amplitude (double amplitude, double dB)
135 return amplitude * pow(10.0, dB / 20.0);
139 heading_add (double h1, double h2)
141 double result = h1 + h2;
148 heading_substract (double h1, double h2)
150 double result = h1 - h2;
157 get_heading_difference (double h1, double h2)
159 double diff = h1 - h2;
170 get_reciprocal_heading (double h)
172 return heading_add(h, 180);
175 ///////////////////////////////////////////////////////////////////////////////
176 // PropertiesHandler //////////////////////////////////////////////////////////
177 ///////////////////////////////////////////////////////////////////////////////
180 MK_VIII::PropertiesHandler::init ()
182 mk_node(ai_caged) = fgGetNode("/instrumentation/attitude-indicator/caged-flag", true);
183 mk_node(ai_roll) = fgGetNode("/instrumentation/attitude-indicator/indicated-roll-deg", true);
184 mk_node(ai_serviceable) = fgGetNode("/instrumentation/attitude-indicator/serviceable", true);
185 mk_node(altimeter_altitude) = fgGetNode("/instrumentation/altimeter/indicated-altitude-ft", true);
186 mk_node(altimeter_serviceable) = fgGetNode("/instrumentation/altimeter/serviceable", true);
187 mk_node(altitude) = fgGetNode("/position/altitude-ft", true);
188 mk_node(altitude_agl) = fgGetNode("/position/altitude-agl-ft", true);
189 mk_node(altitude_gear_agl) = fgGetNode("/position/gear-agl-ft", true);
190 mk_node(altitude_radar_agl) = fgGetNode("/instrumentation/radar-altimeter/radar-altitude-ft", true);
191 mk_node(orientation_roll) = fgGetNode("/orientation/roll-deg", true);
192 mk_node(asi_serviceable) = fgGetNode("/instrumentation/airspeed-indicator/serviceable", true);
193 mk_node(asi_speed) = fgGetNode("/instrumentation/airspeed-indicator/indicated-speed-kt", true);
194 mk_node(autopilot_heading_lock) = fgGetNode("/autopilot/locks/heading", true);
195 mk_node(flaps) = fgGetNode("/controls/flight/flaps", true);
196 mk_node(gear_down) = fgGetNode("/controls/gear/gear-down", true);
197 mk_node(latitude) = fgGetNode("/position/latitude-deg", true);
198 mk_node(longitude) = fgGetNode("/position/longitude-deg", true);
199 mk_node(nav0_cdi_serviceable) = fgGetNode("/instrumentation/nav/cdi/serviceable", true);
200 mk_node(nav0_gs_distance) = fgGetNode("/instrumentation/nav/gs-distance", true);
201 mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection", true);
202 mk_node(nav0_gs_serviceable) = fgGetNode("/instrumentation/nav/gs/serviceable", true);
203 mk_node(nav0_has_gs) = fgGetNode("/instrumentation/nav/has-gs", true);
204 mk_node(nav0_heading_needle_deflection) = fgGetNode("/instrumentation/nav/heading-needle-deflection", true);
205 mk_node(nav0_in_range) = fgGetNode("/instrumentation/nav/in-range", true);
206 mk_node(nav0_nav_loc) = fgGetNode("/instrumentation/nav/nav-loc", true);
207 mk_node(nav0_serviceable) = fgGetNode("/instrumentation/nav/serviceable", true);
208 mk_node(power) = fgGetNode(("/systems/electrical/outputs/" + mk->name).c_str(), mk->num, true);
209 mk_node(replay_state) = fgGetNode("/sim/freeze/replay-state", true);
210 mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
214 MK_VIII::PropertiesHandler::unbind ()
216 vector<SGPropertyNode_ptr>::iterator iter;
218 for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
221 tied_properties.clear();
224 ///////////////////////////////////////////////////////////////////////////////
225 // PowerHandler ///////////////////////////////////////////////////////////////
226 ///////////////////////////////////////////////////////////////////////////////
229 MK_VIII::PowerHandler::bind (SGPropertyNode *node)
231 mk->properties_handler.tie(node, "serviceable", SGRawValuePointer<bool>(&serviceable));
235 MK_VIII::PowerHandler::handle_abnormal_voltage (bool abnormal,
241 if (timer->start_or_elapsed() >= max_duration)
242 return true; // power loss
251 MK_VIII::PowerHandler::update ()
253 double voltage = mk_node(power)->getDoubleValue();
254 bool now_powered = true;
262 if (handle_abnormal_voltage(voltage < 20.5, &low_surge_timer, 0.03))
264 if (handle_abnormal_voltage(voltage < 22.0 || voltage > 30.3, &abnormal_timer, 300))
266 if (handle_abnormal_voltage(voltage > 32.2, &high_surge_timer, 1))
268 if (handle_abnormal_voltage(voltage > 37.8, &very_high_surge_timer, 0.1))
278 power_loss_timer.stop();
281 if (power_loss_timer.start_or_elapsed() >= 0.2)
293 MK_VIII::PowerHandler::power_on ()
296 mk->system_handler.power_on();
300 MK_VIII::PowerHandler::power_off ()
303 mk->system_handler.power_off();
304 mk->voice_player.stop(VoicePlayer::STOP_NOW);
305 mk->self_test_handler.power_off(); // run before IOHandler::power_off()
306 mk->io_handler.power_off();
307 mk->mode2_handler.power_off();
308 mk->mode6_handler.power_off();
311 ///////////////////////////////////////////////////////////////////////////////
312 // SystemHandler //////////////////////////////////////////////////////////////
313 ///////////////////////////////////////////////////////////////////////////////
316 MK_VIII::SystemHandler::power_on ()
318 state = STATE_BOOTING;
320 // [SPEC] 3.5.2 mentions a 20 seconds maximum boot delay. We use a
321 // random delay between 3 and 5 seconds.
323 boot_delay = 3 + sg_random() * 2;
328 MK_VIII::SystemHandler::power_off ()
336 MK_VIII::SystemHandler::update ()
338 if (state == STATE_BOOTING)
340 if (boot_timer.elapsed() >= boot_delay)
342 last_replay_state = mk_node(replay_state)->getIntValue();
344 mk->configuration_module.boot();
346 mk->io_handler.boot();
347 mk->fault_handler.boot();
348 mk->alert_handler.boot();
350 // inputs are needed by the following boot() routines
351 mk->io_handler.update_inputs();
353 mk->mode2_handler.boot();
354 mk->mode6_handler.boot();
358 mk->io_handler.post_boot();
361 else if (state != STATE_OFF && mk->configuration_module.state == ConfigurationModule::STATE_OK)
363 // If the replay state changes, switch to reposition mode for 3
364 // seconds ([SPEC] 6.0.5) to avoid spurious alerts.
366 int replay_state = mk_node(replay_state)->getIntValue();
367 if (replay_state != last_replay_state)
369 mk->alert_handler.reposition();
370 mk->io_handler.reposition();
372 last_replay_state = replay_state;
373 state = STATE_REPOSITION;
374 reposition_timer.start();
377 if (state == STATE_REPOSITION && reposition_timer.elapsed() >= 3)
379 // inputs are needed by StateHandler::post_reposition()
380 mk->io_handler.update_inputs();
382 mk->state_handler.post_reposition();
389 ///////////////////////////////////////////////////////////////////////////////
390 // ConfigurationModule ////////////////////////////////////////////////////////
391 ///////////////////////////////////////////////////////////////////////////////
393 MK_VIII::ConfigurationModule::ConfigurationModule (MK_VIII *device)
396 // arbitrary defaults
397 categories[CATEGORY_AIRCRAFT_MODE_TYPE_SELECT] = 0;
398 categories[CATEGORY_AIR_DATA_INPUT_SELECT] = 1;
399 categories[CATEGORY_POSITION_INPUT_SELECT] = 2;
400 categories[CATEGORY_ALTITUDE_CALLOUTS] = 13;
401 categories[CATEGORY_AUDIO_MENU_SELECT] = 0;
402 categories[CATEGORY_TERRAIN_DISPLAY_SELECT] = 1;
403 categories[CATEGORY_OPTIONS_SELECT_GROUP_1] = 124;
404 categories[CATEGORY_RADIO_ALTITUDE_INPUT_SELECT] = 2;
405 categories[CATEGORY_NAVIGATION_INPUT_SELECT] = 3;
406 categories[CATEGORY_ATTITUDE_INPUT_SELECT] = 6;
407 categories[CATEGORY_HEADING_INPUT_SELECT] = 2;
408 categories[CATEGORY_WINDSHEAR_INPUT_SELECT] = 0;
409 categories[CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT] = 7;
410 categories[CATEGORY_AUDIO_OUTPUT_LEVEL] = 0;
411 categories[CATEGORY_UNDEFINED_INPUT_SELECT_1] = 0;
412 categories[CATEGORY_UNDEFINED_INPUT_SELECT_2] = 0;
413 categories[CATEGORY_UNDEFINED_INPUT_SELECT_3] = 0;
416 static double m1_t1_min_agl1 (double vs) { return -1620 - 1.1133 * vs; }
417 static double m1_t1_min_agl2 (double vs) { return -400 - 0.4 * vs; }
418 static double m1_t4_min_agl1 (double vs) { return -1625.47 - 1.1167 * vs; }
419 static double m1_t4_min_agl2 (double vs) { return -0.1958 * vs; }
421 static int m3_t1_max_agl (bool flap_override) { return 1500; }
422 static double m3_t1_max_alt_loss (bool flap_override, double agl) { return 5.4 + 0.092 * agl; }
423 static int m3_t2_max_agl (bool flap_override) { return flap_override ? 815 : 925; }
424 static double m3_t2_max_alt_loss (bool flap_override, double agl)
426 int bias = agl > 700 ? 5 : 0;
429 return (9.0 + 0.184 * agl) + bias;
431 return (5.4 + 0.092 * agl) + bias;
434 static double m4_t1_min_agl2 (double airspeed) { return -1083 + 8.333 * airspeed; }
435 static double m4_t568_a_min_agl2 (double airspeed) { return -1523 + 11.36 * airspeed; }
436 static double m4_t79_a_min_agl2 (double airspeed) { return -1182 + 11.36 * airspeed; }
437 static double m4_t568_b_min_agl2 (double airspeed) { return -1570 + 11.6 * airspeed; }
438 static double m4_t79_b_min_agl2 (double airspeed) { return -1222 + 11.6 * airspeed; }
441 MK_VIII::ConfigurationModule::m6_t2_is_bank_angle (Parameter<double> *agl,
447 if (agl->ncd || agl->get() > 122)
448 return abs_roll_deg > 33;
452 if (agl->ncd || agl->get() > 2450)
453 return abs_roll_deg > 55;
454 else if (agl->get() > 150)
455 return agl->get() < 153.33333 * abs_roll_deg - 5983.3333;
459 return agl->get() < 4 * abs_roll_deg - 10;
460 else if (agl->get() > 5)
461 return abs_roll_deg > 10;
467 MK_VIII::ConfigurationModule::m6_t4_is_bank_angle (Parameter<double> *agl,
473 if (agl->ncd || agl->get() > 156)
474 return abs_roll_deg > 33;
478 if (agl->ncd || agl->get() > 210)
479 return abs_roll_deg > 50;
483 return agl->get() < 5.7142857 * abs_roll_deg - 75.714286;
489 MK_VIII::ConfigurationModule::read_aircraft_mode_type_select (int value)
491 static const Mode1Handler::EnvelopesConfiguration m1_t1 =
492 { false, 10, m1_t1_min_agl1, 284, m1_t1_min_agl2, 2450 };
493 static const Mode1Handler::EnvelopesConfiguration m1_t4 =
494 { true, 50, m1_t4_min_agl1, 346, m1_t4_min_agl2, 1958 };
496 static const Mode2Handler::Configuration m2_t1 = { 190, 280 };
497 static const Mode2Handler::Configuration m2_t4 = { 220, 310 };
498 static const Mode2Handler::Configuration m2_t5 = { 220, 310 };
500 static const Mode3Handler::Configuration m3_t1 =
501 { 30, m3_t1_max_agl, m3_t1_max_alt_loss };
502 static const Mode3Handler::Configuration m3_t2 =
503 { 50, m3_t2_max_agl, m3_t2_max_alt_loss };
505 static const Mode4Handler::EnvelopesConfiguration m4_t1_ac =
506 { 190, 250, 500, m4_t1_min_agl2, 1000 };
507 static const Mode4Handler::EnvelopesConfiguration m4_t5_ac =
508 { 178, 200, 500, m4_t568_a_min_agl2, 1000 };
509 static const Mode4Handler::EnvelopesConfiguration m4_t68_ac =
510 { 178, 200, 500, m4_t568_a_min_agl2, 750 };
511 static const Mode4Handler::EnvelopesConfiguration m4_t79_ac =
512 { 148, 170, 500, m4_t79_a_min_agl2, 750 };
514 static const Mode4Handler::EnvelopesConfiguration m4_t1_b =
515 { 159, 250, 245, m4_t1_min_agl2, 1000 };
516 static const Mode4Handler::EnvelopesConfiguration m4_t5_b =
517 { 148, 200, 200, m4_t568_b_min_agl2, 1000 };
518 static const Mode4Handler::EnvelopesConfiguration m4_t6_b =
519 { 150, 200, 170, m4_t568_b_min_agl2, 750 };
520 static const Mode4Handler::EnvelopesConfiguration m4_t7_b =
521 { 120, 170, 170, m4_t79_b_min_agl2, 750 };
522 static const Mode4Handler::EnvelopesConfiguration m4_t8_b =
523 { 148, 200, 150, m4_t568_b_min_agl2, 750 };
524 static const Mode4Handler::EnvelopesConfiguration m4_t9_b =
525 { 118, 170, 150, m4_t79_b_min_agl2, 750 };
527 static const Mode4Handler::ModesConfiguration m4_t1 = { &m4_t1_ac, &m4_t1_b };
528 static const Mode4Handler::ModesConfiguration m4_t5 = { &m4_t5_ac, &m4_t5_b };
529 static const Mode4Handler::ModesConfiguration m4_t6 = { &m4_t68_ac, &m4_t6_b };
530 static const Mode4Handler::ModesConfiguration m4_t7 = { &m4_t79_ac, &m4_t7_b };
531 static const Mode4Handler::ModesConfiguration m4_t8 = { &m4_t68_ac, &m4_t8_b };
532 static const Mode4Handler::ModesConfiguration m4_t9 = { &m4_t79_ac, &m4_t9_b };
534 static Mode6Handler::BankAnglePredicate m6_t2 = m6_t2_is_bank_angle;
535 static Mode6Handler::BankAnglePredicate m6_t4 = m6_t4_is_bank_angle;
537 static const IOHandler::FaultsConfiguration f_slow = { 180, 200 };
538 static const IOHandler::FaultsConfiguration f_fast = { 250, 290 };
543 const Mode1Handler::EnvelopesConfiguration *m1;
544 const Mode2Handler::Configuration *m2;
545 const Mode3Handler::Configuration *m3;
546 const Mode4Handler::ModesConfiguration *m4;
547 Mode6Handler::BankAnglePredicate m6;
548 const IOHandler::FaultsConfiguration *f;
550 } aircraft_types[] = {
551 { 0, &m1_t4, &m2_t4, &m3_t2, &m4_t6, m6_t4, &f_fast, 2000 },
552 { 1, &m1_t4, &m2_t4, &m3_t2, &m4_t8, m6_t4, &f_fast, 2000 },
553 { 2, &m1_t4, &m2_t4, &m3_t2, &m4_t5, m6_t4, &f_fast, 2000 },
554 { 3, &m1_t4, &m2_t5, &m3_t2, &m4_t7, m6_t4, &f_slow, 2000 },
555 { 4, &m1_t4, &m2_t5, &m3_t2, &m4_t9, m6_t4, &f_slow, 2000 },
556 { 254, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 3500 },
557 { 255, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 2000 }
560 for (size_t i = 0; i < n_elements(aircraft_types); i++)
561 if (aircraft_types[i].type == value)
563 mk->mode1_handler.conf.envelopes = aircraft_types[i].m1;
564 mk->mode2_handler.conf = aircraft_types[i].m2;
565 mk->mode3_handler.conf = aircraft_types[i].m3;
566 mk->mode4_handler.conf.modes = aircraft_types[i].m4;
567 mk->mode6_handler.conf.is_bank_angle = aircraft_types[i].m6;
568 mk->io_handler.conf.faults = aircraft_types[i].f;
569 mk->conf.runway_database = aircraft_types[i].runway_database;
573 state = STATE_INVALID_AIRCRAFT_TYPE;
578 MK_VIII::ConfigurationModule::read_air_data_input_select (int value)
581 return (value >= 0 && value <= 6) || (value >= 10 && value <= 13) || value == 255;
585 MK_VIII::ConfigurationModule::read_position_input_select (int value)
588 mk->io_handler.conf.use_internal_gps = true;
589 else if ((value >= 0 && value <= 5)
590 || (value >= 10 && value <= 13)
593 mk->io_handler.conf.use_internal_gps = false;
601 MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
616 { 0, { MINIMUMS, SMART_500, 200, 100, 50, 40, 30, 20, 10, 0 } },
617 { 1, { MINIMUMS, SMART_500, 200, 0 } },
618 { 2, { MINIMUMS, SMART_500, 100, 50, 40, 30, 20, 10, 0 } },
619 { 3, { MINIMUMS, SMART_500, 0 } },
620 { 4, { MINIMUMS, 200, 100, 50, 40, 30, 20, 10, 0 } },
621 { 5, { MINIMUMS, 200, 0 } },
622 { 6, { MINIMUMS, 100, 50, 40, 30, 20, 10, 0 } },
624 { 8, { MINIMUMS, 0 } },
625 { 9, { MINIMUMS, 500, 200, 100, 50, 40, 30, 20, 10, 0 } },
626 { 10, { MINIMUMS, 500, 200, 0 } },
627 { 11, { MINIMUMS, 500, 100, 50, 40, 30, 20, 10, 0 } },
628 { 12, { MINIMUMS, 500, 0 } },
629 { 13, { MINIMUMS, 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10, 0 } },
630 { 14, { MINIMUMS, 100, 0 } },
631 { 15, { MINIMUMS, 200, 100, 0 } },
632 { 100, { FIELD_500, 0 } },
633 { 101, { FIELD_500_ABOVE, 0 } }
638 mk->mode6_handler.conf.minimums_enabled = false;
639 mk->mode6_handler.conf.smart_500_enabled = false;
640 mk->mode6_handler.conf.above_field_voice = NULL;
641 for (i = 0; i < n_altitude_callouts; i++)
642 mk->mode6_handler.conf.altitude_callouts_enabled[i] = false;
644 for (i = 0; i < n_elements(values); i++)
645 if (values[i].id == value)
647 for (int j = 0; values[i].callouts[j] != 0; j++)
648 switch (values[i].callouts[j])
651 mk->mode6_handler.conf.minimums_enabled = true;
655 mk->mode6_handler.conf.smart_500_enabled = true;
659 mk->mode6_handler.conf.above_field_voice = mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500);
662 case FIELD_500_ABOVE:
663 mk->mode6_handler.conf.above_field_voice = mk_voice(five_hundred_above);
667 for (unsigned k = 0; k < n_altitude_callouts; k++)
668 if (mk->mode6_handler.altitude_callout_definitions[k] == values[i].callouts[j])
669 mk->mode6_handler.conf.altitude_callouts_enabled[k] = true;
680 MK_VIII::ConfigurationModule::read_audio_menu_select (int value)
682 if (value == 0 || value == 1)
683 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_gear);
684 else if (value == 2 || value == 3)
685 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_flaps);
693 MK_VIII::ConfigurationModule::read_terrain_display_select (int value)
696 mk->tcf_handler.conf.enabled = false;
697 else if (value == 0 || value == 1 || (value >= 3 && value <= 15)
698 || (value >= 18 && value <= 20) || (value >= 235 && value <= 255))
699 mk->tcf_handler.conf.enabled = true;
707 MK_VIII::ConfigurationModule::read_options_select_group_1 (int value)
709 if (value >= 0 && value < 128)
711 mk->io_handler.conf.flap_reversal = test_bits(value, 1 << 1);
712 mk->mode6_handler.conf.bank_angle_enabled = test_bits(value, 1 << 2);
713 mk->io_handler.conf.steep_approach_enabled = test_bits(value, 1 << 6);
721 MK_VIII::ConfigurationModule::read_radio_altitude_input_select (int value)
723 mk->io_handler.conf.altitude_source = value;
724 return (value >= 0 && value <= 4) || (value >= 251 && value <= 255);
728 MK_VIII::ConfigurationModule::read_navigation_input_select (int value)
730 if (value >= 0 && value <= 2)
731 mk->io_handler.conf.localizer_enabled = false;
732 else if ((value >= 3 && value <= 5) || (value >= 250 && value <= 255))
733 mk->io_handler.conf.localizer_enabled = true;
741 MK_VIII::ConfigurationModule::read_attitude_input_select (int value)
744 mk->io_handler.conf.use_attitude_indicator=true;
746 mk->io_handler.conf.use_attitude_indicator=false;
747 return (value >= 0 && value <= 6) || value == 253 || value == 255;
751 MK_VIII::ConfigurationModule::read_heading_input_select (int value)
754 return (value >= 0 && value <= 3) || value == 254 || value == 255;
758 MK_VIII::ConfigurationModule::read_windshear_input_select (int value)
761 return value == 0 || (value >= 253 && value <= 255);
765 MK_VIII::ConfigurationModule::read_input_output_discrete_type_select (int value)
770 IOHandler::LampConfiguration lamp_conf;
771 bool gpws_inhibit_enabled;
772 bool momentary_flap_override_enabled;
773 bool alternate_steep_approach;
775 { 0, { false, false }, false, true, true },
776 { 1, { true, false }, false, true, true },
777 { 2, { false, false }, true, true, true },
778 { 3, { true, false }, true, true, true },
779 { 4, { false, true }, true, true, true },
780 { 5, { true, true }, true, true, true },
781 { 6, { false, false }, true, true, false },
782 { 7, { true, false }, true, true, false },
783 { 254, { false, false }, true, false, true },
784 { 255, { false, false }, true, false, true }
787 for (size_t i = 0; i < n_elements(io_types); i++)
788 if (io_types[i].type == value)
790 mk->io_handler.conf.lamp = &io_types[i].lamp_conf;
791 mk->io_handler.conf.gpws_inhibit_enabled = io_types[i].gpws_inhibit_enabled;
792 mk->io_handler.conf.momentary_flap_override_enabled = io_types[i].momentary_flap_override_enabled;
793 mk->io_handler.conf.alternate_steep_approach = io_types[i].alternate_steep_approach;
801 MK_VIII::ConfigurationModule::read_audio_output_level (int value)
815 for (size_t i = 0; i < n_elements(values); i++)
816 if (values[i].id == value)
818 mk->voice_player.set_volume(mk->voice_player.conf.volume = modify_amplitude(1.0, values[i].relative_dB));
822 // The self test needs the voice player even when the configuration
823 // is invalid, so set a default value.
824 mk->voice_player.set_volume(mk->voice_player.conf.volume = 1.0);
829 MK_VIII::ConfigurationModule::read_undefined_input_select (int value)
836 MK_VIII::ConfigurationModule::boot ()
838 bool (MK_VIII::ConfigurationModule::*readers[N_CATEGORIES]) (int) = {
839 &MK_VIII::ConfigurationModule::read_aircraft_mode_type_select,
840 &MK_VIII::ConfigurationModule::read_air_data_input_select,
841 &MK_VIII::ConfigurationModule::read_position_input_select,
842 &MK_VIII::ConfigurationModule::read_altitude_callouts,
843 &MK_VIII::ConfigurationModule::read_audio_menu_select,
844 &MK_VIII::ConfigurationModule::read_terrain_display_select,
845 &MK_VIII::ConfigurationModule::read_options_select_group_1,
846 &MK_VIII::ConfigurationModule::read_radio_altitude_input_select,
847 &MK_VIII::ConfigurationModule::read_navigation_input_select,
848 &MK_VIII::ConfigurationModule::read_attitude_input_select,
849 &MK_VIII::ConfigurationModule::read_heading_input_select,
850 &MK_VIII::ConfigurationModule::read_windshear_input_select,
851 &MK_VIII::ConfigurationModule::read_input_output_discrete_type_select,
852 &MK_VIII::ConfigurationModule::read_audio_output_level,
853 &MK_VIII::ConfigurationModule::read_undefined_input_select,
854 &MK_VIII::ConfigurationModule::read_undefined_input_select,
855 &MK_VIII::ConfigurationModule::read_undefined_input_select
858 memcpy(effective_categories, categories, sizeof(categories));
861 for (int i = 0; i < N_CATEGORIES; i++)
862 if (! (this->*readers[i])(effective_categories[i]))
864 SG_LOG(SG_INSTR, SG_ALERT, "MK VIII EGPWS configuration category " << i + 1 << ": invalid value " << effective_categories[i]);
866 if (state == STATE_OK)
867 state = STATE_INVALID_DATABASE;
869 mk_doutput(gpws_inop) = true;
870 mk_doutput(tad_inop) = true;
877 if (mk->mode6_handler.conf.above_field_voice && ! mk->tcf_handler.conf.enabled)
880 SG_LOG(SG_INSTR, SG_ALERT, "MK VIII EGPWS configuration module: when category 4 is set to 100 or 101, category 6 must not be set to 2");
881 state = STATE_INVALID_DATABASE;
886 MK_VIII::ConfigurationModule::bind (SGPropertyNode *node)
888 for (int i = 0; i < N_CATEGORIES; i++)
890 std::ostringstream name;
891 name << "configuration-module/category-" << i + 1;
892 mk->properties_handler.tie(node, name.str().c_str(), SGRawValuePointer<int>(&categories[i]));
896 ///////////////////////////////////////////////////////////////////////////////
897 // FaultHandler ///////////////////////////////////////////////////////////////
898 ///////////////////////////////////////////////////////////////////////////////
900 // [INSTALL] only specifies that the faults cause a GPWS INOP
901 // indication. We arbitrarily set a TAD INOP when it makes sense.
902 const unsigned int MK_VIII::FaultHandler::fault_inops[] = {
903 INOP_GPWS | INOP_TAD, // [INSTALL] 3.15.1.3
904 INOP_GPWS, // [INSTALL] appendix E 6.6.2
905 INOP_GPWS, // [INSTALL] appendix E 6.6.4
906 INOP_GPWS, // [INSTALL] appendix E 6.6.6
907 INOP_GPWS | INOP_TAD, // [INSTALL] appendix E 6.6.7
908 INOP_GPWS, // [INSTALL] appendix E 6.6.13
909 INOP_GPWS, // [INSTALL] appendix E 6.6.25
910 INOP_GPWS, // [INSTALL] appendix E 6.6.27
911 INOP_TAD, // unspecified
912 INOP_GPWS, // unspecified
913 INOP_GPWS, // unspecified
914 // [INSTALL] 2.3.10.1 specifies that GPWS INOP is "activated during
915 // any detected partial or total failure of the GPWS modes 1-5", so
916 // do not set any INOP for mode 6 and bank angle.
919 INOP_TAD // unspecified
923 MK_VIII::FaultHandler::has_faults () const
925 for (int i = 0; i < N_FAULTS; i++)
933 MK_VIII::FaultHandler::has_faults (unsigned int inop)
935 for (int i = 0; i < N_FAULTS; i++)
936 if (faults[i] && test_bits(fault_inops[i], inop))
943 MK_VIII::FaultHandler::boot ()
945 memset(faults, 0, sizeof(faults));
949 MK_VIII::FaultHandler::set_fault (Fault fault)
953 faults[fault] = true;
955 mk->self_test_handler.set_inop();
957 if (test_bits(fault_inops[fault], INOP_GPWS))
959 mk_unset_alerts(~mk_alert(TCF_TOO_LOW_TERRAIN));
960 mk_doutput(gpws_inop) = true;
962 if (test_bits(fault_inops[fault], INOP_TAD))
964 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
965 mk_doutput(tad_inop) = true;
971 MK_VIII::FaultHandler::unset_fault (Fault fault)
975 faults[fault] = false;
976 if (! has_faults(INOP_GPWS))
977 mk_doutput(gpws_inop) = false;
978 if (! has_faults(INOP_TAD))
979 mk_doutput(tad_inop) = false;
983 ///////////////////////////////////////////////////////////////////////////////
984 // IOHandler //////////////////////////////////////////////////////////////////
985 ///////////////////////////////////////////////////////////////////////////////
988 MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
990 // [PILOT] page 20 specifies that the terrain clearance is equal to
991 // 75% of the radio altitude, averaged over the previous 15 seconds.
993 // no updates when simulation is paused (dt=0.0), and add 5 samples/second only
994 if (globals->get_sim_time_sec() - last_update < 0.2)
996 last_update = globals->get_sim_time_sec();
998 samples_type::iterator iter;
1000 // remove samples older than 15 seconds
1001 for (iter = samples.begin(); iter != samples.end() && globals->get_sim_time_sec() - (*iter).timestamp >= 15; iter = samples.begin())
1002 samples.erase(iter);
1004 // append new sample
1005 samples.push_back(Sample<double>(agl));
1007 // calculate average
1008 double new_value = 0;
1009 if (samples.size() > 0)
1011 // time consuming loop => queue limited to 75 samples
1012 // (= 15seconds * 5samples/second)
1013 for (iter = samples.begin(); iter != samples.end(); iter++)
1014 new_value += (*iter).value;
1015 new_value /= samples.size();
1019 if (new_value > value)
1026 MK_VIII::IOHandler::TerrainClearanceFilter::reset ()
1033 MK_VIII::IOHandler::IOHandler (MK_VIII *device)
1034 : mk(device), _lamp(LAMP_NONE)
1036 memset(&input_feeders, 0, sizeof(input_feeders));
1037 memset(&inputs.discretes, 0, sizeof(inputs.discretes));
1038 memset(&outputs, 0, sizeof(outputs));
1039 memset(&power_saved, 0, sizeof(power_saved));
1041 mk_dinput_feed(landing_gear) = true;
1042 mk_dinput_feed(landing_flaps) = true;
1043 mk_dinput_feed(glideslope_inhibit) = true;
1044 mk_dinput_feed(decision_height) = true;
1045 mk_dinput_feed(autopilot_engaged) = true;
1046 mk_ainput_feed(uncorrected_barometric_altitude) = true;
1047 mk_ainput_feed(barometric_altitude_rate) = true;
1048 mk_ainput_feed(radio_altitude) = true;
1049 mk_ainput_feed(glideslope_deviation) = true;
1050 mk_ainput_feed(roll_angle) = true;
1051 mk_ainput_feed(localizer_deviation) = true;
1052 mk_ainput_feed(computed_airspeed) = true;
1054 // will be unset on power on
1055 mk_doutput(gpws_inop) = true;
1056 mk_doutput(tad_inop) = true;
1060 MK_VIII::IOHandler::boot ()
1062 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1065 mk_doutput(gpws_inop) = false;
1066 mk_doutput(tad_inop) = false;
1068 mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
1070 altitude_samples.clear();
1071 reset_terrain_clearance();
1075 MK_VIII::IOHandler::post_boot ()
1077 if (momentary_steep_approach_enabled())
1079 last_landing_gear = mk_dinput(landing_gear);
1080 last_real_flaps_down = real_flaps_down();
1083 // read externally-latching input discretes
1084 update_alternate_discrete_input(&mk_dinput(mode6_low_volume));
1085 update_alternate_discrete_input(&mk_dinput(audio_inhibit));
1089 MK_VIII::IOHandler::power_off ()
1091 power_saved.glideslope_cancel = mk_doutput(glideslope_cancel); // [SPEC] 6.2.5
1093 memset(&outputs, 0, sizeof(outputs));
1095 audio_inhibit_fault_timer.stop();
1096 landing_gear_fault_timer.stop();
1097 flaps_down_fault_timer.stop();
1098 momentary_flap_override_fault_timer.stop();
1099 self_test_fault_timer.stop();
1100 glideslope_cancel_fault_timer.stop();
1101 steep_approach_fault_timer.stop();
1102 gpws_inhibit_fault_timer.stop();
1103 ta_tcf_inhibit_fault_timer.stop();
1106 mk_doutput(gpws_inop) = true;
1107 mk_doutput(tad_inop) = true;
1111 MK_VIII::IOHandler::enter_ground ()
1113 reset_terrain_clearance();
1115 if (conf.momentary_flap_override_enabled)
1116 mk_doutput(flap_override) = false; // [INSTALL] 3.15.1.6
1120 MK_VIII::IOHandler::enter_takeoff ()
1122 reset_terrain_clearance();
1124 if (momentary_steep_approach_enabled())
1125 // landing or go-around, disable steep approach as per [SPEC] 6.2.1
1126 mk_doutput(steep_approach) = false;
1130 MK_VIII::IOHandler::update_inputs ()
1132 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1135 // 1. process input feeders
1137 if (mk_dinput_feed(landing_gear))
1138 mk_dinput(landing_gear) = mk_node(gear_down)->getBoolValue();
1139 if (mk_dinput_feed(landing_flaps))
1140 mk_dinput(landing_flaps) = mk_node(flaps)->getDoubleValue() < 1;
1141 if (mk_dinput_feed(glideslope_inhibit))
1142 mk_dinput(glideslope_inhibit) = mk_node(nav0_gs_distance)->getDoubleValue() < 0;
1143 if (mk_dinput_feed(autopilot_engaged))
1147 mode = mk_node(autopilot_heading_lock)->getStringValue();
1148 mk_dinput(autopilot_engaged) = mode && *mode;
1151 if (mk_ainput_feed(uncorrected_barometric_altitude))
1153 if (mk_node(altimeter_serviceable)->getBoolValue())
1154 mk_ainput(uncorrected_barometric_altitude).set(mk_node(altimeter_altitude)->getDoubleValue());
1156 mk_ainput(uncorrected_barometric_altitude).unset();
1158 if (mk_ainput_feed(barometric_altitude_rate))
1159 // Do not use the vsi, because it is pressure-based only, and
1160 // therefore too laggy for GPWS alerting purposes. I guess that
1161 // a real ADC combines pressure-based and inerta-based altitude
1162 // rates to provide a non-laggy rate. That non-laggy rate is
1163 // best emulated by /velocities/vertical-speed-fps * 60.
1164 mk_ainput(barometric_altitude_rate).set(mk_node(vs)->getDoubleValue() * 60);
1165 if (mk_ainput_feed(radio_altitude))
1168 switch (conf.altitude_source)
1171 agl = mk_node(altitude_gear_agl)->getDoubleValue();
1174 agl = mk_node(altitude_radar_agl)->getDoubleValue();
1176 default: // 0,1,2 (and any currently unsupported values)
1177 agl = mk_node(altitude_agl)->getDoubleValue();
1180 // Some flight models may return negative values when on the
1181 // ground or after a crash; do not allow them.
1182 mk_ainput(radio_altitude).set(SG_MAX2(0.0, agl));
1184 if (mk_ainput_feed(glideslope_deviation))
1186 if (mk_node(nav0_serviceable)->getBoolValue()
1187 && mk_node(nav0_gs_serviceable)->getBoolValue()
1188 && mk_node(nav0_in_range)->getBoolValue()
1189 && mk_node(nav0_has_gs)->getBoolValue())
1190 // gs-needle-deflection is expressed in degrees, and 1 dot =
1191 // 0.32 degrees (according to
1192 // http://www.ntsb.gov/Recs/letters/2000/A00_41_45.pdf)
1193 mk_ainput(glideslope_deviation).set(mk_node(nav0_gs_needle_deflection)->getDoubleValue() / 0.32 * GLIDESLOPE_DOTS_TO_DDM);
1195 mk_ainput(glideslope_deviation).unset();
1197 if (mk_ainput_feed(roll_angle))
1199 if (conf.use_attitude_indicator)
1201 // read data from attitude indicator instrument (requires vacuum system to work)
1202 if (mk_node(ai_serviceable)->getBoolValue() && ! mk_node(ai_caged)->getBoolValue())
1203 mk_ainput(roll_angle).set(mk_node(ai_roll)->getDoubleValue());
1205 mk_ainput(roll_angle).unset();
1209 // use simulator source
1210 mk_ainput(roll_angle).set(mk_node(orientation_roll)->getDoubleValue());
1213 if (mk_ainput_feed(localizer_deviation))
1215 if (mk_node(nav0_serviceable)->getBoolValue()
1216 && mk_node(nav0_cdi_serviceable)->getBoolValue()
1217 && mk_node(nav0_in_range)->getBoolValue()
1218 && mk_node(nav0_nav_loc)->getBoolValue())
1219 // heading-needle-deflection is expressed in degrees, and 1
1220 // dot = 2 degrees (0.5 degrees for ILS, but navradio.cxx
1221 // performs the conversion)
1222 mk_ainput(localizer_deviation).set(mk_node(nav0_heading_needle_deflection)->getDoubleValue() / 2 * LOCALIZER_DOTS_TO_DDM);
1224 mk_ainput(localizer_deviation).unset();
1226 if (mk_ainput_feed(computed_airspeed))
1228 if (mk_node(asi_serviceable)->getBoolValue())
1229 mk_ainput(computed_airspeed).set(mk_node(asi_speed)->getDoubleValue());
1231 mk_ainput(computed_airspeed).unset();
1236 mk_data(decision_height).set(&mk_ainput(decision_height));
1237 mk_data(radio_altitude).set(&mk_ainput(radio_altitude));
1238 mk_data(roll_angle).set(&mk_ainput(roll_angle));
1240 // update barometric_altitude_rate as per [SPEC] 6.2.1: "During
1241 // normal conditions, the system will base Mode 1 computations upon
1242 // barometric rate from the ADC. If this computed data is not valid
1243 // or available then the system will use internally computed
1244 // barometric altitude rate."
1246 if (! mk_ainput(barometric_altitude_rate).ncd)
1247 // the altitude rate input is valid, use it
1248 mk_data(barometric_altitude_rate).set(mk_ainput(barometric_altitude_rate).get());
1253 // The altitude rate input is invalid. We can compute an
1254 // altitude rate if all the following conditions are true:
1256 // - the altitude input is valid
1257 // - there is an altitude sample with age >= 1 second
1258 // - that altitude sample is valid
1260 if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1262 if (! altitude_samples.empty() && ! altitude_samples.front().value.ncd)
1264 double elapsed = globals->get_sim_time_sec() - altitude_samples.front().timestamp;
1268 mk_data(barometric_altitude_rate).set((mk_ainput(uncorrected_barometric_altitude).get() - altitude_samples.front().value.get()) / elapsed * 60);
1274 mk_data(barometric_altitude_rate).unset();
1277 altitude_samples.push_back(Sample< Parameter<double> >(mk_ainput(uncorrected_barometric_altitude)));
1279 // Erase everything from the beginning of the list up to the sample
1280 // preceding the most recent sample whose age is >= 1 second.
1282 altitude_samples_type::iterator erase_last = altitude_samples.begin();
1283 altitude_samples_type::iterator iter;
1285 for (iter = altitude_samples.begin(); iter != altitude_samples.end(); iter++)
1286 if (globals->get_sim_time_sec() - (*iter).timestamp >= 1)
1291 if (erase_last != altitude_samples.begin())
1292 altitude_samples.erase(altitude_samples.begin(), erase_last);
1296 if (conf.use_internal_gps)
1298 mk_data(gps_altitude).set(mk_node(altitude)->getDoubleValue());
1299 mk_data(gps_latitude).set(mk_node(latitude)->getDoubleValue());
1300 mk_data(gps_longitude).set(mk_node(longitude)->getDoubleValue());
1301 mk_data(gps_vertical_figure_of_merit).set(0.0);
1305 mk_data(gps_altitude).set(&mk_ainput(gps_altitude));
1306 mk_data(gps_latitude).set(&mk_ainput(gps_latitude));
1307 mk_data(gps_longitude).set(&mk_ainput(gps_longitude));
1308 mk_data(gps_vertical_figure_of_merit).set(&mk_ainput(gps_vertical_figure_of_merit));
1311 // update glideslope and localizer
1313 mk_data(glideslope_deviation_dots).set(&mk_ainput(glideslope_deviation), GLIDESLOPE_DDM_TO_DOTS);
1314 mk_data(localizer_deviation_dots).set(&mk_ainput(localizer_deviation), LOCALIZER_DDM_TO_DOTS);
1316 // Update geometric altitude; [SPEC] 6.7.8 provides an overview of a
1317 // complex algorithm which combines several input sources to
1318 // calculate the geometric altitude. Since the exact algorithm is
1319 // not given, we fallback to a much simpler method.
1321 if (! mk_data(gps_altitude).ncd)
1322 mk_data(geometric_altitude).set(mk_data(gps_altitude).get());
1323 else if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1324 mk_data(geometric_altitude).set(mk_ainput(uncorrected_barometric_altitude).get());
1326 mk_data(geometric_altitude).unset();
1328 // update terrain clearance
1330 update_terrain_clearance();
1332 // 3. perform sanity checks
1334 if (! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 0)
1335 mk_data(radio_altitude).unset();
1337 if (! mk_data(decision_height).ncd && mk_data(decision_height).get() < 0)
1338 mk_data(decision_height).unset();
1340 if (! mk_data(gps_latitude).ncd
1341 && (mk_data(gps_latitude).get() < -90
1342 || mk_data(gps_latitude).get() > 90))
1343 mk_data(gps_latitude).unset();
1345 if (! mk_data(gps_longitude).ncd
1346 && (mk_data(gps_longitude).get() < -180
1347 || mk_data(gps_longitude).get() > 180))
1348 mk_data(gps_longitude).unset();
1350 if (! mk_data(roll_angle).ncd
1351 && ((mk_data(roll_angle).get() < -180)
1352 || (mk_data(roll_angle).get() > 180)))
1353 mk_data(roll_angle).unset();
1355 // 4. process input feeders requiring data computed above
1357 if (mk_dinput_feed(decision_height))
1358 mk_dinput(decision_height) = ! mk_data(radio_altitude).ncd
1359 && ! mk_data(decision_height).ncd
1360 && mk_data(radio_altitude).get() <= mk_data(decision_height).get();
1364 MK_VIII::IOHandler::update_terrain_clearance ()
1366 if (! mk_data(radio_altitude).ncd)
1367 mk_data(terrain_clearance).set(terrain_clearance_filter.update(mk_data(radio_altitude).get()));
1369 mk_data(terrain_clearance).unset();
1373 MK_VIII::IOHandler::reset_terrain_clearance ()
1375 terrain_clearance_filter.reset();
1376 update_terrain_clearance();
1380 MK_VIII::IOHandler::reposition ()
1382 reset_terrain_clearance();
1386 MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
1390 if (! mk->fault_handler.faults[fault])
1391 mk->fault_handler.set_fault(fault);
1394 mk->fault_handler.unset_fault(fault);
1398 MK_VIII::IOHandler::handle_input_fault (bool test,
1400 double max_duration,
1401 FaultHandler::Fault fault)
1405 if (! mk->fault_handler.faults[fault])
1407 if (timer->start_or_elapsed() >= max_duration)
1408 mk->fault_handler.set_fault(fault);
1413 mk->fault_handler.unset_fault(fault);
1419 MK_VIII::IOHandler::update_input_faults ()
1421 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1424 // [INSTALL] 3.15.1.3
1425 handle_input_fault(mk_dinput(audio_inhibit),
1426 &audio_inhibit_fault_timer,
1428 FaultHandler::FAULT_ALL_MODES_INHIBIT);
1430 // [INSTALL] appendix E 6.6.2
1431 handle_input_fault(mk_dinput(landing_gear)
1432 && ! mk_ainput(computed_airspeed).ncd
1433 && mk_ainput(computed_airspeed).get() > conf.faults->max_gear_down_airspeed,
1434 &landing_gear_fault_timer,
1436 FaultHandler::FAULT_GEAR_SWITCH);
1438 // [INSTALL] appendix E 6.6.4
1439 handle_input_fault(flaps_down()
1440 && ! mk_ainput(computed_airspeed).ncd
1441 && mk_ainput(computed_airspeed).get() > conf.faults->max_flaps_down_airspeed,
1442 &flaps_down_fault_timer,
1444 FaultHandler::FAULT_FLAPS_SWITCH);
1446 // [INSTALL] appendix E 6.6.6
1447 if (conf.momentary_flap_override_enabled)
1448 handle_input_fault(mk_dinput(momentary_flap_override),
1449 &momentary_flap_override_fault_timer,
1451 FaultHandler::FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID);
1453 // [INSTALL] appendix E 6.6.7
1454 handle_input_fault(mk_dinput(self_test),
1455 &self_test_fault_timer,
1457 FaultHandler::FAULT_SELF_TEST_INVALID);
1459 // [INSTALL] appendix E 6.6.13
1460 handle_input_fault(mk_dinput(glideslope_cancel),
1461 &glideslope_cancel_fault_timer,
1463 FaultHandler::FAULT_GLIDESLOPE_CANCEL_INVALID);
1465 // [INSTALL] appendix E 6.6.25
1466 if (momentary_steep_approach_enabled())
1467 handle_input_fault(mk_dinput(steep_approach),
1468 &steep_approach_fault_timer,
1470 FaultHandler::FAULT_STEEP_APPROACH_INVALID);
1472 // [INSTALL] appendix E 6.6.27
1473 if (conf.gpws_inhibit_enabled)
1474 handle_input_fault(mk_dinput(gpws_inhibit),
1475 &gpws_inhibit_fault_timer,
1477 FaultHandler::FAULT_GPWS_INHIBIT);
1479 // [INSTALL] does not specify a fault for this one, but it makes
1480 // sense to have it behave like GPWS inhibit
1481 handle_input_fault(mk_dinput(ta_tcf_inhibit),
1482 &ta_tcf_inhibit_fault_timer,
1484 FaultHandler::FAULT_TA_TCF_INHIBIT);
1486 // [PILOT] page 48: "In the event that required data for a
1487 // particular function is not available, then that function is
1488 // automatically inhibited and annunciated"
1490 handle_input_fault(mk_data(radio_altitude).ncd
1491 || mk_ainput(uncorrected_barometric_altitude).ncd
1492 || mk_data(barometric_altitude_rate).ncd
1493 || mk_ainput(computed_airspeed).ncd
1494 || mk_data(terrain_clearance).ncd,
1495 FaultHandler::FAULT_MODES14_INPUTS_INVALID);
1497 if (! mk_dinput(glideslope_inhibit))
1498 handle_input_fault(mk_data(radio_altitude).ncd,
1499 FaultHandler::FAULT_MODE5_INPUTS_INVALID);
1501 if (mk->mode6_handler.altitude_callouts_enabled())
1502 handle_input_fault(mk->mode6_handler.conf.above_field_voice
1503 ? (mk_data(gps_latitude).ncd
1504 || mk_data(gps_longitude).ncd
1505 || mk_data(geometric_altitude).ncd)
1506 : mk_data(radio_altitude).ncd,
1507 FaultHandler::FAULT_MODE6_INPUTS_INVALID);
1509 if (mk->mode6_handler.conf.bank_angle_enabled)
1510 handle_input_fault(mk_data(roll_angle).ncd,
1511 FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID);
1513 if (mk->tcf_handler.conf.enabled)
1514 handle_input_fault(mk_data(radio_altitude).ncd
1515 || mk_data(geometric_altitude).ncd
1516 || mk_data(gps_latitude).ncd
1517 || mk_data(gps_longitude).ncd
1518 || mk_data(gps_vertical_figure_of_merit).ncd,
1519 FaultHandler::FAULT_TCF_INPUTS_INVALID);
1523 MK_VIII::IOHandler::update_alternate_discrete_input (bool *ptr)
1525 assert(mk->system_handler.state == SystemHandler::STATE_ON);
1527 if (ptr == &mk_dinput(mode6_low_volume))
1529 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1530 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1531 mk->mode6_handler.set_volume(*ptr ? modify_amplitude(1.0, -6) : 1.0);
1533 else if (ptr == &mk_dinput(audio_inhibit))
1535 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1536 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1537 mk->voice_player.set_volume(*ptr ? 0.0 : mk->voice_player.conf.volume);
1542 MK_VIII::IOHandler::update_internal_latches ()
1544 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1548 if (conf.momentary_flap_override_enabled
1549 && mk_doutput(flap_override)
1550 && ! mk->state_handler.takeoff
1551 && (mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() <= 50))
1552 mk_doutput(flap_override) = false;
1555 if (momentary_steep_approach_enabled())
1557 if (mk_doutput(steep_approach)
1558 && ! mk->state_handler.takeoff
1559 && ((last_landing_gear && ! mk_dinput(landing_gear))
1560 || (last_real_flaps_down && ! real_flaps_down())))
1561 // gear or flaps raised during approach: it's a go-around
1562 mk_doutput(steep_approach) = false;
1564 last_landing_gear = mk_dinput(landing_gear);
1565 last_real_flaps_down = real_flaps_down();
1569 if (mk_doutput(glideslope_cancel)
1570 && (mk_data(glideslope_deviation_dots).ncd
1571 || mk_data(radio_altitude).ncd
1572 || mk_data(radio_altitude).get() > 2000
1573 || mk_data(radio_altitude).get() < 30))
1574 mk_doutput(glideslope_cancel) = false;
1578 MK_VIII::IOHandler::update_egpws_alert_discrete_1 ()
1580 if (mk->voice_player.voice)
1585 VoicePlayer::Voice *voice;
1587 { 11, mk_voice(sink_rate_pause_sink_rate) },
1588 { 12, mk_voice(pull_up) },
1589 { 13, mk_voice(terrain) },
1590 { 13, mk_voice(terrain_pause_terrain) },
1591 { 14, mk_voice(dont_sink_pause_dont_sink) },
1592 { 15, mk_voice(too_low_gear) },
1593 { 16, mk_voice(too_low_flaps) },
1594 { 17, mk_voice(too_low_terrain) },
1595 { 18, mk_voice(soft_glideslope) },
1596 { 18, mk_voice(hard_glideslope) },
1597 { 19, mk_voice(minimums_minimums) }
1600 for (size_t i = 0; i < n_elements(voices); i++)
1601 if (voices[i].voice == mk->voice_player.voice)
1603 mk_aoutput(egpws_alert_discrete_1) = 1 << voices[i].bit;
1608 mk_aoutput(egpws_alert_discrete_1) = 0;
1612 MK_VIII::IOHandler::update_egpwc_logic_discretes ()
1614 mk_aoutput(egpwc_logic_discretes) = 0;
1616 if (mk->state_handler.takeoff)
1617 mk_aoutput(egpwc_logic_discretes) |= 1 << 18;
1622 unsigned int alerts;
1624 { 13, mk_alert(TCF_TOO_LOW_TERRAIN) },
1625 { 19, mk_alert(MODE1_SINK_RATE) },
1626 { 20, mk_alert(MODE1_PULL_UP) },
1627 { 21, mk_alert(MODE2A_PREFACE) | mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B_LANDING_MODE) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING) },
1628 { 22, mk_alert(MODE2A) | mk_alert(MODE2B) },
1629 { 23, mk_alert(MODE3) },
1630 { 24, mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR) | mk_alert(MODE4AB_TOO_LOW_TERRAIN) | mk_alert(MODE4C_TOO_LOW_TERRAIN) },
1631 { 25, mk_alert(MODE5_SOFT) | mk_alert(MODE5_HARD) }
1634 for (size_t i = 0; i < n_elements(logic); i++)
1635 if (mk_test_alerts(logic[i].alerts))
1636 mk_aoutput(egpwc_logic_discretes) |= 1 << logic[i].bit;
1640 MK_VIII::IOHandler::update_mode6_callouts_discrete_1 ()
1642 if (mk->voice_player.voice)
1647 VoicePlayer::Voice *voice;
1649 { 11, mk_voice(minimums_minimums) },
1650 { 16, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_10) },
1651 { 17, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_20) },
1652 { 18, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_30) },
1653 { 19, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_40) },
1654 { 20, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_50) },
1655 { 23, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_100) },
1656 { 24, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_200) },
1657 { 25, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_300) }
1660 for (size_t i = 0; i < n_elements(voices); i++)
1661 if (voices[i].voice == mk->voice_player.voice)
1663 mk_aoutput(mode6_callouts_discrete_1) = 1 << voices[i].bit;
1668 mk_aoutput(mode6_callouts_discrete_1) = 0;
1672 MK_VIII::IOHandler::update_mode6_callouts_discrete_2 ()
1674 if (mk->voice_player.voice)
1679 VoicePlayer::Voice *voice;
1681 { 11, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_400) },
1682 { 12, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500) },
1683 { 13, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_1000) },
1684 { 18, mk_voice(bank_angle_pause_bank_angle) },
1685 { 18, mk_voice(bank_angle_pause_bank_angle_3) },
1686 { 23, mk_voice(five_hundred_above) }
1689 for (size_t i = 0; i < n_elements(voices); i++)
1690 if (voices[i].voice == mk->voice_player.voice)
1692 mk_aoutput(mode6_callouts_discrete_2) = 1 << voices[i].bit;
1697 mk_aoutput(mode6_callouts_discrete_2) = 0;
1701 MK_VIII::IOHandler::update_egpws_alert_discrete_2 ()
1703 mk_aoutput(egpws_alert_discrete_2) = 1 << 27; // Terrain NA
1705 if (mk_doutput(glideslope_cancel))
1706 mk_aoutput(egpws_alert_discrete_2) |= 1 << 11;
1707 if (mk_doutput(gpws_alert))
1708 mk_aoutput(egpws_alert_discrete_2) |= 1 << 12;
1709 if (mk_doutput(gpws_warning))
1710 mk_aoutput(egpws_alert_discrete_2) |= 1 << 13;
1711 if (mk_doutput(gpws_inop))
1712 mk_aoutput(egpws_alert_discrete_2) |= 1 << 14;
1713 if (mk_doutput(audio_on))
1714 mk_aoutput(egpws_alert_discrete_2) |= 1 << 16;
1715 if (mk_doutput(tad_inop))
1716 mk_aoutput(egpws_alert_discrete_2) |= 1 << 24;
1717 if (mk->fault_handler.has_faults())
1718 mk_aoutput(egpws_alert_discrete_2) |= 1 << 25;
1722 MK_VIII::IOHandler::update_egpwc_alert_discrete_3 ()
1724 mk_aoutput(egpwc_alert_discrete_3) = 1 << 17 | 1 << 18;
1726 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
1727 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 11;
1728 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
1729 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 12;
1730 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
1731 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 13;
1732 if (mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
1733 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 14;
1734 if (mk_doutput(tad_inop))
1735 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 16;
1739 MK_VIII::IOHandler::update_outputs ()
1741 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1744 mk_doutput(audio_on) = ! mk_dinput(audio_inhibit)
1745 && mk->voice_player.voice
1746 && ! mk->voice_player.voice->element->silence;
1748 update_egpws_alert_discrete_1();
1749 update_egpwc_logic_discretes();
1750 update_mode6_callouts_discrete_1();
1751 update_mode6_callouts_discrete_2();
1752 update_egpws_alert_discrete_2();
1753 update_egpwc_alert_discrete_3();
1757 MK_VIII::IOHandler::get_lamp_output (Lamp lamp)
1761 case LAMP_GLIDESLOPE:
1762 return &mk_doutput(gpws_alert);
1765 return conf.lamp->format2 ? &mk_doutput(gpws_alert) : &mk_doutput(gpws_warning);
1768 return &mk_doutput(gpws_warning);
1771 assert_not_reached();
1777 MK_VIII::IOHandler::update_lamps ()
1779 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1782 if (_lamp != LAMP_NONE && conf.lamp->flashing)
1784 // [SPEC] 6.9.3: 70 cycles per minute
1785 if (lamp_timer.elapsed() >= 60.0 / 70.0 / 2.0)
1787 bool *output = get_lamp_output(_lamp);
1788 *output = ! *output;
1795 MK_VIII::IOHandler::set_lamp (Lamp lamp)
1802 mk_doutput(gpws_warning) = false;
1803 mk_doutput(gpws_alert) = false;
1805 if (lamp != LAMP_NONE)
1807 *get_lamp_output(lamp) = true;
1813 MK_VIII::IOHandler::gpws_inhibit () const
1815 return conf.gpws_inhibit_enabled && mk_dinput(gpws_inhibit);
1819 MK_VIII::IOHandler::real_flaps_down () const
1821 return conf.flap_reversal ? mk_dinput(landing_flaps) : ! mk_dinput(landing_flaps);
1825 MK_VIII::IOHandler::flaps_down () const
1827 return flap_override() ? true : real_flaps_down();
1831 MK_VIII::IOHandler::flap_override () const
1833 return conf.momentary_flap_override_enabled ? mk_doutput(flap_override) : false;
1837 MK_VIII::IOHandler::steep_approach () const
1839 if (conf.steep_approach_enabled)
1840 // If alternate action was configured in category 13, we return
1841 // the value of the input discrete (which should be connected to a
1842 // latching steep approach cockpit switch). Otherwise, we return
1843 // the value of the output discrete.
1844 return conf.alternate_steep_approach ? mk_dinput(steep_approach) : mk_doutput(steep_approach);
1850 MK_VIII::IOHandler::momentary_steep_approach_enabled () const
1852 return conf.steep_approach_enabled && ! conf.alternate_steep_approach;
1856 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1861 mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(), RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
1863 mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
1867 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1869 Parameter<double> *input,
1872 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name).c_str(), SGRawValuePointer<double>(input->get_pointer()));
1873 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name + "-ncd").c_str(), SGRawValuePointer<bool>(&input->ncd));
1875 mk->properties_handler.tie(node, (string("input-feeders/arinc429/") + name).c_str(), SGRawValuePointer<bool>(feed));
1879 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1883 SGPropertyNode *child = node->getNode((string("outputs/discretes/") + name).c_str(), true);
1885 mk->properties_handler.tie(child, SGRawValuePointer<bool>(output));
1886 child->setAttribute(SGPropertyNode::WRITE, false);
1890 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1894 SGPropertyNode *child = node->getNode((string("outputs/arinc429/") + name).c_str(), true);
1896 mk->properties_handler.tie(child, SGRawValuePointer<int>(output));
1897 child->setAttribute(SGPropertyNode::WRITE, false);
1901 MK_VIII::IOHandler::bind (SGPropertyNode *node)
1903 mk->properties_handler.tie(node, "inputs/rs-232/present-status", SGRawValueMethods<MK_VIII::IOHandler, bool>(*this, &MK_VIII::IOHandler::get_present_status, &MK_VIII::IOHandler::set_present_status));
1905 tie_input(node, "landing-gear", &mk_dinput(landing_gear), &mk_dinput_feed(landing_gear));
1906 tie_input(node, "landing-flaps", &mk_dinput(landing_flaps), &mk_dinput_feed(landing_flaps));
1907 tie_input(node, "momentary-flap-override", &mk_dinput(momentary_flap_override));
1908 tie_input(node, "self-test", &mk_dinput(self_test));
1909 tie_input(node, "glideslope-inhibit", &mk_dinput(glideslope_inhibit), &mk_dinput_feed(glideslope_inhibit));
1910 tie_input(node, "glideslope-cancel", &mk_dinput(glideslope_cancel));
1911 tie_input(node, "decision-height", &mk_dinput(decision_height), &mk_dinput_feed(decision_height));
1912 tie_input(node, "mode6-low-volume", &mk_dinput(mode6_low_volume));
1913 tie_input(node, "audio-inhibit", &mk_dinput(audio_inhibit));
1914 tie_input(node, "ta-tcf-inhibit", &mk_dinput(ta_tcf_inhibit));
1915 tie_input(node, "autopilot-engaged", &mk_dinput(autopilot_engaged), &mk_dinput_feed(autopilot_engaged));
1916 tie_input(node, "steep-approach", &mk_dinput(steep_approach));
1917 tie_input(node, "gpws-inhibit", &mk_dinput(gpws_inhibit));
1919 tie_input(node, "uncorrected-barometric-altitude", &mk_ainput(uncorrected_barometric_altitude), &mk_ainput_feed(uncorrected_barometric_altitude));
1920 tie_input(node, "barometric-altitude-rate", &mk_ainput(barometric_altitude_rate), &mk_ainput_feed(barometric_altitude_rate));
1921 tie_input(node, "gps-altitude", &mk_ainput(gps_altitude));
1922 tie_input(node, "gps-latitude", &mk_ainput(gps_latitude));
1923 tie_input(node, "gps-longitude", &mk_ainput(gps_longitude));
1924 tie_input(node, "gps-vertical-figure-of-merit", &mk_ainput(gps_vertical_figure_of_merit));
1925 tie_input(node, "radio-altitude", &mk_ainput(radio_altitude), &mk_ainput_feed(radio_altitude));
1926 tie_input(node, "glideslope-deviation", &mk_ainput(glideslope_deviation), &mk_ainput_feed(glideslope_deviation));
1927 tie_input(node, "roll-angle", &mk_ainput(roll_angle), &mk_ainput_feed(roll_angle));
1928 tie_input(node, "localizer-deviation", &mk_ainput(localizer_deviation), &mk_ainput_feed(localizer_deviation));
1929 tie_input(node, "computed-airspeed", &mk_ainput(computed_airspeed), &mk_ainput_feed(computed_airspeed));
1930 tie_input(node, "decision-height", &mk_ainput(decision_height), &mk_ainput_feed(decision_height));
1932 tie_output(node, "gpws-warning", &mk_doutput(gpws_warning));
1933 tie_output(node, "gpws-alert", &mk_doutput(gpws_alert));
1934 tie_output(node, "audio-on", &mk_doutput(audio_on));
1935 tie_output(node, "gpws-inop", &mk_doutput(gpws_inop));
1936 tie_output(node, "tad-inop", &mk_doutput(tad_inop));
1937 tie_output(node, "flap-override", &mk_doutput(flap_override));
1938 tie_output(node, "glideslope-cancel", &mk_doutput(glideslope_cancel));
1939 tie_output(node, "steep-approach", &mk_doutput(steep_approach));
1941 tie_output(node, "egpws-alert-discrete-1", &mk_aoutput(egpws_alert_discrete_1));
1942 tie_output(node, "egpwc-logic-discretes", &mk_aoutput(egpwc_logic_discretes));
1943 tie_output(node, "mode6-callouts-discrete-1", &mk_aoutput(mode6_callouts_discrete_1));
1944 tie_output(node, "mode6-callouts-discrete-2", &mk_aoutput(mode6_callouts_discrete_2));
1945 tie_output(node, "egpws-alert-discrete-2", &mk_aoutput(egpws_alert_discrete_2));
1946 tie_output(node, "egpwc-alert-discrete-3", &mk_aoutput(egpwc_alert_discrete_3));
1950 MK_VIII::IOHandler::get_discrete_input (bool *ptr) const
1956 MK_VIII::IOHandler::set_discrete_input (bool *ptr, bool value)
1961 if (mk->system_handler.state == SystemHandler::STATE_ON)
1963 if (ptr == &mk_dinput(momentary_flap_override))
1965 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1966 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1967 && conf.momentary_flap_override_enabled
1969 mk_doutput(flap_override) = ! mk_doutput(flap_override);
1971 else if (ptr == &mk_dinput(self_test))
1972 mk->self_test_handler.handle_button_event(value);
1973 else if (ptr == &mk_dinput(glideslope_cancel))
1975 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1976 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1979 if (! mk_doutput(glideslope_cancel))
1983 // Although we are not called from update(), we are
1984 // sure that the inputs we use here are defined,
1985 // since state is STATE_ON.
1987 if (! mk_data(glideslope_deviation_dots).ncd
1988 && ! mk_data(radio_altitude).ncd
1989 && mk_data(radio_altitude).get() <= 2000
1990 && mk_data(radio_altitude).get() >= 30)
1991 mk_doutput(glideslope_cancel) = true;
1993 // else do nothing ([PILOT] page 22: "Glideslope Cancel
1994 // can not be deselected (reset) by again pressing the
1995 // Glideslope Cancel switch.")
1998 else if (ptr == &mk_dinput(steep_approach))
2000 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
2001 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
2002 && momentary_steep_approach_enabled()
2004 mk_doutput(steep_approach) = ! mk_doutput(steep_approach);
2010 if (mk->system_handler.state == SystemHandler::STATE_ON)
2011 update_alternate_discrete_input(ptr);
2015 MK_VIII::IOHandler::present_status_section (const char *name)
2017 printf("%s\n", name);
2021 MK_VIII::IOHandler::present_status_item (const char *name, const char *value)
2024 printf("\t%-32s %s\n", name, value);
2026 printf("\t%s\n", name);
2030 MK_VIII::IOHandler::present_status_subitem (const char *name)
2032 printf("\t\t%s\n", name);
2036 MK_VIII::IOHandler::present_status ()
2040 if (mk->system_handler.state != SystemHandler::STATE_ON)
2043 present_status_section("EGPWC CONFIGURATION");
2044 present_status_item("PART NUMBER:", "965-1220-000"); // [SPEC] 1.1
2045 present_status_item("MOD STATUS:", "N/A");
2046 present_status_item("SERIAL NUMBER:", "N/A");
2048 present_status_item("APPLICATION S/W VERSION:", FLIGHTGEAR_VERSION);
2049 present_status_item("TERRAIN DATABASE VERSION:", FLIGHTGEAR_VERSION);
2050 present_status_item("ENVELOPE MOD DATABASE VERSION:", "N/A");
2051 present_status_item("BOOT CODE VERSION:", FLIGHTGEAR_VERSION);
2054 present_status_section("CURRENT FAULTS");
2055 if (mk->configuration_module.state == ConfigurationModule::STATE_OK && ! mk->fault_handler.has_faults())
2056 present_status_item("NO FAULTS");
2059 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2061 present_status_item("GPWS COMPUTER FAULTS:");
2062 switch (mk->configuration_module.state)
2064 case ConfigurationModule::STATE_INVALID_DATABASE:
2065 present_status_subitem("APPLICATION DATABASE FAILED");
2068 case ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE:
2069 present_status_subitem("CONFIGURATION TYPE INVALID");
2073 assert_not_reached();
2079 present_status_item("GPWS COMPUTER OK");
2080 present_status_item("GPWS EXTERNAL FAULTS:");
2082 static const char *fault_names[] = {
2083 "ALL MODES INHIBIT",
2086 "MOMENTARY FLAP OVERRIDE INVALID",
2087 "SELF TEST INVALID",
2088 "GLIDESLOPE CANCEL INVALID",
2089 "STEEP APPROACH INVALID",
2092 "MODES 1-4 INPUTS INVALID",
2093 "MODE 5 INPUTS INVALID",
2094 "MODE 6 INPUTS INVALID",
2095 "BANK ANGLE INPUTS INVALID",
2096 "TCF INPUTS INVALID"
2099 for (size_t i = 0; i < n_elements(fault_names); i++)
2100 if (mk->fault_handler.faults[i])
2101 present_status_subitem(fault_names[i]);
2106 present_status_section("CONFIGURATION:");
2108 static const char *category_names[] = {
2111 "POSITION INPUT TYPE",
2112 "CALLOUTS OPTION TYPE",
2114 "TERRAIN DISPLAY TYPE",
2116 "RADIO ALTITUDE TYPE",
2117 "NAVIGATION INPUT TYPE",
2119 "MAGNETIC HEADING TYPE",
2120 "WINDSHEAR INPUT TYPE",
2125 for (size_t i = 0; i < n_elements(category_names); i++)
2127 std::ostringstream value;
2128 value << "= " << mk->configuration_module.effective_categories[i];
2129 present_status_item(category_names[i], value.str().c_str());
2134 MK_VIII::IOHandler::get_present_status () const
2140 MK_VIII::IOHandler::set_present_status (bool value)
2142 if (value) present_status();
2146 ///////////////////////////////////////////////////////////////////////////////
2147 // VoicePlayer ////////////////////////////////////////////////////////////////
2148 ///////////////////////////////////////////////////////////////////////////////
2151 MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
2153 // uses xmlsound property names
2154 tie(node, "volume", &volume);
2155 tie(node, "pitch", &pitch);
2159 MK_VIII::VoicePlayer::Speaker::update_configuration ()
2161 map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
2162 for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
2164 SGSoundSample *sample = (*iter).second;
2166 sample->set_pitch(pitch);
2170 player->voice->volume_changed();
2173 MK_VIII::VoicePlayer::Voice::~Voice ()
2175 for (iter = elements.begin(); iter != elements.end(); iter++)
2176 delete *iter; // we owned the element
2181 MK_VIII::VoicePlayer::Voice::play ()
2183 iter = elements.begin();
2186 element->play(get_volume());
2190 MK_VIII::VoicePlayer::Voice::stop (bool now)
2194 if (now || element->silence)
2200 iter = elements.end() - 1; // stop after the current element finishes
2205 MK_VIII::VoicePlayer::Voice::set_volume (float _volume)
2212 MK_VIII::VoicePlayer::Voice::volume_changed ()
2215 element->set_volume(get_volume());
2219 MK_VIII::VoicePlayer::Voice::update ()
2223 if (! element->is_playing())
2225 if (++iter == elements.end())
2230 element->play(get_volume());
2236 MK_VIII::VoicePlayer::~VoicePlayer ()
2238 vector<Voice *>::iterator iter1;
2239 for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
2246 MK_VIII::VoicePlayer::init ()
2248 #define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
2250 SGSoundMgr *smgr = globals->get_soundmgr();
2251 _sgr = smgr->find("avionics", true);
2252 _sgr->tie_to_listener();
2254 make_voice(&voices.application_data_base_failed, "application-data-base-failed");
2255 make_voice(&voices.bank_angle, "bank-angle");
2256 make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
2257 make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
2258 make_voice(&voices.bank_angle_inop, "bank-angle-inop");
2259 make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
2260 make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
2261 make_voice(&voices.callouts_inop, "callouts-inop");
2262 make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
2263 make_voice(&voices.dont_sink, "dont-sink");
2264 make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
2265 make_voice(&voices.five_hundred_above, "500-above");
2266 make_voice(&voices.glideslope, "glideslope");
2267 make_voice(&voices.glideslope_inop, "glideslope-inop");
2268 make_voice(&voices.gpws_inop, "gpws-inop");
2269 make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
2270 make_voice(&voices.minimums, "minimums");
2271 make_voice(&voices.minimums_minimums, "minimums", "minimums");
2272 make_voice(&voices.pull_up, "pull-up");
2273 make_voice(&voices.sink_rate, "sink-rate");
2274 make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
2275 make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
2276 make_voice(&voices.terrain, "terrain");
2277 make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
2278 make_voice(&voices.too_low_flaps, "too-low-flaps");
2279 make_voice(&voices.too_low_gear, "too-low-gear");
2280 make_voice(&voices.too_low_terrain, "too-low-terrain");
2282 for (unsigned i = 0; i < n_altitude_callouts; i++)
2284 std::ostringstream name;
2285 name << "altitude-" << mk->mode6_handler.altitude_callout_definitions[i];
2286 make_voice(&voices.altitude_callouts[i], name.str().c_str());
2289 speaker.update_configuration();
2293 MK_VIII::VoicePlayer::get_sample (const char *name)
2295 std::ostringstream refname;
2296 refname << mk->name << "[" << mk->num << "]" << "/" << name;
2298 SGSoundSample *sample = _sgr->find(refname.str());
2301 string filename = "Sounds/mk-viii/" + string(name) + ".wav";
2304 sample = new SGSoundSample(filename.c_str(), SGPath());
2306 catch (const sg_exception &e)
2308 SG_LOG(SG_INSTR, SG_ALERT, "Error loading MK VIII sound sample \"" + filename + "\": " + e.getFormattedMessage());
2312 _sgr->add(sample, refname.str());
2313 samples[refname.str()] = sample;
2320 MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
2322 if (test_bits(flags, PLAY_NOW) || ! voice || voice->element->silence)
2328 looped = test_bits(flags, PLAY_LOOPED);
2331 next_looped = false;
2337 next_voice = _voice;
2338 next_looped = test_bits(flags, PLAY_LOOPED);
2343 MK_VIII::VoicePlayer::stop (unsigned int flags)
2347 voice->stop(test_bits(flags, STOP_NOW));
2357 MK_VIII::VoicePlayer::set_volume (float _volume)
2361 voice->volume_changed();
2365 MK_VIII::VoicePlayer::update ()
2373 if (! voice->element || voice->element->silence)
2376 looped = next_looped;
2379 next_looped = false;
2386 if (! voice->element)
2397 ///////////////////////////////////////////////////////////////////////////////
2398 // SelfTestHandler ////////////////////////////////////////////////////////////
2399 ///////////////////////////////////////////////////////////////////////////////
2402 MK_VIII::SelfTestHandler::_was_here (int position)
2404 if (position > current)
2413 MK_VIII::SelfTestHandler::Action
2414 MK_VIII::SelfTestHandler::sleep (double duration)
2416 Action _action = { ACTION_SLEEP, duration, NULL };
2420 MK_VIII::SelfTestHandler::Action
2421 MK_VIII::SelfTestHandler::play (VoicePlayer::Voice *voice)
2423 mk->voice_player.play(voice);
2424 Action _action = { ACTION_VOICE, 0, NULL };
2428 MK_VIII::SelfTestHandler::Action
2429 MK_VIII::SelfTestHandler::discrete_on (bool *discrete, double duration)
2432 return sleep(duration);
2435 MK_VIII::SelfTestHandler::Action
2436 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, double duration)
2439 Action _action = { ACTION_SLEEP | ACTION_DISCRETE_ON_OFF, duration, discrete };
2443 MK_VIII::SelfTestHandler::Action
2444 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, VoicePlayer::Voice *voice)
2447 mk->voice_player.play(voice);
2448 Action _action = { ACTION_VOICE | ACTION_DISCRETE_ON_OFF, 0, discrete };
2452 MK_VIII::SelfTestHandler::Action
2453 MK_VIII::SelfTestHandler::done ()
2455 Action _action = { ACTION_DONE, 0, NULL };
2459 MK_VIII::SelfTestHandler::Action
2460 MK_VIII::SelfTestHandler::run ()
2462 // Note that "Terrain INOP" and "Terrain NA" are or'ed to the same
2463 // output discrete (see [SPEC] 6.9.3.5).
2465 #define was_here() was_here_offset(0)
2466 #define was_here_offset(offset) _was_here(__LINE__ * 20 + (offset))
2470 if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_DATABASE)
2471 return play(mk_voice(application_data_base_failed));
2472 else if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE)
2473 return play(mk_voice(configuration_type_invalid));
2475 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2479 return discrete_on(&mk_doutput(gpws_inop), 0.7);
2481 return sleep(0.7); // W/S INOP
2483 return discrete_on(&mk_doutput(tad_inop), 0.7);
2486 mk_doutput(gpws_inop) = false;
2487 // do not disable tad_inop since we must enable Terrain NA
2488 // do not disable W/S INOP since we do not emulate it
2489 return sleep(0.7); // Terrain NA
2493 mk_doutput(tad_inop) = false; // disable Terrain NA
2494 if (mk->io_handler.conf.momentary_flap_override_enabled)
2495 return discrete_on_off(&mk_doutput(flap_override), 1.0);
2498 return discrete_on_off(&mk_doutput(audio_on), 1.0);
2501 if (mk->io_handler.momentary_steep_approach_enabled())
2502 return discrete_on_off(&mk_doutput(steep_approach), 1.0);
2507 if (mk_dinput(glideslope_inhibit))
2508 goto glideslope_end;
2511 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
2512 goto glideslope_inop;
2516 return discrete_on_off(&mk_doutput(gpws_alert), mk_voice(glideslope));
2518 return discrete_on_off(&mk_doutput(glideslope_cancel), 0.7);
2519 goto glideslope_end;
2522 return play(mk_voice(glideslope_inop));
2527 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
2531 return discrete_on_off(&mk_doutput(gpws_warning), mk_voice(pull_up));
2535 return play(mk_voice(gpws_inop));
2540 if (mk_dinput(self_test)) // proceed to long self test
2545 if (mk->mode6_handler.conf.bank_angle_enabled
2546 && mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
2547 return play(mk_voice(bank_angle_inop));
2551 if (mk->mode6_handler.altitude_callouts_enabled()
2552 && mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
2553 return play(mk_voice(callouts_inop));
2561 mk_doutput(gpws_inop) = true;
2562 // do not enable W/S INOP, since we do not emulate it
2563 mk_doutput(tad_inop) = true; // Terrain INOP, Terrain NA
2565 return play(mk_voice(sink_rate));
2568 return play(mk_voice(pull_up));
2570 return play(mk_voice(terrain));
2572 return play(mk_voice(pull_up));
2574 return play(mk_voice(dont_sink));
2576 return play(mk_voice(too_low_terrain));
2578 return play(mk->mode4_handler.conf.voice_too_low_gear);
2580 return play(mk_voice(too_low_flaps));
2582 return play(mk_voice(too_low_terrain));
2584 return play(mk_voice(glideslope));
2587 if (mk->mode6_handler.conf.bank_angle_enabled)
2588 return play(mk_voice(bank_angle));
2593 if (! mk->mode6_handler.altitude_callouts_enabled())
2594 goto callouts_disabled;
2598 if (mk->mode6_handler.conf.minimums_enabled)
2599 return play(mk_voice(minimums));
2603 if (mk->mode6_handler.conf.above_field_voice)
2604 return play(mk->mode6_handler.conf.above_field_voice);
2606 for (unsigned i = 0; i < n_altitude_callouts; i++)
2607 if (! was_here_offset(i))
2609 if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
2610 return play(mk_altitude_voice(i));
2614 if (mk->mode6_handler.conf.smart_500_enabled)
2615 return play(mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500));
2620 return play(mk_voice(minimums_minimums));
2625 if (mk->tcf_handler.conf.enabled)
2626 return play(mk_voice(too_low_terrain));
2633 MK_VIII::SelfTestHandler::start ()
2635 assert(state == STATE_START);
2637 memcpy(&saved_outputs, &mk->io_handler.outputs, sizeof(mk->io_handler.outputs));
2638 memset(&mk->io_handler.outputs, 0, sizeof(mk->io_handler.outputs));
2640 // [SPEC] 6.10.6: "The self-test results are annunciated, at 6db
2641 // lower than the normal audio level selected for the aircraft"
2642 mk->voice_player.set_volume(modify_amplitude(mk->voice_player.conf.volume, -6));
2644 if (mk_dinput(mode6_low_volume))
2645 mk->mode6_handler.set_volume(1.0);
2647 state = STATE_RUNNING;
2648 cancel = CANCEL_NONE;
2649 memset(&action, 0, sizeof(action));
2654 MK_VIII::SelfTestHandler::stop ()
2656 if (state != STATE_NONE)
2658 if (state != STATE_START)
2660 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2661 mk->voice_player.set_volume(mk_dinput(audio_inhibit) ? 0.0 : mk->voice_player.conf.volume);
2663 if (mk_dinput(mode6_low_volume))
2664 mk->mode6_handler.set_volume(modify_amplitude(1.0, -6));
2666 memcpy(&mk->io_handler.outputs, &saved_outputs, sizeof(mk->io_handler.outputs));
2669 button_pressed = false;
2671 // reset self-test handler position
2677 MK_VIII::SelfTestHandler::handle_button_event (bool value)
2679 if (state == STATE_NONE)
2682 state = STATE_START;
2684 else if (state == STATE_START)
2687 state = STATE_NONE; // cancel the not-yet-started test
2689 else if (cancel == CANCEL_NONE)
2693 assert(! button_pressed);
2694 button_pressed = true;
2695 button_press_timestamp = globals->get_sim_time_sec();
2701 if (globals->get_sim_time_sec() - button_press_timestamp < 2)
2702 cancel = CANCEL_SHORT;
2704 cancel = CANCEL_LONG;
2711 MK_VIII::SelfTestHandler::update ()
2713 if (state == STATE_NONE)
2715 else if (state == STATE_START)
2717 if (mk->state_handler.ground && ! mk->io_handler.steep_approach())
2727 if (button_pressed && cancel == CANCEL_NONE)
2729 if (globals->get_sim_time_sec() - button_press_timestamp >= 2)
2730 cancel = CANCEL_LONG;
2734 if (! mk->state_handler.ground || cancel != CANCEL_NONE)
2740 if (test_bits(action.flags, ACTION_SLEEP))
2742 if (globals->get_sim_time_sec() - sleep_start < action.sleep_duration)
2745 if (test_bits(action.flags, ACTION_VOICE))
2747 if (mk->voice_player.voice)
2750 if (test_bits(action.flags, ACTION_DISCRETE_ON_OFF))
2751 *action.discrete = false;
2755 if (test_bits(action.flags, ACTION_SLEEP))
2756 sleep_start = globals->get_sim_time_sec();
2757 if (test_bits(action.flags, ACTION_DONE))
2766 ///////////////////////////////////////////////////////////////////////////////
2767 // AlertHandler ///////////////////////////////////////////////////////////////
2768 ///////////////////////////////////////////////////////////////////////////////
2771 MK_VIII::AlertHandler::select_voice_alerts (unsigned int test)
2773 if (has_alerts(test))
2775 voice_alerts = alerts & test;
2780 voice_alerts &= ~test;
2781 if (voice_alerts == 0)
2782 mk->voice_player.stop();
2789 MK_VIII::AlertHandler::boot ()
2795 MK_VIII::AlertHandler::reposition ()
2799 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2800 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2804 MK_VIII::AlertHandler::reset ()
2809 repeated_alerts = 0;
2810 altitude_callout_voice = NULL;
2814 MK_VIII::AlertHandler::update ()
2816 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2819 // Lamps and voices are prioritized according to [SPEC] 6.9.2.
2821 // Voices which can interrupt other voices (VoicePlayer::PLAY_NOW)
2822 // are specified by [INSTALL] appendix E 5.3.5.
2824 // When a voice is overriden by a higher priority voice and the
2825 // overriding voice stops, we restore the previous voice if it was
2826 // looped (this is not specified by [SPEC] but seems to make sense).
2830 if (has_alerts(ALERT_MODE1_PULL_UP | ALERT_MODE2A | ALERT_MODE2B))
2831 mk->io_handler.set_lamp(IOHandler::LAMP_WARNING);
2832 else if (has_alerts(ALERT_MODE1_SINK_RATE
2833 | ALERT_MODE2A_PREFACE
2834 | ALERT_MODE2B_PREFACE
2835 | ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING
2836 | ALERT_MODE2A_ALTITUDE_GAIN
2837 | ALERT_MODE2B_LANDING_MODE
2839 | ALERT_MODE4_TOO_LOW_FLAPS
2840 | ALERT_MODE4_TOO_LOW_GEAR
2841 | ALERT_MODE4AB_TOO_LOW_TERRAIN
2842 | ALERT_MODE4C_TOO_LOW_TERRAIN
2843 | ALERT_TCF_TOO_LOW_TERRAIN))
2844 mk->io_handler.set_lamp(IOHandler::LAMP_CAUTION);
2845 else if (has_alerts(ALERT_MODE5_SOFT | ALERT_MODE5_HARD))
2846 mk->io_handler.set_lamp(IOHandler::LAMP_GLIDESLOPE);
2848 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2852 if (select_voice_alerts(ALERT_MODE1_PULL_UP))
2854 if (! has_old_alerts(ALERT_MODE1_PULL_UP))
2856 if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate))
2857 mk->voice_player.play(mk_voice(sink_rate), VoicePlayer::PLAY_NOW);
2858 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_LOOPED);
2861 else if (select_voice_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2863 if (! has_old_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2864 mk->voice_player.play(mk_voice(terrain_pause_terrain), VoicePlayer::PLAY_NOW);
2866 else if (select_voice_alerts(ALERT_MODE2A | ALERT_MODE2B))
2868 if (mk->voice_player.voice != mk_voice(pull_up))
2869 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_NOW | VoicePlayer::PLAY_LOOPED);
2871 else if (select_voice_alerts(ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING | ALERT_MODE2B_LANDING_MODE))
2873 if (mk->voice_player.voice != mk_voice(terrain))
2874 mk->voice_player.play(mk_voice(terrain), VoicePlayer::PLAY_LOOPED);
2876 else if (select_voice_alerts(ALERT_MODE6_MINIMUMS))
2878 if (! has_old_alerts(ALERT_MODE6_MINIMUMS))
2879 mk->voice_player.play(mk_voice(minimums_minimums));
2881 else if (select_voice_alerts(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2883 if (must_play_voice(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2884 mk->voice_player.play(mk_voice(too_low_terrain));
2886 else if (select_voice_alerts(ALERT_TCF_TOO_LOW_TERRAIN))
2888 if (must_play_voice(ALERT_TCF_TOO_LOW_TERRAIN))
2889 mk->voice_player.play(mk_voice(too_low_terrain));
2891 else if (select_voice_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2893 if (! has_old_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2895 assert(altitude_callout_voice != NULL);
2896 mk->voice_player.play(altitude_callout_voice);
2897 altitude_callout_voice = NULL;
2900 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
2902 if (must_play_voice(ALERT_MODE4_TOO_LOW_GEAR))
2903 mk->voice_player.play(mk->mode4_handler.conf.voice_too_low_gear);
2905 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_FLAPS))
2907 if (must_play_voice(ALERT_MODE4_TOO_LOW_FLAPS))
2908 mk->voice_player.play(mk_voice(too_low_flaps));
2910 else if (select_voice_alerts(ALERT_MODE1_SINK_RATE))
2912 if (must_play_voice(ALERT_MODE1_SINK_RATE))
2913 mk->voice_player.play(mk_voice(sink_rate_pause_sink_rate));
2914 // [SPEC] 6.2.1: "During the time that the voice message for the
2915 // outer envelope is inhibited and the caution/warning lamp is
2916 // on, the Mode 5 alert message is allowed to annunciate for
2917 // excessive glideslope deviation conditions."
2918 else if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate)
2919 && mk->voice_player.next_voice != mk_voice(sink_rate_pause_sink_rate))
2921 if (has_alerts(ALERT_MODE5_HARD))
2923 voice_alerts |= ALERT_MODE5_HARD;
2924 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2925 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2927 else if (has_alerts(ALERT_MODE5_SOFT))
2929 voice_alerts |= ALERT_MODE5_SOFT;
2930 if (must_play_voice(ALERT_MODE5_SOFT))
2931 mk->voice_player.play(mk_voice(soft_glideslope));
2935 else if (select_voice_alerts(ALERT_MODE3))
2937 if (must_play_voice(ALERT_MODE3))
2938 mk->voice_player.play(mk_voice(dont_sink_pause_dont_sink));
2940 else if (select_voice_alerts(ALERT_MODE5_HARD))
2942 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2943 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2945 else if (select_voice_alerts(ALERT_MODE5_SOFT))
2947 if (must_play_voice(ALERT_MODE5_SOFT))
2948 mk->voice_player.play(mk_voice(soft_glideslope));
2950 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_3))
2952 if (mk->voice_player.voice != mk_voice(bank_angle_bank_angle_3))
2953 mk->voice_player.play(mk_voice(bank_angle_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2955 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_3))
2957 if (mk->voice_player.voice != mk_voice(bank_angle_pause_bank_angle_3))
2958 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2960 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2))
2962 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2963 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2965 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_2))
2967 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2968 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2970 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1))
2972 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2973 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2975 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_1))
2977 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2978 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2983 old_alerts = voice_alerts;
2984 repeated_alerts = 0;
2988 MK_VIII::AlertHandler::set_alerts (unsigned int _alerts,
2990 VoicePlayer::Voice *_altitude_callout_voice)
2993 if (test_bits(flags, ALERT_FLAG_REPEAT))
2994 repeated_alerts |= _alerts;
2995 if (_altitude_callout_voice)
2996 altitude_callout_voice = _altitude_callout_voice;
3000 MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
3003 repeated_alerts &= ~_alerts;
3004 if (_alerts & ALERT_MODE6_ALTITUDE_CALLOUT)
3005 altitude_callout_voice = NULL;
3008 ///////////////////////////////////////////////////////////////////////////////
3009 // StateHandler ///////////////////////////////////////////////////////////////
3010 ///////////////////////////////////////////////////////////////////////////////
3013 MK_VIII::StateHandler::update_ground ()
3017 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
3018 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30)
3020 if (potentially_airborne_timer.start_or_elapsed() > 1)
3024 potentially_airborne_timer.stop();
3028 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() < 40
3029 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 30)
3035 MK_VIII::StateHandler::enter_ground ()
3038 mk->io_handler.enter_ground();
3040 // [SPEC] 6.0.1 does not specify this, but it seems to be an
3041 // omission; at this point, we must make sure that we are in takeoff
3042 // mode (otherwise the next takeoff might happen in approach mode).
3048 MK_VIII::StateHandler::leave_ground ()
3051 mk->mode2_handler.leave_ground();
3055 MK_VIII::StateHandler::update_takeoff ()
3059 // [SPEC] 6.0.2 is somewhat confusing: it mentions hardcoded
3060 // terrain clearance (500, 750) and airspeed (178, 200) values,
3061 // but it also mentions the mode 4A boundary, which varies as a
3062 // function of aircraft type. We consider that the mentioned
3063 // values are examples, and that we should use the mode 4A upper
3066 if (! mk_data(terrain_clearance).ncd
3067 && ! mk_ainput(computed_airspeed).ncd
3068 && mk_data(terrain_clearance).get() > mk->mode4_handler.get_upper_agl(mk->mode4_handler.conf.modes->ac))
3073 if (! mk_data(radio_altitude).ncd
3074 && mk_data(radio_altitude).get() < mk->mode4_handler.conf.modes->b->min_agl1
3075 && mk->io_handler.flaps_down()
3076 && mk_dinput(landing_gear))
3082 MK_VIII::StateHandler::enter_takeoff ()
3085 mk->io_handler.enter_takeoff();
3086 mk->mode2_handler.enter_takeoff();
3087 mk->mode3_handler.enter_takeoff();
3088 mk->mode6_handler.enter_takeoff();
3092 MK_VIII::StateHandler::leave_takeoff ()
3095 mk->mode6_handler.leave_takeoff();
3099 MK_VIII::StateHandler::post_reposition ()
3101 // Synchronize the state with the current situation.
3104 (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
3105 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30);
3107 bool _takeoff = _ground;
3109 if (ground && ! _ground)
3111 else if (! ground && _ground)
3114 if (takeoff && ! _takeoff)
3116 else if (! takeoff && _takeoff)
3121 MK_VIII::StateHandler::update ()
3123 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3130 ///////////////////////////////////////////////////////////////////////////////
3131 // Mode1Handler ///////////////////////////////////////////////////////////////
3132 ///////////////////////////////////////////////////////////////////////////////
3135 MK_VIII::Mode1Handler::get_pull_up_bias ()
3137 // [PILOT] page 54: "Steep Approach has priority over Flap Override
3138 // if selected simultaneously."
3140 if (mk->io_handler.steep_approach())
3142 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3149 MK_VIII::Mode1Handler::is_pull_up ()
3151 if (! mk->io_handler.gpws_inhibit()
3152 && ! mk->state_handler.ground // [1]
3153 && ! mk_data(radio_altitude).ncd
3154 && ! mk_data(barometric_altitude_rate).ncd
3155 && mk_data(radio_altitude).get() > conf.envelopes->min_agl)
3157 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl1)
3159 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl1(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3162 else if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl2)
3164 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl2(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3173 MK_VIII::Mode1Handler::update_pull_up ()
3177 if (! mk_test_alert(MODE1_PULL_UP))
3179 // [SPEC] 6.2.1: at least one sink rate must be issued
3180 // before switching to pull up; if no sink rate has
3181 // occurred, a 0.2 second delay is used.
3182 if (mk->voice_player.voice == mk_voice(sink_rate_pause_sink_rate)
3183 || pull_up_timer.start_or_elapsed() >= 0.2)
3184 mk_set_alerts(mk_alert(MODE1_PULL_UP));
3189 pull_up_timer.stop();
3190 mk_unset_alerts(mk_alert(MODE1_PULL_UP));
3195 MK_VIII::Mode1Handler::get_sink_rate_bias ()
3197 // [PILOT] page 54: "Steep Approach has priority over Flap Override
3198 // if selected simultaneously."
3200 if (mk->io_handler.steep_approach())
3202 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3204 else if (! mk_data(glideslope_deviation_dots).ncd)
3208 if (mk_data(glideslope_deviation_dots).get() <= -2)
3210 else if (mk_data(glideslope_deviation_dots).get() < 0)
3211 bias = -150 * mk_data(glideslope_deviation_dots).get();
3213 if (mk_data(radio_altitude).get() < 100)
3214 bias *= 0.01 * mk_data(radio_altitude).get();
3223 MK_VIII::Mode1Handler::is_sink_rate ()
3225 return ! mk->io_handler.gpws_inhibit()
3226 && ! mk->state_handler.ground // [1]
3227 && ! mk_data(radio_altitude).ncd
3228 && ! mk_data(barometric_altitude_rate).ncd
3229 && mk_data(radio_altitude).get() > conf.envelopes->min_agl
3230 && mk_data(radio_altitude).get() < 2450
3231 && mk_data(radio_altitude).get() < -572 - 0.6035 * (mk_data(barometric_altitude_rate).get() + get_sink_rate_bias());
3235 MK_VIII::Mode1Handler::get_sink_rate_tti ()
3237 return mk_data(radio_altitude).get() / fabs(mk_data(barometric_altitude_rate).get());
3241 MK_VIII::Mode1Handler::update_sink_rate ()
3245 if (mk_test_alert(MODE1_SINK_RATE))
3247 double tti = get_sink_rate_tti();
3248 if (tti <= sink_rate_tti * 0.8)
3250 // ~20% degradation, warn again and store the new reference tti
3251 sink_rate_tti = tti;
3252 mk_repeat_alert(mk_alert(MODE1_SINK_RATE));
3257 if (sink_rate_timer.start_or_elapsed() >= 0.8)
3259 mk_set_alerts(mk_alert(MODE1_SINK_RATE));
3260 sink_rate_tti = get_sink_rate_tti();
3266 sink_rate_timer.stop();
3267 mk_unset_alerts(mk_alert(MODE1_SINK_RATE));
3272 MK_VIII::Mode1Handler::update ()
3274 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3281 ///////////////////////////////////////////////////////////////////////////////
3282 // Mode2Handler ///////////////////////////////////////////////////////////////
3283 ///////////////////////////////////////////////////////////////////////////////
3286 MK_VIII::Mode2Handler::ClosureRateFilter::limit_radio_altitude_rate (double r)
3288 // Limit radio altitude rate according to aircraft configuration,
3289 // allowing maximum sensitivity during cruise while providing
3290 // progressively less sensitivity during the landing phases of
3293 if (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3294 { // gs deviation <= +- 2 dots
3295 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3296 SG_CLAMP_RANGE(r, -1000.0, 3000.0);
3297 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3298 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3300 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3303 { // no ILS, or gs deviation > +- 2 dots
3304 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3305 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3306 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3307 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3315 MK_VIII::Mode2Handler::ClosureRateFilter::init ()
3318 last_ra.set(&mk_data(radio_altitude));
3319 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3326 MK_VIII::Mode2Handler::ClosureRateFilter::update ()
3328 double elapsed = timer.start_or_elapsed();
3332 if (! mk_data(radio_altitude).ncd && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3334 if (! last_ra.ncd && ! last_ba.ncd)
3336 // compute radio and barometric altitude rates (positive value = descent)
3337 double ra_rate = -(mk_data(radio_altitude).get() - last_ra.get()) / elapsed * 60;
3338 double ba_rate = -(mk_ainput(uncorrected_barometric_altitude).get() - last_ba.get()) / elapsed * 60;
3340 // limit radio altitude rate according to aircraft configuration
3341 ra_rate = limit_radio_altitude_rate(ra_rate);
3343 // apply a low-pass filter to the radio altitude rate
3344 ra_rate = ra_filter.filter(ra_rate);
3345 // apply a high-pass filter to the barometric altitude rate
3346 ba_rate = ba_filter.filter(ba_rate);
3348 // combine both rates to obtain a closure rate
3349 output.set(ra_rate + ba_rate);
3362 last_ra.set(&mk_data(radio_altitude));
3363 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3367 MK_VIII::Mode2Handler::b_conditions ()
3369 return mk->io_handler.flaps_down()
3370 || (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) < 2)
3371 || takeoff_timer.running;
3375 MK_VIII::Mode2Handler::is_a ()
3377 if (! mk->io_handler.gpws_inhibit()
3378 && ! mk->state_handler.ground // [1]
3379 && ! mk_data(radio_altitude).ncd
3380 && ! mk_ainput(computed_airspeed).ncd
3381 && ! closure_rate_filter.output.ncd
3382 && ! b_conditions())
3384 if (mk_data(radio_altitude).get() < 1220)
3386 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3393 if (mk_ainput(computed_airspeed).get() <= conf->airspeed1)
3395 else if (mk_ainput(computed_airspeed).get() >= conf->airspeed2)
3398 upper_limit = 1650 + 8.9 * (mk_ainput(computed_airspeed).get() - conf->airspeed1);
3400 if (mk_data(radio_altitude).get() < upper_limit)
3402 if (mk_data(radio_altitude).get() < 522 + 0.1968 * closure_rate_filter.output.get())
3412 MK_VIII::Mode2Handler::is_b ()
3414 if (! mk->io_handler.gpws_inhibit()
3415 && ! mk->state_handler.ground // [1]
3416 && ! mk_data(radio_altitude).ncd
3417 && ! mk_data(barometric_altitude_rate).ncd
3418 && ! closure_rate_filter.output.ncd
3420 && mk_data(radio_altitude).get() < 789)
3424 if (mk->io_handler.flaps_down())
3426 if (mk_data(barometric_altitude_rate).get() > -400)
3428 else if (mk_data(barometric_altitude_rate).get() < -1000)
3431 lower_limit = -66.777 - 0.667 * mk_data(barometric_altitude_rate).get();
3436 if (mk_data(radio_altitude).get() > lower_limit)
3438 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3447 MK_VIII::Mode2Handler::check_pull_up (unsigned int preface_alert,
3450 if (pull_up_timer.running)
3452 if (pull_up_timer.elapsed() >= 1)
3454 mk_unset_alerts(preface_alert);
3455 mk_set_alerts(alert);
3460 if (! mk->voice_player.voice)
3461 pull_up_timer.start();
3466 MK_VIII::Mode2Handler::update_a ()
3470 if (mk_test_alert(MODE2A_PREFACE))
3471 check_pull_up(mk_alert(MODE2A_PREFACE), mk_alert(MODE2A));
3472 else if (! mk_test_alert(MODE2A))
3474 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3475 mk_set_alerts(mk_alert(MODE2A_PREFACE));
3476 a_start_time = globals->get_sim_time_sec();
3477 pull_up_timer.stop();
3482 if (mk_test_alert(MODE2A_ALTITUDE_GAIN))
3484 if (mk->io_handler.gpws_inhibit()
3485 || mk->state_handler.ground // [1]
3486 || a_altitude_gain_timer.elapsed() >= 45
3487 || mk_data(radio_altitude).ncd
3488 || mk_ainput(uncorrected_barometric_altitude).ncd
3489 || mk_ainput(uncorrected_barometric_altitude).get() - a_altitude_gain_alt >= 300
3490 // [PILOT] page 12: "the visual alert will remain on
3491 // until [...] landing flaps or the flap override switch
3493 || mk->io_handler.flaps_down())
3495 // exit altitude gain mode
3496 a_altitude_gain_timer.stop();
3497 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3501 // [SPEC] 6.2.2.2: "If the terrain starts to fall away
3502 // during this altitude gain time, the voice will shut
3504 if (closure_rate_filter.output.get() < 0)
3505 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3508 else if (mk_test_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A)))
3510 mk_unset_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A));
3512 if (! mk->io_handler.gpws_inhibit()
3513 && ! mk->state_handler.ground // [1]
3514 && globals->get_sim_time_sec() - a_start_time > 3
3515 && ! mk->io_handler.flaps_down()
3516 && ! mk_data(radio_altitude).ncd
3517 && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3519 // [SPEC] 6.2.2.2: mode 2A envelope violated for more
3520 // than 3 seconds, enable altitude gain feature
3522 a_altitude_gain_timer.start();
3523 a_altitude_gain_alt = mk_ainput(uncorrected_barometric_altitude).get();
3525 unsigned int alerts = mk_alert(MODE2A_ALTITUDE_GAIN);
3526 if (closure_rate_filter.output.get() > 0)
3527 alerts |= mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING);
3529 mk_set_alerts(alerts);
3536 MK_VIII::Mode2Handler::update_b ()
3540 // handle normal mode
3542 if (b && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down()))
3544 if (mk_test_alert(MODE2B_PREFACE))
3545 check_pull_up(mk_alert(MODE2B_PREFACE), mk_alert(MODE2B));
3546 else if (! mk_test_alert(MODE2B))
3548 mk_set_alerts(mk_alert(MODE2B_PREFACE));
3549 pull_up_timer.stop();
3553 mk_unset_alerts(mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B));
3555 // handle landing mode ([SPEC] 6.2.2.3: "If both landing gear and
3556 // flaps are in the landing configuration, then the message will be
3559 if (b && mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3560 mk_set_alerts(mk_alert(MODE2B_LANDING_MODE));
3562 mk_unset_alerts(mk_alert(MODE2B_LANDING_MODE));
3566 MK_VIII::Mode2Handler::boot ()
3568 closure_rate_filter.init();
3572 MK_VIII::Mode2Handler::power_off ()
3574 // [SPEC] 6.0.4: "This latching function is not power saved and a
3575 // system reset will force it false."
3576 takeoff_timer.stop();
3580 MK_VIII::Mode2Handler::leave_ground ()
3582 // takeoff, reset timer
3583 takeoff_timer.start();
3587 MK_VIII::Mode2Handler::enter_takeoff ()
3589 // reset timer, in case it's a go-around
3590 takeoff_timer.start();
3594 MK_VIII::Mode2Handler::update ()
3596 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3599 closure_rate_filter.update();
3601 if (takeoff_timer.running && takeoff_timer.elapsed() >= 60)
3602 takeoff_timer.stop();
3608 ///////////////////////////////////////////////////////////////////////////////
3609 // Mode3Handler ///////////////////////////////////////////////////////////////
3610 ///////////////////////////////////////////////////////////////////////////////
3613 MK_VIII::Mode3Handler::max_alt_loss (double _bias)
3615 return conf->max_alt_loss(mk->io_handler.flap_override(), mk_data(radio_altitude).get()) + mk_data(radio_altitude).get() * _bias;
3619 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
3621 // do not repeat altitude-loss alerts below 30ft agl
3622 if (mk_data(radio_altitude).get() > 30)
3624 if (initial_bias < 0.0) // sanity check
3626 // mk-viii spec: repeat alerts whenever losing 20% of initial altitude
3627 while ((alt_loss > max_alt_loss(initial_bias))&&
3628 (initial_bias < 1.0))
3629 initial_bias += 0.2;
3632 return initial_bias;
3636 MK_VIII::Mode3Handler::is (double *alt_loss)
3638 if (has_descent_alt)
3640 int max_agl = conf->max_agl(mk->io_handler.flap_override());
3642 if (mk_ainput(uncorrected_barometric_altitude).ncd
3643 || mk_ainput(uncorrected_barometric_altitude).get() > descent_alt
3644 || mk_data(radio_altitude).ncd
3645 || mk_data(radio_altitude).get() > max_agl)
3648 has_descent_alt = false;
3652 // gear/flaps: [SPEC] 1.3.1.3
3653 if (! mk->io_handler.gpws_inhibit()
3654 && ! mk->state_handler.ground // [1]
3655 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3656 && ! mk_data(barometric_altitude_rate).ncd
3657 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3658 && ! mk_data(radio_altitude).ncd
3659 && mk_data(barometric_altitude_rate).get() < 0)
3661 double _alt_loss = descent_alt - mk_ainput(uncorrected_barometric_altitude).get();
3662 if (armed || (mk_data(radio_altitude).get() > conf->min_agl
3663 && mk_data(radio_altitude).get() < max_agl
3664 && _alt_loss > max_alt_loss(0)))
3666 *alt_loss = _alt_loss;
3674 if (! mk_data(barometric_altitude_rate).ncd
3675 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3676 && mk_data(barometric_altitude_rate).get() < 0)
3678 has_descent_alt = true;
3679 descent_alt = mk_ainput(uncorrected_barometric_altitude).get();
3687 MK_VIII::Mode3Handler::enter_takeoff ()
3690 has_descent_alt = false;
3694 MK_VIII::Mode3Handler::update ()
3696 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3699 if (mk->state_handler.takeoff)
3703 if (! mk->state_handler.ground /* [1] */ && is(&alt_loss))
3705 if (mk_test_alert(MODE3))
3707 double new_bias = get_bias(bias, alt_loss);
3708 if (new_bias > bias)
3711 mk_repeat_alert(mk_alert(MODE3));
3717 bias = get_bias(0.2, alt_loss);
3718 mk_set_alerts(mk_alert(MODE3));
3725 mk_unset_alerts(mk_alert(MODE3));
3728 ///////////////////////////////////////////////////////////////////////////////
3729 // Mode4Handler ///////////////////////////////////////////////////////////////
3730 ///////////////////////////////////////////////////////////////////////////////
3732 // FIXME: for turbofans, the upper limit should be reduced from 1000
3733 // to 800 ft if a sudden change in radio altitude is detected, in
3734 // order to reduce nuisance alerts caused by overflying another
3735 // aircraft (see [PILOT] page 16).
3738 MK_VIII::Mode4Handler::get_upper_agl (const EnvelopesConfiguration *c)
3740 if (mk_ainput(computed_airspeed).get() >= c->airspeed2)
3742 else if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3743 return c->min_agl2(mk_ainput(computed_airspeed).get());
3748 const MK_VIII::Mode4Handler::EnvelopesConfiguration *
3749 MK_VIII::Mode4Handler::get_ab_envelope ()
3751 // [SPEC] 6.2.4.1: "The Mode 4B envelope can also be selected by
3752 // setting flaps to landing configuration or by selecting flap
3754 return mk_dinput(landing_gear) || mk->io_handler.flaps_down()
3760 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
3762 // do not repeat terrain/gear/flap alerts below 30ft agl
3763 if (mk_data(radio_altitude).get() > 30.0)
3765 if (initial_bias < 0.0) // sanity check
3767 while ((mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)&&
3768 (initial_bias < 1.0))
3769 initial_bias += 0.2;
3772 return initial_bias;
3776 MK_VIII::Mode4Handler::handle_alert (unsigned int alert,
3780 if (mk_test_alerts(alert))
3782 double new_bias = get_bias(*bias, min_agl);
3783 if (new_bias > *bias)
3786 mk_repeat_alert(alert);
3791 *bias = get_bias(0.2, min_agl);
3792 mk_set_alerts(alert);
3797 MK_VIII::Mode4Handler::update_ab ()
3799 if (! mk->io_handler.gpws_inhibit()
3800 && ! mk->state_handler.ground
3801 && ! mk->state_handler.takeoff
3802 && ! mk_data(radio_altitude).ncd
3803 && ! mk_ainput(computed_airspeed).ncd
3804 && mk_data(radio_altitude).get() > 30)
3806 const EnvelopesConfiguration *c = get_ab_envelope();
3807 if (mk_ainput(computed_airspeed).get() < c->airspeed1)
3809 if (mk_dinput(landing_gear))
3811 if (! mk->io_handler.flaps_down() && mk_data(radio_altitude).get() < c->min_agl1)
3813 handle_alert(mk_alert(MODE4_TOO_LOW_FLAPS), c->min_agl1, &ab_bias);
3819 if (mk_data(radio_altitude).get() < c->min_agl1)
3821 handle_alert(mk_alert(MODE4_TOO_LOW_GEAR), c->min_agl1, &ab_bias);
3828 mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
3833 MK_VIII::Mode4Handler::update_ab_expanded ()
3835 if (! mk->io_handler.gpws_inhibit()
3836 && ! mk->state_handler.ground
3837 && ! mk->state_handler.takeoff
3838 && ! mk_data(radio_altitude).ncd
3839 && ! mk_ainput(computed_airspeed).ncd
3840 && mk_data(radio_altitude).get() > 30)
3842 const EnvelopesConfiguration *c = get_ab_envelope();
3843 if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3845 double min_agl = get_upper_agl(c);
3846 if (mk_data(radio_altitude).get() < min_agl)
3848 handle_alert(mk_alert(MODE4AB_TOO_LOW_TERRAIN), min_agl, &ab_expanded_bias);
3854 mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
3855 ab_expanded_bias=0.0;
3859 MK_VIII::Mode4Handler::update_c ()
3861 if (! mk->io_handler.gpws_inhibit()
3862 && ! mk->state_handler.ground // [1]
3863 && mk->state_handler.takeoff
3864 && ! mk_data(radio_altitude).ncd
3865 && ! mk_data(terrain_clearance).ncd
3866 && mk_data(radio_altitude).get() > 30
3867 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3868 && mk_data(radio_altitude).get() < get_upper_agl(conf.modes->ac)
3869 && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
3870 handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
3873 mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
3879 MK_VIII::Mode4Handler::update ()
3881 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3885 update_ab_expanded();
3889 ///////////////////////////////////////////////////////////////////////////////
3890 // Mode5Handler ///////////////////////////////////////////////////////////////
3891 ///////////////////////////////////////////////////////////////////////////////
3894 MK_VIII::Mode5Handler::is_hard ()
3896 if (mk_data(radio_altitude).get() > 30
3897 && mk_data(radio_altitude).get() < 300
3898 && mk_data(glideslope_deviation_dots).get() > 2)
3900 if (mk_data(radio_altitude).get() < 150)
3902 if (mk_data(radio_altitude).get() > 293 - 71.43 * mk_data(glideslope_deviation_dots).get())
3913 MK_VIII::Mode5Handler::is_soft (double bias)
3915 // do not repeat glide-slope alerts below 30ft agl
3916 if (mk_data(radio_altitude).get() > 30)
3918 double bias_dots = 1.3 * bias;
3919 if (mk_data(glideslope_deviation_dots).get() > 1.3 + bias_dots)
3921 if (mk_data(radio_altitude).get() < 150)
3923 if (mk_data(radio_altitude).get() > 243 - 71.43 * (mk_data(glideslope_deviation_dots).get() - bias_dots))
3930 if (mk_data(barometric_altitude_rate).ncd)
3934 if (mk_data(barometric_altitude_rate).get() >= 0)
3936 else if (mk_data(barometric_altitude_rate).get() < -500)
3939 upper_limit = -mk_data(barometric_altitude_rate).get() + 500;
3942 if (mk_data(radio_altitude).get() < upper_limit)
3952 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
3954 if (initial_bias < 0.0) // sanity check
3956 while ((is_soft(initial_bias))&&
3957 (initial_bias < 1.0))
3958 initial_bias += 0.2;
3960 return initial_bias;
3964 MK_VIII::Mode5Handler::update_hard (bool is)
3968 if (! mk_test_alert(MODE5_HARD))
3970 if (hard_timer.start_or_elapsed() >= 0.8)
3971 mk_set_alerts(mk_alert(MODE5_HARD));
3977 mk_unset_alerts(mk_alert(MODE5_HARD));
3982 MK_VIII::Mode5Handler::update_soft (bool is)
3986 if (! mk_test_alert(MODE5_SOFT))
3988 double new_bias = get_soft_bias(soft_bias);
3989 if (new_bias > soft_bias)
3991 soft_bias = new_bias;
3992 mk_repeat_alert(mk_alert(MODE5_SOFT));
3997 if (soft_timer.start_or_elapsed() >= 0.8)
3999 soft_bias = get_soft_bias(0.2);
4000 mk_set_alerts(mk_alert(MODE5_SOFT));
4007 mk_unset_alerts(mk_alert(MODE5_SOFT));
4012 MK_VIII::Mode5Handler::update ()
4014 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4017 if (! mk->io_handler.gpws_inhibit()
4018 && ! mk->state_handler.ground // [1]
4019 && ! mk_dinput(glideslope_inhibit) // not on backcourse
4020 && ! mk_data(radio_altitude).ncd
4021 && ! mk_data(glideslope_deviation_dots).ncd
4022 && (! mk->io_handler.conf.localizer_enabled
4023 || mk_data(localizer_deviation_dots).ncd
4024 || mk_data(radio_altitude).get() < 500
4025 || fabs(mk_data(localizer_deviation_dots).get()) < 2)
4026 && (! mk->state_handler.takeoff || mk->io_handler.flaps_down())
4027 && mk_dinput(landing_gear)
4028 && ! mk_doutput(glideslope_cancel))
4030 update_hard(is_hard());
4031 update_soft(is_soft(0));
4040 ///////////////////////////////////////////////////////////////////////////////
4041 // Mode6Handler ///////////////////////////////////////////////////////////////
4042 ///////////////////////////////////////////////////////////////////////////////
4044 // keep sorted in descending order
4045 const int MK_VIII::Mode6Handler::altitude_callout_definitions[] =
4046 { 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10 };
4049 MK_VIII::Mode6Handler::reset_minimums ()
4051 minimums_issued = false;
4055 MK_VIII::Mode6Handler::reset_altitude_callouts ()
4057 for (unsigned i = 0; i < n_altitude_callouts; i++)
4058 altitude_callouts_issued[i] = false;
4062 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
4064 for (unsigned i = 0; i < n_altitude_callouts; i++)
4065 if (mk->voice_player.voice == mk_altitude_voice(i)
4066 || mk->voice_player.next_voice == mk_altitude_voice(i))
4073 MK_VIII::Mode6Handler::is_near_minimums (double callout)
4077 if (! mk_data(decision_height).ncd)
4079 double diff = callout - mk_data(decision_height).get();
4081 if (mk_data(radio_altitude).get() >= 200)
4083 if (fabs(diff) <= 30)
4088 if (diff >= -3 && diff <= 6)
4097 MK_VIII::Mode6Handler::is_outside_band (double elevation, double callout)
4100 return elevation < callout - (elevation > 150 ? 20 : 10);
4104 MK_VIII::Mode6Handler::inhibit_smart_500 ()
4108 if (! mk_data(glideslope_deviation_dots).ncd
4109 && ! mk_dinput(glideslope_inhibit) // backcourse
4110 && ! mk_doutput(glideslope_cancel))
4112 if (mk->io_handler.conf.localizer_enabled
4113 && ! mk_data(localizer_deviation_dots).ncd)
4115 if (fabs(mk_data(localizer_deviation_dots).get()) <= 2)
4120 if (fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
4129 MK_VIII::Mode6Handler::boot ()
4131 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4134 last_decision_height = mk_dinput(decision_height);
4135 last_radio_altitude.set(&mk_data(radio_altitude));
4138 for (unsigned i = 0; i < n_altitude_callouts; i++)
4139 altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
4140 && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
4142 // extrapolated from [SPEC] 6.4.2
4143 minimums_issued = mk_dinput(decision_height);
4145 if (conf.above_field_voice)
4148 get_altitude_above_field(&last_altitude_above_field);
4149 // extrapolated from [SPEC] 6.4.2
4150 above_field_issued = ! last_altitude_above_field.ncd
4151 && last_altitude_above_field.get() < 550;
4156 MK_VIII::Mode6Handler::power_off ()
4158 runway_timer.stop();
4162 MK_VIII::Mode6Handler::enter_takeoff ()
4164 reset_altitude_callouts(); // [SPEC] 6.4.2
4165 reset_minimums(); // omitted by [SPEC]; common sense
4169 MK_VIII::Mode6Handler::leave_takeoff ()
4171 reset_altitude_callouts(); // [SPEC] 6.0.2
4172 reset_minimums(); // [SPEC] 6.0.2
4176 MK_VIII::Mode6Handler::set_volume (float volume)
4178 mk_voice(minimums_minimums)->set_volume(volume);
4179 mk_voice(five_hundred_above)->set_volume(volume);
4180 for (unsigned i = 0; i < n_altitude_callouts; i++)
4181 mk_altitude_voice(i)->set_volume(volume);
4185 MK_VIII::Mode6Handler::altitude_callouts_enabled ()
4187 if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
4190 for (unsigned i = 0; i < n_altitude_callouts; i++)
4191 if (conf.altitude_callouts_enabled[i])
4198 MK_VIII::Mode6Handler::update_minimums ()
4200 if (! mk->io_handler.gpws_inhibit()
4201 && (mk->voice_player.voice == mk_voice(minimums_minimums)
4202 || mk->voice_player.next_voice == mk_voice(minimums_minimums)))
4205 if (! mk->io_handler.gpws_inhibit()
4206 && ! mk->state_handler.ground // [1]
4207 && conf.minimums_enabled
4208 && ! minimums_issued
4209 && mk_dinput(landing_gear)
4210 && mk_dinput(decision_height)
4211 && ! last_decision_height)
4213 minimums_issued = true;
4215 // If an altitude callout is playing, stop it now so that the
4216 // minimums callout can be played (note that proper connection
4217 // and synchronization of the radio-altitude ARINC 529 input,
4218 // decision-height discrete and decision-height ARINC 529 input
4219 // will prevent an altitude callout from being played near the
4220 // decision height).
4222 if (is_playing_altitude_callout())
4223 mk->voice_player.stop(VoicePlayer::STOP_NOW);
4225 mk_set_alerts(mk_alert(MODE6_MINIMUMS));
4228 mk_unset_alerts(mk_alert(MODE6_MINIMUMS));
4231 last_decision_height = mk_dinput(decision_height);
4235 MK_VIII::Mode6Handler::update_altitude_callouts ()
4237 if (! mk->io_handler.gpws_inhibit() && is_playing_altitude_callout())
4240 if (! mk->io_handler.gpws_inhibit()
4241 && ! mk->state_handler.ground // [1]
4242 && ! mk_data(radio_altitude).ncd)
4243 for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
4244 if ((conf.altitude_callouts_enabled[i]
4245 || (altitude_callout_definitions[i] == 500
4246 && conf.smart_500_enabled))
4247 && ! altitude_callouts_issued[i]
4248 && (last_radio_altitude.ncd
4249 || last_radio_altitude.get() > altitude_callout_definitions[i]))
4251 // lock out all callouts superior or equal to this one
4252 for (unsigned j = 0; j <= i; j++)
4253 altitude_callouts_issued[j] = true;
4255 altitude_callouts_issued[i] = true;
4256 if (! is_near_minimums(altitude_callout_definitions[i])
4257 && ! is_outside_band(mk_data(radio_altitude).get(), altitude_callout_definitions[i])
4258 && (! conf.smart_500_enabled
4259 || altitude_callout_definitions[i] != 500
4260 || ! inhibit_smart_500()))
4262 mk->alert_handler.set_altitude_callout_alert(mk_altitude_voice(i));
4267 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4270 last_radio_altitude.set(&mk_data(radio_altitude));
4274 MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
4276 if (_runway->lengthFt() < mk->conf.runway_database)
4280 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
4282 // get distance to threshold
4283 double distance, az1, az2;
4284 SGGeodesy::inverse(pos, _runway->threshold(), az1, az2, distance);
4285 return distance * SG_METER_TO_NM <= 5;
4289 MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
4291 for (unsigned int r=0; r<airport->numRunways(); ++r) {
4292 FGRunway* rwy(airport->getRunwayByIndex(r));
4294 if (test_runway(rwy)) return true;
4300 bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
4302 bool ok = self->test_airport(a);
4307 MK_VIII::Mode6Handler::update_runway ()
4310 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4315 // Search for the closest runway threshold in range 5
4316 // nm. Passing 30nm to
4317 // get_closest_airport() provides enough margin for large
4318 // airports, which may have a runway located far away from the
4319 // airport's reference point.
4320 AirportFilter filter(this);
4321 FGPositionedRef apt = FGPositioned::findClosest(
4322 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4325 runway.elevation = apt->elevation();
4328 has_runway.set(apt != NULL);
4332 MK_VIII::Mode6Handler::get_altitude_above_field (Parameter<double> *parameter)
4334 if (! has_runway.ncd && has_runway.get() && ! mk_data(geometric_altitude).ncd)
4335 parameter->set(mk_data(geometric_altitude).get() - runway.elevation);
4341 MK_VIII::Mode6Handler::update_above_field_callout ()
4343 if (! conf.above_field_voice)
4346 // update_runway() has to iterate over the whole runway database
4347 // (which contains thousands of runways), so it would be unwise to
4348 // run it every frame. Run it every 3 seconds. Note that the first
4349 // iteration is run in boot().
4350 if (runway_timer.start_or_elapsed() >= 3)
4353 runway_timer.start();
4356 Parameter<double> altitude_above_field;
4357 get_altitude_above_field(&altitude_above_field);
4359 if (! mk->io_handler.gpws_inhibit()
4360 && (mk->voice_player.voice == conf.above_field_voice
4361 || mk->voice_player.next_voice == conf.above_field_voice))
4364 // handle reset term
4365 if (above_field_issued)
4367 if ((! has_runway.ncd && ! has_runway.get())
4368 || (! altitude_above_field.ncd && altitude_above_field.get() > 700))
4369 above_field_issued = false;
4372 if (! mk->io_handler.gpws_inhibit()
4373 && ! mk->state_handler.ground // [1]
4374 && ! above_field_issued
4375 && ! altitude_above_field.ncd
4376 && altitude_above_field.get() < 550
4377 && (last_altitude_above_field.ncd
4378 || last_altitude_above_field.get() >= 550))
4380 above_field_issued = true;
4382 if (! is_outside_band(altitude_above_field.get(), 500))
4384 mk->alert_handler.set_altitude_callout_alert(conf.above_field_voice);
4389 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4392 last_altitude_above_field.set(&altitude_above_field);
4396 MK_VIII::Mode6Handler::is_bank_angle (double abs_roll_angle, double bias)
4398 return conf.is_bank_angle(&mk_data(radio_altitude),
4399 abs_roll_angle - abs_roll_angle * bias,
4400 mk_dinput(autopilot_engaged));
4404 MK_VIII::Mode6Handler::is_high_bank_angle ()
4406 return mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() >= 210;
4410 MK_VIII::Mode6Handler::get_bank_angle_alerts ()
4412 if (! mk->io_handler.gpws_inhibit()
4413 && ! mk->state_handler.ground // [1]
4414 && ! mk_data(roll_angle).ncd)
4416 double abs_roll_angle = fabs(mk_data(roll_angle).get());
4418 if (is_bank_angle(abs_roll_angle, 0.4))
4419 return is_high_bank_angle()
4420 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2) | mk_alert(MODE6_HIGH_BANK_ANGLE_3))
4421 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2) | mk_alert(MODE6_LOW_BANK_ANGLE_3));
4422 else if (is_bank_angle(abs_roll_angle, 0.2))
4423 return is_high_bank_angle()
4424 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2))
4425 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2));
4426 else if (is_bank_angle(abs_roll_angle, 0))
4427 return is_high_bank_angle()
4428 ? mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4429 : mk_alert(MODE6_LOW_BANK_ANGLE_1);
4436 MK_VIII::Mode6Handler::update_bank_angle ()
4438 if (! conf.bank_angle_enabled)
4441 unsigned int alerts = get_bank_angle_alerts();
4443 // [SPEC] 6.4.4 specifies that the non-continuous alerts
4444 // (MODE6_*_BANK_ANGLE_1 and MODE6_*_BANK_ANGLE_2) are locked out
4445 // until the initial envelope is exited.
4447 if (! test_bits(alerts, mk_alert(MODE6_LOW_BANK_ANGLE_3)))
4448 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_3));
4449 if (! test_bits(alerts, mk_alert(MODE6_HIGH_BANK_ANGLE_3)))
4450 mk_unset_alerts(mk_alert(MODE6_HIGH_BANK_ANGLE_3));
4453 mk_set_alerts(alerts);
4455 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_1)
4456 | mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4457 | mk_alert(MODE6_LOW_BANK_ANGLE_2)
4458 | mk_alert(MODE6_HIGH_BANK_ANGLE_2));
4462 MK_VIII::Mode6Handler::update ()
4464 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4467 if (! mk->state_handler.takeoff
4468 && ! mk_data(radio_altitude).ncd
4469 && mk_data(radio_altitude).get() > 1000)
4471 reset_altitude_callouts(); // [SPEC] 6.4.2
4472 reset_minimums(); // common sense
4476 update_altitude_callouts();
4477 update_above_field_callout();
4478 update_bank_angle();
4481 ///////////////////////////////////////////////////////////////////////////////
4482 // TCFHandler /////////////////////////////////////////////////////////////////
4483 ///////////////////////////////////////////////////////////////////////////////
4485 // Gets the difference between the azimuth from @from_lat,@from_lon to
4486 // @to_lat,@to_lon, and @to_heading, in degrees.
4488 MK_VIII::TCFHandler::get_azimuth_difference (double from_lat,
4494 double az1, az2, distance;
4495 geo_inverse_wgs_84(0, from_lat, from_lon, to_lat, to_lon, &az1, &az2, &distance);
4496 return get_heading_difference(az1, to_heading);
4499 // Gets the difference between the azimuth from the current GPS
4500 // position to the center of @_runway, and the heading of @_runway, in
4503 MK_VIII::TCFHandler::get_azimuth_difference (const FGRunway *_runway)
4505 return get_azimuth_difference(mk_data(gps_latitude).get(),
4506 mk_data(gps_longitude).get(),
4507 _runway->latitude(),
4508 _runway->longitude(),
4509 _runway->headingDeg());
4512 // Selects the most likely intended destination runway of @airport,
4513 // and returns it in @_runway. For each runway, the difference between
4514 // the azimuth from the current GPS position to the center of the
4515 // runway and its heading is computed. The runway having the smallest
4518 // This selection algorithm is not specified in [SPEC], but
4519 // http://www.egpws.com/general_information/description/runway_select.htm
4520 // talks about automatic runway selection.
4522 MK_VIII::TCFHandler::select_runway (const FGAirport *airport)
4524 FGRunway* _runway = 0;
4525 double min_diff = 360;
4527 for (unsigned int r=0; r<airport->numRunways(); ++r) {
4528 FGRunway* rwy(airport->getRunwayByIndex(r));
4529 double diff = get_azimuth_difference(rwy);
4530 if (diff < min_diff)
4535 } // of airport runways iteration
4539 bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
4541 return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
4545 MK_VIII::TCFHandler::update_runway ()
4548 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4552 // Search for the intended destination runway of the closest
4553 // airport in range 15 nm. Passing 30nm to get_closest_airport()
4554 // provides enough margin for
4555 // large airports, which may have a runway located far away from
4556 // the airport's reference point.
4557 AirportFilter filter(mk);
4558 FGAirport* apt = FGAirport::findClosest(
4559 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4564 FGRunway* _runway = select_runway(apt);
4566 if (!_runway) return;
4570 runway.center.latitude = _runway->latitude();
4571 runway.center.longitude = _runway->longitude();
4573 runway.elevation = apt->elevation();
4575 double half_length_m = _runway->lengthM() * 0.5;
4576 runway.half_length = half_length_m * SG_METER_TO_NM;
4578 // b3 ________________ b0
4580 // h1>>> | e1<<<<<<<<e0 | <<<h0
4581 // |________________|
4584 // get heading to runway threshold (h0) and end (h1)
4585 runway.edges[0].heading = _runway->headingDeg();
4586 runway.edges[1].heading = get_reciprocal_heading(_runway->headingDeg());
4590 // get position of runway threshold (e0)
4591 geo_direct_wgs_84(0,
4592 runway.center.latitude,
4593 runway.center.longitude,
4594 runway.edges[1].heading,
4596 &runway.edges[0].position.latitude,
4597 &runway.edges[0].position.longitude,
4600 // get position of runway end (e1)
4601 geo_direct_wgs_84(0,
4602 runway.center.latitude,
4603 runway.center.longitude,
4604 runway.edges[0].heading,
4606 &runway.edges[1].position.latitude,
4607 &runway.edges[1].position.longitude,
4610 double half_width_m = _runway->widthM() * 0.5;
4612 // get position of threshold bias area edges (b0 and b1)
4613 get_bias_area_edges(&runway.edges[0].position,
4614 runway.edges[1].heading,
4616 &runway.bias_area[0],
4617 &runway.bias_area[1]);
4619 // get position of end bias area edges (b2 and b3)
4620 get_bias_area_edges(&runway.edges[1].position,
4621 runway.edges[0].heading,
4623 &runway.bias_area[2],
4624 &runway.bias_area[3]);
4628 MK_VIII::TCFHandler::get_bias_area_edges (Position *edge,
4630 double half_width_m,
4631 Position *bias_edge1,
4632 Position *bias_edge2)
4634 double half_bias_width_m = k * SG_NM_TO_METER + half_width_m;
4635 double tmp_latitude = 0.0, tmp_longitude = 0.0, az = 0.0;
4637 geo_direct_wgs_84(0,
4645 geo_direct_wgs_84(0,
4648 heading_substract(reciprocal, 90),
4650 &bias_edge1->latitude,
4651 &bias_edge1->longitude,
4653 geo_direct_wgs_84(0,
4656 heading_add(reciprocal, 90),
4658 &bias_edge2->latitude,
4659 &bias_edge2->longitude,
4663 // Returns true if the current GPS position is inside the edge
4664 // triangle of @edge. The edge triangle, which is specified and
4665 // represented in [SPEC] 6.3.1, is defined as an isocele right
4666 // triangle of infinite height, whose right angle is located at the
4667 // position of @edge, and whose height is parallel to the heading of
4670 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
4672 return get_azimuth_difference(mk_data(gps_latitude).get(),
4673 mk_data(gps_longitude).get(),
4674 edge->position.latitude,
4675 edge->position.longitude,
4676 edge->heading) <= 45;
4679 // Returns true if the current GPS position is inside the bias area of
4680 // the currently selected runway.
4682 MK_VIII::TCFHandler::is_inside_bias_area ()
4685 double angles_sum = 0;
4687 for (int i = 0; i < 4; i++)
4689 double az2, distance;
4690 geo_inverse_wgs_84(0,
4691 mk_data(gps_latitude).get(),
4692 mk_data(gps_longitude).get(),
4693 runway.bias_area[i].latitude,
4694 runway.bias_area[i].longitude,
4695 &az1[i], &az2, &distance);
4698 for (int i = 0; i < 4; i++)
4700 double angle = az1[i == 3 ? 0 : i + 1] - az1[i];
4704 angles_sum += angle;
4707 return angles_sum > 180;
4711 MK_VIII::TCFHandler::is_tcf ()
4713 if (mk_data(radio_altitude).get() > 10)
4717 double distance, az1, az2;
4719 geo_inverse_wgs_84(0,
4720 mk_data(gps_latitude).get(),
4721 mk_data(gps_longitude).get(),
4722 runway.center.latitude,
4723 runway.center.longitude,
4724 &az1, &az2, &distance);
4726 distance *= SG_METER_TO_NM;
4728 // distance to the inner envelope edge
4729 double edge_distance = distance - runway.half_length - k;
4731 if (edge_distance >= 15)
4733 if (mk_data(radio_altitude).get() < 700)
4736 else if (edge_distance >= 12)
4738 if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
4741 else if (edge_distance >= 4)
4743 if (mk_data(radio_altitude).get() < 400)
4746 else if (edge_distance >= 2.45)
4748 if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4753 if (is_inside_edge_triangle(&runway.edges[0]) || is_inside_edge_triangle(&runway.edges[1]))
4755 if (edge_distance >= 1)
4757 if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4760 else if (edge_distance >= 0.05)
4762 if (mk_data(radio_altitude).get() < 200 * edge_distance)
4768 if (! is_inside_bias_area())
4770 if (mk_data(radio_altitude).get() < 245)
4778 if (mk_data(radio_altitude).get() < 700)
4787 MK_VIII::TCFHandler::is_rfcf ()
4791 double distance, az1, az2;
4792 geo_inverse_wgs_84(0,
4793 mk_data(gps_latitude).get(),
4794 mk_data(gps_longitude).get(),
4795 runway.center.latitude,
4796 runway.center.longitude,
4797 &az1, &az2, &distance);
4799 double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
4800 distance = distance * SG_METER_TO_NM - runway.half_length - krf;
4804 double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
4806 if (distance >= 1.5)
4808 if (altitude_above_field < 300)
4811 else if (distance >= 0)
4813 if (altitude_above_field < 200 * distance)
4823 MK_VIII::TCFHandler::update ()
4825 if (mk->configuration_module.state != ConfigurationModule::STATE_OK || ! conf.enabled)
4828 // update_runway() has to iterate over the whole runway database
4829 // (which contains thousands of runways), so it would be unwise to
4830 // run it every frame. Run it every 3 seconds.
4831 if (! runway_timer.running || runway_timer.elapsed() >= 3)
4834 runway_timer.start();
4837 if (! mk_dinput(ta_tcf_inhibit)
4838 && ! mk->state_handler.ground // [1]
4839 && ! mk_data(gps_latitude).ncd
4840 && ! mk_data(gps_longitude).ncd
4841 && ! mk_data(radio_altitude).ncd
4842 && ! mk_data(geometric_altitude).ncd
4843 && ! mk_data(gps_vertical_figure_of_merit).ncd)
4848 _reference = mk_data(radio_altitude).get_pointer();
4850 _reference = mk_data(geometric_altitude).get_pointer();
4856 if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
4858 double new_bias = bias;
4859 // do not repeat terrain alerts below 30ft agl
4860 if (mk_data(radio_altitude).get() > 30)
4862 if (new_bias < 0.0) // sanity check
4864 while ((*reference < initial_value - initial_value * new_bias)&&
4869 if (new_bias > bias)
4872 mk_repeat_alert(mk_alert(TCF_TOO_LOW_TERRAIN));
4878 reference = _reference;
4879 initial_value = *reference;
4880 mk_set_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4887 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4890 ///////////////////////////////////////////////////////////////////////////////
4891 // MK_VIII ////////////////////////////////////////////////////////////////////
4892 ///////////////////////////////////////////////////////////////////////////////
4894 MK_VIII::MK_VIII (SGPropertyNode *node)
4895 : properties_handler(this),
4898 power_handler(this),
4899 system_handler(this),
4900 configuration_module(this),
4901 fault_handler(this),
4904 self_test_handler(this),
4905 alert_handler(this),
4906 state_handler(this),
4907 mode1_handler(this),
4908 mode2_handler(this),
4909 mode3_handler(this),
4910 mode4_handler(this),
4911 mode5_handler(this),
4912 mode6_handler(this),
4915 for (int i = 0; i < node->nChildren(); ++i)
4917 SGPropertyNode *child = node->getChild(i);
4918 string cname = child->getName();
4919 string cval = child->getStringValue();
4921 if (cname == "name")
4923 else if (cname == "number")
4924 num = child->getIntValue();
4927 SG_LOG(SG_INSTR, SG_WARN, "Error in mk-viii config logic");
4929 SG_LOG(SG_INSTR, SG_WARN, "Section = " << name);
4937 properties_handler.init();
4938 voice_player.init();
4944 SGPropertyNode *node = fgGetNode(("/instrumentation/" + name).c_str(), num, true);
4946 configuration_module.bind(node);
4947 power_handler.bind(node);
4948 io_handler.bind(node);
4949 voice_player.bind(node);
4955 properties_handler.unbind();
4959 MK_VIII::update (double dt)
4961 power_handler.update();
4962 system_handler.update();
4964 if (system_handler.state != SystemHandler::STATE_ON)
4967 io_handler.update_inputs();
4968 io_handler.update_input_faults();
4970 voice_player.update();
4971 state_handler.update();
4973 if (self_test_handler.update())
4976 io_handler.update_internal_latches();
4977 io_handler.update_lamps();
4979 mode1_handler.update();
4980 mode2_handler.update();
4981 mode3_handler.update();
4982 mode4_handler.update();
4983 mode5_handler.update();
4984 mode6_handler.update();
4985 tcf_handler.update();
4987 alert_handler.update();
4988 io_handler.update_outputs();