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>
86 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
87 # include <Include/version.h>
89 # include <Include/no_version.h>
92 #include <Main/fg_props.hxx>
93 #include <Main/globals.hxx>
94 #include "instrument_mgr.hxx"
95 #include "mk_viii.hxx"
97 ///////////////////////////////////////////////////////////////////////////////
98 // constants //////////////////////////////////////////////////////////////////
99 ///////////////////////////////////////////////////////////////////////////////
101 #define GLIDESLOPE_DOTS_TO_DDM 0.0875 // [INSTALL] 6.2.30
102 #define GLIDESLOPE_DDM_TO_DOTS (1 / GLIDESLOPE_DOTS_TO_DDM)
104 #define LOCALIZER_DOTS_TO_DDM 0.0775 // [INSTALL] 6.2.33
105 #define LOCALIZER_DDM_TO_DOTS (1 / LOCALIZER_DOTS_TO_DDM)
107 ///////////////////////////////////////////////////////////////////////////////
108 // helpers ////////////////////////////////////////////////////////////////////
109 ///////////////////////////////////////////////////////////////////////////////
111 #define assert_not_reached() assert(false)
112 #define n_elements(_array) (sizeof(_array) / sizeof((_array)[0]))
113 #define test_bits(_bits, _test) (((_bits) & (_test)) != 0)
115 #define mk_node(_name) (mk->properties_handler.external_properties._name)
117 #define mk_dinput_feed(_name) (mk->io_handler.input_feeders.discretes._name)
118 #define mk_dinput(_name) (mk->io_handler.inputs.discretes._name)
119 #define mk_ainput_feed(_name) (mk->io_handler.input_feeders.arinc429._name)
120 #define mk_ainput(_name) (mk->io_handler.inputs.arinc429._name)
121 #define mk_doutput(_name) (mk->io_handler.outputs.discretes._name)
122 #define mk_aoutput(_name) (mk->io_handler.outputs.arinc429._name)
123 #define mk_data(_name) (mk->io_handler.data._name)
125 #define mk_voice(_name) (mk->voice_player.voices._name)
126 #define mk_altitude_voice(_i) (mk->voice_player.voices.altitude_callouts[(_i)])
128 #define mk_alert(_name) (AlertHandler::ALERT_ ## _name)
129 #define mk_alert_flag(_name) (AlertHandler::ALERT_FLAG_ ## _name)
130 #define mk_set_alerts (mk->alert_handler.set_alerts)
131 #define mk_unset_alerts (mk->alert_handler.unset_alerts)
132 #define mk_repeat_alert (mk->alert_handler.repeat_alert)
133 #define mk_test_alert(_name) (mk_test_alerts(mk_alert(_name)))
134 #define mk_test_alerts(_test) (test_bits(mk->alert_handler.alerts, (_test)))
136 const double MK_VIII::TCFHandler::k = 0.25;
139 modify_amplitude (double amplitude, double dB)
141 return amplitude * pow(10.0, dB / 20.0);
145 get_heading_difference (double h1, double h2)
147 double diff = h1 - h2;
157 ///////////////////////////////////////////////////////////////////////////////
158 // PropertiesHandler //////////////////////////////////////////////////////////
159 ///////////////////////////////////////////////////////////////////////////////
162 MK_VIII::PropertiesHandler::init ()
164 mk_node(ai_caged) = fgGetNode("/instrumentation/attitude-indicator/caged-flag", true);
165 mk_node(ai_roll) = fgGetNode("/instrumentation/attitude-indicator/indicated-roll-deg", true);
166 mk_node(ai_serviceable) = fgGetNode("/instrumentation/attitude-indicator/serviceable", true);
167 mk_node(altimeter_altitude) = fgGetNode("/instrumentation/altimeter/indicated-altitude-ft", true);
168 mk_node(altimeter_serviceable) = fgGetNode("/instrumentation/altimeter/serviceable", true);
169 mk_node(altitude) = fgGetNode("/position/altitude-ft", true);
170 mk_node(altitude_agl) = fgGetNode("/position/altitude-agl-ft", true);
171 mk_node(altitude_gear_agl) = fgGetNode("/position/gear-agl-ft", true);
172 mk_node(altitude_radar_agl) = fgGetNode("/instrumentation/radar-altimeter/radar-altitude-ft", true);
173 mk_node(orientation_roll) = fgGetNode("/orientation/roll-deg", true);
174 mk_node(asi_serviceable) = fgGetNode("/instrumentation/airspeed-indicator/serviceable", true);
175 mk_node(asi_speed) = fgGetNode("/instrumentation/airspeed-indicator/indicated-speed-kt", true);
176 mk_node(autopilot_heading_lock) = fgGetNode("/autopilot/locks/heading", true);
177 mk_node(flaps) = fgGetNode("/controls/flight/flaps", true);
178 mk_node(gear_down) = fgGetNode("/controls/gear/gear-down", true);
179 mk_node(latitude) = fgGetNode("/position/latitude-deg", true);
180 mk_node(longitude) = fgGetNode("/position/longitude-deg", true);
181 mk_node(nav0_cdi_serviceable) = fgGetNode("/instrumentation/nav/cdi/serviceable", true);
182 mk_node(nav0_gs_distance) = fgGetNode("/instrumentation/nav/gs-distance", true);
183 mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection", true);
184 mk_node(nav0_gs_serviceable) = fgGetNode("/instrumentation/nav/gs/serviceable", true);
185 mk_node(nav0_has_gs) = fgGetNode("/instrumentation/nav/has-gs", true);
186 mk_node(nav0_heading_needle_deflection) = fgGetNode("/instrumentation/nav/heading-needle-deflection", true);
187 mk_node(nav0_in_range) = fgGetNode("/instrumentation/nav/in-range", true);
188 mk_node(nav0_nav_loc) = fgGetNode("/instrumentation/nav/nav-loc", true);
189 mk_node(nav0_serviceable) = fgGetNode("/instrumentation/nav/serviceable", true);
190 mk_node(power) = fgGetNode(("/systems/electrical/outputs/" + mk->name).c_str(), mk->num, true);
191 mk_node(replay_state) = fgGetNode("/sim/freeze/replay-state", true);
192 mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
195 ///////////////////////////////////////////////////////////////////////////////
196 // PowerHandler ///////////////////////////////////////////////////////////////
197 ///////////////////////////////////////////////////////////////////////////////
200 MK_VIII::PowerHandler::bind (SGPropertyNode *node)
202 mk->properties_handler.tie(node, "serviceable", SGRawValuePointer<bool>(&serviceable));
206 MK_VIII::PowerHandler::handle_abnormal_voltage (bool abnormal,
212 if (timer->start_or_elapsed() >= max_duration)
213 return true; // power loss
222 MK_VIII::PowerHandler::update ()
224 double voltage = mk_node(power)->getDoubleValue();
225 bool now_powered = true;
233 if (handle_abnormal_voltage(voltage < 20.5, &low_surge_timer, 0.03))
235 if (handle_abnormal_voltage(voltage < 22.0 || voltage > 30.3, &abnormal_timer, 300))
237 if (handle_abnormal_voltage(voltage > 32.2, &high_surge_timer, 1))
239 if (handle_abnormal_voltage(voltage > 37.8, &very_high_surge_timer, 0.1))
249 power_loss_timer.stop();
252 if (power_loss_timer.start_or_elapsed() >= 0.2)
264 MK_VIII::PowerHandler::power_on ()
267 mk->system_handler.power_on();
271 MK_VIII::PowerHandler::power_off ()
274 mk->system_handler.power_off();
275 mk->voice_player.stop(VoicePlayer::STOP_NOW);
276 mk->self_test_handler.power_off(); // run before IOHandler::power_off()
277 mk->io_handler.power_off();
278 mk->mode2_handler.power_off();
279 mk->mode6_handler.power_off();
282 ///////////////////////////////////////////////////////////////////////////////
283 // SystemHandler //////////////////////////////////////////////////////////////
284 ///////////////////////////////////////////////////////////////////////////////
287 MK_VIII::SystemHandler::power_on ()
289 state = STATE_BOOTING;
291 // [SPEC] 3.5.2 mentions a 20 seconds maximum boot delay. We use a
292 // random delay between 3 and 5 seconds.
294 boot_delay = 3 + sg_random() * 2;
299 MK_VIII::SystemHandler::power_off ()
307 MK_VIII::SystemHandler::update ()
309 if (state == STATE_BOOTING)
311 if (boot_timer.elapsed() >= boot_delay)
313 last_replay_state = mk_node(replay_state)->getIntValue();
315 mk->configuration_module.boot();
317 mk->io_handler.boot();
318 mk->fault_handler.boot();
319 mk->alert_handler.boot();
321 // inputs are needed by the following boot() routines
322 mk->io_handler.update_inputs();
324 mk->mode2_handler.boot();
325 mk->mode6_handler.boot();
329 mk->io_handler.post_boot();
332 else if (state != STATE_OFF && mk->configuration_module.state == ConfigurationModule::STATE_OK)
334 // If the replay state changes, switch to reposition mode for 3
335 // seconds ([SPEC] 6.0.5) to avoid spurious alerts.
337 int replay_state = mk_node(replay_state)->getIntValue();
338 if (replay_state != last_replay_state)
340 mk->alert_handler.reposition();
341 mk->io_handler.reposition();
343 last_replay_state = replay_state;
344 state = STATE_REPOSITION;
345 reposition_timer.start();
348 if (state == STATE_REPOSITION && reposition_timer.elapsed() >= 3)
350 // inputs are needed by StateHandler::post_reposition()
351 mk->io_handler.update_inputs();
353 mk->state_handler.post_reposition();
360 ///////////////////////////////////////////////////////////////////////////////
361 // ConfigurationModule ////////////////////////////////////////////////////////
362 ///////////////////////////////////////////////////////////////////////////////
364 MK_VIII::ConfigurationModule::ConfigurationModule (MK_VIII *device)
367 // arbitrary defaults
368 categories[CATEGORY_AIRCRAFT_MODE_TYPE_SELECT] = 0;
369 categories[CATEGORY_AIR_DATA_INPUT_SELECT] = 1;
370 categories[CATEGORY_POSITION_INPUT_SELECT] = 2;
371 categories[CATEGORY_ALTITUDE_CALLOUTS] = 13;
372 categories[CATEGORY_AUDIO_MENU_SELECT] = 0;
373 categories[CATEGORY_TERRAIN_DISPLAY_SELECT] = 1;
374 categories[CATEGORY_OPTIONS_SELECT_GROUP_1] = 124;
375 categories[CATEGORY_RADIO_ALTITUDE_INPUT_SELECT] = 2;
376 categories[CATEGORY_NAVIGATION_INPUT_SELECT] = 3;
377 categories[CATEGORY_ATTITUDE_INPUT_SELECT] = 6;
378 categories[CATEGORY_HEADING_INPUT_SELECT] = 2;
379 categories[CATEGORY_WINDSHEAR_INPUT_SELECT] = 0;
380 categories[CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT] = 7;
381 categories[CATEGORY_AUDIO_OUTPUT_LEVEL] = 0;
382 categories[CATEGORY_UNDEFINED_INPUT_SELECT_1] = 0;
383 categories[CATEGORY_UNDEFINED_INPUT_SELECT_2] = 0;
384 categories[CATEGORY_UNDEFINED_INPUT_SELECT_3] = 0;
387 static double m1_t1_min_agl1 (double vs) { return -1620 - 1.1133 * vs; }
388 static double m1_t1_min_agl2 (double vs) { return -400 - 0.4 * vs; }
389 static double m1_t4_min_agl1 (double vs) { return -1625.47 - 1.1167 * vs; }
390 static double m1_t4_min_agl2 (double vs) { return -0.1958 * vs; }
392 static int m3_t1_max_agl (bool flap_override) { return 1500; }
393 static double m3_t1_max_alt_loss (bool flap_override, double agl) { return 5.4 + 0.092 * agl; }
394 static int m3_t2_max_agl (bool flap_override) { return flap_override ? 815 : 925; }
395 static double m3_t2_max_alt_loss (bool flap_override, double agl)
397 int bias = agl > 700 ? 5 : 0;
400 return (9.0 + 0.184 * agl) + bias;
402 return (5.4 + 0.092 * agl) + bias;
405 static double m4_t1_min_agl2 (double airspeed) { return -1083 + 8.333 * airspeed; }
406 static double m4_t568_a_min_agl2 (double airspeed) { return -1523 + 11.36 * airspeed; }
407 static double m4_t79_a_min_agl2 (double airspeed) { return -1182 + 11.36 * airspeed; }
408 static double m4_t568_b_min_agl2 (double airspeed) { return -1570 + 11.6 * airspeed; }
409 static double m4_t79_b_min_agl2 (double airspeed) { return -1222 + 11.6 * airspeed; }
412 MK_VIII::ConfigurationModule::m6_t2_is_bank_angle (Parameter<double> *agl,
418 if (agl->ncd || agl->get() > 122)
419 return abs_roll_deg > 33;
423 if (agl->ncd || agl->get() > 2450)
424 return abs_roll_deg > 55;
425 else if (agl->get() > 150)
426 return agl->get() < 153.33333 * abs_roll_deg - 5983.3333;
430 return agl->get() < 4 * abs_roll_deg - 10;
431 else if (agl->get() > 5)
432 return abs_roll_deg > 10;
438 MK_VIII::ConfigurationModule::m6_t4_is_bank_angle (Parameter<double> *agl,
444 if (agl->ncd || agl->get() > 156)
445 return abs_roll_deg > 33;
449 if (agl->ncd || agl->get() > 210)
450 return abs_roll_deg > 50;
454 return agl->get() < 5.7142857 * abs_roll_deg - 75.714286;
460 MK_VIII::ConfigurationModule::read_aircraft_mode_type_select (int value)
462 static const Mode1Handler::EnvelopesConfiguration m1_t1 =
463 { false, 10, m1_t1_min_agl1, 284, m1_t1_min_agl2, 2450 };
464 static const Mode1Handler::EnvelopesConfiguration m1_t4 =
465 { true, 50, m1_t4_min_agl1, 346, m1_t4_min_agl2, 1958 };
467 static const Mode2Handler::Configuration m2_t1 = { 190, 280 };
468 static const Mode2Handler::Configuration m2_t4 = { 220, 310 };
469 static const Mode2Handler::Configuration m2_t5 = { 220, 310 };
471 static const Mode3Handler::Configuration m3_t1 =
472 { 30, m3_t1_max_agl, m3_t1_max_alt_loss };
473 static const Mode3Handler::Configuration m3_t2 =
474 { 50, m3_t2_max_agl, m3_t2_max_alt_loss };
476 static const Mode4Handler::EnvelopesConfiguration m4_t1_ac =
477 { 190, 250, 500, m4_t1_min_agl2, 1000 };
478 static const Mode4Handler::EnvelopesConfiguration m4_t5_ac =
479 { 178, 200, 500, m4_t568_a_min_agl2, 1000 };
480 static const Mode4Handler::EnvelopesConfiguration m4_t68_ac =
481 { 178, 200, 500, m4_t568_a_min_agl2, 750 };
482 static const Mode4Handler::EnvelopesConfiguration m4_t79_ac =
483 { 148, 170, 500, m4_t79_a_min_agl2, 750 };
485 static const Mode4Handler::EnvelopesConfiguration m4_t1_b =
486 { 159, 250, 245, m4_t1_min_agl2, 1000 };
487 static const Mode4Handler::EnvelopesConfiguration m4_t5_b =
488 { 148, 200, 200, m4_t568_b_min_agl2, 1000 };
489 static const Mode4Handler::EnvelopesConfiguration m4_t6_b =
490 { 150, 200, 170, m4_t568_b_min_agl2, 750 };
491 static const Mode4Handler::EnvelopesConfiguration m4_t7_b =
492 { 120, 170, 170, m4_t79_b_min_agl2, 750 };
493 static const Mode4Handler::EnvelopesConfiguration m4_t8_b =
494 { 148, 200, 150, m4_t568_b_min_agl2, 750 };
495 static const Mode4Handler::EnvelopesConfiguration m4_t9_b =
496 { 118, 170, 150, m4_t79_b_min_agl2, 750 };
498 static const Mode4Handler::ModesConfiguration m4_t1 = { &m4_t1_ac, &m4_t1_b };
499 static const Mode4Handler::ModesConfiguration m4_t5 = { &m4_t5_ac, &m4_t5_b };
500 static const Mode4Handler::ModesConfiguration m4_t6 = { &m4_t68_ac, &m4_t6_b };
501 static const Mode4Handler::ModesConfiguration m4_t7 = { &m4_t79_ac, &m4_t7_b };
502 static const Mode4Handler::ModesConfiguration m4_t8 = { &m4_t68_ac, &m4_t8_b };
503 static const Mode4Handler::ModesConfiguration m4_t9 = { &m4_t79_ac, &m4_t9_b };
505 static Mode6Handler::BankAnglePredicate m6_t2 = m6_t2_is_bank_angle;
506 static Mode6Handler::BankAnglePredicate m6_t4 = m6_t4_is_bank_angle;
508 static const IOHandler::FaultsConfiguration f_slow = { 180, 200 };
509 static const IOHandler::FaultsConfiguration f_fast = { 250, 290 };
514 const Mode1Handler::EnvelopesConfiguration *m1;
515 const Mode2Handler::Configuration *m2;
516 const Mode3Handler::Configuration *m3;
517 const Mode4Handler::ModesConfiguration *m4;
518 Mode6Handler::BankAnglePredicate m6;
519 const IOHandler::FaultsConfiguration *f;
521 } aircraft_types[] = {
522 { 0, &m1_t4, &m2_t4, &m3_t2, &m4_t6, m6_t4, &f_fast, 2000 },
523 { 1, &m1_t4, &m2_t4, &m3_t2, &m4_t8, m6_t4, &f_fast, 2000 },
524 { 2, &m1_t4, &m2_t4, &m3_t2, &m4_t5, m6_t4, &f_fast, 2000 },
525 { 3, &m1_t4, &m2_t5, &m3_t2, &m4_t7, m6_t4, &f_slow, 2000 },
526 { 4, &m1_t4, &m2_t5, &m3_t2, &m4_t9, m6_t4, &f_slow, 2000 },
527 { 254, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 3500 },
528 { 255, &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 2000 }
531 for (size_t i = 0; i < n_elements(aircraft_types); i++)
532 if (aircraft_types[i].type == value)
534 mk->mode1_handler.conf.envelopes = aircraft_types[i].m1;
535 mk->mode2_handler.conf = aircraft_types[i].m2;
536 mk->mode3_handler.conf = aircraft_types[i].m3;
537 mk->mode4_handler.conf.modes = aircraft_types[i].m4;
538 mk->mode6_handler.conf.is_bank_angle = aircraft_types[i].m6;
539 mk->io_handler.conf.faults = aircraft_types[i].f;
540 mk->conf.runway_database = aircraft_types[i].runway_database;
544 state = STATE_INVALID_AIRCRAFT_TYPE;
549 MK_VIII::ConfigurationModule::read_air_data_input_select (int value)
552 return (value >= 0 && value <= 6) || (value >= 10 && value <= 13) || value == 255;
556 MK_VIII::ConfigurationModule::read_position_input_select (int value)
559 mk->io_handler.conf.use_internal_gps = true;
560 else if ((value >= 0 && value <= 5)
561 || (value >= 10 && value <= 13)
564 mk->io_handler.conf.use_internal_gps = false;
572 MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
587 { 0, { MINIMUMS, SMART_500, 200, 100, 50, 40, 30, 20, 10, 0 } },
588 { 1, { MINIMUMS, SMART_500, 200, 0 } },
589 { 2, { MINIMUMS, SMART_500, 100, 50, 40, 30, 20, 10, 0 } },
590 { 3, { MINIMUMS, SMART_500, 0 } },
591 { 4, { MINIMUMS, 200, 100, 50, 40, 30, 20, 10, 0 } },
592 { 5, { MINIMUMS, 200, 0 } },
593 { 6, { MINIMUMS, 100, 50, 40, 30, 20, 10, 0 } },
595 { 8, { MINIMUMS, 0 } },
596 { 9, { MINIMUMS, 500, 200, 100, 50, 40, 30, 20, 10, 0 } },
597 { 10, { MINIMUMS, 500, 200, 0 } },
598 { 11, { MINIMUMS, 500, 100, 50, 40, 30, 20, 10, 0 } },
599 { 12, { MINIMUMS, 500, 0 } },
600 { 13, { MINIMUMS, 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10, 0 } },
601 { 14, { MINIMUMS, 100, 0 } },
602 { 15, { MINIMUMS, 200, 100, 0 } },
603 { 100, { FIELD_500, 0 } },
604 { 101, { FIELD_500_ABOVE, 0 } }
609 mk->mode6_handler.conf.minimums_enabled = false;
610 mk->mode6_handler.conf.smart_500_enabled = false;
611 mk->mode6_handler.conf.above_field_voice = NULL;
612 for (i = 0; i < n_altitude_callouts; i++)
613 mk->mode6_handler.conf.altitude_callouts_enabled[i] = false;
615 for (i = 0; i < n_elements(values); i++)
616 if (values[i].id == value)
618 for (int j = 0; values[i].callouts[j] != 0; j++)
619 switch (values[i].callouts[j])
622 mk->mode6_handler.conf.minimums_enabled = true;
626 mk->mode6_handler.conf.smart_500_enabled = true;
630 mk->mode6_handler.conf.above_field_voice = mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500);
633 case FIELD_500_ABOVE:
634 mk->mode6_handler.conf.above_field_voice = mk_voice(five_hundred_above);
638 for (unsigned k = 0; k < n_altitude_callouts; k++)
639 if (mk->mode6_handler.altitude_callout_definitions[k] == values[i].callouts[j])
640 mk->mode6_handler.conf.altitude_callouts_enabled[k] = true;
651 MK_VIII::ConfigurationModule::read_audio_menu_select (int value)
653 if (value == 0 || value == 1)
654 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_gear);
655 else if (value == 2 || value == 3)
656 mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_flaps);
664 MK_VIII::ConfigurationModule::read_terrain_display_select (int value)
667 mk->tcf_handler.conf.enabled = false;
668 else if (value == 0 || value == 1 || (value >= 3 && value <= 15)
669 || (value >= 18 && value <= 20) || (value >= 235 && value <= 255))
670 mk->tcf_handler.conf.enabled = true;
678 MK_VIII::ConfigurationModule::read_options_select_group_1 (int value)
680 if (value >= 0 && value < 128)
682 mk->io_handler.conf.flap_reversal = test_bits(value, 1 << 1);
683 mk->mode6_handler.conf.bank_angle_enabled = test_bits(value, 1 << 2);
684 mk->io_handler.conf.steep_approach_enabled = test_bits(value, 1 << 6);
692 MK_VIII::ConfigurationModule::read_radio_altitude_input_select (int value)
694 mk->io_handler.conf.altitude_source = value;
695 return (value >= 0 && value <= 4) || (value >= 251 && value <= 255);
699 MK_VIII::ConfigurationModule::read_navigation_input_select (int value)
701 if (value >= 0 && value <= 2)
702 mk->io_handler.conf.localizer_enabled = false;
703 else if ((value >= 3 && value <= 5) || (value >= 250 && value <= 255))
704 mk->io_handler.conf.localizer_enabled = true;
712 MK_VIII::ConfigurationModule::read_attitude_input_select (int value)
715 mk->io_handler.conf.use_attitude_indicator=true;
717 mk->io_handler.conf.use_attitude_indicator=false;
718 return (value >= 0 && value <= 6) || value == 253 || value == 255;
722 MK_VIII::ConfigurationModule::read_heading_input_select (int value)
725 return (value >= 0 && value <= 3) || value == 254 || value == 255;
729 MK_VIII::ConfigurationModule::read_windshear_input_select (int value)
732 return value == 0 || (value >= 253 && value <= 255);
736 MK_VIII::ConfigurationModule::read_input_output_discrete_type_select (int value)
741 IOHandler::LampConfiguration lamp_conf;
742 bool gpws_inhibit_enabled;
743 bool momentary_flap_override_enabled;
744 bool alternate_steep_approach;
746 { 0, { false, false }, false, true, true },
747 { 1, { true, false }, false, true, true },
748 { 2, { false, false }, true, true, true },
749 { 3, { true, false }, true, true, true },
750 { 4, { false, true }, true, true, true },
751 { 5, { true, true }, true, true, true },
752 { 6, { false, false }, true, true, false },
753 { 7, { true, false }, true, true, false },
754 { 254, { false, false }, true, false, true },
755 { 255, { false, false }, true, false, true }
758 for (size_t i = 0; i < n_elements(io_types); i++)
759 if (io_types[i].type == value)
761 mk->io_handler.conf.lamp = &io_types[i].lamp_conf;
762 mk->io_handler.conf.gpws_inhibit_enabled = io_types[i].gpws_inhibit_enabled;
763 mk->io_handler.conf.momentary_flap_override_enabled = io_types[i].momentary_flap_override_enabled;
764 mk->io_handler.conf.alternate_steep_approach = io_types[i].alternate_steep_approach;
772 MK_VIII::ConfigurationModule::read_audio_output_level (int value)
786 for (size_t i = 0; i < n_elements(values); i++)
787 if (values[i].id == value)
789 mk->voice_player.set_volume(mk->voice_player.conf.volume = modify_amplitude(1.0, values[i].relative_dB));
793 // The self test needs the voice player even when the configuration
794 // is invalid, so set a default value.
795 mk->voice_player.set_volume(mk->voice_player.conf.volume = 1.0);
800 MK_VIII::ConfigurationModule::read_undefined_input_select (int value)
807 MK_VIII::ConfigurationModule::boot ()
809 bool (MK_VIII::ConfigurationModule::*readers[N_CATEGORIES]) (int) = {
810 &MK_VIII::ConfigurationModule::read_aircraft_mode_type_select,
811 &MK_VIII::ConfigurationModule::read_air_data_input_select,
812 &MK_VIII::ConfigurationModule::read_position_input_select,
813 &MK_VIII::ConfigurationModule::read_altitude_callouts,
814 &MK_VIII::ConfigurationModule::read_audio_menu_select,
815 &MK_VIII::ConfigurationModule::read_terrain_display_select,
816 &MK_VIII::ConfigurationModule::read_options_select_group_1,
817 &MK_VIII::ConfigurationModule::read_radio_altitude_input_select,
818 &MK_VIII::ConfigurationModule::read_navigation_input_select,
819 &MK_VIII::ConfigurationModule::read_attitude_input_select,
820 &MK_VIII::ConfigurationModule::read_heading_input_select,
821 &MK_VIII::ConfigurationModule::read_windshear_input_select,
822 &MK_VIII::ConfigurationModule::read_input_output_discrete_type_select,
823 &MK_VIII::ConfigurationModule::read_audio_output_level,
824 &MK_VIII::ConfigurationModule::read_undefined_input_select,
825 &MK_VIII::ConfigurationModule::read_undefined_input_select,
826 &MK_VIII::ConfigurationModule::read_undefined_input_select
829 memcpy(effective_categories, categories, sizeof(categories));
832 for (int i = 0; i < N_CATEGORIES; i++)
833 if (! (this->*readers[i])(effective_categories[i]))
835 SG_LOG(SG_INSTR, SG_ALERT, "MK VIII EGPWS configuration category " << i + 1 << ": invalid value " << effective_categories[i]);
837 if (state == STATE_OK)
838 state = STATE_INVALID_DATABASE;
840 mk_doutput(gpws_inop) = true;
841 mk_doutput(tad_inop) = true;
848 if (mk->mode6_handler.conf.above_field_voice && ! mk->tcf_handler.conf.enabled)
851 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");
852 state = STATE_INVALID_DATABASE;
857 MK_VIII::ConfigurationModule::bind (SGPropertyNode *node)
859 for (int i = 0; i < N_CATEGORIES; i++)
861 std::ostringstream name;
862 name << "configuration-module/category-" << i + 1;
863 mk->properties_handler.tie(node, name.str().c_str(), SGRawValuePointer<int>(&categories[i]));
867 ///////////////////////////////////////////////////////////////////////////////
868 // FaultHandler ///////////////////////////////////////////////////////////////
869 ///////////////////////////////////////////////////////////////////////////////
871 // [INSTALL] only specifies that the faults cause a GPWS INOP
872 // indication. We arbitrarily set a TAD INOP when it makes sense.
873 const unsigned int MK_VIII::FaultHandler::fault_inops[] = {
874 INOP_GPWS | INOP_TAD, // [INSTALL] 3.15.1.3
875 INOP_GPWS, // [INSTALL] appendix E 6.6.2
876 INOP_GPWS, // [INSTALL] appendix E 6.6.4
877 INOP_GPWS, // [INSTALL] appendix E 6.6.6
878 INOP_GPWS | INOP_TAD, // [INSTALL] appendix E 6.6.7
879 INOP_GPWS, // [INSTALL] appendix E 6.6.13
880 INOP_GPWS, // [INSTALL] appendix E 6.6.25
881 INOP_GPWS, // [INSTALL] appendix E 6.6.27
882 INOP_TAD, // unspecified
883 INOP_GPWS, // unspecified
884 INOP_GPWS, // unspecified
885 // [INSTALL] 2.3.10.1 specifies that GPWS INOP is "activated during
886 // any detected partial or total failure of the GPWS modes 1-5", so
887 // do not set any INOP for mode 6 and bank angle.
890 INOP_TAD // unspecified
894 MK_VIII::FaultHandler::has_faults () const
896 for (int i = 0; i < N_FAULTS; i++)
904 MK_VIII::FaultHandler::has_faults (unsigned int inop)
906 for (int i = 0; i < N_FAULTS; i++)
907 if (faults[i] && test_bits(fault_inops[i], inop))
914 MK_VIII::FaultHandler::boot ()
916 memset(faults, 0, sizeof(faults));
920 MK_VIII::FaultHandler::set_fault (Fault fault)
924 faults[fault] = true;
926 mk->self_test_handler.set_inop();
928 if (test_bits(fault_inops[fault], INOP_GPWS))
930 mk_unset_alerts(~mk_alert(TCF_TOO_LOW_TERRAIN));
931 mk_doutput(gpws_inop) = true;
933 if (test_bits(fault_inops[fault], INOP_TAD))
935 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
936 mk_doutput(tad_inop) = true;
942 MK_VIII::FaultHandler::unset_fault (Fault fault)
946 faults[fault] = false;
947 if (! has_faults(INOP_GPWS))
948 mk_doutput(gpws_inop) = false;
949 if (! has_faults(INOP_TAD))
950 mk_doutput(tad_inop) = false;
954 ///////////////////////////////////////////////////////////////////////////////
955 // IOHandler //////////////////////////////////////////////////////////////////
956 ///////////////////////////////////////////////////////////////////////////////
959 MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
961 // [PILOT] page 20 specifies that the terrain clearance is equal to
962 // 75% of the radio altitude, averaged over the previous 15 seconds.
964 // no updates when simulation is paused (dt=0.0), and add 5 samples/second only
965 if (globals->get_sim_time_sec() - last_update < 0.2)
967 last_update = globals->get_sim_time_sec();
969 samples_type::iterator iter;
971 // remove samples older than 15 seconds
972 for (iter = samples.begin(); iter != samples.end() && globals->get_sim_time_sec() - (*iter).timestamp >= 15; iter = samples.begin())
976 samples.push_back(Sample<double>(agl));
979 double new_value = 0;
980 if (samples.size() > 0)
982 // time consuming loop => queue limited to 75 samples
983 // (= 15seconds * 5samples/second)
984 for (iter = samples.begin(); iter != samples.end(); iter++)
985 new_value += (*iter).value;
986 new_value /= samples.size();
990 if (new_value > value)
997 MK_VIII::IOHandler::TerrainClearanceFilter::reset ()
1004 MK_VIII::IOHandler::IOHandler (MK_VIII *device)
1005 : mk(device), _lamp(LAMP_NONE)
1007 memset(&input_feeders, 0, sizeof(input_feeders));
1008 memset(&inputs.discretes, 0, sizeof(inputs.discretes));
1009 memset(&outputs, 0, sizeof(outputs));
1010 memset(&power_saved, 0, sizeof(power_saved));
1012 mk_dinput_feed(landing_gear) = true;
1013 mk_dinput_feed(landing_flaps) = true;
1014 mk_dinput_feed(glideslope_inhibit) = true;
1015 mk_dinput_feed(decision_height) = true;
1016 mk_dinput_feed(autopilot_engaged) = true;
1017 mk_ainput_feed(uncorrected_barometric_altitude) = true;
1018 mk_ainput_feed(barometric_altitude_rate) = true;
1019 mk_ainput_feed(radio_altitude) = true;
1020 mk_ainput_feed(glideslope_deviation) = true;
1021 mk_ainput_feed(roll_angle) = true;
1022 mk_ainput_feed(localizer_deviation) = true;
1023 mk_ainput_feed(computed_airspeed) = true;
1025 // will be unset on power on
1026 mk_doutput(gpws_inop) = true;
1027 mk_doutput(tad_inop) = true;
1031 MK_VIII::IOHandler::boot ()
1033 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1036 mk_doutput(gpws_inop) = false;
1037 mk_doutput(tad_inop) = false;
1039 mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
1041 altitude_samples.clear();
1042 reset_terrain_clearance();
1046 MK_VIII::IOHandler::post_boot ()
1048 if (momentary_steep_approach_enabled())
1050 last_landing_gear = mk_dinput(landing_gear);
1051 last_real_flaps_down = real_flaps_down();
1054 // read externally-latching input discretes
1055 update_alternate_discrete_input(&mk_dinput(mode6_low_volume));
1056 update_alternate_discrete_input(&mk_dinput(audio_inhibit));
1060 MK_VIII::IOHandler::power_off ()
1062 power_saved.glideslope_cancel = mk_doutput(glideslope_cancel); // [SPEC] 6.2.5
1064 memset(&outputs, 0, sizeof(outputs));
1066 audio_inhibit_fault_timer.stop();
1067 landing_gear_fault_timer.stop();
1068 flaps_down_fault_timer.stop();
1069 momentary_flap_override_fault_timer.stop();
1070 self_test_fault_timer.stop();
1071 glideslope_cancel_fault_timer.stop();
1072 steep_approach_fault_timer.stop();
1073 gpws_inhibit_fault_timer.stop();
1074 ta_tcf_inhibit_fault_timer.stop();
1077 mk_doutput(gpws_inop) = true;
1078 mk_doutput(tad_inop) = true;
1082 MK_VIII::IOHandler::enter_ground ()
1084 reset_terrain_clearance();
1086 if (conf.momentary_flap_override_enabled)
1087 mk_doutput(flap_override) = false; // [INSTALL] 3.15.1.6
1091 MK_VIII::IOHandler::enter_takeoff ()
1093 reset_terrain_clearance();
1095 if (momentary_steep_approach_enabled())
1096 // landing or go-around, disable steep approach as per [SPEC] 6.2.1
1097 mk_doutput(steep_approach) = false;
1101 MK_VIII::IOHandler::update_inputs ()
1103 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1106 // 1. process input feeders
1108 if (mk_dinput_feed(landing_gear))
1109 mk_dinput(landing_gear) = mk_node(gear_down)->getBoolValue();
1110 if (mk_dinput_feed(landing_flaps))
1111 mk_dinput(landing_flaps) = mk_node(flaps)->getDoubleValue() < 1;
1112 if (mk_dinput_feed(glideslope_inhibit))
1113 mk_dinput(glideslope_inhibit) = mk_node(nav0_gs_distance)->getDoubleValue() < 0;
1114 if (mk_dinput_feed(autopilot_engaged))
1118 mode = mk_node(autopilot_heading_lock)->getStringValue();
1119 mk_dinput(autopilot_engaged) = mode && *mode;
1122 if (mk_ainput_feed(uncorrected_barometric_altitude))
1124 if (mk_node(altimeter_serviceable)->getBoolValue())
1125 mk_ainput(uncorrected_barometric_altitude).set(mk_node(altimeter_altitude)->getDoubleValue());
1127 mk_ainput(uncorrected_barometric_altitude).unset();
1129 if (mk_ainput_feed(barometric_altitude_rate))
1130 // Do not use the vsi, because it is pressure-based only, and
1131 // therefore too laggy for GPWS alerting purposes. I guess that
1132 // a real ADC combines pressure-based and inerta-based altitude
1133 // rates to provide a non-laggy rate. That non-laggy rate is
1134 // best emulated by /velocities/vertical-speed-fps * 60.
1135 mk_ainput(barometric_altitude_rate).set(mk_node(vs)->getDoubleValue() * 60);
1136 if (mk_ainput_feed(radio_altitude))
1139 switch (conf.altitude_source)
1142 agl = mk_node(altitude_gear_agl)->getDoubleValue();
1145 agl = mk_node(altitude_radar_agl)->getDoubleValue();
1147 default: // 0,1,2 (and any currently unsupported values)
1148 agl = mk_node(altitude_agl)->getDoubleValue();
1151 // Some flight models may return negative values when on the
1152 // ground or after a crash; do not allow them.
1153 mk_ainput(radio_altitude).set(SG_MAX2(0.0, agl));
1155 if (mk_ainput_feed(glideslope_deviation))
1157 if (mk_node(nav0_serviceable)->getBoolValue()
1158 && mk_node(nav0_gs_serviceable)->getBoolValue()
1159 && mk_node(nav0_in_range)->getBoolValue()
1160 && mk_node(nav0_has_gs)->getBoolValue())
1161 // gs-needle-deflection is expressed in degrees, and 1 dot =
1162 // 0.32 degrees (according to
1163 // http://www.ntsb.gov/Recs/letters/2000/A00_41_45.pdf)
1164 mk_ainput(glideslope_deviation).set(mk_node(nav0_gs_needle_deflection)->getDoubleValue() / 0.32 * GLIDESLOPE_DOTS_TO_DDM);
1166 mk_ainput(glideslope_deviation).unset();
1168 if (mk_ainput_feed(roll_angle))
1170 if (conf.use_attitude_indicator)
1172 // read data from attitude indicator instrument (requires vacuum system to work)
1173 if (mk_node(ai_serviceable)->getBoolValue() && ! mk_node(ai_caged)->getBoolValue())
1174 mk_ainput(roll_angle).set(mk_node(ai_roll)->getDoubleValue());
1176 mk_ainput(roll_angle).unset();
1180 // use simulator source
1181 mk_ainput(roll_angle).set(mk_node(orientation_roll)->getDoubleValue());
1184 if (mk_ainput_feed(localizer_deviation))
1186 if (mk_node(nav0_serviceable)->getBoolValue()
1187 && mk_node(nav0_cdi_serviceable)->getBoolValue()
1188 && mk_node(nav0_in_range)->getBoolValue()
1189 && mk_node(nav0_nav_loc)->getBoolValue())
1190 // heading-needle-deflection is expressed in degrees, and 1
1191 // dot = 2 degrees (0.5 degrees for ILS, but navradio.cxx
1192 // performs the conversion)
1193 mk_ainput(localizer_deviation).set(mk_node(nav0_heading_needle_deflection)->getDoubleValue() / 2 * LOCALIZER_DOTS_TO_DDM);
1195 mk_ainput(localizer_deviation).unset();
1197 if (mk_ainput_feed(computed_airspeed))
1199 if (mk_node(asi_serviceable)->getBoolValue())
1200 mk_ainput(computed_airspeed).set(mk_node(asi_speed)->getDoubleValue());
1202 mk_ainput(computed_airspeed).unset();
1207 mk_data(decision_height).set(&mk_ainput(decision_height));
1208 mk_data(radio_altitude).set(&mk_ainput(radio_altitude));
1209 mk_data(roll_angle).set(&mk_ainput(roll_angle));
1211 // update barometric_altitude_rate as per [SPEC] 6.2.1: "During
1212 // normal conditions, the system will base Mode 1 computations upon
1213 // barometric rate from the ADC. If this computed data is not valid
1214 // or available then the system will use internally computed
1215 // barometric altitude rate."
1217 if (! mk_ainput(barometric_altitude_rate).ncd)
1218 // the altitude rate input is valid, use it
1219 mk_data(barometric_altitude_rate).set(mk_ainput(barometric_altitude_rate).get());
1224 // The altitude rate input is invalid. We can compute an
1225 // altitude rate if all the following conditions are true:
1227 // - the altitude input is valid
1228 // - there is an altitude sample with age >= 1 second
1229 // - that altitude sample is valid
1231 if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1233 if (! altitude_samples.empty() && ! altitude_samples.front().value.ncd)
1235 double elapsed = globals->get_sim_time_sec() - altitude_samples.front().timestamp;
1239 mk_data(barometric_altitude_rate).set((mk_ainput(uncorrected_barometric_altitude).get() - altitude_samples.front().value.get()) / elapsed * 60);
1245 mk_data(barometric_altitude_rate).unset();
1248 altitude_samples.push_back(Sample< Parameter<double> >(mk_ainput(uncorrected_barometric_altitude)));
1250 // Erase everything from the beginning of the list up to the sample
1251 // preceding the most recent sample whose age is >= 1 second.
1253 altitude_samples_type::iterator erase_last = altitude_samples.begin();
1254 altitude_samples_type::iterator iter;
1256 for (iter = altitude_samples.begin(); iter != altitude_samples.end(); iter++)
1257 if (globals->get_sim_time_sec() - (*iter).timestamp >= 1)
1262 if (erase_last != altitude_samples.begin())
1263 altitude_samples.erase(altitude_samples.begin(), erase_last);
1267 if (conf.use_internal_gps)
1269 mk_data(gps_altitude).set(mk_node(altitude)->getDoubleValue());
1270 mk_data(gps_latitude).set(mk_node(latitude)->getDoubleValue());
1271 mk_data(gps_longitude).set(mk_node(longitude)->getDoubleValue());
1272 mk_data(gps_vertical_figure_of_merit).set(0.0);
1276 mk_data(gps_altitude).set(&mk_ainput(gps_altitude));
1277 mk_data(gps_latitude).set(&mk_ainput(gps_latitude));
1278 mk_data(gps_longitude).set(&mk_ainput(gps_longitude));
1279 mk_data(gps_vertical_figure_of_merit).set(&mk_ainput(gps_vertical_figure_of_merit));
1282 // update glideslope and localizer
1284 mk_data(glideslope_deviation_dots).set(&mk_ainput(glideslope_deviation), GLIDESLOPE_DDM_TO_DOTS);
1285 mk_data(localizer_deviation_dots).set(&mk_ainput(localizer_deviation), LOCALIZER_DDM_TO_DOTS);
1287 // Update geometric altitude; [SPEC] 6.7.8 provides an overview of a
1288 // complex algorithm which combines several input sources to
1289 // calculate the geometric altitude. Since the exact algorithm is
1290 // not given, we fallback to a much simpler method.
1292 if (! mk_data(gps_altitude).ncd)
1293 mk_data(geometric_altitude).set(mk_data(gps_altitude).get());
1294 else if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1295 mk_data(geometric_altitude).set(mk_ainput(uncorrected_barometric_altitude).get());
1297 mk_data(geometric_altitude).unset();
1299 // update terrain clearance
1301 update_terrain_clearance();
1303 // 3. perform sanity checks
1305 if (! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 0)
1306 mk_data(radio_altitude).unset();
1308 if (! mk_data(decision_height).ncd && mk_data(decision_height).get() < 0)
1309 mk_data(decision_height).unset();
1311 if (! mk_data(gps_latitude).ncd
1312 && (mk_data(gps_latitude).get() < -90
1313 || mk_data(gps_latitude).get() > 90))
1314 mk_data(gps_latitude).unset();
1316 if (! mk_data(gps_longitude).ncd
1317 && (mk_data(gps_longitude).get() < -180
1318 || mk_data(gps_longitude).get() > 180))
1319 mk_data(gps_longitude).unset();
1321 if (! mk_data(roll_angle).ncd
1322 && ((mk_data(roll_angle).get() < -180)
1323 || (mk_data(roll_angle).get() > 180)))
1324 mk_data(roll_angle).unset();
1326 // 4. process input feeders requiring data computed above
1328 if (mk_dinput_feed(decision_height))
1329 mk_dinput(decision_height) = ! mk_data(radio_altitude).ncd
1330 && ! mk_data(decision_height).ncd
1331 && mk_data(radio_altitude).get() <= mk_data(decision_height).get();
1335 MK_VIII::IOHandler::update_terrain_clearance ()
1337 if (! mk_data(radio_altitude).ncd)
1338 mk_data(terrain_clearance).set(terrain_clearance_filter.update(mk_data(radio_altitude).get()));
1340 mk_data(terrain_clearance).unset();
1344 MK_VIII::IOHandler::reset_terrain_clearance ()
1346 terrain_clearance_filter.reset();
1347 update_terrain_clearance();
1351 MK_VIII::IOHandler::reposition ()
1353 reset_terrain_clearance();
1357 MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
1361 if (! mk->fault_handler.faults[fault])
1362 mk->fault_handler.set_fault(fault);
1365 mk->fault_handler.unset_fault(fault);
1369 MK_VIII::IOHandler::handle_input_fault (bool test,
1371 double max_duration,
1372 FaultHandler::Fault fault)
1376 if (! mk->fault_handler.faults[fault])
1378 if (timer->start_or_elapsed() >= max_duration)
1379 mk->fault_handler.set_fault(fault);
1384 mk->fault_handler.unset_fault(fault);
1390 MK_VIII::IOHandler::update_input_faults ()
1392 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1395 // [INSTALL] 3.15.1.3
1396 handle_input_fault(mk_dinput(audio_inhibit),
1397 &audio_inhibit_fault_timer,
1399 FaultHandler::FAULT_ALL_MODES_INHIBIT);
1401 // [INSTALL] appendix E 6.6.2
1402 handle_input_fault(mk_dinput(landing_gear)
1403 && ! mk_ainput(computed_airspeed).ncd
1404 && mk_ainput(computed_airspeed).get() > conf.faults->max_gear_down_airspeed,
1405 &landing_gear_fault_timer,
1407 FaultHandler::FAULT_GEAR_SWITCH);
1409 // [INSTALL] appendix E 6.6.4
1410 handle_input_fault(flaps_down()
1411 && ! mk_ainput(computed_airspeed).ncd
1412 && mk_ainput(computed_airspeed).get() > conf.faults->max_flaps_down_airspeed,
1413 &flaps_down_fault_timer,
1415 FaultHandler::FAULT_FLAPS_SWITCH);
1417 // [INSTALL] appendix E 6.6.6
1418 if (conf.momentary_flap_override_enabled)
1419 handle_input_fault(mk_dinput(momentary_flap_override),
1420 &momentary_flap_override_fault_timer,
1422 FaultHandler::FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID);
1424 // [INSTALL] appendix E 6.6.7
1425 handle_input_fault(mk_dinput(self_test),
1426 &self_test_fault_timer,
1428 FaultHandler::FAULT_SELF_TEST_INVALID);
1430 // [INSTALL] appendix E 6.6.13
1431 handle_input_fault(mk_dinput(glideslope_cancel),
1432 &glideslope_cancel_fault_timer,
1434 FaultHandler::FAULT_GLIDESLOPE_CANCEL_INVALID);
1436 // [INSTALL] appendix E 6.6.25
1437 if (momentary_steep_approach_enabled())
1438 handle_input_fault(mk_dinput(steep_approach),
1439 &steep_approach_fault_timer,
1441 FaultHandler::FAULT_STEEP_APPROACH_INVALID);
1443 // [INSTALL] appendix E 6.6.27
1444 if (conf.gpws_inhibit_enabled)
1445 handle_input_fault(mk_dinput(gpws_inhibit),
1446 &gpws_inhibit_fault_timer,
1448 FaultHandler::FAULT_GPWS_INHIBIT);
1450 // [INSTALL] does not specify a fault for this one, but it makes
1451 // sense to have it behave like GPWS inhibit
1452 handle_input_fault(mk_dinput(ta_tcf_inhibit),
1453 &ta_tcf_inhibit_fault_timer,
1455 FaultHandler::FAULT_TA_TCF_INHIBIT);
1457 // [PILOT] page 48: "In the event that required data for a
1458 // particular function is not available, then that function is
1459 // automatically inhibited and annunciated"
1461 handle_input_fault(mk_data(radio_altitude).ncd
1462 || mk_ainput(uncorrected_barometric_altitude).ncd
1463 || mk_data(barometric_altitude_rate).ncd
1464 || mk_ainput(computed_airspeed).ncd
1465 || mk_data(terrain_clearance).ncd,
1466 FaultHandler::FAULT_MODES14_INPUTS_INVALID);
1468 if (! mk_dinput(glideslope_inhibit))
1469 handle_input_fault(mk_data(radio_altitude).ncd,
1470 FaultHandler::FAULT_MODE5_INPUTS_INVALID);
1472 if (mk->mode6_handler.altitude_callouts_enabled())
1473 handle_input_fault(mk->mode6_handler.conf.above_field_voice
1474 ? (mk_data(gps_latitude).ncd
1475 || mk_data(gps_longitude).ncd
1476 || mk_data(geometric_altitude).ncd)
1477 : mk_data(radio_altitude).ncd,
1478 FaultHandler::FAULT_MODE6_INPUTS_INVALID);
1480 if (mk->mode6_handler.conf.bank_angle_enabled)
1481 handle_input_fault(mk_data(roll_angle).ncd,
1482 FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID);
1484 if (mk->tcf_handler.conf.enabled)
1485 handle_input_fault(mk_data(radio_altitude).ncd
1486 || mk_data(geometric_altitude).ncd
1487 || mk_data(gps_latitude).ncd
1488 || mk_data(gps_longitude).ncd
1489 || mk_data(gps_vertical_figure_of_merit).ncd,
1490 FaultHandler::FAULT_TCF_INPUTS_INVALID);
1494 MK_VIII::IOHandler::update_alternate_discrete_input (bool *ptr)
1496 assert(mk->system_handler.state == SystemHandler::STATE_ON);
1498 if (ptr == &mk_dinput(mode6_low_volume))
1500 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1501 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1502 mk->mode6_handler.set_volume(*ptr ? modify_amplitude(1.0, -6) : 1.0);
1504 else if (ptr == &mk_dinput(audio_inhibit))
1506 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1507 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1508 mk->voice_player.set_volume(*ptr ? 0.0 : mk->voice_player.conf.volume);
1513 MK_VIII::IOHandler::update_internal_latches ()
1515 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1519 if (conf.momentary_flap_override_enabled
1520 && mk_doutput(flap_override)
1521 && ! mk->state_handler.takeoff
1522 && (mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() <= 50))
1523 mk_doutput(flap_override) = false;
1526 if (momentary_steep_approach_enabled())
1528 if (mk_doutput(steep_approach)
1529 && ! mk->state_handler.takeoff
1530 && ((last_landing_gear && ! mk_dinput(landing_gear))
1531 || (last_real_flaps_down && ! real_flaps_down())))
1532 // gear or flaps raised during approach: it's a go-around
1533 mk_doutput(steep_approach) = false;
1535 last_landing_gear = mk_dinput(landing_gear);
1536 last_real_flaps_down = real_flaps_down();
1540 if (mk_doutput(glideslope_cancel)
1541 && (mk_data(glideslope_deviation_dots).ncd
1542 || mk_data(radio_altitude).ncd
1543 || mk_data(radio_altitude).get() > 2000
1544 || mk_data(radio_altitude).get() < 30))
1545 mk_doutput(glideslope_cancel) = false;
1549 MK_VIII::IOHandler::update_egpws_alert_discrete_1 ()
1551 if (mk->voice_player.voice)
1556 VoicePlayer::Voice *voice;
1558 { 11, mk_voice(sink_rate_pause_sink_rate) },
1559 { 12, mk_voice(pull_up) },
1560 { 13, mk_voice(terrain) },
1561 { 13, mk_voice(terrain_pause_terrain) },
1562 { 14, mk_voice(dont_sink_pause_dont_sink) },
1563 { 15, mk_voice(too_low_gear) },
1564 { 16, mk_voice(too_low_flaps) },
1565 { 17, mk_voice(too_low_terrain) },
1566 { 18, mk_voice(soft_glideslope) },
1567 { 18, mk_voice(hard_glideslope) },
1568 { 19, mk_voice(minimums_minimums) }
1571 for (size_t i = 0; i < n_elements(voices); i++)
1572 if (voices[i].voice == mk->voice_player.voice)
1574 mk_aoutput(egpws_alert_discrete_1) = 1 << voices[i].bit;
1579 mk_aoutput(egpws_alert_discrete_1) = 0;
1583 MK_VIII::IOHandler::update_egpwc_logic_discretes ()
1585 mk_aoutput(egpwc_logic_discretes) = 0;
1587 if (mk->state_handler.takeoff)
1588 mk_aoutput(egpwc_logic_discretes) |= 1 << 18;
1593 unsigned int alerts;
1595 { 13, mk_alert(TCF_TOO_LOW_TERRAIN) },
1596 { 19, mk_alert(MODE1_SINK_RATE) },
1597 { 20, mk_alert(MODE1_PULL_UP) },
1598 { 21, mk_alert(MODE2A_PREFACE) | mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B_LANDING_MODE) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING) },
1599 { 22, mk_alert(MODE2A) | mk_alert(MODE2B) },
1600 { 23, mk_alert(MODE3) },
1601 { 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) },
1602 { 25, mk_alert(MODE5_SOFT) | mk_alert(MODE5_HARD) }
1605 for (size_t i = 0; i < n_elements(logic); i++)
1606 if (mk_test_alerts(logic[i].alerts))
1607 mk_aoutput(egpwc_logic_discretes) |= 1 << logic[i].bit;
1611 MK_VIII::IOHandler::update_mode6_callouts_discrete_1 ()
1613 if (mk->voice_player.voice)
1618 VoicePlayer::Voice *voice;
1620 { 11, mk_voice(minimums_minimums) },
1621 { 16, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_10) },
1622 { 17, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_20) },
1623 { 18, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_30) },
1624 { 19, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_40) },
1625 { 20, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_50) },
1626 { 23, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_100) },
1627 { 24, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_200) },
1628 { 25, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_300) }
1631 for (size_t i = 0; i < n_elements(voices); i++)
1632 if (voices[i].voice == mk->voice_player.voice)
1634 mk_aoutput(mode6_callouts_discrete_1) = 1 << voices[i].bit;
1639 mk_aoutput(mode6_callouts_discrete_1) = 0;
1643 MK_VIII::IOHandler::update_mode6_callouts_discrete_2 ()
1645 if (mk->voice_player.voice)
1650 VoicePlayer::Voice *voice;
1652 { 11, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_400) },
1653 { 12, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500) },
1654 { 13, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_1000) },
1655 { 18, mk_voice(bank_angle_pause_bank_angle) },
1656 { 18, mk_voice(bank_angle_pause_bank_angle_3) },
1657 { 23, mk_voice(five_hundred_above) }
1660 for (size_t i = 0; i < n_elements(voices); i++)
1661 if (voices[i].voice == mk->voice_player.voice)
1663 mk_aoutput(mode6_callouts_discrete_2) = 1 << voices[i].bit;
1668 mk_aoutput(mode6_callouts_discrete_2) = 0;
1672 MK_VIII::IOHandler::update_egpws_alert_discrete_2 ()
1674 mk_aoutput(egpws_alert_discrete_2) = 1 << 27; // Terrain NA
1676 if (mk_doutput(glideslope_cancel))
1677 mk_aoutput(egpws_alert_discrete_2) |= 1 << 11;
1678 if (mk_doutput(gpws_alert))
1679 mk_aoutput(egpws_alert_discrete_2) |= 1 << 12;
1680 if (mk_doutput(gpws_warning))
1681 mk_aoutput(egpws_alert_discrete_2) |= 1 << 13;
1682 if (mk_doutput(gpws_inop))
1683 mk_aoutput(egpws_alert_discrete_2) |= 1 << 14;
1684 if (mk_doutput(audio_on))
1685 mk_aoutput(egpws_alert_discrete_2) |= 1 << 16;
1686 if (mk_doutput(tad_inop))
1687 mk_aoutput(egpws_alert_discrete_2) |= 1 << 24;
1688 if (mk->fault_handler.has_faults())
1689 mk_aoutput(egpws_alert_discrete_2) |= 1 << 25;
1693 MK_VIII::IOHandler::update_egpwc_alert_discrete_3 ()
1695 mk_aoutput(egpwc_alert_discrete_3) = 1 << 17 | 1 << 18;
1697 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
1698 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 11;
1699 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
1700 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 12;
1701 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
1702 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 13;
1703 if (mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
1704 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 14;
1705 if (mk_doutput(tad_inop))
1706 mk_aoutput(egpwc_alert_discrete_3) |= 1 << 16;
1710 MK_VIII::IOHandler::update_outputs ()
1712 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1715 mk_doutput(audio_on) = ! mk_dinput(audio_inhibit)
1716 && mk->voice_player.voice
1717 && ! mk->voice_player.voice->element->silence;
1719 update_egpws_alert_discrete_1();
1720 update_egpwc_logic_discretes();
1721 update_mode6_callouts_discrete_1();
1722 update_mode6_callouts_discrete_2();
1723 update_egpws_alert_discrete_2();
1724 update_egpwc_alert_discrete_3();
1728 MK_VIII::IOHandler::get_lamp_output (Lamp lamp)
1732 case LAMP_GLIDESLOPE:
1733 return &mk_doutput(gpws_alert);
1736 return conf.lamp->format2 ? &mk_doutput(gpws_alert) : &mk_doutput(gpws_warning);
1739 return &mk_doutput(gpws_warning);
1742 assert_not_reached();
1748 MK_VIII::IOHandler::update_lamps ()
1750 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1753 if (_lamp != LAMP_NONE && conf.lamp->flashing)
1755 // [SPEC] 6.9.3: 70 cycles per minute
1756 if (lamp_timer.elapsed() >= 60.0 / 70.0 / 2.0)
1758 bool *output = get_lamp_output(_lamp);
1759 *output = ! *output;
1766 MK_VIII::IOHandler::set_lamp (Lamp lamp)
1773 mk_doutput(gpws_warning) = false;
1774 mk_doutput(gpws_alert) = false;
1776 if (lamp != LAMP_NONE)
1778 *get_lamp_output(lamp) = true;
1784 MK_VIII::IOHandler::gpws_inhibit () const
1786 return conf.gpws_inhibit_enabled && mk_dinput(gpws_inhibit);
1790 MK_VIII::IOHandler::real_flaps_down () const
1792 return conf.flap_reversal ? mk_dinput(landing_flaps) : ! mk_dinput(landing_flaps);
1796 MK_VIII::IOHandler::flaps_down () const
1798 return flap_override() ? true : real_flaps_down();
1802 MK_VIII::IOHandler::flap_override () const
1804 return conf.momentary_flap_override_enabled ? mk_doutput(flap_override) : false;
1808 MK_VIII::IOHandler::steep_approach () const
1810 if (conf.steep_approach_enabled)
1811 // If alternate action was configured in category 13, we return
1812 // the value of the input discrete (which should be connected to a
1813 // latching steep approach cockpit switch). Otherwise, we return
1814 // the value of the output discrete.
1815 return conf.alternate_steep_approach ? mk_dinput(steep_approach) : mk_doutput(steep_approach);
1821 MK_VIII::IOHandler::momentary_steep_approach_enabled () const
1823 return conf.steep_approach_enabled && ! conf.alternate_steep_approach;
1827 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1832 mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(),
1833 FGVoicePlayer::RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
1835 mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
1839 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1841 Parameter<double> *input,
1844 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name).c_str(), SGRawValuePointer<double>(input->get_pointer()));
1845 mk->properties_handler.tie(node, (string("inputs/arinc429/") + name + "-ncd").c_str(), SGRawValuePointer<bool>(&input->ncd));
1847 mk->properties_handler.tie(node, (string("input-feeders/arinc429/") + name).c_str(), SGRawValuePointer<bool>(feed));
1851 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1855 SGPropertyNode *child = node->getNode((string("outputs/discretes/") + name).c_str(), true);
1857 mk->properties_handler.tie(child, SGRawValuePointer<bool>(output));
1858 child->setAttribute(SGPropertyNode::WRITE, false);
1862 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1866 SGPropertyNode *child = node->getNode((string("outputs/arinc429/") + name).c_str(), true);
1868 mk->properties_handler.tie(child, SGRawValuePointer<int>(output));
1869 child->setAttribute(SGPropertyNode::WRITE, false);
1873 MK_VIII::IOHandler::bind (SGPropertyNode *node)
1875 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));
1877 tie_input(node, "landing-gear", &mk_dinput(landing_gear), &mk_dinput_feed(landing_gear));
1878 tie_input(node, "landing-flaps", &mk_dinput(landing_flaps), &mk_dinput_feed(landing_flaps));
1879 tie_input(node, "momentary-flap-override", &mk_dinput(momentary_flap_override));
1880 tie_input(node, "self-test", &mk_dinput(self_test));
1881 tie_input(node, "glideslope-inhibit", &mk_dinput(glideslope_inhibit), &mk_dinput_feed(glideslope_inhibit));
1882 tie_input(node, "glideslope-cancel", &mk_dinput(glideslope_cancel));
1883 tie_input(node, "decision-height", &mk_dinput(decision_height), &mk_dinput_feed(decision_height));
1884 tie_input(node, "mode6-low-volume", &mk_dinput(mode6_low_volume));
1885 tie_input(node, "audio-inhibit", &mk_dinput(audio_inhibit));
1886 tie_input(node, "ta-tcf-inhibit", &mk_dinput(ta_tcf_inhibit));
1887 tie_input(node, "autopilot-engaged", &mk_dinput(autopilot_engaged), &mk_dinput_feed(autopilot_engaged));
1888 tie_input(node, "steep-approach", &mk_dinput(steep_approach));
1889 tie_input(node, "gpws-inhibit", &mk_dinput(gpws_inhibit));
1891 tie_input(node, "uncorrected-barometric-altitude", &mk_ainput(uncorrected_barometric_altitude), &mk_ainput_feed(uncorrected_barometric_altitude));
1892 tie_input(node, "barometric-altitude-rate", &mk_ainput(barometric_altitude_rate), &mk_ainput_feed(barometric_altitude_rate));
1893 tie_input(node, "gps-altitude", &mk_ainput(gps_altitude));
1894 tie_input(node, "gps-latitude", &mk_ainput(gps_latitude));
1895 tie_input(node, "gps-longitude", &mk_ainput(gps_longitude));
1896 tie_input(node, "gps-vertical-figure-of-merit", &mk_ainput(gps_vertical_figure_of_merit));
1897 tie_input(node, "radio-altitude", &mk_ainput(radio_altitude), &mk_ainput_feed(radio_altitude));
1898 tie_input(node, "glideslope-deviation", &mk_ainput(glideslope_deviation), &mk_ainput_feed(glideslope_deviation));
1899 tie_input(node, "roll-angle", &mk_ainput(roll_angle), &mk_ainput_feed(roll_angle));
1900 tie_input(node, "localizer-deviation", &mk_ainput(localizer_deviation), &mk_ainput_feed(localizer_deviation));
1901 tie_input(node, "computed-airspeed", &mk_ainput(computed_airspeed), &mk_ainput_feed(computed_airspeed));
1902 tie_input(node, "decision-height", &mk_ainput(decision_height), &mk_ainput_feed(decision_height));
1904 tie_output(node, "gpws-warning", &mk_doutput(gpws_warning));
1905 tie_output(node, "gpws-alert", &mk_doutput(gpws_alert));
1906 tie_output(node, "audio-on", &mk_doutput(audio_on));
1907 tie_output(node, "gpws-inop", &mk_doutput(gpws_inop));
1908 tie_output(node, "tad-inop", &mk_doutput(tad_inop));
1909 tie_output(node, "flap-override", &mk_doutput(flap_override));
1910 tie_output(node, "glideslope-cancel", &mk_doutput(glideslope_cancel));
1911 tie_output(node, "steep-approach", &mk_doutput(steep_approach));
1913 tie_output(node, "egpws-alert-discrete-1", &mk_aoutput(egpws_alert_discrete_1));
1914 tie_output(node, "egpwc-logic-discretes", &mk_aoutput(egpwc_logic_discretes));
1915 tie_output(node, "mode6-callouts-discrete-1", &mk_aoutput(mode6_callouts_discrete_1));
1916 tie_output(node, "mode6-callouts-discrete-2", &mk_aoutput(mode6_callouts_discrete_2));
1917 tie_output(node, "egpws-alert-discrete-2", &mk_aoutput(egpws_alert_discrete_2));
1918 tie_output(node, "egpwc-alert-discrete-3", &mk_aoutput(egpwc_alert_discrete_3));
1922 MK_VIII::IOHandler::get_discrete_input (bool *ptr) const
1928 MK_VIII::IOHandler::set_discrete_input (bool *ptr, bool value)
1933 if (mk->system_handler.state == SystemHandler::STATE_ON)
1935 if (ptr == &mk_dinput(momentary_flap_override))
1937 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1938 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1939 && conf.momentary_flap_override_enabled
1941 mk_doutput(flap_override) = ! mk_doutput(flap_override);
1943 else if (ptr == &mk_dinput(self_test))
1944 mk->self_test_handler.handle_button_event(value);
1945 else if (ptr == &mk_dinput(glideslope_cancel))
1947 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1948 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1951 if (! mk_doutput(glideslope_cancel))
1955 // Although we are not called from update(), we are
1956 // sure that the inputs we use here are defined,
1957 // since state is STATE_ON.
1959 if (! mk_data(glideslope_deviation_dots).ncd
1960 && ! mk_data(radio_altitude).ncd
1961 && mk_data(radio_altitude).get() <= 2000
1962 && mk_data(radio_altitude).get() >= 30)
1963 mk_doutput(glideslope_cancel) = true;
1965 // else do nothing ([PILOT] page 22: "Glideslope Cancel
1966 // can not be deselected (reset) by again pressing the
1967 // Glideslope Cancel switch.")
1970 else if (ptr == &mk_dinput(steep_approach))
1972 if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1973 && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1974 && momentary_steep_approach_enabled()
1976 mk_doutput(steep_approach) = ! mk_doutput(steep_approach);
1982 if (mk->system_handler.state == SystemHandler::STATE_ON)
1983 update_alternate_discrete_input(ptr);
1987 MK_VIII::IOHandler::present_status_section (const char *name)
1989 printf("%s\n", name);
1993 MK_VIII::IOHandler::present_status_item (const char *name, const char *value)
1996 printf("\t%-32s %s\n", name, value);
1998 printf("\t%s\n", name);
2002 MK_VIII::IOHandler::present_status_subitem (const char *name)
2004 printf("\t\t%s\n", name);
2008 MK_VIII::IOHandler::present_status ()
2012 if (mk->system_handler.state != SystemHandler::STATE_ON)
2015 present_status_section("EGPWC CONFIGURATION");
2016 present_status_item("PART NUMBER:", "965-1220-000"); // [SPEC] 1.1
2017 present_status_item("MOD STATUS:", "N/A");
2018 present_status_item("SERIAL NUMBER:", "N/A");
2020 present_status_item("APPLICATION S/W VERSION:", FLIGHTGEAR_VERSION);
2021 present_status_item("TERRAIN DATABASE VERSION:", FLIGHTGEAR_VERSION);
2022 present_status_item("ENVELOPE MOD DATABASE VERSION:", "N/A");
2023 present_status_item("BOOT CODE VERSION:", FLIGHTGEAR_VERSION);
2026 present_status_section("CURRENT FAULTS");
2027 if (mk->configuration_module.state == ConfigurationModule::STATE_OK && ! mk->fault_handler.has_faults())
2028 present_status_item("NO FAULTS");
2031 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2033 present_status_item("GPWS COMPUTER FAULTS:");
2034 switch (mk->configuration_module.state)
2036 case ConfigurationModule::STATE_INVALID_DATABASE:
2037 present_status_subitem("APPLICATION DATABASE FAILED");
2040 case ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE:
2041 present_status_subitem("CONFIGURATION TYPE INVALID");
2045 assert_not_reached();
2051 present_status_item("GPWS COMPUTER OK");
2052 present_status_item("GPWS EXTERNAL FAULTS:");
2054 static const char *fault_names[] = {
2055 "ALL MODES INHIBIT",
2058 "MOMENTARY FLAP OVERRIDE INVALID",
2059 "SELF TEST INVALID",
2060 "GLIDESLOPE CANCEL INVALID",
2061 "STEEP APPROACH INVALID",
2064 "MODES 1-4 INPUTS INVALID",
2065 "MODE 5 INPUTS INVALID",
2066 "MODE 6 INPUTS INVALID",
2067 "BANK ANGLE INPUTS INVALID",
2068 "TCF INPUTS INVALID"
2071 for (size_t i = 0; i < n_elements(fault_names); i++)
2072 if (mk->fault_handler.faults[i])
2073 present_status_subitem(fault_names[i]);
2078 present_status_section("CONFIGURATION:");
2080 static const char *category_names[] = {
2083 "POSITION INPUT TYPE",
2084 "CALLOUTS OPTION TYPE",
2086 "TERRAIN DISPLAY TYPE",
2088 "RADIO ALTITUDE TYPE",
2089 "NAVIGATION INPUT TYPE",
2091 "MAGNETIC HEADING TYPE",
2092 "WINDSHEAR INPUT TYPE",
2097 for (size_t i = 0; i < n_elements(category_names); i++)
2099 std::ostringstream value;
2100 value << "= " << mk->configuration_module.effective_categories[i];
2101 present_status_item(category_names[i], value.str().c_str());
2106 MK_VIII::IOHandler::get_present_status () const
2112 MK_VIII::IOHandler::set_present_status (bool value)
2114 if (value) present_status();
2118 ///////////////////////////////////////////////////////////////////////////////
2119 // MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
2120 ///////////////////////////////////////////////////////////////////////////////
2123 MK_VIII::VoicePlayer::init ()
2125 FGVoicePlayer::init();
2127 #define STDPAUSE 0.75 // [SPEC] 6.4.4: "the standard 0.75 second delay"
2128 make_voice(&voices.application_data_base_failed, "application-data-base-failed");
2129 make_voice(&voices.bank_angle, "bank-angle");
2130 make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
2131 make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
2132 make_voice(&voices.bank_angle_inop, "bank-angle-inop");
2133 make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
2134 make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
2135 make_voice(&voices.callouts_inop, "callouts-inop");
2136 make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
2137 make_voice(&voices.dont_sink, "dont-sink");
2138 make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
2139 make_voice(&voices.five_hundred_above, "500-above");
2140 make_voice(&voices.glideslope, "glideslope");
2141 make_voice(&voices.glideslope_inop, "glideslope-inop");
2142 make_voice(&voices.gpws_inop, "gpws-inop");
2143 make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
2144 make_voice(&voices.minimums, "minimums");
2145 make_voice(&voices.minimums_minimums, "minimums", "minimums");
2146 make_voice(&voices.pull_up, "pull-up");
2147 make_voice(&voices.sink_rate, "sink-rate");
2148 make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
2149 make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
2150 make_voice(&voices.terrain, "terrain");
2151 make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
2152 make_voice(&voices.too_low_flaps, "too-low-flaps");
2153 make_voice(&voices.too_low_gear, "too-low-gear");
2154 make_voice(&voices.too_low_terrain, "too-low-terrain");
2156 for (unsigned i = 0; i < n_altitude_callouts; i++)
2158 std::ostringstream name;
2159 name << "altitude-" << MK_VIII::Mode6Handler::altitude_callout_definitions[i];
2160 make_voice(&voices.altitude_callouts[i], name.str().c_str());
2162 speaker.update_configuration();
2165 ///////////////////////////////////////////////////////////////////////////////
2166 // SelfTestHandler ////////////////////////////////////////////////////////////
2167 ///////////////////////////////////////////////////////////////////////////////
2170 MK_VIII::SelfTestHandler::_was_here (int position)
2172 if (position > current)
2181 MK_VIII::SelfTestHandler::Action
2182 MK_VIII::SelfTestHandler::sleep (double duration)
2184 Action _action = { ACTION_SLEEP, duration, NULL };
2188 MK_VIII::SelfTestHandler::Action
2189 MK_VIII::SelfTestHandler::play (VoicePlayer::Voice *voice)
2191 mk->voice_player.play(voice);
2192 Action _action = { ACTION_VOICE, 0, NULL };
2196 MK_VIII::SelfTestHandler::Action
2197 MK_VIII::SelfTestHandler::discrete_on (bool *discrete, double duration)
2200 return sleep(duration);
2203 MK_VIII::SelfTestHandler::Action
2204 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, double duration)
2207 Action _action = { ACTION_SLEEP | ACTION_DISCRETE_ON_OFF, duration, discrete };
2211 MK_VIII::SelfTestHandler::Action
2212 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, VoicePlayer::Voice *voice)
2215 mk->voice_player.play(voice);
2216 Action _action = { ACTION_VOICE | ACTION_DISCRETE_ON_OFF, 0, discrete };
2220 MK_VIII::SelfTestHandler::Action
2221 MK_VIII::SelfTestHandler::done ()
2223 Action _action = { ACTION_DONE, 0, NULL };
2227 MK_VIII::SelfTestHandler::Action
2228 MK_VIII::SelfTestHandler::run ()
2230 // Note that "Terrain INOP" and "Terrain NA" are or'ed to the same
2231 // output discrete (see [SPEC] 6.9.3.5).
2233 #define was_here() was_here_offset(0)
2234 #define was_here_offset(offset) _was_here(__LINE__ * 20 + (offset))
2238 if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_DATABASE)
2239 return play(mk_voice(application_data_base_failed));
2240 else if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE)
2241 return play(mk_voice(configuration_type_invalid));
2243 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2247 return discrete_on(&mk_doutput(gpws_inop), 0.7);
2249 return sleep(0.7); // W/S INOP
2251 return discrete_on(&mk_doutput(tad_inop), 0.7);
2254 mk_doutput(gpws_inop) = false;
2255 // do not disable tad_inop since we must enable Terrain NA
2256 // do not disable W/S INOP since we do not emulate it
2257 return sleep(0.7); // Terrain NA
2261 mk_doutput(tad_inop) = false; // disable Terrain NA
2262 if (mk->io_handler.conf.momentary_flap_override_enabled)
2263 return discrete_on_off(&mk_doutput(flap_override), 1.0);
2266 return discrete_on_off(&mk_doutput(audio_on), 1.0);
2269 if (mk->io_handler.momentary_steep_approach_enabled())
2270 return discrete_on_off(&mk_doutput(steep_approach), 1.0);
2275 if (mk_dinput(glideslope_inhibit))
2276 goto glideslope_end;
2279 if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
2280 goto glideslope_inop;
2284 return discrete_on_off(&mk_doutput(gpws_alert), mk_voice(glideslope));
2286 return discrete_on_off(&mk_doutput(glideslope_cancel), 0.7);
2287 goto glideslope_end;
2290 return play(mk_voice(glideslope_inop));
2295 if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
2299 return discrete_on_off(&mk_doutput(gpws_warning), mk_voice(pull_up));
2303 return play(mk_voice(gpws_inop));
2308 if (mk_dinput(self_test)) // proceed to long self test
2313 if (mk->mode6_handler.conf.bank_angle_enabled
2314 && mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
2315 return play(mk_voice(bank_angle_inop));
2319 if (mk->mode6_handler.altitude_callouts_enabled()
2320 && mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
2321 return play(mk_voice(callouts_inop));
2329 mk_doutput(gpws_inop) = true;
2330 // do not enable W/S INOP, since we do not emulate it
2331 mk_doutput(tad_inop) = true; // Terrain INOP, Terrain NA
2333 return play(mk_voice(sink_rate));
2336 return play(mk_voice(pull_up));
2338 return play(mk_voice(terrain));
2340 return play(mk_voice(pull_up));
2342 return play(mk_voice(dont_sink));
2344 return play(mk_voice(too_low_terrain));
2346 return play(mk->mode4_handler.conf.voice_too_low_gear);
2348 return play(mk_voice(too_low_flaps));
2350 return play(mk_voice(too_low_terrain));
2352 return play(mk_voice(glideslope));
2355 if (mk->mode6_handler.conf.bank_angle_enabled)
2356 return play(mk_voice(bank_angle));
2361 if (! mk->mode6_handler.altitude_callouts_enabled())
2362 goto callouts_disabled;
2366 if (mk->mode6_handler.conf.minimums_enabled)
2367 return play(mk_voice(minimums));
2371 if (mk->mode6_handler.conf.above_field_voice)
2372 return play(mk->mode6_handler.conf.above_field_voice);
2374 for (unsigned i = 0; i < n_altitude_callouts; i++)
2375 if (! was_here_offset(i))
2377 if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
2378 return play(mk_altitude_voice(i));
2382 if (mk->mode6_handler.conf.smart_500_enabled)
2383 return play(mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500));
2388 return play(mk_voice(minimums_minimums));
2393 if (mk->tcf_handler.conf.enabled)
2394 return play(mk_voice(too_low_terrain));
2401 MK_VIII::SelfTestHandler::start ()
2403 assert(state == STATE_START);
2405 memcpy(&saved_outputs, &mk->io_handler.outputs, sizeof(mk->io_handler.outputs));
2406 memset(&mk->io_handler.outputs, 0, sizeof(mk->io_handler.outputs));
2408 // [SPEC] 6.10.6: "The self-test results are annunciated, at 6db
2409 // lower than the normal audio level selected for the aircraft"
2410 mk->voice_player.set_volume(modify_amplitude(mk->voice_player.conf.volume, -6));
2412 if (mk_dinput(mode6_low_volume))
2413 mk->mode6_handler.set_volume(1.0);
2415 state = STATE_RUNNING;
2416 cancel = CANCEL_NONE;
2417 memset(&action, 0, sizeof(action));
2422 MK_VIII::SelfTestHandler::stop ()
2424 if (state != STATE_NONE)
2426 if (state != STATE_START)
2428 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2429 mk->voice_player.set_volume(mk_dinput(audio_inhibit) ? 0.0 : mk->voice_player.conf.volume);
2431 if (mk_dinput(mode6_low_volume))
2432 mk->mode6_handler.set_volume(modify_amplitude(1.0, -6));
2434 memcpy(&mk->io_handler.outputs, &saved_outputs, sizeof(mk->io_handler.outputs));
2437 button_pressed = false;
2439 // reset self-test handler position
2445 MK_VIII::SelfTestHandler::handle_button_event (bool value)
2447 if (state == STATE_NONE)
2450 state = STATE_START;
2452 else if (state == STATE_START)
2455 state = STATE_NONE; // cancel the not-yet-started test
2457 else if (cancel == CANCEL_NONE)
2461 assert(! button_pressed);
2462 button_pressed = true;
2463 button_press_timestamp = globals->get_sim_time_sec();
2469 if (globals->get_sim_time_sec() - button_press_timestamp < 2)
2470 cancel = CANCEL_SHORT;
2472 cancel = CANCEL_LONG;
2479 MK_VIII::SelfTestHandler::update ()
2481 if (state == STATE_NONE)
2483 else if (state == STATE_START)
2485 if (mk->state_handler.ground && ! mk->io_handler.steep_approach())
2495 if (button_pressed && cancel == CANCEL_NONE)
2497 if (globals->get_sim_time_sec() - button_press_timestamp >= 2)
2498 cancel = CANCEL_LONG;
2502 if (! mk->state_handler.ground || cancel != CANCEL_NONE)
2508 if (test_bits(action.flags, ACTION_SLEEP))
2510 if (globals->get_sim_time_sec() - sleep_start < action.sleep_duration)
2513 if (test_bits(action.flags, ACTION_VOICE))
2515 if (mk->voice_player.voice)
2518 if (test_bits(action.flags, ACTION_DISCRETE_ON_OFF))
2519 *action.discrete = false;
2523 if (test_bits(action.flags, ACTION_SLEEP))
2524 sleep_start = globals->get_sim_time_sec();
2525 if (test_bits(action.flags, ACTION_DONE))
2534 ///////////////////////////////////////////////////////////////////////////////
2535 // AlertHandler ///////////////////////////////////////////////////////////////
2536 ///////////////////////////////////////////////////////////////////////////////
2539 MK_VIII::AlertHandler::select_voice_alerts (unsigned int test)
2541 if (has_alerts(test))
2543 voice_alerts = alerts & test;
2548 voice_alerts &= ~test;
2549 if (voice_alerts == 0)
2550 mk->voice_player.stop();
2557 MK_VIII::AlertHandler::boot ()
2563 MK_VIII::AlertHandler::reposition ()
2567 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2568 mk->voice_player.stop(VoicePlayer::STOP_NOW);
2572 MK_VIII::AlertHandler::reset ()
2577 repeated_alerts = 0;
2578 altitude_callout_voice = NULL;
2582 MK_VIII::AlertHandler::update ()
2584 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2587 // Lamps and voices are prioritized according to [SPEC] 6.9.2.
2589 // Voices which can interrupt other voices (VoicePlayer::PLAY_NOW)
2590 // are specified by [INSTALL] appendix E 5.3.5.
2592 // When a voice is overriden by a higher priority voice and the
2593 // overriding voice stops, we restore the previous voice if it was
2594 // looped (this is not specified by [SPEC] but seems to make sense).
2598 if (has_alerts(ALERT_MODE1_PULL_UP | ALERT_MODE2A | ALERT_MODE2B))
2599 mk->io_handler.set_lamp(IOHandler::LAMP_WARNING);
2600 else if (has_alerts(ALERT_MODE1_SINK_RATE
2601 | ALERT_MODE2A_PREFACE
2602 | ALERT_MODE2B_PREFACE
2603 | ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING
2604 | ALERT_MODE2A_ALTITUDE_GAIN
2605 | ALERT_MODE2B_LANDING_MODE
2607 | ALERT_MODE4_TOO_LOW_FLAPS
2608 | ALERT_MODE4_TOO_LOW_GEAR
2609 | ALERT_MODE4AB_TOO_LOW_TERRAIN
2610 | ALERT_MODE4C_TOO_LOW_TERRAIN
2611 | ALERT_TCF_TOO_LOW_TERRAIN))
2612 mk->io_handler.set_lamp(IOHandler::LAMP_CAUTION);
2613 else if (has_alerts(ALERT_MODE5_SOFT | ALERT_MODE5_HARD))
2614 mk->io_handler.set_lamp(IOHandler::LAMP_GLIDESLOPE);
2616 mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2620 if (select_voice_alerts(ALERT_MODE1_PULL_UP))
2622 if (! has_old_alerts(ALERT_MODE1_PULL_UP))
2624 if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate))
2625 mk->voice_player.play(mk_voice(sink_rate), VoicePlayer::PLAY_NOW);
2626 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_LOOPED);
2629 else if (select_voice_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2631 if (! has_old_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2632 mk->voice_player.play(mk_voice(terrain_pause_terrain), VoicePlayer::PLAY_NOW);
2634 else if (select_voice_alerts(ALERT_MODE2A | ALERT_MODE2B))
2636 if (mk->voice_player.voice != mk_voice(pull_up))
2637 mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_NOW | VoicePlayer::PLAY_LOOPED);
2639 else if (select_voice_alerts(ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING | ALERT_MODE2B_LANDING_MODE))
2641 if (mk->voice_player.voice != mk_voice(terrain))
2642 mk->voice_player.play(mk_voice(terrain), VoicePlayer::PLAY_LOOPED);
2644 else if (select_voice_alerts(ALERT_MODE6_MINIMUMS))
2646 if (! has_old_alerts(ALERT_MODE6_MINIMUMS))
2647 mk->voice_player.play(mk_voice(minimums_minimums));
2649 else if (select_voice_alerts(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2651 if (must_play_voice(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2652 mk->voice_player.play(mk_voice(too_low_terrain));
2654 else if (select_voice_alerts(ALERT_TCF_TOO_LOW_TERRAIN))
2656 if (must_play_voice(ALERT_TCF_TOO_LOW_TERRAIN))
2657 mk->voice_player.play(mk_voice(too_low_terrain));
2659 else if (select_voice_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2661 if (! has_old_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2663 assert(altitude_callout_voice != NULL);
2664 mk->voice_player.play(altitude_callout_voice);
2665 altitude_callout_voice = NULL;
2668 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
2670 if (must_play_voice(ALERT_MODE4_TOO_LOW_GEAR))
2671 mk->voice_player.play(mk->mode4_handler.conf.voice_too_low_gear);
2673 else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_FLAPS))
2675 if (must_play_voice(ALERT_MODE4_TOO_LOW_FLAPS))
2676 mk->voice_player.play(mk_voice(too_low_flaps));
2678 else if (select_voice_alerts(ALERT_MODE1_SINK_RATE))
2680 if (must_play_voice(ALERT_MODE1_SINK_RATE))
2681 mk->voice_player.play(mk_voice(sink_rate_pause_sink_rate));
2682 // [SPEC] 6.2.1: "During the time that the voice message for the
2683 // outer envelope is inhibited and the caution/warning lamp is
2684 // on, the Mode 5 alert message is allowed to annunciate for
2685 // excessive glideslope deviation conditions."
2686 else if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate)
2687 && mk->voice_player.next_voice != mk_voice(sink_rate_pause_sink_rate))
2689 if (has_alerts(ALERT_MODE5_HARD))
2691 voice_alerts |= ALERT_MODE5_HARD;
2692 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2693 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2695 else if (has_alerts(ALERT_MODE5_SOFT))
2697 voice_alerts |= ALERT_MODE5_SOFT;
2698 if (must_play_voice(ALERT_MODE5_SOFT))
2699 mk->voice_player.play(mk_voice(soft_glideslope));
2703 else if (select_voice_alerts(ALERT_MODE3))
2705 if (must_play_voice(ALERT_MODE3))
2706 mk->voice_player.play(mk_voice(dont_sink_pause_dont_sink));
2708 else if (select_voice_alerts(ALERT_MODE5_HARD))
2710 if (mk->voice_player.voice != mk_voice(hard_glideslope))
2711 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2713 else if (select_voice_alerts(ALERT_MODE5_SOFT))
2715 if (must_play_voice(ALERT_MODE5_SOFT))
2716 mk->voice_player.play(mk_voice(soft_glideslope));
2718 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_3))
2720 if (mk->voice_player.voice != mk_voice(bank_angle_bank_angle_3))
2721 mk->voice_player.play(mk_voice(bank_angle_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2723 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_3))
2725 if (mk->voice_player.voice != mk_voice(bank_angle_pause_bank_angle_3))
2726 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2728 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2))
2730 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2731 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2733 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_2))
2735 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2736 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2738 else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1))
2740 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2741 mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2743 else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_1))
2745 if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2746 mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2749 // remember all alerts voiced so far...
2750 old_alerts |= voice_alerts;
2751 // ... forget those no longer active
2752 old_alerts &= alerts;
2753 repeated_alerts = 0;
2757 MK_VIII::AlertHandler::set_alerts (unsigned int _alerts,
2759 VoicePlayer::Voice *_altitude_callout_voice)
2762 if (test_bits(flags, ALERT_FLAG_REPEAT))
2763 repeated_alerts |= _alerts;
2764 if (_altitude_callout_voice)
2765 altitude_callout_voice = _altitude_callout_voice;
2769 MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
2772 repeated_alerts &= ~_alerts;
2773 if (_alerts & ALERT_MODE6_ALTITUDE_CALLOUT)
2774 altitude_callout_voice = NULL;
2777 ///////////////////////////////////////////////////////////////////////////////
2778 // StateHandler ///////////////////////////////////////////////////////////////
2779 ///////////////////////////////////////////////////////////////////////////////
2782 MK_VIII::StateHandler::update_ground ()
2786 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2787 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30)
2789 if (potentially_airborne_timer.start_or_elapsed() > 1)
2793 potentially_airborne_timer.stop();
2797 if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() < 40
2798 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 30)
2804 MK_VIII::StateHandler::enter_ground ()
2807 mk->io_handler.enter_ground();
2809 // [SPEC] 6.0.1 does not specify this, but it seems to be an
2810 // omission; at this point, we must make sure that we are in takeoff
2811 // mode (otherwise the next takeoff might happen in approach mode).
2817 MK_VIII::StateHandler::leave_ground ()
2820 mk->mode2_handler.leave_ground();
2824 MK_VIII::StateHandler::update_takeoff ()
2828 // [SPEC] 6.0.2 is somewhat confusing: it mentions hardcoded
2829 // terrain clearance (500, 750) and airspeed (178, 200) values,
2830 // but it also mentions the mode 4A boundary, which varies as a
2831 // function of aircraft type. We consider that the mentioned
2832 // values are examples, and that we should use the mode 4A upper
2835 if (! mk_data(terrain_clearance).ncd
2836 && ! mk_ainput(computed_airspeed).ncd
2837 && mk_data(terrain_clearance).get() > mk->mode4_handler.get_upper_agl(mk->mode4_handler.conf.modes->ac))
2842 if (! mk_data(radio_altitude).ncd
2843 && mk_data(radio_altitude).get() < mk->mode4_handler.conf.modes->b->min_agl1
2844 && mk->io_handler.flaps_down()
2845 && mk_dinput(landing_gear))
2851 MK_VIII::StateHandler::enter_takeoff ()
2854 mk->io_handler.enter_takeoff();
2855 mk->mode2_handler.enter_takeoff();
2856 mk->mode3_handler.enter_takeoff();
2857 mk->mode6_handler.enter_takeoff();
2861 MK_VIII::StateHandler::leave_takeoff ()
2864 mk->mode6_handler.leave_takeoff();
2868 MK_VIII::StateHandler::post_reposition ()
2870 // Synchronize the state with the current situation.
2873 (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2874 && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30);
2876 bool _takeoff = _ground;
2878 if (ground && ! _ground)
2880 else if (! ground && _ground)
2883 if (takeoff && ! _takeoff)
2885 else if (! takeoff && _takeoff)
2890 MK_VIII::StateHandler::update ()
2892 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2899 ///////////////////////////////////////////////////////////////////////////////
2900 // Mode1Handler ///////////////////////////////////////////////////////////////
2901 ///////////////////////////////////////////////////////////////////////////////
2904 MK_VIII::Mode1Handler::get_pull_up_bias ()
2906 // [PILOT] page 54: "Steep Approach has priority over Flap Override
2907 // if selected simultaneously."
2909 if (mk->io_handler.steep_approach())
2911 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
2918 MK_VIII::Mode1Handler::is_pull_up ()
2920 if (! mk->io_handler.gpws_inhibit()
2921 && ! mk->state_handler.ground // [1]
2922 && ! mk_data(radio_altitude).ncd
2923 && ! mk_data(barometric_altitude_rate).ncd
2924 && mk_data(radio_altitude).get() > conf.envelopes->min_agl)
2926 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl1)
2928 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl1(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
2931 else if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl2)
2933 if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl2(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
2942 MK_VIII::Mode1Handler::update_pull_up ()
2946 if (! mk_test_alert(MODE1_PULL_UP))
2948 // [SPEC] 6.2.1: at least one sink rate must be issued
2949 // before switching to pull up; if no sink rate has
2950 // occurred, a 0.2 second delay is used.
2951 if (mk->voice_player.voice == mk_voice(sink_rate_pause_sink_rate)
2952 || pull_up_timer.start_or_elapsed() >= 0.2)
2953 mk_set_alerts(mk_alert(MODE1_PULL_UP));
2958 pull_up_timer.stop();
2959 mk_unset_alerts(mk_alert(MODE1_PULL_UP));
2964 MK_VIII::Mode1Handler::get_sink_rate_bias ()
2966 // [PILOT] page 54: "Steep Approach has priority over Flap Override
2967 // if selected simultaneously."
2969 if (mk->io_handler.steep_approach())
2971 else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
2973 else if (! mk_data(glideslope_deviation_dots).ncd)
2977 if (mk_data(glideslope_deviation_dots).get() <= -2)
2979 else if (mk_data(glideslope_deviation_dots).get() < 0)
2980 bias = -150 * mk_data(glideslope_deviation_dots).get();
2982 if (mk_data(radio_altitude).get() < 100)
2983 bias *= 0.01 * mk_data(radio_altitude).get();
2992 MK_VIII::Mode1Handler::is_sink_rate ()
2994 return ! mk->io_handler.gpws_inhibit()
2995 && ! mk->state_handler.ground // [1]
2996 && ! mk_data(radio_altitude).ncd
2997 && ! mk_data(barometric_altitude_rate).ncd
2998 && mk_data(radio_altitude).get() > conf.envelopes->min_agl
2999 && mk_data(radio_altitude).get() < 2450
3000 && mk_data(radio_altitude).get() < -572 - 0.6035 * (mk_data(barometric_altitude_rate).get() + get_sink_rate_bias());
3004 MK_VIII::Mode1Handler::get_sink_rate_tti ()
3006 return mk_data(radio_altitude).get() / fabs(mk_data(barometric_altitude_rate).get());
3010 MK_VIII::Mode1Handler::update_sink_rate ()
3014 if (mk_test_alert(MODE1_SINK_RATE))
3016 double tti = get_sink_rate_tti();
3017 if (tti <= sink_rate_tti * 0.8)
3019 // ~20% degradation, warn again and store the new reference tti
3020 sink_rate_tti = tti;
3021 mk_repeat_alert(mk_alert(MODE1_SINK_RATE));
3026 if (sink_rate_timer.start_or_elapsed() >= 0.8)
3028 mk_set_alerts(mk_alert(MODE1_SINK_RATE));
3029 sink_rate_tti = get_sink_rate_tti();
3035 sink_rate_timer.stop();
3036 mk_unset_alerts(mk_alert(MODE1_SINK_RATE));
3041 MK_VIII::Mode1Handler::update ()
3043 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3050 ///////////////////////////////////////////////////////////////////////////////
3051 // Mode2Handler ///////////////////////////////////////////////////////////////
3052 ///////////////////////////////////////////////////////////////////////////////
3055 MK_VIII::Mode2Handler::ClosureRateFilter::limit_radio_altitude_rate (double r)
3057 // Limit radio altitude rate according to aircraft configuration,
3058 // allowing maximum sensitivity during cruise while providing
3059 // progressively less sensitivity during the landing phases of
3062 if (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3063 { // gs deviation <= +- 2 dots
3064 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3065 SG_CLAMP_RANGE(r, -1000.0, 3000.0);
3066 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3067 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3069 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3072 { // no ILS, or gs deviation > +- 2 dots
3073 if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3074 SG_CLAMP_RANGE(r, 0.0, 4000.0);
3075 else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3076 SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3084 MK_VIII::Mode2Handler::ClosureRateFilter::init ()
3087 last_ra.set(&mk_data(radio_altitude));
3088 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3095 MK_VIII::Mode2Handler::ClosureRateFilter::update ()
3097 double elapsed = timer.start_or_elapsed();
3101 if (! mk_data(radio_altitude).ncd && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3103 if (! last_ra.ncd && ! last_ba.ncd)
3105 // compute radio and barometric altitude rates (positive value = descent)
3106 double ra_rate = -(mk_data(radio_altitude).get() - last_ra.get()) / elapsed * 60;
3107 double ba_rate = -(mk_ainput(uncorrected_barometric_altitude).get() - last_ba.get()) / elapsed * 60;
3109 // limit radio altitude rate according to aircraft configuration
3110 ra_rate = limit_radio_altitude_rate(ra_rate);
3112 // apply a low-pass filter to the radio altitude rate
3113 ra_rate = ra_filter.filter(ra_rate);
3114 // apply a high-pass filter to the barometric altitude rate
3115 ba_rate = ba_filter.filter(ba_rate);
3117 // combine both rates to obtain a closure rate
3118 output.set(ra_rate + ba_rate);
3131 last_ra.set(&mk_data(radio_altitude));
3132 last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3136 MK_VIII::Mode2Handler::b_conditions ()
3138 return mk->io_handler.flaps_down()
3139 || (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) < 2)
3140 || takeoff_timer.running;
3144 MK_VIII::Mode2Handler::is_a ()
3146 if (! mk->io_handler.gpws_inhibit()
3147 && ! mk->state_handler.ground // [1]
3148 && ! mk_data(radio_altitude).ncd
3149 && ! mk_ainput(computed_airspeed).ncd
3150 && ! closure_rate_filter.output.ncd
3151 && ! b_conditions())
3153 if (mk_data(radio_altitude).get() < 1220)
3155 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3162 if (mk_ainput(computed_airspeed).get() <= conf->airspeed1)
3164 else if (mk_ainput(computed_airspeed).get() >= conf->airspeed2)
3167 upper_limit = 1650 + 8.9 * (mk_ainput(computed_airspeed).get() - conf->airspeed1);
3169 if (mk_data(radio_altitude).get() < upper_limit)
3171 if (mk_data(radio_altitude).get() < 522 + 0.1968 * closure_rate_filter.output.get())
3181 MK_VIII::Mode2Handler::is_b ()
3183 if (! mk->io_handler.gpws_inhibit()
3184 && ! mk->state_handler.ground // [1]
3185 && ! mk_data(radio_altitude).ncd
3186 && ! mk_data(barometric_altitude_rate).ncd
3187 && ! closure_rate_filter.output.ncd
3189 && mk_data(radio_altitude).get() < 789)
3193 if (mk->io_handler.flaps_down())
3195 if (mk_data(barometric_altitude_rate).get() > -400)
3197 else if (mk_data(barometric_altitude_rate).get() < -1000)
3200 lower_limit = -66.777 - 0.667 * mk_data(barometric_altitude_rate).get();
3205 if (mk_data(radio_altitude).get() > lower_limit)
3207 if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3216 MK_VIII::Mode2Handler::check_pull_up (unsigned int preface_alert,
3219 if (pull_up_timer.running)
3221 if (pull_up_timer.elapsed() >= 1)
3223 mk_unset_alerts(preface_alert);
3224 mk_set_alerts(alert);
3229 if (! mk->voice_player.voice)
3230 pull_up_timer.start();
3235 MK_VIII::Mode2Handler::update_a ()
3239 if (mk_test_alert(MODE2A_PREFACE))
3240 check_pull_up(mk_alert(MODE2A_PREFACE), mk_alert(MODE2A));
3241 else if (! mk_test_alert(MODE2A))
3243 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3244 mk_set_alerts(mk_alert(MODE2A_PREFACE));
3245 a_start_time = globals->get_sim_time_sec();
3246 pull_up_timer.stop();
3251 if (mk_test_alert(MODE2A_ALTITUDE_GAIN))
3253 if (mk->io_handler.gpws_inhibit()
3254 || mk->state_handler.ground // [1]
3255 || a_altitude_gain_timer.elapsed() >= 45
3256 || mk_data(radio_altitude).ncd
3257 || mk_ainput(uncorrected_barometric_altitude).ncd
3258 || mk_ainput(uncorrected_barometric_altitude).get() - a_altitude_gain_alt >= 300
3259 // [PILOT] page 12: "the visual alert will remain on
3260 // until [...] landing flaps or the flap override switch
3262 || mk->io_handler.flaps_down())
3264 // exit altitude gain mode
3265 a_altitude_gain_timer.stop();
3266 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3270 // [SPEC] 6.2.2.2: "If the terrain starts to fall away
3271 // during this altitude gain time, the voice will shut
3273 if (closure_rate_filter.output.get() < 0)
3274 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3277 else if (mk_test_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A)))
3279 mk_unset_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A));
3281 if (! mk->io_handler.gpws_inhibit()
3282 && ! mk->state_handler.ground // [1]
3283 && globals->get_sim_time_sec() - a_start_time > 3
3284 && ! mk->io_handler.flaps_down()
3285 && ! mk_data(radio_altitude).ncd
3286 && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3288 // [SPEC] 6.2.2.2: mode 2A envelope violated for more
3289 // than 3 seconds, enable altitude gain feature
3291 a_altitude_gain_timer.start();
3292 a_altitude_gain_alt = mk_ainput(uncorrected_barometric_altitude).get();
3294 unsigned int alerts = mk_alert(MODE2A_ALTITUDE_GAIN);
3295 if (closure_rate_filter.output.get() > 0)
3296 alerts |= mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING);
3298 mk_set_alerts(alerts);
3305 MK_VIII::Mode2Handler::update_b ()
3309 // handle normal mode
3311 if (b && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down()))
3313 if (mk_test_alert(MODE2B_PREFACE))
3314 check_pull_up(mk_alert(MODE2B_PREFACE), mk_alert(MODE2B));
3315 else if (! mk_test_alert(MODE2B))
3317 mk_set_alerts(mk_alert(MODE2B_PREFACE));
3318 pull_up_timer.stop();
3322 mk_unset_alerts(mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B));
3324 // handle landing mode ([SPEC] 6.2.2.3: "If both landing gear and
3325 // flaps are in the landing configuration, then the message will be
3328 if (b && mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3329 mk_set_alerts(mk_alert(MODE2B_LANDING_MODE));
3331 mk_unset_alerts(mk_alert(MODE2B_LANDING_MODE));
3335 MK_VIII::Mode2Handler::boot ()
3337 closure_rate_filter.init();
3341 MK_VIII::Mode2Handler::power_off ()
3343 // [SPEC] 6.0.4: "This latching function is not power saved and a
3344 // system reset will force it false."
3345 takeoff_timer.stop();
3349 MK_VIII::Mode2Handler::leave_ground ()
3351 // takeoff, reset timer
3352 takeoff_timer.start();
3356 MK_VIII::Mode2Handler::enter_takeoff ()
3358 // reset timer, in case it's a go-around
3359 takeoff_timer.start();
3363 MK_VIII::Mode2Handler::update ()
3365 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3368 closure_rate_filter.update();
3370 if (takeoff_timer.running && takeoff_timer.elapsed() >= 60)
3371 takeoff_timer.stop();
3377 ///////////////////////////////////////////////////////////////////////////////
3378 // Mode3Handler ///////////////////////////////////////////////////////////////
3379 ///////////////////////////////////////////////////////////////////////////////
3382 MK_VIII::Mode3Handler::max_alt_loss (double _bias)
3384 return conf->max_alt_loss(mk->io_handler.flap_override(), mk_data(radio_altitude).get()) + mk_data(radio_altitude).get() * _bias;
3388 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
3390 // do not repeat altitude-loss alerts below 30ft agl
3391 if (mk_data(radio_altitude).get() > 30)
3393 if (initial_bias < 0.0) // sanity check
3395 // mk-viii spec: repeat alerts whenever losing 20% of initial altitude
3396 while ((alt_loss > max_alt_loss(initial_bias))&&
3397 (initial_bias < 1.0))
3398 initial_bias += 0.2;
3401 return initial_bias;
3405 MK_VIII::Mode3Handler::is (double *alt_loss)
3407 if (has_descent_alt)
3409 int max_agl = conf->max_agl(mk->io_handler.flap_override());
3411 if (mk_ainput(uncorrected_barometric_altitude).ncd
3412 || mk_ainput(uncorrected_barometric_altitude).get() > descent_alt
3413 || mk_data(radio_altitude).ncd
3414 || mk_data(radio_altitude).get() > max_agl)
3417 has_descent_alt = false;
3421 // gear/flaps: [SPEC] 1.3.1.3
3422 if (! mk->io_handler.gpws_inhibit()
3423 && ! mk->state_handler.ground // [1]
3424 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3425 && ! mk_data(barometric_altitude_rate).ncd
3426 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3427 && ! mk_data(radio_altitude).ncd
3428 && mk_data(barometric_altitude_rate).get() < 0)
3430 double _alt_loss = descent_alt - mk_ainput(uncorrected_barometric_altitude).get();
3431 if (armed || (mk_data(radio_altitude).get() > conf->min_agl
3432 && mk_data(radio_altitude).get() < max_agl
3433 && _alt_loss > max_alt_loss(0)))
3435 *alt_loss = _alt_loss;
3443 if (! mk_data(barometric_altitude_rate).ncd
3444 && ! mk_ainput(uncorrected_barometric_altitude).ncd
3445 && mk_data(barometric_altitude_rate).get() < 0)
3447 has_descent_alt = true;
3448 descent_alt = mk_ainput(uncorrected_barometric_altitude).get();
3456 MK_VIII::Mode3Handler::enter_takeoff ()
3459 has_descent_alt = false;
3463 MK_VIII::Mode3Handler::update ()
3465 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3468 if (mk->state_handler.takeoff)
3472 if (! mk->state_handler.ground /* [1] */ && is(&alt_loss))
3474 if (mk_test_alert(MODE3))
3476 double new_bias = get_bias(bias, alt_loss);
3477 if (new_bias > bias)
3480 mk_repeat_alert(mk_alert(MODE3));
3486 bias = get_bias(0.2, alt_loss);
3487 mk_set_alerts(mk_alert(MODE3));
3494 mk_unset_alerts(mk_alert(MODE3));
3497 ///////////////////////////////////////////////////////////////////////////////
3498 // Mode4Handler ///////////////////////////////////////////////////////////////
3499 ///////////////////////////////////////////////////////////////////////////////
3501 // FIXME: for turbofans, the upper limit should be reduced from 1000
3502 // to 800 ft if a sudden change in radio altitude is detected, in
3503 // order to reduce nuisance alerts caused by overflying another
3504 // aircraft (see [PILOT] page 16).
3507 MK_VIII::Mode4Handler::get_upper_agl (const EnvelopesConfiguration *c)
3509 if (mk_ainput(computed_airspeed).get() >= c->airspeed2)
3511 else if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3512 return c->min_agl2(mk_ainput(computed_airspeed).get());
3517 const MK_VIII::Mode4Handler::EnvelopesConfiguration *
3518 MK_VIII::Mode4Handler::get_ab_envelope ()
3520 // [SPEC] 6.2.4.1: "The Mode 4B envelope can also be selected by
3521 // setting flaps to landing configuration or by selecting flap
3523 return mk_dinput(landing_gear) || mk->io_handler.flaps_down()
3529 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
3531 // do not repeat terrain/gear/flap alerts below 30ft agl
3532 if (mk_data(radio_altitude).get() > 30.0)
3534 if (initial_bias < 0.0) // sanity check
3536 while ((mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)&&
3537 (initial_bias < 1.0))
3538 initial_bias += 0.2;
3541 return initial_bias;
3545 MK_VIII::Mode4Handler::handle_alert (unsigned int alert,
3549 if (mk_test_alerts(alert))
3551 double new_bias = get_bias(*bias, min_agl);
3552 if (new_bias > *bias)
3555 mk_repeat_alert(alert);
3560 *bias = get_bias(0.2, min_agl);
3561 mk_set_alerts(alert);
3566 MK_VIII::Mode4Handler::update_ab ()
3568 if (! mk->io_handler.gpws_inhibit()
3569 && ! mk->state_handler.ground
3570 && ! mk->state_handler.takeoff
3571 && ! mk_data(radio_altitude).ncd
3572 && ! mk_ainput(computed_airspeed).ncd
3573 && mk_data(radio_altitude).get() > 30)
3575 const EnvelopesConfiguration *c = get_ab_envelope();
3576 if (mk_ainput(computed_airspeed).get() < c->airspeed1)
3578 if (mk_dinput(landing_gear))
3580 if (! mk->io_handler.flaps_down() && mk_data(radio_altitude).get() < c->min_agl1)
3582 handle_alert(mk_alert(MODE4_TOO_LOW_FLAPS), c->min_agl1, &ab_bias);
3588 if (mk_data(radio_altitude).get() < c->min_agl1)
3590 handle_alert(mk_alert(MODE4_TOO_LOW_GEAR), c->min_agl1, &ab_bias);
3597 mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
3602 MK_VIII::Mode4Handler::update_ab_expanded ()
3604 if (! mk->io_handler.gpws_inhibit()
3605 && ! mk->state_handler.ground
3606 && ! mk->state_handler.takeoff
3607 && ! mk_data(radio_altitude).ncd
3608 && ! mk_ainput(computed_airspeed).ncd
3609 && mk_data(radio_altitude).get() > 30)
3611 const EnvelopesConfiguration *c = get_ab_envelope();
3612 if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3614 double min_agl = get_upper_agl(c);
3615 if (mk_data(radio_altitude).get() < min_agl)
3617 handle_alert(mk_alert(MODE4AB_TOO_LOW_TERRAIN), min_agl, &ab_expanded_bias);
3623 mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
3624 ab_expanded_bias=0.0;
3628 MK_VIII::Mode4Handler::update_c ()
3630 if (! mk->io_handler.gpws_inhibit()
3631 && ! mk->state_handler.ground // [1]
3632 && mk->state_handler.takeoff
3633 && ! mk_data(radio_altitude).ncd
3634 && ! mk_data(terrain_clearance).ncd
3635 && mk_data(radio_altitude).get() > 30
3636 && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3637 && mk_data(radio_altitude).get() < get_upper_agl(conf.modes->ac)
3638 && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
3639 handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
3642 mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
3648 MK_VIII::Mode4Handler::update ()
3650 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3654 update_ab_expanded();
3658 ///////////////////////////////////////////////////////////////////////////////
3659 // Mode5Handler ///////////////////////////////////////////////////////////////
3660 ///////////////////////////////////////////////////////////////////////////////
3663 MK_VIII::Mode5Handler::is_hard ()
3665 if (mk_data(radio_altitude).get() > 30
3666 && mk_data(radio_altitude).get() < 300
3667 && mk_data(glideslope_deviation_dots).get() > 2)
3669 if (mk_data(radio_altitude).get() < 150)
3671 if (mk_data(radio_altitude).get() > 293 - 71.43 * mk_data(glideslope_deviation_dots).get())
3682 MK_VIII::Mode5Handler::is_soft (double bias)
3684 // do not repeat glide-slope alerts below 30ft agl
3685 if (mk_data(radio_altitude).get() > 30)
3687 double bias_dots = 1.3 * bias;
3688 if (mk_data(glideslope_deviation_dots).get() > 1.3 + bias_dots)
3690 if (mk_data(radio_altitude).get() < 150)
3692 if (mk_data(radio_altitude).get() > 243 - 71.43 * (mk_data(glideslope_deviation_dots).get() - bias_dots))
3699 if (mk_data(barometric_altitude_rate).ncd)
3703 if (mk_data(barometric_altitude_rate).get() >= 0)
3705 else if (mk_data(barometric_altitude_rate).get() < -500)
3708 upper_limit = -mk_data(barometric_altitude_rate).get() + 500;
3711 if (mk_data(radio_altitude).get() < upper_limit)
3721 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
3723 if (initial_bias < 0.0) // sanity check
3725 while ((is_soft(initial_bias))&&
3726 (initial_bias < 1.0))
3727 initial_bias += 0.2;
3729 return initial_bias;
3733 MK_VIII::Mode5Handler::update_hard (bool is)
3737 if (! mk_test_alert(MODE5_HARD))
3739 if (hard_timer.start_or_elapsed() >= 0.8)
3740 mk_set_alerts(mk_alert(MODE5_HARD));
3746 mk_unset_alerts(mk_alert(MODE5_HARD));
3751 MK_VIII::Mode5Handler::update_soft (bool is)
3755 if (! mk_test_alert(MODE5_SOFT))
3757 double new_bias = get_soft_bias(soft_bias);
3758 if (new_bias > soft_bias)
3760 soft_bias = new_bias;
3761 mk_repeat_alert(mk_alert(MODE5_SOFT));
3766 if (soft_timer.start_or_elapsed() >= 0.8)
3768 soft_bias = get_soft_bias(0.2);
3769 mk_set_alerts(mk_alert(MODE5_SOFT));
3776 mk_unset_alerts(mk_alert(MODE5_SOFT));
3781 MK_VIII::Mode5Handler::update ()
3783 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3786 if (! mk->io_handler.gpws_inhibit()
3787 && ! mk->state_handler.ground // [1]
3788 && ! mk_dinput(glideslope_inhibit) // not on backcourse
3789 && ! mk_data(radio_altitude).ncd
3790 && ! mk_data(glideslope_deviation_dots).ncd
3791 && (! mk->io_handler.conf.localizer_enabled
3792 || mk_data(localizer_deviation_dots).ncd
3793 || mk_data(radio_altitude).get() < 500
3794 || fabs(mk_data(localizer_deviation_dots).get()) < 2)
3795 && (! mk->state_handler.takeoff || mk->io_handler.flaps_down())
3796 && mk_dinput(landing_gear)
3797 && ! mk_doutput(glideslope_cancel))
3799 update_hard(is_hard());
3800 update_soft(is_soft(0));
3809 ///////////////////////////////////////////////////////////////////////////////
3810 // Mode6Handler ///////////////////////////////////////////////////////////////
3811 ///////////////////////////////////////////////////////////////////////////////
3813 // keep sorted in descending order
3814 const int MK_VIII::Mode6Handler::altitude_callout_definitions[] =
3815 { 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10 };
3818 MK_VIII::Mode6Handler::reset_minimums ()
3820 minimums_issued = false;
3824 MK_VIII::Mode6Handler::reset_altitude_callouts ()
3826 for (unsigned i = 0; i < n_altitude_callouts; i++)
3827 altitude_callouts_issued[i] = false;
3831 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
3833 for (unsigned i = 0; i < n_altitude_callouts; i++)
3834 if (mk->voice_player.voice == mk_altitude_voice(i)
3835 || mk->voice_player.next_voice == mk_altitude_voice(i))
3842 MK_VIII::Mode6Handler::is_near_minimums (double callout)
3846 if (! mk_data(decision_height).ncd)
3848 double diff = callout - mk_data(decision_height).get();
3850 if (mk_data(radio_altitude).get() >= 200)
3852 if (fabs(diff) <= 30)
3857 if (diff >= -3 && diff <= 6)
3866 MK_VIII::Mode6Handler::is_outside_band (double elevation, double callout)
3869 return elevation < callout - (elevation > 150 ? 20 : 10);
3873 MK_VIII::Mode6Handler::inhibit_smart_500 ()
3877 if (! mk_data(glideslope_deviation_dots).ncd
3878 && ! mk_dinput(glideslope_inhibit) // backcourse
3879 && ! mk_doutput(glideslope_cancel))
3881 if (mk->io_handler.conf.localizer_enabled
3882 && ! mk_data(localizer_deviation_dots).ncd)
3884 if (fabs(mk_data(localizer_deviation_dots).get()) <= 2)
3889 if (fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3898 MK_VIII::Mode6Handler::boot ()
3900 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3903 last_decision_height = mk_dinput(decision_height);
3904 last_radio_altitude.set(&mk_data(radio_altitude));
3907 for (unsigned i = 0; i < n_altitude_callouts; i++)
3908 altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
3909 && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
3911 // extrapolated from [SPEC] 6.4.2
3912 minimums_issued = mk_dinput(decision_height);
3914 if (conf.above_field_voice)
3917 get_altitude_above_field(&last_altitude_above_field);
3918 // extrapolated from [SPEC] 6.4.2
3919 above_field_issued = ! last_altitude_above_field.ncd
3920 && last_altitude_above_field.get() < 550;
3925 MK_VIII::Mode6Handler::power_off ()
3927 runway_timer.stop();
3931 MK_VIII::Mode6Handler::enter_takeoff ()
3933 reset_altitude_callouts(); // [SPEC] 6.4.2
3934 reset_minimums(); // omitted by [SPEC]; common sense
3938 MK_VIII::Mode6Handler::leave_takeoff ()
3940 reset_altitude_callouts(); // [SPEC] 6.0.2
3941 reset_minimums(); // [SPEC] 6.0.2
3945 MK_VIII::Mode6Handler::set_volume (float volume)
3947 mk_voice(minimums_minimums)->set_volume(volume);
3948 mk_voice(five_hundred_above)->set_volume(volume);
3949 for (unsigned i = 0; i < n_altitude_callouts; i++)
3950 mk_altitude_voice(i)->set_volume(volume);
3954 MK_VIII::Mode6Handler::altitude_callouts_enabled ()
3956 if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
3959 for (unsigned i = 0; i < n_altitude_callouts; i++)
3960 if (conf.altitude_callouts_enabled[i])
3967 MK_VIII::Mode6Handler::update_minimums ()
3969 if (! mk->io_handler.gpws_inhibit()
3970 && (mk->voice_player.voice == mk_voice(minimums_minimums)
3971 || mk->voice_player.next_voice == mk_voice(minimums_minimums)))
3974 if (! mk->io_handler.gpws_inhibit()
3975 && ! mk->state_handler.ground // [1]
3976 && conf.minimums_enabled
3977 && ! minimums_issued
3978 && mk_dinput(landing_gear)
3979 && mk_dinput(decision_height)
3980 && ! last_decision_height)
3982 minimums_issued = true;
3984 // If an altitude callout is playing, stop it now so that the
3985 // minimums callout can be played (note that proper connection
3986 // and synchronization of the radio-altitude ARINC 529 input,
3987 // decision-height discrete and decision-height ARINC 529 input
3988 // will prevent an altitude callout from being played near the
3989 // decision height).
3991 if (is_playing_altitude_callout())
3992 mk->voice_player.stop(VoicePlayer::STOP_NOW);
3994 mk_set_alerts(mk_alert(MODE6_MINIMUMS));
3997 mk_unset_alerts(mk_alert(MODE6_MINIMUMS));
4000 last_decision_height = mk_dinput(decision_height);
4004 MK_VIII::Mode6Handler::update_altitude_callouts ()
4006 if (! mk->io_handler.gpws_inhibit() && is_playing_altitude_callout())
4009 if (! mk->io_handler.gpws_inhibit()
4010 && ! mk->state_handler.ground // [1]
4011 && ! mk_data(radio_altitude).ncd)
4012 for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
4013 if ((conf.altitude_callouts_enabled[i]
4014 || (altitude_callout_definitions[i] == 500
4015 && conf.smart_500_enabled))
4016 && ! altitude_callouts_issued[i]
4017 && (last_radio_altitude.ncd
4018 || last_radio_altitude.get() > altitude_callout_definitions[i]))
4020 // lock out all callouts superior or equal to this one
4021 for (unsigned j = 0; j <= i; j++)
4022 altitude_callouts_issued[j] = true;
4024 altitude_callouts_issued[i] = true;
4025 if (! is_near_minimums(altitude_callout_definitions[i])
4026 && ! is_outside_band(mk_data(radio_altitude).get(), altitude_callout_definitions[i])
4027 && (! conf.smart_500_enabled
4028 || altitude_callout_definitions[i] != 500
4029 || ! inhibit_smart_500()))
4031 mk->alert_handler.set_altitude_callout_alert(mk_altitude_voice(i));
4036 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4039 last_radio_altitude.set(&mk_data(radio_altitude));
4043 MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
4045 if (_runway->lengthFt() < mk->conf.runway_database)
4049 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
4051 // get distance to threshold
4052 return SGGeodesy::distanceNm(pos, _runway->threshold()) <= 5;
4056 MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
4058 for (unsigned int r=0; r<airport->numRunways(); ++r) {
4059 FGRunway* rwy(airport->getRunwayByIndex(r));
4061 if (test_runway(rwy)) return true;
4067 bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
4069 bool ok = self->test_airport(a);
4074 MK_VIII::Mode6Handler::update_runway ()
4077 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4082 // Search for the closest runway threshold in range 5
4083 // nm. Passing 30nm to
4084 // get_closest_airport() provides enough margin for large
4085 // airports, which may have a runway located far away from the
4086 // airport's reference point.
4087 AirportFilter filter(this);
4088 FGPositionedRef apt = FGPositioned::findClosest(
4089 SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4092 runway.elevation = apt->elevation();
4095 has_runway.set(apt != NULL);
4099 MK_VIII::Mode6Handler::get_altitude_above_field (Parameter<double> *parameter)
4101 if (! has_runway.ncd && has_runway.get() && ! mk_data(geometric_altitude).ncd)
4102 parameter->set(mk_data(geometric_altitude).get() - runway.elevation);
4108 MK_VIII::Mode6Handler::update_above_field_callout ()
4110 if (! conf.above_field_voice)
4113 // update_runway() has to iterate over the whole runway database
4114 // (which contains thousands of runways), so it would be unwise to
4115 // run it every frame. Run it every 3 seconds. Note that the first
4116 // iteration is run in boot().
4117 if (runway_timer.start_or_elapsed() >= 3)
4120 runway_timer.start();
4123 Parameter<double> altitude_above_field;
4124 get_altitude_above_field(&altitude_above_field);
4126 if (! mk->io_handler.gpws_inhibit()
4127 && (mk->voice_player.voice == conf.above_field_voice
4128 || mk->voice_player.next_voice == conf.above_field_voice))
4131 // handle reset term
4132 if (above_field_issued)
4134 if ((! has_runway.ncd && ! has_runway.get())
4135 || (! altitude_above_field.ncd && altitude_above_field.get() > 700))
4136 above_field_issued = false;
4139 if (! mk->io_handler.gpws_inhibit()
4140 && ! mk->state_handler.ground // [1]
4141 && ! above_field_issued
4142 && ! altitude_above_field.ncd
4143 && altitude_above_field.get() < 550
4144 && (last_altitude_above_field.ncd
4145 || last_altitude_above_field.get() >= 550))
4147 above_field_issued = true;
4149 if (! is_outside_band(altitude_above_field.get(), 500))
4151 mk->alert_handler.set_altitude_callout_alert(conf.above_field_voice);
4156 mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4159 last_altitude_above_field.set(&altitude_above_field);
4163 MK_VIII::Mode6Handler::is_bank_angle (double abs_roll_angle, double bias)
4165 return conf.is_bank_angle(&mk_data(radio_altitude),
4166 abs_roll_angle - abs_roll_angle * bias,
4167 mk_dinput(autopilot_engaged));
4171 MK_VIII::Mode6Handler::is_high_bank_angle ()
4173 return mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() >= 210;
4177 MK_VIII::Mode6Handler::get_bank_angle_alerts ()
4179 if (! mk->io_handler.gpws_inhibit()
4180 && ! mk->state_handler.ground // [1]
4181 && ! mk_data(roll_angle).ncd)
4183 double abs_roll_angle = fabs(mk_data(roll_angle).get());
4185 if (is_bank_angle(abs_roll_angle, 0.4))
4186 return is_high_bank_angle()
4187 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2) | mk_alert(MODE6_HIGH_BANK_ANGLE_3))
4188 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2) | mk_alert(MODE6_LOW_BANK_ANGLE_3));
4189 else if (is_bank_angle(abs_roll_angle, 0.2))
4190 return is_high_bank_angle()
4191 ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2))
4192 : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2));
4193 else if (is_bank_angle(abs_roll_angle, 0))
4194 return is_high_bank_angle()
4195 ? mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4196 : mk_alert(MODE6_LOW_BANK_ANGLE_1);
4203 MK_VIII::Mode6Handler::update_bank_angle ()
4205 if (! conf.bank_angle_enabled)
4208 unsigned int alerts = get_bank_angle_alerts();
4210 // [SPEC] 6.4.4 specifies that the non-continuous alerts
4211 // (MODE6_*_BANK_ANGLE_1 and MODE6_*_BANK_ANGLE_2) are locked out
4212 // until the initial envelope is exited.
4214 if (! test_bits(alerts, mk_alert(MODE6_LOW_BANK_ANGLE_3)))
4215 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_3));
4216 if (! test_bits(alerts, mk_alert(MODE6_HIGH_BANK_ANGLE_3)))
4217 mk_unset_alerts(mk_alert(MODE6_HIGH_BANK_ANGLE_3));
4220 mk_set_alerts(alerts);
4222 mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_1)
4223 | mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4224 | mk_alert(MODE6_LOW_BANK_ANGLE_2)
4225 | mk_alert(MODE6_HIGH_BANK_ANGLE_2));
4229 MK_VIII::Mode6Handler::update ()
4231 if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4234 if (! mk->state_handler.takeoff
4235 && ! mk_data(radio_altitude).ncd
4236 && mk_data(radio_altitude).get() > 1000)
4238 reset_altitude_callouts(); // [SPEC] 6.4.2
4239 reset_minimums(); // common sense
4243 update_altitude_callouts();
4244 update_above_field_callout();
4245 update_bank_angle();
4248 ///////////////////////////////////////////////////////////////////////////////
4249 // TCFHandler /////////////////////////////////////////////////////////////////
4250 ///////////////////////////////////////////////////////////////////////////////
4252 bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
4254 return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
4258 MK_VIII::TCFHandler::update_runway ()
4261 if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4265 // Search for the intended destination runway of the closest
4266 // airport in range 15 nm. Passing 30nm to get_closest_airport()
4267 // provides enough margin for
4268 // large airports, which may have a runway located far away from
4269 // the airport's reference point.
4270 AirportFilter filter(mk);
4271 SGGeod apos = SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get());
4272 FGAirport* apt = FGAirport::findClosest(apos, 30.0, &filter);
4276 FGRunway* _runway = apt->findBestRunwayForPos(apos).get();
4278 if (!_runway) return;
4282 runway.center = _runway->pointOnCenterline(_runway->lengthM() * 0.5);
4284 runway.elevation = apt->elevation();
4286 runway.half_width_m = _runway->widthM() * 0.5;
4287 double half_length_m = _runway->lengthM() * 0.5;
4288 runway.half_length = half_length_m * SG_METER_TO_NM;
4295 // get heading of runway end (h0)
4296 runway.edge.heading = _runway->headingDeg();
4298 // get position of runway threshold (e0)
4299 runway.edge.position = _runway->begin();
4301 // get cartesian coordinates of both runway ends
4302 runway.bias_points[0] = _runway->cart();
4303 runway.bias_points[1] = _runway->reciprocalRunway()->cart();
4306 // Returns true if the current GPS position is inside the edge
4307 // triangle of @edge. The edge triangle, which is specified and
4308 // represented in [SPEC] 6.3.1, is defined as an isocele right
4309 // triangle of infinite height, whose right angle is located at the
4310 // position of @edge, and whose height is parallel to the heading of
4313 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
4315 double az = SGGeodesy::courseDeg( SGGeod::fromDeg(mk_data(gps_longitude).get(),
4316 mk_data(gps_latitude).get()),
4318 return get_heading_difference(az, edge->heading) <= 45;
4321 // Returns true if the current GPS position is inside the bias area of
4322 // the currently selected runway.
4324 MK_VIII::TCFHandler::is_inside_bias_area ()
4326 double half_bias_width_m = k * SG_NM_TO_METER + runway.half_width_m;
4327 SGVec3d cpos = SGVec3d::fromGeod( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4328 mk_data(gps_latitude).get(),
4329 runway.elevation) );
4330 SGLineSegmentd bias_line = SGLineSegmentd(runway.bias_points[0], runway.bias_points[1]);
4331 return dist(cpos, bias_line) < half_bias_width_m;
4335 MK_VIII::TCFHandler::is_tcf ()
4337 if (mk_data(radio_altitude).get() > 10)
4341 double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4342 mk_data(gps_latitude).get(),
4346 // distance to the inner envelope edge
4347 double edge_distance = distance - runway.half_length - k;
4349 if (edge_distance > 15)
4351 if (mk_data(radio_altitude).get() < 700)
4354 else if (edge_distance > 12)
4356 if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
4359 else if (edge_distance > 4)
4361 if (mk_data(radio_altitude).get() < 400)
4364 else if (edge_distance > 2.45)
4366 if (mk_data(radio_altitude).get() < 100 * edge_distance)
4369 else if ( is_inside_edge_triangle(&runway.edge) && (edge_distance > 0.01) )
4371 if (mk_data(radio_altitude).get() < 100 * edge_distance)
4374 else if (! is_inside_bias_area())
4376 if (mk_data(radio_altitude).get() < 245)
4380 else if (mk_data(radio_altitude).get() < 700)
4389 MK_VIII::TCFHandler::is_rfcf ()
4393 double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
4394 mk_data(gps_latitude).get(),
4397 distance -= runway.half_length;
4401 double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
4403 double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
4405 if ( (distance > 1.5) && (altitude_above_field < 300.0) )
4407 else if ( (distance > 0.0) && (altitude_above_field < 200 * distance) )
4416 MK_VIII::TCFHandler::update ()
4418 if (mk->configuration_module.state != ConfigurationModule::STATE_OK || ! conf.enabled)
4421 // update_runway() has to iterate over the whole runway database
4422 // (which contains thousands of runways), so it would be unwise to
4423 // run it every frame. Run it every 3 seconds.
4424 if (! runway_timer.running || runway_timer.elapsed() >= 3)
4427 runway_timer.start();
4430 if (! mk_dinput(ta_tcf_inhibit)
4431 && ! mk->state_handler.ground // [1]
4432 && ! mk_data(gps_latitude).ncd
4433 && ! mk_data(gps_longitude).ncd
4434 && ! mk_data(radio_altitude).ncd
4435 && ! mk_data(geometric_altitude).ncd
4436 && ! mk_data(gps_vertical_figure_of_merit).ncd)
4441 _reference = mk_data(radio_altitude).get_pointer();
4443 _reference = mk_data(geometric_altitude).get_pointer();
4449 if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
4451 double new_bias = bias;
4452 // do not repeat terrain alerts below 30ft agl
4453 if (mk_data(radio_altitude).get() > 30)
4455 if (new_bias < 0.0) // sanity check
4457 while ((*reference < initial_value - initial_value * new_bias)&&
4462 if (new_bias > bias)
4465 mk_repeat_alert(mk_alert(TCF_TOO_LOW_TERRAIN));
4471 reference = _reference;
4472 initial_value = *reference;
4473 mk_set_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4480 mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4483 ///////////////////////////////////////////////////////////////////////////////
4484 // MK_VIII ////////////////////////////////////////////////////////////////////
4485 ///////////////////////////////////////////////////////////////////////////////
4487 MK_VIII::MK_VIII (SGPropertyNode *node)
4488 : properties_handler(this),
4491 power_handler(this),
4492 system_handler(this),
4493 configuration_module(this),
4494 fault_handler(this),
4497 self_test_handler(this),
4498 alert_handler(this),
4499 state_handler(this),
4500 mode1_handler(this),
4501 mode2_handler(this),
4502 mode3_handler(this),
4503 mode4_handler(this),
4504 mode5_handler(this),
4505 mode6_handler(this),
4508 for (int i = 0; i < node->nChildren(); ++i)
4510 SGPropertyNode *child = node->getChild(i);
4511 string cname = child->getName();
4512 string cval = child->getStringValue();
4514 if (cname == "name")
4516 else if (cname == "number")
4517 num = child->getIntValue();
4520 SG_LOG(SG_INSTR, SG_WARN, "Error in mk-viii config logic");
4522 SG_LOG(SG_INSTR, SG_WARN, "Section = " << name);
4530 properties_handler.init();
4531 voice_player.init();
4537 SGPropertyNode *node = fgGetNode(("/instrumentation/" + name).c_str(), num, true);
4539 configuration_module.bind(node);
4540 power_handler.bind(node);
4541 io_handler.bind(node);
4542 voice_player.bind(node, "Sounds/mk-viii/");
4548 properties_handler.unbind();
4552 MK_VIII::update (double dt)
4554 power_handler.update();
4555 system_handler.update();
4557 if (system_handler.state != SystemHandler::STATE_ON)
4560 io_handler.update_inputs();
4561 io_handler.update_input_faults();
4563 voice_player.update();
4564 state_handler.update();
4566 if (self_test_handler.update())
4569 io_handler.update_internal_latches();
4570 io_handler.update_lamps();
4572 mode1_handler.update();
4573 mode2_handler.update();
4574 mode3_handler.update();
4575 mode4_handler.update();
4576 mode5_handler.update();
4577 mode6_handler.update();
4578 tcf_handler.update();
4580 alert_handler.update();
4581 io_handler.update_outputs();