]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/mk_viii.hxx
Jean-Yves Lefort :
[flightgear.git] / src / Instrumentation / mk_viii.hxx
1 // mk_viii.cxx -- Honeywell MK VIII EGPWS emulation
2 //
3 // Written by Jean-Yves Lefort, started September 2005.
4 //
5 // Copyright (C) 2005, 2006  Jean-Yves Lefort - jylefort@FreeBSD.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21
22 #ifndef __INSTRUMENTS_MK_VIII_HXX
23 #define __INSTRUMENTS_MK_VIII_HXX
24
25 #include <assert.h>
26
27 #include <vector>
28 #include <deque>
29 #include <map>
30
31 #include <simgear/props/props.hxx>
32 #include <simgear/sound/sample_openal.hxx>
33 #include <simgear/structure/subsystem_mgr.hxx>
34
35 SG_USING_STD(vector);
36 SG_USING_STD(deque);
37 SG_USING_STD(map);
38
39 #include "Airports/runways.hxx"
40 #include "Airports/simple.hxx"
41 #include "Main/globals.hxx"
42
43 ///////////////////////////////////////////////////////////////////////////////
44 // MK_VIII ////////////////////////////////////////////////////////////////////
45 ///////////////////////////////////////////////////////////////////////////////
46
47 class MK_VIII : public SGSubsystem
48 {
49   // keep in sync with Mode6Handler::altitude_callout_definitions[]
50   static const int n_altitude_callouts = 11;
51
52   /////////////////////////////////////////////////////////////////////////////
53   // MK_VIII::RawValueMethodsData /////////////////////////////////////////////
54   /////////////////////////////////////////////////////////////////////////////
55
56   template <class C, class VT, class DT>
57   class RawValueMethodsData : public SGRawValue<VT>
58   {
59   public:
60     typedef VT (C::*getter_t) (DT) const;
61     typedef void (C::*setter_t) (DT, VT);
62
63     RawValueMethodsData (C &obj, DT data, getter_t getter = 0, setter_t setter = 0)
64       : _obj(obj), _data(data), _getter(getter), _setter(setter) {}
65
66     virtual VT getValue () const
67     {
68       if (_getter)
69         return (_obj.*_getter)(_data);
70       else
71         return SGRawValue<VT>::DefaultValue;
72     }
73     virtual bool setValue (VT value)
74     {
75       if (_setter)
76         {
77           (_obj.*_setter)(_data, value);
78           return true;
79         }
80       else
81         return false;
82     }
83     virtual SGRawValue<VT> *clone () const 
84     {
85       return new RawValueMethodsData<C,VT,DT>(_obj, _data, _getter, _setter);
86     }
87
88   private:
89     C           &_obj;
90     DT          _data;
91     getter_t    _getter;
92     setter_t    _setter;
93   };
94
95   /////////////////////////////////////////////////////////////////////////////
96   // MK_VIII::Parameter ///////////////////////////////////////////////////////
97   /////////////////////////////////////////////////////////////////////////////
98
99   template <class T>
100   class Parameter
101   {
102     T _value;
103
104   public:
105     bool ncd;
106
107     inline Parameter ()
108       : _value(0), ncd(true) {}
109
110     inline T get () const { assert(! ncd); return _value; }
111     inline T *get_pointer () { return &_value; }
112     inline void set (T value) { ncd = false; _value = value; }
113     inline void unset () { ncd = true; }
114
115     inline void set (const Parameter<T> *parameter)
116     {
117       if (parameter->ncd)
118         unset();
119       else
120         set(parameter->get());
121     }
122
123     inline void set (const Parameter<double> *parameter, double factor)
124     {
125       if (parameter->ncd)
126         unset();
127       else
128         set(parameter->get() * factor);
129     }
130   };
131
132   /////////////////////////////////////////////////////////////////////////////
133   // MK_VIII::Sample //////////////////////////////////////////////////////////
134   /////////////////////////////////////////////////////////////////////////////
135
136   template <class T>
137   class Sample
138   {
139   public:
140     double      timestamp;
141     T           value;
142
143     inline Sample (T _value)
144       : timestamp(globals->get_sim_time_sec()), value(_value) {}
145   };
146
147   /////////////////////////////////////////////////////////////////////////////
148   // MK_VIII::Timer ///////////////////////////////////////////////////////////
149   /////////////////////////////////////////////////////////////////////////////
150
151   class Timer
152   {
153     double      start_time;
154
155   public:
156     bool        running;
157
158     inline Timer ()
159       : running(false) {}
160
161     inline void start () { running = true; start_time = globals->get_sim_time_sec(); }
162     inline void stop () { running = false; }
163     inline double elapsed () const { assert(running); return globals->get_sim_time_sec() - start_time; }
164     inline double start_or_elapsed ()
165     {
166       if (running)
167         return elapsed();
168       else
169         {
170           start();
171           return 0;
172         }
173     }
174   };
175
176   /////////////////////////////////////////////////////////////////////////////
177   // MK_VIII::PropertiesHandler ///////////////////////////////////////////////
178   /////////////////////////////////////////////////////////////////////////////
179
180   class PropertiesHandler
181   {
182     MK_VIII *mk;
183
184     vector<SGPropertyNode *> tied_properties;
185
186   public:
187     struct
188     {
189       SGPropertyNode *ai_caged;
190       SGPropertyNode *ai_roll;
191       SGPropertyNode *ai_serviceable;
192       SGPropertyNode *altimeter_altitude;
193       SGPropertyNode *altimeter_serviceable;
194       SGPropertyNode *altitude;
195       SGPropertyNode *altitude_agl;
196       SGPropertyNode *asi_serviceable;
197       SGPropertyNode *asi_speed;
198       SGPropertyNode *autopilot_heading_lock;
199       SGPropertyNode *flaps;
200       SGPropertyNode *gear_down;
201       SGPropertyNode *latitude;
202       SGPropertyNode *longitude;
203       SGPropertyNode *nav0_cdi_serviceable;
204       SGPropertyNode *nav0_gs_distance;
205       SGPropertyNode *nav0_gs_needle_deflection;
206       SGPropertyNode *nav0_gs_serviceable;
207       SGPropertyNode *nav0_has_gs;
208       SGPropertyNode *nav0_heading_needle_deflection;
209       SGPropertyNode *nav0_in_range;
210       SGPropertyNode *nav0_nav_loc;
211       SGPropertyNode *nav0_serviceable;
212       SGPropertyNode *power;
213       SGPropertyNode *replay_state;
214       SGPropertyNode *vs;
215     } external_properties;
216
217     inline PropertiesHandler (MK_VIII *device)
218       : mk(device) {}
219
220     template <class T>
221     inline void tie (SGPropertyNode *node, const SGRawValue<T> &raw_value)
222     {
223       node->tie(raw_value);
224       tied_properties.push_back(node);
225     }
226
227     template <class T>
228     inline void tie (SGPropertyNode *node,
229                      const char *relative_path,
230                      const SGRawValue<T> &raw_value)
231     {
232       tie(node->getNode(relative_path, true), raw_value);
233     }
234
235     void init ();
236     void unbind ();
237   };
238
239   /////////////////////////////////////////////////////////////////////////////
240   // MK_VIII::PowerHandler ////////////////////////////////////////////////////
241   /////////////////////////////////////////////////////////////////////////////
242
243   class PowerHandler
244   {
245     MK_VIII *mk;
246
247     bool serviceable;
248     bool powered;
249
250     Timer power_loss_timer;
251     Timer abnormal_timer;
252     Timer low_surge_timer;
253     Timer high_surge_timer;
254     Timer very_high_surge_timer;
255
256     bool handle_abnormal_voltage (bool abnormal,
257                                   Timer *timer,
258                                   double max_duration);
259
260     void power_on ();
261     void power_off ();
262
263   public:
264     inline PowerHandler (MK_VIII *device)
265       : mk(device), serviceable(false), powered(false) {}
266
267     void bind (SGPropertyNode *node);
268     void update ();
269   };
270
271   /////////////////////////////////////////////////////////////////////////////
272   // MK_VIII::SystemHandler ///////////////////////////////////////////////////
273   /////////////////////////////////////////////////////////////////////////////
274
275   class SystemHandler
276   {
277     MK_VIII *mk;
278
279     double      boot_delay;
280     Timer       boot_timer;
281
282     int         last_replay_state;
283     Timer       reposition_timer;
284
285   public:
286     typedef enum
287     {
288       STATE_OFF,
289       STATE_BOOTING,
290       STATE_ON,
291       STATE_REPOSITION
292     } State;
293
294     State state;
295
296     inline SystemHandler (MK_VIII *device)
297       : mk(device), state(STATE_OFF) {}
298
299     void power_on ();
300     void power_off ();
301     void update ();
302   };
303
304   /////////////////////////////////////////////////////////////////////////////
305   // MK_VIII::ConfigurationModule /////////////////////////////////////////////
306   /////////////////////////////////////////////////////////////////////////////
307
308   class ConfigurationModule
309   {
310   public:
311     // keep in sync with IOHandler::present_status()
312     typedef enum 
313     {
314       CATEGORY_AIRCRAFT_MODE_TYPE_SELECT,
315       CATEGORY_AIR_DATA_INPUT_SELECT,
316       CATEGORY_POSITION_INPUT_SELECT,
317       CATEGORY_ALTITUDE_CALLOUTS,
318       CATEGORY_AUDIO_MENU_SELECT,
319       CATEGORY_TERRAIN_DISPLAY_SELECT,
320       CATEGORY_OPTIONS_SELECT_GROUP_1,
321       CATEGORY_RADIO_ALTITUDE_INPUT_SELECT,
322       CATEGORY_NAVIGATION_INPUT_SELECT,
323       CATEGORY_ATTITUDE_INPUT_SELECT,
324       CATEGORY_HEADING_INPUT_SELECT,
325       CATEGORY_WINDSHEAR_INPUT_SELECT,
326       CATEGORY_INPUT_OUTPUT_DISCRETE_TYPE_SELECT,
327       CATEGORY_AUDIO_OUTPUT_LEVEL,
328       CATEGORY_UNDEFINED_INPUT_SELECT_1,
329       CATEGORY_UNDEFINED_INPUT_SELECT_2,
330       CATEGORY_UNDEFINED_INPUT_SELECT_3,
331       N_CATEGORIES
332     } Category;
333
334     typedef enum
335     {
336       STATE_OK,
337       STATE_INVALID_DATABASE,
338       STATE_INVALID_AIRCRAFT_TYPE
339     } State;
340     State state;
341
342     int effective_categories[N_CATEGORIES];
343
344     ConfigurationModule (MK_VIII *device);
345
346     void boot ();
347     void bind (SGPropertyNode *node);
348
349   private:
350     MK_VIII *mk;
351
352     int categories[N_CATEGORIES];
353
354     bool read_aircraft_mode_type_select (int value);
355     bool read_air_data_input_select (int value);
356     bool read_position_input_select (int value);
357     bool read_altitude_callouts (int value);
358     bool read_audio_menu_select (int value);
359     bool read_terrain_display_select (int value);
360     bool read_options_select_group_1 (int value);
361     bool read_radio_altitude_input_select (int value);
362     bool read_navigation_input_select (int value);
363     bool read_attitude_input_select (int value);
364     bool read_heading_input_select (int value);
365     bool read_windshear_input_select (int value);
366     bool read_input_output_discrete_type_select (int value);
367     bool read_audio_output_level (int value);
368     bool read_undefined_input_select (int value);
369
370     static bool m6_t2_is_bank_angle (Parameter<double> *agl,
371                                      double abs_roll_deg,
372                                      bool ap_engaged);
373     static bool m6_t4_is_bank_angle (Parameter<double> *agl,
374                                      double abs_roll_deg,
375                                      bool ap_engaged);
376   };
377
378   /////////////////////////////////////////////////////////////////////////////
379   // MK_VIII::FaultHandler ////////////////////////////////////////////////////
380   /////////////////////////////////////////////////////////////////////////////
381
382   class FaultHandler
383   {
384     enum
385     {
386       INOP_GPWS         = 1 << 0,
387       INOP_TAD          = 1 << 1
388     };
389
390     MK_VIII *mk;
391
392     static const unsigned int fault_inops[];
393
394     bool has_faults (unsigned int inop);
395
396   public:
397     // keep in sync with IOHandler::present_status()
398     typedef enum
399     {
400       FAULT_ALL_MODES_INHIBIT,
401       FAULT_GEAR_SWITCH,
402       FAULT_FLAPS_SWITCH,
403       FAULT_MOMENTARY_FLAP_OVERRIDE_INVALID,
404       FAULT_SELF_TEST_INVALID,
405       FAULT_GLIDESLOPE_CANCEL_INVALID,
406       FAULT_STEEP_APPROACH_INVALID,
407       FAULT_GPWS_INHIBIT,
408       FAULT_TA_TCF_INHIBIT,
409       FAULT_MODES14_INPUTS_INVALID,
410       FAULT_MODE5_INPUTS_INVALID,
411       FAULT_MODE6_INPUTS_INVALID,
412       FAULT_BANK_ANGLE_INPUTS_INVALID,
413       FAULT_TCF_INPUTS_INVALID,
414       N_FAULTS
415     } Fault;
416
417     bool faults[N_FAULTS];
418
419     inline FaultHandler (MK_VIII *device)
420       : mk(device) {}
421
422     void boot ();
423
424     void set_fault (Fault fault);
425     void unset_fault (Fault fault);
426
427     bool has_faults () const;
428   };
429
430   /////////////////////////////////////////////////////////////////////////////
431   // MK_VIII::IOHandler ///////////////////////////////////////////////////////
432   /////////////////////////////////////////////////////////////////////////////
433
434   class IOHandler
435   {
436   public:
437     enum Lamp
438     {
439       LAMP_NONE,
440       LAMP_GLIDESLOPE,
441       LAMP_CAUTION,
442       LAMP_WARNING
443     };
444
445     struct LampConfiguration
446     {
447       bool format2;
448       bool flashing;
449     };
450
451     struct FaultsConfiguration
452     {
453       double max_flaps_down_airspeed;
454       double max_gear_down_airspeed;
455     };
456
457     struct _s_Conf
458     {
459       const LampConfiguration   *lamp;
460       const FaultsConfiguration *faults;
461       bool                      flap_reversal;
462       bool                      steep_approach_enabled;
463       bool                      gpws_inhibit_enabled;
464       bool                      momentary_flap_override_enabled;
465       bool                      alternate_steep_approach;
466       bool                      use_internal_gps;
467       bool                      localizer_enabled;
468     } conf;
469
470     struct _s_input_feeders
471     {
472       struct _s_discretes
473       {
474         bool landing_gear;
475         bool landing_flaps;
476         bool glideslope_inhibit;
477         bool decision_height;
478         bool autopilot_engaged;
479       } discretes;
480
481       struct _s_arinc429
482       {
483         bool uncorrected_barometric_altitude;
484         bool barometric_altitude_rate;
485         bool radio_altitude;
486         bool glideslope_deviation;
487         bool roll_angle;
488         bool localizer_deviation;
489         bool computed_airspeed;
490         bool decision_height;
491       } arinc429;
492     } input_feeders;
493
494     struct _s_inputs
495     {
496       struct _s_discretes
497       {
498         bool landing_gear;              // appendix E 6.6.2, 3.15.1.4
499         bool landing_flaps;             // appendix E 6.6.4, 3.15.1.2
500         bool momentary_flap_override;   // appendix E 6.6.6, 3.15.1.6
501         bool self_test;                 // appendix E 6.6.7, 3.15.1.10
502         bool glideslope_inhibit;        // appendix E 6.6.11, 3.15.1.1
503         bool glideslope_cancel;         // appendix E 6.6.13, 3.15.1.5
504         bool decision_height;           // appendix E 6.6.14, 3.10.2
505         bool mode6_low_volume;          // appendix E 6.6.15, 3.15.1.7
506         bool audio_inhibit;             // appendix E 6.6.16, 3.15.1.3
507         bool ta_tcf_inhibit;            // appendix E 6.6.20, 3.15.1.9
508         bool autopilot_engaged;         // appendix E 6.6.21, 3.15.1.8
509         bool steep_approach;            // appendix E 6.6.25, 3.15.1.11
510         bool gpws_inhibit;              // appendix E 6.6.27, 3.15.1.12
511       } discretes;
512
513       struct _s_arinc429
514       {
515         Parameter<double> uncorrected_barometric_altitude;      // appendix E 6.2.1
516         Parameter<double> barometric_altitude_rate;             // appendix E 6.2.2
517         Parameter<double> gps_altitude;                         // appendix E 6.2.4
518         Parameter<double> gps_latitude;                         // appendix E 6.2.7
519         Parameter<double> gps_longitude;                        // appendix E 6.2.8
520         Parameter<double> gps_vertical_figure_of_merit;         // appendix E 6.2.13
521         Parameter<double> radio_altitude;                       // appendix E 6.2.29
522         Parameter<double> glideslope_deviation;                 // appendix E 6.2.30
523         Parameter<double> roll_angle;                           // appendix E 6.2.31
524         Parameter<double> localizer_deviation;                  // appendix E 6.2.33
525         Parameter<double> computed_airspeed;                    // appendix E 6.2.39
526         Parameter<double> decision_height;                      // appendix E 6.2.41
527       } arinc429;
528     } inputs;
529
530     struct Outputs
531     {
532       struct _s_discretes
533       {
534         bool gpws_warning;              // appendix E 7.4.1, 3.15.2.5
535         bool gpws_alert;                // appendix E 7.4.1, 3.15.2.6
536         bool audio_on;                  // appendix E 7.4.2, 3.15.2.10
537         bool gpws_inop;                 // appendix E 7.4.3, 3.15.2.3
538         bool tad_inop;                  // appendix E 7.4.3, 3.15.2.4
539         bool flap_override;             // appendix E 7.4.5, 3.15.2.8
540         bool glideslope_cancel;         // appendix E 7.4.6, 3.15.2.7
541         bool steep_approach;            // appendix E 7.4.12, 3.15.2.9
542       } discretes;
543
544       struct _s_arinc429
545       {
546         int egpws_alert_discrete_1;     // appendix E 7.1.1.1
547         int egpwc_logic_discretes;      // appendix E 7.1.1.2
548         int mode6_callouts_discrete_1;  // appendix E 7.1.1.3
549         int mode6_callouts_discrete_2;  // appendix E 7.1.1.4
550         int egpws_alert_discrete_2;     // appendix E 7.1.1.5
551         int egpwc_alert_discrete_3;     // appendix E 7.1.1.6
552       } arinc429;
553     };
554
555     Outputs outputs;
556
557     struct _s_data
558     {
559       Parameter<double> barometric_altitude_rate;
560       Parameter<double> decision_height;
561       Parameter<double> geometric_altitude;
562       Parameter<double> glideslope_deviation_dots;
563       Parameter<double> gps_altitude;
564       Parameter<double> gps_latitude;
565       Parameter<double> gps_longitude;
566       Parameter<double> gps_vertical_figure_of_merit;
567       Parameter<double> localizer_deviation_dots;
568       Parameter<double> radio_altitude;
569       Parameter<double> roll_angle;
570       Parameter<double> terrain_clearance;
571     } data;
572
573     IOHandler (MK_VIII *device);
574
575     void boot ();
576     void post_boot ();
577     void power_off ();
578
579     void enter_ground ();
580     void enter_takeoff ();
581
582     void update_inputs ();
583     void update_input_faults ();
584     void update_alternate_discrete_input (bool *ptr);
585     void update_internal_latches ();
586
587     void update_egpws_alert_discrete_1 ();
588     void update_egpwc_logic_discretes ();
589     void update_mode6_callouts_discrete_1 ();
590     void update_mode6_callouts_discrete_2 ();
591     void update_egpws_alert_discrete_2 ();
592     void update_egpwc_alert_discrete_3 ();
593     void update_outputs ();
594
595     void update_lamps ();
596     void set_lamp (Lamp lamp);
597
598     bool gpws_inhibit () const;
599     bool real_flaps_down () const;
600     bool flaps_down () const;
601     bool flap_override () const;
602     bool steep_approach () const;
603     bool momentary_steep_approach_enabled () const;
604
605     void bind (SGPropertyNode *node);
606
607   private:
608
609     ///////////////////////////////////////////////////////////////////////////
610     // MK_VIII::IOHandler::TerrainClearanceFilter /////////////////////////////
611     ///////////////////////////////////////////////////////////////////////////
612
613     class TerrainClearanceFilter
614     {
615       deque< Sample<double> >   samples;
616       double                    value;
617
618     public:
619       inline TerrainClearanceFilter ()
620         : value(0) {}
621
622       double update (double agl);
623       void reset ();
624     };
625
626     ///////////////////////////////////////////////////////////////////////////
627     // MK_VIII::IOHandler (continued) /////////////////////////////////////////
628     ///////////////////////////////////////////////////////////////////////////
629
630     MK_VIII *mk;
631
632     TerrainClearanceFilter terrain_clearance_filter;
633
634     Lamp        _lamp;
635     Timer       lamp_timer;
636
637     Timer audio_inhibit_fault_timer;
638     Timer landing_gear_fault_timer;
639     Timer flaps_down_fault_timer;
640     Timer momentary_flap_override_fault_timer;
641     Timer self_test_fault_timer;
642     Timer glideslope_cancel_fault_timer;
643     Timer steep_approach_fault_timer;
644     Timer gpws_inhibit_fault_timer;
645     Timer ta_tcf_inhibit_fault_timer;
646
647     bool last_landing_gear;
648     bool last_real_flaps_down;
649
650     deque< Sample< Parameter<double> > > altitude_samples;
651
652     struct
653     {
654       bool glideslope_cancel;
655     } power_saved;
656
657     void update_terrain_clearance ();
658     void reset_terrain_clearance ();
659
660     void handle_input_fault (bool test, FaultHandler::Fault fault);
661     void handle_input_fault (bool test,
662                              Timer *timer,
663                              double max_duration,
664                              FaultHandler::Fault fault);
665
666     void tie_input (SGPropertyNode *node,
667                     const char *name,
668                     bool *input,
669                     bool *feed = NULL);
670     void tie_input (SGPropertyNode *node,
671                     const char *name,
672                     Parameter<double> *input,
673                     bool *feed = NULL);
674     void tie_output (SGPropertyNode *node,
675                      const char *name,
676                      bool *output);
677     void tie_output (SGPropertyNode *node,
678                      const char *name,
679                      int *output);
680
681     bool get_discrete_input (bool *ptr) const;
682     void set_discrete_input (bool *ptr, bool value);
683
684     void present_status ();
685     void present_status_section (const char *name);
686     void present_status_item (const char *name, const char *value = NULL);
687     void present_status_subitem (const char *name);
688
689     bool get_present_status () const;
690     void set_present_status (bool value);
691
692     bool *get_lamp_output (Lamp lamp);
693   };
694
695   /////////////////////////////////////////////////////////////////////////////
696   // MK_VIII::VoicePlayer /////////////////////////////////////////////////////
697   /////////////////////////////////////////////////////////////////////////////
698
699   class VoicePlayer
700   {
701   public:
702
703     ///////////////////////////////////////////////////////////////////////////
704     // MK_VIII::VoicePlayer::Voice ////////////////////////////////////////////
705     ///////////////////////////////////////////////////////////////////////////
706
707     class Voice
708     {
709     public:
710
711       /////////////////////////////////////////////////////////////////////////
712       // MK_VIII::VoicePlayer::Voice::Element ////////////////////////////////////////
713       /////////////////////////////////////////////////////////////////////////
714
715       class Element
716       {
717       public:
718         bool silence;
719
720         virtual inline void play (double volume) {}
721         virtual inline void stop () {}
722         virtual bool is_playing () = 0;
723         virtual inline void set_volume (double volume) {}
724       };
725
726       /////////////////////////////////////////////////////////////////////////
727       // MK_VIII::VoicePlayer::Voice::SampleElement ///////////////////////////
728       /////////////////////////////////////////////////////////////////////////
729
730       class SampleElement : public Element
731       {
732         SGSoundSample   *_sample;
733         double          _volume;
734
735       public:
736         inline SampleElement (SGSoundSample *sample, double volume = 1.0)
737           : _sample(sample), _volume(volume) { silence = false; }
738
739         virtual inline void play (double volume) { set_volume(volume); _sample->play_once(); }
740         virtual inline void stop () { _sample->stop(); }
741         virtual inline bool is_playing () { return _sample->is_playing(); }
742         virtual inline void set_volume (double volume) { _sample->set_volume(volume * _volume); }
743       };
744
745       /////////////////////////////////////////////////////////////////////////
746       // MK_VIII::VoicePlayer::Voice::SilenceElement //////////////////////////
747       /////////////////////////////////////////////////////////////////////////
748
749       class SilenceElement : public Element
750       {
751         double _duration;
752         double start_time;
753
754       public:
755         inline SilenceElement (double duration)
756           : _duration(duration) { silence = true; }
757
758         virtual inline void play (double volume) { start_time = globals->get_sim_time_sec(); }
759         virtual inline bool is_playing () { return globals->get_sim_time_sec() - start_time < _duration; }
760       };
761
762       /////////////////////////////////////////////////////////////////////////
763       // MK_VIII::VoicePlayer::Voice (continued) //////////////////////////////
764       /////////////////////////////////////////////////////////////////////////
765
766       Element *element;
767
768       inline Voice (VoicePlayer *_player)
769         : player(_player), volume(1.0), element(NULL) {}
770
771       ~Voice ();
772
773       inline void append (Element *_element) { elements.push_back(_element); }
774
775       void play ();
776       void stop (bool now);
777       void set_volume (double _volume);
778       void volume_changed ();
779       void update ();
780
781     private:
782       VoicePlayer *player;
783
784       double volume;
785
786       vector<Element *>                 elements;
787       vector<Element *>::iterator       iter;
788
789       inline double get_volume () const { return player->volume * player->speaker.volume * volume; }
790     };
791
792     ///////////////////////////////////////////////////////////////////////////
793     // MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
794     ///////////////////////////////////////////////////////////////////////////
795
796     struct
797     {
798       double volume;
799     } conf;
800
801     double volume;
802
803     Voice *voice;
804     Voice *next_voice;
805
806     struct
807     {
808       Voice *application_data_base_failed;
809       Voice *bank_angle;
810       Voice *bank_angle_bank_angle;
811       Voice *bank_angle_bank_angle_3;
812       Voice *bank_angle_inop;
813       Voice *bank_angle_pause_bank_angle;
814       Voice *bank_angle_pause_bank_angle_3;
815       Voice *callouts_inop;
816       Voice *configuration_type_invalid;
817       Voice *dont_sink;
818       Voice *dont_sink_pause_dont_sink;
819       Voice *five_hundred_above;
820       Voice *glideslope;
821       Voice *glideslope_inop;
822       Voice *gpws_inop;
823       Voice *hard_glideslope;
824       Voice *minimums;
825       Voice *minimums_minimums;
826       Voice *pull_up;
827       Voice *sink_rate;
828       Voice *sink_rate_pause_sink_rate;
829       Voice *soft_glideslope;
830       Voice *terrain;
831       Voice *terrain_pause_terrain;
832       Voice *too_low_flaps;
833       Voice *too_low_gear;
834       Voice *too_low_terrain;
835       Voice *altitude_callouts[n_altitude_callouts];
836     } voices;
837
838     inline VoicePlayer (MK_VIII *device)
839       : mk(device), speaker(this), voice(NULL), next_voice(NULL) {}
840
841     ~VoicePlayer ();
842
843     void init ();
844
845     enum
846     {
847       PLAY_NOW          = 1 << 0,
848       PLAY_LOOPED       = 1 << 1
849     };
850     void play (Voice *_voice, unsigned int flags = 0);
851
852     enum
853     {
854       STOP_NOW          = 1 << 0
855     };
856     void stop (unsigned int flags = 0);
857
858     void set_volume (double _volume);
859     void update ();
860
861     inline void bind (SGPropertyNode *node) { speaker.bind(node); }
862
863   private:
864
865     ///////////////////////////////////////////////////////////////////////////
866     // MK_VIII::VoicePlayer::Speaker //////////////////////////////////////////
867     ///////////////////////////////////////////////////////////////////////////
868
869     class Speaker
870     {
871       VoicePlayer *player;
872
873       double    pitch;
874       float     position[3];
875       float     orientation[3];
876       float     inner_cone;
877       float     outer_cone;
878       float     outer_gain;
879       float     reference_dist;
880       float     max_dist;
881
882       template <class T>
883       inline void tie (SGPropertyNode *node, const char *name, T *ptr)
884       {
885         player->mk->properties_handler.tie
886           (node, (string("speaker/") + name).c_str(),
887            RawValueMethodsData<MK_VIII::VoicePlayer::Speaker,T,T*>
888            (*this, ptr,
889             &MK_VIII::VoicePlayer::Speaker::get_property,
890             &MK_VIII::VoicePlayer::Speaker::set_property));
891       }
892
893       template <class T>
894       inline void set_property (T *ptr, T value) { *ptr = value; update_configuration(); }
895
896       template <class T>
897       inline T get_property (T *ptr) const { return *ptr; }
898
899     public:
900       double volume;
901
902       inline Speaker (VoicePlayer *_player)
903         : player(_player),
904           volume(1),
905           pitch(1),
906           inner_cone(360),
907           outer_cone(360),
908           outer_gain(0),
909           reference_dist(3),
910           max_dist(10)
911       {
912         position[0] = 0; position[1] = 0; position[2] = 0;
913         orientation[0] = 0; orientation[1] = 0; orientation[2] = 0;
914       }
915
916       void bind (SGPropertyNode *node);
917       void update_configuration ();
918     };
919
920     ///////////////////////////////////////////////////////////////////////////
921     // MK_VIII::VoicePlayer (continued) ///////////////////////////////////////
922     ///////////////////////////////////////////////////////////////////////////
923
924     MK_VIII *mk;
925
926     Speaker speaker;
927
928     map<string, SGSoundSample *>        samples;
929     vector<Voice *>                     _voices;
930
931     bool looped;
932     bool next_looped;
933
934     SGSoundSample *get_sample (const char *name);
935
936     inline void append (Voice *voice, Voice::Element *element) { voice->append(element); }
937     inline void append (Voice *voice, const char *sample_name) { voice->append(new Voice::SampleElement(get_sample(sample_name))); }
938     inline void append (Voice *voice, double silence) { voice->append(new Voice::SilenceElement(silence)); }
939
940     inline void make_voice (Voice **voice) { *voice = new Voice(this); _voices.push_back(*voice); }
941
942     template <class T1>
943     inline void make_voice (Voice **voice, T1 e1) { make_voice(voice); append(*voice, e1); }
944     template <class T1, class T2>
945     inline void make_voice (Voice **voice, T1 e1, T2 e2) { make_voice(voice, e1); append(*voice, e2); }
946     template <class T1, class T2, class T3>
947     inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3) { make_voice(voice, e1, e2); append(*voice, e3); }
948     template <class T1, class T2, class T3, class T4>
949     inline void make_voice (Voice **voice, T1 e1, T2 e2, T3 e3, T4 e4) { make_voice(voice, e1, e2, e3); append(*voice, e4); }
950   };
951
952   /////////////////////////////////////////////////////////////////////////////
953   // MK_VIII::SelfTestHandler /////////////////////////////////////////////////
954   /////////////////////////////////////////////////////////////////////////////
955
956   class SelfTestHandler
957   {
958     MK_VIII *mk;
959
960     typedef enum
961     {
962       CANCEL_NONE,
963       CANCEL_SHORT,
964       CANCEL_LONG
965     } Cancel;
966
967     enum
968     {
969       ACTION_SLEEP              = 1 << 0,
970       ACTION_VOICE              = 1 << 1,
971       ACTION_DISCRETE_ON_OFF    = 1 << 2,
972       ACTION_DONE               = 1 << 3
973     };
974
975     typedef struct
976     {
977       unsigned int      flags;
978       double            sleep_duration;
979       bool              *discrete;
980     } Action;
981
982     Cancel              cancel;
983     Action              action;
984     int                 current;
985     bool                button_pressed;
986     double              button_press_timestamp;
987     IOHandler::Outputs  saved_outputs;
988     double              sleep_start;
989
990     bool _was_here (int position);
991
992     Action sleep (double duration);
993     Action play (VoicePlayer::Voice *voice);
994     Action discrete_on (bool *discrete, double duration);
995     Action discrete_on_off (bool *discrete, double duration);
996     Action discrete_on_off (bool *discrete, VoicePlayer::Voice *voice);
997     Action done ();
998
999     Action run ();
1000
1001     void start ();
1002     void stop ();
1003     void shutdown ();
1004
1005   public:
1006     typedef enum
1007     {
1008       STATE_NONE,
1009       STATE_START,
1010       STATE_RUNNING
1011     } State;
1012
1013     State state;
1014
1015     inline SelfTestHandler (MK_VIII *device)
1016       : mk(device), state(STATE_NONE), button_pressed(false) {}
1017
1018     inline void power_off () { stop(); }
1019     inline void set_inop () { stop(); }
1020     void handle_button_event (bool value);
1021     bool update ();
1022   };
1023
1024   /////////////////////////////////////////////////////////////////////////////
1025   // MK_VIII::AlertHandler ////////////////////////////////////////////////////
1026   /////////////////////////////////////////////////////////////////////////////
1027
1028   class AlertHandler
1029   {
1030     MK_VIII *mk;
1031
1032     unsigned int old_alerts;
1033     unsigned int voice_alerts;
1034     unsigned int repeated_alerts;
1035     VoicePlayer::Voice *altitude_callout_voice;
1036
1037     void reset ();
1038     inline bool has_alerts (unsigned int test) const { return (alerts & test) != 0; }
1039     inline bool has_old_alerts (unsigned int test) const { return (old_alerts & test) != 0; }
1040     inline bool must_play_voice (unsigned int test) const { return ! has_old_alerts(test) || (repeated_alerts & test) != 0; }
1041     bool select_voice_alerts (unsigned int test);
1042
1043   public:
1044     enum
1045     {
1046       ALERT_MODE1_PULL_UP                               = 1 << 0,
1047       ALERT_MODE1_SINK_RATE                             = 1 << 1,
1048
1049       ALERT_MODE2A_PREFACE                              = 1 << 2,
1050       ALERT_MODE2B_PREFACE                              = 1 << 3,
1051       ALERT_MODE2A                                      = 1 << 4,
1052       ALERT_MODE2B                                      = 1 << 5,
1053       ALERT_MODE2B_LANDING_MODE                         = 1 << 6,
1054       ALERT_MODE2A_ALTITUDE_GAIN                        = 1 << 7,
1055       ALERT_MODE2A_ALTITUDE_GAIN_TERRAIN_CLOSING        = 1 << 8,
1056
1057       ALERT_MODE3                                       = 1 << 9,
1058
1059       ALERT_MODE4_TOO_LOW_FLAPS                         = 1 << 10,
1060       ALERT_MODE4_TOO_LOW_GEAR                          = 1 << 11,
1061       ALERT_MODE4AB_TOO_LOW_TERRAIN                     = 1 << 12,
1062       ALERT_MODE4C_TOO_LOW_TERRAIN                      = 1 << 13,
1063
1064       ALERT_MODE5_SOFT                                  = 1 << 14,
1065       ALERT_MODE5_HARD                                  = 1 << 15,
1066
1067       ALERT_MODE6_MINIMUMS                              = 1 << 16,
1068       ALERT_MODE6_ALTITUDE_CALLOUT                      = 1 << 17,
1069       ALERT_MODE6_LOW_BANK_ANGLE_1                      = 1 << 18,
1070       ALERT_MODE6_HIGH_BANK_ANGLE_1                     = 1 << 19,
1071       ALERT_MODE6_LOW_BANK_ANGLE_2                      = 1 << 20,
1072       ALERT_MODE6_HIGH_BANK_ANGLE_2                     = 1 << 21,
1073       ALERT_MODE6_LOW_BANK_ANGLE_3                      = 1 << 22,
1074       ALERT_MODE6_HIGH_BANK_ANGLE_3                     = 1 << 23,
1075
1076       ALERT_TCF_TOO_LOW_TERRAIN                         = 1 << 24
1077     };
1078
1079     enum
1080     {
1081       ALERT_FLAG_REPEAT = 1 << 0
1082     };
1083
1084     unsigned int alerts;
1085
1086     inline AlertHandler (MK_VIII *device)
1087       : mk(device) {}
1088
1089     void boot ();
1090     void reposition ();
1091     void update ();
1092
1093     void set_alerts (unsigned int _alerts,
1094                      unsigned int flags = 0,
1095                      VoicePlayer::Voice *_altitude_callout_voice = NULL);
1096     void unset_alerts (unsigned int _alerts);
1097
1098     inline void repeat_alert (unsigned int alert) { set_alerts(alert, ALERT_FLAG_REPEAT); }
1099     inline void set_altitude_callout_alert (VoicePlayer::Voice *voice) { set_alerts(ALERT_MODE6_ALTITUDE_CALLOUT, 0, voice); }
1100   };
1101
1102   /////////////////////////////////////////////////////////////////////////////
1103   // MK_VIII::StateHandler ////////////////////////////////////////////////////
1104   /////////////////////////////////////////////////////////////////////////////
1105
1106   class StateHandler
1107   {
1108     MK_VIII *mk;
1109
1110     Timer potentially_airborne_timer;
1111
1112     void update_ground ();
1113     void enter_ground ();
1114     void leave_ground ();
1115
1116     void update_takeoff ();
1117     void enter_takeoff ();
1118     void leave_takeoff ();
1119
1120   public:
1121     bool ground;
1122     bool takeoff;
1123
1124     inline StateHandler (MK_VIII *device)
1125       : mk(device), ground(true), takeoff(true) {}
1126
1127     void post_reposition ();
1128     void update ();
1129   };
1130
1131   /////////////////////////////////////////////////////////////////////////////
1132   // MK_VIII::Mode1Handler ////////////////////////////////////////////////////
1133   /////////////////////////////////////////////////////////////////////////////
1134
1135   class Mode1Handler
1136   {
1137     MK_VIII *mk;
1138
1139     Timer pull_up_timer;
1140     Timer sink_rate_timer;
1141
1142     double sink_rate_tti;       // time-to-impact in minutes
1143
1144     double get_pull_up_bias ();
1145     bool is_pull_up ();
1146
1147     double get_sink_rate_bias ();
1148     bool is_sink_rate ();
1149     double get_sink_rate_tti ();
1150
1151     void update_pull_up ();
1152     void update_sink_rate ();
1153
1154   public:
1155     typedef struct
1156     {
1157       bool      flap_override_bias;
1158       int       min_agl;
1159       double    (*pull_up_min_agl1) (double vs);
1160       int       pull_up_max_agl1;
1161       double    (*pull_up_min_agl2) (double vs);
1162       int       pull_up_max_agl2;
1163     } EnvelopesConfiguration;
1164
1165     struct
1166     {
1167       const EnvelopesConfiguration *envelopes;
1168     } conf;
1169
1170     inline Mode1Handler (MK_VIII *device)
1171       : mk(device) {}
1172
1173     void update ();
1174   };
1175
1176   /////////////////////////////////////////////////////////////////////////////
1177   // MK_VIII::Mode2Handler ////////////////////////////////////////////////////
1178   /////////////////////////////////////////////////////////////////////////////
1179
1180   class Mode2Handler
1181   {
1182
1183     ///////////////////////////////////////////////////////////////////////////
1184     // MK_VIII::Mode2Handler::ClosureRateFilter ///////////////////////////////
1185     ///////////////////////////////////////////////////////////////////////////
1186
1187     class ClosureRateFilter
1188     {
1189       /////////////////////////////////////////////////////////////////////////
1190       // MK_VIII::Mode2Handler::ClosureRateFilter::PassFilter /////////////////
1191       /////////////////////////////////////////////////////////////////////////
1192
1193       class PassFilter
1194       {
1195         double a0;
1196         double a1;
1197         double b1;
1198
1199         double last_input;
1200         double last_output;
1201
1202       public:
1203         inline PassFilter (double _a0, double _a1, double _b1)
1204           : a0(_a0), a1(_a1), b1(_b1) {}
1205
1206         inline double filter (double input)
1207         {
1208           last_output = a0 * input + a1 * last_input + b1 * last_output;
1209           last_input = input;
1210
1211           return last_output;
1212         }
1213
1214         inline void reset ()
1215         {
1216           last_input = 0;
1217           last_output = 0;
1218         }
1219       };
1220
1221       /////////////////////////////////////////////////////////////////////////
1222       // MK_VIII::Mode2Handler::ClosureRateFilter (continued) /////////////////
1223       /////////////////////////////////////////////////////////////////////////
1224
1225       MK_VIII *mk;
1226
1227       Timer             timer;
1228       Parameter<double> last_ra;        // last radio altitude
1229       Parameter<double> last_ba;        // last barometric altitude
1230       PassFilter        ra_filter;      // radio altitude rate filter
1231       PassFilter        ba_filter;      // barometric altitude rate filter
1232
1233       double limit_radio_altitude_rate (double r);
1234
1235     public:
1236       Parameter<double> output;
1237
1238       inline ClosureRateFilter (MK_VIII *device)
1239         : mk(device),
1240           ra_filter(0.05, 0, 0.95),             // low-pass filter
1241           ba_filter(0.93, -0.93, 0.86) {}       // high-pass-filter
1242
1243       void init ();
1244       void update ();
1245     };
1246
1247     ///////////////////////////////////////////////////////////////////////////
1248     // MK_VIII::Mode2Handler (continued) //////////////////////////////////////
1249     ///////////////////////////////////////////////////////////////////////////
1250
1251     MK_VIII *mk;
1252
1253     ClosureRateFilter closure_rate_filter;
1254
1255     Timer takeoff_timer;
1256     Timer pull_up_timer;
1257
1258     double      a_start_time;
1259     Timer       a_altitude_gain_timer;
1260     double      a_altitude_gain_alt;
1261
1262     void check_pull_up (unsigned int preface_alert, unsigned int alert);
1263
1264     bool b_conditions ();
1265
1266     bool is_a ();
1267     bool is_b ();
1268
1269     void update_a ();
1270     void update_b ();
1271
1272   public:
1273     typedef struct
1274     {
1275       int airspeed1;
1276       int airspeed2;
1277     } Configuration;
1278
1279     const Configuration *conf;
1280
1281     inline Mode2Handler (MK_VIII *device)
1282       : mk(device), closure_rate_filter(device) {}
1283
1284     void boot ();
1285     void power_off ();
1286     void leave_ground ();
1287     void enter_takeoff ();
1288     void update ();
1289   };
1290
1291   /////////////////////////////////////////////////////////////////////////////
1292   // MK_VIII::Mode3Handler ////////////////////////////////////////////////////
1293   /////////////////////////////////////////////////////////////////////////////
1294
1295   class Mode3Handler
1296   {
1297     MK_VIII *mk;
1298
1299     bool        armed;
1300     bool        has_descent_alt;
1301     double      descent_alt;
1302     double      bias;
1303
1304     double max_alt_loss (double _bias);
1305     double get_bias (double initial_bias, double alt_loss);
1306     bool is (double *alt_loss);
1307
1308   public:
1309     typedef struct
1310     {
1311       int       min_agl;
1312       int       (*max_agl) (bool flap_override);
1313       double    (*max_alt_loss) (bool flap_override, double agl);
1314     } Configuration;
1315
1316     const Configuration *conf;
1317
1318     inline Mode3Handler (MK_VIII *device)
1319       : mk(device), armed(false), has_descent_alt(false) {}
1320
1321     void enter_takeoff ();
1322     void update ();
1323   };
1324
1325   /////////////////////////////////////////////////////////////////////////////
1326   // MK_VIII::Mode4Handler ////////////////////////////////////////////////////
1327   /////////////////////////////////////////////////////////////////////////////
1328
1329   class Mode4Handler
1330   {
1331   public:
1332     typedef struct
1333     {
1334       int       airspeed1;
1335       int       airspeed2;
1336       int       min_agl1;
1337       double    (*min_agl2) (double airspeed);
1338       int       min_agl3;
1339     } EnvelopesConfiguration;
1340
1341     typedef struct
1342     {
1343       const EnvelopesConfiguration *ac;
1344       const EnvelopesConfiguration *b;
1345     } ModesConfiguration;
1346
1347     struct
1348     {
1349       VoicePlayer::Voice        *voice_too_low_gear;
1350       const ModesConfiguration  *modes;
1351     } conf;
1352
1353     inline Mode4Handler (MK_VIII *device)
1354       : mk(device) {}
1355
1356     double get_upper_agl (const EnvelopesConfiguration *c);
1357     void update ();
1358
1359   private:
1360     MK_VIII *mk;
1361
1362     double ab_bias;
1363     double ab_expanded_bias;
1364     double c_bias;
1365
1366     const EnvelopesConfiguration *get_ab_envelope ();
1367     double get_bias (double initial_bias, double min_agl);
1368     void handle_alert (unsigned int alert, double min_agl, double *bias);
1369
1370     void update_ab ();
1371     void update_ab_expanded ();
1372     void update_c ();
1373   };
1374
1375   /////////////////////////////////////////////////////////////////////////////
1376   // MK_VIII::Mode5Handler ////////////////////////////////////////////////////
1377   /////////////////////////////////////////////////////////////////////////////
1378
1379   class Mode5Handler
1380   {
1381     MK_VIII *mk;
1382
1383     Timer hard_timer;
1384     Timer soft_timer;
1385
1386     double soft_bias;
1387
1388     bool is_hard ();
1389     bool is_soft (double bias);
1390
1391     double get_soft_bias (double initial_bias);
1392
1393     void update_hard (bool is);
1394     void update_soft (bool is);
1395
1396   public:
1397     inline Mode5Handler (MK_VIII *device)
1398       : mk(device) {}
1399
1400     void update ();
1401   };
1402
1403   /////////////////////////////////////////////////////////////////////////////
1404   // MK_VIII::Mode6Handler ////////////////////////////////////////////////////
1405   /////////////////////////////////////////////////////////////////////////////
1406
1407   class Mode6Handler
1408   {
1409   public:
1410     // keep in sync with altitude_callout_definitions[]
1411     typedef enum
1412     {
1413       ALTITUDE_CALLOUT_1000,
1414       ALTITUDE_CALLOUT_500,
1415       ALTITUDE_CALLOUT_400,
1416       ALTITUDE_CALLOUT_300,
1417       ALTITUDE_CALLOUT_200,
1418       ALTITUDE_CALLOUT_100,
1419       ALTITUDE_CALLOUT_50,
1420       ALTITUDE_CALLOUT_40,
1421       ALTITUDE_CALLOUT_30,
1422       ALTITUDE_CALLOUT_20,
1423       ALTITUDE_CALLOUT_10
1424     } AltitudeCallout;
1425
1426     typedef bool (*BankAnglePredicate) (Parameter<double> *agl,
1427                                         double abs_roll_deg,
1428                                         bool ap_engaged);
1429
1430     struct
1431     {
1432       bool                      minimums_enabled;
1433       bool                      smart_500_enabled;
1434       VoicePlayer::Voice        *above_field_voice;
1435
1436       bool                      altitude_callouts_enabled[n_altitude_callouts];
1437       bool                      bank_angle_enabled;
1438       BankAnglePredicate        is_bank_angle;
1439     } conf;
1440
1441     static const int altitude_callout_definitions[];
1442
1443     inline Mode6Handler (MK_VIII *device)
1444       : mk(device) {}
1445
1446     void boot ();
1447     void power_off ();
1448     void enter_takeoff ();
1449     void leave_takeoff ();
1450     void set_volume (double volume);
1451     bool altitude_callouts_enabled ();
1452     void update ();
1453
1454   private:
1455     MK_VIII *mk;
1456
1457     bool                last_decision_height;
1458     Parameter<double>   last_radio_altitude;
1459     Parameter<double>   last_altitude_above_field;
1460
1461     bool altitude_callouts_issued[n_altitude_callouts];
1462     bool minimums_issued;
1463     bool above_field_issued;
1464
1465     Timer               runway_timer;
1466     Parameter<bool>     has_runway;
1467
1468     struct
1469     {
1470       double elevation;         // elevation in feet
1471     } runway;
1472
1473     void reset_minimums ();
1474     void reset_altitude_callouts ();
1475     bool is_playing_altitude_callout ();
1476     bool is_near_minimums (double callout);
1477     bool is_outside_band (double elevation, double callout);
1478     bool inhibit_smart_500 ();
1479
1480     void update_minimums ();
1481     void update_altitude_callouts ();
1482
1483     bool test_runway (const FGRunway *_runway);
1484     bool test_airport (const FGAirport *airport);
1485     void update_runway ();
1486
1487     void get_altitude_above_field (Parameter<double> *parameter);
1488     void update_above_field_callout ();
1489
1490     bool is_bank_angle (double abs_roll_angle, double bias);
1491     bool is_high_bank_angle ();
1492     unsigned int get_bank_angle_alerts ();
1493     void update_bank_angle ();
1494   };
1495
1496   /////////////////////////////////////////////////////////////////////////////
1497   // MK_VIII::TCFHandler //////////////////////////////////////////////////////
1498   /////////////////////////////////////////////////////////////////////////////
1499
1500   class TCFHandler
1501   {
1502     typedef struct
1503     {
1504       double latitude;          // latitude in degrees
1505       double longitude;         // longitude in degrees
1506     } Position;
1507
1508     typedef struct
1509     {
1510       Position  position;       // position of threshold
1511       double    heading;        // runway heading
1512     } RunwayEdge;
1513
1514     MK_VIII *mk;
1515
1516     static const double k;
1517
1518     Timer       runway_timer;
1519     bool        has_runway;
1520
1521     struct
1522     {
1523       Position          center;         // center point
1524       double            elevation;      // elevation in feet
1525       double            half_length;    // runway half length, in nautical miles
1526       RunwayEdge        edges[2];       // runway threshold and end
1527       Position          bias_area[4];   // vertices of the bias area
1528     } runway;
1529
1530     double bias;
1531     double *reference;
1532     double initial_value;
1533
1534     double get_azimuth_difference (double from_lat,
1535                                    double from_lon,
1536                                    double to_lat,
1537                                    double to_lon,
1538                                    double to_heading);
1539     double get_azimuth_difference (const FGRunway *_runway);
1540
1541     void select_runway (const FGAirport *airport, FGRunway *_runway);
1542     bool test_airport (const FGAirport *airport);
1543     void update_runway ();
1544
1545     void get_bias_area_edges (Position *edge,
1546                               double reciprocal,
1547                               double half_width_m,
1548                               Position *bias_edge1,
1549                               Position *bias_edge2);
1550
1551     bool is_inside_edge_triangle (RunwayEdge *edge);
1552     bool is_inside_bias_area ();
1553
1554     bool is_tcf ();
1555     bool is_rfcf ();
1556
1557   public:
1558     struct
1559     {
1560       bool enabled;
1561     } conf;
1562
1563     inline TCFHandler (MK_VIII *device)
1564       : mk(device) {}
1565
1566     void update ();
1567   };
1568
1569   /////////////////////////////////////////////////////////////////////////////
1570   // MK_VIII (continued) //////////////////////////////////////////////////////
1571   /////////////////////////////////////////////////////////////////////////////
1572
1573   string        name;
1574   int           num;
1575
1576   PropertiesHandler     properties_handler;
1577   PowerHandler          power_handler;
1578   SystemHandler         system_handler;
1579   ConfigurationModule   configuration_module;
1580   FaultHandler          fault_handler;
1581   IOHandler             io_handler;
1582   VoicePlayer           voice_player;
1583   SelfTestHandler       self_test_handler;
1584   AlertHandler          alert_handler;
1585   StateHandler          state_handler;
1586   Mode1Handler          mode1_handler;
1587   Mode2Handler          mode2_handler;
1588   Mode3Handler          mode3_handler;
1589   Mode4Handler          mode4_handler;
1590   Mode5Handler          mode5_handler;
1591   Mode6Handler          mode6_handler;
1592   TCFHandler            tcf_handler;
1593
1594   struct
1595   {
1596     int runway_database;
1597   } conf;
1598
1599 public:
1600   MK_VIII (SGPropertyNode *node);
1601
1602   virtual void init ();
1603   virtual void bind ();
1604   virtual void unbind ();
1605   virtual void update (double dt);
1606 };
1607
1608 #endif // __INSTRUMENTS_MK_VIII_HXX