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