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