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