]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/mk_viii.cxx
22cdb723bf3cf8fb39bc8faca94b693c6959ddab
[flightgear.git] / src / Instrumentation / mk_viii.cxx
1 // mk_viii.cxx -- Honeywell MK VIII EGPWS emulation
2 //
3 // Written by Jean-Yves Lefort, started September 2005.
4 //
5 // Copyright (C) 2005, 2006  Jean-Yves Lefort - jylefort@FreeBSD.org
6 //
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.
11 //
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.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22 //
23 // References:
24 //
25 //      [PILOT]         Honeywell International Inc., "MK VI and MK VIII,
26 //                      Enhanced Ground Proximity Warning System (EGPWS),
27 //                      Pilot's Guide", May 2004
28 //
29 //                      http://www.egpws.com/engineering_support/documents/pilot_guides/060-4314-000.pdf
30 //
31 //      [SPEC]          Honeywell International Inc., "Product Specification
32 //                      for the MK VI and MK VIII Enhanced Ground Proximity
33 //                      Warning System (EGPWS)", June 2004
34 //
35 //                      http://www.egpws.com/engineering_support/documents/specs/965-1180-601.pdf
36 //
37 //      [INSTALL]       Honeywell Inc., "MK VI, MK VIII, Enhanced Ground
38 //                      Proximity Warning System (Class A TAWS), Installation
39 //                      Design Guide", July 2003
40 //
41 //                      http://www.egpws.com/engineering_support/documents/install/060-4314-150.pdf
42 //
43 // Notes:
44 //
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
48 //                      make no sense.
49
50 #ifdef _MSC_VER
51 #  pragma warning( disable: 4355 )
52 #endif
53
54 #ifdef HAVE_CONFIG_H
55 #  include <config.h>
56 #endif
57
58 #include <stdio.h>
59 #include <string.h>
60 #include <assert.h>
61 #include <math.h>
62
63 #include <string>
64 #include <sstream>
65
66 #include <simgear/constants.h>
67 #include <simgear/sg_inlines.h>
68 #include <simgear/debug/logstream.hxx>
69 #include <simgear/math/sg_geodesy.hxx>
70 #include <simgear/math/sg_random.h>
71 #include <simgear/misc/sg_path.hxx>
72 #include <simgear/sound/soundmgr_openal.hxx>
73 #include <simgear/structure/exception.hxx>
74
75 using std::string;
76
77 #include <Airports/runways.hxx>
78 #include <Airports/simple.hxx>
79 #ifndef _MSC_VER
80 #  include <Include/version.h>
81 #endif
82 #include <Main/fg_props.hxx>
83 #include <Main/globals.hxx>
84 #include "instrument_mgr.hxx"
85 #include "mk_viii.hxx"
86
87 ///////////////////////////////////////////////////////////////////////////////
88 // constants //////////////////////////////////////////////////////////////////
89 ///////////////////////////////////////////////////////////////////////////////
90
91 #define GLIDESLOPE_DOTS_TO_DDM          0.0875          // [INSTALL] 6.2.30
92 #define GLIDESLOPE_DDM_TO_DOTS          (1 / GLIDESLOPE_DOTS_TO_DDM)
93
94 #define LOCALIZER_DOTS_TO_DDM           0.0775          // [INSTALL] 6.2.33
95 #define LOCALIZER_DDM_TO_DOTS           (1 / LOCALIZER_DOTS_TO_DDM)
96
97 ///////////////////////////////////////////////////////////////////////////////
98 // helpers ////////////////////////////////////////////////////////////////////
99 ///////////////////////////////////////////////////////////////////////////////
100
101 #define assert_not_reached()    assert(false)
102 #define n_elements(_array)      (sizeof(_array) / sizeof((_array)[0]))
103 #define test_bits(_bits, _test) (((_bits) & (_test)) != 0)
104
105 #define mk_node(_name)          (mk->properties_handler.external_properties._name)
106
107 #define mk_dinput_feed(_name)   (mk->io_handler.input_feeders.discretes._name)
108 #define mk_dinput(_name)        (mk->io_handler.inputs.discretes._name)
109 #define mk_ainput_feed(_name)   (mk->io_handler.input_feeders.arinc429._name)
110 #define mk_ainput(_name)        (mk->io_handler.inputs.arinc429._name)
111 #define mk_doutput(_name)       (mk->io_handler.outputs.discretes._name)
112 #define mk_aoutput(_name)       (mk->io_handler.outputs.arinc429._name)
113 #define mk_data(_name)          (mk->io_handler.data._name)
114
115 #define mk_voice(_name)         (mk->voice_player.voices._name)
116 #define mk_altitude_voice(_i)   (mk->voice_player.voices.altitude_callouts[(_i)])
117
118 #define mk_alert(_name)         (AlertHandler::ALERT_ ## _name)
119 #define mk_alert_flag(_name)    (AlertHandler::ALERT_FLAG_ ## _name)
120 #define mk_set_alerts           (mk->alert_handler.set_alerts)
121 #define mk_unset_alerts         (mk->alert_handler.unset_alerts)
122 #define mk_repeat_alert         (mk->alert_handler.repeat_alert)
123 #define mk_test_alert(_name)    (mk_test_alerts(mk_alert(_name)))
124 #define mk_test_alerts(_test)   (test_bits(mk->alert_handler.alerts, (_test)))
125
126 const double MK_VIII::TCFHandler::k = 0.25;
127
128 static double
129 modify_amplitude (double amplitude, double dB)
130 {
131   return amplitude * pow(10.0, dB / 20.0);
132 }
133
134 static double
135 heading_add (double h1, double h2)
136 {
137   double result = h1 + h2;
138   if (result >= 360)
139     result -= 360;
140   return result;
141 }
142
143 static double
144 heading_substract (double h1, double h2)
145 {
146   double result = h1 - h2;
147   if (result < 0)
148     result += 360;
149   return result;
150 }
151
152 static double
153 get_heading_difference (double h1, double h2)
154 {
155   double diff = h1 - h2;
156
157   if (diff < -180)
158     diff += 360;
159   else if (diff > 180)
160     diff -= 360;
161
162   return fabs(diff);
163 }
164
165 static double
166 get_reciprocal_heading (double h)
167 {
168   return heading_add(h, 180);
169 }
170
171 ///////////////////////////////////////////////////////////////////////////////
172 // PropertiesHandler //////////////////////////////////////////////////////////
173 ///////////////////////////////////////////////////////////////////////////////
174
175 void
176 MK_VIII::PropertiesHandler::init ()
177 {
178   mk_node(ai_caged) = fgGetNode("/instrumentation/attitude-indicator/caged-flag", true);
179   mk_node(ai_roll) = fgGetNode("/instrumentation/attitude-indicator/indicated-roll-deg", true);
180   mk_node(ai_serviceable) = fgGetNode("/instrumentation/attitude-indicator/serviceable", true);
181   mk_node(altimeter_altitude) = fgGetNode("/instrumentation/altimeter/indicated-altitude-ft", true);
182   mk_node(altimeter_serviceable) = fgGetNode("/instrumentation/altimeter/serviceable", true);
183   mk_node(altitude) = fgGetNode("/position/altitude-ft", true);
184   mk_node(altitude_agl) = fgGetNode("/position/altitude-agl-ft", true);
185   mk_node(altitude_gear_agl) = fgGetNode("/position/gear-agl-ft", true);
186   mk_node(orientation_roll) = fgGetNode("/orientation/roll-deg", true);
187   mk_node(asi_serviceable) = fgGetNode("/instrumentation/airspeed-indicator/serviceable", true);
188   mk_node(asi_speed) = fgGetNode("/instrumentation/airspeed-indicator/indicated-speed-kt", true);
189   mk_node(autopilot_heading_lock) = fgGetNode("/autopilot/locks/heading", true);
190   mk_node(flaps) = fgGetNode("/controls/flight/flaps", true);
191   mk_node(gear_down) = fgGetNode("/controls/gear/gear-down", true);
192   mk_node(latitude) = fgGetNode("/position/latitude-deg", true);
193   mk_node(longitude) = fgGetNode("/position/longitude-deg", true);
194   mk_node(nav0_cdi_serviceable) = fgGetNode("/instrumentation/nav/cdi/serviceable", true);
195   mk_node(nav0_gs_distance) = fgGetNode("/instrumentation/nav/gs-distance", true);
196   mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection", true);
197   mk_node(nav0_gs_serviceable) = fgGetNode("/instrumentation/nav/gs/serviceable", true);
198   mk_node(nav0_has_gs) = fgGetNode("/instrumentation/nav/has-gs", true);
199   mk_node(nav0_heading_needle_deflection) = fgGetNode("/instrumentation/nav/heading-needle-deflection", true);
200   mk_node(nav0_in_range) = fgGetNode("/instrumentation/nav/in-range", true);
201   mk_node(nav0_nav_loc) = fgGetNode("/instrumentation/nav/nav-loc", true);
202   mk_node(nav0_serviceable) = fgGetNode("/instrumentation/nav/serviceable", true);
203   mk_node(power) = fgGetNode(("/systems/electrical/outputs/" + mk->name).c_str(), mk->num, true);
204   mk_node(replay_state) = fgGetNode("/sim/freeze/replay-state", true);
205   mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
206 }
207
208 void
209 MK_VIII::PropertiesHandler::unbind ()
210 {
211   vector<SGPropertyNode_ptr>::iterator iter;
212
213   for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
214     (*iter)->untie();
215
216   tied_properties.clear();
217 }
218
219 ///////////////////////////////////////////////////////////////////////////////
220 // PowerHandler ///////////////////////////////////////////////////////////////
221 ///////////////////////////////////////////////////////////////////////////////
222
223 void
224 MK_VIII::PowerHandler::bind (SGPropertyNode *node)
225 {
226   mk->properties_handler.tie(node, "serviceable", SGRawValuePointer<bool>(&serviceable));
227 }
228
229 bool
230 MK_VIII::PowerHandler::handle_abnormal_voltage (bool abnormal,
231                                                 Timer *timer,
232                                                 double max_duration)
233 {
234   if (abnormal)
235     {
236       if (timer->start_or_elapsed() >= max_duration)
237         return true;            // power loss
238     }
239   else
240     timer->stop();
241
242   return false;
243 }
244
245 void
246 MK_VIII::PowerHandler::update ()
247 {
248   double voltage = mk_node(power)->getDoubleValue();
249   bool now_powered = true;
250
251   // [SPEC] 3.2.1
252
253   if (! serviceable)
254     now_powered = false;
255   if (voltage < 15)
256     now_powered = false;
257   if (handle_abnormal_voltage(voltage < 20.5, &low_surge_timer, 0.03))
258     now_powered = false;
259   if (handle_abnormal_voltage(voltage < 22.0 || voltage > 30.3, &abnormal_timer, 300))
260     now_powered = false;
261   if (handle_abnormal_voltage(voltage > 32.2, &high_surge_timer, 1))
262     now_powered = false;
263   if (handle_abnormal_voltage(voltage > 37.8, &very_high_surge_timer, 0.1))
264     now_powered = false;
265   if (voltage > 46.3)
266     now_powered = false;
267
268   if (powered)
269     {
270       // [SPEC] 3.5.2
271
272       if (now_powered)
273         power_loss_timer.stop();
274       else
275         {
276           if (power_loss_timer.start_or_elapsed() >= 0.2)
277             power_off();
278         }
279     }
280   else
281     {
282       if (now_powered)
283         power_on();
284     }
285 }
286
287 void
288 MK_VIII::PowerHandler::power_on ()
289 {
290   powered = true;
291   mk->system_handler.power_on();
292 }
293
294 void
295 MK_VIII::PowerHandler::power_off ()
296 {
297   powered = false;
298   mk->system_handler.power_off();
299   mk->voice_player.stop(VoicePlayer::STOP_NOW);
300   mk->self_test_handler.power_off(); // run before IOHandler::power_off()
301   mk->io_handler.power_off();
302   mk->mode2_handler.power_off();
303   mk->mode6_handler.power_off();
304 }
305
306 ///////////////////////////////////////////////////////////////////////////////
307 // SystemHandler //////////////////////////////////////////////////////////////
308 ///////////////////////////////////////////////////////////////////////////////
309
310 void
311 MK_VIII::SystemHandler::power_on ()
312 {
313   state = STATE_BOOTING;
314
315   // [SPEC] 3.5.2 mentions a 20 seconds maximum boot delay. We use a
316   // random delay between 3 and 5 seconds.
317
318   boot_delay = 3 + sg_random() * 2;
319   boot_timer.start();
320 }
321
322 void
323 MK_VIII::SystemHandler::power_off ()
324 {
325   state = STATE_OFF;
326
327   boot_timer.stop();
328 }
329
330 void
331 MK_VIII::SystemHandler::update ()
332 {
333   if (state == STATE_BOOTING)
334     {
335       if (boot_timer.elapsed() >= boot_delay)
336         {
337           last_replay_state = mk_node(replay_state)->getIntValue();
338
339           mk->configuration_module.boot();
340
341           mk->io_handler.boot();
342           mk->fault_handler.boot();
343           mk->alert_handler.boot();
344
345           // inputs are needed by the following boot() routines
346           mk->io_handler.update_inputs();
347
348           mk->mode2_handler.boot();
349           mk->mode6_handler.boot();
350
351           state = STATE_ON;
352
353           mk->io_handler.post_boot();
354         }
355     }
356   else if (state != STATE_OFF && mk->configuration_module.state == ConfigurationModule::STATE_OK)
357     {
358       // If the replay state changes, switch to reposition mode for 3
359       // seconds ([SPEC] 6.0.5) to avoid spurious alerts.
360
361       int replay_state = mk_node(replay_state)->getIntValue();
362       if (replay_state != last_replay_state)
363         {
364           mk->alert_handler.reposition();
365
366           last_replay_state = replay_state;
367           state = STATE_REPOSITION;
368           reposition_timer.start();
369         }
370
371       if (state == STATE_REPOSITION && reposition_timer.elapsed() >= 3)
372         {
373           // inputs are needed by StateHandler::post_reposition()
374           mk->io_handler.update_inputs();
375
376           mk->state_handler.post_reposition();
377
378           state = STATE_ON;
379         }
380     }
381 }
382
383 ///////////////////////////////////////////////////////////////////////////////
384 // ConfigurationModule ////////////////////////////////////////////////////////
385 ///////////////////////////////////////////////////////////////////////////////
386
387 MK_VIII::ConfigurationModule::ConfigurationModule (MK_VIII *device)
388   : mk(device)
389 {
390   // arbitrary defaults
391   categories[CATEGORY_AIRCRAFT_MODE_TYPE_SELECT] = 0;
392   categories[CATEGORY_AIR_DATA_INPUT_SELECT] = 1;
393   categories[CATEGORY_POSITION_INPUT_SELECT] = 2;
394   categories[CATEGORY_ALTITUDE_CALLOUTS] = 13;
395   categories[CATEGORY_AUDIO_MENU_SELECT] = 0;
396   categories[CATEGORY_TERRAIN_DISPLAY_SELECT] = 1;
397   categories[CATEGORY_OPTIONS_SELECT_GROUP_1] = 124;
398   categories[CATEGORY_RADIO_ALTITUDE_INPUT_SELECT] = 2;
399   categories[CATEGORY_NAVIGATION_INPUT_SELECT] = 3;
400   categories[CATEGORY_ATTITUDE_INPUT_SELECT] = 6;
401   categories[CATEGORY_HEADING_INPUT_SELECT] = 2;
402   categories[CATEGORY_WINDSHEAR_INPUT_SELECT] = 0;
403   categories[CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT] = 7;
404   categories[CATEGORY_AUDIO_OUTPUT_LEVEL] = 0;
405   categories[CATEGORY_UNDEFINED_INPUT_SELECT_1] = 0;
406   categories[CATEGORY_UNDEFINED_INPUT_SELECT_2] = 0;
407   categories[CATEGORY_UNDEFINED_INPUT_SELECT_3] = 0;
408 }
409
410 static double m1_t1_min_agl1 (double vs) { return -1620 - 1.1133 * vs; }
411 static double m1_t1_min_agl2 (double vs) { return -400 - 0.4 * vs; }
412 static double m1_t4_min_agl1 (double vs) { return -1625.47 - 1.1167 * vs; }
413 static double m1_t4_min_agl2 (double vs) { return -0.1958 * vs; }
414
415 static int m3_t1_max_agl (bool flap_override) { return 1500; }
416 static double m3_t1_max_alt_loss (bool flap_override, double agl) { return 5.4 + 0.092 * agl; }
417 static int m3_t2_max_agl (bool flap_override) { return flap_override ? 815 : 925; }
418 static double m3_t2_max_alt_loss (bool flap_override, double agl)
419 {
420   int bias = agl > 700 ? 5 : 0;
421
422   if (flap_override)
423     return (9.0 + 0.184 * agl) + bias;
424   else
425     return (5.4 + 0.092 * agl) + bias;
426 }
427
428 static double m4_t1_min_agl2 (double airspeed) { return -1083 + 8.333 * airspeed; }
429 static double m4_t568_a_min_agl2 (double airspeed) { return -1523 + 11.36 * airspeed; }
430 static double m4_t79_a_min_agl2 (double airspeed) { return -1182 + 11.36 * airspeed; }
431 static double m4_t568_b_min_agl2 (double airspeed) { return -1570 + 11.6 * airspeed; }
432 static double m4_t79_b_min_agl2 (double airspeed) { return -1222 + 11.6 * airspeed; }
433
434 bool
435 MK_VIII::ConfigurationModule::m6_t2_is_bank_angle (Parameter<double> *agl,
436                                                    double abs_roll_deg,
437                                                    bool ap_engaged)
438 {
439   if (ap_engaged)
440     {
441       if (agl->ncd || agl->get() > 122)
442         return abs_roll_deg > 33;
443     }
444   else
445     {
446       if (agl->ncd || agl->get() > 2450)
447         return abs_roll_deg > 55;
448       else if (agl->get() > 150)
449         return agl->get() < 153.33333 * abs_roll_deg - 5983.3333;
450     }
451
452   if (agl->get() > 30)
453     return agl->get() < 4 * abs_roll_deg - 10;
454   else if (agl->get() > 5)
455     return abs_roll_deg > 10;
456
457   return false;
458 }
459
460 bool
461 MK_VIII::ConfigurationModule::m6_t4_is_bank_angle (Parameter<double> *agl,
462                                                    double abs_roll_deg,
463                                                    bool ap_engaged)
464 {
465   if (ap_engaged)
466     {
467       if (agl->ncd || agl->get() > 156)
468         return abs_roll_deg > 33;
469     }
470   else
471     {
472       if (agl->ncd || agl->get() > 210)
473         return abs_roll_deg > 50;
474     }
475
476   if (agl->get() > 10)
477     return agl->get() < 5.7142857 * abs_roll_deg - 75.714286;
478
479   return false;
480 }
481
482 bool
483 MK_VIII::ConfigurationModule::read_aircraft_mode_type_select (int value)
484 {
485   static const Mode1Handler::EnvelopesConfiguration m1_t1 =
486     { false, 10, m1_t1_min_agl1, 284, m1_t1_min_agl2, 2450 };
487   static const Mode1Handler::EnvelopesConfiguration m1_t4 =
488     { true, 50, m1_t4_min_agl1, 346, m1_t4_min_agl2, 1958 };
489
490   static const Mode2Handler::Configuration m2_t1 = { 190, 280 };
491   static const Mode2Handler::Configuration m2_t4 = { 220, 310 };
492   static const Mode2Handler::Configuration m2_t5 = { 220, 310 };
493
494   static const Mode3Handler::Configuration m3_t1 =
495     { 30, m3_t1_max_agl, m3_t1_max_alt_loss };
496   static const Mode3Handler::Configuration m3_t2 = 
497     { 50, m3_t2_max_agl, m3_t2_max_alt_loss };
498
499   static const Mode4Handler::EnvelopesConfiguration m4_t1_ac =
500     { 190, 250, 500, m4_t1_min_agl2, 1000 };
501   static const Mode4Handler::EnvelopesConfiguration m4_t5_ac =
502     { 178, 200, 500, m4_t568_a_min_agl2, 1000 };
503   static const Mode4Handler::EnvelopesConfiguration m4_t68_ac =
504     { 178, 200, 500, m4_t568_a_min_agl2, 750 };
505   static const Mode4Handler::EnvelopesConfiguration m4_t79_ac =
506     { 148, 170, 500, m4_t79_a_min_agl2, 750 };
507
508   static const Mode4Handler::EnvelopesConfiguration m4_t1_b =
509     { 159, 250, 245, m4_t1_min_agl2, 1000 };
510   static const Mode4Handler::EnvelopesConfiguration m4_t5_b =
511     { 148, 200, 200, m4_t568_b_min_agl2, 1000 };
512   static const Mode4Handler::EnvelopesConfiguration m4_t6_b =
513     { 150, 200, 170, m4_t568_b_min_agl2, 750 };
514   static const Mode4Handler::EnvelopesConfiguration m4_t7_b =
515     { 120, 170, 170, m4_t79_b_min_agl2, 750 };
516   static const Mode4Handler::EnvelopesConfiguration m4_t8_b =
517     { 148, 200, 150, m4_t568_b_min_agl2, 750 };
518   static const Mode4Handler::EnvelopesConfiguration m4_t9_b =
519     { 118, 170, 150, m4_t79_b_min_agl2, 750 };
520
521   static const Mode4Handler::ModesConfiguration m4_t1 = { &m4_t1_ac, &m4_t1_b };
522   static const Mode4Handler::ModesConfiguration m4_t5 = { &m4_t5_ac, &m4_t5_b };
523   static const Mode4Handler::ModesConfiguration m4_t6 = { &m4_t68_ac, &m4_t6_b };
524   static const Mode4Handler::ModesConfiguration m4_t7 = { &m4_t79_ac, &m4_t7_b };
525   static const Mode4Handler::ModesConfiguration m4_t8 = { &m4_t68_ac, &m4_t8_b };
526   static const Mode4Handler::ModesConfiguration m4_t9 = { &m4_t79_ac, &m4_t9_b };
527
528   static Mode6Handler::BankAnglePredicate m6_t2 = m6_t2_is_bank_angle;
529   static Mode6Handler::BankAnglePredicate m6_t4 = m6_t4_is_bank_angle;
530
531   static const IOHandler::FaultsConfiguration f_slow = { 180, 200 };
532   static const IOHandler::FaultsConfiguration f_fast = { 250, 290 };
533
534   static const struct
535   {
536     int                                         type;
537     const Mode1Handler::EnvelopesConfiguration  *m1;
538     const Mode2Handler::Configuration           *m2;
539     const Mode3Handler::Configuration           *m3;
540     const Mode4Handler::ModesConfiguration      *m4;
541     Mode6Handler::BankAnglePredicate            m6;
542     const IOHandler::FaultsConfiguration        *f;
543     int                                         runway_database;
544   } aircraft_types[] = {
545     { 0,        &m1_t4, &m2_t4, &m3_t2, &m4_t6, m6_t4, &f_fast, 2000 },
546     { 1,        &m1_t4, &m2_t4, &m3_t2, &m4_t8, m6_t4, &f_fast, 2000 },
547     { 2,        &m1_t4, &m2_t4, &m3_t2, &m4_t5, m6_t4, &f_fast, 2000 },
548     { 3,        &m1_t4, &m2_t5, &m3_t2, &m4_t7, m6_t4, &f_slow, 2000 },
549     { 4,        &m1_t4, &m2_t5, &m3_t2, &m4_t9, m6_t4, &f_slow, 2000 },
550     { 254,      &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 3500 },
551     { 255,      &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 2000 }
552   };
553
554   for (size_t i = 0; i < n_elements(aircraft_types); i++)
555     if (aircraft_types[i].type == value)
556       {
557         mk->mode1_handler.conf.envelopes = aircraft_types[i].m1;
558         mk->mode2_handler.conf = aircraft_types[i].m2;
559         mk->mode3_handler.conf = aircraft_types[i].m3;
560         mk->mode4_handler.conf.modes = aircraft_types[i].m4;
561         mk->mode6_handler.conf.is_bank_angle = aircraft_types[i].m6;
562         mk->io_handler.conf.faults = aircraft_types[i].f;
563         mk->conf.runway_database = aircraft_types[i].runway_database;
564         return true;
565       }
566
567   state = STATE_INVALID_AIRCRAFT_TYPE;
568   return false;
569 }
570
571 bool
572 MK_VIII::ConfigurationModule::read_air_data_input_select (int value)
573 {
574   // unimplemented
575   return (value >= 0 && value <= 6) || (value >= 10 && value <= 13) || value == 255;
576 }
577
578 bool
579 MK_VIII::ConfigurationModule::read_position_input_select (int value)
580 {
581   if (value == 2)
582     mk->io_handler.conf.use_internal_gps = true;
583   else if ((value >= 0 && value <= 5)
584            || (value >= 10 && value <= 13)
585            || (value == 253)
586            || (value == 255))
587     mk->io_handler.conf.use_internal_gps = false;
588   else
589     return false;
590
591   return true;
592 }
593
594 bool
595 MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
596 {
597   enum
598   {
599     MINIMUMS            = -1,
600     SMART_500           = -2,
601     FIELD_500           = -3,
602     FIELD_500_ABOVE     = -4
603   };
604
605   static const struct
606   {
607     int id;
608     int callouts[13];
609   } values[] = {
610     { 0, { MINIMUMS, SMART_500, 200, 100, 50, 40, 30, 20, 10, 0 } },
611     { 1, { MINIMUMS, SMART_500, 200, 0 } },
612     { 2, { MINIMUMS, SMART_500, 100, 50, 40, 30, 20, 10, 0 } },
613     { 3, { MINIMUMS, SMART_500, 0 } },
614     { 4, { MINIMUMS, 200, 100, 50, 40, 30, 20, 10, 0 } },
615     { 5, { MINIMUMS, 200, 0 } },
616     { 6, { MINIMUMS, 100, 50, 40, 30, 20, 10, 0 } },
617     { 7, { 0 } },
618     { 8, { MINIMUMS, 0 } },
619     { 9, { MINIMUMS, 500, 200, 100, 50, 40, 30, 20, 10, 0 } },
620     { 10, { MINIMUMS, 500, 200, 0 } },
621     { 11, { MINIMUMS, 500, 100, 50, 40, 30, 20, 10, 0 } },
622     { 12, { MINIMUMS, 500, 0 } },
623     { 13, { MINIMUMS, 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10, 0 } },
624     { 14, { MINIMUMS, 100, 0 } },
625     { 15, { MINIMUMS, 200, 100, 0 } },
626     { 100, { FIELD_500, 0 } },
627     { 101, { FIELD_500_ABOVE, 0 } }
628   };
629
630   unsigned i;
631
632   mk->mode6_handler.conf.minimums_enabled = false;
633   mk->mode6_handler.conf.smart_500_enabled = false;
634   mk->mode6_handler.conf.above_field_voice = NULL;
635   for (i = 0; i < n_altitude_callouts; i++)
636     mk->mode6_handler.conf.altitude_callouts_enabled[i] = false;
637
638   for (i = 0; i < n_elements(values); i++)
639     if (values[i].id == value)
640       {
641         for (int j = 0; values[i].callouts[j] != 0; j++)
642           switch (values[i].callouts[j])
643             {
644             case MINIMUMS:
645               mk->mode6_handler.conf.minimums_enabled = true;
646               break;
647
648             case SMART_500:
649               mk->mode6_handler.conf.smart_500_enabled = true;
650               break;
651
652             case FIELD_500:
653               mk->mode6_handler.conf.above_field_voice = mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500);
654               break;
655
656             case FIELD_500_ABOVE:
657               mk->mode6_handler.conf.above_field_voice = mk_voice(five_hundred_above);
658               break;
659
660             default:
661               for (unsigned k = 0; k < n_altitude_callouts; k++)
662                 if (mk->mode6_handler.altitude_callout_definitions[k] == values[i].callouts[j])
663                   mk->mode6_handler.conf.altitude_callouts_enabled[k] = true;
664               break;
665             }
666
667         return true;
668       }
669
670   return false;
671 }
672
673 bool
674 MK_VIII::ConfigurationModule::read_audio_menu_select (int value)
675 {
676   if (value == 0 || value == 1)
677     mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_gear);
678   else if (value == 2 || value == 3)
679     mk->mode4_handler.conf.voice_too_low_gear = mk_voice(too_low_flaps);
680   else
681     return false;
682
683   return true;
684 }
685
686 bool
687 MK_VIII::ConfigurationModule::read_terrain_display_select (int value)
688 {
689   if (value == 2)
690     mk->tcf_handler.conf.enabled = false;
691   else if (value == 0 || value == 1 || (value >= 3 && value <= 15)
692            || (value >= 18 && value <= 20) || (value >= 235 && value <= 255))
693     mk->tcf_handler.conf.enabled = true;
694   else
695     return false;
696
697   return true;
698 }
699
700 bool
701 MK_VIII::ConfigurationModule::read_options_select_group_1 (int value)
702 {
703   if (value >= 0 && value < 128)
704     {
705       mk->io_handler.conf.flap_reversal = test_bits(value, 1 << 1);
706       mk->mode6_handler.conf.bank_angle_enabled = test_bits(value, 1 << 2);
707       mk->io_handler.conf.steep_approach_enabled = test_bits(value, 1 << 6);
708       return true;
709     }
710   else
711     return false;
712 }
713
714 bool
715 MK_VIII::ConfigurationModule::read_radio_altitude_input_select (int value)
716 {
717   if (value >= 0 && value <= 2)
718     mk->io_handler.conf.use_gear_altitude = true;
719   else
720     mk->io_handler.conf.use_gear_altitude = false;
721   return (value >= 0 && value <= 4) || (value >= 251 && value <= 255);
722 }
723
724 bool
725 MK_VIII::ConfigurationModule::read_navigation_input_select (int value)
726 {
727   if (value >= 0 && value <= 2)
728     mk->io_handler.conf.localizer_enabled = false;
729   else if ((value >= 3 && value <= 5) || (value >= 250 && value <= 255))
730     mk->io_handler.conf.localizer_enabled = true;
731   else
732     return false;
733
734   return true;
735 }
736
737 bool
738 MK_VIII::ConfigurationModule::read_attitude_input_select (int value)
739 {
740   if (value == 2)
741     mk->io_handler.conf.use_attitude_indicator=true;
742   else
743     mk->io_handler.conf.use_attitude_indicator=false;
744   return (value >= 0 && value <= 6) || value == 253 || value == 255;
745 }
746
747 bool
748 MK_VIII::ConfigurationModule::read_heading_input_select (int value)
749 {
750   // unimplemented
751   return (value >= 0 && value <= 3) || value == 254 || value == 255;
752 }
753
754 bool
755 MK_VIII::ConfigurationModule::read_windshear_input_select (int value)
756 {
757   // unimplemented
758   return value == 0 || (value >= 253 && value <= 255);
759 }
760
761 bool
762 MK_VIII::ConfigurationModule::read_input_output_discrete_type_select (int value)
763 {
764   static const struct
765   {
766     int                                 type;
767     IOHandler::LampConfiguration        lamp_conf;
768     bool                                gpws_inhibit_enabled;
769     bool                                momentary_flap_override_enabled;
770     bool                                alternate_steep_approach;
771   } io_types[] = {
772     { 0,        { false, false }, false, true, true },
773     { 1,        { true, false }, false, true, true },
774     { 2,        { false, false }, true, true, true },
775     { 3,        { true, false }, true, true, true },
776     { 4,        { false, true }, true, true, true },
777     { 5,        { true, true }, true, true, true },
778     { 6,        { false, false }, true, true, false },
779     { 7,        { true, false }, true, true, false },
780     { 254,      { false, false }, true, false, true },
781     { 255,      { false, false }, true, false, true }
782   };
783
784   for (size_t i = 0; i < n_elements(io_types); i++)
785     if (io_types[i].type == value)
786       {
787         mk->io_handler.conf.lamp = &io_types[i].lamp_conf;
788         mk->io_handler.conf.gpws_inhibit_enabled = io_types[i].gpws_inhibit_enabled;
789         mk->io_handler.conf.momentary_flap_override_enabled = io_types[i].momentary_flap_override_enabled;
790         mk->io_handler.conf.alternate_steep_approach = io_types[i].alternate_steep_approach;
791         return true;
792       }
793
794   return false;
795 }
796
797 bool
798 MK_VIII::ConfigurationModule::read_audio_output_level (int value)
799 {
800   static const struct
801   {
802     int id;
803     int relative_dB;
804   } values[] = {
805     { 0, 0 },
806     { 1, -6 },
807     { 2, -12 },
808     { 3, -18 },
809     { 4, -24 }
810   };
811
812   for (size_t i = 0; i < n_elements(values); i++)
813     if (values[i].id == value)
814       {
815         mk->voice_player.set_volume(mk->voice_player.conf.volume = modify_amplitude(1.0, values[i].relative_dB));
816         return true;
817       }
818
819   // The self test needs the voice player even when the configuration
820   // is invalid, so set a default value.
821   mk->voice_player.set_volume(mk->voice_player.conf.volume = 1.0);
822   return false;
823 }
824
825 bool
826 MK_VIII::ConfigurationModule::read_undefined_input_select (int value)
827 {
828   // unimplemented
829   return value == 0;
830 }
831
832 void
833 MK_VIII::ConfigurationModule::boot ()
834 {
835   bool (MK_VIII::ConfigurationModule::*readers[N_CATEGORIES]) (int) = {
836     &MK_VIII::ConfigurationModule::read_aircraft_mode_type_select,
837     &MK_VIII::ConfigurationModule::read_air_data_input_select,
838     &MK_VIII::ConfigurationModule::read_position_input_select,
839     &MK_VIII::ConfigurationModule::read_altitude_callouts,
840     &MK_VIII::ConfigurationModule::read_audio_menu_select,
841     &MK_VIII::ConfigurationModule::read_terrain_display_select,
842     &MK_VIII::ConfigurationModule::read_options_select_group_1,
843     &MK_VIII::ConfigurationModule::read_radio_altitude_input_select,
844     &MK_VIII::ConfigurationModule::read_navigation_input_select,
845     &MK_VIII::ConfigurationModule::read_attitude_input_select,
846     &MK_VIII::ConfigurationModule::read_heading_input_select,
847     &MK_VIII::ConfigurationModule::read_windshear_input_select,
848     &MK_VIII::ConfigurationModule::read_input_output_discrete_type_select,
849     &MK_VIII::ConfigurationModule::read_audio_output_level,
850     &MK_VIII::ConfigurationModule::read_undefined_input_select,
851     &MK_VIII::ConfigurationModule::read_undefined_input_select,
852     &MK_VIII::ConfigurationModule::read_undefined_input_select
853   };
854
855   memcpy(effective_categories, categories, sizeof(categories));
856   state = STATE_OK;
857
858   for (int i = 0; i < N_CATEGORIES; i++)
859     if (! (this->*readers[i])(effective_categories[i]))
860       {
861         SG_LOG(SG_INSTR, SG_ALERT, "MK VIII EGPWS configuration category " << i + 1 << ": invalid value " << effective_categories[i]);
862
863         if (state == STATE_OK)
864           state = STATE_INVALID_DATABASE;
865
866         mk_doutput(gpws_inop) = true;
867         mk_doutput(tad_inop) = true;
868
869         return;
870       }
871
872   // handle conflicts
873
874   if (mk->mode6_handler.conf.above_field_voice && ! mk->tcf_handler.conf.enabled)
875     {
876       // [INSTALL] 3.6
877       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");
878       state = STATE_INVALID_DATABASE;
879     }
880 }
881
882 void
883 MK_VIII::ConfigurationModule::bind (SGPropertyNode *node)
884 {
885   for (int i = 0; i < N_CATEGORIES; i++)
886     {
887       std::ostringstream name;
888       name << "configuration-module/category-" << i + 1;
889       mk->properties_handler.tie(node, name.str().c_str(), SGRawValuePointer<int>(&categories[i]));
890     }
891 }
892
893 ///////////////////////////////////////////////////////////////////////////////
894 // FaultHandler ///////////////////////////////////////////////////////////////
895 ///////////////////////////////////////////////////////////////////////////////
896
897 // [INSTALL] only specifies that the faults cause a GPWS INOP
898 // indication. We arbitrarily set a TAD INOP when it makes sense.
899 const unsigned int MK_VIII::FaultHandler::fault_inops[] = {
900   INOP_GPWS | INOP_TAD,         // [INSTALL] 3.15.1.3
901   INOP_GPWS,                    // [INSTALL] appendix E 6.6.2
902   INOP_GPWS,                    // [INSTALL] appendix E 6.6.4
903   INOP_GPWS,                    // [INSTALL] appendix E 6.6.6
904   INOP_GPWS | INOP_TAD,         // [INSTALL] appendix E 6.6.7
905   INOP_GPWS,                    // [INSTALL] appendix E 6.6.13
906   INOP_GPWS,                    // [INSTALL] appendix E 6.6.25
907   INOP_GPWS,                    // [INSTALL] appendix E 6.6.27
908   INOP_TAD,                     // unspecified
909   INOP_GPWS,                    // unspecified
910   INOP_GPWS,                    // unspecified
911   // [INSTALL] 2.3.10.1 specifies that GPWS INOP is "activated during
912   // any detected partial or total failure of the GPWS modes 1-5", so
913   // do not set any INOP for mode 6 and bank angle.
914   0,                            // unspecified
915   0,                            // unspecified
916   INOP_TAD                      // unspecified
917 };
918
919 bool
920 MK_VIII::FaultHandler::has_faults () const
921 {
922   for (int i = 0; i < N_FAULTS; i++)
923     if (faults[i])
924       return true;
925
926   return false;
927 }
928
929 bool
930 MK_VIII::FaultHandler::has_faults (unsigned int inop)
931 {
932   for (int i = 0; i < N_FAULTS; i++)
933     if (faults[i] && test_bits(fault_inops[i], inop))
934       return true;
935
936   return false;
937 }
938
939 void
940 MK_VIII::FaultHandler::boot ()
941 {
942   memset(faults, 0, sizeof(faults));
943 }
944
945 void
946 MK_VIII::FaultHandler::set_fault (Fault fault)
947 {
948   if (! faults[fault])
949     {
950       faults[fault] = true;
951
952       mk->self_test_handler.set_inop();
953
954       if (test_bits(fault_inops[fault], INOP_GPWS))
955         {
956           mk_unset_alerts(~mk_alert(TCF_TOO_LOW_TERRAIN));
957           mk_doutput(gpws_inop) = true;
958         }
959       if (test_bits(fault_inops[fault], INOP_TAD))
960         {
961           mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
962           mk_doutput(tad_inop) = true;
963         }
964     }
965 }
966
967 void
968 MK_VIII::FaultHandler::unset_fault (Fault fault)
969 {
970   if (faults[fault])
971     {
972       faults[fault] = false;
973       if (! has_faults(INOP_GPWS))
974         mk_doutput(gpws_inop) = false;
975       if (! has_faults(INOP_TAD))
976         mk_doutput(tad_inop) = false;
977     }
978 }
979
980 ///////////////////////////////////////////////////////////////////////////////
981 // IOHandler //////////////////////////////////////////////////////////////////
982 ///////////////////////////////////////////////////////////////////////////////
983
984 double
985 MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
986 {
987   // [PILOT] page 20 specifies that the terrain clearance is equal to
988   // 75% of the radio altitude, averaged over the previous 15 seconds.
989
990   samples_type::iterator iter;
991
992   // remove samples older than 15 seconds
993   for (iter = samples.begin(); iter != samples.end() && globals->get_sim_time_sec() - (*iter).timestamp >= 15; iter = samples.begin())
994     samples.erase(iter);
995
996   // append new sample
997   samples.push_back(Sample<double>(agl));
998
999   // calculate average
1000   double new_value = 0;
1001   if (samples.size() > 0)
1002     {
1003       for (iter = samples.begin(); iter != samples.end(); iter++)
1004         new_value += (*iter).value;
1005       new_value /= samples.size();
1006     }
1007   new_value *= 0.75;
1008
1009   if (new_value > value)
1010     value = new_value;
1011
1012   return value;
1013 }
1014
1015 void
1016 MK_VIII::IOHandler::TerrainClearanceFilter::reset ()
1017 {
1018   samples.clear();
1019   value = 0;
1020 }
1021
1022 MK_VIII::IOHandler::IOHandler (MK_VIII *device)
1023   : mk(device), _lamp(LAMP_NONE)
1024 {
1025   memset(&input_feeders, 0, sizeof(input_feeders));
1026   memset(&inputs.discretes, 0, sizeof(inputs.discretes));
1027   memset(&outputs, 0, sizeof(outputs));
1028   memset(&power_saved, 0, sizeof(power_saved));
1029
1030   mk_dinput_feed(landing_gear) = true;
1031   mk_dinput_feed(landing_flaps) = true;
1032   mk_dinput_feed(glideslope_inhibit) = true;
1033   mk_dinput_feed(decision_height) = true;
1034   mk_dinput_feed(autopilot_engaged) = true;
1035   mk_ainput_feed(uncorrected_barometric_altitude) = true;
1036   mk_ainput_feed(barometric_altitude_rate) = true;
1037   mk_ainput_feed(radio_altitude) = true;
1038   mk_ainput_feed(glideslope_deviation) = true;
1039   mk_ainput_feed(roll_angle) = true;
1040   mk_ainput_feed(localizer_deviation) = true;
1041   mk_ainput_feed(computed_airspeed) = true;
1042
1043   // will be unset on power on
1044   mk_doutput(gpws_inop) = true;
1045   mk_doutput(tad_inop) = true;
1046 }
1047
1048 void
1049 MK_VIII::IOHandler::boot ()
1050 {
1051   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1052     return;
1053
1054   mk_doutput(gpws_inop) = false;
1055   mk_doutput(tad_inop) = false;
1056
1057   mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
1058
1059   altitude_samples.clear();
1060 }
1061
1062 void
1063 MK_VIII::IOHandler::post_boot ()
1064 {
1065   if (momentary_steep_approach_enabled())
1066     {
1067       last_landing_gear = mk_dinput(landing_gear);
1068       last_real_flaps_down = real_flaps_down();
1069     }
1070
1071   // read externally-latching input discretes
1072   update_alternate_discrete_input(&mk_dinput(mode6_low_volume));
1073   update_alternate_discrete_input(&mk_dinput(audio_inhibit));
1074 }
1075
1076 void
1077 MK_VIII::IOHandler::power_off ()
1078 {
1079   power_saved.glideslope_cancel = mk_doutput(glideslope_cancel); // [SPEC] 6.2.5
1080
1081   memset(&outputs, 0, sizeof(outputs));
1082
1083   audio_inhibit_fault_timer.stop();
1084   landing_gear_fault_timer.stop();
1085   flaps_down_fault_timer.stop();
1086   momentary_flap_override_fault_timer.stop();
1087   self_test_fault_timer.stop();
1088   glideslope_cancel_fault_timer.stop();
1089   steep_approach_fault_timer.stop();
1090   gpws_inhibit_fault_timer.stop();
1091   ta_tcf_inhibit_fault_timer.stop();
1092
1093   // [SPEC] 6.9.3.5
1094   mk_doutput(gpws_inop) = true;
1095   mk_doutput(tad_inop) = true;
1096 }
1097
1098 void
1099 MK_VIII::IOHandler::enter_ground ()
1100 {
1101   reset_terrain_clearance();
1102
1103   if (conf.momentary_flap_override_enabled)
1104     mk_doutput(flap_override) = false;  // [INSTALL] 3.15.1.6
1105 }
1106
1107 void
1108 MK_VIII::IOHandler::enter_takeoff ()
1109 {
1110   reset_terrain_clearance();
1111
1112   if (momentary_steep_approach_enabled())
1113     // landing or go-around, disable steep approach as per [SPEC] 6.2.1
1114     mk_doutput(steep_approach) = false;
1115 }
1116
1117 void
1118 MK_VIII::IOHandler::update_inputs ()
1119 {
1120   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1121     return;
1122
1123   // 1. process input feeders
1124
1125   if (mk_dinput_feed(landing_gear))
1126     mk_dinput(landing_gear) = mk_node(gear_down)->getBoolValue();
1127   if (mk_dinput_feed(landing_flaps))
1128     mk_dinput(landing_flaps) = mk_node(flaps)->getDoubleValue() < 1;
1129   if (mk_dinput_feed(glideslope_inhibit))
1130     mk_dinput(glideslope_inhibit) = mk_node(nav0_gs_distance)->getDoubleValue() < 0;
1131   if (mk_dinput_feed(autopilot_engaged))
1132     {
1133       const char *mode;
1134
1135       mode = mk_node(autopilot_heading_lock)->getStringValue();
1136       mk_dinput(autopilot_engaged) = mode && *mode;
1137     }
1138
1139   if (mk_ainput_feed(uncorrected_barometric_altitude))
1140     {
1141       if (mk_node(altimeter_serviceable)->getBoolValue())
1142         mk_ainput(uncorrected_barometric_altitude).set(mk_node(altimeter_altitude)->getDoubleValue());
1143       else
1144         mk_ainput(uncorrected_barometric_altitude).unset();
1145     }
1146   if (mk_ainput_feed(barometric_altitude_rate))
1147     // Do not use the vsi, because it is pressure-based only, and
1148     // therefore too laggy for GPWS alerting purposes. I guess that
1149     // a real ADC combines pressure-based and inerta-based altitude
1150     // rates to provide a non-laggy rate. That non-laggy rate is
1151     // best emulated by /velocities/vertical-speed-fps * 60.
1152     mk_ainput(barometric_altitude_rate).set(mk_node(vs)->getDoubleValue() * 60);
1153   if (mk_ainput_feed(radio_altitude))
1154     {
1155       double agl;
1156       if (conf.use_gear_altitude)
1157         agl = mk_node(altitude_gear_agl)->getDoubleValue();
1158       else
1159         agl = mk_node(altitude_agl)->getDoubleValue();
1160       // Some flight models may return negative values when on the
1161       // ground or after a crash; do not allow them.
1162       mk_ainput(radio_altitude).set(SG_MAX2(0.0, agl));
1163     }
1164   if (mk_ainput_feed(glideslope_deviation))
1165     {
1166       if (mk_node(nav0_serviceable)->getBoolValue()
1167           && mk_node(nav0_gs_serviceable)->getBoolValue()
1168           && mk_node(nav0_in_range)->getBoolValue()
1169           && mk_node(nav0_has_gs)->getBoolValue())
1170         // gs-needle-deflection is expressed in degrees, and 1 dot =
1171         // 0.32 degrees (according to
1172         // http://www.ntsb.gov/Recs/letters/2000/A00_41_45.pdf)
1173         mk_ainput(glideslope_deviation).set(mk_node(nav0_gs_needle_deflection)->getDoubleValue() / 0.32 * GLIDESLOPE_DOTS_TO_DDM);
1174       else
1175         mk_ainput(glideslope_deviation).unset();
1176     }
1177   if (mk_ainput_feed(roll_angle))
1178     {
1179       if (conf.use_attitude_indicator)
1180       {
1181         // read data from attitude indicator instrument (requires vacuum system to work)
1182       if (mk_node(ai_serviceable)->getBoolValue() && ! mk_node(ai_caged)->getBoolValue())
1183         mk_ainput(roll_angle).set(mk_node(ai_roll)->getDoubleValue());
1184       else
1185         mk_ainput(roll_angle).unset();
1186     }
1187       else
1188       {
1189         // use simulator source
1190         mk_ainput(roll_angle).set(mk_node(orientation_roll)->getDoubleValue());
1191       }
1192     }
1193   if (mk_ainput_feed(localizer_deviation))
1194     {
1195       if (mk_node(nav0_serviceable)->getBoolValue()
1196           && mk_node(nav0_cdi_serviceable)->getBoolValue()
1197           && mk_node(nav0_in_range)->getBoolValue()
1198           && mk_node(nav0_nav_loc)->getBoolValue())
1199         // heading-needle-deflection is expressed in degrees, and 1
1200         // dot = 2 degrees (0.5 degrees for ILS, but navradio.cxx
1201         // performs the conversion)
1202         mk_ainput(localizer_deviation).set(mk_node(nav0_heading_needle_deflection)->getDoubleValue() / 2 * LOCALIZER_DOTS_TO_DDM);
1203       else
1204         mk_ainput(localizer_deviation).unset();
1205     }
1206   if (mk_ainput_feed(computed_airspeed))
1207     {
1208       if (mk_node(asi_serviceable)->getBoolValue())
1209         mk_ainput(computed_airspeed).set(mk_node(asi_speed)->getDoubleValue());
1210       else
1211         mk_ainput(computed_airspeed).unset();
1212     }
1213
1214   // 2. compute data
1215
1216   mk_data(decision_height).set(&mk_ainput(decision_height));
1217   mk_data(radio_altitude).set(&mk_ainput(radio_altitude));
1218   mk_data(roll_angle).set(&mk_ainput(roll_angle));
1219
1220   // update barometric_altitude_rate as per [SPEC] 6.2.1: "During
1221   // normal conditions, the system will base Mode 1 computations upon
1222   // barometric rate from the ADC. If this computed data is not valid
1223   // or available then the system will use internally computed
1224   // barometric altitude rate."
1225
1226   if (! mk_ainput(barometric_altitude_rate).ncd)
1227     // the altitude rate input is valid, use it
1228     mk_data(barometric_altitude_rate).set(mk_ainput(barometric_altitude_rate).get());
1229   else
1230     {
1231       bool has = false;
1232
1233       // The altitude rate input is invalid. We can compute an
1234       // altitude rate if all the following conditions are true:
1235       //
1236       //   - the altitude input is valid
1237       //   - there is an altitude sample with age >= 1 second
1238       //   - that altitude sample is valid
1239
1240       if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1241         {
1242           if (! altitude_samples.empty() && ! altitude_samples.front().value.ncd)
1243             {
1244               double elapsed = globals->get_sim_time_sec() - altitude_samples.front().timestamp;
1245               if (elapsed >= 1)
1246                 {
1247                   has = true;
1248                   mk_data(barometric_altitude_rate).set((mk_ainput(uncorrected_barometric_altitude).get() - altitude_samples.front().value.get()) / elapsed * 60);
1249                 }
1250             }
1251         }
1252
1253       if (! has)
1254         mk_data(barometric_altitude_rate).unset();
1255     }
1256
1257   altitude_samples.push_back(Sample< Parameter<double> >(mk_ainput(uncorrected_barometric_altitude)));
1258
1259   // Erase everything from the beginning of the list up to the sample
1260   // preceding the most recent sample whose age is >= 1 second.
1261
1262   altitude_samples_type::iterator erase_last = altitude_samples.begin();
1263   altitude_samples_type::iterator iter;
1264
1265   for (iter = altitude_samples.begin(); iter != altitude_samples.end(); iter++)
1266     if (globals->get_sim_time_sec() - (*iter).timestamp >= 1)
1267       erase_last = iter;
1268     else
1269       break;
1270
1271   if (erase_last != altitude_samples.begin())
1272     altitude_samples.erase(altitude_samples.begin(), erase_last);
1273
1274   // update GPS data
1275
1276   if (conf.use_internal_gps)
1277     {
1278       mk_data(gps_altitude).set(mk_node(altitude)->getDoubleValue());
1279       mk_data(gps_latitude).set(mk_node(latitude)->getDoubleValue());
1280       mk_data(gps_longitude).set(mk_node(longitude)->getDoubleValue());
1281       mk_data(gps_vertical_figure_of_merit).set(0.0);
1282     }
1283   else
1284     {
1285       mk_data(gps_altitude).set(&mk_ainput(gps_altitude));
1286       mk_data(gps_latitude).set(&mk_ainput(gps_latitude));
1287       mk_data(gps_longitude).set(&mk_ainput(gps_longitude));
1288       mk_data(gps_vertical_figure_of_merit).set(&mk_ainput(gps_vertical_figure_of_merit));
1289     }
1290
1291   // update glideslope and localizer
1292
1293   mk_data(glideslope_deviation_dots).set(&mk_ainput(glideslope_deviation), GLIDESLOPE_DDM_TO_DOTS);
1294   mk_data(localizer_deviation_dots).set(&mk_ainput(localizer_deviation), LOCALIZER_DDM_TO_DOTS);
1295
1296   // Update geometric altitude; [SPEC] 6.7.8 provides an overview of a
1297   // complex algorithm which combines several input sources to
1298   // calculate the geometric altitude. Since the exact algorithm is
1299   // not given, we fallback to a much simpler method.
1300
1301   if (! mk_data(gps_altitude).ncd)
1302     mk_data(geometric_altitude).set(mk_data(gps_altitude).get());
1303   else if (! mk_ainput(uncorrected_barometric_altitude).ncd)
1304     mk_data(geometric_altitude).set(mk_ainput(uncorrected_barometric_altitude).get());
1305   else
1306     mk_data(geometric_altitude).unset();
1307
1308   // update terrain clearance
1309
1310   update_terrain_clearance();
1311
1312   // 3. perform sanity checks
1313
1314   if (! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 0)
1315     mk_data(radio_altitude).unset();
1316
1317   if (! mk_data(decision_height).ncd && mk_data(decision_height).get() < 0)
1318     mk_data(decision_height).unset();
1319
1320   if (! mk_data(gps_latitude).ncd
1321       && (mk_data(gps_latitude).get() < -90
1322           || mk_data(gps_latitude).get() > 90))
1323     mk_data(gps_latitude).unset();
1324
1325   if (! mk_data(gps_longitude).ncd
1326       && (mk_data(gps_longitude).get() < -180
1327           || mk_data(gps_longitude).get() > 180))
1328     mk_data(gps_longitude).unset();
1329
1330   if (! mk_data(roll_angle).ncd
1331       && ((mk_data(roll_angle).get() < -180)
1332           || (mk_data(roll_angle).get() > 180)))
1333     mk_data(roll_angle).unset();
1334
1335   // 4. process input feeders requiring data computed above
1336
1337   if (mk_dinput_feed(decision_height))
1338     mk_dinput(decision_height) = ! mk_data(radio_altitude).ncd
1339       && ! mk_data(decision_height).ncd
1340       && mk_data(radio_altitude).get() <= mk_data(decision_height).get();
1341 }
1342
1343 void
1344 MK_VIII::IOHandler::update_terrain_clearance ()
1345 {
1346   if (! mk_data(radio_altitude).ncd)
1347     mk_data(terrain_clearance).set(terrain_clearance_filter.update(mk_data(radio_altitude).get()));
1348   else
1349     mk_data(terrain_clearance).unset();
1350 }
1351
1352 void
1353 MK_VIII::IOHandler::reset_terrain_clearance ()
1354 {
1355   terrain_clearance_filter.reset();
1356   update_terrain_clearance();
1357 }
1358
1359 void
1360 MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
1361 {
1362   if (test)
1363     {
1364       if (! mk->fault_handler.faults[fault])
1365         mk->fault_handler.set_fault(fault);
1366     }
1367   else
1368     mk->fault_handler.unset_fault(fault);
1369 }
1370
1371 void
1372 MK_VIII::IOHandler::handle_input_fault (bool test,
1373                                         Timer *timer,
1374                                         double max_duration,
1375                                         FaultHandler::Fault fault)
1376 {
1377   if (test)
1378     {
1379       if (! mk->fault_handler.faults[fault])
1380         {
1381           if (timer->start_or_elapsed() >= max_duration)
1382             mk->fault_handler.set_fault(fault);
1383         }
1384     }
1385   else
1386     {
1387       mk->fault_handler.unset_fault(fault);
1388       timer->stop();
1389     }
1390 }
1391
1392 void
1393 MK_VIII::IOHandler::update_input_faults ()
1394 {
1395   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1396     return;
1397
1398   // [INSTALL] 3.15.1.3
1399   handle_input_fault(mk_dinput(audio_inhibit),
1400                      &audio_inhibit_fault_timer,
1401                      60,
1402                      FaultHandler::FAULT_ALL_MODES_INHIBIT);
1403
1404   // [INSTALL] appendix E 6.6.2
1405   handle_input_fault(mk_dinput(landing_gear)
1406                      && ! mk_ainput(computed_airspeed).ncd
1407                      && mk_ainput(computed_airspeed).get() > conf.faults->max_gear_down_airspeed,
1408                      &landing_gear_fault_timer,
1409                      60,
1410                      FaultHandler::FAULT_GEAR_SWITCH);
1411
1412   // [INSTALL] appendix E 6.6.4
1413   handle_input_fault(flaps_down()
1414                      && ! mk_ainput(computed_airspeed).ncd
1415                      && mk_ainput(computed_airspeed).get() > conf.faults->max_flaps_down_airspeed,
1416                      &flaps_down_fault_timer,
1417                      60,
1418                      FaultHandler::FAULT_FLAPS_SWITCH);
1419
1420   // [INSTALL] appendix E 6.6.6
1421   if (conf.momentary_flap_override_enabled)
1422     handle_input_fault(mk_dinput(momentary_flap_override),
1423                        &momentary_flap_override_fault_timer,
1424                        15,
1425                        FaultHandler::FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID);
1426
1427   // [INSTALL] appendix E 6.6.7
1428   handle_input_fault(mk_dinput(self_test),
1429                      &self_test_fault_timer,
1430                      60,
1431                      FaultHandler::FAULT_SELF_TEST_INVALID);
1432
1433   // [INSTALL] appendix E 6.6.13
1434   handle_input_fault(mk_dinput(glideslope_cancel),
1435                      &glideslope_cancel_fault_timer,
1436                      15,
1437                      FaultHandler::FAULT_GLIDESLOPE_CANCEL_INVALID);
1438
1439   // [INSTALL] appendix E 6.6.25
1440   if (momentary_steep_approach_enabled())
1441     handle_input_fault(mk_dinput(steep_approach),
1442                        &steep_approach_fault_timer,
1443                        15,
1444                        FaultHandler::FAULT_STEEP_APPROACH_INVALID);
1445
1446   // [INSTALL] appendix E 6.6.27
1447   if (conf.gpws_inhibit_enabled)
1448     handle_input_fault(mk_dinput(gpws_inhibit),
1449                        &gpws_inhibit_fault_timer,
1450                        5,
1451                        FaultHandler::FAULT_GPWS_INHIBIT);
1452
1453   // [INSTALL] does not specify a fault for this one, but it makes
1454   // sense to have it behave like GPWS inhibit
1455   handle_input_fault(mk_dinput(ta_tcf_inhibit),
1456                      &ta_tcf_inhibit_fault_timer,
1457                      5,
1458                      FaultHandler::FAULT_TA_TCF_INHIBIT);
1459
1460   // [PILOT] page 48: "In the event that required data for a
1461   // particular function is not available, then that function is
1462   // automatically inhibited and annunciated"
1463
1464   handle_input_fault(mk_data(radio_altitude).ncd
1465                      || mk_ainput(uncorrected_barometric_altitude).ncd
1466                      || mk_data(barometric_altitude_rate).ncd
1467                      || mk_ainput(computed_airspeed).ncd
1468                      || mk_data(terrain_clearance).ncd,
1469                      FaultHandler::FAULT_MODES14_INPUTS_INVALID);
1470
1471   if (! mk_dinput(glideslope_inhibit))
1472     handle_input_fault(mk_data(radio_altitude).ncd,
1473                        FaultHandler::FAULT_MODE5_INPUTS_INVALID);
1474
1475   if (mk->mode6_handler.altitude_callouts_enabled())
1476     handle_input_fault(mk->mode6_handler.conf.above_field_voice
1477                        ? (mk_data(gps_latitude).ncd
1478                           || mk_data(gps_longitude).ncd
1479                           || mk_data(geometric_altitude).ncd)
1480                        : mk_data(radio_altitude).ncd,
1481                        FaultHandler::FAULT_MODE6_INPUTS_INVALID);
1482
1483   if (mk->mode6_handler.conf.bank_angle_enabled)
1484     handle_input_fault(mk_data(roll_angle).ncd,
1485                        FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID);
1486
1487   if (mk->tcf_handler.conf.enabled)
1488     handle_input_fault(mk_data(radio_altitude).ncd
1489                        || mk_data(geometric_altitude).ncd
1490                        || mk_data(gps_latitude).ncd
1491                        || mk_data(gps_longitude).ncd
1492                        || mk_data(gps_vertical_figure_of_merit).ncd,
1493                        FaultHandler::FAULT_TCF_INPUTS_INVALID);
1494 }
1495
1496 void
1497 MK_VIII::IOHandler::update_alternate_discrete_input (bool *ptr)
1498 {
1499   assert(mk->system_handler.state == SystemHandler::STATE_ON);
1500
1501   if (ptr == &mk_dinput(mode6_low_volume))
1502     {
1503       if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1504           && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1505         mk->mode6_handler.set_volume(*ptr ? modify_amplitude(1.0, -6) : 1.0);
1506     }
1507   else if (ptr == &mk_dinput(audio_inhibit))
1508     {
1509       if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1510           && mk->self_test_handler.state == SelfTestHandler::STATE_NONE)
1511         mk->voice_player.set_volume(*ptr ? 0.0 : mk->voice_player.conf.volume);
1512     }
1513 }
1514
1515 void
1516 MK_VIII::IOHandler::update_internal_latches ()
1517 {
1518   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1519     return;
1520
1521   // [SPEC] 6.2.1
1522   if (conf.momentary_flap_override_enabled
1523       && mk_doutput(flap_override)
1524       && ! mk->state_handler.takeoff
1525       && (mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() <= 50))
1526     mk_doutput(flap_override) = false;
1527
1528   // [SPEC] 6.2.1
1529   if (momentary_steep_approach_enabled())
1530     {
1531       if (mk_doutput(steep_approach)
1532           && ! mk->state_handler.takeoff
1533           && ((last_landing_gear && ! mk_dinput(landing_gear))
1534               || (last_real_flaps_down && ! real_flaps_down())))
1535         // gear or flaps raised during approach: it's a go-around
1536         mk_doutput(steep_approach) = false;
1537
1538       last_landing_gear = mk_dinput(landing_gear);
1539       last_real_flaps_down = real_flaps_down();
1540     }
1541
1542   // [SPEC] 6.2.5
1543   if (mk_doutput(glideslope_cancel)
1544       && (mk_data(glideslope_deviation_dots).ncd
1545           || mk_data(radio_altitude).ncd
1546           || mk_data(radio_altitude).get() > 2000
1547           || mk_data(radio_altitude).get() < 30))
1548     mk_doutput(glideslope_cancel) = false;
1549 }
1550
1551 void
1552 MK_VIII::IOHandler::update_egpws_alert_discrete_1 ()
1553 {
1554   if (mk->voice_player.voice)
1555     {
1556       const struct
1557       {
1558         int                     bit;
1559         VoicePlayer::Voice      *voice;
1560       } voices[] = {
1561         { 11, mk_voice(sink_rate_pause_sink_rate) },
1562         { 12, mk_voice(pull_up) },
1563         { 13, mk_voice(terrain) },
1564         { 13, mk_voice(terrain_pause_terrain) },
1565         { 14, mk_voice(dont_sink_pause_dont_sink) },
1566         { 15, mk_voice(too_low_gear) },
1567         { 16, mk_voice(too_low_flaps) },
1568         { 17, mk_voice(too_low_terrain) },
1569         { 18, mk_voice(soft_glideslope) },
1570         { 18, mk_voice(hard_glideslope) },
1571         { 19, mk_voice(minimums_minimums) }
1572       };
1573
1574       for (size_t i = 0; i < n_elements(voices); i++)
1575         if (voices[i].voice == mk->voice_player.voice)
1576           {
1577             mk_aoutput(egpws_alert_discrete_1) = 1 << voices[i].bit;
1578             return;
1579           }
1580     }
1581
1582   mk_aoutput(egpws_alert_discrete_1) = 0;
1583 }
1584
1585 void
1586 MK_VIII::IOHandler::update_egpwc_logic_discretes ()
1587 {
1588   mk_aoutput(egpwc_logic_discretes) = 0;
1589
1590   if (mk->state_handler.takeoff)
1591     mk_aoutput(egpwc_logic_discretes) |= 1 << 18;
1592
1593   static const struct
1594   {
1595     int                 bit;
1596     unsigned int        alerts;
1597   } logic[] = {
1598     { 13, mk_alert(TCF_TOO_LOW_TERRAIN) },
1599     { 19, mk_alert(MODE1_SINK_RATE) },
1600     { 20, mk_alert(MODE1_PULL_UP) },
1601     { 21, mk_alert(MODE2A_PREFACE) | mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B_LANDING_MODE) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING) },
1602     { 22, mk_alert(MODE2A) | mk_alert(MODE2B) },
1603     { 23, mk_alert(MODE3) },
1604     { 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) },
1605     { 25, mk_alert(MODE5_SOFT) | mk_alert(MODE5_HARD) }
1606   };
1607
1608   for (size_t i = 0; i < n_elements(logic); i++)
1609     if (mk_test_alerts(logic[i].alerts))
1610       mk_aoutput(egpwc_logic_discretes) |= 1 << logic[i].bit;
1611 }
1612
1613 void
1614 MK_VIII::IOHandler::update_mode6_callouts_discrete_1 ()
1615 {
1616   if (mk->voice_player.voice)
1617     {
1618       const struct
1619       {
1620         int                     bit;
1621         VoicePlayer::Voice      *voice;
1622       } voices[] = {
1623         { 11, mk_voice(minimums_minimums) },
1624         { 16, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_10) },
1625         { 17, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_20) },
1626         { 18, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_30) },
1627         { 19, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_40) },
1628         { 20, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_50) },
1629         { 23, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_100) },
1630         { 24, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_200) },
1631         { 25, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_300) }
1632       };
1633
1634       for (size_t i = 0; i < n_elements(voices); i++)
1635         if (voices[i].voice == mk->voice_player.voice)
1636           {
1637             mk_aoutput(mode6_callouts_discrete_1) = 1 << voices[i].bit;
1638             return;
1639           }
1640     }
1641
1642   mk_aoutput(mode6_callouts_discrete_1) = 0;
1643 }
1644
1645 void
1646 MK_VIII::IOHandler::update_mode6_callouts_discrete_2 ()
1647 {
1648   if (mk->voice_player.voice)
1649     {
1650       const struct
1651       {
1652         int                     bit;
1653         VoicePlayer::Voice      *voice;
1654       } voices[] = {
1655         { 11, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_400) },
1656         { 12, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500) },
1657         { 13, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_1000) },
1658         { 18, mk_voice(bank_angle_pause_bank_angle) },
1659         { 18, mk_voice(bank_angle_pause_bank_angle_3) },
1660         { 23, mk_voice(five_hundred_above) }
1661       };
1662
1663       for (size_t i = 0; i < n_elements(voices); i++)
1664         if (voices[i].voice == mk->voice_player.voice)
1665           {
1666             mk_aoutput(mode6_callouts_discrete_2) = 1 << voices[i].bit;
1667             return;
1668           }
1669     }
1670
1671   mk_aoutput(mode6_callouts_discrete_2) = 0;
1672 }
1673
1674 void
1675 MK_VIII::IOHandler::update_egpws_alert_discrete_2 ()
1676 {
1677   mk_aoutput(egpws_alert_discrete_2) = 1 << 27; // Terrain NA
1678
1679   if (mk_doutput(glideslope_cancel))
1680     mk_aoutput(egpws_alert_discrete_2) |= 1 << 11;
1681   if (mk_doutput(gpws_alert))
1682     mk_aoutput(egpws_alert_discrete_2) |= 1 << 12;
1683   if (mk_doutput(gpws_warning))
1684     mk_aoutput(egpws_alert_discrete_2) |= 1 << 13;
1685   if (mk_doutput(gpws_inop))
1686     mk_aoutput(egpws_alert_discrete_2) |= 1 << 14;
1687   if (mk_doutput(audio_on))
1688     mk_aoutput(egpws_alert_discrete_2) |= 1 << 16;
1689   if (mk_doutput(tad_inop))
1690     mk_aoutput(egpws_alert_discrete_2) |= 1 << 24;
1691   if (mk->fault_handler.has_faults())
1692     mk_aoutput(egpws_alert_discrete_2) |= 1 << 25;
1693 }
1694
1695 void
1696 MK_VIII::IOHandler::update_egpwc_alert_discrete_3 ()
1697 {
1698   mk_aoutput(egpwc_alert_discrete_3) = 1 << 17 | 1 << 18;
1699
1700   if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
1701     mk_aoutput(egpwc_alert_discrete_3) |= 1 << 11;
1702   if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
1703     mk_aoutput(egpwc_alert_discrete_3) |= 1 << 12;
1704   if (mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
1705     mk_aoutput(egpwc_alert_discrete_3) |= 1 << 13;
1706   if (mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
1707     mk_aoutput(egpwc_alert_discrete_3) |= 1 << 14;
1708   if (mk_doutput(tad_inop))
1709     mk_aoutput(egpwc_alert_discrete_3) |= 1 << 16;
1710 }
1711
1712 void
1713 MK_VIII::IOHandler::update_outputs ()
1714 {
1715   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1716     return;
1717
1718   mk_doutput(audio_on) = ! mk_dinput(audio_inhibit)
1719     && mk->voice_player.voice
1720     && ! mk->voice_player.voice->element->silence;
1721
1722   update_egpws_alert_discrete_1();
1723   update_egpwc_logic_discretes();
1724   update_mode6_callouts_discrete_1();
1725   update_mode6_callouts_discrete_2();
1726   update_egpws_alert_discrete_2();
1727   update_egpwc_alert_discrete_3();
1728 }
1729
1730 bool *
1731 MK_VIII::IOHandler::get_lamp_output (Lamp lamp)
1732 {
1733   switch (lamp)
1734     {
1735     case LAMP_GLIDESLOPE:
1736       return &mk_doutput(gpws_alert);
1737
1738     case LAMP_CAUTION:
1739       return conf.lamp->format2 ? &mk_doutput(gpws_alert) : &mk_doutput(gpws_warning);
1740
1741     case LAMP_WARNING:
1742       return &mk_doutput(gpws_warning);
1743
1744     default:
1745       assert_not_reached();
1746       return NULL;
1747     }
1748 }
1749
1750 void
1751 MK_VIII::IOHandler::update_lamps ()
1752 {
1753   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
1754     return;
1755
1756   if (_lamp != LAMP_NONE && conf.lamp->flashing)
1757     {
1758       // [SPEC] 6.9.3: 70 cycles per minute
1759       if (lamp_timer.elapsed() >= 60.0 / 70.0 / 2.0)
1760         {
1761           bool *output = get_lamp_output(_lamp);
1762           *output = ! *output;
1763           lamp_timer.start();
1764         }
1765     }
1766 }
1767
1768 void
1769 MK_VIII::IOHandler::set_lamp (Lamp lamp)
1770 {
1771   if (lamp == _lamp)
1772     return;
1773
1774   _lamp = lamp;
1775
1776   mk_doutput(gpws_warning) = false;
1777   mk_doutput(gpws_alert) = false;
1778
1779   if (lamp != LAMP_NONE)
1780     {
1781       *get_lamp_output(lamp) = true;
1782       lamp_timer.start();
1783     }
1784 }
1785
1786 bool
1787 MK_VIII::IOHandler::gpws_inhibit () const
1788 {
1789   return conf.gpws_inhibit_enabled && mk_dinput(gpws_inhibit);
1790 }
1791
1792 bool
1793 MK_VIII::IOHandler::real_flaps_down () const
1794 {
1795   return conf.flap_reversal ? mk_dinput(landing_flaps) : ! mk_dinput(landing_flaps);
1796 }
1797
1798 bool
1799 MK_VIII::IOHandler::flaps_down () const
1800 {
1801   return flap_override() ? true : real_flaps_down();
1802 }
1803
1804 bool
1805 MK_VIII::IOHandler::flap_override () const
1806 {
1807   return conf.momentary_flap_override_enabled ? mk_doutput(flap_override) : false;
1808 }
1809
1810 bool
1811 MK_VIII::IOHandler::steep_approach () const
1812 {
1813   if (conf.steep_approach_enabled)
1814     // If alternate action was configured in category 13, we return
1815     // the value of the input discrete (which should be connected to a
1816     // latching steep approach cockpit switch). Otherwise, we return
1817     // the value of the output discrete.
1818     return conf.alternate_steep_approach ? mk_dinput(steep_approach) : mk_doutput(steep_approach);
1819   else
1820     return false;
1821 }
1822
1823 bool
1824 MK_VIII::IOHandler::momentary_steep_approach_enabled () const
1825 {
1826   return conf.steep_approach_enabled && ! conf.alternate_steep_approach;
1827 }
1828
1829 void
1830 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1831                                const char *name,
1832                                bool *input,
1833                                bool *feed)
1834 {
1835   mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(), RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
1836   if (feed)
1837     mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
1838 }
1839
1840 void
1841 MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
1842                                const char *name,
1843                                Parameter<double> *input,
1844                                bool *feed)
1845 {
1846   mk->properties_handler.tie(node, (string("inputs/arinc429/") + name).c_str(), SGRawValuePointer<double>(input->get_pointer()));
1847   mk->properties_handler.tie(node, (string("inputs/arinc429/") + name + "-ncd").c_str(), SGRawValuePointer<bool>(&input->ncd));
1848   if (feed)
1849     mk->properties_handler.tie(node, (string("input-feeders/arinc429/") + name).c_str(), SGRawValuePointer<bool>(feed));
1850 }
1851
1852 void
1853 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1854                                 const char *name,
1855                                 bool *output)
1856 {
1857   SGPropertyNode *child = node->getNode((string("outputs/discretes/") + name).c_str(), true);
1858
1859   mk->properties_handler.tie(child, SGRawValuePointer<bool>(output));
1860   child->setAttribute(SGPropertyNode::WRITE, false);
1861 }
1862
1863 void
1864 MK_VIII::IOHandler::tie_output (SGPropertyNode *node,
1865                                 const char *name,
1866                                 int *output)
1867 {
1868   SGPropertyNode *child = node->getNode((string("outputs/arinc429/") + name).c_str(), true);
1869
1870   mk->properties_handler.tie(child, SGRawValuePointer<int>(output));
1871   child->setAttribute(SGPropertyNode::WRITE, false);
1872 }
1873
1874 void
1875 MK_VIII::IOHandler::bind (SGPropertyNode *node)
1876 {
1877   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));
1878
1879   tie_input(node, "landing-gear", &mk_dinput(landing_gear), &mk_dinput_feed(landing_gear));
1880   tie_input(node, "landing-flaps", &mk_dinput(landing_flaps), &mk_dinput_feed(landing_flaps));
1881   tie_input(node, "momentary-flap-override", &mk_dinput(momentary_flap_override));
1882   tie_input(node, "self-test", &mk_dinput(self_test));
1883   tie_input(node, "glideslope-inhibit", &mk_dinput(glideslope_inhibit), &mk_dinput_feed(glideslope_inhibit));
1884   tie_input(node, "glideslope-cancel", &mk_dinput(glideslope_cancel));
1885   tie_input(node, "decision-height", &mk_dinput(decision_height), &mk_dinput_feed(decision_height));
1886   tie_input(node, "mode6-low-volume", &mk_dinput(mode6_low_volume));
1887   tie_input(node, "audio-inhibit", &mk_dinput(audio_inhibit));
1888   tie_input(node, "ta-tcf-inhibit", &mk_dinput(ta_tcf_inhibit));
1889   tie_input(node, "autopilot-engaged", &mk_dinput(autopilot_engaged), &mk_dinput_feed(autopilot_engaged));
1890   tie_input(node, "steep-approach", &mk_dinput(steep_approach));
1891   tie_input(node, "gpws-inhibit", &mk_dinput(gpws_inhibit));
1892
1893   tie_input(node, "uncorrected-barometric-altitude", &mk_ainput(uncorrected_barometric_altitude), &mk_ainput_feed(uncorrected_barometric_altitude));
1894   tie_input(node, "barometric-altitude-rate", &mk_ainput(barometric_altitude_rate), &mk_ainput_feed(barometric_altitude_rate));
1895   tie_input(node, "gps-altitude", &mk_ainput(gps_altitude));
1896   tie_input(node, "gps-latitude", &mk_ainput(gps_latitude));
1897   tie_input(node, "gps-longitude", &mk_ainput(gps_longitude));
1898   tie_input(node, "gps-vertical-figure-of-merit", &mk_ainput(gps_vertical_figure_of_merit));
1899   tie_input(node, "radio-altitude", &mk_ainput(radio_altitude), &mk_ainput_feed(radio_altitude));
1900   tie_input(node, "glideslope-deviation", &mk_ainput(glideslope_deviation), &mk_ainput_feed(glideslope_deviation));
1901   tie_input(node, "roll-angle", &mk_ainput(roll_angle), &mk_ainput_feed(roll_angle));
1902   tie_input(node, "localizer-deviation", &mk_ainput(localizer_deviation), &mk_ainput_feed(localizer_deviation));
1903   tie_input(node, "computed-airspeed", &mk_ainput(computed_airspeed), &mk_ainput_feed(computed_airspeed));
1904   tie_input(node, "decision-height", &mk_ainput(decision_height), &mk_ainput_feed(decision_height));
1905
1906   tie_output(node, "gpws-warning", &mk_doutput(gpws_warning));
1907   tie_output(node, "gpws-alert", &mk_doutput(gpws_alert));
1908   tie_output(node, "audio-on", &mk_doutput(audio_on));
1909   tie_output(node, "gpws-inop", &mk_doutput(gpws_inop));
1910   tie_output(node, "tad-inop", &mk_doutput(tad_inop));
1911   tie_output(node, "flap-override", &mk_doutput(flap_override));
1912   tie_output(node, "glideslope-cancel", &mk_doutput(glideslope_cancel));
1913   tie_output(node, "steep-approach", &mk_doutput(steep_approach));
1914
1915   tie_output(node, "egpws-alert-discrete-1", &mk_aoutput(egpws_alert_discrete_1));
1916   tie_output(node, "egpwc-logic-discretes", &mk_aoutput(egpwc_logic_discretes));
1917   tie_output(node, "mode6-callouts-discrete-1", &mk_aoutput(mode6_callouts_discrete_1));
1918   tie_output(node, "mode6-callouts-discrete-2", &mk_aoutput(mode6_callouts_discrete_2));
1919   tie_output(node, "egpws-alert-discrete-2", &mk_aoutput(egpws_alert_discrete_2));
1920   tie_output(node, "egpwc-alert-discrete-3", &mk_aoutput(egpwc_alert_discrete_3));
1921 }
1922
1923 bool
1924 MK_VIII::IOHandler::get_discrete_input (bool *ptr) const
1925 {
1926    return *ptr;
1927 }
1928
1929 void
1930 MK_VIII::IOHandler::set_discrete_input (bool *ptr, bool value)
1931 {
1932   if (value == *ptr)
1933     return;
1934
1935   if (mk->system_handler.state == SystemHandler::STATE_ON)
1936     {
1937       if (ptr == &mk_dinput(momentary_flap_override))
1938         {
1939           if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1940               && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1941               && conf.momentary_flap_override_enabled
1942               && value)
1943             mk_doutput(flap_override) = ! mk_doutput(flap_override);
1944         }
1945       else if (ptr == &mk_dinput(self_test))
1946         mk->self_test_handler.handle_button_event(value);
1947       else if (ptr == &mk_dinput(glideslope_cancel))
1948         {
1949           if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1950               && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1951               && value)
1952             {
1953               if (! mk_doutput(glideslope_cancel))
1954                 {
1955                   // [SPEC] 6.2.5
1956
1957                   // Although we are not called from update(), we are
1958                   // sure that the inputs we use here are defined,
1959                   // since state is STATE_ON.
1960
1961                   if (! mk_data(glideslope_deviation_dots).ncd
1962                       && ! mk_data(radio_altitude).ncd
1963                       && mk_data(radio_altitude).get() <= 2000
1964                       && mk_data(radio_altitude).get() >= 30)
1965                     mk_doutput(glideslope_cancel) = true;
1966                 }
1967               // else do nothing ([PILOT] page 22: "Glideslope Cancel
1968               // can not be deselected (reset) by again pressing the
1969               // Glideslope Cancel switch.")
1970             }
1971         }
1972       else if (ptr == &mk_dinput(steep_approach))
1973         {
1974           if (mk->configuration_module.state == ConfigurationModule::STATE_OK
1975               && mk->self_test_handler.state == SelfTestHandler::STATE_NONE
1976               && momentary_steep_approach_enabled()
1977               && value)
1978             mk_doutput(steep_approach) = ! mk_doutput(steep_approach);
1979         }
1980     }
1981
1982   *ptr = value;
1983
1984   if (mk->system_handler.state == SystemHandler::STATE_ON)
1985     update_alternate_discrete_input(ptr);
1986 }
1987
1988 void
1989 MK_VIII::IOHandler::present_status_section (const char *name)
1990 {
1991   printf("%s\n", name);
1992 }
1993
1994 void
1995 MK_VIII::IOHandler::present_status_item (const char *name, const char *value)
1996 {
1997   if (value)
1998     printf("\t%-32s %s\n", name, value);
1999   else
2000     printf("\t%s\n", name);
2001 }
2002
2003 void
2004 MK_VIII::IOHandler::present_status_subitem (const char *name)
2005 {
2006   printf("\t\t%s\n", name);
2007 }
2008
2009 void
2010 MK_VIII::IOHandler::present_status ()
2011 {
2012   // [SPEC] 6.10.13
2013
2014   if (mk->system_handler.state != SystemHandler::STATE_ON)
2015     return;
2016
2017   present_status_section("EGPWC CONFIGURATION");
2018   present_status_item("PART NUMBER:", "965-1220-000"); // [SPEC] 1.1
2019   present_status_item("MOD STATUS:", "N/A");
2020   present_status_item("SERIAL NUMBER:", "N/A");
2021   printf("\n");
2022   present_status_item("APPLICATION S/W VERSION:", FLIGHTGEAR_VERSION);
2023   present_status_item("TERRAIN DATABASE VERSION:", FLIGHTGEAR_VERSION);
2024   present_status_item("ENVELOPE MOD DATABASE VERSION:", "N/A");
2025   present_status_item("BOOT CODE VERSION:", FLIGHTGEAR_VERSION);
2026   printf("\n");
2027
2028   present_status_section("CURRENT FAULTS");
2029   if (mk->configuration_module.state == ConfigurationModule::STATE_OK && ! mk->fault_handler.has_faults())
2030     present_status_item("NO FAULTS");
2031   else
2032     {
2033       if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2034         {
2035           present_status_item("GPWS COMPUTER FAULTS:");
2036           switch (mk->configuration_module.state)
2037             {
2038             case ConfigurationModule::STATE_INVALID_DATABASE:
2039               present_status_subitem("APPLICATION DATABASE FAILED");
2040               break;
2041
2042             case ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE:
2043               present_status_subitem("CONFIGURATION TYPE INVALID");
2044               break;
2045
2046             default:
2047               assert_not_reached();
2048               break;
2049             }
2050         }
2051       else
2052         {
2053           present_status_item("GPWS COMPUTER OK");
2054           present_status_item("GPWS EXTERNAL FAULTS:");
2055
2056           static const char *fault_names[] = {
2057             "ALL MODES INHIBIT",
2058             "GEAR SWITCH",
2059             "FLAPS SWITCH",
2060             "MOMENTARY FLAP OVERRIDE INVALID",
2061             "SELF TEST INVALID",
2062             "GLIDESLOPE CANCEL INVALID",
2063             "STEEP APPROACH INVALID",
2064             "GPWS INHIBIT",
2065             "TA & TCF INHIBIT",
2066             "MODES 1-4 INPUTS INVALID",
2067             "MODE 5 INPUTS INVALID",
2068             "MODE 6 INPUTS INVALID",
2069             "BANK ANGLE INPUTS INVALID",
2070             "TCF INPUTS INVALID"
2071           };
2072
2073           for (size_t i = 0; i < n_elements(fault_names); i++)
2074             if (mk->fault_handler.faults[i])
2075               present_status_subitem(fault_names[i]);
2076         }
2077     }
2078   printf("\n");
2079
2080   present_status_section("CONFIGURATION:");
2081
2082   static const char *category_names[] = {
2083     "AIRCRAFT TYPE",
2084     "AIR DATA TYPE",
2085     "POSITION INPUT TYPE",
2086     "CALLOUTS OPTION TYPE",
2087     "AUDIO MENU TYPE",
2088     "TERRAIN DISPLAY TYPE",
2089     "OPTIONS 1 TYPE",
2090     "RADIO ALTITUDE TYPE",
2091     "NAVIGATION INPUT TYPE",
2092     "ATTITUDE TYPE",
2093     "MAGNETIC HEADING TYPE",
2094     "WINDSHEAR INPUT TYPE",
2095     "IO DISCRETE TYPE",
2096     "VOLUME SELECT"
2097   };
2098
2099   for (size_t i = 0; i < n_elements(category_names); i++)
2100     {
2101       std::ostringstream value;
2102       value << "= " << mk->configuration_module.effective_categories[i];
2103       present_status_item(category_names[i], value.str().c_str());
2104     }
2105 }
2106
2107 bool
2108 MK_VIII::IOHandler::get_present_status () const
2109 {
2110    return false;
2111 }
2112
2113 void
2114 MK_VIII::IOHandler::set_present_status (bool value)
2115
2116    if (value) present_status();
2117 }
2118
2119
2120 ///////////////////////////////////////////////////////////////////////////////
2121 // VoicePlayer ////////////////////////////////////////////////////////////////
2122 ///////////////////////////////////////////////////////////////////////////////
2123
2124 void
2125 MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
2126 {
2127   // uses xmlsound property names
2128   tie(node, "volume", &volume);
2129   tie(node, "pitch", &pitch);
2130 }
2131
2132 void
2133 MK_VIII::VoicePlayer::Speaker::update_configuration ()
2134 {
2135   map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
2136   for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
2137     {
2138       SGSoundSample *sample = (*iter).second;
2139
2140       sample->set_pitch(pitch);
2141     }
2142
2143   if (player->voice)
2144     player->voice->volume_changed();
2145 }
2146
2147 MK_VIII::VoicePlayer::Voice::~Voice ()
2148 {
2149   for (iter = elements.begin(); iter != elements.end(); iter++)
2150     delete *iter;               // we owned the element
2151   elements.clear();
2152 }
2153
2154 void
2155 MK_VIII::VoicePlayer::Voice::play ()
2156 {
2157   iter = elements.begin();
2158   element = *iter;
2159
2160   element->play(get_volume());
2161 }
2162
2163 void
2164 MK_VIII::VoicePlayer::Voice::stop (bool now)
2165 {
2166   if (element)
2167     {
2168       if (now || element->silence)
2169         {
2170           element->stop();
2171           element = NULL;
2172         }
2173       else
2174         iter = elements.end() - 1; // stop after the current element finishes
2175     }
2176 }
2177
2178 void
2179 MK_VIII::VoicePlayer::Voice::set_volume (float _volume)
2180 {
2181   volume = _volume;
2182   volume_changed();
2183 }
2184
2185 void
2186 MK_VIII::VoicePlayer::Voice::volume_changed ()
2187 {
2188   if (element)
2189     element->set_volume(get_volume());
2190 }
2191
2192 void
2193 MK_VIII::VoicePlayer::Voice::update ()
2194 {
2195   if (element)
2196     {
2197       if (! element->is_playing())
2198         {
2199           if (++iter == elements.end())
2200             element = NULL;
2201           else
2202             {
2203               element = *iter;
2204               element->play(get_volume());
2205             }
2206         }
2207     }
2208 }
2209
2210 MK_VIII::VoicePlayer::~VoicePlayer ()
2211 {
2212   vector<Voice *>::iterator iter1;
2213   for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
2214     delete *iter1;
2215   _voices.clear();
2216   samples.clear();
2217 }
2218
2219 void
2220 MK_VIII::VoicePlayer::init ()
2221 {
2222 #define STDPAUSE 0.75   // [SPEC] 6.4.4: "the standard 0.75 second delay"
2223
2224   SGSoundMgr *smgr = globals->get_soundmgr();
2225   _sgr = smgr->find("avionics", true);
2226   _sgr->tie_to_listener();
2227
2228   make_voice(&voices.application_data_base_failed, "application-data-base-failed");
2229   make_voice(&voices.bank_angle, "bank-angle");
2230   make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
2231   make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
2232   make_voice(&voices.bank_angle_inop, "bank-angle-inop");
2233   make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
2234   make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
2235   make_voice(&voices.callouts_inop, "callouts-inop");
2236   make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
2237   make_voice(&voices.dont_sink, "dont-sink");
2238   make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
2239   make_voice(&voices.five_hundred_above, "500-above");
2240   make_voice(&voices.glideslope, "glideslope");
2241   make_voice(&voices.glideslope_inop, "glideslope-inop");
2242   make_voice(&voices.gpws_inop, "gpws-inop");
2243   make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
2244   make_voice(&voices.minimums, "minimums");
2245   make_voice(&voices.minimums_minimums, "minimums", "minimums");
2246   make_voice(&voices.pull_up, "pull-up");
2247   make_voice(&voices.sink_rate, "sink-rate");
2248   make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
2249   make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
2250   make_voice(&voices.terrain, "terrain");
2251   make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
2252   make_voice(&voices.too_low_flaps, "too-low-flaps");
2253   make_voice(&voices.too_low_gear, "too-low-gear");
2254   make_voice(&voices.too_low_terrain, "too-low-terrain");
2255
2256   for (unsigned i = 0; i < n_altitude_callouts; i++)
2257     {
2258       std::ostringstream name;
2259       name << "altitude-" << mk->mode6_handler.altitude_callout_definitions[i];
2260       make_voice(&voices.altitude_callouts[i], name.str().c_str());
2261     }
2262
2263   speaker.update_configuration();
2264 }
2265
2266 SGSoundSample *
2267 MK_VIII::VoicePlayer::get_sample (const char *name)
2268 {
2269   std::ostringstream refname;
2270   refname << mk->name << "[" << mk->num << "]" << "/" << name;
2271
2272   SGSoundSample *sample = _sgr->find(refname.str());
2273   if (! sample)
2274     {
2275       string filename = "Sounds/mk-viii" + string(name) + ".wav";
2276       try
2277         {
2278           sample = new SGSoundSample(filename.c_str(), SGPath());
2279         }
2280       catch (const sg_exception &e)
2281         {
2282           SG_LOG(SG_INSTR, SG_ALERT, "Error loading MK VIII sound sample \"" + filename + "\": " + e.getFormattedMessage());
2283           exit(1);
2284         }
2285
2286       _sgr->add(sample, refname.str());
2287       samples[refname.str()] = sample;
2288     }
2289
2290   return sample;
2291 }
2292
2293 void
2294 MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
2295 {
2296   if (test_bits(flags, PLAY_NOW) || ! voice || voice->element->silence)
2297     {
2298       if (voice)
2299         voice->stop(true);
2300
2301       voice = _voice;
2302       looped = test_bits(flags, PLAY_LOOPED);
2303
2304       next_voice = NULL;
2305       next_looped = false;
2306
2307       voice->play();
2308     }
2309   else
2310     {
2311       next_voice = _voice;
2312       next_looped = test_bits(flags, PLAY_LOOPED);
2313     }
2314 }
2315
2316 void
2317 MK_VIII::VoicePlayer::stop (unsigned int flags)
2318 {
2319   if (voice)
2320     {
2321       voice->stop(test_bits(flags, STOP_NOW));
2322       if (voice->element)
2323         looped = false;
2324       else
2325         voice = NULL;
2326       next_voice = NULL;
2327     }
2328 }
2329
2330 void
2331 MK_VIII::VoicePlayer::set_volume (float _volume)
2332 {
2333   volume = _volume;
2334   if (voice)
2335     voice->volume_changed();
2336 }
2337
2338 void
2339 MK_VIII::VoicePlayer::update ()
2340 {
2341   if (voice)
2342     {
2343       voice->update();
2344
2345       if (next_voice)
2346         {
2347           if (! voice->element || voice->element->silence)
2348             {
2349               voice = next_voice;
2350               looped = next_looped;
2351
2352               next_voice = NULL;
2353               next_looped = false;
2354
2355               voice->play();
2356             }
2357         }
2358       else
2359         {
2360           if (! voice->element)
2361             {
2362               if (looped)
2363                 voice->play();
2364               else
2365                 voice = NULL;
2366             }
2367         }
2368     }
2369 }
2370
2371 ///////////////////////////////////////////////////////////////////////////////
2372 // SelfTestHandler ////////////////////////////////////////////////////////////
2373 ///////////////////////////////////////////////////////////////////////////////
2374
2375 bool
2376 MK_VIII::SelfTestHandler::_was_here (int position)
2377 {
2378   if (position > current)
2379     {
2380       current = position;
2381       return false;
2382     }
2383   else
2384     return true;
2385 }
2386
2387 MK_VIII::SelfTestHandler::Action
2388 MK_VIII::SelfTestHandler::sleep (double duration)
2389 {
2390   Action _action = { ACTION_SLEEP, duration, NULL };
2391   return _action;
2392 }
2393
2394 MK_VIII::SelfTestHandler::Action
2395 MK_VIII::SelfTestHandler::play (VoicePlayer::Voice *voice)
2396 {
2397   mk->voice_player.play(voice);
2398   Action _action = { ACTION_VOICE, 0, NULL };
2399   return _action;
2400 }
2401
2402 MK_VIII::SelfTestHandler::Action
2403 MK_VIII::SelfTestHandler::discrete_on (bool *discrete, double duration)
2404 {
2405   *discrete = true;
2406   return sleep(duration);
2407 }
2408
2409 MK_VIII::SelfTestHandler::Action
2410 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, double duration)
2411 {
2412   *discrete = true;
2413   Action _action = { ACTION_SLEEP | ACTION_DISCRETE_ON_OFF, duration, discrete };
2414   return _action;
2415 }
2416
2417 MK_VIII::SelfTestHandler::Action
2418 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, VoicePlayer::Voice *voice)
2419 {
2420   *discrete = true;
2421   mk->voice_player.play(voice);
2422   Action _action = { ACTION_VOICE | ACTION_DISCRETE_ON_OFF, 0, discrete };
2423   return _action;
2424 }
2425
2426 MK_VIII::SelfTestHandler::Action
2427 MK_VIII::SelfTestHandler::done ()
2428 {
2429   Action _action = { ACTION_DONE, 0, NULL };
2430   return _action;
2431 }
2432
2433 MK_VIII::SelfTestHandler::Action
2434 MK_VIII::SelfTestHandler::run ()
2435 {
2436   // Note that "Terrain INOP" and "Terrain NA" are or'ed to the same
2437   // output discrete (see [SPEC] 6.9.3.5).
2438
2439 #define was_here()              was_here_offset(0)
2440 #define was_here_offset(offset) _was_here(__LINE__ * 20 + (offset))
2441
2442   if (! was_here())
2443     {
2444       if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_DATABASE)
2445         return play(mk_voice(application_data_base_failed));
2446       else if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE)
2447         return play(mk_voice(configuration_type_invalid));
2448     }
2449   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2450     return done();
2451
2452   if (! was_here())
2453     return discrete_on(&mk_doutput(gpws_inop), 0.7);
2454   if (! was_here())
2455     return sleep(0.7);          // W/S INOP
2456   if (! was_here())
2457     return discrete_on(&mk_doutput(tad_inop), 0.7);
2458   if (! was_here())
2459     {
2460       mk_doutput(gpws_inop) = false;
2461       // do not disable tad_inop since we must enable Terrain NA
2462       // do not disable W/S INOP since we do not emulate it
2463       return sleep(0.7);        // Terrain NA
2464     }
2465   if (! was_here())
2466     {
2467       mk_doutput(tad_inop) = false; // disable Terrain NA
2468       if (mk->io_handler.conf.momentary_flap_override_enabled)
2469         return discrete_on_off(&mk_doutput(flap_override), 1.0);
2470     }
2471   if (! was_here())
2472     return discrete_on_off(&mk_doutput(audio_on), 1.0);
2473   if (! was_here())
2474     {
2475       if (mk->io_handler.momentary_steep_approach_enabled())
2476         return discrete_on_off(&mk_doutput(steep_approach), 1.0);
2477     }
2478
2479   if (! was_here())
2480     {
2481       if (mk_dinput(glideslope_inhibit))
2482         goto glideslope_end;
2483       else
2484         {
2485           if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
2486             goto glideslope_inop;
2487         }
2488     }
2489   if (! was_here())
2490     return discrete_on_off(&mk_doutput(gpws_alert), mk_voice(glideslope));
2491   if (! was_here())
2492     return discrete_on_off(&mk_doutput(glideslope_cancel), 0.7);
2493   goto glideslope_end;
2494  glideslope_inop:
2495   if (! was_here())
2496     return play(mk_voice(glideslope_inop));
2497  glideslope_end:
2498
2499   if (! was_here())
2500     {
2501       if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
2502         goto gpws_inop;
2503     }
2504   if (! was_here())
2505     return discrete_on_off(&mk_doutput(gpws_warning), mk_voice(pull_up));
2506   goto gpws_end;
2507  gpws_inop:
2508   if (! was_here())
2509     return play(mk_voice(gpws_inop));
2510  gpws_end:
2511
2512   if (! was_here())
2513     {
2514       if (mk_dinput(self_test)) // proceed to long self test
2515         goto long_test;
2516     }
2517   if (! was_here())
2518     {
2519       if (mk->mode6_handler.conf.bank_angle_enabled
2520           && mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
2521         return play(mk_voice(bank_angle_inop));
2522     }
2523   if (! was_here())
2524     {
2525       if (mk->mode6_handler.altitude_callouts_enabled()
2526           && mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
2527         return play(mk_voice(callouts_inop));
2528     }
2529   if (! was_here())
2530     return done();
2531
2532  long_test:
2533   if (! was_here())
2534     {
2535       mk_doutput(gpws_inop) = true;
2536       // do not enable W/S INOP, since we do not emulate it
2537       mk_doutput(tad_inop) = true; // Terrain INOP, Terrain NA
2538
2539       return play(mk_voice(sink_rate));
2540     }
2541   if (! was_here())
2542     return play(mk_voice(pull_up));
2543   if (! was_here())
2544     return play(mk_voice(terrain));
2545   if (! was_here())
2546     return play(mk_voice(pull_up));
2547   if (! was_here())
2548     return play(mk_voice(dont_sink));
2549   if (! was_here())
2550     return play(mk_voice(too_low_terrain));
2551   if (! was_here())
2552     return play(mk->mode4_handler.conf.voice_too_low_gear);
2553   if (! was_here())
2554     return play(mk_voice(too_low_flaps));
2555   if (! was_here())
2556     return play(mk_voice(too_low_terrain));
2557   if (! was_here())
2558     return play(mk_voice(glideslope));
2559   if (! was_here())
2560     {
2561       if (mk->mode6_handler.conf.bank_angle_enabled)
2562         return play(mk_voice(bank_angle));
2563     }
2564
2565   if (! was_here())
2566     {
2567       if (! mk->mode6_handler.altitude_callouts_enabled())
2568         goto callouts_disabled;
2569     }
2570   if (! was_here())
2571     {
2572       if (mk->mode6_handler.conf.minimums_enabled)
2573         return play(mk_voice(minimums));
2574     }
2575   if (! was_here())
2576     {
2577       if (mk->mode6_handler.conf.above_field_voice)
2578         return play(mk->mode6_handler.conf.above_field_voice);
2579     }
2580   for (unsigned i = 0; i < n_altitude_callouts; i++)
2581     if (! was_here_offset(i))
2582       {
2583         if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
2584           return play(mk_altitude_voice(i));
2585       }
2586   if (! was_here())
2587     {
2588       if (mk->mode6_handler.conf.smart_500_enabled)
2589         return play(mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500));
2590     }
2591   goto callouts_end;
2592  callouts_disabled:
2593   if (! was_here())
2594     return play(mk_voice(minimums_minimums));
2595  callouts_end:
2596
2597   if (! was_here())
2598     {
2599       if (mk->tcf_handler.conf.enabled)
2600         return play(mk_voice(too_low_terrain));
2601     }
2602
2603   return done();
2604 }
2605
2606 void
2607 MK_VIII::SelfTestHandler::start ()
2608 {
2609   assert(state == STATE_START);
2610
2611   memcpy(&saved_outputs, &mk->io_handler.outputs, sizeof(mk->io_handler.outputs));
2612   memset(&mk->io_handler.outputs, 0, sizeof(mk->io_handler.outputs));
2613
2614   // [SPEC] 6.10.6: "The self-test results are annunciated, at 6db
2615   // lower than the normal audio level selected for the aircraft"
2616   mk->voice_player.set_volume(modify_amplitude(mk->voice_player.conf.volume, -6));
2617
2618   if (mk_dinput(mode6_low_volume))
2619     mk->mode6_handler.set_volume(1.0);
2620
2621   state = STATE_RUNNING;
2622   cancel = CANCEL_NONE;
2623   memset(&action, 0, sizeof(action));
2624   current = 0;
2625 }
2626
2627 void
2628 MK_VIII::SelfTestHandler::stop ()
2629 {
2630   if (state != STATE_NONE)
2631     {
2632       if (state != STATE_START)
2633         {
2634           mk->voice_player.stop(VoicePlayer::STOP_NOW);
2635           mk->voice_player.set_volume(mk_dinput(audio_inhibit) ? 0.0 : mk->voice_player.conf.volume);
2636
2637           if (mk_dinput(mode6_low_volume))
2638             mk->mode6_handler.set_volume(modify_amplitude(1.0, -6));
2639
2640           memcpy(&mk->io_handler.outputs, &saved_outputs, sizeof(mk->io_handler.outputs));
2641         }
2642
2643       button_pressed = false;
2644       state = STATE_NONE;
2645       // reset self-test handler position
2646       current=0;
2647     }
2648 }
2649
2650 void
2651 MK_VIII::SelfTestHandler::handle_button_event (bool value)
2652 {
2653   if (state == STATE_NONE)
2654     {
2655       if (value)
2656         state = STATE_START;
2657     }
2658   else if (state == STATE_START)
2659     {
2660       if (value)
2661         state = STATE_NONE;     // cancel the not-yet-started test
2662     }
2663   else if (cancel == CANCEL_NONE)
2664     {
2665       if (value)
2666         {
2667           assert(! button_pressed);
2668           button_pressed = true;
2669           button_press_timestamp = globals->get_sim_time_sec();
2670         }
2671       else
2672         {
2673           if (button_pressed)
2674             {
2675               if (globals->get_sim_time_sec() - button_press_timestamp < 2)
2676                 cancel = CANCEL_SHORT;
2677               else
2678                 cancel = CANCEL_LONG;
2679             }
2680         }
2681     }
2682 }
2683
2684 bool
2685 MK_VIII::SelfTestHandler::update ()
2686 {
2687   if (state == STATE_NONE)
2688     return false;
2689   else if (state == STATE_START)
2690     {
2691       if (mk->state_handler.ground && ! mk->io_handler.steep_approach())
2692         start();
2693       else
2694         {
2695           state = STATE_NONE;
2696           return false;
2697         }
2698     }
2699   else
2700     {
2701       if (button_pressed && cancel == CANCEL_NONE)
2702         {
2703           if (globals->get_sim_time_sec() - button_press_timestamp >= 2)
2704             cancel = CANCEL_LONG;
2705         }
2706     }
2707
2708   if (! mk->state_handler.ground || cancel != CANCEL_NONE)
2709     {
2710       stop();
2711       return false;
2712     }
2713
2714   if (test_bits(action.flags, ACTION_SLEEP))
2715     {
2716       if (globals->get_sim_time_sec() - sleep_start < action.sleep_duration)
2717         return true;
2718     }
2719   if (test_bits(action.flags, ACTION_VOICE))
2720     {
2721       if (mk->voice_player.voice)
2722         return true;
2723     }
2724   if (test_bits(action.flags, ACTION_DISCRETE_ON_OFF))
2725     *action.discrete = false;
2726
2727   action = run();
2728
2729   if (test_bits(action.flags, ACTION_SLEEP))
2730     sleep_start = globals->get_sim_time_sec();
2731   if (test_bits(action.flags, ACTION_DONE))
2732     {
2733       stop();
2734       return false;
2735     }
2736
2737   return true;
2738 }
2739
2740 ///////////////////////////////////////////////////////////////////////////////
2741 // AlertHandler ///////////////////////////////////////////////////////////////
2742 ///////////////////////////////////////////////////////////////////////////////
2743
2744 bool
2745 MK_VIII::AlertHandler::select_voice_alerts (unsigned int test)
2746 {
2747   if (has_alerts(test))
2748     {
2749       voice_alerts = alerts & test;
2750       return true;
2751     }
2752   else
2753     {
2754       voice_alerts &= ~test;
2755       if (voice_alerts == 0)
2756         mk->voice_player.stop();
2757
2758       return false;
2759     }
2760 }
2761
2762 void
2763 MK_VIII::AlertHandler::boot ()
2764 {
2765   reset();
2766 }
2767
2768 void
2769 MK_VIII::AlertHandler::reposition ()
2770 {
2771   reset();
2772
2773   mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2774   mk->voice_player.stop(VoicePlayer::STOP_NOW);
2775 }
2776
2777 void
2778 MK_VIII::AlertHandler::reset ()
2779 {
2780   alerts = 0;
2781   old_alerts = 0;
2782   voice_alerts = 0;
2783   repeated_alerts = 0;
2784   altitude_callout_voice = NULL;
2785 }
2786
2787 void
2788 MK_VIII::AlertHandler::update ()
2789 {
2790   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2791     return;
2792
2793   // Lamps and voices are prioritized according to [SPEC] 6.9.2.
2794   //
2795   // Voices which can interrupt other voices (VoicePlayer::PLAY_NOW)
2796   // are specified by [INSTALL] appendix E 5.3.5.
2797   //
2798   // When a voice is overriden by a higher priority voice and the
2799   // overriding voice stops, we restore the previous voice if it was
2800   // looped (this is not specified by [SPEC] but seems to make sense).
2801
2802   // update lamp
2803
2804   if (has_alerts(ALERT_MODE1_PULL_UP | ALERT_MODE2A | ALERT_MODE2B))
2805     mk->io_handler.set_lamp(IOHandler::LAMP_WARNING);
2806   else if (has_alerts(ALERT_MODE1_SINK_RATE
2807                       | ALERT_MODE2A_PREFACE
2808                       | ALERT_MODE2B_PREFACE
2809                       | ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING
2810                       | ALERT_MODE2A_ALTITUDE_GAIN
2811                       | ALERT_MODE2B_LANDING_MODE
2812                       | ALERT_MODE3
2813                       | ALERT_MODE4_TOO_LOW_FLAPS
2814                       | ALERT_MODE4_TOO_LOW_GEAR
2815                       | ALERT_MODE4AB_TOO_LOW_TERRAIN
2816                       | ALERT_MODE4C_TOO_LOW_TERRAIN
2817                       | ALERT_TCF_TOO_LOW_TERRAIN))
2818     mk->io_handler.set_lamp(IOHandler::LAMP_CAUTION);
2819   else if (has_alerts(ALERT_MODE5_SOFT | ALERT_MODE5_HARD))
2820     mk->io_handler.set_lamp(IOHandler::LAMP_GLIDESLOPE);
2821   else
2822     mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2823
2824   // update voice
2825
2826   if (select_voice_alerts(ALERT_MODE1_PULL_UP))
2827     {
2828       if (! has_old_alerts(ALERT_MODE1_PULL_UP))
2829         {
2830           if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate))
2831             mk->voice_player.play(mk_voice(sink_rate), VoicePlayer::PLAY_NOW);
2832           mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_LOOPED);
2833         }
2834     }
2835   else if (select_voice_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2836     {
2837       if (! has_old_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2838         mk->voice_player.play(mk_voice(terrain_pause_terrain), VoicePlayer::PLAY_NOW);
2839     }
2840   else if (select_voice_alerts(ALERT_MODE2A | ALERT_MODE2B))
2841     {
2842       if (mk->voice_player.voice != mk_voice(pull_up))
2843         mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_NOW | VoicePlayer::PLAY_LOOPED);
2844     }
2845   else if (select_voice_alerts(ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING | ALERT_MODE2B_LANDING_MODE))
2846     {
2847       if (mk->voice_player.voice != mk_voice(terrain))
2848         mk->voice_player.play(mk_voice(terrain), VoicePlayer::PLAY_LOOPED);
2849     }
2850   else if (select_voice_alerts(ALERT_MODE6_MINIMUMS))
2851     {
2852       if (! has_old_alerts(ALERT_MODE6_MINIMUMS))
2853         mk->voice_player.play(mk_voice(minimums_minimums));
2854     }
2855   else if (select_voice_alerts(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2856     {
2857       if (must_play_voice(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2858         mk->voice_player.play(mk_voice(too_low_terrain));
2859     }
2860   else if (select_voice_alerts(ALERT_TCF_TOO_LOW_TERRAIN))
2861     {
2862       if (must_play_voice(ALERT_TCF_TOO_LOW_TERRAIN))
2863         mk->voice_player.play(mk_voice(too_low_terrain));
2864     }
2865   else if (select_voice_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2866     {
2867       if (! has_old_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2868         {
2869           assert(altitude_callout_voice != NULL);
2870           mk->voice_player.play(altitude_callout_voice);
2871         }
2872     }
2873   else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
2874     {
2875       if (must_play_voice(ALERT_MODE4_TOO_LOW_GEAR))
2876         mk->voice_player.play(mk->mode4_handler.conf.voice_too_low_gear);
2877     }
2878   else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_FLAPS))
2879     {
2880       if (must_play_voice(ALERT_MODE4_TOO_LOW_FLAPS))
2881         mk->voice_player.play(mk_voice(too_low_flaps));
2882     }
2883   else if (select_voice_alerts(ALERT_MODE1_SINK_RATE))
2884     {
2885       if (must_play_voice(ALERT_MODE1_SINK_RATE))
2886         mk->voice_player.play(mk_voice(sink_rate_pause_sink_rate));
2887       // [SPEC] 6.2.1: "During the time that the voice message for the
2888       // outer envelope is inhibited and the caution/warning lamp is
2889       // on, the Mode 5 alert message is allowed to annunciate for
2890       // excessive glideslope deviation conditions."
2891       else if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate)
2892                && mk->voice_player.next_voice != mk_voice(sink_rate_pause_sink_rate))
2893         {
2894           if (has_alerts(ALERT_MODE5_HARD))
2895             {
2896               voice_alerts |= ALERT_MODE5_HARD;
2897               if (mk->voice_player.voice != mk_voice(hard_glideslope))
2898                 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2899             }
2900           else if (has_alerts(ALERT_MODE5_SOFT))
2901             {
2902               voice_alerts |= ALERT_MODE5_SOFT;
2903               if (must_play_voice(ALERT_MODE5_SOFT))
2904                 mk->voice_player.play(mk_voice(soft_glideslope));
2905             }
2906         }
2907     }
2908   else if (select_voice_alerts(ALERT_MODE3))
2909     {
2910       if (must_play_voice(ALERT_MODE3))
2911         mk->voice_player.play(mk_voice(dont_sink_pause_dont_sink));
2912     }
2913   else if (select_voice_alerts(ALERT_MODE5_HARD))
2914     {
2915       if (mk->voice_player.voice != mk_voice(hard_glideslope))
2916         mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2917     }
2918   else if (select_voice_alerts(ALERT_MODE5_SOFT))
2919     {
2920       if (must_play_voice(ALERT_MODE5_SOFT))
2921         mk->voice_player.play(mk_voice(soft_glideslope));
2922     }
2923   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_3))
2924     {
2925       if (mk->voice_player.voice != mk_voice(bank_angle_bank_angle_3))
2926         mk->voice_player.play(mk_voice(bank_angle_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2927     }
2928   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_3))
2929     {
2930       if (mk->voice_player.voice != mk_voice(bank_angle_pause_bank_angle_3))
2931         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2932     }
2933   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2))
2934     {
2935       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2936         mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2937     }
2938   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_2))
2939     {
2940       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2941         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2942     }
2943   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1))
2944     {
2945       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2946         mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2947     }
2948   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_1))
2949     {
2950       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2951         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2952     }
2953
2954   // set new state
2955
2956   old_alerts = voice_alerts;
2957   repeated_alerts = 0;
2958   altitude_callout_voice = NULL;
2959 }
2960
2961 void
2962 MK_VIII::AlertHandler::set_alerts (unsigned int _alerts,
2963                                    unsigned int flags,
2964                                    VoicePlayer::Voice *_altitude_callout_voice)
2965 {
2966   alerts |= _alerts;
2967   if (test_bits(flags, ALERT_FLAG_REPEAT))
2968     repeated_alerts |= _alerts;
2969   if (_altitude_callout_voice)
2970     altitude_callout_voice = _altitude_callout_voice;
2971 }
2972
2973 void
2974 MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
2975 {
2976   alerts &= ~_alerts;
2977   repeated_alerts &= ~_alerts;
2978 }
2979
2980 ///////////////////////////////////////////////////////////////////////////////
2981 // StateHandler ///////////////////////////////////////////////////////////////
2982 ///////////////////////////////////////////////////////////////////////////////
2983
2984 void
2985 MK_VIII::StateHandler::update_ground ()
2986 {
2987   if (ground)
2988     {
2989       if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2990           && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30)
2991         {
2992           if (potentially_airborne_timer.start_or_elapsed() > 1)
2993             leave_ground();
2994         }
2995       else
2996         potentially_airborne_timer.stop();
2997     }
2998   else
2999     {
3000       if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() < 40
3001           && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 30)
3002         enter_ground();
3003     }
3004 }
3005
3006 void
3007 MK_VIII::StateHandler::enter_ground ()
3008 {
3009   ground = true;
3010   mk->io_handler.enter_ground();
3011
3012   // [SPEC] 6.0.1 does not specify this, but it seems to be an
3013   // omission; at this point, we must make sure that we are in takeoff
3014   // mode (otherwise the next takeoff might happen in approach mode).
3015   if (! takeoff)
3016     enter_takeoff();
3017 }
3018
3019 void
3020 MK_VIII::StateHandler::leave_ground ()
3021 {
3022   ground = false;
3023   mk->mode2_handler.leave_ground();
3024 }
3025
3026 void
3027 MK_VIII::StateHandler::update_takeoff ()
3028 {
3029   if (takeoff)
3030     {
3031       // [SPEC] 6.0.2 is somewhat confusing: it mentions hardcoded
3032       // terrain clearance (500, 750) and airspeed (178, 200) values,
3033       // but it also mentions the mode 4A boundary, which varies as a
3034       // function of aircraft type. We consider that the mentioned
3035       // values are examples, and that we should use the mode 4A upper
3036       // boundary.
3037
3038       if (! mk_data(terrain_clearance).ncd
3039           && ! mk_ainput(computed_airspeed).ncd
3040           && mk_data(terrain_clearance).get() > mk->mode4_handler.get_upper_agl(mk->mode4_handler.conf.modes->ac))
3041         leave_takeoff();
3042     }
3043   else
3044     {
3045       if (! mk_data(radio_altitude).ncd
3046           && mk_data(radio_altitude).get() < mk->mode4_handler.conf.modes->b->min_agl1
3047           && mk->io_handler.flaps_down()
3048           && mk_dinput(landing_gear))
3049         enter_takeoff();
3050     }
3051 }
3052
3053 void
3054 MK_VIII::StateHandler::enter_takeoff ()
3055 {
3056   takeoff = true;
3057   mk->io_handler.enter_takeoff();
3058   mk->mode2_handler.enter_takeoff();
3059   mk->mode3_handler.enter_takeoff();
3060   mk->mode6_handler.enter_takeoff();
3061 }
3062
3063 void
3064 MK_VIII::StateHandler::leave_takeoff ()
3065 {
3066   takeoff = false;
3067   mk->mode6_handler.leave_takeoff();
3068 }
3069
3070 void
3071 MK_VIII::StateHandler::post_reposition ()
3072 {
3073   // Synchronize the state with the current situation.
3074
3075   bool _ground = !
3076     (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
3077      && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30);
3078
3079   bool _takeoff = _ground;
3080
3081   if (ground && ! _ground)
3082     leave_ground();
3083   else if (! ground && _ground)
3084     enter_ground();
3085
3086   if (takeoff && ! _takeoff)
3087     leave_takeoff();
3088   else if (! takeoff && _takeoff)
3089     enter_takeoff();
3090 }
3091
3092 void
3093 MK_VIII::StateHandler::update ()
3094 {
3095   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3096     return;
3097
3098   update_ground();
3099   update_takeoff();
3100 }
3101
3102 ///////////////////////////////////////////////////////////////////////////////
3103 // Mode1Handler ///////////////////////////////////////////////////////////////
3104 ///////////////////////////////////////////////////////////////////////////////
3105
3106 double
3107 MK_VIII::Mode1Handler::get_pull_up_bias ()
3108 {
3109   // [PILOT] page 54: "Steep Approach has priority over Flap Override
3110   // if selected simultaneously."
3111
3112   if (mk->io_handler.steep_approach())
3113     return 200;
3114   else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3115     return 300;
3116   else
3117     return 0;
3118 }
3119
3120 bool
3121 MK_VIII::Mode1Handler::is_pull_up ()
3122 {
3123   if (! mk->io_handler.gpws_inhibit()
3124       && ! mk->state_handler.ground // [1]
3125       && ! mk_data(radio_altitude).ncd
3126       && ! mk_data(barometric_altitude_rate).ncd
3127       && mk_data(radio_altitude).get() > conf.envelopes->min_agl)
3128     {
3129       if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl1)
3130         {
3131           if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl1(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3132             return true;
3133         }
3134       else if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl2)
3135         {
3136           if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl2(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3137             return true;
3138         }
3139     }
3140
3141   return false;
3142 }
3143
3144 void
3145 MK_VIII::Mode1Handler::update_pull_up ()
3146 {
3147   if (is_pull_up())
3148     {
3149       if (! mk_test_alert(MODE1_PULL_UP))
3150         {
3151           // [SPEC] 6.2.1: at least one sink rate must be issued
3152           // before switching to pull up; if no sink rate has
3153           // occurred, a 0.2 second delay is used.
3154           if (mk->voice_player.voice == mk_voice(sink_rate_pause_sink_rate)
3155               || pull_up_timer.start_or_elapsed() >= 0.2)
3156             mk_set_alerts(mk_alert(MODE1_PULL_UP));
3157         }
3158     }
3159   else
3160     {
3161       pull_up_timer.stop();
3162       mk_unset_alerts(mk_alert(MODE1_PULL_UP));
3163     }
3164 }
3165
3166 double
3167 MK_VIII::Mode1Handler::get_sink_rate_bias ()
3168 {
3169   // [PILOT] page 54: "Steep Approach has priority over Flap Override
3170   // if selected simultaneously."
3171
3172   if (mk->io_handler.steep_approach())
3173     return 500;
3174   else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3175     return 300;
3176   else if (! mk_data(glideslope_deviation_dots).ncd)
3177     {
3178       double bias = 0;
3179
3180       if (mk_data(glideslope_deviation_dots).get() <= -2)
3181         bias = 300;
3182       else if (mk_data(glideslope_deviation_dots).get() < 0)
3183         bias = -150 * mk_data(glideslope_deviation_dots).get();
3184
3185       if (mk_data(radio_altitude).get() < 100)
3186         bias *= 0.01 * mk_data(radio_altitude).get();
3187
3188       return bias;
3189     }
3190   else
3191     return 0;
3192 }
3193
3194 bool
3195 MK_VIII::Mode1Handler::is_sink_rate ()
3196 {
3197   return ! mk->io_handler.gpws_inhibit()
3198     && ! mk->state_handler.ground // [1]
3199     && ! mk_data(radio_altitude).ncd
3200     && ! mk_data(barometric_altitude_rate).ncd
3201     && mk_data(radio_altitude).get() > conf.envelopes->min_agl
3202     && mk_data(radio_altitude).get() < 2450
3203     && mk_data(radio_altitude).get() < -572 - 0.6035 * (mk_data(barometric_altitude_rate).get() + get_sink_rate_bias());
3204 }
3205
3206 double
3207 MK_VIII::Mode1Handler::get_sink_rate_tti ()
3208 {
3209   return mk_data(radio_altitude).get() / fabs(mk_data(barometric_altitude_rate).get());
3210 }
3211
3212 void
3213 MK_VIII::Mode1Handler::update_sink_rate ()
3214 {
3215   if (is_sink_rate())
3216     {
3217       if (mk_test_alert(MODE1_SINK_RATE))
3218         {
3219           double tti = get_sink_rate_tti();
3220           if (tti <= sink_rate_tti * 0.8)
3221             {
3222               // ~20% degradation, warn again and store the new reference tti
3223               sink_rate_tti = tti;
3224               mk_repeat_alert(mk_alert(MODE1_SINK_RATE));
3225             }
3226         }
3227       else
3228         {
3229           if (sink_rate_timer.start_or_elapsed() >= 0.8)
3230             {
3231               mk_set_alerts(mk_alert(MODE1_SINK_RATE));
3232               sink_rate_tti = get_sink_rate_tti();
3233             }
3234         }
3235     }
3236   else
3237     {
3238       sink_rate_timer.stop();
3239       mk_unset_alerts(mk_alert(MODE1_SINK_RATE));
3240     }
3241 }
3242
3243 void
3244 MK_VIII::Mode1Handler::update ()
3245 {
3246   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3247     return;
3248
3249   update_pull_up();
3250   update_sink_rate();
3251 }
3252
3253 ///////////////////////////////////////////////////////////////////////////////
3254 // Mode2Handler ///////////////////////////////////////////////////////////////
3255 ///////////////////////////////////////////////////////////////////////////////
3256
3257 double
3258 MK_VIII::Mode2Handler::ClosureRateFilter::limit_radio_altitude_rate (double r)
3259 {
3260   // Limit radio altitude rate according to aircraft configuration,
3261   // allowing maximum sensitivity during cruise while providing
3262   // progressively less sensitivity during the landing phases of
3263   // flight.
3264
3265   if (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3266     {                           // gs deviation <= +- 2 dots
3267       if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3268         SG_CLAMP_RANGE(r, -1000.0, 3000.0);
3269       else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3270         SG_CLAMP_RANGE(r, 0.0, 4000.0);
3271       else
3272         SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3273     }
3274   else
3275     {                           // no ILS, or gs deviation > +- 2 dots
3276       if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3277         SG_CLAMP_RANGE(r, 0.0, 4000.0);
3278       else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3279         SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3280       // else no clamp
3281     }
3282
3283   return r;
3284 }
3285
3286 void
3287 MK_VIII::Mode2Handler::ClosureRateFilter::init ()
3288 {
3289   timer.stop();
3290   last_ra.set(&mk_data(radio_altitude));
3291   last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3292   ra_filter.reset();
3293   ba_filter.reset();
3294   output.unset();
3295 }
3296
3297 void
3298 MK_VIII::Mode2Handler::ClosureRateFilter::update ()
3299 {
3300   double elapsed = timer.start_or_elapsed();
3301   if (elapsed < 1)
3302     return;
3303
3304   if (! mk_data(radio_altitude).ncd && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3305     {
3306       if (! last_ra.ncd && ! last_ba.ncd)
3307         {
3308           // compute radio and barometric altitude rates (positive value = descent)
3309           double ra_rate = -(mk_data(radio_altitude).get() - last_ra.get()) / elapsed * 60;
3310           double ba_rate = -(mk_ainput(uncorrected_barometric_altitude).get() - last_ba.get()) / elapsed * 60;
3311
3312           // limit radio altitude rate according to aircraft configuration
3313           ra_rate = limit_radio_altitude_rate(ra_rate);
3314
3315           // apply a low-pass filter to the radio altitude rate
3316           ra_rate = ra_filter.filter(ra_rate);
3317           // apply a high-pass filter to the barometric altitude rate
3318           ba_rate = ba_filter.filter(ba_rate);
3319
3320           // combine both rates to obtain a closure rate
3321           output.set(ra_rate + ba_rate);
3322         }
3323       else
3324         output.unset();
3325     }
3326   else
3327     {
3328       ra_filter.reset();
3329       ba_filter.reset();
3330       output.unset();
3331     }
3332
3333   timer.start();
3334   last_ra.set(&mk_data(radio_altitude));
3335   last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3336 }
3337
3338 bool
3339 MK_VIII::Mode2Handler::b_conditions ()
3340 {
3341   return mk->io_handler.flaps_down()
3342     || (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) < 2)
3343     || takeoff_timer.running;
3344 }
3345
3346 bool
3347 MK_VIII::Mode2Handler::is_a ()
3348 {
3349   if (! mk->io_handler.gpws_inhibit()
3350       && ! mk->state_handler.ground // [1]
3351       && ! mk_data(radio_altitude).ncd
3352       && ! mk_ainput(computed_airspeed).ncd
3353       && ! closure_rate_filter.output.ncd
3354       && ! b_conditions())
3355     {
3356       if (mk_data(radio_altitude).get() < 1220)
3357         {
3358           if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3359             return true;
3360         }
3361       else
3362         {
3363           double upper_limit;
3364
3365           if (mk_ainput(computed_airspeed).get() <= conf->airspeed1)
3366             upper_limit = 1650;
3367           else if (mk_ainput(computed_airspeed).get() >= conf->airspeed2)
3368             upper_limit = 2450;
3369           else
3370             upper_limit = 1650 + 8.9 * (mk_ainput(computed_airspeed).get() - conf->airspeed1);
3371
3372           if (mk_data(radio_altitude).get() < upper_limit)
3373             {
3374               if (mk_data(radio_altitude).get() < 522 + 0.1968 * closure_rate_filter.output.get())
3375                 return true;
3376             }
3377         }
3378     }
3379
3380   return false;
3381 }
3382
3383 bool
3384 MK_VIII::Mode2Handler::is_b ()
3385 {
3386   if (! mk->io_handler.gpws_inhibit()
3387       && ! mk->state_handler.ground // [1]
3388       && ! mk_data(radio_altitude).ncd
3389       && ! mk_data(barometric_altitude_rate).ncd
3390       && ! closure_rate_filter.output.ncd
3391       && b_conditions()
3392       && mk_data(radio_altitude).get() < 789)
3393     {
3394       double lower_limit;
3395
3396       if (mk->io_handler.flaps_down())
3397         {
3398           if (mk_data(barometric_altitude_rate).get() > -400)
3399             lower_limit = 200;
3400           else if (mk_data(barometric_altitude_rate).get() < -1000)
3401             lower_limit = 600;
3402           else
3403             lower_limit = -66.777 - 0.667 * mk_data(barometric_altitude_rate).get();
3404         }
3405       else
3406         lower_limit = 30;
3407
3408       if (mk_data(radio_altitude).get() > lower_limit)
3409         {
3410           if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3411             return true;
3412         }
3413     }
3414
3415   return false;
3416 }
3417
3418 void
3419 MK_VIII::Mode2Handler::check_pull_up (unsigned int preface_alert,
3420                                       unsigned int alert)
3421 {
3422   if (pull_up_timer.running)
3423     {
3424       if (pull_up_timer.elapsed() >= 1)
3425         {
3426           mk_unset_alerts(preface_alert);
3427           mk_set_alerts(alert);
3428         }
3429     }
3430   else
3431     {
3432       if (! mk->voice_player.voice)
3433         pull_up_timer.start();
3434     }
3435 }
3436
3437 void
3438 MK_VIII::Mode2Handler::update_a ()
3439 {
3440   if (is_a())
3441     {
3442       if (mk_test_alert(MODE2A_PREFACE))
3443         check_pull_up(mk_alert(MODE2A_PREFACE), mk_alert(MODE2A));
3444       else if (! mk_test_alert(MODE2A))
3445         {
3446           mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3447           mk_set_alerts(mk_alert(MODE2A_PREFACE));
3448           a_start_time = globals->get_sim_time_sec();
3449           pull_up_timer.stop();
3450         }
3451     }
3452   else
3453     {
3454       if (mk_test_alert(MODE2A_ALTITUDE_GAIN))
3455         {
3456           if (mk->io_handler.gpws_inhibit()
3457               || mk->state_handler.ground // [1]
3458               || a_altitude_gain_timer.elapsed() >= 45
3459               || mk_data(radio_altitude).ncd
3460               || mk_ainput(uncorrected_barometric_altitude).ncd
3461               || mk_ainput(uncorrected_barometric_altitude).get() - a_altitude_gain_alt >= 300
3462               // [PILOT] page 12: "the visual alert will remain on
3463               // until [...] landing flaps or the flap override switch
3464               // is activated"
3465               || mk->io_handler.flaps_down())
3466             {
3467               // exit altitude gain mode
3468               a_altitude_gain_timer.stop();
3469               mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3470             }
3471           else
3472             {
3473               // [SPEC] 6.2.2.2: "If the terrain starts to fall away
3474               // during this altitude gain time, the voice will shut
3475               // off"
3476               if (closure_rate_filter.output.get() < 0)
3477                 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3478             }
3479         }
3480       else if (mk_test_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A)))
3481         {
3482           mk_unset_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A));
3483
3484           if (! mk->io_handler.gpws_inhibit()
3485               && ! mk->state_handler.ground // [1]
3486               && globals->get_sim_time_sec() - a_start_time > 3
3487               && ! mk->io_handler.flaps_down()
3488               && ! mk_data(radio_altitude).ncd
3489               && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3490             {
3491               // [SPEC] 6.2.2.2: mode 2A envelope violated for more
3492               // than 3 seconds, enable altitude gain feature
3493
3494               a_altitude_gain_timer.start();
3495               a_altitude_gain_alt = mk_ainput(uncorrected_barometric_altitude).get();
3496
3497               unsigned int alerts = mk_alert(MODE2A_ALTITUDE_GAIN);
3498               if (closure_rate_filter.output.get() > 0)
3499                 alerts |= mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING);
3500
3501               mk_set_alerts(alerts);
3502             }
3503         }
3504     }
3505 }
3506
3507 void
3508 MK_VIII::Mode2Handler::update_b ()
3509 {
3510   bool b = is_b();
3511
3512   // handle normal mode
3513
3514   if (b && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down()))
3515     {
3516       if (mk_test_alert(MODE2B_PREFACE))
3517         check_pull_up(mk_alert(MODE2B_PREFACE), mk_alert(MODE2B));
3518       else if (! mk_test_alert(MODE2B))
3519         {
3520           mk_set_alerts(mk_alert(MODE2B_PREFACE));
3521           pull_up_timer.stop();
3522         }
3523     }
3524   else
3525     mk_unset_alerts(mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B));
3526
3527   // handle landing mode ([SPEC] 6.2.2.3: "If both landing gear and
3528   // flaps are in the landing configuration, then the message will be
3529   // Terrain")
3530
3531   if (b && mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3532     mk_set_alerts(mk_alert(MODE2B_LANDING_MODE));
3533   else
3534     mk_unset_alerts(mk_alert(MODE2B_LANDING_MODE));
3535 }
3536
3537 void
3538 MK_VIII::Mode2Handler::boot ()
3539 {
3540   closure_rate_filter.init();
3541 }
3542
3543 void
3544 MK_VIII::Mode2Handler::power_off ()
3545 {
3546   // [SPEC] 6.0.4: "This latching function is not power saved and a
3547   // system reset will force it false."
3548   takeoff_timer.stop();
3549 }
3550
3551 void
3552 MK_VIII::Mode2Handler::leave_ground ()
3553 {
3554   // takeoff, reset timer
3555   takeoff_timer.start();
3556 }
3557
3558 void
3559 MK_VIII::Mode2Handler::enter_takeoff ()
3560 {
3561   // reset timer, in case it's a go-around
3562   takeoff_timer.start();
3563 }
3564
3565 void
3566 MK_VIII::Mode2Handler::update ()
3567 {
3568   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3569     return;
3570
3571   closure_rate_filter.update();
3572
3573   if (takeoff_timer.running && takeoff_timer.elapsed() >= 60)
3574     takeoff_timer.stop();
3575
3576   update_a();
3577   update_b();
3578 }
3579
3580 ///////////////////////////////////////////////////////////////////////////////
3581 // Mode3Handler ///////////////////////////////////////////////////////////////
3582 ///////////////////////////////////////////////////////////////////////////////
3583
3584 double
3585 MK_VIII::Mode3Handler::max_alt_loss (double _bias)
3586 {
3587   return conf->max_alt_loss(mk->io_handler.flap_override(), mk_data(radio_altitude).get()) + mk_data(radio_altitude).get() * _bias;
3588 }
3589
3590 double
3591 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
3592 {
3593   if (mk_data(radio_altitude).get() > 0)
3594     while (alt_loss > max_alt_loss(initial_bias))
3595       initial_bias += 0.2;
3596
3597   return initial_bias;
3598 }
3599
3600 bool
3601 MK_VIII::Mode3Handler::is (double *alt_loss)
3602 {
3603   if (has_descent_alt)
3604     {
3605       int max_agl = conf->max_agl(mk->io_handler.flap_override());
3606
3607       if (mk_ainput(uncorrected_barometric_altitude).ncd
3608           || mk_ainput(uncorrected_barometric_altitude).get() > descent_alt
3609           || mk_data(radio_altitude).ncd
3610           || mk_data(radio_altitude).get() > max_agl)
3611         {
3612           armed = false;
3613           has_descent_alt = false;
3614         }
3615       else
3616         {
3617           // gear/flaps: [SPEC] 1.3.1.3
3618           if (! mk->io_handler.gpws_inhibit()
3619               && ! mk->state_handler.ground // [1]
3620               && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3621               && ! mk_data(barometric_altitude_rate).ncd
3622               && ! mk_ainput(uncorrected_barometric_altitude).ncd
3623               && ! mk_data(radio_altitude).ncd
3624               && mk_data(barometric_altitude_rate).get() < 0)
3625             {
3626               double _alt_loss = descent_alt - mk_ainput(uncorrected_barometric_altitude).get();
3627               if (armed || (mk_data(radio_altitude).get() > conf->min_agl
3628                             && mk_data(radio_altitude).get() < max_agl
3629                             && _alt_loss > max_alt_loss(0)))
3630                 {
3631                   *alt_loss = _alt_loss;
3632                   return true;
3633                 }
3634             }
3635         }
3636     }
3637   else
3638     {
3639       if (! mk_data(barometric_altitude_rate).ncd
3640           && ! mk_ainput(uncorrected_barometric_altitude).ncd
3641           && mk_data(barometric_altitude_rate).get() < 0)
3642         {
3643           has_descent_alt = true;
3644           descent_alt = mk_ainput(uncorrected_barometric_altitude).get();
3645         }
3646     }
3647
3648   return false;
3649 }
3650
3651 void
3652 MK_VIII::Mode3Handler::enter_takeoff ()
3653 {
3654   armed = false;
3655   has_descent_alt = false;
3656 }
3657
3658 void
3659 MK_VIII::Mode3Handler::update ()
3660 {
3661   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3662     return;
3663
3664   if (mk->state_handler.takeoff)
3665     {
3666       double alt_loss;
3667
3668       if (! mk->state_handler.ground /* [1] */ && is(&alt_loss))
3669         {
3670           if (mk_test_alert(MODE3))
3671             {
3672               double new_bias = get_bias(bias, alt_loss);
3673               if (new_bias > bias)
3674                 {
3675                   bias = new_bias;
3676                   mk_repeat_alert(mk_alert(MODE3));
3677                 }
3678             }
3679           else
3680             {
3681               armed = true;
3682               bias = get_bias(0.2, alt_loss);
3683               mk_set_alerts(mk_alert(MODE3));
3684             }
3685
3686           return;
3687         }
3688     }
3689
3690   mk_unset_alerts(mk_alert(MODE3));
3691 }
3692
3693 ///////////////////////////////////////////////////////////////////////////////
3694 // Mode4Handler ///////////////////////////////////////////////////////////////
3695 ///////////////////////////////////////////////////////////////////////////////
3696
3697 // FIXME: for turbofans, the upper limit should be reduced from 1000
3698 // to 800 ft if a sudden change in radio altitude is detected, in
3699 // order to reduce nuisance alerts caused by overflying another
3700 // aircraft (see [PILOT] page 16).
3701
3702 double
3703 MK_VIII::Mode4Handler::get_upper_agl (const EnvelopesConfiguration *c)
3704 {
3705   if (mk_ainput(computed_airspeed).get() >= c->airspeed2)
3706     return c->min_agl3;
3707   else if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3708     return c->min_agl2(mk_ainput(computed_airspeed).get());
3709   else
3710     return c->min_agl1;
3711 }
3712
3713 const MK_VIII::Mode4Handler::EnvelopesConfiguration *
3714 MK_VIII::Mode4Handler::get_ab_envelope ()
3715 {
3716   // [SPEC] 6.2.4.1: "The Mode 4B envelope can also be selected by
3717   // setting flaps to landing configuration or by selecting flap
3718   // override."
3719   return mk_dinput(landing_gear) || mk->io_handler.flaps_down()
3720     ? conf.modes->b
3721     : conf.modes->ac;
3722 }
3723
3724 double
3725 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
3726 {
3727   while (mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)
3728     initial_bias += 0.2;
3729
3730   return initial_bias;
3731 }
3732
3733 void
3734 MK_VIII::Mode4Handler::handle_alert (unsigned int alert,
3735                                      double min_agl,
3736                                      double *bias)
3737 {
3738   if (mk_test_alerts(alert))
3739     {
3740       double new_bias = get_bias(*bias, min_agl);
3741       if (new_bias > *bias)
3742         {
3743           *bias = new_bias;
3744           mk_repeat_alert(alert);
3745         }
3746     }
3747   else
3748     {
3749       *bias = get_bias(0.2, min_agl);
3750       mk_set_alerts(alert);
3751     }
3752 }
3753
3754 void
3755 MK_VIII::Mode4Handler::update_ab ()
3756 {
3757   if (! mk->io_handler.gpws_inhibit()
3758       && ! mk->state_handler.ground
3759       && ! mk->state_handler.takeoff
3760       && ! mk_data(radio_altitude).ncd
3761       && ! mk_ainput(computed_airspeed).ncd
3762       && mk_data(radio_altitude).get() > 30)
3763     {
3764       const EnvelopesConfiguration *c = get_ab_envelope();
3765       if (mk_ainput(computed_airspeed).get() < c->airspeed1)
3766         {
3767           if (mk_dinput(landing_gear))
3768             {
3769               if (! mk->io_handler.flaps_down() && mk_data(radio_altitude).get() < c->min_agl1)
3770                 {
3771                   handle_alert(mk_alert(MODE4_TOO_LOW_FLAPS), c->min_agl1, &ab_bias);
3772                   return;
3773                 }
3774             }
3775           else
3776             {
3777               if (mk_data(radio_altitude).get() < c->min_agl1)
3778                 {
3779                   handle_alert(mk_alert(MODE4_TOO_LOW_GEAR), c->min_agl1, &ab_bias);
3780                   return;
3781                 }
3782             }
3783         }
3784     }
3785
3786   mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
3787   ab_bias=0.0;
3788 }
3789
3790 void
3791 MK_VIII::Mode4Handler::update_ab_expanded ()
3792 {
3793   if (! mk->io_handler.gpws_inhibit()
3794       && ! mk->state_handler.ground
3795       && ! mk->state_handler.takeoff
3796       && ! mk_data(radio_altitude).ncd
3797       && ! mk_ainput(computed_airspeed).ncd
3798       && mk_data(radio_altitude).get() > 30)
3799     {
3800       const EnvelopesConfiguration *c = get_ab_envelope();
3801       if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3802         {
3803           double min_agl = get_upper_agl(c);
3804           if (mk_data(radio_altitude).get() < min_agl)
3805             {
3806               handle_alert(mk_alert(MODE4AB_TOO_LOW_TERRAIN), min_agl, &ab_expanded_bias);
3807               return;
3808             }
3809         }
3810     }
3811
3812   mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
3813   ab_expanded_bias=0.0;
3814 }
3815
3816 void
3817 MK_VIII::Mode4Handler::update_c ()
3818 {
3819   if (! mk->io_handler.gpws_inhibit()
3820       && ! mk->state_handler.ground // [1]
3821       && mk->state_handler.takeoff
3822       && ! mk_data(radio_altitude).ncd
3823       && ! mk_data(terrain_clearance).ncd
3824       && mk_data(radio_altitude).get() > 30
3825       && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3826       && mk_data(radio_altitude).get() < get_upper_agl(conf.modes->ac)
3827       && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
3828     handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
3829   else
3830   {
3831     mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
3832     c_bias=0.0;
3833   }
3834 }
3835
3836 void
3837 MK_VIII::Mode4Handler::update ()
3838 {
3839   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3840     return;
3841
3842   update_ab();
3843   update_ab_expanded();
3844   update_c();
3845 }
3846
3847 ///////////////////////////////////////////////////////////////////////////////
3848 // Mode5Handler ///////////////////////////////////////////////////////////////
3849 ///////////////////////////////////////////////////////////////////////////////
3850
3851 bool
3852 MK_VIII::Mode5Handler::is_hard ()
3853 {
3854   if (mk_data(radio_altitude).get() > 30
3855       && mk_data(radio_altitude).get() < 300
3856       && mk_data(glideslope_deviation_dots).get() > 2)
3857     {
3858       if (mk_data(radio_altitude).get() < 150)
3859         {
3860           if (mk_data(radio_altitude).get() > 293 - 71.43 * mk_data(glideslope_deviation_dots).get())
3861             return true;
3862         }
3863       else
3864         return true;
3865     }
3866
3867   return false;
3868 }
3869
3870 bool
3871 MK_VIII::Mode5Handler::is_soft (double bias)
3872 {
3873   if (mk_data(radio_altitude).get() > 30)
3874     {
3875       double bias_dots = 1.3 * bias;
3876       if (mk_data(glideslope_deviation_dots).get() > 1.3 + bias_dots)
3877         {
3878           if (mk_data(radio_altitude).get() < 150)
3879             {
3880               if (mk_data(radio_altitude).get() > 243 - 71.43 * (mk_data(glideslope_deviation_dots).get() - bias_dots))
3881                 return true;
3882             }
3883           else
3884             {
3885               double upper_limit;
3886
3887               if (mk_data(barometric_altitude_rate).ncd)
3888                 upper_limit = 1000;
3889               else
3890                 {
3891                   if (mk_data(barometric_altitude_rate).get() >= 0)
3892                     upper_limit = 500;
3893                   else if (mk_data(barometric_altitude_rate).get() < -500)
3894                     upper_limit = 1000;
3895                   else
3896                     upper_limit = -mk_data(barometric_altitude_rate).get() + 500;
3897                 }
3898
3899               if (mk_data(radio_altitude).get() < upper_limit)
3900                 return true;
3901             }
3902         }
3903     }
3904
3905   return false;
3906 }
3907
3908 double
3909 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
3910 {
3911   while (is_soft(initial_bias))
3912     initial_bias += 0.2;
3913
3914   return initial_bias;
3915 }
3916
3917 void
3918 MK_VIII::Mode5Handler::update_hard (bool is)
3919 {
3920   if (is)
3921     {
3922       if (! mk_test_alert(MODE5_HARD))
3923         {
3924           if (hard_timer.start_or_elapsed() >= 0.8)
3925             mk_set_alerts(mk_alert(MODE5_HARD));
3926         }
3927     }
3928   else
3929     {
3930       hard_timer.stop();
3931       mk_unset_alerts(mk_alert(MODE5_HARD));
3932     }
3933 }
3934
3935 void
3936 MK_VIII::Mode5Handler::update_soft (bool is)
3937 {
3938   if (is)
3939     {
3940       if (! mk_test_alert(MODE5_SOFT))
3941         {
3942           double new_bias = get_soft_bias(soft_bias);
3943           if (new_bias > soft_bias)
3944             {
3945               soft_bias = new_bias;
3946               mk_repeat_alert(mk_alert(MODE5_SOFT));
3947             }
3948         }
3949       else
3950         {
3951           if (soft_timer.start_or_elapsed() >= 0.8)
3952             {
3953               soft_bias = get_soft_bias(0.2);
3954               mk_set_alerts(mk_alert(MODE5_SOFT));
3955             }
3956         }
3957     }
3958   else
3959     {
3960       soft_timer.stop();
3961       mk_unset_alerts(mk_alert(MODE5_SOFT));
3962     }
3963 }
3964
3965 void
3966 MK_VIII::Mode5Handler::update ()
3967 {
3968   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3969     return;
3970
3971   if (! mk->io_handler.gpws_inhibit()
3972       && ! mk->state_handler.ground // [1]
3973       && ! mk_dinput(glideslope_inhibit) // not on backcourse
3974       && ! mk_data(radio_altitude).ncd
3975       && ! mk_data(glideslope_deviation_dots).ncd
3976       && (! mk->io_handler.conf.localizer_enabled
3977           || mk_data(localizer_deviation_dots).ncd
3978           || mk_data(radio_altitude).get() < 500
3979           || fabs(mk_data(localizer_deviation_dots).get()) < 2)
3980       && (! mk->state_handler.takeoff || mk->io_handler.flaps_down())
3981       && mk_dinput(landing_gear)
3982       && ! mk_doutput(glideslope_cancel))
3983     {
3984       update_hard(is_hard());
3985       update_soft(is_soft(0));
3986     }
3987   else
3988     {
3989       update_hard(false);
3990       update_soft(false);
3991     }
3992 }
3993
3994 ///////////////////////////////////////////////////////////////////////////////
3995 // Mode6Handler ///////////////////////////////////////////////////////////////
3996 ///////////////////////////////////////////////////////////////////////////////
3997
3998 // keep sorted in descending order
3999 const int MK_VIII::Mode6Handler::altitude_callout_definitions[] = 
4000   { 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10 };
4001
4002 void
4003 MK_VIII::Mode6Handler::reset_minimums ()
4004 {
4005   minimums_issued = false;
4006 }
4007
4008 void
4009 MK_VIII::Mode6Handler::reset_altitude_callouts ()
4010 {
4011   for (unsigned i = 0; i < n_altitude_callouts; i++)
4012     altitude_callouts_issued[i] = false;
4013 }
4014
4015 bool
4016 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
4017 {
4018   for (unsigned i = 0; i < n_altitude_callouts; i++)
4019     if (mk->voice_player.voice == mk_altitude_voice(i)
4020         || mk->voice_player.next_voice == mk_altitude_voice(i))
4021       return true;
4022
4023   return false;
4024 }
4025
4026 bool
4027 MK_VIII::Mode6Handler::is_near_minimums (double callout)
4028 {
4029   // [SPEC] 6.4.2
4030
4031   if (! mk_data(decision_height).ncd)
4032     {
4033       double diff = callout - mk_data(decision_height).get();
4034
4035       if (mk_data(radio_altitude).get() >= 200)
4036         {
4037           if (fabs(diff) <= 30)
4038             return true;
4039         }
4040       else
4041         {
4042           if (diff >= -3 && diff <= 6)
4043             return true;
4044         }
4045     }
4046
4047   return false;
4048 }
4049
4050 bool
4051 MK_VIII::Mode6Handler::is_outside_band (double elevation, double callout)
4052 {
4053   // [SPEC] 6.4.2
4054   return elevation < callout - (elevation > 150 ? 20 : 10);
4055 }
4056
4057 bool
4058 MK_VIII::Mode6Handler::inhibit_smart_500 ()
4059 {
4060   // [SPEC] 6.4.3
4061
4062   if (! mk_data(glideslope_deviation_dots).ncd
4063       && ! mk_dinput(glideslope_inhibit) // backcourse
4064       && ! mk_doutput(glideslope_cancel))
4065     {
4066       if (mk->io_handler.conf.localizer_enabled
4067           && ! mk_data(localizer_deviation_dots).ncd)
4068         {
4069           if (fabs(mk_data(localizer_deviation_dots).get()) <= 2)
4070             return true;
4071         }
4072       else
4073         {
4074           if (fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
4075             return true;
4076         }
4077     }
4078
4079   return false;
4080 }
4081
4082 void
4083 MK_VIII::Mode6Handler::boot ()
4084 {
4085   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4086     return;
4087
4088   last_decision_height = mk_dinput(decision_height);
4089   last_radio_altitude.set(&mk_data(radio_altitude));
4090
4091   // [SPEC] 6.4.2
4092   for (unsigned i = 0; i < n_altitude_callouts; i++)
4093     altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
4094       && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
4095
4096   // extrapolated from [SPEC] 6.4.2
4097   minimums_issued = mk_dinput(decision_height);
4098
4099   if (conf.above_field_voice)
4100     {
4101       update_runway();
4102       get_altitude_above_field(&last_altitude_above_field);
4103       // extrapolated from [SPEC] 6.4.2
4104       above_field_issued = ! last_altitude_above_field.ncd
4105         && last_altitude_above_field.get() < 550;
4106     }
4107 }
4108
4109 void
4110 MK_VIII::Mode6Handler::power_off ()
4111 {
4112   runway_timer.stop();
4113 }
4114
4115 void
4116 MK_VIII::Mode6Handler::enter_takeoff ()
4117 {
4118   reset_altitude_callouts();    // [SPEC] 6.4.2
4119   reset_minimums();             // omitted by [SPEC]; common sense
4120 }
4121
4122 void
4123 MK_VIII::Mode6Handler::leave_takeoff ()
4124 {
4125   reset_altitude_callouts();    // [SPEC] 6.0.2
4126   reset_minimums();             // [SPEC] 6.0.2
4127 }
4128
4129 void
4130 MK_VIII::Mode6Handler::set_volume (float volume)
4131 {
4132   mk_voice(minimums_minimums)->set_volume(volume);
4133   mk_voice(five_hundred_above)->set_volume(volume);
4134   for (unsigned i = 0; i < n_altitude_callouts; i++)
4135     mk_altitude_voice(i)->set_volume(volume);
4136 }
4137
4138 bool
4139 MK_VIII::Mode6Handler::altitude_callouts_enabled ()
4140 {
4141   if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
4142     return true;
4143
4144   for (unsigned i = 0; i < n_altitude_callouts; i++)
4145     if (conf.altitude_callouts_enabled[i])
4146       return true;
4147
4148   return false;
4149 }
4150
4151 void
4152 MK_VIII::Mode6Handler::update_minimums ()
4153 {
4154   if (! mk->io_handler.gpws_inhibit()
4155       && (mk->voice_player.voice == mk_voice(minimums_minimums)
4156           || mk->voice_player.next_voice == mk_voice(minimums_minimums)))
4157     goto end;
4158
4159   if (! mk->io_handler.gpws_inhibit()
4160       && ! mk->state_handler.ground // [1]
4161       && conf.minimums_enabled
4162       && ! minimums_issued
4163       && mk_dinput(landing_gear)
4164       && mk_dinput(decision_height)
4165       && ! last_decision_height)
4166     {
4167       minimums_issued = true;
4168
4169       // If an altitude callout is playing, stop it now so that the
4170       // minimums callout can be played (note that proper connection
4171       // and synchronization of the radio-altitude ARINC 529 input,
4172       // decision-height discrete and decision-height ARINC 529 input
4173       // will prevent an altitude callout from being played near the
4174       // decision height).
4175
4176       if (is_playing_altitude_callout())
4177         mk->voice_player.stop(VoicePlayer::STOP_NOW);
4178
4179       mk_set_alerts(mk_alert(MODE6_MINIMUMS));
4180     }
4181   else
4182     mk_unset_alerts(mk_alert(MODE6_MINIMUMS));
4183
4184  end:
4185   last_decision_height = mk_dinput(decision_height);
4186 }
4187
4188 void
4189 MK_VIII::Mode6Handler::update_altitude_callouts ()
4190 {
4191   if (! mk->io_handler.gpws_inhibit() && is_playing_altitude_callout())
4192     goto end;
4193
4194   if (! mk->io_handler.gpws_inhibit()
4195       && ! mk->state_handler.ground // [1]
4196       && ! mk_data(radio_altitude).ncd)
4197     for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
4198       if ((conf.altitude_callouts_enabled[i]
4199            || (altitude_callout_definitions[i] == 500
4200                && conf.smart_500_enabled))
4201           && ! altitude_callouts_issued[i]
4202           && (last_radio_altitude.ncd
4203               || last_radio_altitude.get() > altitude_callout_definitions[i]))
4204         {
4205           // lock out all callouts superior or equal to this one
4206           for (unsigned j = 0; j <= i; j++)
4207             altitude_callouts_issued[j] = true;
4208
4209           altitude_callouts_issued[i] = true;
4210           if (! is_near_minimums(altitude_callout_definitions[i])
4211               && ! is_outside_band(mk_data(radio_altitude).get(), altitude_callout_definitions[i])
4212               && (! conf.smart_500_enabled
4213                   || altitude_callout_definitions[i] != 500
4214                   || ! inhibit_smart_500()))
4215             {
4216               mk->alert_handler.set_altitude_callout_alert(mk_altitude_voice(i));
4217               goto end;
4218             }
4219         }
4220
4221   mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4222
4223  end:
4224   last_radio_altitude.set(&mk_data(radio_altitude));
4225 }
4226
4227 bool
4228 MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
4229 {
4230   if (_runway->lengthFt() < mk->conf.runway_database)
4231     return false;
4232
4233   SGGeod pos(
4234     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
4235   
4236   // get distance to threshold
4237   double distance, az1, az2;
4238   SGGeodesy::inverse(pos, _runway->threshold(), az1, az2, distance);
4239   return distance * SG_METER_TO_NM <= 5;
4240 }
4241
4242 bool
4243 MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
4244 {
4245   for (unsigned int r=0; r<airport->numRunways(); ++r) {
4246     FGRunway* rwy(airport->getRunwayByIndex(r));
4247     
4248     if (test_runway(rwy)) return true;
4249   }
4250
4251   return false;
4252 }
4253
4254 bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
4255 {
4256   bool ok = self->test_airport(a);
4257   return ok;
4258 }
4259
4260 void
4261 MK_VIII::Mode6Handler::update_runway ()
4262 {
4263  
4264   if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4265      has_runway.unset();
4266      return;
4267   }
4268
4269   // Search for the closest runway threshold in range 5
4270   // nm. Passing 30nm to
4271   // get_closest_airport() provides enough margin for large
4272   // airports, which may have a runway located far away from the
4273   // airport's reference point.
4274   AirportFilter filter(this);
4275   FGPositionedRef apt = FGPositioned::findClosest(
4276     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4277     30.0, &filter);
4278   if (apt) {
4279     runway.elevation = apt->elevation();
4280   }
4281   
4282   has_runway.set(apt != NULL);
4283 }
4284
4285 void
4286 MK_VIII::Mode6Handler::get_altitude_above_field (Parameter<double> *parameter)
4287 {
4288   if (! has_runway.ncd && has_runway.get() && ! mk_data(geometric_altitude).ncd)
4289     parameter->set(mk_data(geometric_altitude).get() - runway.elevation);
4290   else
4291     parameter->unset();
4292 }
4293
4294 void
4295 MK_VIII::Mode6Handler::update_above_field_callout ()
4296 {
4297   if (! conf.above_field_voice)
4298     return;
4299
4300   // update_runway() has to iterate over the whole runway database
4301   // (which contains thousands of runways), so it would be unwise to
4302   // run it every frame. Run it every 3 seconds. Note that the first
4303   // iteration is run in boot().
4304   if (runway_timer.start_or_elapsed() >= 3)
4305     {
4306       update_runway();
4307       runway_timer.start();
4308     }
4309
4310   Parameter<double> altitude_above_field;
4311   get_altitude_above_field(&altitude_above_field);
4312
4313   if (! mk->io_handler.gpws_inhibit()
4314       && (mk->voice_player.voice == conf.above_field_voice
4315           || mk->voice_player.next_voice == conf.above_field_voice))
4316     goto end;
4317
4318   // handle reset term
4319   if (above_field_issued)
4320     {
4321       if ((! has_runway.ncd && ! has_runway.get())
4322           || (! altitude_above_field.ncd && altitude_above_field.get() > 700))
4323         above_field_issued = false;
4324     }
4325
4326   if (! mk->io_handler.gpws_inhibit()
4327       && ! mk->state_handler.ground // [1]
4328       && ! above_field_issued
4329       && ! altitude_above_field.ncd
4330       && altitude_above_field.get() < 550
4331       && (last_altitude_above_field.ncd
4332           || last_altitude_above_field.get() >= 550))
4333     {
4334       above_field_issued = true;
4335
4336       if (! is_outside_band(altitude_above_field.get(), 500))
4337         {
4338           mk->alert_handler.set_altitude_callout_alert(conf.above_field_voice);
4339           goto end;
4340         }
4341     }
4342
4343   mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4344
4345  end:
4346   last_altitude_above_field.set(&altitude_above_field);
4347 }
4348
4349 bool
4350 MK_VIII::Mode6Handler::is_bank_angle (double abs_roll_angle, double bias)
4351 {
4352   return conf.is_bank_angle(&mk_data(radio_altitude),
4353                             abs_roll_angle - abs_roll_angle * bias,
4354                             mk_dinput(autopilot_engaged));
4355 }
4356
4357 bool
4358 MK_VIII::Mode6Handler::is_high_bank_angle ()
4359 {
4360   return mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() >= 210;
4361 }
4362
4363 unsigned int
4364 MK_VIII::Mode6Handler::get_bank_angle_alerts ()
4365 {
4366   if (! mk->io_handler.gpws_inhibit()
4367       && ! mk->state_handler.ground // [1]
4368       && ! mk_data(roll_angle).ncd)
4369     {
4370       double abs_roll_angle = fabs(mk_data(roll_angle).get());
4371
4372       if (is_bank_angle(abs_roll_angle, 0.4))
4373         return is_high_bank_angle()
4374           ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2) | mk_alert(MODE6_HIGH_BANK_ANGLE_3))
4375           : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2) | mk_alert(MODE6_LOW_BANK_ANGLE_3));
4376       else if (is_bank_angle(abs_roll_angle, 0.2))
4377         return is_high_bank_angle()
4378           ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2))
4379           : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2));
4380       else if (is_bank_angle(abs_roll_angle, 0))
4381         return is_high_bank_angle()
4382           ? mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4383           : mk_alert(MODE6_LOW_BANK_ANGLE_1);
4384     }
4385
4386   return 0;
4387 }
4388
4389 void
4390 MK_VIII::Mode6Handler::update_bank_angle ()
4391 {
4392   if (! conf.bank_angle_enabled)
4393     return;
4394
4395   unsigned int alerts = get_bank_angle_alerts();
4396
4397   // [SPEC] 6.4.4 specifies that the non-continuous alerts
4398   // (MODE6_*_BANK_ANGLE_1 and MODE6_*_BANK_ANGLE_2) are locked out
4399   // until the initial envelope is exited.
4400
4401   if (! test_bits(alerts, mk_alert(MODE6_LOW_BANK_ANGLE_3)))
4402     mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_3));
4403   if (! test_bits(alerts, mk_alert(MODE6_HIGH_BANK_ANGLE_3)))
4404     mk_unset_alerts(mk_alert(MODE6_HIGH_BANK_ANGLE_3));
4405
4406   if (alerts != 0)
4407     mk_set_alerts(alerts);
4408   else
4409     mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_1)
4410                     | mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4411                     | mk_alert(MODE6_LOW_BANK_ANGLE_2)
4412                     | mk_alert(MODE6_HIGH_BANK_ANGLE_2));
4413 }
4414
4415 void
4416 MK_VIII::Mode6Handler::update ()
4417 {
4418   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4419     return;
4420
4421   if (! mk->state_handler.takeoff
4422       && ! mk_data(radio_altitude).ncd
4423       && mk_data(radio_altitude).get() > 1000)
4424     {
4425       reset_altitude_callouts();        // [SPEC] 6.4.2
4426       reset_minimums();                 // common sense
4427     }
4428
4429   update_minimums();
4430   update_altitude_callouts();
4431   update_above_field_callout();
4432   update_bank_angle();
4433 }
4434
4435 ///////////////////////////////////////////////////////////////////////////////
4436 // TCFHandler /////////////////////////////////////////////////////////////////
4437 ///////////////////////////////////////////////////////////////////////////////
4438
4439 // Gets the difference between the azimuth from @from_lat,@from_lon to
4440 // @to_lat,@to_lon, and @to_heading, in degrees.
4441 double
4442 MK_VIII::TCFHandler::get_azimuth_difference (double from_lat,
4443                                              double from_lon,
4444                                              double to_lat,
4445                                              double to_lon,
4446                                              double to_heading)
4447 {
4448   double az1, az2, distance;
4449   geo_inverse_wgs_84(0, from_lat, from_lon, to_lat, to_lon, &az1, &az2, &distance);
4450   return get_heading_difference(az1, to_heading);
4451 }
4452
4453 // Gets the difference between the azimuth from the current GPS
4454 // position to the center of @_runway, and the heading of @_runway, in
4455 // degrees.
4456 double
4457 MK_VIII::TCFHandler::get_azimuth_difference (const FGRunway *_runway)
4458 {
4459   return get_azimuth_difference(mk_data(gps_latitude).get(),
4460                                 mk_data(gps_longitude).get(),
4461                                 _runway->latitude(),
4462                                 _runway->longitude(),
4463                                 _runway->headingDeg());
4464 }
4465
4466 // Selects the most likely intended destination runway of @airport,
4467 // and returns it in @_runway. For each runway, the difference between
4468 // the azimuth from the current GPS position to the center of the
4469 // runway and its heading is computed. The runway having the smallest
4470 // difference wins.
4471 //
4472 // This selection algorithm is not specified in [SPEC], but
4473 // http://www.egpws.com/general_information/description/runway_select.htm
4474 // talks about automatic runway selection.
4475 FGRunway*
4476 MK_VIII::TCFHandler::select_runway (const FGAirport *airport)
4477 {
4478   FGRunway* _runway = 0;
4479   double min_diff = 360;
4480   
4481   for (unsigned int r=0; r<airport->numRunways(); ++r) {
4482     FGRunway* rwy(airport->getRunwayByIndex(r));
4483     double diff = get_azimuth_difference(rwy);
4484     if (diff < min_diff)
4485           {
4486       min_diff = diff;
4487       _runway = rwy;
4488     }
4489   } // of airport runways iteration
4490   return _runway;
4491 }
4492
4493 bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
4494 {
4495   return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
4496 }
4497    
4498 void
4499 MK_VIII::TCFHandler::update_runway ()
4500 {
4501   has_runway = false;
4502   if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4503     return;
4504   }
4505
4506   // Search for the intended destination runway of the closest
4507   // airport in range 15 nm. Passing 30nm to get_closest_airport() 
4508   // provides enough margin for
4509   // large airports, which may have a runway located far away from
4510   // the airport's reference point.
4511   AirportFilter filter(mk);
4512   FGAirport* apt = FGAirport::findClosest(
4513     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4514     30.0, &filter);
4515       
4516   if (!apt) return;
4517   
4518           has_runway = true;
4519
4520           FGRunway* _runway = select_runway(apt);
4521     
4522           runway.center.latitude = _runway->latitude();
4523           runway.center.longitude = _runway->longitude();
4524
4525           runway.elevation = apt->elevation();
4526
4527           double half_length_m = _runway->lengthM() * 0.5;
4528           runway.half_length = half_length_m * SG_METER_TO_NM;
4529
4530           //        b3 ________________ b0
4531           //          |                |
4532           //    h1>>> |  e1<<<<<<<<e0  | <<<h0
4533           //          |________________|
4534           //        b2                  b1
4535
4536           // get heading to runway threshold (h0) and end (h1)
4537           runway.edges[0].heading = _runway->headingDeg();
4538           runway.edges[1].heading = get_reciprocal_heading(_runway->headingDeg());
4539
4540           double az;
4541
4542           // get position of runway threshold (e0)
4543           geo_direct_wgs_84(0,
4544                             runway.center.latitude,
4545                             runway.center.longitude,
4546                             runway.edges[1].heading,
4547                             half_length_m,
4548                             &runway.edges[0].position.latitude,
4549                             &runway.edges[0].position.longitude,
4550                             &az);
4551
4552           // get position of runway end (e1)
4553           geo_direct_wgs_84(0,
4554                             runway.center.latitude,
4555                             runway.center.longitude,
4556                             runway.edges[0].heading,
4557                             half_length_m,
4558                             &runway.edges[1].position.latitude,
4559                             &runway.edges[1].position.longitude,
4560                             &az);
4561
4562           double half_width_m = _runway->widthM() * 0.5;
4563
4564           // get position of threshold bias area edges (b0 and b1)
4565           get_bias_area_edges(&runway.edges[0].position,
4566                               runway.edges[1].heading,
4567                               half_width_m,
4568                               &runway.bias_area[0],
4569                               &runway.bias_area[1]);
4570
4571           // get position of end bias area edges (b2 and b3)
4572           get_bias_area_edges(&runway.edges[1].position,
4573                               runway.edges[0].heading,
4574                               half_width_m,
4575                               &runway.bias_area[2],
4576                               &runway.bias_area[3]);
4577 }
4578
4579 void
4580 MK_VIII::TCFHandler::get_bias_area_edges (Position *edge,
4581                                           double reciprocal,
4582                                           double half_width_m,
4583                                           Position *bias_edge1,
4584                                           Position *bias_edge2)
4585 {
4586   double half_bias_width_m = k * SG_NM_TO_METER + half_width_m;
4587   double tmp_latitude = 0.0, tmp_longitude = 0.0, az = 0.0;
4588
4589   geo_direct_wgs_84(0,
4590                     edge->latitude,
4591                     edge->longitude,
4592                     reciprocal,
4593                     k * SG_NM_TO_METER,
4594                     &tmp_latitude,
4595                     &tmp_longitude,
4596                     &az);
4597   geo_direct_wgs_84(0,
4598                     tmp_latitude,
4599                     tmp_longitude,
4600                     heading_substract(reciprocal, 90),
4601                     half_bias_width_m,
4602                     &bias_edge1->latitude,
4603                     &bias_edge1->longitude,
4604                     &az);
4605   geo_direct_wgs_84(0,
4606                     tmp_latitude,
4607                     tmp_longitude,
4608                     heading_add(reciprocal, 90),
4609                     half_bias_width_m,
4610                     &bias_edge2->latitude,
4611                     &bias_edge2->longitude,
4612                     &az);
4613 }
4614
4615 // Returns true if the current GPS position is inside the edge
4616 // triangle of @edge. The edge triangle, which is specified and
4617 // represented in [SPEC] 6.3.1, is defined as an isocele right
4618 // triangle of infinite height, whose right angle is located at the
4619 // position of @edge, and whose height is parallel to the heading of
4620 // @edge.
4621 bool
4622 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
4623 {
4624   return get_azimuth_difference(mk_data(gps_latitude).get(),
4625                                 mk_data(gps_longitude).get(),
4626                                 edge->position.latitude,
4627                                 edge->position.longitude,
4628                                 edge->heading) <= 45;
4629 }
4630
4631 // Returns true if the current GPS position is inside the bias area of
4632 // the currently selected runway.
4633 bool
4634 MK_VIII::TCFHandler::is_inside_bias_area ()
4635 {
4636   double az1[4];
4637   double angles_sum = 0;
4638
4639   for (int i = 0; i < 4; i++)
4640     {
4641       double az2, distance;
4642       geo_inverse_wgs_84(0,
4643                          mk_data(gps_latitude).get(),
4644                          mk_data(gps_longitude).get(),
4645                          runway.bias_area[i].latitude,
4646                          runway.bias_area[i].longitude,
4647                          &az1[i], &az2, &distance);
4648     }
4649
4650   for (int i = 0; i < 4; i++)
4651     {
4652       double angle = az1[i == 3 ? 0 : i + 1] - az1[i];
4653       if (angle < -180)
4654         angle += 360;
4655
4656       angles_sum += angle;
4657     }
4658
4659   return angles_sum > 180;
4660 }
4661
4662 bool
4663 MK_VIII::TCFHandler::is_tcf ()
4664 {
4665   if (mk_data(radio_altitude).get() > 10)
4666     {
4667       if (has_runway)
4668         {
4669           double distance, az1, az2;
4670
4671           geo_inverse_wgs_84(0,
4672                              mk_data(gps_latitude).get(),
4673                              mk_data(gps_longitude).get(),
4674                              runway.center.latitude,
4675                              runway.center.longitude,
4676                              &az1, &az2, &distance);
4677
4678           distance *= SG_METER_TO_NM;
4679
4680           // distance to the inner envelope edge
4681           double edge_distance = distance - runway.half_length - k;
4682
4683           if (edge_distance >= 15)
4684             {
4685               if (mk_data(radio_altitude).get() < 700)
4686                 return true;
4687             }
4688           else if (edge_distance >= 12)
4689             {
4690               if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
4691                 return true;
4692             }
4693           else if (edge_distance >= 4)
4694             {
4695               if (mk_data(radio_altitude).get() < 400)
4696                 return true;
4697             }
4698           else if (edge_distance >= 2.45)
4699             {
4700               if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4701                 return true;
4702             }
4703           else
4704             {
4705               if (is_inside_edge_triangle(&runway.edges[0]) || is_inside_edge_triangle(&runway.edges[1]))
4706                 {
4707                   if (edge_distance >= 1)
4708                     {
4709                       if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4710                         return true;
4711                     }
4712                   else if (edge_distance >= 0.05)
4713                     {
4714                       if (mk_data(radio_altitude).get() < 200 * edge_distance)
4715                         return true;
4716                     }
4717                 }
4718               else
4719                 {
4720                   if (! is_inside_bias_area())
4721                     {
4722                       if (mk_data(radio_altitude).get() < 245)
4723                         return true;
4724                     }
4725                 }
4726             }
4727         }
4728       else
4729         {
4730           if (mk_data(radio_altitude).get() < 700)
4731             return true;
4732         }
4733     }
4734
4735   return false;
4736 }
4737
4738 bool
4739 MK_VIII::TCFHandler::is_rfcf ()
4740 {
4741   if (has_runway)
4742     {
4743       double distance, az1, az2;
4744       geo_inverse_wgs_84(0,
4745                          mk_data(gps_latitude).get(),
4746                          mk_data(gps_longitude).get(),
4747                          runway.center.latitude,
4748                          runway.center.longitude,
4749                          &az1, &az2, &distance);
4750
4751       double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
4752       distance = distance * SG_METER_TO_NM - runway.half_length - krf;
4753
4754       if (distance <= 5)
4755         {
4756           double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
4757
4758           if (distance >= 1.5)
4759             {
4760               if (altitude_above_field < 300)
4761                 return true;
4762             }
4763           else if (distance >= 0)
4764             {
4765               if (altitude_above_field < 200 * distance)
4766                 return true;
4767             }
4768         }
4769     }
4770
4771   return false;
4772 }
4773
4774 void
4775 MK_VIII::TCFHandler::update ()
4776 {
4777   if (mk->configuration_module.state != ConfigurationModule::STATE_OK || ! conf.enabled)
4778     return;
4779
4780   // update_runway() has to iterate over the whole runway database
4781   // (which contains thousands of runways), so it would be unwise to
4782   // run it every frame. Run it every 3 seconds.
4783   if (! runway_timer.running || runway_timer.elapsed() >= 3)
4784     {
4785       update_runway();
4786       runway_timer.start();
4787     }
4788
4789   if (! mk_dinput(ta_tcf_inhibit)
4790       && ! mk->state_handler.ground // [1]
4791       && ! mk_data(gps_latitude).ncd
4792       && ! mk_data(gps_longitude).ncd
4793       && ! mk_data(radio_altitude).ncd
4794       && ! mk_data(geometric_altitude).ncd
4795       && ! mk_data(gps_vertical_figure_of_merit).ncd)
4796     {
4797       double *_reference;
4798
4799       if (is_tcf())
4800         _reference = mk_data(radio_altitude).get_pointer();
4801       else if (is_rfcf())
4802         _reference = mk_data(geometric_altitude).get_pointer();
4803       else
4804         _reference = NULL;
4805
4806       if (_reference)
4807         {
4808           if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
4809             {
4810               double new_bias = bias;
4811               while (*reference < initial_value - initial_value * new_bias)
4812                 new_bias += 0.2;
4813
4814               if (new_bias > bias)
4815                 {
4816                   bias = new_bias;
4817                   mk_repeat_alert(mk_alert(TCF_TOO_LOW_TERRAIN));
4818                 }
4819             }
4820           else
4821             {
4822               bias = 0.2;
4823               reference = _reference;
4824               initial_value = *reference;
4825               mk_set_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4826             }
4827
4828           return;
4829         }
4830     }
4831
4832   mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4833 }
4834
4835 ///////////////////////////////////////////////////////////////////////////////
4836 // MK_VIII ////////////////////////////////////////////////////////////////////
4837 ///////////////////////////////////////////////////////////////////////////////
4838
4839 MK_VIII::MK_VIII (SGPropertyNode *node)
4840   : properties_handler(this),
4841     name("mk-viii"),
4842     num(0),
4843     power_handler(this),
4844     system_handler(this),
4845     configuration_module(this),
4846     fault_handler(this),
4847     io_handler(this),
4848     voice_player(this),
4849     self_test_handler(this),
4850     alert_handler(this),
4851     state_handler(this),
4852     mode1_handler(this),
4853     mode2_handler(this),
4854     mode3_handler(this),
4855     mode4_handler(this),
4856     mode5_handler(this),
4857     mode6_handler(this),
4858     tcf_handler(this)
4859 {
4860   for (int i = 0; i < node->nChildren(); ++i)
4861     {
4862       SGPropertyNode *child = node->getChild(i);
4863       string cname = child->getName();
4864       string cval = child->getStringValue();
4865
4866       if (cname == "name")
4867         name = cval;
4868       else if (cname == "number")
4869         num = child->getIntValue();
4870       else
4871         {
4872           SG_LOG(SG_INSTR, SG_WARN, "Error in mk-viii config logic");
4873           if (name.length())
4874             SG_LOG(SG_INSTR, SG_WARN, "Section = " << name);
4875         }
4876     }
4877 }
4878
4879 void
4880 MK_VIII::init ()
4881 {
4882   properties_handler.init();
4883   voice_player.init();
4884 }
4885
4886 void
4887 MK_VIII::bind ()
4888 {
4889   SGPropertyNode *node = fgGetNode(("/instrumentation/" + name).c_str(), num, true);
4890
4891   configuration_module.bind(node);
4892   power_handler.bind(node);
4893   io_handler.bind(node);
4894   voice_player.bind(node);
4895 }
4896
4897 void
4898 MK_VIII::unbind ()
4899 {
4900   properties_handler.unbind();
4901 }
4902
4903 void
4904 MK_VIII::update (double dt)
4905 {
4906   power_handler.update();
4907   system_handler.update();
4908
4909   if (system_handler.state != SystemHandler::STATE_ON)
4910     return;
4911
4912   io_handler.update_inputs();
4913   io_handler.update_input_faults();
4914
4915   voice_player.update();
4916   state_handler.update();
4917
4918   if (self_test_handler.update())
4919     return;
4920
4921   io_handler.update_internal_latches();
4922   io_handler.update_lamps();
4923
4924   mode1_handler.update();
4925   mode2_handler.update();
4926   mode3_handler.update();
4927   mode4_handler.update();
4928   mode5_handler.update();
4929   mode6_handler.update();
4930   tcf_handler.update();
4931
4932   alert_handler.update();
4933   io_handler.update_outputs();
4934 }