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