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