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