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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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/SGMathFwd.hxx>
70 #include <simgear/math/SGLimits.hxx>
71 #include <simgear/math/SGGeometryFwd.hxx>
72 #include <simgear/math/SGGeodesy.hxx>
73 #include <simgear/math/sg_random.h>
74 #include <simgear/math/SGLineSegment.hxx>
75 #include <simgear/math/SGIntersect.hxx>
76 #include <simgear/misc/sg_path.hxx>
77 #include <simgear/sound/soundmgr_openal.hxx>
78 #include <simgear/sound/sample_group.hxx>
79 #include <simgear/structure/exception.hxx>
83 #include <Airports/runways.hxx>
84 #include <Airports/airport.hxx>
85 #include <Include/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 get_heading_difference (double h1, double h2)
141 double diff = h1 - h2;
151 ///////////////////////////////////////////////////////////////////////////////
152 // PropertiesHandler //////////////////////////////////////////////////////////
153 ///////////////////////////////////////////////////////////////////////////////
156 MK_VIII::PropertiesHandler::init ()
158 mk_node(ai_caged) = fgGetNode("/instrumentation/attitude-indicator/caged-flag", true);
159 mk_node(ai_roll) = fgGetNode("/instrumentation/attitude-indicator/indicated-roll-deg", true);
160 mk_node(ai_serviceable) = fgGetNode("/instrumentation/attitude-indicator/serviceable", true);
161 mk_node(altimeter_altitude) = fgGetNode("/instrumentation/altimeter/indicated-altitude-ft", true);
162 mk_node(altimeter_serviceable) = fgGetNode("/instrumentation/altimeter/serviceable", true);
163 mk_node(altitude) = fgGetNode("/position/altitude-ft", true);
164 mk_node(altitude_agl) = fgGetNode("/position/altitude-agl-ft", true);
165 mk_node(altitude_gear_agl) = fgGetNode("/position/gear-agl-ft", true);
166 mk_node(altitude_radar_agl) = fgGetNode("/instrumentation/radar-altimeter/radar-altitude-ft", true);
167 mk_node(orientation_roll) = fgGetNode("/orientation/roll-deg", true);
168 mk_node(asi_serviceable) = fgGetNode("/instrumentation/airspeed-indicator/serviceable", true);
169 mk_node(asi_speed) = fgGetNode("/instrumentation/airspeed-indicator/indicated-speed-kt", true);
170 mk_node(autopilot_heading_lock) = fgGetNode("/autopilot/locks/heading", true);
171 mk_node(flaps) = fgGetNode("/controls/flight/flaps", true);
172 mk_node(gear_down) = fgGetNode("/controls/gear/gear-down", true);
173 mk_node(latitude) = fgGetNode("/position/latitude-deg", true);
174 mk_node(longitude) = fgGetNode("/position/longitude-deg", true);
175 mk_node(nav0_cdi_serviceable) = fgGetNode("/instrumentation/nav/cdi/serviceable", true);
176 mk_node(nav0_gs_distance) = fgGetNode("/instrumentation/nav/gs-distance", true);
177 mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection-deg", true);
178 mk_node(nav0_gs_serviceable) = fgGetNode("/instrumentation/nav/gs/serviceable", true);
179 mk_node(nav0_has_gs) = fgGetNode("/instrumentation/nav/has-gs", true);
180 mk_node(nav0_heading_needle_deflection) = fgGetNode("/instrumentation/nav/heading-needle-deflection", true);
181 mk_node(nav0_in_range) = fgGetNode("/instrumentation/nav/in-range", true);
182 mk_node(nav0_nav_loc) = fgGetNode("/instrumentation/nav/nav-loc", true);
183 mk_node(nav0_serviceable) = fgGetNode("/instrumentation/nav/serviceable", true);
184 mk_node(power) = fgGetNode(("/systems/electrical/outputs/" + mk->name).c_str(), mk->num, true);
185 mk_node(replay_state) = fgGetNode("/sim/freeze/replay-state", true);
186 mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
189 ///////////////////////////////////////////////////////////////////////////////
190 // PowerHandler ///////////////////////////////////////////////////////////////
191 ///////////////////////////////////////////////////////////////////////////////
194 MK_VIII::PowerHandler::bind (SGPropertyNode *node)
196 mk->properties_handler.tie(node, "serviceable", SGRawValuePointer<bool>(&serviceable));
200 MK_VIII::PowerHandler::handle_abnormal_voltage (bool abnormal,
206 if (timer->start_or_elapsed() >= max_duration)
207 return true; // power loss
216 MK_VIII::PowerHandler::update ()
218 double voltage = mk_node(power)->getDoubleValue();
219 bool now_powered = true;
227 if (handle_abnormal_voltage(voltage < 20.5, &low_surge_timer, 0.03))
229 if (handle_abnormal_voltage(voltage < 22.0 || voltage > 30.3, &abnormal_timer, 300))
231 if (handle_abnormal_voltage(voltage > 32.2, &high_surge_timer, 1))
233 if (handle_abnormal_voltage(voltage > 37.8, &very_high_surge_timer, 0.1))
243 power_loss_timer.stop();
246 if (power_loss_timer.start_or_elapsed() >= 0.2)
258 MK_VIII::PowerHandler::power_on ()
261 mk->system_handler.power_on();
265 MK_VIII::PowerHandler::power_off ()
268 mk->system_handler.power_off();
269 mk->voice_player.stop(VoicePlayer::STOP_NOW);
270 mk->self_test_handler.power_off(); // run before IOHandler::power_off()
271 mk->io_handler.power_off();
272 mk->mode2_handler.power_off();
273 mk->mode6_handler.power_off();
276 ///////////////////////////////////////////////////////////////////////////////
277 // SystemHandler //////////////////////////////////////////////////////////////
278 ///////////////////////////////////////////////////////////////////////////////
281 MK_VIII::SystemHandler::power_on ()
283 state = STATE_BOOTING;
285 // [SPEC] 3.5.2 mentions a 20 seconds maximum boot delay. We use a
286 // random delay between 3 and 5 seconds.
288 boot_delay = 3 + sg_random() * 2;
293 MK_VIII::SystemHandler::power_off ()
301 MK_VIII::SystemHandler::update ()
303 if (state == STATE_BOOTING)
305 if (boot_timer.elapsed() >= boot_delay)
307 last_replay_state = mk_node(replay_state)->getIntValue();
309 mk->configuration_module.boot();
311 mk->io_handler.boot();
312 mk->fault_handler.boot();
313 mk->alert_handler.boot();
315 // inputs are needed by the following boot() routines
316 mk->io_handler.update_inputs();
318 mk->mode2_handler.boot();
319 mk->mode6_handler.boot();
323 mk->io_handler.post_boot();
326 else if (state != STATE_OFF && mk->configuration_module.state == ConfigurationModule::STATE_OK)
328 // If the replay state changes, switch to reposition mode for 3
329 // seconds ([SPEC] 6.0.5) to avoid spurious alerts.
331 int replay_state = mk_node(replay_state)->getIntValue();
332 if (replay_state != last_replay_state)
334 mk->alert_handler.reposition();
335 mk->io_handler.reposition();
337 last_replay_state = replay_state;
338 state = STATE_REPOSITION;
339 reposition_timer.start();
342 if (state == STATE_REPOSITION && reposition_timer.elapsed() >= 3)
344 // inputs are needed by StateHandler::post_reposition()
345 mk->io_handler.update_inputs();
347 mk->state_handler.post_reposition();
354 ///////////////////////////////////////////////////////////////////////////////
355 // ConfigurationModule ////////////////////////////////////////////////////////
356 ///////////////////////////////////////////////////////////////////////////////
358 MK_VIII::ConfigurationModule::ConfigurationModule (MK_VIII *device)
361 // arbitrary defaults
362 categories[CATEGORY_AIRCRAFT_MODE_TYPE_SELECT] = 0;
363 categories[CATEGORY_AIR_DATA_INPUT_SELECT] = 1;
364 categories[CATEGORY_POSITION_INPUT_SELECT] = 2;
365 categories[CATEGORY_ALTITUDE_CALLOUTS] = 13;
366 categories[CATEGORY_AUDIO_MENU_SELECT] = 0;
367 categories[CATEGORY_TERRAIN_DISPLAY_SELECT] = 1;
368 categories[CATEGORY_OPTIONS_SELECT_GROUP_1] = 124;
369 categories[CATEGORY_RADIO_ALTITUDE_INPUT_SELECT] = 2;
370 categories[CATEGORY_NAVIGATION_INPUT_SELECT] = 3;
371 categories[CATEGORY_ATTITUDE_INPUT_SELECT] = 6;
372 categories[CATEGORY_HEADING_INPUT_SELECT] = 2;
373 categories[CATEGORY_WINDSHEAR_INPUT_SELECT] = 0;
374 categories[CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT] = 7;
375 categories[CATEGORY_AUDIO_OUTPUT_LEVEL] = 0;
376 categories[CATEGORY_UNDEFINED_INPUT_SELECT_1] = 0;
377 categories[CATEGORY_UNDEFINED_INPUT_SELECT_2] = 0;
378 categories[CATEGORY_UNDEFINED_INPUT_SELECT_3] = 0;
381 static double m1_t1_min_agl1 (double vs) { return -1620 - 1.1133 * vs; }
382 static double m1_t1_min_agl2 (double vs) { return -400 - 0.4 * vs; }
383 static double m1_t4_min_agl1 (double vs) { return -1625.47 - 1.1167 * vs; }
384 static double m1_t4_min_agl2 (double vs) { return -0.1958 * vs; }
386 static int m3_t1_max_agl (bool flap_override) { return 1500; }
387 static double m3_t1_max_alt_loss (bool flap_override, double agl) { return 5.4 + 0.092 * agl; }
388 static int m3_t2_max_agl (bool flap_override) { return flap_override ? 815 : 925; }
389 static double m3_t2_max_alt_loss (bool flap_override, double agl)
391 int bias = agl > 700 ? 5 : 0;
394 return (9.0 + 0.184 * agl) + bias;
396 return (5.4 + 0.092 * agl) + bias;
399 static double m4_t1_min_agl2 (double airspeed) { return -1083 + 8.333 * airspeed; }
400 static double m4_t568_a_min_agl2 (double airspeed) { return -1523 + 11.36 * airspeed; }
401 static double m4_t79_a_min_agl2 (double airspeed) { return -1182 + 11.36 * airspeed; }
402 static double m4_t568_b_min_agl2 (double airspeed) { return -1570 + 11.6 * airspeed; }
403 static double m4_t79_b_min_agl2 (double airspeed) { return -1222 + 11.6 * airspeed; }
406 MK_VIII::ConfigurationModule::m6_t2_is_bank_angle (Parameter<double> *agl,
412 if (agl->ncd || agl->get() > 122)
413 return abs_roll_deg > 33;
417 if (agl->ncd || agl->get() > 2450)
418 return abs_roll_deg > 55;
419 else if (agl->get() > 150)
420 return agl->get() < 153.33333 * abs_roll_deg - 5983.3333;
424 return agl->get() < 4 * abs_roll_deg - 10;
425 else if (agl->get() > 5)
426 return abs_roll_deg > 10;
432 MK_VIII::ConfigurationModule::m6_t4_is_bank_angle (Parameter<double> *agl,
438 if (agl->ncd || agl->get() > 156)
439 return abs_roll_deg > 33;
443 if (agl->ncd || agl->get() > 210)
444 return abs_roll_deg > 50;
448 return agl->get() < 5.7142857 * abs_roll_deg - 75.714286;
454 MK_VIII::ConfigurationModule::read_aircraft_mode_type_select (int value)
456 static const Mode1Handler::EnvelopesConfiguration m1_t1 =
457 { false, 10, m1_t1_min_agl1, 284, m1_t1_min_agl2, 2450 };
458 static const Mode1Handler::EnvelopesConfiguration m1_t4 =
459 { true, 50, m1_t4_min_agl1, 346, m1_t4_min_agl2, 1958 };
461 static const Mode2Handler::Configuration m2_t1 = { 190, 280 };
462 static const Mode2Handler::Configuration m2_t4 = { 220, 310 };
463 static const Mode2Handler::Configuration m2_t5 = { 220, 310 };
465 static const Mode3Handler::Configuration m3_t1 =
466 { 30, m3_t1_max_agl, m3_t1_max_alt_loss };
467 static const Mode3Handler::Configuration m3_t2 =
468 { 50, m3_t2_max_agl, m3_t2_max_alt_loss };
470 static const Mode4Handler::EnvelopesConfiguration m4_t1_ac =
471 { 190, 250, 500, m4_t1_min_agl2, 1000 };
472 static const Mode4Handler::EnvelopesConfiguration m4_t5_ac =
473 { 178, 200, 500, m4_t568_a_min_agl2, 1000 };
474 static const Mode4Handler::EnvelopesConfiguration m4_t68_ac =
475 { 178, 200, 500, m4_t568_a_min_agl2, 750 };
476 static const Mode4Handler::EnvelopesConfiguration m4_t79_ac =
477 { 148, 170, 500, m4_t79_a_min_agl2, 750 };
479 static const Mode4Handler::EnvelopesConfiguration m4_t1_b =
480 { 159, 250, 245, m4_t1_min_agl2, 1000 };
481 static const Mode4Handler::EnvelopesConfiguration m4_t5_b =
482 { 148, 200, 200, m4_t568_b_min_agl2, 1000 };
483 static const Mode4Handler::EnvelopesConfiguration m4_t6_b =
484 { 150, 200, 170, m4_t568_b_min_agl2, 750 };
485 static const Mode4Handler::EnvelopesConfiguration m4_t7_b =
486 { 120, 170, 170, m4_t79_b_min_agl2, 750 };
487 static const Mode4Handler::EnvelopesConfiguration m4_t8_b =
488 { 148, 200, 150, m4_t568_b_min_agl2, 750 };
489 static const Mode4Handler::EnvelopesConfiguration m4_t9_b =
490 { 118, 170, 150, m4_t79_b_min_agl2, 750 };
492 static const Mode4Handler::ModesConfiguration m4_t1 = { &m4_t1_ac, &m4_t1_b };
493 static const Mode4Handler::ModesConfiguration m4_t5 = { &m4_t5_ac, &m4_t5_b };
494 static const Mode4Handler::ModesConfiguration m4_t6 = { &m4_t68_ac, &m4_t6_b };
495 static const Mode4Handler::ModesConfiguration m4_t7 = { &m4_t79_ac, &m4_t7_b };
496 static const Mode4Handler::ModesConfiguration m4_t8 = { &m4_t68_ac, &m4_t8_b };
497 static const Mode4Handler::ModesConfiguration m4_t9 = { &m4_t79_ac, &m4_t9_b };
499 static Mode6Handler::BankAnglePredicate m6_t2 = m6_t2_is_bank_angle;
500 static Mode6Handler::BankAnglePredicate m6_t4 = m6_t4_is_bank_angle;
502 static const IOHandler::FaultsConfiguration f_slow = { 180, 200 };
503 static const IOHandler::FaultsConfiguration f_fast = { 250, 290 };
508 const Mode1Handler::EnvelopesConfiguration *m1;
509 const Mode2Handler::Configuration *m2;
510 const Mode3Handler::Configuration *m3;
511 const Mode4Handler::ModesConfiguration *m4;
512 Mode6Handler::BankAnglePredicate m6;
513 const IOHandler::FaultsConfiguration *f;
515 } aircraft_types[] = {
516 { 0, &m1_t4, &m2_t4, &m3_t2, &m4_t6, m6_t4, &f_fast, 2000 },
517 { 1, &m1_t4, &m2_t4, &m3_t2, &m4_t8, m6_t4, &f_fast, 2000 },
518 { 2, &m1_t4, &m2_t4, &m3_t2, &m4_t5, m6_t4, &f_fast, 2000 },
519 { 3, &m1_t4, &m2_t5, &m3_t2, &m4_t7, m6_t4, &f_slow, 2000 },
520 { 4, &m1_t4, &m2_t5, &m3_t2, &m4_t9, m6_t4, &f_slow, 2000 },
521 { 254, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 3500 },
522 { 255, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 2000 }
525 for (size_t i = 0; i < n_elements(aircraft_types); i++)
526 if (aircraft_types[i].type == value)
528 mk->mode1_handler.conf.envelopes = aircraft_types[i].m1;
529 mk->mode2_handler.conf = aircraft_types[i].m2;
530 mk->mode3_handler.conf = aircraft_types[i].m3;
531 mk->mode4_handler.conf.modes = aircraft_types[i].m4;
532 mk->mode6_handler.conf.is_bank_angle = aircraft_types[i].m6;
533 mk->io_handler.conf.faults = aircraft_types[i].f;
534 mk->conf.runway_database = aircraft_types[i].runway_database;
538 state = STATE_INVALID_AIRCRAFT_TYPE;
543 MK_VIII::ConfigurationModule::read_air_data_input_select (int value)
546 return (value >= 0 && value <= 6) || (value >= 10 && value <= 13) || value == 255;
550 MK_VIII::ConfigurationModule::read_position_input_select (int value)
553 mk->io_handler.conf.use_internal_gps = true;
554 else if ((value >= 0 && value <= 5)
555 || (value >= 10 && value <= 13)
558 mk->io_handler.conf.use_internal_gps = false;
566 MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
581 { 0, { MINIMUMS, SMART_500, 200, 100, 50, 40, 30, 20, 10, 0 } },
582 { 1, { MINIMUMS, SMART_500, 200, 0 } },
583 { 2, { MINIMUMS, SMART_500, 100, 50, 40, 30, 20, 10, 0 } },
584 { 3, { MINIMUMS, SMART_500, 0 } },
585 { 4, { MINIMUMS, 200, 100, 50, 40, 30, 20, 10, 0 } },
586 { 5, { MINIMUMS, 200, 0 } },
587 { 6, { MINIMUMS, 100, 50, 40, 30, 20, 10, 0 } },
589 { 8, { MINIMUMS, 0 } },
590 { 9, { MINIMUMS, 500, 200, 100, 50, 40, 30, 20, 10, 0 } },
591 { 10, { MINIMUMS, 500, 200, 0 } },
592 { 11, { MINIMUMS, 500, 100, 50, 40, 30, 20, 10, 0 } },
593 { 12, { MINIMUMS, 500, 0 } },
594 { 13, { MINIMUMS, 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10, 0 } },
595 { 14, { MINIMUMS, 100, 0 } },
596 { 15, { MINIMUMS, 200, 100, 0 } },
597 { 100, { FIELD_500, 0 } },
598 { 101, { FIELD_500_ABOVE, 0 } }
603 mk->mode6_handler.conf.minimums_enabled = false;
604 mk->mode6_handler.conf.smart_500_enabled = false;
605 mk->mode6_handler.conf.above_field_voice = NULL;
606 for (i = 0; i < n_altitude_callouts; i++)
607 mk->mode6_handler.conf.altitude_callouts_enabled[i] = false;
609 for (i = 0; i < n_elements(values); i++)
610 if (values[i].id == value)
612 for (int j = 0; values[i].callouts[j] != 0; j++)
613 switch (values[i].callouts[j])
616 mk->mode6_handler.conf.minimums_enabled = true;
620 mk->mode6_handler.conf.smart_500_enabled = true;
624 mk->mode6_handler.conf.above_field_voice = mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500);
627 case FIELD_500_ABOVE:
628 mk->mode6_handler.conf.above_field_voice = mk_voice(five_hundred_above);
632 for (unsigned k = 0; k < n_altitude_callouts; k++)
633 if (mk->mode6_handler.altitude_callout_definitions[k] == values[i].callouts[j])
634 mk->mode6_handler.conf.altitude_callouts_enabled[k] = true;
645 MK_VIII::ConfigurationModule::read_audio_menu_select (int value)
647 if (value == 0 || value == 1)
648 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_gear);
649 else if (value == 2 || value == 3)
650 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_flaps);
658 MK_VIII::ConfigurationModule::read_terrain_display_select (int value)
661 mk->tcf_handler.conf.enabled = false;
662 else if (value == 0 || value == 1 || (value >= 3 && value <= 15)
663 || (value >= 18 && value <= 20) || (value >= 235 && value <= 255))
664 mk->tcf_handler.conf.enabled = true;
672 MK_VIII::ConfigurationModule::read_options_select_group_1 (int value)
674 if (value >= 0 && value < 128)
676 mk->io_handler.conf.flap_reversal = test_bits(value, 1 << 1);
677 mk->mode6_handler.conf.bank_angle_enabled = test_bits(value, 1 << 2);
678 mk->io_handler.conf.steep_approach_enabled = test_bits(value, 1 << 6);
686 MK_VIII::ConfigurationModule::read_radio_altitude_input_select (int value)
688 mk->io_handler.conf.altitude_source = value;
689 return (value >= 0 && value <= 4) || (value >= 251 && value <= 255);
693 MK_VIII::ConfigurationModule::read_navigation_input_select (int value)
695 if (value >= 0 && value <= 2)
696 mk->io_handler.conf.localizer_enabled = false;
697 else if ((value >= 3 && value <= 5) || (value >= 250 && value <= 255))
698 mk->io_handler.conf.localizer_enabled = true;
706 MK_VIII::ConfigurationModule::read_attitude_input_select (int value)
709 mk->io_handler.conf.use_attitude_indicator=true;
711 mk->io_handler.conf.use_attitude_indicator=false;
712 return (value >= 0 && value <= 6) || value == 253 || value == 255;
716 MK_VIII::ConfigurationModule::read_heading_input_select (int value)
719 return (value >= 0 && value <= 3) || value == 254 || value == 255;
723 MK_VIII::ConfigurationModule::read_windshear_input_select (int value)
726 return value == 0 || (value >= 253 && value <= 255);
730 MK_VIII::ConfigurationModule::read_input_output_discrete_type_select (int value)
735 IOHandler::LampConfiguration lamp_conf;
736 bool gpws_inhibit_enabled;
737 bool momentary_flap_override_enabled;
738 bool alternate_steep_approach;
740 { 0, { false, false }, false, true, true },
741 { 1, { true, false }, false, true, true },
742 { 2, { false, false }, true, true, true },
743 { 3, { true, false }, true, true, true },
744 { 4, { false, true }, true, true, true },
745 { 5, { true, true }, true, true, true },
746 { 6, { false, false }, true, true, false },
747 { 7, { true, false }, true, true, false },
748 { 254, { false, false }, true, false, true },
749 { 255, { false, false }, true, false, true }
752 for (size_t i = 0; i < n_elements(io_types); i++)
753 if (io_types[i].type == value)
755 mk->io_handler.conf.lamp = &io_types[i].lamp_conf;
756 mk->io_handler.conf.gpws_inhibit_enabled = io_types[i].gpws_inhibit_enabled;
757 mk->io_handler.conf.momentary_flap_override_enabled = io_types[i].momentary_flap_override_enabled;
758 mk->io_handler.conf.alternate_steep_approach = io_types[i].alternate_steep_approach;
766 MK_VIII::ConfigurationModule::read_audio_output_level (int value)
780 for (size_t i = 0; i < n_elements(values); i++)
781 if (values[i].id == value)
783 mk->voice_player.set_volume(mk->voice_player.conf.volume = modify_amplitude(1.0, values[i].relative_dB));
787 // The self test needs the voice player even when the configuration
788 // is invalid, so set a default value.
789 mk->voice_player.set_volume(mk->voice_player.conf.volume = 1.0);
794 MK_VIII::ConfigurationModule::read_undefined_input_select (int value)
801 MK_VIII::ConfigurationModule::boot ()
803 bool (MK_VIII::ConfigurationModule::*readers[N_CATEGORIES]) (int) = {
804 &MK_VIII::ConfigurationModule::read_aircraft_mode_type_select,
805 &MK_VIII::ConfigurationModule::read_air_data_input_select,
806 &MK_VIII::ConfigurationModule::read_position_input_select,
807 &MK_VIII::ConfigurationModule::read_altitude_callouts,
808 &MK_VIII::ConfigurationModule::read_audio_menu_select,
809 &MK_VIII::ConfigurationModule::read_terrain_display_select,
810 &MK_VIII::ConfigurationModule::read_options_select_group_1,
811 &MK_VIII::ConfigurationModule::read_radio_altitude_input_select,
812 &MK_VIII::ConfigurationModule::read_navigation_input_select,
813 &MK_VIII::ConfigurationModule::read_attitude_input_select,
814 &MK_VIII::ConfigurationModule::read_heading_input_select,
815 &MK_VIII::ConfigurationModule::read_windshear_input_select,
816 &MK_VIII::ConfigurationModule::read_input_output_discrete_type_select,
817 &MK_VIII::ConfigurationModule::read_audio_output_level,
818 &MK_VIII::ConfigurationModule::read_undefined_input_select,
819 &MK_VIII::ConfigurationModule::read_undefined_input_select,
820 &MK_VIII::ConfigurationModule::read_undefined_input_select
823 memcpy(effective_categories, categories, sizeof(categories));
826 for (int i = 0; i < N_CATEGORIES; i++)
827 if (! (this->*readers[i])(effective_categories[i]))
829 SG_LOG(SG_INSTR, SG_ALERT, "MK VIII EGPWS configuration category " << i + 1 << ": invalid value " << effective_categories[i]);
831 if (state == STATE_OK)
832 state = STATE_INVALID_DATABASE;
834 mk_doutput(gpws_inop) = true;
835 mk_doutput(tad_inop) = true;
842 if (mk->mode6_handler.conf.above_field_voice && ! mk->tcf_handler.conf.enabled)
845 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");
846 state = STATE_INVALID_DATABASE;
851 MK_VIII::ConfigurationModule::bind (SGPropertyNode *node)
853 for (int i = 0; i < N_CATEGORIES; i++)
855 std::ostringstream name;
856 name << "configuration-module/category-" << i + 1;
857 mk->properties_handler.tie(node, name.str().c_str(), SGRawValuePointer<int>(&categories[i]));
861 ///////////////////////////////////////////////////////////////////////////////
862 // FaultHandler ///////////////////////////////////////////////////////////////
863 ///////////////////////////////////////////////////////////////////////////////
865 // [INSTALL] only specifies that the faults cause a GPWS INOP
866 // indication. We arbitrarily set a TAD INOP when it makes sense.
867 const unsigned int MK_VIII::FaultHandler::fault_inops[] = {
868 INOP_GPWS | INOP_TAD, // [INSTALL] 3.15.1.3
869 INOP_GPWS, // [INSTALL] appendix E 6.6.2
870 INOP_GPWS, // [INSTALL] appendix E 6.6.4
871 INOP_GPWS, // [INSTALL] appendix E 6.6.6
872 INOP_GPWS | INOP_TAD, // [INSTALL] appendix E 6.6.7
873 INOP_GPWS, // [INSTALL] appendix E 6.6.13
874 INOP_GPWS, // [INSTALL] appendix E 6.6.25
875 INOP_GPWS, // [INSTALL] appendix E 6.6.27
876 INOP_TAD, // unspecified
877 INOP_GPWS, // unspecified
878 INOP_GPWS, // unspecified
879 // [INSTALL] 2.3.10.1 specifies that GPWS INOP is "activated during
880 // any detected partial or total failure of the GPWS modes 1-5", so
881 // do not set any INOP for mode 6 and bank angle.
884 INOP_TAD // unspecified
888 MK_VIII::FaultHandler::has_faults () const
890 for (int i = 0; i < N_FAULTS; i++)
898 MK_VIII::FaultHandler::has_faults (unsigned int inop)
900 for (int i = 0; i < N_FAULTS; i++)
901 if (faults[i] && test_bits(fault_inops[i], inop))
908 MK_VIII::FaultHandler::boot ()
910 memset(faults, 0, sizeof(faults));
914 MK_VIII::FaultHandler::set_fault (Fault fault)
918 faults[fault] = true;
920 mk->self_test_handler.set_inop();
922 if (test_bits(fault_inops[fault], INOP_GPWS))
924 mk_unset_alerts(~mk_alert(TCF_TOO_LOW_TERRAIN));
925 mk_doutput(gpws_inop) = true;
927 if (test_bits(fault_inops[fault], INOP_TAD))
929 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
930 mk_doutput(tad_inop) = true;
936 MK_VIII::FaultHandler::unset_fault (Fault fault)
940 faults[fault] = false;
941 if (! has_faults(INOP_GPWS))
942 mk_doutput(gpws_inop) = false;
943 if (! has_faults(INOP_TAD))
944 mk_doutput(tad_inop) = false;
948 ///////////////////////////////////////////////////////////////////////////////
949 // IOHandler //////////////////////////////////////////////////////////////////
950 ///////////////////////////////////////////////////////////////////////////////
953 MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
955 // [PILOT] page 20 specifies that the terrain clearance is equal to
956 // 75% of the radio altitude, averaged over the previous 15 seconds.
958 // no updates when simulation is paused (dt=0.0), and add 5 samples/second only
959 if (globals->get_sim_time_sec() - last_update < 0.2)
961 last_update = globals->get_sim_time_sec();
963 samples_type::iterator iter;
965 // remove samples older than 15 seconds
966 for (iter = samples.begin(); iter != samples.end() && globals->get_sim_time_sec() - (*iter).timestamp >= 15; iter = samples.begin())
970 samples.push_back(Sample<double>(agl));
973 double new_value = 0;
974 if (! samples.empty())
976 // time consuming loop => queue limited to 75 samples
977 // (= 15seconds * 5samples/second)
978 for (iter = samples.begin(); iter != samples.end(); iter++)
979 new_value += (*iter).value;
980 new_value /= samples.size();
984 if (new_value > value)
991 MK_VIII::IOHandler::TerrainClearanceFilter::reset ()
998 MK_VIII::IOHandler::IOHandler (MK_VIII *device)
999 : mk(device), _lamp(LAMP_NONE)
1001 memset(&input_feeders, 0, sizeof(input_feeders));
1002 memset(&inputs.discretes, 0, sizeof(inputs.discretes));
1003 memset(&outputs, 0, sizeof(outputs));
1004 memset(&power_saved, 0, sizeof(power_saved));
1006 mk_dinput_feed(landing_gear) = true;
1007 mk_dinput_feed(landing_flaps) = true;
1008 mk_dinput_feed(glideslope_inhibit) = true;
1009 mk_dinput_feed(decision_height) = true;
1010 mk_dinput_feed(autopilot_engaged) = true;
1011 mk_ainput_feed(uncorrected_barometric_altitude) = true;
1012 mk_ainput_feed(barometric_altitude_rate) = true;
1013 mk_ainput_feed(radio_altitude) = true;
1014 mk_ainput_feed(glideslope_deviation) = true;
1015 mk_ainput_feed(roll_angle) = true;
1016 mk_ainput_feed(localizer_deviation) = true;
1017 mk_ainput_feed(computed_airspeed) = true;
1019 // will be unset on power on
1020 mk_doutput(gpws_inop) = true;
1021 mk_doutput(tad_inop) = true;
1025 MK_VIII::IOHandler::boot ()
1027 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1030 mk_doutput(gpws_inop) = false;
1031 mk_doutput(tad_inop) = false;
1033 mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
1035 altitude_samples.clear();
1036 reset_terrain_clearance();
1040 MK_VIII::IOHandler::post_boot ()
1042 if (momentary_steep_approach_enabled())
1044 last_landing_gear = mk_dinput(landing_gear);
1045 last_real_flaps_down = real_flaps_down();
1048 // read externally-latching input discretes
1049 update_alternate_discrete_input(&mk_dinput(mode6_low_volume));
1050 update_alternate_discrete_input(&mk_dinput(audio_inhibit));
1054 MK_VIII::IOHandler::power_off ()
1056 power_saved.glideslope_cancel = mk_doutput(glideslope_cancel); // [SPEC] 6.2.5
1058 memset(&outputs, 0, sizeof(outputs));
1060 audio_inhibit_fault_timer.stop();
1061 landing_gear_fault_timer.stop();
1062 flaps_down_fault_timer.stop();
1063 momentary_flap_override_fault_timer.stop();
1064 self_test_fault_timer.stop();
1065 glideslope_cancel_fault_timer.stop();
1066 steep_approach_fault_timer.stop();
1067 gpws_inhibit_fault_timer.stop();
1068 ta_tcf_inhibit_fault_timer.stop();
1071 mk_doutput(gpws_inop) = true;
1072 mk_doutput(tad_inop) = true;
1076 MK_VIII::IOHandler::enter_ground ()
1078 reset_terrain_clearance();
1080 if (conf.momentary_flap_override_enabled)
1081 mk_doutput(flap_override) = false; // [INSTALL] 3.15.1.6
1085 MK_VIII::IOHandler::enter_takeoff ()
1087 reset_terrain_clearance();
1089 if (momentary_steep_approach_enabled())
1090 // landing or go-around, disable steep approach as per [SPEC] 6.2.1
1091 mk_doutput(steep_approach) = false;
1095 MK_VIII::IOHandler::update_inputs ()
1097 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1100 // 1. process input feeders
1102 if (mk_dinput_feed(landing_gear))
1103 mk_dinput(landing_gear) = mk_node(gear_down)->getBoolValue();
1104 if (mk_dinput_feed(landing_flaps))
1105 mk_dinput(landing_flaps) = mk_node(flaps)->getDoubleValue() < 1;
1106 if (mk_dinput_feed(glideslope_inhibit))
1107 mk_dinput(glideslope_inhibit) = mk_node(nav0_gs_distance)->getDoubleValue() < 0;
1108 if (mk_dinput_feed(autopilot_engaged))
1112 mode = mk_node(autopilot_heading_lock)->getStringValue();
1113 mk_dinput(autopilot_engaged) = mode && *mode;
1116 if (mk_ainput_feed(uncorrected_barometric_altitude))
1118 if (mk_node(altimeter_serviceable)->getBoolValue())
1119 mk_ainput(uncorrected_barometric_altitude).set(mk_node(altimeter_altitude)->getDoubleValue());
1121 mk_ainput(uncorrected_barometric_altitude).unset();
1123 if (mk_ainput_feed(barometric_altitude_rate))
1124 // Do not use the vsi, because it is pressure-based only, and
1125 // therefore too laggy for GPWS alerting purposes. I guess that
1126 // a real ADC combines pressure-based and inerta-based altitude
1127 // rates to provide a non-laggy rate. That non-laggy rate is
1128 // best emulated by /velocities/vertical-speed-fps * 60.
1129 mk_ainput(barometric_altitude_rate).set(mk_node(vs)->getDoubleValue() * 60);
1130 if (mk_ainput_feed(radio_altitude))
1133 switch (conf.altitude_source)
1136 agl = mk_node(altitude_gear_agl)->getDoubleValue();
1139 agl = mk_node(altitude_radar_agl)->getDoubleValue();
1141 default: // 0,1,2 (and any currently unsupported values)
1142 agl = mk_node(altitude_agl)->getDoubleValue();
1145 // Some flight models may return negative values when on the
1146 // ground or after a crash; do not allow them.
1147 mk_ainput(radio_altitude).set(SG_MAX2(0.0, agl));
1149 if (mk_ainput_feed(glideslope_deviation))
1151 if (mk_node(nav0_serviceable)->getBoolValue()
1152 && mk_node(nav0_gs_serviceable)->getBoolValue()
1153 && mk_node(nav0_in_range)->getBoolValue()
1154 && mk_node(nav0_has_gs)->getBoolValue())
1155 // gs-needle-deflection is expressed in degrees, and 1 dot =
1156 // 0.32 degrees (according to
1157 // http://www.ntsb.gov/Recs/letters/2000/A00_41_45.pdf)
1158 mk_ainput(glideslope_deviation).set(mk_node(nav0_gs_needle_deflection)->getDoubleValue() / 0.32 * GLIDESLOPE_DOTS_TO_DDM);
1160 mk_ainput(glideslope_deviation).unset();
1162 if (mk_ainput_feed(roll_angle))
1164 if (conf.use_attitude_indicator)
1166 // read data from attitude indicator instrument (requires vacuum system to work)
1167 if (mk_node(ai_serviceable)->getBoolValue() && ! mk_node(ai_caged)->getBoolValue())
1168 mk_ainput(roll_angle).set(mk_node(ai_roll)->getDoubleValue());
1170 mk_ainput(roll_angle).unset();
1174 // use simulator source
1175 mk_ainput(roll_angle).set(mk_node(orientation_roll)->getDoubleValue());
1178 if (mk_ainput_feed(localizer_deviation))
1180 if (mk_node(nav0_serviceable)->getBoolValue()
1181 && mk_node(nav0_cdi_serviceable)->getBoolValue()
1182 && mk_node(nav0_in_range)->getBoolValue()
1183 && mk_node(nav0_nav_loc)->getBoolValue())
1184 // heading-needle-deflection is expressed in degrees, and 1
1185 // dot = 2 degrees (0.5 degrees for ILS, but navradio.cxx
1186 // performs the conversion)
1187 mk_ainput(localizer_deviation).set(mk_node(nav0_heading_needle_deflection)->getDoubleValue() / 2 * LOCALIZER_DOTS_TO_DDM);
1189 mk_ainput(localizer_deviation).unset();
1191 if (mk_ainput_feed(computed_airspeed))
1193 if (mk_node(asi_serviceable)->getBoolValue())
1194 mk_ainput(computed_airspeed).set(mk_node(asi_speed)->getDoubleValue());
1196 mk_ainput(computed_airspeed).unset();
1201 mk_data(decision_height).set(&mk_ainput(decision_height));
1202 mk_data(radio_altitude).set(&mk_ainput(radio_altitude));
1203 mk_data(roll_angle).set(&mk_ainput(roll_angle));
1205 // update barometric_altitude_rate as per [SPEC] 6.2.1: "During
1206 // normal conditions, the system will base Mode 1 computations upon
1207 // barometric rate from the ADC. If this computed data is not valid
1208 // or available then the system will use internally computed
1209 // barometric altitude rate."
1211 if (! mk_ainput(barometric_altitude_rate).ncd)
1212 // the altitude rate input is valid, use it
1213 mk_data(barometric_altitude_rate).set(mk_ainput(barometric_altitude_rate).get());
1218 // The altitude rate input is invalid. We can compute an
1219 // altitude rate if all the following conditions are true:
1221 // - the altitude input is valid
1222 // - there is an altitude sample with age >= 1 second
1223 // - that altitude sample is valid
1225 if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1227 if (! altitude_samples.empty() && ! altitude_samples.front().value.ncd)
1229 double elapsed = globals->get_sim_time_sec() - altitude_samples.front().timestamp;
1233 mk_data(barometric_altitude_rate).set((mk_ainput(uncorrected_barometric_altitude).get() - altitude_samples.front().value.get()) / elapsed * 60);
1239 mk_data(barometric_altitude_rate).unset();
1242 altitude_samples.push_back(Sample< Parameter<double> >(mk_ainput(uncorrected_barometric_altitude)));
1244 // Erase everything from the beginning of the list up to the sample
1245 // preceding the most recent sample whose age is >= 1 second.
1247 altitude_samples_type::iterator erase_last = altitude_samples.begin();
1248 altitude_samples_type::iterator iter;
1250 for (iter = altitude_samples.begin(); iter != altitude_samples.end(); iter++)
1251 if (globals->get_sim_time_sec() - (*iter).timestamp >= 1)
1256 if (erase_last != altitude_samples.begin())
1257 altitude_samples.erase(altitude_samples.begin(), erase_last);
1261 if (conf.use_internal_gps)
1263 mk_data(gps_altitude).set(mk_node(altitude)->getDoubleValue());
1264 mk_data(gps_latitude).set(mk_node(latitude)->getDoubleValue());
1265 mk_data(gps_longitude).set(mk_node(longitude)->getDoubleValue());
1266 mk_data(gps_vertical_figure_of_merit).set(0.0);
1270 mk_data(gps_altitude).set(&mk_ainput(gps_altitude));
1271 mk_data(gps_latitude).set(&mk_ainput(gps_latitude));
1272 mk_data(gps_longitude).set(&mk_ainput(gps_longitude));
1273 mk_data(gps_vertical_figure_of_merit).set(&mk_ainput(gps_vertical_figure_of_merit));
1276 // update glideslope and localizer
1278 mk_data(glideslope_deviation_dots).set(&mk_ainput(glideslope_deviation), GLIDESLOPE_DDM_TO_DOTS);
1279 mk_data(localizer_deviation_dots).set(&mk_ainput(localizer_deviation), LOCALIZER_DDM_TO_DOTS);
1281 // Update geometric altitude; [SPEC] 6.7.8 provides an overview of a
1282 // complex algorithm which combines several input sources to
1283 // calculate the geometric altitude. Since the exact algorithm is
1284 // not given, we fallback to a much simpler method.
1286 if (! mk_data(gps_altitude).ncd)
1287 mk_data(geometric_altitude).set(mk_data(gps_altitude).get());
1288 else if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1289 mk_data(geometric_altitude).set(mk_ainput(uncorrected_barometric_altitude).get());
1291 mk_data(geometric_altitude).unset();
1293 // update terrain clearance
1295 update_terrain_clearance();
1297 // 3. perform sanity checks
1299 if (! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 0)
1300 mk_data(radio_altitude).unset();
1302 if (! mk_data(decision_height).ncd && mk_data(decision_height).get() < 0)
1303 mk_data(decision_height).unset();
1305 if (! mk_data(gps_latitude).ncd
1306 && (mk_data(gps_latitude).get() < -90
1307 || mk_data(gps_latitude).get() > 90))
1308 mk_data(gps_latitude).unset();
1310 if (! mk_data(gps_longitude).ncd
1311 && (mk_data(gps_longitude).get() < -180
1312 || mk_data(gps_longitude).get() > 180))
1313 mk_data(gps_longitude).unset();
1315 if (! mk_data(roll_angle).ncd
1316 && ((mk_data(roll_angle).get() < -180)
1317 || (mk_data(roll_angle).get() > 180)))
1318 mk_data(roll_angle).unset();
1320 // 4. process input feeders requiring data computed above
1322 if (mk_dinput_feed(decision_height))
1323 mk_dinput(decision_height) = ! mk_data(radio_altitude).ncd
1324 && ! mk_data(decision_height).ncd
1325 && mk_data(radio_altitude).get() <= mk_data(decision_height).get();
1329 MK_VIII::IOHandler::update_terrain_clearance ()
1331 if (! mk_data(radio_altitude).ncd)
1332 mk_data(terrain_clearance).set(terrain_clearance_filter.update(mk_data(radio_altitude).get()));
1334 mk_data(terrain_clearance).unset();
1338 MK_VIII::IOHandler::reset_terrain_clearance ()
1340 terrain_clearance_filter.reset();
1341 update_terrain_clearance();
1345 MK_VIII::IOHandler::reposition ()
1347 reset_terrain_clearance();
1351 MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
1355 if (! mk->fault_handler.faults[fault])
1356 mk->fault_handler.set_fault(fault);
1359 mk->fault_handler.unset_fault(fault);
1363 MK_VIII::IOHandler::handle_input_fault (bool test,
1365 double max_duration,
1366 FaultHandler::Fault fault)
1370 if (! mk->fault_handler.faults[fault])
1372 if (timer->start_or_elapsed() >= max_duration)
1373 mk->fault_handler.set_fault(fault);
1378 mk->fault_handler.unset_fault(fault);
1384 MK_VIII::IOHandler::update_input_faults ()
1386 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1389 // [INSTALL] 3.15.1.3
1390 handle_input_fault(mk_dinput(audio_inhibit),
1391 &audio_inhibit_fault_timer,
1393 FaultHandler::FAULT_ALL_MODES_INHIBIT);
1395 // [INSTALL] appendix E 6.6.2
1396 handle_input_fault(mk_dinput(landing_gear)
1397 && ! mk_ainput(computed_airspeed).ncd
1398 && mk_ainput(computed_airspeed).get() > conf.faults->max_gear_down_airspeed,
1399 &landing_gear_fault_timer,
1401 FaultHandler::FAULT_GEAR_SWITCH);
1403 // [INSTALL] appendix E 6.6.4
1404 handle_input_fault(flaps_down()
1405 && ! mk_ainput(computed_airspeed).ncd
1406 && mk_ainput(computed_airspeed).get() > conf.faults->max_flaps_down_airspeed,
1407 &flaps_down_fault_timer,
1409 FaultHandler::FAULT_FLAPS_SWITCH);
1411 // [INSTALL] appendix E 6.6.6
1412 if (conf.momentary_flap_override_enabled)
1413 handle_input_fault(mk_dinput(momentary_flap_override),
1414 &momentary_flap_override_fault_timer,
1416 FaultHandler::FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID);
1418 // [INSTALL] appendix E 6.6.7
1419 handle_input_fault(mk_dinput(self_test),
1420 &self_test_fault_timer,
1422 FaultHandler::FAULT_SELF_TEST_INVALID);
1424 // [INSTALL] appendix E 6.6.13
1425 handle_input_fault(mk_dinput(glideslope_cancel),
1426 &glideslope_cancel_fault_timer,
1428 FaultHandler::FAULT_GLIDESLOPE_CANCEL_INVALID);
1430 // [INSTALL] appendix E 6.6.25
1431 if (momentary_steep_approach_enabled())
1432 handle_input_fault(mk_dinput(steep_approach),
1433 &steep_approach_fault_timer,
1435 FaultHandler::FAULT_STEEP_APPROACH_INVALID);
1437 // [INSTALL] appendix E 6.6.27
1438 if (conf.gpws_inhibit_enabled)
1439 handle_input_fault(mk_dinput(gpws_inhibit),
1440 &gpws_inhibit_fault_timer,
1442 FaultHandler::FAULT_GPWS_INHIBIT);
1444 // [INSTALL] does not specify a fault for this one, but it makes
1445 // sense to have it behave like GPWS inhibit
1446 handle_input_fault(mk_dinput(ta_tcf_inhibit),
1447 &ta_tcf_inhibit_fault_timer,
1449 FaultHandler::FAULT_TA_TCF_INHIBIT);
1451 // [PILOT] page 48: "In the event that required data for a
1452 // particular function is not available, then that function is
1453 // automatically inhibited and annunciated"
1455 handle_input_fault(mk_data(radio_altitude).ncd
1456 || mk_ainput(uncorrected_barometric_altitude).ncd
1457 || mk_data(barometric_altitude_rate).ncd
1458 || mk_ainput(computed_airspeed).ncd
1459 || mk_data(terrain_clearance).ncd,
1460 FaultHandler::FAULT_MODES14_INPUTS_INVALID);
1462 if (! mk_dinput(glideslope_inhibit))
1463 handle_input_fault(mk_data(radio_altitude).ncd,
1464 FaultHandler::FAULT_MODE5_INPUTS_INVALID);
1466 if (mk->mode6_handler.altitude_callouts_enabled())
1467 handle_input_fault(mk->mode6_handler.conf.above_field_voice
1468 ? (mk_data(gps_latitude).ncd
1469 || mk_data(gps_longitude).ncd
1470 || mk_data(geometric_altitude).ncd)
1471 : mk_data(radio_altitude).ncd,
1472 FaultHandler::FAULT_MODE6_INPUTS_INVALID);
1474 if (mk->mode6_handler.conf.bank_angle_enabled)
1475 handle_input_fault(mk_data(roll_angle).ncd,
1476 FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID);
1478 if (mk->tcf_handler.conf.enabled)
1479 handle_input_fault(mk_data(radio_altitude).ncd
1480 || mk_data(geometric_altitude).ncd
1481 || mk_data(gps_latitude).ncd
1482 || mk_data(gps_longitude).ncd
1483 || mk_data(gps_vertical_figure_of_merit).ncd,
1484 FaultHandler::FAULT_TCF_INPUTS_INVALID);
1488 MK_VIII::IOHandler::update_alternate_discrete_input (bool *ptr)
1490 assert(mk->system_handler.state == SystemHandler::STATE_ON);
1492 if (ptr == &mk_dinput(mode6_low_volume))
1494 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1495 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1496 mk->mode6_handler.set_volume(*ptr ? modify_amplitude(1.0, -6) : 1.0);
1498 else if (ptr == &mk_dinput(audio_inhibit))
1500 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1501 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1502 mk->voice_player.set_volume(*ptr ? 0.0 : mk->voice_player.conf.volume);
1507 MK_VIII::IOHandler::update_internal_latches ()
1509 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1513 if (conf.momentary_flap_override_enabled
1514 && mk_doutput(flap_override)
1515 && ! mk->state_handler.takeoff
1516 && (mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() <= 50))
1517 mk_doutput(flap_override) = false;
1520 if (momentary_steep_approach_enabled())
1522 if (mk_doutput(steep_approach)
1523 && ! mk->state_handler.takeoff
1524 && ((last_landing_gear && ! mk_dinput(landing_gear))
1525 || (last_real_flaps_down && ! real_flaps_down())))
1526 // gear or flaps raised during approach: it's a go-around
1527 mk_doutput(steep_approach) = false;
1529 last_landing_gear = mk_dinput(landing_gear);
1530 last_real_flaps_down = real_flaps_down();
1534 if (mk_doutput(glideslope_cancel)
1535 && (mk_data(glideslope_deviation_dots).ncd
1536 || mk_data(radio_altitude).ncd
1537 || mk_data(radio_altitude).get() > 2000
1538 || mk_data(radio_altitude).get() < 30))
1539 mk_doutput(glideslope_cancel) = false;
1543 MK_VIII::IOHandler::update_egpws_alert_discrete_1 ()
1545 if (mk->voice_player.voice)
1550 VoicePlayer::Voice *voice;
1552 { 11, mk_voice(sink_rate_pause_sink_rate) },
1553 { 12, mk_voice(pull_up) },
1554 { 13, mk_voice(terrain) },
1555 { 13, mk_voice(terrain_pause_terrain) },
1556 { 14, mk_voice(dont_sink_pause_dont_sink) },
1557 { 15, mk_voice(too_low_gear) },
1558 { 16, mk_voice(too_low_flaps) },
1559 { 17, mk_voice(too_low_terrain) },
1560 { 18, mk_voice(soft_glideslope) },
1561 { 18, mk_voice(hard_glideslope) },
1562 { 19, mk_voice(minimums_minimums) }
1565 for (size_t i = 0; i < n_elements(voices); i++)
1566 if (voices[i].voice == mk->voice_player.voice)
1568 mk_aoutput(egpws_alert_discrete_1) = 1 << voices[i].bit;
1573 mk_aoutput(egpws_alert_discrete_1) = 0;
1577 MK_VIII::IOHandler::update_egpwc_logic_discretes ()
1579 mk_aoutput(egpwc_logic_discretes) = 0;
1581 if (mk->state_handler.takeoff)
1582 mk_aoutput(egpwc_logic_discretes) |= 1 << 18;
1587 unsigned int alerts;
1589 { 13, mk_alert(TCF_TOO_LOW_TERRAIN) },
1590 { 19, mk_alert(MODE1_SINK_RATE) },
1591 { 20, mk_alert(MODE1_PULL_UP) },
1592 { 21, mk_alert(MODE2A_PREFACE) | mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B_LANDING_MODE) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING) },
1593 { 22, mk_alert(MODE2A) | mk_alert(MODE2B) },
1594 { 23, mk_alert(MODE3) },
1595 { 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) },
1596 { 25, mk_alert(MODE5_SOFT) | mk_alert(MODE5_HARD) }
1599 for (size_t i = 0; i < n_elements(logic); i++)
1600 if (mk_test_alerts(logic[i].alerts))
1601 mk_aoutput(egpwc_logic_discretes) |= 1 << logic[i].bit;
1605 MK_VIII::IOHandler::update_mode6_callouts_discrete_1 ()
1607 if (mk->voice_player.voice)
1612 VoicePlayer::Voice *voice;
1614 { 11, mk_voice(minimums_minimums) },
1615 { 16, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_10) },
1616 { 17, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_20) },
1617 { 18, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_30) },
1618 { 19, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_40) },
1619 { 20, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_50) },
1620 { 23, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_100) },
1621 { 24, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_200) },
1622 { 25, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_300) }
1625 for (size_t i = 0; i < n_elements(voices); i++)
1626 if (voices[i].voice == mk->voice_player.voice)
1628 mk_aoutput(mode6_callouts_discrete_1) = 1 << voices[i].bit;
1633 mk_aoutput(mode6_callouts_discrete_1) = 0;
1637 MK_VIII::IOHandler::update_mode6_callouts_discrete_2 ()
1639 if (mk->voice_player.voice)
1644 VoicePlayer::Voice *voice;
1646 { 11, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_400) },
1647 { 12, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500) },
1648 { 13, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_1000) },
1649 { 18, mk_voice(bank_angle_pause_bank_angle) },
1650 { 18, mk_voice(bank_angle_pause_bank_angle_3) },
1651 { 23, mk_voice(five_hundred_above) }
1654 for (size_t i = 0; i < n_elements(voices); i++)
1655 if (voices[i].voice == mk->voice_player.voice)
1657 mk_aoutput(mode6_callouts_discrete_2) = 1 << voices[i].bit;
1662 mk_aoutput(mode6_callouts_discrete_2) = 0;
1666 MK_VIII::IOHandler::update_egpws_alert_discrete_2 ()
1668 mk_aoutput(egpws_alert_discrete_2) = 1 << 27; // Terrain NA
1670 if (mk_doutput(glideslope_cancel))
1671 mk_aoutput(egpws_alert_discrete_2) |= 1 << 11;
1672 if (mk_doutput(gpws_alert))
1673 mk_aoutput(egpws_alert_discrete_2) |= 1 << 12;
1674 if (mk_doutput(gpws_warning))
1675 mk_aoutput(egpws_alert_discrete_2) |= 1 << 13;
1676 if (mk_doutput(gpws_inop))
1677 mk_aoutput(egpws_alert_discrete_2) |= 1 << 14;
1678 if (mk_doutput(audio_on))
1679 mk_aoutput(egpws_alert_discrete_2) |= 1 << 16;
1680 if (mk_doutput(tad_inop))
1681 mk_aoutput(egpws_alert_discrete_2) |= 1 << 24;
1682 if (mk->fault_handler.has_faults())
1683 mk_aoutput(egpws_alert_discrete_2) |= 1 << 25;
1687 MK_VIII::IOHandler::update_egpwc_alert_discrete_3 ()
1689 mk_aoutput(egpwc_alert_discrete_3) = 1 << 17 | 1 << 18;
1691 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
1692 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 11;
1693 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
1694 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 12;
1695 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
1696 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 13;
1697 if (mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
1698 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 14;
1699 if (mk_doutput(tad_inop))
1700 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 16;
1704 MK_VIII::IOHandler::update_outputs ()
1706 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1709 mk_doutput(audio_on) = ! mk_dinput(audio_inhibit)
1710 && mk->voice_player.voice
1711 && ! mk->voice_player.voice->element->silence;
1713 update_egpws_alert_discrete_1();
1714 update_egpwc_logic_discretes();
1715 update_mode6_callouts_discrete_1();
1716 update_mode6_callouts_discrete_2();
1717 update_egpws_alert_discrete_2();
1718 update_egpwc_alert_discrete_3();
1722 MK_VIII::IOHandler::get_lamp_output (Lamp lamp)
1726 case LAMP_GLIDESLOPE:
1727 return &mk_doutput(gpws_alert);
1730 return conf.lamp->format2 ? &mk_doutput(gpws_alert) : &mk_doutput(gpws_warning);
1733 return &mk_doutput(gpws_warning);
1736 assert_not_reached();
1742 MK_VIII::IOHandler::update_lamps ()
1744 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1747 if (_lamp != LAMP_NONE && conf.lamp->flashing)
1749 // [SPEC] 6.9.3: 70 cycles per minute
1750 if (lamp_timer.elapsed() >= 60.0 / 70.0 / 2.0)
1752 bool *output = get_lamp_output(_lamp);
1753 *output = ! *output;
1760 MK_VIII::IOHandler::set_lamp (Lamp lamp)
1767 mk_doutput(gpws_warning) = false;
1768 mk_doutput(gpws_alert) = false;
1770 if (lamp != LAMP_NONE)
1772 *get_lamp_output(lamp) = true;
1778 MK_VIII::IOHandler::gpws_inhibit () const
1780 return conf.gpws_inhibit_enabled && mk_dinput(gpws_inhibit);
1784 MK_VIII::IOHandler::real_flaps_down () const
1786 return conf.flap_reversal ? mk_dinput(landing_flaps) : ! mk_dinput(landing_flaps);
1790 MK_VIII::IOHandler::flaps_down () const
1792 return flap_override() ? true : real_flaps_down();
1796 MK_VIII::IOHandler::flap_override () const
1798 return conf.momentary_flap_override_enabled ? mk_doutput(flap_override) : false;
1802 MK_VIII::IOHandler::steep_approach () const
1804 if (conf.steep_approach_enabled)
1805 // If alternate action was configured in category 13, we return
1806 // the value of the input discrete (which should be connected to a
1807 // latching steep approach cockpit switch). Otherwise, we return
1808 // the value of the output discrete.
1809 return conf.alternate_steep_approach ? mk_dinput(steep_approach) : mk_doutput(steep_approach);
1815 MK_VIII::IOHandler::momentary_steep_approach_enabled () const
1817 return conf.steep_approach_enabled && ! conf.alternate_steep_approach;
1821 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1826 mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(),
1827 FGVoicePlayer::RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
1829 mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
1833 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1835 Parameter<double> *input,
1838 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name).c_str(), SGRawValuePointer<double>(input->get_pointer()));
1839 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name + "-ncd").c_str(), SGRawValuePointer<bool>(&input->ncd));
1841 mk->properties_handler.tie(node, (string("input-feeders/arinc429/") + name).c_str(), SGRawValuePointer<bool>(feed));
1845 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1849 SGPropertyNode *child = node->getNode((string("outputs/discretes/") + name).c_str(), true);
1851 mk->properties_handler.tie(child, SGRawValuePointer<bool>(output));
1852 child->setAttribute(SGPropertyNode::WRITE, false);
1856 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1860 SGPropertyNode *child = node->getNode((string("outputs/arinc429/") + name).c_str(), true);
1862 mk->properties_handler.tie(child, SGRawValuePointer<int>(output));
1863 child->setAttribute(SGPropertyNode::WRITE, false);
1867 MK_VIII::IOHandler::bind (SGPropertyNode *node)
1869 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));
1871 tie_input(node, "landing-gear", &mk_dinput(landing_gear), &mk_dinput_feed(landing_gear));
1872 tie_input(node, "landing-flaps", &mk_dinput(landing_flaps), &mk_dinput_feed(landing_flaps));
1873 tie_input(node, "momentary-flap-override", &mk_dinput(momentary_flap_override));
1874 tie_input(node, "self-test", &mk_dinput(self_test));
1875 tie_input(node, "glideslope-inhibit", &mk_dinput(glideslope_inhibit), &mk_dinput_feed(glideslope_inhibit));
1876 tie_input(node, "glideslope-cancel", &mk_dinput(glideslope_cancel));
1877 tie_input(node, "decision-height", &mk_dinput(decision_height), &mk_dinput_feed(decision_height));
1878 tie_input(node, "mode6-low-volume", &mk_dinput(mode6_low_volume));
1879 tie_input(node, "audio-inhibit", &mk_dinput(audio_inhibit));
1880 tie_input(node, "ta-tcf-inhibit", &mk_dinput(ta_tcf_inhibit));
1881 tie_input(node, "autopilot-engaged", &mk_dinput(autopilot_engaged), &mk_dinput_feed(autopilot_engaged));
1882 tie_input(node, "steep-approach", &mk_dinput(steep_approach));
1883 tie_input(node, "gpws-inhibit", &mk_dinput(gpws_inhibit));
1885 tie_input(node, "uncorrected-barometric-altitude", &mk_ainput(uncorrected_barometric_altitude), &mk_ainput_feed(uncorrected_barometric_altitude));
1886 tie_input(node, "barometric-altitude-rate", &mk_ainput(barometric_altitude_rate), &mk_ainput_feed(barometric_altitude_rate));
1887 tie_input(node, "gps-altitude", &mk_ainput(gps_altitude));
1888 tie_input(node, "gps-latitude", &mk_ainput(gps_latitude));
1889 tie_input(node, "gps-longitude", &mk_ainput(gps_longitude));
1890 tie_input(node, "gps-vertical-figure-of-merit", &mk_ainput(gps_vertical_figure_of_merit));
1891 tie_input(node, "radio-altitude", &mk_ainput(radio_altitude), &mk_ainput_feed(radio_altitude));
1892 tie_input(node, "glideslope-deviation", &mk_ainput(glideslope_deviation), &mk_ainput_feed(glideslope_deviation));
1893 tie_input(node, "roll-angle", &mk_ainput(roll_angle), &mk_ainput_feed(roll_angle));
1894 tie_input(node, "localizer-deviation", &mk_ainput(localizer_deviation), &mk_ainput_feed(localizer_deviation));
1895 tie_input(node, "computed-airspeed", &mk_ainput(computed_airspeed), &mk_ainput_feed(computed_airspeed));
1896 tie_input(node, "decision-height", &mk_ainput(decision_height), &mk_ainput_feed(decision_height));
1898 tie_output(node, "gpws-warning", &mk_doutput(gpws_warning));
1899 tie_output(node, "gpws-alert", &mk_doutput(gpws_alert));
1900 tie_output(node, "audio-on", &mk_doutput(audio_on));
1901 tie_output(node, "gpws-inop", &mk_doutput(gpws_inop));
1902 tie_output(node, "tad-inop", &mk_doutput(tad_inop));
1903 tie_output(node, "flap-override", &mk_doutput(flap_override));
1904 tie_output(node, "glideslope-cancel", &mk_doutput(glideslope_cancel));
1905 tie_output(node, "steep-approach", &mk_doutput(steep_approach));
1907 tie_output(node, "egpws-alert-discrete-1", &mk_aoutput(egpws_alert_discrete_1));
1908 tie_output(node, "egpwc-logic-discretes", &mk_aoutput(egpwc_logic_discretes));
1909 tie_output(node, "mode6-callouts-discrete-1", &mk_aoutput(mode6_callouts_discrete_1));
1910 tie_output(node, "mode6-callouts-discrete-2", &mk_aoutput(mode6_callouts_discrete_2));
1911 tie_output(node, "egpws-alert-discrete-2", &mk_aoutput(egpws_alert_discrete_2));
1912 tie_output(node, "egpwc-alert-discrete-3", &mk_aoutput(egpwc_alert_discrete_3));
1916 MK_VIII::IOHandler::get_discrete_input (bool *ptr) const
1922 MK_VIII::IOHandler::set_discrete_input (bool *ptr, bool value)
1927 if (mk->system_handler.state == SystemHandler::STATE_ON)
1929 if (ptr == &mk_dinput(momentary_flap_override))
1931 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1932 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1933 && conf.momentary_flap_override_enabled
1935 mk_doutput(flap_override) = ! mk_doutput(flap_override);
1937 else if (ptr == &mk_dinput(self_test))
1938 mk->self_test_handler.handle_button_event(value);
1939 else if (ptr == &mk_dinput(glideslope_cancel))
1941 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1942 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1945 if (! mk_doutput(glideslope_cancel))
1949 // Although we are not called from update(), we are
1950 // sure that the inputs we use here are defined,
1951 // since state is STATE_ON.
1953 if (! mk_data(glideslope_deviation_dots).ncd
1954 && ! mk_data(radio_altitude).ncd
1955 && mk_data(radio_altitude).get() <= 2000
1956 && mk_data(radio_altitude).get() >= 30)
1957 mk_doutput(glideslope_cancel) = true;
1959 // else do nothing ([PILOT] page 22: "Glideslope Cancel
1960 // can not be deselected (reset) by again pressing the
1961 // Glideslope Cancel switch.")
1964 else if (ptr == &mk_dinput(steep_approach))
1966 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1967 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1968 && momentary_steep_approach_enabled()
1970 mk_doutput(steep_approach) = ! mk_doutput(steep_approach);
1976 if (mk->system_handler.state == SystemHandler::STATE_ON)
1977 update_alternate_discrete_input(ptr);
1981 MK_VIII::IOHandler::present_status_section (const char *name)
1983 printf("%s\n", name);
1987 MK_VIII::IOHandler::present_status_item (const char *name, const char *value)
1990 printf("\t%-32s %s\n", name, value);
1992 printf("\t%s\n", name);
1996 MK_VIII::IOHandler::present_status_subitem (const char *name)
1998 printf("\t\t%s\n", name);
2002 MK_VIII::IOHandler::present_status ()
2006 if (mk->system_handler.state != SystemHandler::STATE_ON)
2009 present_status_section("EGPWC CONFIGURATION");
2010 present_status_item("PART NUMBER:", "965-1220-000"); // [SPEC] 1.1
2011 present_status_item("MOD STATUS:", "N/A");
2012 present_status_item("SERIAL NUMBER:", "N/A");
2014 present_status_item("APPLICATION S/W VERSION:", FLIGHTGEAR_VERSION);
2015 present_status_item("TERRAIN DATABASE VERSION:", FLIGHTGEAR_VERSION);
2016 present_status_item("ENVELOPE MOD DATABASE VERSION:", "N/A");
2017 present_status_item("BOOT CODE VERSION:", FLIGHTGEAR_VERSION);
2020 present_status_section("CURRENT FAULTS");
2021 if (mk->configuration_module.state == ConfigurationModule::STATE_OK && ! mk->fault_handler.has_faults())
2022 present_status_item("NO FAULTS");
2025 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2027 present_status_item("GPWS COMPUTER FAULTS:");
2028 switch (mk->configuration_module.state)
2030 case ConfigurationModule::STATE_INVALID_DATABASE:
2031 present_status_subitem("APPLICATION DATABASE FAILED");
2034 case ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE:
2035 present_status_subitem("CONFIGURATION TYPE INVALID");
2039 assert_not_reached();
2045 present_status_item("GPWS COMPUTER OK");
2046 present_status_item("GPWS EXTERNAL FAULTS:");
2048 static const char *fault_names[] = {
2049 "ALL MODES INHIBIT",
2052 "MOMENTARY FLAP OVERRIDE INVALID",
2053 "SELF TEST INVALID",
2054 "GLIDESLOPE CANCEL INVALID",
2055 "STEEP APPROACH INVALID",
2058 "MODES 1-4 INPUTS INVALID",
2059 "MODE 5 INPUTS INVALID",
2060 "MODE 6 INPUTS INVALID",
2061 "BANK ANGLE INPUTS INVALID",
2062 "TCF INPUTS INVALID"
2065 for (size_t i = 0; i < n_elements(fault_names); i++)
2066 if (mk->fault_handler.faults[i])
2067 present_status_subitem(fault_names[i]);
2072 present_status_section("CONFIGURATION:");
2074 static const char *category_names[] = {
2077 "POSITION INPUT TYPE",
2078 "CALLOUTS OPTION TYPE",
2080 "TERRAIN DISPLAY TYPE",
2082 "RADIO ALTITUDE TYPE",
2083 "NAVIGATION INPUT TYPE",
2085 "MAGNETIC HEADING TYPE",
2086 "WINDSHEAR INPUT TYPE",
2091 for (size_t i = 0; i < n_elements(category_names); i++)
2093 std::ostringstream value;
2094 value << "= " << mk->configuration_module.effective_categories[i];
2095 present_status_item(category_names[i], value.str().c_str());
2100 MK_VIII::IOHandler::get_present_status () const
2106 MK_VIII::IOHandler::set_present_status (bool value)
2108 if (value) present_status();
2112 ///////////////////////////////////////////////////////////////////////////////
2113 // MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
2114 ///////////////////////////////////////////////////////////////////////////////
2117 MK_VIII::VoicePlayer::init ()
2119 FGVoicePlayer::init();
2121 #define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
2122 make_voice(&voices.application_data_base_failed, "application-data-base-failed");
2123 make_voice(&voices.bank_angle, "bank-angle");
2124 make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
2125 make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
2126 make_voice(&voices.bank_angle_inop, "bank-angle-inop");
2127 make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
2128 make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
2129 make_voice(&voices.callouts_inop, "callouts-inop");
2130 make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
2131 make_voice(&voices.dont_sink, "dont-sink");
2132 make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
2133 make_voice(&voices.five_hundred_above, "500-above");
2134 make_voice(&voices.glideslope, "glideslope");
2135 make_voice(&voices.glideslope_inop, "glideslope-inop");
2136 make_voice(&voices.gpws_inop, "gpws-inop");
2137 make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
2138 make_voice(&voices.minimums, "minimums");
2139 make_voice(&voices.minimums_minimums, "minimums", "minimums");
2140 make_voice(&voices.pull_up, "pull-up");
2141 make_voice(&voices.sink_rate, "sink-rate");
2142 make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
2143 make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
2144 make_voice(&voices.terrain, "terrain");
2145 make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
2146 make_voice(&voices.too_low_flaps, "too-low-flaps");
2147 make_voice(&voices.too_low_gear, "too-low-gear");
2148 make_voice(&voices.too_low_terrain, "too-low-terrain");
2150 for (unsigned i = 0; i < n_altitude_callouts; i++)
2152 std::ostringstream name;
2153 name << "altitude-" << MK_VIII::Mode6Handler::altitude_callout_definitions[i];
2154 make_voice(&voices.altitude_callouts[i], name.str().c_str());
2156 speaker.update_configuration();
2159 ///////////////////////////////////////////////////////////////////////////////
2160 // SelfTestHandler ////////////////////////////////////////////////////////////
2161 ///////////////////////////////////////////////////////////////////////////////
2164 MK_VIII::SelfTestHandler::_was_here (int position)
2166 if (position > current)
2175 MK_VIII::SelfTestHandler::Action
2176 MK_VIII::SelfTestHandler::sleep (double duration)
2178 Action _action = { ACTION_SLEEP, duration, NULL };
2182 MK_VIII::SelfTestHandler::Action
2183 MK_VIII::SelfTestHandler::play (VoicePlayer::Voice *voice)
2185 mk->voice_player.play(voice);
2186 Action _action = { ACTION_VOICE, 0, NULL };
2190 MK_VIII::SelfTestHandler::Action
2191 MK_VIII::SelfTestHandler::discrete_on (bool *discrete, double duration)
2194 return sleep(duration);
2197 MK_VIII::SelfTestHandler::Action
2198 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, double duration)
2201 Action _action = { ACTION_SLEEP | ACTION_DISCRETE_ON_OFF, duration, discrete };
2205 MK_VIII::SelfTestHandler::Action
2206 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, VoicePlayer::Voice *voice)
2209 mk->voice_player.play(voice);
2210 Action _action = { ACTION_VOICE | ACTION_DISCRETE_ON_OFF, 0, discrete };
2214 MK_VIII::SelfTestHandler::Action
2215 MK_VIII::SelfTestHandler::done ()
2217 Action _action = { ACTION_DONE, 0, NULL };
2221 MK_VIII::SelfTestHandler::Action
2222 MK_VIII::SelfTestHandler::run ()
2224 // Note that "Terrain INOP" and "Terrain NA" are or'ed to the same
2225 // output discrete (see [SPEC] 6.9.3.5).
2227 #define was_here() was_here_offset(0)
2228 #define was_here_offset(offset) _was_here(__LINE__ * 20 + (offset))
2232 if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_DATABASE)
2233 return play(mk_voice(application_data_base_failed));
2234 else if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE)
2235 return play(mk_voice(configuration_type_invalid));
2237 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2241 return discrete_on(&mk_doutput(gpws_inop), 0.7);
2243 return sleep(0.7); // W/S INOP
2245 return discrete_on(&mk_doutput(tad_inop), 0.7);
2248 mk_doutput(gpws_inop) = false;
2249 // do not disable tad_inop since we must enable Terrain NA
2250 // do not disable W/S INOP since we do not emulate it
2251 return sleep(0.7); // Terrain NA
2255 mk_doutput(tad_inop) = false; // disable Terrain NA
2256 if (mk->io_handler.conf.momentary_flap_override_enabled)
2257 return discrete_on_off(&mk_doutput(flap_override), 1.0);
2260 return discrete_on_off(&mk_doutput(audio_on), 1.0);
2263 if (mk->io_handler.momentary_steep_approach_enabled())
2264 return discrete_on_off(&mk_doutput(steep_approach), 1.0);
2269 if (mk_dinput(glideslope_inhibit))
2270 goto glideslope_end;
2273 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
2274 goto glideslope_inop;
2278 return discrete_on_off(&mk_doutput(gpws_alert), mk_voice(glideslope));
2280 return discrete_on_off(&mk_doutput(glideslope_cancel), 0.7);
2281 goto glideslope_end;
2284 return play(mk_voice(glideslope_inop));
2289 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
2293 return discrete_on_off(&mk_doutput(gpws_warning), mk_voice(pull_up));
2297 return play(mk_voice(gpws_inop));
2302 if (mk_dinput(self_test)) // proceed to long self test
2307 if (mk->mode6_handler.conf.bank_angle_enabled
2308 && mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
2309 return play(mk_voice(bank_angle_inop));
2313 if (mk->mode6_handler.altitude_callouts_enabled()
2314 && mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
2315 return play(mk_voice(callouts_inop));
2323 mk_doutput(gpws_inop) = true;
2324 // do not enable W/S INOP, since we do not emulate it
2325 mk_doutput(tad_inop) = true; // Terrain INOP, Terrain NA
2327 return play(mk_voice(sink_rate));
2330 return play(mk_voice(pull_up));
2332 return play(mk_voice(terrain));
2334 return play(mk_voice(pull_up));
2336 return play(mk_voice(dont_sink));
2338 return play(mk_voice(too_low_terrain));
2340 return play(mk->mode4_handler.conf.voice_too_low_gear);
2342 return play(mk_voice(too_low_flaps));
2344 return play(mk_voice(too_low_terrain));
2346 return play(mk_voice(glideslope));
2349 if (mk->mode6_handler.conf.bank_angle_enabled)
2350 return play(mk_voice(bank_angle));
2355 if (! mk->mode6_handler.altitude_callouts_enabled())
2356 goto callouts_disabled;
2360 if (mk->mode6_handler.conf.minimums_enabled)
2361 return play(mk_voice(minimums));
2365 if (mk->mode6_handler.conf.above_field_voice)
2366 return play(mk->mode6_handler.conf.above_field_voice);
2368 for (unsigned i = 0; i < n_altitude_callouts; i++)
2369 if (! was_here_offset(i))
2371 if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
2372 return play(mk_altitude_voice(i));
2376 if (mk->mode6_handler.conf.smart_500_enabled)
2377 return play(mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500));
2382 return play(mk_voice(minimums_minimums));
2387 if (mk->tcf_handler.conf.enabled)
2388 return play(mk_voice(too_low_terrain));
2395 MK_VIII::SelfTestHandler::start ()
2397 assert(state == STATE_START);
2399 memcpy(&saved_outputs, &mk->io_handler.outputs, sizeof(mk->io_handler.outputs));
2400 memset(&mk->io_handler.outputs, 0, sizeof(mk->io_handler.outputs));
2402 // [SPEC] 6.10.6: "The self-test results are annunciated, at 6db
2403 // lower than the normal audio level selected for the aircraft"
2404 mk->voice_player.set_volume(modify_amplitude(mk->voice_player.conf.volume, -6));
2406 if (mk_dinput(mode6_low_volume))
2407 mk->mode6_handler.set_volume(1.0);
2409 state = STATE_RUNNING;
2410 cancel = CANCEL_NONE;
2411 memset(&action, 0, sizeof(action));
2416 MK_VIII::SelfTestHandler::stop ()
2418 if (state != STATE_NONE)
2420 if (state != STATE_START)
2422 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2423 mk->voice_player.set_volume(mk_dinput(audio_inhibit) ? 0.0 : mk->voice_player.conf.volume);
2425 if (mk_dinput(mode6_low_volume))
2426 mk->mode6_handler.set_volume(modify_amplitude(1.0, -6));
2428 memcpy(&mk->io_handler.outputs, &saved_outputs, sizeof(mk->io_handler.outputs));
2431 button_pressed = false;
2433 // reset self-test handler position
2439 MK_VIII::SelfTestHandler::handle_button_event (bool value)
2441 if (state == STATE_NONE)
2444 state = STATE_START;
2446 else if (state == STATE_START)
2449 state = STATE_NONE; // cancel the not-yet-started test
2451 else if (cancel == CANCEL_NONE)
2455 assert(! button_pressed);
2456 button_pressed = true;
2457 button_press_timestamp = globals->get_sim_time_sec();
2463 if (globals->get_sim_time_sec() - button_press_timestamp < 2)
2464 cancel = CANCEL_SHORT;
2466 cancel = CANCEL_LONG;
2473 MK_VIII::SelfTestHandler::update ()
2475 if (state == STATE_NONE)
2477 else if (state == STATE_START)
2479 if (mk->state_handler.ground && ! mk->io_handler.steep_approach())
2489 if (button_pressed && cancel == CANCEL_NONE)
2491 if (globals->get_sim_time_sec() - button_press_timestamp >= 2)
2492 cancel = CANCEL_LONG;
2496 if (! mk->state_handler.ground || cancel != CANCEL_NONE)
2502 if (test_bits(action.flags, ACTION_SLEEP))
2504 if (globals->get_sim_time_sec() - sleep_start < action.sleep_duration)
2507 if (test_bits(action.flags, ACTION_VOICE))
2509 if (mk->voice_player.voice)
2512 if (test_bits(action.flags, ACTION_DISCRETE_ON_OFF))
2513 *action.discrete = false;
2517 if (test_bits(action.flags, ACTION_SLEEP))
2518 sleep_start = globals->get_sim_time_sec();
2519 if (test_bits(action.flags, ACTION_DONE))
2528 ///////////////////////////////////////////////////////////////////////////////
2529 // AlertHandler ///////////////////////////////////////////////////////////////
2530 ///////////////////////////////////////////////////////////////////////////////
2533 MK_VIII::AlertHandler::select_voice_alerts (unsigned int test)
2535 if (has_alerts(test))
2537 voice_alerts = alerts & test;
2542 voice_alerts &= ~test;
2543 if (voice_alerts == 0)
2544 mk->voice_player.stop();
2551 MK_VIII::AlertHandler::boot ()
2557 MK_VIII::AlertHandler::reposition ()
2561 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2562 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2566 MK_VIII::AlertHandler::reset ()
2571 repeated_alerts = 0;
2572 altitude_callout_voice = NULL;
2576 MK_VIII::AlertHandler::update ()
2578 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2581 // Lamps and voices are prioritized according to [SPEC] 6.9.2.
2583 // Voices which can interrupt other voices (VoicePlayer::PLAY_NOW)
2584 // are specified by [INSTALL] appendix E 5.3.5.
2586 // When a voice is overriden by a higher priority voice and the
2587 // overriding voice stops, we restore the previous voice if it was
2588 // looped (this is not specified by [SPEC] but seems to make sense).
2592 if (has_alerts(ALERT_MODE1_PULL_UP | ALERT_MODE2A | ALERT_MODE2B))
2593 mk->io_handler.set_lamp(IOHandler::LAMP_WARNING);
2594 else if (has_alerts(ALERT_MODE1_SINK_RATE
2595 | ALERT_MODE2A_PREFACE
2596 | ALERT_MODE2B_PREFACE
2597 | ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING
2598 | ALERT_MODE2A_ALTITUDE_GAIN
2599 | ALERT_MODE2B_LANDING_MODE
2601 | ALERT_MODE4_TOO_LOW_FLAPS
2602 | ALERT_MODE4_TOO_LOW_GEAR
2603 | ALERT_MODE4AB_TOO_LOW_TERRAIN
2604 | ALERT_MODE4C_TOO_LOW_TERRAIN
2605 | ALERT_TCF_TOO_LOW_TERRAIN))
2606 mk->io_handler.set_lamp(IOHandler::LAMP_CAUTION);
2607 else if (has_alerts(ALERT_MODE5_SOFT | ALERT_MODE5_HARD))
2608 mk->io_handler.set_lamp(IOHandler::LAMP_GLIDESLOPE);
2610 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2614 if (select_voice_alerts(ALERT_MODE1_PULL_UP))
2616 if (! has_old_alerts(ALERT_MODE1_PULL_UP))
2618 if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate))
2619 mk->voice_player.play(mk_voice(sink_rate), VoicePlayer::PLAY_NOW);
2620 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_LOOPED);
2623 else if (select_voice_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2625 if (! has_old_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2626 mk->voice_player.play(mk_voice(terrain_pause_terrain), VoicePlayer::PLAY_NOW);
2628 else if (select_voice_alerts(ALERT_MODE2A | ALERT_MODE2B))
2630 if (mk->voice_player.voice != mk_voice(pull_up))
2631 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_NOW | VoicePlayer::PLAY_LOOPED);
2633 else if (select_voice_alerts(ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING | ALERT_MODE2B_LANDING_MODE))
2635 if (mk->voice_player.voice != mk_voice(terrain))
2636 mk->voice_player.play(mk_voice(terrain), VoicePlayer::PLAY_LOOPED);
2638 else if (select_voice_alerts(ALERT_MODE6_MINIMUMS))
2640 if (! has_old_alerts(ALERT_MODE6_MINIMUMS))
2641 mk->voice_player.play(mk_voice(minimums_minimums));
2643 else if (select_voice_alerts(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2645 if (must_play_voice(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2646 mk->voice_player.play(mk_voice(too_low_terrain));
2648 else if (select_voice_alerts(ALERT_TCF_TOO_LOW_TERRAIN))
2650 if (must_play_voice(ALERT_TCF_TOO_LOW_TERRAIN))
2651 mk->voice_player.play(mk_voice(too_low_terrain));
2653 else if (select_voice_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2655 if (! has_old_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2657 assert(altitude_callout_voice != NULL);
2658 mk->voice_player.play(altitude_callout_voice);
2659 altitude_callout_voice = NULL;
2662 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
2664 if (must_play_voice(ALERT_MODE4_TOO_LOW_GEAR))
2665 mk->voice_player.play(mk->mode4_handler.conf.voice_too_low_gear);
2667 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_FLAPS))
2669 if (must_play_voice(ALERT_MODE4_TOO_LOW_FLAPS))
2670 mk->voice_player.play(mk_voice(too_low_flaps));
2672 else if (select_voice_alerts(ALERT_MODE1_SINK_RATE))
2674 if (must_play_voice(ALERT_MODE1_SINK_RATE))
2675 mk->voice_player.play(mk_voice(sink_rate_pause_sink_rate));
2676 // [SPEC] 6.2.1: "During the time that the voice message for the
2677 // outer envelope is inhibited and the caution/warning lamp is
2678 // on, the Mode 5 alert message is allowed to annunciate for
2679 // excessive glideslope deviation conditions."
2680 else if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate)
2681 && mk->voice_player.next_voice != mk_voice(sink_rate_pause_sink_rate))
2683 if (has_alerts(ALERT_MODE5_HARD))
2685 voice_alerts |= ALERT_MODE5_HARD;
2686 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2687 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2689 else if (has_alerts(ALERT_MODE5_SOFT))
2691 voice_alerts |= ALERT_MODE5_SOFT;
2692 if (must_play_voice(ALERT_MODE5_SOFT))
2693 mk->voice_player.play(mk_voice(soft_glideslope));
2697 else if (select_voice_alerts(ALERT_MODE3))
2699 if (must_play_voice(ALERT_MODE3))
2700 mk->voice_player.play(mk_voice(dont_sink_pause_dont_sink));
2702 else if (select_voice_alerts(ALERT_MODE5_HARD))
2704 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2705 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2707 else if (select_voice_alerts(ALERT_MODE5_SOFT))
2709 if (must_play_voice(ALERT_MODE5_SOFT))
2710 mk->voice_player.play(mk_voice(soft_glideslope));
2712 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_3))
2714 if (mk->voice_player.voice != mk_voice(bank_angle_bank_angle_3))
2715 mk->voice_player.play(mk_voice(bank_angle_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2717 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_3))
2719 if (mk->voice_player.voice != mk_voice(bank_angle_pause_bank_angle_3))
2720 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2722 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2))
2724 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2725 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2727 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_2))
2729 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2730 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2732 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1))
2734 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2735 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2737 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_1))
2739 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2740 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2743 // remember all alerts voiced so far...
2744 old_alerts |= voice_alerts;
2745 // ... forget those no longer active
2746 old_alerts &= alerts;
2747 repeated_alerts = 0;
2751 MK_VIII::AlertHandler::set_alerts (unsigned int _alerts,
2753 VoicePlayer::Voice *_altitude_callout_voice)
2756 if (test_bits(flags, ALERT_FLAG_REPEAT))
2757 repeated_alerts |= _alerts;
2758 if (_altitude_callout_voice)
2759 altitude_callout_voice = _altitude_callout_voice;
2763 MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
2766 repeated_alerts &= ~_alerts;
2767 if (_alerts & ALERT_MODE6_ALTITUDE_CALLOUT)
2768 altitude_callout_voice = NULL;
2771 ///////////////////////////////////////////////////////////////////////////////
2772 // StateHandler ///////////////////////////////////////////////////////////////
2773 ///////////////////////////////////////////////////////////////////////////////
2776 MK_VIII::StateHandler::update_ground ()
2780 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2781 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30)
2783 if (potentially_airborne_timer.start_or_elapsed() > 1)
2787 potentially_airborne_timer.stop();
2791 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() < 40
2792 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 30)
2798 MK_VIII::StateHandler::enter_ground ()
2801 mk->io_handler.enter_ground();
2803 // [SPEC] 6.0.1 does not specify this, but it seems to be an
2804 // omission; at this point, we must make sure that we are in takeoff
2805 // mode (otherwise the next takeoff might happen in approach mode).
2811 MK_VIII::StateHandler::leave_ground ()
2814 mk->mode2_handler.leave_ground();
2818 MK_VIII::StateHandler::update_takeoff ()
2822 // [SPEC] 6.0.2 is somewhat confusing: it mentions hardcoded
2823 // terrain clearance (500, 750) and airspeed (178, 200) values,
2824 // but it also mentions the mode 4A boundary, which varies as a
2825 // function of aircraft type. We consider that the mentioned
2826 // values are examples, and that we should use the mode 4A upper
2829 if (! mk_data(terrain_clearance).ncd
2830 && ! mk_ainput(computed_airspeed).ncd
2831 && mk_data(terrain_clearance).get() > mk->mode4_handler.get_upper_agl(mk->mode4_handler.conf.modes->ac))
2836 if (! mk_data(radio_altitude).ncd
2837 && mk_data(radio_altitude).get() < mk->mode4_handler.conf.modes->b->min_agl1
2838 && mk->io_handler.flaps_down()
2839 && mk_dinput(landing_gear))
2845 MK_VIII::StateHandler::enter_takeoff ()
2848 mk->io_handler.enter_takeoff();
2849 mk->mode2_handler.enter_takeoff();
2850 mk->mode3_handler.enter_takeoff();
2851 mk->mode6_handler.enter_takeoff();
2855 MK_VIII::StateHandler::leave_takeoff ()
2858 mk->mode6_handler.leave_takeoff();
2862 MK_VIII::StateHandler::post_reposition ()
2864 // Synchronize the state with the current situation.
2867 (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2868 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30);
2870 bool _takeoff = _ground;
2872 if (ground && ! _ground)
2874 else if (! ground && _ground)
2877 if (takeoff && ! _takeoff)
2879 else if (! takeoff && _takeoff)
2884 MK_VIII::StateHandler::update ()
2886 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2893 ///////////////////////////////////////////////////////////////////////////////
2894 // Mode1Handler ///////////////////////////////////////////////////////////////
2895 ///////////////////////////////////////////////////////////////////////////////
2898 MK_VIII::Mode1Handler::get_pull_up_bias ()
2900 // [PILOT] page 54: "Steep Approach has priority over Flap Override
2901 // if selected simultaneously."
2903 if (mk->io_handler.steep_approach())
2905 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
2912 MK_VIII::Mode1Handler::is_pull_up ()
2914 if (! mk->io_handler.gpws_inhibit()
2915 && ! mk->state_handler.ground // [1]
2916 && ! mk_data(radio_altitude).ncd
2917 && ! mk_data(barometric_altitude_rate).ncd
2918 && mk_data(radio_altitude).get() > conf.envelopes->min_agl)
2920 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl1)
2922 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl1(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
2925 else if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl2)
2927 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl2(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
2936 MK_VIII::Mode1Handler::update_pull_up ()
2940 if (! mk_test_alert(MODE1_PULL_UP))
2942 // [SPEC] 6.2.1: at least one sink rate must be issued
2943 // before switching to pull up; if no sink rate has
2944 // occurred, a 0.2 second delay is used.
2945 if (mk->voice_player.voice == mk_voice(sink_rate_pause_sink_rate)
2946 || pull_up_timer.start_or_elapsed() >= 0.2)
2947 mk_set_alerts(mk_alert(MODE1_PULL_UP));
2952 pull_up_timer.stop();
2953 mk_unset_alerts(mk_alert(MODE1_PULL_UP));
2958 MK_VIII::Mode1Handler::get_sink_rate_bias ()
2960 // [PILOT] page 54: "Steep Approach has priority over Flap Override
2961 // if selected simultaneously."
2963 if (mk->io_handler.steep_approach())
2965 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
2967 else if (! mk_data(glideslope_deviation_dots).ncd)
2971 if (mk_data(glideslope_deviation_dots).get() <= -2)
2973 else if (mk_data(glideslope_deviation_dots).get() < 0)
2974 bias = -150 * mk_data(glideslope_deviation_dots).get();
2976 if (mk_data(radio_altitude).get() < 100)
2977 bias *= 0.01 * mk_data(radio_altitude).get();
2986 MK_VIII::Mode1Handler::is_sink_rate ()
2988 return ! mk->io_handler.gpws_inhibit()
2989 && ! mk->state_handler.ground // [1]
2990 && ! mk_data(radio_altitude).ncd
2991 && ! mk_data(barometric_altitude_rate).ncd
2992 && mk_data(radio_altitude).get() > conf.envelopes->min_agl
2993 && mk_data(radio_altitude).get() < 2450
2994 && mk_data(radio_altitude).get() < -572 - 0.6035 * (mk_data(barometric_altitude_rate).get() + get_sink_rate_bias());
2998 MK_VIII::Mode1Handler::get_sink_rate_tti ()
3000 return mk_data(radio_altitude).get() / fabs(mk_data(barometric_altitude_rate).get());
3004 MK_VIII::Mode1Handler::update_sink_rate ()
3008 if (mk_test_alert(MODE1_SINK_RATE))
3010 double tti = get_sink_rate_tti();
3011 if (tti <= sink_rate_tti * 0.8)
3013 // ~20% degradation, warn again and store the new reference tti
3014 sink_rate_tti = tti;
3015 mk_repeat_alert(mk_alert(MODE1_SINK_RATE));
3020 if (sink_rate_timer.start_or_elapsed() >= 0.8)
3022 mk_set_alerts(mk_alert(MODE1_SINK_RATE));
3023 sink_rate_tti = get_sink_rate_tti();
3029 sink_rate_timer.stop();
3030 mk_unset_alerts(mk_alert(MODE1_SINK_RATE));
3035 MK_VIII::Mode1Handler::update ()
3037 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3044 ///////////////////////////////////////////////////////////////////////////////
3045 // Mode2Handler ///////////////////////////////////////////////////////////////
3046 ///////////////////////////////////////////////////////////////////////////////
3049 MK_VIII::Mode2Handler::ClosureRateFilter::limit_radio_altitude_rate (double r)
3051 // Limit radio altitude rate according to aircraft configuration,
3052 // allowing maximum sensitivity during cruise while providing
3053 // progressively less sensitivity during the landing phases of
3056 if (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3057 { // gs deviation <= +- 2 dots
3058 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3059 SG_CLAMP_RANGE(r, -1000.0, 3000.0);
3060 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3061 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3063 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3066 { // no ILS, or gs deviation > +- 2 dots
3067 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3068 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3069 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3070 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3078 MK_VIII::Mode2Handler::ClosureRateFilter::init ()
3081 last_ra.set(&mk_data(radio_altitude));
3082 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3089 MK_VIII::Mode2Handler::ClosureRateFilter::update ()
3091 double elapsed = timer.start_or_elapsed();
3095 if (! mk_data(radio_altitude).ncd && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3097 if (! last_ra.ncd && ! last_ba.ncd)
3099 // compute radio and barometric altitude rates (positive value = descent)
3100 double ra_rate = -(mk_data(radio_altitude).get() - last_ra.get()) / elapsed * 60;
3101 double ba_rate = -(mk_ainput(uncorrected_barometric_altitude).get() - last_ba.get()) / elapsed * 60;
3103 // limit radio altitude rate according to aircraft configuration
3104 ra_rate = limit_radio_altitude_rate(ra_rate);
3106 // apply a low-pass filter to the radio altitude rate
3107 ra_rate = ra_filter.filter(ra_rate);
3108 // apply a high-pass filter to the barometric altitude rate
3109 ba_rate = ba_filter.filter(ba_rate);
3111 // combine both rates to obtain a closure rate
3112 output.set(ra_rate + ba_rate);
3125 last_ra.set(&mk_data(radio_altitude));
3126 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3130 MK_VIII::Mode2Handler::b_conditions ()
3132 return mk->io_handler.flaps_down()
3133 || (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) < 2)
3134 || takeoff_timer.running;
3138 MK_VIII::Mode2Handler::is_a ()
3140 if (! mk->io_handler.gpws_inhibit()
3141 && ! mk->state_handler.ground // [1]
3142 && ! mk_data(radio_altitude).ncd
3143 && ! mk_ainput(computed_airspeed).ncd
3144 && ! closure_rate_filter.output.ncd
3145 && ! b_conditions())
3147 if (mk_data(radio_altitude).get() < 1220)
3149 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3156 if (mk_ainput(computed_airspeed).get() <= conf->airspeed1)
3158 else if (mk_ainput(computed_airspeed).get() >= conf->airspeed2)
3161 upper_limit = 1650 + 8.9 * (mk_ainput(computed_airspeed).get() - conf->airspeed1);
3163 if (mk_data(radio_altitude).get() < upper_limit)
3165 if (mk_data(radio_altitude).get() < 522 + 0.1968 * closure_rate_filter.output.get())
3175 MK_VIII::Mode2Handler::is_b ()
3177 if (! mk->io_handler.gpws_inhibit()
3178 && ! mk->state_handler.ground // [1]
3179 && ! mk_data(radio_altitude).ncd
3180 && ! mk_data(barometric_altitude_rate).ncd
3181 && ! closure_rate_filter.output.ncd
3183 && mk_data(radio_altitude).get() < 789)
3187 if (mk->io_handler.flaps_down())
3189 if (mk_data(barometric_altitude_rate).get() > -400)
3191 else if (mk_data(barometric_altitude_rate).get() < -1000)
3194 lower_limit = -66.777 - 0.667 * mk_data(barometric_altitude_rate).get();
3199 if (mk_data(radio_altitude).get() > lower_limit)
3201 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3210 MK_VIII::Mode2Handler::check_pull_up (unsigned int preface_alert,
3213 if (pull_up_timer.running)
3215 if (pull_up_timer.elapsed() >= 1)
3217 mk_unset_alerts(preface_alert);
3218 mk_set_alerts(alert);
3223 if (! mk->voice_player.voice)
3224 pull_up_timer.start();
3229 MK_VIII::Mode2Handler::update_a ()
3233 if (mk_test_alert(MODE2A_PREFACE))
3234 check_pull_up(mk_alert(MODE2A_PREFACE), mk_alert(MODE2A));
3235 else if (! mk_test_alert(MODE2A))
3237 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3238 mk_set_alerts(mk_alert(MODE2A_PREFACE));
3239 a_start_time = globals->get_sim_time_sec();
3240 pull_up_timer.stop();
3245 if (mk_test_alert(MODE2A_ALTITUDE_GAIN))
3247 if (mk->io_handler.gpws_inhibit()
3248 || mk->state_handler.ground // [1]
3249 || a_altitude_gain_timer.elapsed() >= 45
3250 || mk_data(radio_altitude).ncd
3251 || mk_ainput(uncorrected_barometric_altitude).ncd
3252 || mk_ainput(uncorrected_barometric_altitude).get() - a_altitude_gain_alt >= 300
3253 // [PILOT] page 12: "the visual alert will remain on
3254 // until [...] landing flaps or the flap override switch
3256 || mk->io_handler.flaps_down())
3258 // exit altitude gain mode
3259 a_altitude_gain_timer.stop();
3260 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3264 // [SPEC] 6.2.2.2: "If the terrain starts to fall away
3265 // during this altitude gain time, the voice will shut
3267 if (closure_rate_filter.output.get() < 0)
3268 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3271 else if (mk_test_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A)))
3273 mk_unset_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A));
3275 if (! mk->io_handler.gpws_inhibit()
3276 && ! mk->state_handler.ground // [1]
3277 && globals->get_sim_time_sec() - a_start_time > 3
3278 && ! mk->io_handler.flaps_down()
3279 && ! mk_data(radio_altitude).ncd
3280 && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3282 // [SPEC] 6.2.2.2: mode 2A envelope violated for more
3283 // than 3 seconds, enable altitude gain feature
3285 a_altitude_gain_timer.start();
3286 a_altitude_gain_alt = mk_ainput(uncorrected_barometric_altitude).get();
3288 unsigned int alerts = mk_alert(MODE2A_ALTITUDE_GAIN);
3289 if (closure_rate_filter.output.get() > 0)
3290 alerts |= mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING);
3292 mk_set_alerts(alerts);
3299 MK_VIII::Mode2Handler::update_b ()
3303 // handle normal mode
3305 if (b && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down()))
3307 if (mk_test_alert(MODE2B_PREFACE))
3308 check_pull_up(mk_alert(MODE2B_PREFACE), mk_alert(MODE2B));
3309 else if (! mk_test_alert(MODE2B))
3311 mk_set_alerts(mk_alert(MODE2B_PREFACE));
3312 pull_up_timer.stop();
3316 mk_unset_alerts(mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B));
3318 // handle landing mode ([SPEC] 6.2.2.3: "If both landing gear and
3319 // flaps are in the landing configuration, then the message will be
3322 if (b && mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3323 mk_set_alerts(mk_alert(MODE2B_LANDING_MODE));
3325 mk_unset_alerts(mk_alert(MODE2B_LANDING_MODE));
3329 MK_VIII::Mode2Handler::boot ()
3331 closure_rate_filter.init();
3335 MK_VIII::Mode2Handler::power_off ()
3337 // [SPEC] 6.0.4: "This latching function is not power saved and a
3338 // system reset will force it false."
3339 takeoff_timer.stop();
3343 MK_VIII::Mode2Handler::leave_ground ()
3345 // takeoff, reset timer
3346 takeoff_timer.start();
3350 MK_VIII::Mode2Handler::enter_takeoff ()
3352 // reset timer, in case it's a go-around
3353 takeoff_timer.start();
3357 MK_VIII::Mode2Handler::update ()
3359 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3362 closure_rate_filter.update();
3364 if (takeoff_timer.running && takeoff_timer.elapsed() >= 60)
3365 takeoff_timer.stop();
3371 ///////////////////////////////////////////////////////////////////////////////
3372 // Mode3Handler ///////////////////////////////////////////////////////////////
3373 ///////////////////////////////////////////////////////////////////////////////
3376 MK_VIII::Mode3Handler::max_alt_loss (double _bias)
3378 return conf->max_alt_loss(mk->io_handler.flap_override(), mk_data(radio_altitude).get()) + mk_data(radio_altitude).get() * _bias;
3382 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
3384 // do not repeat altitude-loss alerts below 30ft agl
3385 if (mk_data(radio_altitude).get() > 30)
3387 if (initial_bias < 0.0) // sanity check
3389 // mk-viii spec: repeat alerts whenever losing 20% of initial altitude
3390 while ((alt_loss > max_alt_loss(initial_bias))&&
3391 (initial_bias < 1.0))
3392 initial_bias += 0.2;
3395 return initial_bias;
3399 MK_VIII::Mode3Handler::is (double *alt_loss)
3401 if (has_descent_alt)
3403 int max_agl = conf->max_agl(mk->io_handler.flap_override());
3405 if (mk_ainput(uncorrected_barometric_altitude).ncd
3406 || mk_ainput(uncorrected_barometric_altitude).get() > descent_alt
3407 || mk_data(radio_altitude).ncd
3408 || mk_data(radio_altitude).get() > max_agl)
3411 has_descent_alt = false;
3415 // gear/flaps: [SPEC] 1.3.1.3
3416 if (! mk->io_handler.gpws_inhibit()
3417 && ! mk->state_handler.ground // [1]
3418 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3419 && ! mk_data(barometric_altitude_rate).ncd
3420 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3421 && ! mk_data(radio_altitude).ncd
3422 && mk_data(barometric_altitude_rate).get() < 0)
3424 double _alt_loss = descent_alt - mk_ainput(uncorrected_barometric_altitude).get();
3425 if (armed || (mk_data(radio_altitude).get() > conf->min_agl
3426 && mk_data(radio_altitude).get() < max_agl
3427 && _alt_loss > max_alt_loss(0)))
3429 *alt_loss = _alt_loss;
3437 if (! mk_data(barometric_altitude_rate).ncd
3438 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3439 && mk_data(barometric_altitude_rate).get() < 0)
3441 has_descent_alt = true;
3442 descent_alt = mk_ainput(uncorrected_barometric_altitude).get();
3450 MK_VIII::Mode3Handler::enter_takeoff ()
3453 has_descent_alt = false;
3457 MK_VIII::Mode3Handler::update ()
3459 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3462 if (mk->state_handler.takeoff)
3466 if (! mk->state_handler.ground /* [1] */ && is(&alt_loss))
3468 if (mk_test_alert(MODE3))
3470 double new_bias = get_bias(bias, alt_loss);
3471 if (new_bias > bias)
3474 mk_repeat_alert(mk_alert(MODE3));
3480 bias = get_bias(0.2, alt_loss);
3481 mk_set_alerts(mk_alert(MODE3));
3488 mk_unset_alerts(mk_alert(MODE3));
3491 ///////////////////////////////////////////////////////////////////////////////
3492 // Mode4Handler ///////////////////////////////////////////////////////////////
3493 ///////////////////////////////////////////////////////////////////////////////
3495 // FIXME: for turbofans, the upper limit should be reduced from 1000
3496 // to 800 ft if a sudden change in radio altitude is detected, in
3497 // order to reduce nuisance alerts caused by overflying another
3498 // aircraft (see [PILOT] page 16).
3501 MK_VIII::Mode4Handler::get_upper_agl (const EnvelopesConfiguration *c)
3503 if (mk_ainput(computed_airspeed).get() >= c->airspeed2)
3505 else if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3506 return c->min_agl2(mk_ainput(computed_airspeed).get());
3511 const MK_VIII::Mode4Handler::EnvelopesConfiguration *
3512 MK_VIII::Mode4Handler::get_ab_envelope ()
3514 // [SPEC] 6.2.4.1: "The Mode 4B envelope can also be selected by
3515 // setting flaps to landing configuration or by selecting flap
3517 return mk_dinput(landing_gear) || mk->io_handler.flaps_down()
3523 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
3525 // do not repeat terrain/gear/flap alerts below 30ft agl
3526 if (mk_data(radio_altitude).get() > 30.0)
3528 if (initial_bias < 0.0) // sanity check
3530 while ((mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)&&
3531 (initial_bias < 1.0))
3532 initial_bias += 0.2;
3535 return initial_bias;
3539 MK_VIII::Mode4Handler::handle_alert (unsigned int alert,
3543 if (mk_test_alerts(alert))
3545 double new_bias = get_bias(*bias, min_agl);
3546 if (new_bias > *bias)
3549 mk_repeat_alert(alert);
3554 *bias = get_bias(0.2, min_agl);
3555 mk_set_alerts(alert);
3560 MK_VIII::Mode4Handler::update_ab ()
3562 if (! mk->io_handler.gpws_inhibit()
3563 && ! mk->state_handler.ground
3564 && ! mk->state_handler.takeoff
3565 && ! mk_data(radio_altitude).ncd
3566 && ! mk_ainput(computed_airspeed).ncd
3567 && mk_data(radio_altitude).get() > 30)
3569 const EnvelopesConfiguration *c = get_ab_envelope();
3570 if (mk_ainput(computed_airspeed).get() < c->airspeed1)
3572 if (mk_dinput(landing_gear))
3574 if (! mk->io_handler.flaps_down() && mk_data(radio_altitude).get() < c->min_agl1)
3576 handle_alert(mk_alert(MODE4_TOO_LOW_FLAPS), c->min_agl1, &ab_bias);
3582 if (mk_data(radio_altitude).get() < c->min_agl1)
3584 handle_alert(mk_alert(MODE4_TOO_LOW_GEAR), c->min_agl1, &ab_bias);
3591 mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
3596 MK_VIII::Mode4Handler::update_ab_expanded ()
3598 if (! mk->io_handler.gpws_inhibit()
3599 && ! mk->state_handler.ground
3600 && ! mk->state_handler.takeoff
3601 && ! mk_data(radio_altitude).ncd
3602 && ! mk_ainput(computed_airspeed).ncd
3603 && mk_data(radio_altitude).get() > 30)
3605 const EnvelopesConfiguration *c = get_ab_envelope();
3606 if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3608 double min_agl = get_upper_agl(c);
3609 if (mk_data(radio_altitude).get() < min_agl)
3611 handle_alert(mk_alert(MODE4AB_TOO_LOW_TERRAIN), min_agl, &ab_expanded_bias);
3617 mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
3618 ab_expanded_bias=0.0;
3622 MK_VIII::Mode4Handler::update_c ()
3624 if (! mk->io_handler.gpws_inhibit()
3625 && ! mk->state_handler.ground // [1]
3626 && mk->state_handler.takeoff
3627 && ! mk_data(radio_altitude).ncd
3628 && ! mk_data(terrain_clearance).ncd
3629 && mk_data(radio_altitude).get() > 30
3630 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3631 && mk_data(radio_altitude).get() < get_upper_agl(conf.modes->ac)
3632 && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
3633 handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
3636 mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
3642 MK_VIII::Mode4Handler::update ()
3644 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3648 update_ab_expanded();
3652 ///////////////////////////////////////////////////////////////////////////////
3653 // Mode5Handler ///////////////////////////////////////////////////////////////
3654 ///////////////////////////////////////////////////////////////////////////////
3657 MK_VIII::Mode5Handler::is_hard ()
3659 if (mk_data(radio_altitude).get() > 30
3660 && mk_data(radio_altitude).get() < 300
3661 && mk_data(glideslope_deviation_dots).get() > 2)
3663 if (mk_data(radio_altitude).get() < 150)
3665 if (mk_data(radio_altitude).get() > 293 - 71.43 * mk_data(glideslope_deviation_dots).get())
3676 MK_VIII::Mode5Handler::is_soft (double bias)
3678 // do not repeat glide-slope alerts below 30ft agl
3679 if (mk_data(radio_altitude).get() > 30)
3681 double bias_dots = 1.3 * bias;
3682 if (mk_data(glideslope_deviation_dots).get() > 1.3 + bias_dots)
3684 if (mk_data(radio_altitude).get() < 150)
3686 if (mk_data(radio_altitude).get() > 243 - 71.43 * (mk_data(glideslope_deviation_dots).get() - bias_dots))
3693 if (mk_data(barometric_altitude_rate).ncd)
3697 if (mk_data(barometric_altitude_rate).get() >= 0)
3699 else if (mk_data(barometric_altitude_rate).get() < -500)
3702 upper_limit = -mk_data(barometric_altitude_rate).get() + 500;
3705 if (mk_data(radio_altitude).get() < upper_limit)
3715 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
3717 if (initial_bias < 0.0) // sanity check
3719 while ((is_soft(initial_bias))&&
3720 (initial_bias < 1.0))
3721 initial_bias += 0.2;
3723 return initial_bias;
3727 MK_VIII::Mode5Handler::update_hard (bool is)
3731 if (! mk_test_alert(MODE5_HARD))
3733 if (hard_timer.start_or_elapsed() >= 0.8)
3734 mk_set_alerts(mk_alert(MODE5_HARD));
3740 mk_unset_alerts(mk_alert(MODE5_HARD));
3745 MK_VIII::Mode5Handler::update_soft (bool is)
3749 if (! mk_test_alert(MODE5_SOFT))
3751 double new_bias = get_soft_bias(soft_bias);
3752 if (new_bias > soft_bias)
3754 soft_bias = new_bias;
3755 mk_repeat_alert(mk_alert(MODE5_SOFT));
3760 if (soft_timer.start_or_elapsed() >= 0.8)
3762 soft_bias = get_soft_bias(0.2);
3763 mk_set_alerts(mk_alert(MODE5_SOFT));
3770 mk_unset_alerts(mk_alert(MODE5_SOFT));
3775 MK_VIII::Mode5Handler::update ()
3777 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3780 if (! mk->io_handler.gpws_inhibit()
3781 && ! mk->state_handler.ground // [1]
3782 && ! mk_dinput(glideslope_inhibit) // not on backcourse
3783 && ! mk_data(radio_altitude).ncd
3784 && ! mk_data(glideslope_deviation_dots).ncd
3785 && (! mk->io_handler.conf.localizer_enabled
3786 || mk_data(localizer_deviation_dots).ncd
3787 || mk_data(radio_altitude).get() < 500
3788 || fabs(mk_data(localizer_deviation_dots).get()) < 2)
3789 && (! mk->state_handler.takeoff || mk->io_handler.flaps_down())
3790 && mk_dinput(landing_gear)
3791 && ! mk_doutput(glideslope_cancel))
3793 update_hard(is_hard());
3794 update_soft(is_soft(0));
3803 ///////////////////////////////////////////////////////////////////////////////
3804 // Mode6Handler ///////////////////////////////////////////////////////////////
3805 ///////////////////////////////////////////////////////////////////////////////
3807 // keep sorted in descending order
3808 const int MK_VIII::Mode6Handler::altitude_callout_definitions[] =
3809 { 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10 };
3812 MK_VIII::Mode6Handler::reset_minimums ()
3814 minimums_issued = false;
3818 MK_VIII::Mode6Handler::reset_altitude_callouts ()
3820 for (unsigned i = 0; i < n_altitude_callouts; i++)
3821 altitude_callouts_issued[i] = false;
3825 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
3827 for (unsigned i = 0; i < n_altitude_callouts; i++)
3828 if (mk->voice_player.voice == mk_altitude_voice(i)
3829 || mk->voice_player.next_voice == mk_altitude_voice(i))
3836 MK_VIII::Mode6Handler::is_near_minimums (double callout)
3840 if (! mk_data(decision_height).ncd)
3842 double diff = callout - mk_data(decision_height).get();
3844 if (mk_data(radio_altitude).get() >= 200)
3846 if (fabs(diff) <= 30)
3851 if (diff >= -3 && diff <= 6)
3860 MK_VIII::Mode6Handler::is_outside_band (double elevation, double callout)
3863 return elevation < callout - (elevation > 150 ? 20 : 10);
3867 MK_VIII::Mode6Handler::inhibit_smart_500 ()
3871 if (! mk_data(glideslope_deviation_dots).ncd
3872 && ! mk_dinput(glideslope_inhibit) // backcourse
3873 && ! mk_doutput(glideslope_cancel))
3875 if (mk->io_handler.conf.localizer_enabled
3876 && ! mk_data(localizer_deviation_dots).ncd)
3878 if (fabs(mk_data(localizer_deviation_dots).get()) <= 2)
3883 if (fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3892 MK_VIII::Mode6Handler::boot ()
3894 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3897 last_decision_height = mk_dinput(decision_height);
3898 last_radio_altitude.set(&mk_data(radio_altitude));
3901 for (unsigned i = 0; i < n_altitude_callouts; i++)
3902 altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
3903 && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
3905 // extrapolated from [SPEC] 6.4.2
3906 minimums_issued = mk_dinput(decision_height);
3908 if (conf.above_field_voice)
3911 get_altitude_above_field(&last_altitude_above_field);
3912 // extrapolated from [SPEC] 6.4.2
3913 above_field_issued = ! last_altitude_above_field.ncd
3914 && last_altitude_above_field.get() < 550;
3919 MK_VIII::Mode6Handler::power_off ()
3921 runway_timer.stop();
3925 MK_VIII::Mode6Handler::enter_takeoff ()
3927 reset_altitude_callouts(); // [SPEC] 6.4.2
3928 reset_minimums(); // omitted by [SPEC]; common sense
3932 MK_VIII::Mode6Handler::leave_takeoff ()
3934 reset_altitude_callouts(); // [SPEC] 6.0.2
3935 reset_minimums(); // [SPEC] 6.0.2
3939 MK_VIII::Mode6Handler::set_volume (float volume)
3941 mk_voice(minimums_minimums)->set_volume(volume);
3942 mk_voice(five_hundred_above)->set_volume(volume);
3943 for (unsigned i = 0; i < n_altitude_callouts; i++)
3944 mk_altitude_voice(i)->set_volume(volume);
3948 MK_VIII::Mode6Handler::altitude_callouts_enabled ()
3950 if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
3953 for (unsigned i = 0; i < n_altitude_callouts; i++)
3954 if (conf.altitude_callouts_enabled[i])
3961 MK_VIII::Mode6Handler::update_minimums ()
3963 if (! mk->io_handler.gpws_inhibit()
3964 && (mk->voice_player.voice == mk_voice(minimums_minimums)
3965 || mk->voice_player.next_voice == mk_voice(minimums_minimums)))
3968 if (! mk->io_handler.gpws_inhibit()
3969 && ! mk->state_handler.ground // [1]
3970 && conf.minimums_enabled
3971 && ! minimums_issued
3972 && mk_dinput(landing_gear)
3973 && mk_dinput(decision_height)
3974 && ! last_decision_height)
3976 minimums_issued = true;
3978 // If an altitude callout is playing, stop it now so that the
3979 // minimums callout can be played (note that proper connection
3980 // and synchronization of the radio-altitude ARINC 529 input,
3981 // decision-height discrete and decision-height ARINC 529 input
3982 // will prevent an altitude callout from being played near the
3983 // decision height).
3985 if (is_playing_altitude_callout())
3986 mk->voice_player.stop(VoicePlayer::STOP_NOW);
3988 mk_set_alerts(mk_alert(MODE6_MINIMUMS));
3991 mk_unset_alerts(mk_alert(MODE6_MINIMUMS));
3994 last_decision_height = mk_dinput(decision_height);
3998 MK_VIII::Mode6Handler::update_altitude_callouts ()
4000 if (! mk->io_handler.gpws_inhibit() && is_playing_altitude_callout())
4003 if (! mk->io_handler.gpws_inhibit()
4004 && ! mk->state_handler.ground // [1]
4005 && ! mk_data(radio_altitude).ncd)
4006 for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
4007 if ((conf.altitude_callouts_enabled[i]
4008 || (altitude_callout_definitions[i] == 500
4009 && conf.smart_500_enabled))
4010 && ! altitude_callouts_issued[i]
4011 && (last_radio_altitude.ncd
4012 || last_radio_altitude.get() > altitude_callout_definitions[i]))
4014 // lock out all callouts superior or equal to this one
4015 for (unsigned j = 0; j <= i; j++)
4016 altitude_callouts_issued[j] = true;
4018 altitude_callouts_issued[i] = true;
4019 if (! is_near_minimums(altitude_callout_definitions[i])
4020 && ! is_outside_band(mk_data(radio_altitude).get(), altitude_callout_definitions[i])
4021 && (! conf.smart_500_enabled
4022 || altitude_callout_definitions[i] != 500
4023 || ! inhibit_smart_500()))
4025 mk->alert_handler.set_altitude_callout_alert(mk_altitude_voice(i));
4030 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4033 last_radio_altitude.set(&mk_data(radio_altitude));
4037 MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
4039 if (_runway->lengthFt() < mk->conf.runway_database)
4043 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
4045 // get distance to threshold
4046 return SGGeodesy::distanceNm(pos, _runway->threshold()) <= 5;
4050 MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
4052 for (unsigned int r=0; r<airport->numRunways(); ++r) {
4053 FGRunway* rwy(airport->getRunwayByIndex(r));
4055 if (test_runway(rwy)) return true;
4061 bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
4063 bool ok = self->test_airport(a);
4068 MK_VIII::Mode6Handler::update_runway ()
4071 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4076 // Search for the closest runway threshold in range 5
4077 // nm. Passing 30nm to
4078 // get_closest_airport() provides enough margin for large
4079 // airports, which may have a runway located far away from the
4080 // airport's reference point.
4081 AirportFilter filter(this);
4082 FGPositionedRef apt = FGPositioned::findClosest(
4083 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4086 runway.elevation = apt->elevation();
4089 has_runway.set(apt != NULL);
4093 MK_VIII::Mode6Handler::get_altitude_above_field (Parameter<double> *parameter)
4095 if (! has_runway.ncd && has_runway.get() && ! mk_data(geometric_altitude).ncd)
4096 parameter->set(mk_data(geometric_altitude).get() - runway.elevation);
4102 MK_VIII::Mode6Handler::update_above_field_callout ()
4104 if (! conf.above_field_voice)
4107 // update_runway() has to iterate over the whole runway database
4108 // (which contains thousands of runways), so it would be unwise to
4109 // run it every frame. Run it every 3 seconds. Note that the first
4110 // iteration is run in boot().
4111 if (runway_timer.start_or_elapsed() >= 3)
4114 runway_timer.start();
4117 Parameter<double> altitude_above_field;
4118 get_altitude_above_field(&altitude_above_field);
4120 if (! mk->io_handler.gpws_inhibit()
4121 && (mk->voice_player.voice == conf.above_field_voice
4122 || mk->voice_player.next_voice == conf.above_field_voice))
4125 // handle reset term
4126 if (above_field_issued)
4128 if ((! has_runway.ncd && ! has_runway.get())
4129 || (! altitude_above_field.ncd && altitude_above_field.get() > 700))
4130 above_field_issued = false;
4133 if (! mk->io_handler.gpws_inhibit()
4134 && ! mk->state_handler.ground // [1]
4135 && ! above_field_issued
4136 && ! altitude_above_field.ncd
4137 && altitude_above_field.get() < 550
4138 && (last_altitude_above_field.ncd
4139 || last_altitude_above_field.get() >= 550))
4141 above_field_issued = true;
4143 if (! is_outside_band(altitude_above_field.get(), 500))
4145 mk->alert_handler.set_altitude_callout_alert(conf.above_field_voice);
4150 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4153 last_altitude_above_field.set(&altitude_above_field);
4157 MK_VIII::Mode6Handler::is_bank_angle (double abs_roll_angle, double bias)
4159 return conf.is_bank_angle(&mk_data(radio_altitude),
4160 abs_roll_angle - abs_roll_angle * bias,
4161 mk_dinput(autopilot_engaged));
4165 MK_VIII::Mode6Handler::is_high_bank_angle ()
4167 return mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() >= 210;
4171 MK_VIII::Mode6Handler::get_bank_angle_alerts ()
4173 if (! mk->io_handler.gpws_inhibit()
4174 && ! mk->state_handler.ground // [1]
4175 && ! mk_data(roll_angle).ncd)
4177 double abs_roll_angle = fabs(mk_data(roll_angle).get());
4179 if (is_bank_angle(abs_roll_angle, 0.4))
4180 return is_high_bank_angle()
4181 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2) | mk_alert(MODE6_HIGH_BANK_ANGLE_3))
4182 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2) | mk_alert(MODE6_LOW_BANK_ANGLE_3));
4183 else if (is_bank_angle(abs_roll_angle, 0.2))
4184 return is_high_bank_angle()
4185 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2))
4186 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2));
4187 else if (is_bank_angle(abs_roll_angle, 0))
4188 return is_high_bank_angle()
4189 ? mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4190 : mk_alert(MODE6_LOW_BANK_ANGLE_1);
4197 MK_VIII::Mode6Handler::update_bank_angle ()
4199 if (! conf.bank_angle_enabled)
4202 unsigned int alerts = get_bank_angle_alerts();
4204 // [SPEC] 6.4.4 specifies that the non-continuous alerts
4205 // (MODE6_*_BANK_ANGLE_1 and MODE6_*_BANK_ANGLE_2) are locked out
4206 // until the initial envelope is exited.
4208 if (! test_bits(alerts, mk_alert(MODE6_LOW_BANK_ANGLE_3)))
4209 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_3));
4210 if (! test_bits(alerts, mk_alert(MODE6_HIGH_BANK_ANGLE_3)))
4211 mk_unset_alerts(mk_alert(MODE6_HIGH_BANK_ANGLE_3));
4214 mk_set_alerts(alerts);
4216 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_1)
4217 | mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4218 | mk_alert(MODE6_LOW_BANK_ANGLE_2)
4219 | mk_alert(MODE6_HIGH_BANK_ANGLE_2));
4223 MK_VIII::Mode6Handler::update ()
4225 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4228 if (! mk->state_handler.takeoff
4229 && ! mk_data(radio_altitude).ncd
4230 && mk_data(radio_altitude).get() > 1000)
4232 reset_altitude_callouts(); // [SPEC] 6.4.2
4233 reset_minimums(); // common sense
4237 update_altitude_callouts();
4238 update_above_field_callout();
4239 update_bank_angle();
4242 ///////////////////////////////////////////////////////////////////////////////
4243 // TCFHandler /////////////////////////////////////////////////////////////////
4244 ///////////////////////////////////////////////////////////////////////////////
4246 bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
4248 return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
4252 MK_VIII::TCFHandler::update_runway ()
4255 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4259 // Search for the intended destination runway of the closest
4260 // airport in range 15 nm. Passing 30nm to get_closest_airport()
4261 // provides enough margin for
4262 // large airports, which may have a runway located far away from
4263 // the airport's reference point.
4264 AirportFilter filter(mk);
4265 SGGeod apos = SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get());
4266 FGAirport* apt = FGAirport::findClosest(apos, 30.0, &filter);
4270 FGRunway* _runway = apt->findBestRunwayForPos(apos).get();
4272 if (!_runway) return;
4276 runway.center = _runway->pointOnCenterline(_runway->lengthM() * 0.5);
4278 runway.elevation = apt->elevation();
4280 runway.half_width_m = _runway->widthM() * 0.5;
4281 double half_length_m = _runway->lengthM() * 0.5;
4282 runway.half_length = half_length_m * SG_METER_TO_NM;
4289 // get heading of runway end (h0)
4290 runway.edge.heading = _runway->headingDeg();
4292 // get position of runway threshold (e0)
4293 runway.edge.position = _runway->begin();
4295 // get cartesian coordinates of both runway ends
4296 runway.bias_points[0] = _runway->cart();
4297 runway.bias_points[1] = _runway->reciprocalRunway()->cart();
4300 // Returns true if the current GPS position is inside the edge
4301 // triangle of @edge. The edge triangle, which is specified and
4302 // represented in [SPEC] 6.3.1, is defined as an isocele right
4303 // triangle of infinite height, whose right angle is located at the
4304 // position of @edge, and whose height is parallel to the heading of
4307 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
4309 double az = SGGeodesy::courseDeg( SGGeod::fromDeg(mk_data(gps_longitude).get(),
4310 mk_data(gps_latitude).get()),
4312 return get_heading_difference(az, edge->heading) <= 45;
4315 // Returns true if the current GPS position is inside the bias area of
4316 // the currently selected runway.
4318 MK_VIII::TCFHandler::is_inside_bias_area ()
4320 double half_bias_width_m = k * SG_NM_TO_METER + runway.half_width_m;
4321 SGVec3d cpos = SGVec3d::fromGeod( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4322 mk_data(gps_latitude).get(),
4323 runway.elevation) );
4324 SGLineSegmentd bias_line = SGLineSegmentd(runway.bias_points[0], runway.bias_points[1]);
4325 return dist(cpos, bias_line) < half_bias_width_m;
4329 MK_VIII::TCFHandler::is_tcf ()
4331 if (mk_data(radio_altitude).get() > 10)
4335 double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4336 mk_data(gps_latitude).get(),
4340 // distance to the inner envelope edge
4341 double edge_distance = distance - runway.half_length - k;
4343 if (edge_distance > 15)
4345 if (mk_data(radio_altitude).get() < 700)
4348 else if (edge_distance > 12)
4350 if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
4353 else if (edge_distance > 4)
4355 if (mk_data(radio_altitude).get() < 400)
4358 else if (edge_distance > 2.45)
4360 if (mk_data(radio_altitude).get() < 100 * edge_distance)
4363 else if ( is_inside_edge_triangle(&runway.edge) && (edge_distance > 0.01) )
4365 if (mk_data(radio_altitude).get() < 100 * edge_distance)
4368 else if (! is_inside_bias_area())
4370 if (mk_data(radio_altitude).get() < 245)
4374 else if (mk_data(radio_altitude).get() < 700)
4383 MK_VIII::TCFHandler::is_rfcf ()
4387 double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4388 mk_data(gps_latitude).get(),
4391 distance -= runway.half_length;
4395 double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
4397 double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
4399 if ( (distance > 1.5) && (altitude_above_field < 300.0) )
4401 else if ( (distance > 0.0) && (altitude_above_field < 200 * distance) )
4410 MK_VIII::TCFHandler::update ()
4412 if (mk->configuration_module.state != ConfigurationModule::STATE_OK || ! conf.enabled)
4415 // update_runway() has to iterate over the whole runway database
4416 // (which contains thousands of runways), so it would be unwise to
4417 // run it every frame. Run it every 3 seconds.
4418 if (! runway_timer.running || runway_timer.elapsed() >= 3)
4421 runway_timer.start();
4424 if (! mk_dinput(ta_tcf_inhibit)
4425 && ! mk->state_handler.ground // [1]
4426 && ! mk_data(gps_latitude).ncd
4427 && ! mk_data(gps_longitude).ncd
4428 && ! mk_data(radio_altitude).ncd
4429 && ! mk_data(geometric_altitude).ncd
4430 && ! mk_data(gps_vertical_figure_of_merit).ncd)
4435 _reference = mk_data(radio_altitude).get_pointer();
4437 _reference = mk_data(geometric_altitude).get_pointer();
4443 if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
4445 double new_bias = bias;
4446 // do not repeat terrain alerts below 30ft agl
4447 if (mk_data(radio_altitude).get() > 30)
4449 if (new_bias < 0.0) // sanity check
4451 while ((*reference < initial_value - initial_value * new_bias)&&
4456 if (new_bias > bias)
4459 mk_repeat_alert(mk_alert(TCF_TOO_LOW_TERRAIN));
4465 reference = _reference;
4466 initial_value = *reference;
4467 mk_set_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4474 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4477 ///////////////////////////////////////////////////////////////////////////////
4478 // MK_VIII ////////////////////////////////////////////////////////////////////
4479 ///////////////////////////////////////////////////////////////////////////////
4481 MK_VIII::MK_VIII (SGPropertyNode *node)
4482 : properties_handler(this),
4485 power_handler(this),
4486 system_handler(this),
4487 configuration_module(this),
4488 fault_handler(this),
4491 self_test_handler(this),
4492 alert_handler(this),
4493 state_handler(this),
4494 mode1_handler(this),
4495 mode2_handler(this),
4496 mode3_handler(this),
4497 mode4_handler(this),
4498 mode5_handler(this),
4499 mode6_handler(this),
4502 for (int i = 0; i < node->nChildren(); ++i)
4504 SGPropertyNode *child = node->getChild(i);
4505 string cname = child->getName();
4506 string cval = child->getStringValue();
4508 if (cname == "name")
4510 else if (cname == "number")
4511 num = child->getIntValue();
4514 SG_LOG(SG_INSTR, SG_WARN, "Error in mk-viii config logic");
4516 SG_LOG(SG_INSTR, SG_WARN, "Section = " << name);
4524 properties_handler.init();
4525 voice_player.init();
4531 SGPropertyNode *node = fgGetNode(("/instrumentation/" + name).c_str(), num, true);
4533 configuration_module.bind(node);
4534 power_handler.bind(node);
4535 io_handler.bind(node);
4536 voice_player.bind(node, "Sounds/mk-viii/");
4542 properties_handler.unbind();
4546 MK_VIII::update (double dt)
4548 power_handler.update();
4549 system_handler.update();
4551 if (system_handler.state != SystemHandler::STATE_ON)
4554 io_handler.update_inputs();
4555 io_handler.update_input_faults();
4557 voice_player.update();
4558 state_handler.update();
4560 if (self_test_handler.update())
4563 io_handler.update_internal_latches();
4564 io_handler.update_lamps();
4566 mode1_handler.update();
4567 mode2_handler.update();
4568 mode3_handler.update();
4569 mode4_handler.update();
4570 mode5_handler.update();
4571 mode6_handler.update();
4572 tcf_handler.update();
4574 alert_handler.update();
4575 io_handler.update_outputs();