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