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