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