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