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