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