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