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