]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/mk_viii.cxx
12f3ef050c67f35b1d8caea8238fa1ea764dfb11
[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       SGPath sample_path(globals->get_fg_root());
2276       sample_path.append("Sounds/mk-viii");
2277
2278       string filename = string(name) + ".wav";
2279       try
2280         {
2281           sample = new SGSoundSample(sample_path.c_str(), filename.c_str());
2282         }
2283       catch (const sg_exception &e)
2284         {
2285           SG_LOG(SG_INSTR, SG_ALERT, "Error loading MK VIII sound sample \"" + filename + "\": " + e.getFormattedMessage());
2286           exit(1);
2287         }
2288
2289       _sgr->add(sample, refname.str());
2290       samples[refname.str()] = sample;
2291     }
2292
2293   return sample;
2294 }
2295
2296 void
2297 MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
2298 {
2299   if (test_bits(flags, PLAY_NOW) || ! voice || voice->element->silence)
2300     {
2301       if (voice)
2302         voice->stop(true);
2303
2304       voice = _voice;
2305       looped = test_bits(flags, PLAY_LOOPED);
2306
2307       next_voice = NULL;
2308       next_looped = false;
2309
2310       voice->play();
2311     }
2312   else
2313     {
2314       next_voice = _voice;
2315       next_looped = test_bits(flags, PLAY_LOOPED);
2316     }
2317 }
2318
2319 void
2320 MK_VIII::VoicePlayer::stop (unsigned int flags)
2321 {
2322   if (voice)
2323     {
2324       voice->stop(test_bits(flags, STOP_NOW));
2325       if (voice->element)
2326         looped = false;
2327       else
2328         voice = NULL;
2329       next_voice = NULL;
2330     }
2331 }
2332
2333 void
2334 MK_VIII::VoicePlayer::set_volume (float _volume)
2335 {
2336   volume = _volume;
2337   if (voice)
2338     voice->volume_changed();
2339 }
2340
2341 void
2342 MK_VIII::VoicePlayer::update ()
2343 {
2344   if (voice)
2345     {
2346       voice->update();
2347
2348       if (next_voice)
2349         {
2350           if (! voice->element || voice->element->silence)
2351             {
2352               voice = next_voice;
2353               looped = next_looped;
2354
2355               next_voice = NULL;
2356               next_looped = false;
2357
2358               voice->play();
2359             }
2360         }
2361       else
2362         {
2363           if (! voice->element)
2364             {
2365               if (looped)
2366                 voice->play();
2367               else
2368                 voice = NULL;
2369             }
2370         }
2371     }
2372 }
2373
2374 ///////////////////////////////////////////////////////////////////////////////
2375 // SelfTestHandler ////////////////////////////////////////////////////////////
2376 ///////////////////////////////////////////////////////////////////////////////
2377
2378 bool
2379 MK_VIII::SelfTestHandler::_was_here (int position)
2380 {
2381   if (position > current)
2382     {
2383       current = position;
2384       return false;
2385     }
2386   else
2387     return true;
2388 }
2389
2390 MK_VIII::SelfTestHandler::Action
2391 MK_VIII::SelfTestHandler::sleep (double duration)
2392 {
2393   Action _action = { ACTION_SLEEP, duration, NULL };
2394   return _action;
2395 }
2396
2397 MK_VIII::SelfTestHandler::Action
2398 MK_VIII::SelfTestHandler::play (VoicePlayer::Voice *voice)
2399 {
2400   mk->voice_player.play(voice);
2401   Action _action = { ACTION_VOICE, 0, NULL };
2402   return _action;
2403 }
2404
2405 MK_VIII::SelfTestHandler::Action
2406 MK_VIII::SelfTestHandler::discrete_on (bool *discrete, double duration)
2407 {
2408   *discrete = true;
2409   return sleep(duration);
2410 }
2411
2412 MK_VIII::SelfTestHandler::Action
2413 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, double duration)
2414 {
2415   *discrete = true;
2416   Action _action = { ACTION_SLEEP | ACTION_DISCRETE_ON_OFF, duration, discrete };
2417   return _action;
2418 }
2419
2420 MK_VIII::SelfTestHandler::Action
2421 MK_VIII::SelfTestHandler::discrete_on_off (bool *discrete, VoicePlayer::Voice *voice)
2422 {
2423   *discrete = true;
2424   mk->voice_player.play(voice);
2425   Action _action = { ACTION_VOICE | ACTION_DISCRETE_ON_OFF, 0, discrete };
2426   return _action;
2427 }
2428
2429 MK_VIII::SelfTestHandler::Action
2430 MK_VIII::SelfTestHandler::done ()
2431 {
2432   Action _action = { ACTION_DONE, 0, NULL };
2433   return _action;
2434 }
2435
2436 MK_VIII::SelfTestHandler::Action
2437 MK_VIII::SelfTestHandler::run ()
2438 {
2439   // Note that "Terrain INOP" and "Terrain NA" are or'ed to the same
2440   // output discrete (see [SPEC] 6.9.3.5).
2441
2442 #define was_here()              was_here_offset(0)
2443 #define was_here_offset(offset) _was_here(__LINE__ * 20 + (offset))
2444
2445   if (! was_here())
2446     {
2447       if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_DATABASE)
2448         return play(mk_voice(application_data_base_failed));
2449       else if (mk->configuration_module.state == ConfigurationModule::STATE_INVALID_AIRCRAFT_TYPE)
2450         return play(mk_voice(configuration_type_invalid));
2451     }
2452   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2453     return done();
2454
2455   if (! was_here())
2456     return discrete_on(&mk_doutput(gpws_inop), 0.7);
2457   if (! was_here())
2458     return sleep(0.7);          // W/S INOP
2459   if (! was_here())
2460     return discrete_on(&mk_doutput(tad_inop), 0.7);
2461   if (! was_here())
2462     {
2463       mk_doutput(gpws_inop) = false;
2464       // do not disable tad_inop since we must enable Terrain NA
2465       // do not disable W/S INOP since we do not emulate it
2466       return sleep(0.7);        // Terrain NA
2467     }
2468   if (! was_here())
2469     {
2470       mk_doutput(tad_inop) = false; // disable Terrain NA
2471       if (mk->io_handler.conf.momentary_flap_override_enabled)
2472         return discrete_on_off(&mk_doutput(flap_override), 1.0);
2473     }
2474   if (! was_here())
2475     return discrete_on_off(&mk_doutput(audio_on), 1.0);
2476   if (! was_here())
2477     {
2478       if (mk->io_handler.momentary_steep_approach_enabled())
2479         return discrete_on_off(&mk_doutput(steep_approach), 1.0);
2480     }
2481
2482   if (! was_here())
2483     {
2484       if (mk_dinput(glideslope_inhibit))
2485         goto glideslope_end;
2486       else
2487         {
2488           if (mk->fault_handler.faults[FaultHandler::FAULT_MODE5_INPUTS_INVALID])
2489             goto glideslope_inop;
2490         }
2491     }
2492   if (! was_here())
2493     return discrete_on_off(&mk_doutput(gpws_alert), mk_voice(glideslope));
2494   if (! was_here())
2495     return discrete_on_off(&mk_doutput(glideslope_cancel), 0.7);
2496   goto glideslope_end;
2497  glideslope_inop:
2498   if (! was_here())
2499     return play(mk_voice(glideslope_inop));
2500  glideslope_end:
2501
2502   if (! was_here())
2503     {
2504       if (mk->fault_handler.faults[FaultHandler::FAULT_MODES14_INPUTS_INVALID])
2505         goto gpws_inop;
2506     }
2507   if (! was_here())
2508     return discrete_on_off(&mk_doutput(gpws_warning), mk_voice(pull_up));
2509   goto gpws_end;
2510  gpws_inop:
2511   if (! was_here())
2512     return play(mk_voice(gpws_inop));
2513  gpws_end:
2514
2515   if (! was_here())
2516     {
2517       if (mk_dinput(self_test)) // proceed to long self test
2518         goto long_test;
2519     }
2520   if (! was_here())
2521     {
2522       if (mk->mode6_handler.conf.bank_angle_enabled
2523           && mk->fault_handler.faults[FaultHandler::FAULT_BANK_ANGLE_INPUTS_INVALID])
2524         return play(mk_voice(bank_angle_inop));
2525     }
2526   if (! was_here())
2527     {
2528       if (mk->mode6_handler.altitude_callouts_enabled()
2529           && mk->fault_handler.faults[FaultHandler::FAULT_MODE6_INPUTS_INVALID])
2530         return play(mk_voice(callouts_inop));
2531     }
2532   if (! was_here())
2533     return done();
2534
2535  long_test:
2536   if (! was_here())
2537     {
2538       mk_doutput(gpws_inop) = true;
2539       // do not enable W/S INOP, since we do not emulate it
2540       mk_doutput(tad_inop) = true; // Terrain INOP, Terrain NA
2541
2542       return play(mk_voice(sink_rate));
2543     }
2544   if (! was_here())
2545     return play(mk_voice(pull_up));
2546   if (! was_here())
2547     return play(mk_voice(terrain));
2548   if (! was_here())
2549     return play(mk_voice(pull_up));
2550   if (! was_here())
2551     return play(mk_voice(dont_sink));
2552   if (! was_here())
2553     return play(mk_voice(too_low_terrain));
2554   if (! was_here())
2555     return play(mk->mode4_handler.conf.voice_too_low_gear);
2556   if (! was_here())
2557     return play(mk_voice(too_low_flaps));
2558   if (! was_here())
2559     return play(mk_voice(too_low_terrain));
2560   if (! was_here())
2561     return play(mk_voice(glideslope));
2562   if (! was_here())
2563     {
2564       if (mk->mode6_handler.conf.bank_angle_enabled)
2565         return play(mk_voice(bank_angle));
2566     }
2567
2568   if (! was_here())
2569     {
2570       if (! mk->mode6_handler.altitude_callouts_enabled())
2571         goto callouts_disabled;
2572     }
2573   if (! was_here())
2574     {
2575       if (mk->mode6_handler.conf.minimums_enabled)
2576         return play(mk_voice(minimums));
2577     }
2578   if (! was_here())
2579     {
2580       if (mk->mode6_handler.conf.above_field_voice)
2581         return play(mk->mode6_handler.conf.above_field_voice);
2582     }
2583   for (unsigned i = 0; i < n_altitude_callouts; i++)
2584     if (! was_here_offset(i))
2585       {
2586         if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
2587           return play(mk_altitude_voice(i));
2588       }
2589   if (! was_here())
2590     {
2591       if (mk->mode6_handler.conf.smart_500_enabled)
2592         return play(mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_500));
2593     }
2594   goto callouts_end;
2595  callouts_disabled:
2596   if (! was_here())
2597     return play(mk_voice(minimums_minimums));
2598  callouts_end:
2599
2600   if (! was_here())
2601     {
2602       if (mk->tcf_handler.conf.enabled)
2603         return play(mk_voice(too_low_terrain));
2604     }
2605
2606   return done();
2607 }
2608
2609 void
2610 MK_VIII::SelfTestHandler::start ()
2611 {
2612   assert(state == STATE_START);
2613
2614   memcpy(&saved_outputs, &mk->io_handler.outputs, sizeof(mk->io_handler.outputs));
2615   memset(&mk->io_handler.outputs, 0, sizeof(mk->io_handler.outputs));
2616
2617   // [SPEC] 6.10.6: "The self-test results are annunciated, at 6db
2618   // lower than the normal audio level selected for the aircraft"
2619   mk->voice_player.set_volume(modify_amplitude(mk->voice_player.conf.volume, -6));
2620
2621   if (mk_dinput(mode6_low_volume))
2622     mk->mode6_handler.set_volume(1.0);
2623
2624   state = STATE_RUNNING;
2625   cancel = CANCEL_NONE;
2626   memset(&action, 0, sizeof(action));
2627   current = 0;
2628 }
2629
2630 void
2631 MK_VIII::SelfTestHandler::stop ()
2632 {
2633   if (state != STATE_NONE)
2634     {
2635       if (state != STATE_START)
2636         {
2637           mk->voice_player.stop(VoicePlayer::STOP_NOW);
2638           mk->voice_player.set_volume(mk_dinput(audio_inhibit) ? 0.0 : mk->voice_player.conf.volume);
2639
2640           if (mk_dinput(mode6_low_volume))
2641             mk->mode6_handler.set_volume(modify_amplitude(1.0, -6));
2642
2643           memcpy(&mk->io_handler.outputs, &saved_outputs, sizeof(mk->io_handler.outputs));
2644         }
2645
2646       button_pressed = false;
2647       state = STATE_NONE;
2648       // reset self-test handler position
2649       current=0;
2650     }
2651 }
2652
2653 void
2654 MK_VIII::SelfTestHandler::handle_button_event (bool value)
2655 {
2656   if (state == STATE_NONE)
2657     {
2658       if (value)
2659         state = STATE_START;
2660     }
2661   else if (state == STATE_START)
2662     {
2663       if (value)
2664         state = STATE_NONE;     // cancel the not-yet-started test
2665     }
2666   else if (cancel == CANCEL_NONE)
2667     {
2668       if (value)
2669         {
2670           assert(! button_pressed);
2671           button_pressed = true;
2672           button_press_timestamp = globals->get_sim_time_sec();
2673         }
2674       else
2675         {
2676           if (button_pressed)
2677             {
2678               if (globals->get_sim_time_sec() - button_press_timestamp < 2)
2679                 cancel = CANCEL_SHORT;
2680               else
2681                 cancel = CANCEL_LONG;
2682             }
2683         }
2684     }
2685 }
2686
2687 bool
2688 MK_VIII::SelfTestHandler::update ()
2689 {
2690   if (state == STATE_NONE)
2691     return false;
2692   else if (state == STATE_START)
2693     {
2694       if (mk->state_handler.ground && ! mk->io_handler.steep_approach())
2695         start();
2696       else
2697         {
2698           state = STATE_NONE;
2699           return false;
2700         }
2701     }
2702   else
2703     {
2704       if (button_pressed && cancel == CANCEL_NONE)
2705         {
2706           if (globals->get_sim_time_sec() - button_press_timestamp >= 2)
2707             cancel = CANCEL_LONG;
2708         }
2709     }
2710
2711   if (! mk->state_handler.ground || cancel != CANCEL_NONE)
2712     {
2713       stop();
2714       return false;
2715     }
2716
2717   if (test_bits(action.flags, ACTION_SLEEP))
2718     {
2719       if (globals->get_sim_time_sec() - sleep_start < action.sleep_duration)
2720         return true;
2721     }
2722   if (test_bits(action.flags, ACTION_VOICE))
2723     {
2724       if (mk->voice_player.voice)
2725         return true;
2726     }
2727   if (test_bits(action.flags, ACTION_DISCRETE_ON_OFF))
2728     *action.discrete = false;
2729
2730   action = run();
2731
2732   if (test_bits(action.flags, ACTION_SLEEP))
2733     sleep_start = globals->get_sim_time_sec();
2734   if (test_bits(action.flags, ACTION_DONE))
2735     {
2736       stop();
2737       return false;
2738     }
2739
2740   return true;
2741 }
2742
2743 ///////////////////////////////////////////////////////////////////////////////
2744 // AlertHandler ///////////////////////////////////////////////////////////////
2745 ///////////////////////////////////////////////////////////////////////////////
2746
2747 bool
2748 MK_VIII::AlertHandler::select_voice_alerts (unsigned int test)
2749 {
2750   if (has_alerts(test))
2751     {
2752       voice_alerts = alerts & test;
2753       return true;
2754     }
2755   else
2756     {
2757       voice_alerts &= ~test;
2758       if (voice_alerts == 0)
2759         mk->voice_player.stop();
2760
2761       return false;
2762     }
2763 }
2764
2765 void
2766 MK_VIII::AlertHandler::boot ()
2767 {
2768   reset();
2769 }
2770
2771 void
2772 MK_VIII::AlertHandler::reposition ()
2773 {
2774   reset();
2775
2776   mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2777   mk->voice_player.stop(VoicePlayer::STOP_NOW);
2778 }
2779
2780 void
2781 MK_VIII::AlertHandler::reset ()
2782 {
2783   alerts = 0;
2784   old_alerts = 0;
2785   voice_alerts = 0;
2786   repeated_alerts = 0;
2787   altitude_callout_voice = NULL;
2788 }
2789
2790 void
2791 MK_VIII::AlertHandler::update ()
2792 {
2793   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
2794     return;
2795
2796   // Lamps and voices are prioritized according to [SPEC] 6.9.2.
2797   //
2798   // Voices which can interrupt other voices (VoicePlayer::PLAY_NOW)
2799   // are specified by [INSTALL] appendix E 5.3.5.
2800   //
2801   // When a voice is overriden by a higher priority voice and the
2802   // overriding voice stops, we restore the previous voice if it was
2803   // looped (this is not specified by [SPEC] but seems to make sense).
2804
2805   // update lamp
2806
2807   if (has_alerts(ALERT_MODE1_PULL_UP | ALERT_MODE2A | ALERT_MODE2B))
2808     mk->io_handler.set_lamp(IOHandler::LAMP_WARNING);
2809   else if (has_alerts(ALERT_MODE1_SINK_RATE
2810                       | ALERT_MODE2A_PREFACE
2811                       | ALERT_MODE2B_PREFACE
2812                       | ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING
2813                       | ALERT_MODE2A_ALTITUDE_GAIN
2814                       | ALERT_MODE2B_LANDING_MODE
2815                       | ALERT_MODE3
2816                       | ALERT_MODE4_TOO_LOW_FLAPS
2817                       | ALERT_MODE4_TOO_LOW_GEAR
2818                       | ALERT_MODE4AB_TOO_LOW_TERRAIN
2819                       | ALERT_MODE4C_TOO_LOW_TERRAIN
2820                       | ALERT_TCF_TOO_LOW_TERRAIN))
2821     mk->io_handler.set_lamp(IOHandler::LAMP_CAUTION);
2822   else if (has_alerts(ALERT_MODE5_SOFT | ALERT_MODE5_HARD))
2823     mk->io_handler.set_lamp(IOHandler::LAMP_GLIDESLOPE);
2824   else
2825     mk->io_handler.set_lamp(IOHandler::LAMP_NONE);
2826
2827   // update voice
2828
2829   if (select_voice_alerts(ALERT_MODE1_PULL_UP))
2830     {
2831       if (! has_old_alerts(ALERT_MODE1_PULL_UP))
2832         {
2833           if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate))
2834             mk->voice_player.play(mk_voice(sink_rate), VoicePlayer::PLAY_NOW);
2835           mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_LOOPED);
2836         }
2837     }
2838   else if (select_voice_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2839     {
2840       if (! has_old_alerts(ALERT_MODE2A_PREFACE | ALERT_MODE2B_PREFACE))
2841         mk->voice_player.play(mk_voice(terrain_pause_terrain), VoicePlayer::PLAY_NOW);
2842     }
2843   else if (select_voice_alerts(ALERT_MODE2A | ALERT_MODE2B))
2844     {
2845       if (mk->voice_player.voice != mk_voice(pull_up))
2846         mk->voice_player.play(mk_voice(pull_up), VoicePlayer::PLAY_NOW | VoicePlayer::PLAY_LOOPED);
2847     }
2848   else if (select_voice_alerts(ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING | ALERT_MODE2B_LANDING_MODE))
2849     {
2850       if (mk->voice_player.voice != mk_voice(terrain))
2851         mk->voice_player.play(mk_voice(terrain), VoicePlayer::PLAY_LOOPED);
2852     }
2853   else if (select_voice_alerts(ALERT_MODE6_MINIMUMS))
2854     {
2855       if (! has_old_alerts(ALERT_MODE6_MINIMUMS))
2856         mk->voice_player.play(mk_voice(minimums_minimums));
2857     }
2858   else if (select_voice_alerts(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2859     {
2860       if (must_play_voice(ALERT_MODE4AB_TOO_LOW_TERRAIN | ALERT_MODE4C_TOO_LOW_TERRAIN))
2861         mk->voice_player.play(mk_voice(too_low_terrain));
2862     }
2863   else if (select_voice_alerts(ALERT_TCF_TOO_LOW_TERRAIN))
2864     {
2865       if (must_play_voice(ALERT_TCF_TOO_LOW_TERRAIN))
2866         mk->voice_player.play(mk_voice(too_low_terrain));
2867     }
2868   else if (select_voice_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2869     {
2870       if (! has_old_alerts(ALERT_MODE6_ALTITUDE_CALLOUT))
2871         {
2872           assert(altitude_callout_voice != NULL);
2873           mk->voice_player.play(altitude_callout_voice);
2874         }
2875     }
2876   else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
2877     {
2878       if (must_play_voice(ALERT_MODE4_TOO_LOW_GEAR))
2879         mk->voice_player.play(mk->mode4_handler.conf.voice_too_low_gear);
2880     }
2881   else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_FLAPS))
2882     {
2883       if (must_play_voice(ALERT_MODE4_TOO_LOW_FLAPS))
2884         mk->voice_player.play(mk_voice(too_low_flaps));
2885     }
2886   else if (select_voice_alerts(ALERT_MODE1_SINK_RATE))
2887     {
2888       if (must_play_voice(ALERT_MODE1_SINK_RATE))
2889         mk->voice_player.play(mk_voice(sink_rate_pause_sink_rate));
2890       // [SPEC] 6.2.1: "During the time that the voice message for the
2891       // outer envelope is inhibited and the caution/warning lamp is
2892       // on, the Mode 5 alert message is allowed to annunciate for
2893       // excessive glideslope deviation conditions."
2894       else if (mk->voice_player.voice != mk_voice(sink_rate_pause_sink_rate)
2895                && mk->voice_player.next_voice != mk_voice(sink_rate_pause_sink_rate))
2896         {
2897           if (has_alerts(ALERT_MODE5_HARD))
2898             {
2899               voice_alerts |= ALERT_MODE5_HARD;
2900               if (mk->voice_player.voice != mk_voice(hard_glideslope))
2901                 mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2902             }
2903           else if (has_alerts(ALERT_MODE5_SOFT))
2904             {
2905               voice_alerts |= ALERT_MODE5_SOFT;
2906               if (must_play_voice(ALERT_MODE5_SOFT))
2907                 mk->voice_player.play(mk_voice(soft_glideslope));
2908             }
2909         }
2910     }
2911   else if (select_voice_alerts(ALERT_MODE3))
2912     {
2913       if (must_play_voice(ALERT_MODE3))
2914         mk->voice_player.play(mk_voice(dont_sink_pause_dont_sink));
2915     }
2916   else if (select_voice_alerts(ALERT_MODE5_HARD))
2917     {
2918       if (mk->voice_player.voice != mk_voice(hard_glideslope))
2919         mk->voice_player.play(mk_voice(hard_glideslope), VoicePlayer::PLAY_LOOPED);
2920     }
2921   else if (select_voice_alerts(ALERT_MODE5_SOFT))
2922     {
2923       if (must_play_voice(ALERT_MODE5_SOFT))
2924         mk->voice_player.play(mk_voice(soft_glideslope));
2925     }
2926   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_3))
2927     {
2928       if (mk->voice_player.voice != mk_voice(bank_angle_bank_angle_3))
2929         mk->voice_player.play(mk_voice(bank_angle_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2930     }
2931   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_3))
2932     {
2933       if (mk->voice_player.voice != mk_voice(bank_angle_pause_bank_angle_3))
2934         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle_3), VoicePlayer::PLAY_LOOPED);
2935     }
2936   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2))
2937     {
2938       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2939         mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2940     }
2941   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_2))
2942     {
2943       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_2 | ALERT_MODE6_HIGH_BANK_ANGLE_2))
2944         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2945     }
2946   else if (select_voice_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1))
2947     {
2948       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2949         mk->voice_player.play(mk_voice(bank_angle_bank_angle));
2950     }
2951   else if (select_voice_alerts(ALERT_MODE6_HIGH_BANK_ANGLE_1))
2952     {
2953       if (! has_old_alerts(ALERT_MODE6_LOW_BANK_ANGLE_1 | ALERT_MODE6_HIGH_BANK_ANGLE_1))
2954         mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
2955     }
2956
2957   // set new state
2958
2959   old_alerts = voice_alerts;
2960   repeated_alerts = 0;
2961   altitude_callout_voice = NULL;
2962 }
2963
2964 void
2965 MK_VIII::AlertHandler::set_alerts (unsigned int _alerts,
2966                                    unsigned int flags,
2967                                    VoicePlayer::Voice *_altitude_callout_voice)
2968 {
2969   alerts |= _alerts;
2970   if (test_bits(flags, ALERT_FLAG_REPEAT))
2971     repeated_alerts |= _alerts;
2972   if (_altitude_callout_voice)
2973     altitude_callout_voice = _altitude_callout_voice;
2974 }
2975
2976 void
2977 MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
2978 {
2979   alerts &= ~_alerts;
2980   repeated_alerts &= ~_alerts;
2981 }
2982
2983 ///////////////////////////////////////////////////////////////////////////////
2984 // StateHandler ///////////////////////////////////////////////////////////////
2985 ///////////////////////////////////////////////////////////////////////////////
2986
2987 void
2988 MK_VIII::StateHandler::update_ground ()
2989 {
2990   if (ground)
2991     {
2992       if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
2993           && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30)
2994         {
2995           if (potentially_airborne_timer.start_or_elapsed() > 1)
2996             leave_ground();
2997         }
2998       else
2999         potentially_airborne_timer.stop();
3000     }
3001   else
3002     {
3003       if (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() < 40
3004           && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() < 30)
3005         enter_ground();
3006     }
3007 }
3008
3009 void
3010 MK_VIII::StateHandler::enter_ground ()
3011 {
3012   ground = true;
3013   mk->io_handler.enter_ground();
3014
3015   // [SPEC] 6.0.1 does not specify this, but it seems to be an
3016   // omission; at this point, we must make sure that we are in takeoff
3017   // mode (otherwise the next takeoff might happen in approach mode).
3018   if (! takeoff)
3019     enter_takeoff();
3020 }
3021
3022 void
3023 MK_VIII::StateHandler::leave_ground ()
3024 {
3025   ground = false;
3026   mk->mode2_handler.leave_ground();
3027 }
3028
3029 void
3030 MK_VIII::StateHandler::update_takeoff ()
3031 {
3032   if (takeoff)
3033     {
3034       // [SPEC] 6.0.2 is somewhat confusing: it mentions hardcoded
3035       // terrain clearance (500, 750) and airspeed (178, 200) values,
3036       // but it also mentions the mode 4A boundary, which varies as a
3037       // function of aircraft type. We consider that the mentioned
3038       // values are examples, and that we should use the mode 4A upper
3039       // boundary.
3040
3041       if (! mk_data(terrain_clearance).ncd
3042           && ! mk_ainput(computed_airspeed).ncd
3043           && mk_data(terrain_clearance).get() > mk->mode4_handler.get_upper_agl(mk->mode4_handler.conf.modes->ac))
3044         leave_takeoff();
3045     }
3046   else
3047     {
3048       if (! mk_data(radio_altitude).ncd
3049           && mk_data(radio_altitude).get() < mk->mode4_handler.conf.modes->b->min_agl1
3050           && mk->io_handler.flaps_down()
3051           && mk_dinput(landing_gear))
3052         enter_takeoff();
3053     }
3054 }
3055
3056 void
3057 MK_VIII::StateHandler::enter_takeoff ()
3058 {
3059   takeoff = true;
3060   mk->io_handler.enter_takeoff();
3061   mk->mode2_handler.enter_takeoff();
3062   mk->mode3_handler.enter_takeoff();
3063   mk->mode6_handler.enter_takeoff();
3064 }
3065
3066 void
3067 MK_VIII::StateHandler::leave_takeoff ()
3068 {
3069   takeoff = false;
3070   mk->mode6_handler.leave_takeoff();
3071 }
3072
3073 void
3074 MK_VIII::StateHandler::post_reposition ()
3075 {
3076   // Synchronize the state with the current situation.
3077
3078   bool _ground = !
3079     (! mk_ainput(computed_airspeed).ncd && mk_ainput(computed_airspeed).get() > 60
3080      && ! mk_data(radio_altitude).ncd && mk_data(radio_altitude).get() > 30);
3081
3082   bool _takeoff = _ground;
3083
3084   if (ground && ! _ground)
3085     leave_ground();
3086   else if (! ground && _ground)
3087     enter_ground();
3088
3089   if (takeoff && ! _takeoff)
3090     leave_takeoff();
3091   else if (! takeoff && _takeoff)
3092     enter_takeoff();
3093 }
3094
3095 void
3096 MK_VIII::StateHandler::update ()
3097 {
3098   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3099     return;
3100
3101   update_ground();
3102   update_takeoff();
3103 }
3104
3105 ///////////////////////////////////////////////////////////////////////////////
3106 // Mode1Handler ///////////////////////////////////////////////////////////////
3107 ///////////////////////////////////////////////////////////////////////////////
3108
3109 double
3110 MK_VIII::Mode1Handler::get_pull_up_bias ()
3111 {
3112   // [PILOT] page 54: "Steep Approach has priority over Flap Override
3113   // if selected simultaneously."
3114
3115   if (mk->io_handler.steep_approach())
3116     return 200;
3117   else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3118     return 300;
3119   else
3120     return 0;
3121 }
3122
3123 bool
3124 MK_VIII::Mode1Handler::is_pull_up ()
3125 {
3126   if (! mk->io_handler.gpws_inhibit()
3127       && ! mk->state_handler.ground // [1]
3128       && ! mk_data(radio_altitude).ncd
3129       && ! mk_data(barometric_altitude_rate).ncd
3130       && mk_data(radio_altitude).get() > conf.envelopes->min_agl)
3131     {
3132       if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl1)
3133         {
3134           if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl1(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3135             return true;
3136         }
3137       else if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_max_agl2)
3138         {
3139           if (mk_data(radio_altitude).get() < conf.envelopes->pull_up_min_agl2(mk_data(barometric_altitude_rate).get() + get_pull_up_bias()))
3140             return true;
3141         }
3142     }
3143
3144   return false;
3145 }
3146
3147 void
3148 MK_VIII::Mode1Handler::update_pull_up ()
3149 {
3150   if (is_pull_up())
3151     {
3152       if (! mk_test_alert(MODE1_PULL_UP))
3153         {
3154           // [SPEC] 6.2.1: at least one sink rate must be issued
3155           // before switching to pull up; if no sink rate has
3156           // occurred, a 0.2 second delay is used.
3157           if (mk->voice_player.voice == mk_voice(sink_rate_pause_sink_rate)
3158               || pull_up_timer.start_or_elapsed() >= 0.2)
3159             mk_set_alerts(mk_alert(MODE1_PULL_UP));
3160         }
3161     }
3162   else
3163     {
3164       pull_up_timer.stop();
3165       mk_unset_alerts(mk_alert(MODE1_PULL_UP));
3166     }
3167 }
3168
3169 double
3170 MK_VIII::Mode1Handler::get_sink_rate_bias ()
3171 {
3172   // [PILOT] page 54: "Steep Approach has priority over Flap Override
3173   // if selected simultaneously."
3174
3175   if (mk->io_handler.steep_approach())
3176     return 500;
3177   else if (conf.envelopes->flap_override_bias && mk->io_handler.flap_override())
3178     return 300;
3179   else if (! mk_data(glideslope_deviation_dots).ncd)
3180     {
3181       double bias = 0;
3182
3183       if (mk_data(glideslope_deviation_dots).get() <= -2)
3184         bias = 300;
3185       else if (mk_data(glideslope_deviation_dots).get() < 0)
3186         bias = -150 * mk_data(glideslope_deviation_dots).get();
3187
3188       if (mk_data(radio_altitude).get() < 100)
3189         bias *= 0.01 * mk_data(radio_altitude).get();
3190
3191       return bias;
3192     }
3193   else
3194     return 0;
3195 }
3196
3197 bool
3198 MK_VIII::Mode1Handler::is_sink_rate ()
3199 {
3200   return ! mk->io_handler.gpws_inhibit()
3201     && ! mk->state_handler.ground // [1]
3202     && ! mk_data(radio_altitude).ncd
3203     && ! mk_data(barometric_altitude_rate).ncd
3204     && mk_data(radio_altitude).get() > conf.envelopes->min_agl
3205     && mk_data(radio_altitude).get() < 2450
3206     && mk_data(radio_altitude).get() < -572 - 0.6035 * (mk_data(barometric_altitude_rate).get() + get_sink_rate_bias());
3207 }
3208
3209 double
3210 MK_VIII::Mode1Handler::get_sink_rate_tti ()
3211 {
3212   return mk_data(radio_altitude).get() / fabs(mk_data(barometric_altitude_rate).get());
3213 }
3214
3215 void
3216 MK_VIII::Mode1Handler::update_sink_rate ()
3217 {
3218   if (is_sink_rate())
3219     {
3220       if (mk_test_alert(MODE1_SINK_RATE))
3221         {
3222           double tti = get_sink_rate_tti();
3223           if (tti <= sink_rate_tti * 0.8)
3224             {
3225               // ~20% degradation, warn again and store the new reference tti
3226               sink_rate_tti = tti;
3227               mk_repeat_alert(mk_alert(MODE1_SINK_RATE));
3228             }
3229         }
3230       else
3231         {
3232           if (sink_rate_timer.start_or_elapsed() >= 0.8)
3233             {
3234               mk_set_alerts(mk_alert(MODE1_SINK_RATE));
3235               sink_rate_tti = get_sink_rate_tti();
3236             }
3237         }
3238     }
3239   else
3240     {
3241       sink_rate_timer.stop();
3242       mk_unset_alerts(mk_alert(MODE1_SINK_RATE));
3243     }
3244 }
3245
3246 void
3247 MK_VIII::Mode1Handler::update ()
3248 {
3249   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3250     return;
3251
3252   update_pull_up();
3253   update_sink_rate();
3254 }
3255
3256 ///////////////////////////////////////////////////////////////////////////////
3257 // Mode2Handler ///////////////////////////////////////////////////////////////
3258 ///////////////////////////////////////////////////////////////////////////////
3259
3260 double
3261 MK_VIII::Mode2Handler::ClosureRateFilter::limit_radio_altitude_rate (double r)
3262 {
3263   // Limit radio altitude rate according to aircraft configuration,
3264   // allowing maximum sensitivity during cruise while providing
3265   // progressively less sensitivity during the landing phases of
3266   // flight.
3267
3268   if (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
3269     {                           // gs deviation <= +- 2 dots
3270       if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3271         SG_CLAMP_RANGE(r, -1000.0, 3000.0);
3272       else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3273         SG_CLAMP_RANGE(r, 0.0, 4000.0);
3274       else
3275         SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3276     }
3277   else
3278     {                           // no ILS, or gs deviation > +- 2 dots
3279       if (mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3280         SG_CLAMP_RANGE(r, 0.0, 4000.0);
3281       else if (mk_dinput(landing_gear) || mk->io_handler.flaps_down())
3282         SG_CLAMP_RANGE(r, 1000.0, 5000.0);
3283       // else no clamp
3284     }
3285
3286   return r;
3287 }
3288
3289 void
3290 MK_VIII::Mode2Handler::ClosureRateFilter::init ()
3291 {
3292   timer.stop();
3293   last_ra.set(&mk_data(radio_altitude));
3294   last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3295   ra_filter.reset();
3296   ba_filter.reset();
3297   output.unset();
3298 }
3299
3300 void
3301 MK_VIII::Mode2Handler::ClosureRateFilter::update ()
3302 {
3303   double elapsed = timer.start_or_elapsed();
3304   if (elapsed < 1)
3305     return;
3306
3307   if (! mk_data(radio_altitude).ncd && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3308     {
3309       if (! last_ra.ncd && ! last_ba.ncd)
3310         {
3311           // compute radio and barometric altitude rates (positive value = descent)
3312           double ra_rate = -(mk_data(radio_altitude).get() - last_ra.get()) / elapsed * 60;
3313           double ba_rate = -(mk_ainput(uncorrected_barometric_altitude).get() - last_ba.get()) / elapsed * 60;
3314
3315           // limit radio altitude rate according to aircraft configuration
3316           ra_rate = limit_radio_altitude_rate(ra_rate);
3317
3318           // apply a low-pass filter to the radio altitude rate
3319           ra_rate = ra_filter.filter(ra_rate);
3320           // apply a high-pass filter to the barometric altitude rate
3321           ba_rate = ba_filter.filter(ba_rate);
3322
3323           // combine both rates to obtain a closure rate
3324           output.set(ra_rate + ba_rate);
3325         }
3326       else
3327         output.unset();
3328     }
3329   else
3330     {
3331       ra_filter.reset();
3332       ba_filter.reset();
3333       output.unset();
3334     }
3335
3336   timer.start();
3337   last_ra.set(&mk_data(radio_altitude));
3338   last_ba.set(&mk_ainput(uncorrected_barometric_altitude));
3339 }
3340
3341 bool
3342 MK_VIII::Mode2Handler::b_conditions ()
3343 {
3344   return mk->io_handler.flaps_down()
3345     || (! mk_data(glideslope_deviation_dots).ncd && fabs(mk_data(glideslope_deviation_dots).get()) < 2)
3346     || takeoff_timer.running;
3347 }
3348
3349 bool
3350 MK_VIII::Mode2Handler::is_a ()
3351 {
3352   if (! mk->io_handler.gpws_inhibit()
3353       && ! mk->state_handler.ground // [1]
3354       && ! mk_data(radio_altitude).ncd
3355       && ! mk_ainput(computed_airspeed).ncd
3356       && ! closure_rate_filter.output.ncd
3357       && ! b_conditions())
3358     {
3359       if (mk_data(radio_altitude).get() < 1220)
3360         {
3361           if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3362             return true;
3363         }
3364       else
3365         {
3366           double upper_limit;
3367
3368           if (mk_ainput(computed_airspeed).get() <= conf->airspeed1)
3369             upper_limit = 1650;
3370           else if (mk_ainput(computed_airspeed).get() >= conf->airspeed2)
3371             upper_limit = 2450;
3372           else
3373             upper_limit = 1650 + 8.9 * (mk_ainput(computed_airspeed).get() - conf->airspeed1);
3374
3375           if (mk_data(radio_altitude).get() < upper_limit)
3376             {
3377               if (mk_data(radio_altitude).get() < 522 + 0.1968 * closure_rate_filter.output.get())
3378                 return true;
3379             }
3380         }
3381     }
3382
3383   return false;
3384 }
3385
3386 bool
3387 MK_VIII::Mode2Handler::is_b ()
3388 {
3389   if (! mk->io_handler.gpws_inhibit()
3390       && ! mk->state_handler.ground // [1]
3391       && ! mk_data(radio_altitude).ncd
3392       && ! mk_data(barometric_altitude_rate).ncd
3393       && ! closure_rate_filter.output.ncd
3394       && b_conditions()
3395       && mk_data(radio_altitude).get() < 789)
3396     {
3397       double lower_limit;
3398
3399       if (mk->io_handler.flaps_down())
3400         {
3401           if (mk_data(barometric_altitude_rate).get() > -400)
3402             lower_limit = 200;
3403           else if (mk_data(barometric_altitude_rate).get() < -1000)
3404             lower_limit = 600;
3405           else
3406             lower_limit = -66.777 - 0.667 * mk_data(barometric_altitude_rate).get();
3407         }
3408       else
3409         lower_limit = 30;
3410
3411       if (mk_data(radio_altitude).get() > lower_limit)
3412         {
3413           if (mk_data(radio_altitude).get() < -1579 + 0.7895 * closure_rate_filter.output.get())
3414             return true;
3415         }
3416     }
3417
3418   return false;
3419 }
3420
3421 void
3422 MK_VIII::Mode2Handler::check_pull_up (unsigned int preface_alert,
3423                                       unsigned int alert)
3424 {
3425   if (pull_up_timer.running)
3426     {
3427       if (pull_up_timer.elapsed() >= 1)
3428         {
3429           mk_unset_alerts(preface_alert);
3430           mk_set_alerts(alert);
3431         }
3432     }
3433   else
3434     {
3435       if (! mk->voice_player.voice)
3436         pull_up_timer.start();
3437     }
3438 }
3439
3440 void
3441 MK_VIII::Mode2Handler::update_a ()
3442 {
3443   if (is_a())
3444     {
3445       if (mk_test_alert(MODE2A_PREFACE))
3446         check_pull_up(mk_alert(MODE2A_PREFACE), mk_alert(MODE2A));
3447       else if (! mk_test_alert(MODE2A))
3448         {
3449           mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3450           mk_set_alerts(mk_alert(MODE2A_PREFACE));
3451           a_start_time = globals->get_sim_time_sec();
3452           pull_up_timer.stop();
3453         }
3454     }
3455   else
3456     {
3457       if (mk_test_alert(MODE2A_ALTITUDE_GAIN))
3458         {
3459           if (mk->io_handler.gpws_inhibit()
3460               || mk->state_handler.ground // [1]
3461               || a_altitude_gain_timer.elapsed() >= 45
3462               || mk_data(radio_altitude).ncd
3463               || mk_ainput(uncorrected_barometric_altitude).ncd
3464               || mk_ainput(uncorrected_barometric_altitude).get() - a_altitude_gain_alt >= 300
3465               // [PILOT] page 12: "the visual alert will remain on
3466               // until [...] landing flaps or the flap override switch
3467               // is activated"
3468               || mk->io_handler.flaps_down())
3469             {
3470               // exit altitude gain mode
3471               a_altitude_gain_timer.stop();
3472               mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN) | mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3473             }
3474           else
3475             {
3476               // [SPEC] 6.2.2.2: "If the terrain starts to fall away
3477               // during this altitude gain time, the voice will shut
3478               // off"
3479               if (closure_rate_filter.output.get() < 0)
3480                 mk_unset_alerts(mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING));
3481             }
3482         }
3483       else if (mk_test_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A)))
3484         {
3485           mk_unset_alerts(mk_alert(MODE2A_PREFACE) | mk_alert(MODE2A));
3486
3487           if (! mk->io_handler.gpws_inhibit()
3488               && ! mk->state_handler.ground // [1]
3489               && globals->get_sim_time_sec() - a_start_time > 3
3490               && ! mk->io_handler.flaps_down()
3491               && ! mk_data(radio_altitude).ncd
3492               && ! mk_ainput(uncorrected_barometric_altitude).ncd)
3493             {
3494               // [SPEC] 6.2.2.2: mode 2A envelope violated for more
3495               // than 3 seconds, enable altitude gain feature
3496
3497               a_altitude_gain_timer.start();
3498               a_altitude_gain_alt = mk_ainput(uncorrected_barometric_altitude).get();
3499
3500               unsigned int alerts = mk_alert(MODE2A_ALTITUDE_GAIN);
3501               if (closure_rate_filter.output.get() > 0)
3502                 alerts |= mk_alert(MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING);
3503
3504               mk_set_alerts(alerts);
3505             }
3506         }
3507     }
3508 }
3509
3510 void
3511 MK_VIII::Mode2Handler::update_b ()
3512 {
3513   bool b = is_b();
3514
3515   // handle normal mode
3516
3517   if (b && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down()))
3518     {
3519       if (mk_test_alert(MODE2B_PREFACE))
3520         check_pull_up(mk_alert(MODE2B_PREFACE), mk_alert(MODE2B));
3521       else if (! mk_test_alert(MODE2B))
3522         {
3523           mk_set_alerts(mk_alert(MODE2B_PREFACE));
3524           pull_up_timer.stop();
3525         }
3526     }
3527   else
3528     mk_unset_alerts(mk_alert(MODE2B_PREFACE) | mk_alert(MODE2B));
3529
3530   // handle landing mode ([SPEC] 6.2.2.3: "If both landing gear and
3531   // flaps are in the landing configuration, then the message will be
3532   // Terrain")
3533
3534   if (b && mk_dinput(landing_gear) && mk->io_handler.flaps_down())
3535     mk_set_alerts(mk_alert(MODE2B_LANDING_MODE));
3536   else
3537     mk_unset_alerts(mk_alert(MODE2B_LANDING_MODE));
3538 }
3539
3540 void
3541 MK_VIII::Mode2Handler::boot ()
3542 {
3543   closure_rate_filter.init();
3544 }
3545
3546 void
3547 MK_VIII::Mode2Handler::power_off ()
3548 {
3549   // [SPEC] 6.0.4: "This latching function is not power saved and a
3550   // system reset will force it false."
3551   takeoff_timer.stop();
3552 }
3553
3554 void
3555 MK_VIII::Mode2Handler::leave_ground ()
3556 {
3557   // takeoff, reset timer
3558   takeoff_timer.start();
3559 }
3560
3561 void
3562 MK_VIII::Mode2Handler::enter_takeoff ()
3563 {
3564   // reset timer, in case it's a go-around
3565   takeoff_timer.start();
3566 }
3567
3568 void
3569 MK_VIII::Mode2Handler::update ()
3570 {
3571   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3572     return;
3573
3574   closure_rate_filter.update();
3575
3576   if (takeoff_timer.running && takeoff_timer.elapsed() >= 60)
3577     takeoff_timer.stop();
3578
3579   update_a();
3580   update_b();
3581 }
3582
3583 ///////////////////////////////////////////////////////////////////////////////
3584 // Mode3Handler ///////////////////////////////////////////////////////////////
3585 ///////////////////////////////////////////////////////////////////////////////
3586
3587 double
3588 MK_VIII::Mode3Handler::max_alt_loss (double _bias)
3589 {
3590   return conf->max_alt_loss(mk->io_handler.flap_override(), mk_data(radio_altitude).get()) + mk_data(radio_altitude).get() * _bias;
3591 }
3592
3593 double
3594 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
3595 {
3596   if (mk_data(radio_altitude).get() > 0)
3597     while (alt_loss > max_alt_loss(initial_bias))
3598       initial_bias += 0.2;
3599
3600   return initial_bias;
3601 }
3602
3603 bool
3604 MK_VIII::Mode3Handler::is (double *alt_loss)
3605 {
3606   if (has_descent_alt)
3607     {
3608       int max_agl = conf->max_agl(mk->io_handler.flap_override());
3609
3610       if (mk_ainput(uncorrected_barometric_altitude).ncd
3611           || mk_ainput(uncorrected_barometric_altitude).get() > descent_alt
3612           || mk_data(radio_altitude).ncd
3613           || mk_data(radio_altitude).get() > max_agl)
3614         {
3615           armed = false;
3616           has_descent_alt = false;
3617         }
3618       else
3619         {
3620           // gear/flaps: [SPEC] 1.3.1.3
3621           if (! mk->io_handler.gpws_inhibit()
3622               && ! mk->state_handler.ground // [1]
3623               && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3624               && ! mk_data(barometric_altitude_rate).ncd
3625               && ! mk_ainput(uncorrected_barometric_altitude).ncd
3626               && ! mk_data(radio_altitude).ncd
3627               && mk_data(barometric_altitude_rate).get() < 0)
3628             {
3629               double _alt_loss = descent_alt - mk_ainput(uncorrected_barometric_altitude).get();
3630               if (armed || (mk_data(radio_altitude).get() > conf->min_agl
3631                             && mk_data(radio_altitude).get() < max_agl
3632                             && _alt_loss > max_alt_loss(0)))
3633                 {
3634                   *alt_loss = _alt_loss;
3635                   return true;
3636                 }
3637             }
3638         }
3639     }
3640   else
3641     {
3642       if (! mk_data(barometric_altitude_rate).ncd
3643           && ! mk_ainput(uncorrected_barometric_altitude).ncd
3644           && mk_data(barometric_altitude_rate).get() < 0)
3645         {
3646           has_descent_alt = true;
3647           descent_alt = mk_ainput(uncorrected_barometric_altitude).get();
3648         }
3649     }
3650
3651   return false;
3652 }
3653
3654 void
3655 MK_VIII::Mode3Handler::enter_takeoff ()
3656 {
3657   armed = false;
3658   has_descent_alt = false;
3659 }
3660
3661 void
3662 MK_VIII::Mode3Handler::update ()
3663 {
3664   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3665     return;
3666
3667   if (mk->state_handler.takeoff)
3668     {
3669       double alt_loss;
3670
3671       if (! mk->state_handler.ground /* [1] */ && is(&alt_loss))
3672         {
3673           if (mk_test_alert(MODE3))
3674             {
3675               double new_bias = get_bias(bias, alt_loss);
3676               if (new_bias > bias)
3677                 {
3678                   bias = new_bias;
3679                   mk_repeat_alert(mk_alert(MODE3));
3680                 }
3681             }
3682           else
3683             {
3684               armed = true;
3685               bias = get_bias(0.2, alt_loss);
3686               mk_set_alerts(mk_alert(MODE3));
3687             }
3688
3689           return;
3690         }
3691     }
3692
3693   mk_unset_alerts(mk_alert(MODE3));
3694 }
3695
3696 ///////////////////////////////////////////////////////////////////////////////
3697 // Mode4Handler ///////////////////////////////////////////////////////////////
3698 ///////////////////////////////////////////////////////////////////////////////
3699
3700 // FIXME: for turbofans, the upper limit should be reduced from 1000
3701 // to 800 ft if a sudden change in radio altitude is detected, in
3702 // order to reduce nuisance alerts caused by overflying another
3703 // aircraft (see [PILOT] page 16).
3704
3705 double
3706 MK_VIII::Mode4Handler::get_upper_agl (const EnvelopesConfiguration *c)
3707 {
3708   if (mk_ainput(computed_airspeed).get() >= c->airspeed2)
3709     return c->min_agl3;
3710   else if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3711     return c->min_agl2(mk_ainput(computed_airspeed).get());
3712   else
3713     return c->min_agl1;
3714 }
3715
3716 const MK_VIII::Mode4Handler::EnvelopesConfiguration *
3717 MK_VIII::Mode4Handler::get_ab_envelope ()
3718 {
3719   // [SPEC] 6.2.4.1: "The Mode 4B envelope can also be selected by
3720   // setting flaps to landing configuration or by selecting flap
3721   // override."
3722   return mk_dinput(landing_gear) || mk->io_handler.flaps_down()
3723     ? conf.modes->b
3724     : conf.modes->ac;
3725 }
3726
3727 double
3728 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
3729 {
3730   while (mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)
3731     initial_bias += 0.2;
3732
3733   return initial_bias;
3734 }
3735
3736 void
3737 MK_VIII::Mode4Handler::handle_alert (unsigned int alert,
3738                                      double min_agl,
3739                                      double *bias)
3740 {
3741   if (mk_test_alerts(alert))
3742     {
3743       double new_bias = get_bias(*bias, min_agl);
3744       if (new_bias > *bias)
3745         {
3746           *bias = new_bias;
3747           mk_repeat_alert(alert);
3748         }
3749     }
3750   else
3751     {
3752       *bias = get_bias(0.2, min_agl);
3753       mk_set_alerts(alert);
3754     }
3755 }
3756
3757 void
3758 MK_VIII::Mode4Handler::update_ab ()
3759 {
3760   if (! mk->io_handler.gpws_inhibit()
3761       && ! mk->state_handler.ground
3762       && ! mk->state_handler.takeoff
3763       && ! mk_data(radio_altitude).ncd
3764       && ! mk_ainput(computed_airspeed).ncd
3765       && mk_data(radio_altitude).get() > 30)
3766     {
3767       const EnvelopesConfiguration *c = get_ab_envelope();
3768       if (mk_ainput(computed_airspeed).get() < c->airspeed1)
3769         {
3770           if (mk_dinput(landing_gear))
3771             {
3772               if (! mk->io_handler.flaps_down() && mk_data(radio_altitude).get() < c->min_agl1)
3773                 {
3774                   handle_alert(mk_alert(MODE4_TOO_LOW_FLAPS), c->min_agl1, &ab_bias);
3775                   return;
3776                 }
3777             }
3778           else
3779             {
3780               if (mk_data(radio_altitude).get() < c->min_agl1)
3781                 {
3782                   handle_alert(mk_alert(MODE4_TOO_LOW_GEAR), c->min_agl1, &ab_bias);
3783                   return;
3784                 }
3785             }
3786         }
3787     }
3788
3789   mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
3790   ab_bias=0.0;
3791 }
3792
3793 void
3794 MK_VIII::Mode4Handler::update_ab_expanded ()
3795 {
3796   if (! mk->io_handler.gpws_inhibit()
3797       && ! mk->state_handler.ground
3798       && ! mk->state_handler.takeoff
3799       && ! mk_data(radio_altitude).ncd
3800       && ! mk_ainput(computed_airspeed).ncd
3801       && mk_data(radio_altitude).get() > 30)
3802     {
3803       const EnvelopesConfiguration *c = get_ab_envelope();
3804       if (mk_ainput(computed_airspeed).get() >= c->airspeed1)
3805         {
3806           double min_agl = get_upper_agl(c);
3807           if (mk_data(radio_altitude).get() < min_agl)
3808             {
3809               handle_alert(mk_alert(MODE4AB_TOO_LOW_TERRAIN), min_agl, &ab_expanded_bias);
3810               return;
3811             }
3812         }
3813     }
3814
3815   mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
3816   ab_expanded_bias=0.0;
3817 }
3818
3819 void
3820 MK_VIII::Mode4Handler::update_c ()
3821 {
3822   if (! mk->io_handler.gpws_inhibit()
3823       && ! mk->state_handler.ground // [1]
3824       && mk->state_handler.takeoff
3825       && ! mk_data(radio_altitude).ncd
3826       && ! mk_data(terrain_clearance).ncd
3827       && mk_data(radio_altitude).get() > 30
3828       && (! mk_dinput(landing_gear) || ! mk->io_handler.flaps_down())
3829       && mk_data(radio_altitude).get() < get_upper_agl(conf.modes->ac)
3830       && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
3831     handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
3832   else
3833   {
3834     mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
3835     c_bias=0.0;
3836   }
3837 }
3838
3839 void
3840 MK_VIII::Mode4Handler::update ()
3841 {
3842   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3843     return;
3844
3845   update_ab();
3846   update_ab_expanded();
3847   update_c();
3848 }
3849
3850 ///////////////////////////////////////////////////////////////////////////////
3851 // Mode5Handler ///////////////////////////////////////////////////////////////
3852 ///////////////////////////////////////////////////////////////////////////////
3853
3854 bool
3855 MK_VIII::Mode5Handler::is_hard ()
3856 {
3857   if (mk_data(radio_altitude).get() > 30
3858       && mk_data(radio_altitude).get() < 300
3859       && mk_data(glideslope_deviation_dots).get() > 2)
3860     {
3861       if (mk_data(radio_altitude).get() < 150)
3862         {
3863           if (mk_data(radio_altitude).get() > 293 - 71.43 * mk_data(glideslope_deviation_dots).get())
3864             return true;
3865         }
3866       else
3867         return true;
3868     }
3869
3870   return false;
3871 }
3872
3873 bool
3874 MK_VIII::Mode5Handler::is_soft (double bias)
3875 {
3876   if (mk_data(radio_altitude).get() > 30)
3877     {
3878       double bias_dots = 1.3 * bias;
3879       if (mk_data(glideslope_deviation_dots).get() > 1.3 + bias_dots)
3880         {
3881           if (mk_data(radio_altitude).get() < 150)
3882             {
3883               if (mk_data(radio_altitude).get() > 243 - 71.43 * (mk_data(glideslope_deviation_dots).get() - bias_dots))
3884                 return true;
3885             }
3886           else
3887             {
3888               double upper_limit;
3889
3890               if (mk_data(barometric_altitude_rate).ncd)
3891                 upper_limit = 1000;
3892               else
3893                 {
3894                   if (mk_data(barometric_altitude_rate).get() >= 0)
3895                     upper_limit = 500;
3896                   else if (mk_data(barometric_altitude_rate).get() < -500)
3897                     upper_limit = 1000;
3898                   else
3899                     upper_limit = -mk_data(barometric_altitude_rate).get() + 500;
3900                 }
3901
3902               if (mk_data(radio_altitude).get() < upper_limit)
3903                 return true;
3904             }
3905         }
3906     }
3907
3908   return false;
3909 }
3910
3911 double
3912 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
3913 {
3914   while (is_soft(initial_bias))
3915     initial_bias += 0.2;
3916
3917   return initial_bias;
3918 }
3919
3920 void
3921 MK_VIII::Mode5Handler::update_hard (bool is)
3922 {
3923   if (is)
3924     {
3925       if (! mk_test_alert(MODE5_HARD))
3926         {
3927           if (hard_timer.start_or_elapsed() >= 0.8)
3928             mk_set_alerts(mk_alert(MODE5_HARD));
3929         }
3930     }
3931   else
3932     {
3933       hard_timer.stop();
3934       mk_unset_alerts(mk_alert(MODE5_HARD));
3935     }
3936 }
3937
3938 void
3939 MK_VIII::Mode5Handler::update_soft (bool is)
3940 {
3941   if (is)
3942     {
3943       if (! mk_test_alert(MODE5_SOFT))
3944         {
3945           double new_bias = get_soft_bias(soft_bias);
3946           if (new_bias > soft_bias)
3947             {
3948               soft_bias = new_bias;
3949               mk_repeat_alert(mk_alert(MODE5_SOFT));
3950             }
3951         }
3952       else
3953         {
3954           if (soft_timer.start_or_elapsed() >= 0.8)
3955             {
3956               soft_bias = get_soft_bias(0.2);
3957               mk_set_alerts(mk_alert(MODE5_SOFT));
3958             }
3959         }
3960     }
3961   else
3962     {
3963       soft_timer.stop();
3964       mk_unset_alerts(mk_alert(MODE5_SOFT));
3965     }
3966 }
3967
3968 void
3969 MK_VIII::Mode5Handler::update ()
3970 {
3971   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
3972     return;
3973
3974   if (! mk->io_handler.gpws_inhibit()
3975       && ! mk->state_handler.ground // [1]
3976       && ! mk_dinput(glideslope_inhibit) // not on backcourse
3977       && ! mk_data(radio_altitude).ncd
3978       && ! mk_data(glideslope_deviation_dots).ncd
3979       && (! mk->io_handler.conf.localizer_enabled
3980           || mk_data(localizer_deviation_dots).ncd
3981           || mk_data(radio_altitude).get() < 500
3982           || fabs(mk_data(localizer_deviation_dots).get()) < 2)
3983       && (! mk->state_handler.takeoff || mk->io_handler.flaps_down())
3984       && mk_dinput(landing_gear)
3985       && ! mk_doutput(glideslope_cancel))
3986     {
3987       update_hard(is_hard());
3988       update_soft(is_soft(0));
3989     }
3990   else
3991     {
3992       update_hard(false);
3993       update_soft(false);
3994     }
3995 }
3996
3997 ///////////////////////////////////////////////////////////////////////////////
3998 // Mode6Handler ///////////////////////////////////////////////////////////////
3999 ///////////////////////////////////////////////////////////////////////////////
4000
4001 // keep sorted in descending order
4002 const int MK_VIII::Mode6Handler::altitude_callout_definitions[] = 
4003   { 1000, 500, 400, 300, 200, 100, 50, 40, 30, 20, 10 };
4004
4005 void
4006 MK_VIII::Mode6Handler::reset_minimums ()
4007 {
4008   minimums_issued = false;
4009 }
4010
4011 void
4012 MK_VIII::Mode6Handler::reset_altitude_callouts ()
4013 {
4014   for (unsigned i = 0; i < n_altitude_callouts; i++)
4015     altitude_callouts_issued[i] = false;
4016 }
4017
4018 bool
4019 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
4020 {
4021   for (unsigned i = 0; i < n_altitude_callouts; i++)
4022     if (mk->voice_player.voice == mk_altitude_voice(i)
4023         || mk->voice_player.next_voice == mk_altitude_voice(i))
4024       return true;
4025
4026   return false;
4027 }
4028
4029 bool
4030 MK_VIII::Mode6Handler::is_near_minimums (double callout)
4031 {
4032   // [SPEC] 6.4.2
4033
4034   if (! mk_data(decision_height).ncd)
4035     {
4036       double diff = callout - mk_data(decision_height).get();
4037
4038       if (mk_data(radio_altitude).get() >= 200)
4039         {
4040           if (fabs(diff) <= 30)
4041             return true;
4042         }
4043       else
4044         {
4045           if (diff >= -3 && diff <= 6)
4046             return true;
4047         }
4048     }
4049
4050   return false;
4051 }
4052
4053 bool
4054 MK_VIII::Mode6Handler::is_outside_band (double elevation, double callout)
4055 {
4056   // [SPEC] 6.4.2
4057   return elevation < callout - (elevation > 150 ? 20 : 10);
4058 }
4059
4060 bool
4061 MK_VIII::Mode6Handler::inhibit_smart_500 ()
4062 {
4063   // [SPEC] 6.4.3
4064
4065   if (! mk_data(glideslope_deviation_dots).ncd
4066       && ! mk_dinput(glideslope_inhibit) // backcourse
4067       && ! mk_doutput(glideslope_cancel))
4068     {
4069       if (mk->io_handler.conf.localizer_enabled
4070           && ! mk_data(localizer_deviation_dots).ncd)
4071         {
4072           if (fabs(mk_data(localizer_deviation_dots).get()) <= 2)
4073             return true;
4074         }
4075       else
4076         {
4077           if (fabs(mk_data(glideslope_deviation_dots).get()) <= 2)
4078             return true;
4079         }
4080     }
4081
4082   return false;
4083 }
4084
4085 void
4086 MK_VIII::Mode6Handler::boot ()
4087 {
4088   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4089     return;
4090
4091   last_decision_height = mk_dinput(decision_height);
4092   last_radio_altitude.set(&mk_data(radio_altitude));
4093
4094   // [SPEC] 6.4.2
4095   for (unsigned i = 0; i < n_altitude_callouts; i++)
4096     altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
4097       && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
4098
4099   // extrapolated from [SPEC] 6.4.2
4100   minimums_issued = mk_dinput(decision_height);
4101
4102   if (conf.above_field_voice)
4103     {
4104       update_runway();
4105       get_altitude_above_field(&last_altitude_above_field);
4106       // extrapolated from [SPEC] 6.4.2
4107       above_field_issued = ! last_altitude_above_field.ncd
4108         && last_altitude_above_field.get() < 550;
4109     }
4110 }
4111
4112 void
4113 MK_VIII::Mode6Handler::power_off ()
4114 {
4115   runway_timer.stop();
4116 }
4117
4118 void
4119 MK_VIII::Mode6Handler::enter_takeoff ()
4120 {
4121   reset_altitude_callouts();    // [SPEC] 6.4.2
4122   reset_minimums();             // omitted by [SPEC]; common sense
4123 }
4124
4125 void
4126 MK_VIII::Mode6Handler::leave_takeoff ()
4127 {
4128   reset_altitude_callouts();    // [SPEC] 6.0.2
4129   reset_minimums();             // [SPEC] 6.0.2
4130 }
4131
4132 void
4133 MK_VIII::Mode6Handler::set_volume (float volume)
4134 {
4135   mk_voice(minimums_minimums)->set_volume(volume);
4136   mk_voice(five_hundred_above)->set_volume(volume);
4137   for (unsigned i = 0; i < n_altitude_callouts; i++)
4138     mk_altitude_voice(i)->set_volume(volume);
4139 }
4140
4141 bool
4142 MK_VIII::Mode6Handler::altitude_callouts_enabled ()
4143 {
4144   if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
4145     return true;
4146
4147   for (unsigned i = 0; i < n_altitude_callouts; i++)
4148     if (conf.altitude_callouts_enabled[i])
4149       return true;
4150
4151   return false;
4152 }
4153
4154 void
4155 MK_VIII::Mode6Handler::update_minimums ()
4156 {
4157   if (! mk->io_handler.gpws_inhibit()
4158       && (mk->voice_player.voice == mk_voice(minimums_minimums)
4159           || mk->voice_player.next_voice == mk_voice(minimums_minimums)))
4160     goto end;
4161
4162   if (! mk->io_handler.gpws_inhibit()
4163       && ! mk->state_handler.ground // [1]
4164       && conf.minimums_enabled
4165       && ! minimums_issued
4166       && mk_dinput(landing_gear)
4167       && mk_dinput(decision_height)
4168       && ! last_decision_height)
4169     {
4170       minimums_issued = true;
4171
4172       // If an altitude callout is playing, stop it now so that the
4173       // minimums callout can be played (note that proper connection
4174       // and synchronization of the radio-altitude ARINC 529 input,
4175       // decision-height discrete and decision-height ARINC 529 input
4176       // will prevent an altitude callout from being played near the
4177       // decision height).
4178
4179       if (is_playing_altitude_callout())
4180         mk->voice_player.stop(VoicePlayer::STOP_NOW);
4181
4182       mk_set_alerts(mk_alert(MODE6_MINIMUMS));
4183     }
4184   else
4185     mk_unset_alerts(mk_alert(MODE6_MINIMUMS));
4186
4187  end:
4188   last_decision_height = mk_dinput(decision_height);
4189 }
4190
4191 void
4192 MK_VIII::Mode6Handler::update_altitude_callouts ()
4193 {
4194   if (! mk->io_handler.gpws_inhibit() && is_playing_altitude_callout())
4195     goto end;
4196
4197   if (! mk->io_handler.gpws_inhibit()
4198       && ! mk->state_handler.ground // [1]
4199       && ! mk_data(radio_altitude).ncd)
4200     for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
4201       if ((conf.altitude_callouts_enabled[i]
4202            || (altitude_callout_definitions[i] == 500
4203                && conf.smart_500_enabled))
4204           && ! altitude_callouts_issued[i]
4205           && (last_radio_altitude.ncd
4206               || last_radio_altitude.get() > altitude_callout_definitions[i]))
4207         {
4208           // lock out all callouts superior or equal to this one
4209           for (unsigned j = 0; j <= i; j++)
4210             altitude_callouts_issued[j] = true;
4211
4212           altitude_callouts_issued[i] = true;
4213           if (! is_near_minimums(altitude_callout_definitions[i])
4214               && ! is_outside_band(mk_data(radio_altitude).get(), altitude_callout_definitions[i])
4215               && (! conf.smart_500_enabled
4216                   || altitude_callout_definitions[i] != 500
4217                   || ! inhibit_smart_500()))
4218             {
4219               mk->alert_handler.set_altitude_callout_alert(mk_altitude_voice(i));
4220               goto end;
4221             }
4222         }
4223
4224   mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4225
4226  end:
4227   last_radio_altitude.set(&mk_data(radio_altitude));
4228 }
4229
4230 bool
4231 MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
4232 {
4233   if (_runway->lengthFt() < mk->conf.runway_database)
4234     return false;
4235
4236   SGGeod pos(
4237     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
4238   
4239   // get distance to threshold
4240   double distance, az1, az2;
4241   SGGeodesy::inverse(pos, _runway->threshold(), az1, az2, distance);
4242   return distance * SG_METER_TO_NM <= 5;
4243 }
4244
4245 bool
4246 MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
4247 {
4248   for (unsigned int r=0; r<airport->numRunways(); ++r) {
4249     FGRunway* rwy(airport->getRunwayByIndex(r));
4250     
4251     if (test_runway(rwy)) return true;
4252   }
4253
4254   return false;
4255 }
4256
4257 bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
4258 {
4259   bool ok = self->test_airport(a);
4260   return ok;
4261 }
4262
4263 void
4264 MK_VIII::Mode6Handler::update_runway ()
4265 {
4266  
4267   if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4268      has_runway.unset();
4269      return;
4270   }
4271
4272   // Search for the closest runway threshold in range 5
4273   // nm. Passing 30nm to
4274   // get_closest_airport() provides enough margin for large
4275   // airports, which may have a runway located far away from the
4276   // airport's reference point.
4277   AirportFilter filter(this);
4278   FGPositionedRef apt = FGPositioned::findClosest(
4279     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4280     30.0, &filter);
4281   if (apt) {
4282     runway.elevation = apt->elevation();
4283   }
4284   
4285   has_runway.set(apt != NULL);
4286 }
4287
4288 void
4289 MK_VIII::Mode6Handler::get_altitude_above_field (Parameter<double> *parameter)
4290 {
4291   if (! has_runway.ncd && has_runway.get() && ! mk_data(geometric_altitude).ncd)
4292     parameter->set(mk_data(geometric_altitude).get() - runway.elevation);
4293   else
4294     parameter->unset();
4295 }
4296
4297 void
4298 MK_VIII::Mode6Handler::update_above_field_callout ()
4299 {
4300   if (! conf.above_field_voice)
4301     return;
4302
4303   // update_runway() has to iterate over the whole runway database
4304   // (which contains thousands of runways), so it would be unwise to
4305   // run it every frame. Run it every 3 seconds. Note that the first
4306   // iteration is run in boot().
4307   if (runway_timer.start_or_elapsed() >= 3)
4308     {
4309       update_runway();
4310       runway_timer.start();
4311     }
4312
4313   Parameter<double> altitude_above_field;
4314   get_altitude_above_field(&altitude_above_field);
4315
4316   if (! mk->io_handler.gpws_inhibit()
4317       && (mk->voice_player.voice == conf.above_field_voice
4318           || mk->voice_player.next_voice == conf.above_field_voice))
4319     goto end;
4320
4321   // handle reset term
4322   if (above_field_issued)
4323     {
4324       if ((! has_runway.ncd && ! has_runway.get())
4325           || (! altitude_above_field.ncd && altitude_above_field.get() > 700))
4326         above_field_issued = false;
4327     }
4328
4329   if (! mk->io_handler.gpws_inhibit()
4330       && ! mk->state_handler.ground // [1]
4331       && ! above_field_issued
4332       && ! altitude_above_field.ncd
4333       && altitude_above_field.get() < 550
4334       && (last_altitude_above_field.ncd
4335           || last_altitude_above_field.get() >= 550))
4336     {
4337       above_field_issued = true;
4338
4339       if (! is_outside_band(altitude_above_field.get(), 500))
4340         {
4341           mk->alert_handler.set_altitude_callout_alert(conf.above_field_voice);
4342           goto end;
4343         }
4344     }
4345
4346   mk_unset_alerts(mk_alert(MODE6_ALTITUDE_CALLOUT));
4347
4348  end:
4349   last_altitude_above_field.set(&altitude_above_field);
4350 }
4351
4352 bool
4353 MK_VIII::Mode6Handler::is_bank_angle (double abs_roll_angle, double bias)
4354 {
4355   return conf.is_bank_angle(&mk_data(radio_altitude),
4356                             abs_roll_angle - abs_roll_angle * bias,
4357                             mk_dinput(autopilot_engaged));
4358 }
4359
4360 bool
4361 MK_VIII::Mode6Handler::is_high_bank_angle ()
4362 {
4363   return mk_data(radio_altitude).ncd || mk_data(radio_altitude).get() >= 210;
4364 }
4365
4366 unsigned int
4367 MK_VIII::Mode6Handler::get_bank_angle_alerts ()
4368 {
4369   if (! mk->io_handler.gpws_inhibit()
4370       && ! mk->state_handler.ground // [1]
4371       && ! mk_data(roll_angle).ncd)
4372     {
4373       double abs_roll_angle = fabs(mk_data(roll_angle).get());
4374
4375       if (is_bank_angle(abs_roll_angle, 0.4))
4376         return is_high_bank_angle()
4377           ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2) | mk_alert(MODE6_HIGH_BANK_ANGLE_3))
4378           : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2) | mk_alert(MODE6_LOW_BANK_ANGLE_3));
4379       else if (is_bank_angle(abs_roll_angle, 0.2))
4380         return is_high_bank_angle()
4381           ? (mk_alert(MODE6_HIGH_BANK_ANGLE_1) | mk_alert(MODE6_HIGH_BANK_ANGLE_2))
4382           : (mk_alert(MODE6_LOW_BANK_ANGLE_1) | mk_alert(MODE6_LOW_BANK_ANGLE_2));
4383       else if (is_bank_angle(abs_roll_angle, 0))
4384         return is_high_bank_angle()
4385           ? mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4386           : mk_alert(MODE6_LOW_BANK_ANGLE_1);
4387     }
4388
4389   return 0;
4390 }
4391
4392 void
4393 MK_VIII::Mode6Handler::update_bank_angle ()
4394 {
4395   if (! conf.bank_angle_enabled)
4396     return;
4397
4398   unsigned int alerts = get_bank_angle_alerts();
4399
4400   // [SPEC] 6.4.4 specifies that the non-continuous alerts
4401   // (MODE6_*_BANK_ANGLE_1 and MODE6_*_BANK_ANGLE_2) are locked out
4402   // until the initial envelope is exited.
4403
4404   if (! test_bits(alerts, mk_alert(MODE6_LOW_BANK_ANGLE_3)))
4405     mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_3));
4406   if (! test_bits(alerts, mk_alert(MODE6_HIGH_BANK_ANGLE_3)))
4407     mk_unset_alerts(mk_alert(MODE6_HIGH_BANK_ANGLE_3));
4408
4409   if (alerts != 0)
4410     mk_set_alerts(alerts);
4411   else
4412     mk_unset_alerts(mk_alert(MODE6_LOW_BANK_ANGLE_1)
4413                     | mk_alert(MODE6_HIGH_BANK_ANGLE_1)
4414                     | mk_alert(MODE6_LOW_BANK_ANGLE_2)
4415                     | mk_alert(MODE6_HIGH_BANK_ANGLE_2));
4416 }
4417
4418 void
4419 MK_VIII::Mode6Handler::update ()
4420 {
4421   if (mk->configuration_module.state != ConfigurationModule::STATE_OK)
4422     return;
4423
4424   if (! mk->state_handler.takeoff
4425       && ! mk_data(radio_altitude).ncd
4426       && mk_data(radio_altitude).get() > 1000)
4427     {
4428       reset_altitude_callouts();        // [SPEC] 6.4.2
4429       reset_minimums();                 // common sense
4430     }
4431
4432   update_minimums();
4433   update_altitude_callouts();
4434   update_above_field_callout();
4435   update_bank_angle();
4436 }
4437
4438 ///////////////////////////////////////////////////////////////////////////////
4439 // TCFHandler /////////////////////////////////////////////////////////////////
4440 ///////////////////////////////////////////////////////////////////////////////
4441
4442 // Gets the difference between the azimuth from @from_lat,@from_lon to
4443 // @to_lat,@to_lon, and @to_heading, in degrees.
4444 double
4445 MK_VIII::TCFHandler::get_azimuth_difference (double from_lat,
4446                                              double from_lon,
4447                                              double to_lat,
4448                                              double to_lon,
4449                                              double to_heading)
4450 {
4451   double az1, az2, distance;
4452   geo_inverse_wgs_84(0, from_lat, from_lon, to_lat, to_lon, &az1, &az2, &distance);
4453   return get_heading_difference(az1, to_heading);
4454 }
4455
4456 // Gets the difference between the azimuth from the current GPS
4457 // position to the center of @_runway, and the heading of @_runway, in
4458 // degrees.
4459 double
4460 MK_VIII::TCFHandler::get_azimuth_difference (const FGRunway *_runway)
4461 {
4462   return get_azimuth_difference(mk_data(gps_latitude).get(),
4463                                 mk_data(gps_longitude).get(),
4464                                 _runway->latitude(),
4465                                 _runway->longitude(),
4466                                 _runway->headingDeg());
4467 }
4468
4469 // Selects the most likely intended destination runway of @airport,
4470 // and returns it in @_runway. For each runway, the difference between
4471 // the azimuth from the current GPS position to the center of the
4472 // runway and its heading is computed. The runway having the smallest
4473 // difference wins.
4474 //
4475 // This selection algorithm is not specified in [SPEC], but
4476 // http://www.egpws.com/general_information/description/runway_select.htm
4477 // talks about automatic runway selection.
4478 FGRunway*
4479 MK_VIII::TCFHandler::select_runway (const FGAirport *airport)
4480 {
4481   FGRunway* _runway = 0;
4482   double min_diff = 360;
4483   
4484   for (unsigned int r=0; r<airport->numRunways(); ++r) {
4485     FGRunway* rwy(airport->getRunwayByIndex(r));
4486     double diff = get_azimuth_difference(rwy);
4487     if (diff < min_diff)
4488           {
4489       min_diff = diff;
4490       _runway = rwy;
4491     }
4492   } // of airport runways iteration
4493   return _runway;
4494 }
4495
4496 bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
4497 {
4498   return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
4499 }
4500    
4501 void
4502 MK_VIII::TCFHandler::update_runway ()
4503 {
4504   has_runway = false;
4505   if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
4506     return;
4507   }
4508
4509   // Search for the intended destination runway of the closest
4510   // airport in range 15 nm. Passing 30nm to get_closest_airport() 
4511   // provides enough margin for
4512   // large airports, which may have a runway located far away from
4513   // the airport's reference point.
4514   AirportFilter filter(mk);
4515   FGAirport* apt = FGAirport::findClosest(
4516     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
4517     30.0, &filter);
4518       
4519   if (!apt) return;
4520   
4521           has_runway = true;
4522
4523           FGRunway* _runway = select_runway(apt);
4524     
4525           runway.center.latitude = _runway->latitude();
4526           runway.center.longitude = _runway->longitude();
4527
4528           runway.elevation = apt->elevation();
4529
4530           double half_length_m = _runway->lengthM() * 0.5;
4531           runway.half_length = half_length_m * SG_METER_TO_NM;
4532
4533           //        b3 ________________ b0
4534           //          |                |
4535           //    h1>>> |  e1<<<<<<<<e0  | <<<h0
4536           //          |________________|
4537           //        b2                  b1
4538
4539           // get heading to runway threshold (h0) and end (h1)
4540           runway.edges[0].heading = _runway->headingDeg();
4541           runway.edges[1].heading = get_reciprocal_heading(_runway->headingDeg());
4542
4543           double az;
4544
4545           // get position of runway threshold (e0)
4546           geo_direct_wgs_84(0,
4547                             runway.center.latitude,
4548                             runway.center.longitude,
4549                             runway.edges[1].heading,
4550                             half_length_m,
4551                             &runway.edges[0].position.latitude,
4552                             &runway.edges[0].position.longitude,
4553                             &az);
4554
4555           // get position of runway end (e1)
4556           geo_direct_wgs_84(0,
4557                             runway.center.latitude,
4558                             runway.center.longitude,
4559                             runway.edges[0].heading,
4560                             half_length_m,
4561                             &runway.edges[1].position.latitude,
4562                             &runway.edges[1].position.longitude,
4563                             &az);
4564
4565           double half_width_m = _runway->widthM() * 0.5;
4566
4567           // get position of threshold bias area edges (b0 and b1)
4568           get_bias_area_edges(&runway.edges[0].position,
4569                               runway.edges[1].heading,
4570                               half_width_m,
4571                               &runway.bias_area[0],
4572                               &runway.bias_area[1]);
4573
4574           // get position of end bias area edges (b2 and b3)
4575           get_bias_area_edges(&runway.edges[1].position,
4576                               runway.edges[0].heading,
4577                               half_width_m,
4578                               &runway.bias_area[2],
4579                               &runway.bias_area[3]);
4580 }
4581
4582 void
4583 MK_VIII::TCFHandler::get_bias_area_edges (Position *edge,
4584                                           double reciprocal,
4585                                           double half_width_m,
4586                                           Position *bias_edge1,
4587                                           Position *bias_edge2)
4588 {
4589   double half_bias_width_m = k * SG_NM_TO_METER + half_width_m;
4590   double tmp_latitude = 0.0, tmp_longitude = 0.0, az = 0.0;
4591
4592   geo_direct_wgs_84(0,
4593                     edge->latitude,
4594                     edge->longitude,
4595                     reciprocal,
4596                     k * SG_NM_TO_METER,
4597                     &tmp_latitude,
4598                     &tmp_longitude,
4599                     &az);
4600   geo_direct_wgs_84(0,
4601                     tmp_latitude,
4602                     tmp_longitude,
4603                     heading_substract(reciprocal, 90),
4604                     half_bias_width_m,
4605                     &bias_edge1->latitude,
4606                     &bias_edge1->longitude,
4607                     &az);
4608   geo_direct_wgs_84(0,
4609                     tmp_latitude,
4610                     tmp_longitude,
4611                     heading_add(reciprocal, 90),
4612                     half_bias_width_m,
4613                     &bias_edge2->latitude,
4614                     &bias_edge2->longitude,
4615                     &az);
4616 }
4617
4618 // Returns true if the current GPS position is inside the edge
4619 // triangle of @edge. The edge triangle, which is specified and
4620 // represented in [SPEC] 6.3.1, is defined as an isocele right
4621 // triangle of infinite height, whose right angle is located at the
4622 // position of @edge, and whose height is parallel to the heading of
4623 // @edge.
4624 bool
4625 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
4626 {
4627   return get_azimuth_difference(mk_data(gps_latitude).get(),
4628                                 mk_data(gps_longitude).get(),
4629                                 edge->position.latitude,
4630                                 edge->position.longitude,
4631                                 edge->heading) <= 45;
4632 }
4633
4634 // Returns true if the current GPS position is inside the bias area of
4635 // the currently selected runway.
4636 bool
4637 MK_VIII::TCFHandler::is_inside_bias_area ()
4638 {
4639   double az1[4];
4640   double angles_sum = 0;
4641
4642   for (int i = 0; i < 4; i++)
4643     {
4644       double az2, distance;
4645       geo_inverse_wgs_84(0,
4646                          mk_data(gps_latitude).get(),
4647                          mk_data(gps_longitude).get(),
4648                          runway.bias_area[i].latitude,
4649                          runway.bias_area[i].longitude,
4650                          &az1[i], &az2, &distance);
4651     }
4652
4653   for (int i = 0; i < 4; i++)
4654     {
4655       double angle = az1[i == 3 ? 0 : i + 1] - az1[i];
4656       if (angle < -180)
4657         angle += 360;
4658
4659       angles_sum += angle;
4660     }
4661
4662   return angles_sum > 180;
4663 }
4664
4665 bool
4666 MK_VIII::TCFHandler::is_tcf ()
4667 {
4668   if (mk_data(radio_altitude).get() > 10)
4669     {
4670       if (has_runway)
4671         {
4672           double distance, az1, az2;
4673
4674           geo_inverse_wgs_84(0,
4675                              mk_data(gps_latitude).get(),
4676                              mk_data(gps_longitude).get(),
4677                              runway.center.latitude,
4678                              runway.center.longitude,
4679                              &az1, &az2, &distance);
4680
4681           distance *= SG_METER_TO_NM;
4682
4683           // distance to the inner envelope edge
4684           double edge_distance = distance - runway.half_length - k;
4685
4686           if (edge_distance >= 15)
4687             {
4688               if (mk_data(radio_altitude).get() < 700)
4689                 return true;
4690             }
4691           else if (edge_distance >= 12)
4692             {
4693               if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
4694                 return true;
4695             }
4696           else if (edge_distance >= 4)
4697             {
4698               if (mk_data(radio_altitude).get() < 400)
4699                 return true;
4700             }
4701           else if (edge_distance >= 2.45)
4702             {
4703               if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4704                 return true;
4705             }
4706           else
4707             {
4708               if (is_inside_edge_triangle(&runway.edges[0]) || is_inside_edge_triangle(&runway.edges[1]))
4709                 {
4710                   if (edge_distance >= 1)
4711                     {
4712                       if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
4713                         return true;
4714                     }
4715                   else if (edge_distance >= 0.05)
4716                     {
4717                       if (mk_data(radio_altitude).get() < 200 * edge_distance)
4718                         return true;
4719                     }
4720                 }
4721               else
4722                 {
4723                   if (! is_inside_bias_area())
4724                     {
4725                       if (mk_data(radio_altitude).get() < 245)
4726                         return true;
4727                     }
4728                 }
4729             }
4730         }
4731       else
4732         {
4733           if (mk_data(radio_altitude).get() < 700)
4734             return true;
4735         }
4736     }
4737
4738   return false;
4739 }
4740
4741 bool
4742 MK_VIII::TCFHandler::is_rfcf ()
4743 {
4744   if (has_runway)
4745     {
4746       double distance, az1, az2;
4747       geo_inverse_wgs_84(0,
4748                          mk_data(gps_latitude).get(),
4749                          mk_data(gps_longitude).get(),
4750                          runway.center.latitude,
4751                          runway.center.longitude,
4752                          &az1, &az2, &distance);
4753
4754       double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
4755       distance = distance * SG_METER_TO_NM - runway.half_length - krf;
4756
4757       if (distance <= 5)
4758         {
4759           double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
4760
4761           if (distance >= 1.5)
4762             {
4763               if (altitude_above_field < 300)
4764                 return true;
4765             }
4766           else if (distance >= 0)
4767             {
4768               if (altitude_above_field < 200 * distance)
4769                 return true;
4770             }
4771         }
4772     }
4773
4774   return false;
4775 }
4776
4777 void
4778 MK_VIII::TCFHandler::update ()
4779 {
4780   if (mk->configuration_module.state != ConfigurationModule::STATE_OK || ! conf.enabled)
4781     return;
4782
4783   // update_runway() has to iterate over the whole runway database
4784   // (which contains thousands of runways), so it would be unwise to
4785   // run it every frame. Run it every 3 seconds.
4786   if (! runway_timer.running || runway_timer.elapsed() >= 3)
4787     {
4788       update_runway();
4789       runway_timer.start();
4790     }
4791
4792   if (! mk_dinput(ta_tcf_inhibit)
4793       && ! mk->state_handler.ground // [1]
4794       && ! mk_data(gps_latitude).ncd
4795       && ! mk_data(gps_longitude).ncd
4796       && ! mk_data(radio_altitude).ncd
4797       && ! mk_data(geometric_altitude).ncd
4798       && ! mk_data(gps_vertical_figure_of_merit).ncd)
4799     {
4800       double *_reference;
4801
4802       if (is_tcf())
4803         _reference = mk_data(radio_altitude).get_pointer();
4804       else if (is_rfcf())
4805         _reference = mk_data(geometric_altitude).get_pointer();
4806       else
4807         _reference = NULL;
4808
4809       if (_reference)
4810         {
4811           if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
4812             {
4813               double new_bias = bias;
4814               while (*reference < initial_value - initial_value * new_bias)
4815                 new_bias += 0.2;
4816
4817               if (new_bias > bias)
4818                 {
4819                   bias = new_bias;
4820                   mk_repeat_alert(mk_alert(TCF_TOO_LOW_TERRAIN));
4821                 }
4822             }
4823           else
4824             {
4825               bias = 0.2;
4826               reference = _reference;
4827               initial_value = *reference;
4828               mk_set_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4829             }
4830
4831           return;
4832         }
4833     }
4834
4835   mk_unset_alerts(mk_alert(TCF_TOO_LOW_TERRAIN));
4836 }
4837
4838 ///////////////////////////////////////////////////////////////////////////////
4839 // MK_VIII ////////////////////////////////////////////////////////////////////
4840 ///////////////////////////////////////////////////////////////////////////////
4841
4842 MK_VIII::MK_VIII (SGPropertyNode *node)
4843   : properties_handler(this),
4844     name("mk-viii"),
4845     num(0),
4846     power_handler(this),
4847     system_handler(this),
4848     configuration_module(this),
4849     fault_handler(this),
4850     io_handler(this),
4851     voice_player(this),
4852     self_test_handler(this),
4853     alert_handler(this),
4854     state_handler(this),
4855     mode1_handler(this),
4856     mode2_handler(this),
4857     mode3_handler(this),
4858     mode4_handler(this),
4859     mode5_handler(this),
4860     mode6_handler(this),
4861     tcf_handler(this)
4862 {
4863   for (int i = 0; i < node->nChildren(); ++i)
4864     {
4865       SGPropertyNode *child = node->getChild(i);
4866       string cname = child->getName();
4867       string cval = child->getStringValue();
4868
4869       if (cname == "name")
4870         name = cval;
4871       else if (cname == "number")
4872         num = child->getIntValue();
4873       else
4874         {
4875           SG_LOG(SG_INSTR, SG_WARN, "Error in mk-viii config logic");
4876           if (name.length())
4877             SG_LOG(SG_INSTR, SG_WARN, "Section = " << name);
4878         }
4879     }
4880 }
4881
4882 void
4883 MK_VIII::init ()
4884 {
4885   properties_handler.init();
4886   voice_player.init();
4887 }
4888
4889 void
4890 MK_VIII::bind ()
4891 {
4892   SGPropertyNode *node = fgGetNode(("/instrumentation/" + name).c_str(), num, true);
4893
4894   configuration_module.bind(node);
4895   power_handler.bind(node);
4896   io_handler.bind(node);
4897   voice_player.bind(node);
4898 }
4899
4900 void
4901 MK_VIII::unbind ()
4902 {
4903   properties_handler.unbind();
4904 }
4905
4906 void
4907 MK_VIII::update (double dt)
4908 {
4909   power_handler.update();
4910   system_handler.update();
4911
4912   if (system_handler.state != SystemHandler::STATE_ON)
4913     return;
4914
4915   io_handler.update_inputs();
4916   io_handler.update_input_faults();
4917
4918   voice_player.update();
4919   state_handler.update();
4920
4921   if (self_test_handler.update())
4922     return;
4923
4924   io_handler.update_internal_latches();
4925   io_handler.update_lamps();
4926
4927   mode1_handler.update();
4928   mode2_handler.update();
4929   mode3_handler.update();
4930   mode4_handler.update();
4931   mode5_handler.update();
4932   mode6_handler.update();
4933   tcf_handler.update();
4934
4935   alert_handler.update();
4936   io_handler.update_outputs();
4937 }