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