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