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