]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/mk_viii.cxx
Support for multiple data dirs.
[flightgear.git] / src / Instrumentation / mk_viii.cxx
old mode 100755 (executable)
new mode 100644 (file)
index 50e1df1..9f89dbb
@@ -16,7 +16,7 @@
 //
 // You should have received a copy of the GNU General Public License
 // along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 ///////////////////////////////////////////////////////////////////////////////
 //
 #include <simgear/constants.h>
 #include <simgear/sg_inlines.h>
 #include <simgear/debug/logstream.hxx>
-#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/SGMathFwd.hxx>
+#include <simgear/math/SGLimits.hxx>
+#include <simgear/math/SGGeometryFwd.hxx>
+#include <simgear/math/SGGeodesy.hxx>
 #include <simgear/math/sg_random.h>
+#include <simgear/math/SGLineSegment.hxx>
+#include <simgear/math/SGIntersect.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/sound/soundmgr_openal.hxx>
+#include <simgear/sound/sample_group.hxx>
 #include <simgear/structure/exception.hxx>
 
 using std::string;
 
-#include "Airports/runways.hxx"
-#include "Airports/simple.hxx"
-#ifndef _MSC_VER
-#  include "Include/version.h"
+#include <Airports/runways.hxx>
+#include <Airports/airport.hxx>
+
+#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
+#  include <Include/version.h>
+#else
+#  include <Include/no_version.h>
 #endif
-#include "Main/fg_props.hxx"
-#include "Main/globals.hxx"
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
 #include "instrument_mgr.hxx"
 #include "mk_viii.hxx"
 
@@ -131,24 +141,6 @@ modify_amplitude (double amplitude, double dB)
   return amplitude * pow(10.0, dB / 20.0);
 }
 
-static double
-heading_add (double h1, double h2)
-{
-  double result = h1 + h2;
-  if (result >= 360)
-    result -= 360;
-  return result;
-}
-
-static double
-heading_substract (double h1, double h2)
-{
-  double result = h1 - h2;
-  if (result < 0)
-    result += 360;
-  return result;
-}
-
 static double
 get_heading_difference (double h1, double h2)
 {
@@ -162,12 +154,6 @@ get_heading_difference (double h1, double h2)
   return fabs(diff);
 }
 
-static double
-get_reciprocal_heading (double h)
-{
-  return heading_add(h, 180);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // PropertiesHandler //////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
@@ -182,6 +168,9 @@ MK_VIII::PropertiesHandler::init ()
   mk_node(altimeter_serviceable) = fgGetNode("/instrumentation/altimeter/serviceable", true);
   mk_node(altitude) = fgGetNode("/position/altitude-ft", true);
   mk_node(altitude_agl) = fgGetNode("/position/altitude-agl-ft", true);
+  mk_node(altitude_gear_agl) = fgGetNode("/position/gear-agl-ft", true);
+  mk_node(altitude_radar_agl) = fgGetNode("/instrumentation/radar-altimeter/radar-altitude-ft", true);
+  mk_node(orientation_roll) = fgGetNode("/orientation/roll-deg", true);
   mk_node(asi_serviceable) = fgGetNode("/instrumentation/airspeed-indicator/serviceable", true);
   mk_node(asi_speed) = fgGetNode("/instrumentation/airspeed-indicator/indicated-speed-kt", true);
   mk_node(autopilot_heading_lock) = fgGetNode("/autopilot/locks/heading", true);
@@ -191,7 +180,7 @@ MK_VIII::PropertiesHandler::init ()
   mk_node(longitude) = fgGetNode("/position/longitude-deg", true);
   mk_node(nav0_cdi_serviceable) = fgGetNode("/instrumentation/nav/cdi/serviceable", true);
   mk_node(nav0_gs_distance) = fgGetNode("/instrumentation/nav/gs-distance", true);
-  mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection", true);
+  mk_node(nav0_gs_needle_deflection) = fgGetNode("/instrumentation/nav/gs-needle-deflection-deg", true);
   mk_node(nav0_gs_serviceable) = fgGetNode("/instrumentation/nav/gs/serviceable", true);
   mk_node(nav0_has_gs) = fgGetNode("/instrumentation/nav/has-gs", true);
   mk_node(nav0_heading_needle_deflection) = fgGetNode("/instrumentation/nav/heading-needle-deflection", true);
@@ -203,17 +192,6 @@ MK_VIII::PropertiesHandler::init ()
   mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
 }
 
-void
-MK_VIII::PropertiesHandler::unbind ()
-{
-  vector<SGPropertyNode_ptr>::iterator iter;
-
-  for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
-    (*iter)->untie();
-
-  tied_properties.clear();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // PowerHandler ///////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
@@ -360,6 +338,7 @@ MK_VIII::SystemHandler::update ()
       if (replay_state != last_replay_state)
        {
          mk->alert_handler.reposition();
+         mk->io_handler.reposition();
 
          last_replay_state = replay_state;
          state = STATE_REPOSITION;
@@ -549,7 +528,7 @@ MK_VIII::ConfigurationModule::read_aircraft_mode_type_select (int value)
     { 255,     &m1_t1, &m2_t1, &m3_t1, &m4_t1, m6_t2, &f_fast, 2000 }
   };
 
-  for (int i = 0; i < n_elements(aircraft_types); i++)
+  for (size_t i = 0; i < n_elements(aircraft_types); i++)
     if (aircraft_types[i].type == value)
       {
        mk->mode1_handler.conf.envelopes = aircraft_types[i].m1;
@@ -625,7 +604,7 @@ MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
     { 101, { FIELD_500_ABOVE, 0 } }
   };
 
-  int i;
+  unsigned i;
 
   mk->mode6_handler.conf.minimums_enabled = false;
   mk->mode6_handler.conf.smart_500_enabled = false;
@@ -656,7 +635,7 @@ MK_VIII::ConfigurationModule::read_altitude_callouts (int value)
              break;
 
            default:
-             for (int k = 0; k < n_altitude_callouts; k++)
+             for (unsigned k = 0; k < n_altitude_callouts; k++)
                if (mk->mode6_handler.altitude_callout_definitions[k] == values[i].callouts[j])
                  mk->mode6_handler.conf.altitude_callouts_enabled[k] = true;
              break;
@@ -712,7 +691,7 @@ MK_VIII::ConfigurationModule::read_options_select_group_1 (int value)
 bool
 MK_VIII::ConfigurationModule::read_radio_altitude_input_select (int value)
 {
-  // unimplemented
+  mk->io_handler.conf.altitude_source = value;
   return (value >= 0 && value <= 4) || (value >= 251 && value <= 255);
 }
 
@@ -732,7 +711,10 @@ MK_VIII::ConfigurationModule::read_navigation_input_select (int value)
 bool
 MK_VIII::ConfigurationModule::read_attitude_input_select (int value)
 {
-  // unimplemented
+  if (value == 2)
+    mk->io_handler.conf.use_attitude_indicator=true;
+  else
+    mk->io_handler.conf.use_attitude_indicator=false;
   return (value >= 0 && value <= 6) || value == 253 || value == 255;
 }
 
@@ -773,7 +755,7 @@ MK_VIII::ConfigurationModule::read_input_output_discrete_type_select (int value)
     { 255,     { false, false }, true, false, true }
   };
 
-  for (int i = 0; i < n_elements(io_types); i++)
+  for (size_t i = 0; i < n_elements(io_types); i++)
     if (io_types[i].type == value)
       {
        mk->io_handler.conf.lamp = &io_types[i].lamp_conf;
@@ -801,7 +783,7 @@ MK_VIII::ConfigurationModule::read_audio_output_level (int value)
     { 4, -24 }
   };
 
-  for (int i = 0; i < n_elements(values); i++)
+  for (size_t i = 0; i < n_elements(values); i++)
     if (values[i].id == value)
       {
        mk->voice_player.set_volume(mk->voice_player.conf.volume = modify_amplitude(1.0, values[i].relative_dB));
@@ -979,6 +961,11 @@ MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
   // [PILOT] page 20 specifies that the terrain clearance is equal to
   // 75% of the radio altitude, averaged over the previous 15 seconds.
 
+  // no updates when simulation is paused (dt=0.0), and add 5 samples/second only 
+  if (globals->get_sim_time_sec() - last_update < 0.2)
+      return value;
+  last_update = globals->get_sim_time_sec();
+
   samples_type::iterator iter;
 
   // remove samples older than 15 seconds
@@ -990,10 +977,12 @@ MK_VIII::IOHandler::TerrainClearanceFilter::update (double agl)
 
   // calculate average
   double new_value = 0;
-  if (samples.size() > 0)
+  if (! samples.empty())
     {
+      // time consuming loop => queue limited to 75 samples
+      // (= 15seconds * 5samples/second)
       for (iter = samples.begin(); iter != samples.end(); iter++)
-       new_value += (*iter).value;
+        new_value += (*iter).value;
       new_value /= samples.size();
     }
   new_value *= 0.75;
@@ -1009,6 +998,7 @@ MK_VIII::IOHandler::TerrainClearanceFilter::reset ()
 {
   samples.clear();
   value = 0;
+  last_update = -1.0;
 }
 
 MK_VIII::IOHandler::IOHandler (MK_VIII *device)
@@ -1049,6 +1039,7 @@ MK_VIII::IOHandler::boot ()
   mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
 
   altitude_samples.clear();
+  reset_terrain_clearance();
 }
 
 void
@@ -1144,9 +1135,21 @@ MK_VIII::IOHandler::update_inputs ()
     mk_ainput(barometric_altitude_rate).set(mk_node(vs)->getDoubleValue() * 60);
   if (mk_ainput_feed(radio_altitude))
     {
+      double agl;
+      switch (conf.altitude_source)
+      {
+          case 3:
+              agl = mk_node(altitude_gear_agl)->getDoubleValue();
+              break;
+          case 4:
+              agl = mk_node(altitude_radar_agl)->getDoubleValue();
+              break;
+          default: // 0,1,2 (and any currently unsupported values)
+              agl = mk_node(altitude_agl)->getDoubleValue();
+              break;
+      }
       // Some flight models may return negative values when on the
       // ground or after a crash; do not allow them.
-      double agl = mk_node(altitude_agl)->getDoubleValue();
       mk_ainput(radio_altitude).set(SG_MAX2(0.0, agl));
     }
   if (mk_ainput_feed(glideslope_deviation))
@@ -1164,11 +1167,20 @@ MK_VIII::IOHandler::update_inputs ()
     }
   if (mk_ainput_feed(roll_angle))
     {
+      if (conf.use_attitude_indicator)
+      {
+        // read data from attitude indicator instrument (requires vacuum system to work)
       if (mk_node(ai_serviceable)->getBoolValue() && ! mk_node(ai_caged)->getBoolValue())
        mk_ainput(roll_angle).set(mk_node(ai_roll)->getDoubleValue());
       else
        mk_ainput(roll_angle).unset();
     }
+      else
+      {
+        // use simulator source
+        mk_ainput(roll_angle).set(mk_node(orientation_roll)->getDoubleValue());
+      }
+    }
   if (mk_ainput_feed(localizer_deviation))
     {
       if (mk_node(nav0_serviceable)->getBoolValue()
@@ -1335,6 +1347,12 @@ MK_VIII::IOHandler::reset_terrain_clearance ()
   update_terrain_clearance();
 }
 
+void
+MK_VIII::IOHandler::reposition ()
+{
+  reset_terrain_clearance();
+}
+
 void
 MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
 {
@@ -1550,7 +1568,7 @@ MK_VIII::IOHandler::update_egpws_alert_discrete_1 ()
        { 19, mk_voice(minimums_minimums) }
       };
 
-      for (int i = 0; i < n_elements(voices); i++)
+      for (size_t i = 0; i < n_elements(voices); i++)
        if (voices[i].voice == mk->voice_player.voice)
          {
            mk_aoutput(egpws_alert_discrete_1) = 1 << voices[i].bit;
@@ -1584,7 +1602,7 @@ MK_VIII::IOHandler::update_egpwc_logic_discretes ()
     { 25, mk_alert(MODE5_SOFT) | mk_alert(MODE5_HARD) }
   };
 
-  for (int i = 0; i < n_elements(logic); i++)
+  for (size_t i = 0; i < n_elements(logic); i++)
     if (mk_test_alerts(logic[i].alerts))
       mk_aoutput(egpwc_logic_discretes) |= 1 << logic[i].bit;
 }
@@ -1610,7 +1628,7 @@ MK_VIII::IOHandler::update_mode6_callouts_discrete_1 ()
        { 25, mk_altitude_voice(Mode6Handler::ALTITUDE_CALLOUT_300) }
       };
 
-      for (int i = 0; i < n_elements(voices); i++)
+      for (size_t i = 0; i < n_elements(voices); i++)
        if (voices[i].voice == mk->voice_player.voice)
          {
            mk_aoutput(mode6_callouts_discrete_1) = 1 << voices[i].bit;
@@ -1639,7 +1657,7 @@ MK_VIII::IOHandler::update_mode6_callouts_discrete_2 ()
        { 23, mk_voice(five_hundred_above) }
       };
 
-      for (int i = 0; i < n_elements(voices); i++)
+      for (size_t i = 0; i < n_elements(voices); i++)
        if (voices[i].voice == mk->voice_player.voice)
          {
            mk_aoutput(mode6_callouts_discrete_2) = 1 << voices[i].bit;
@@ -1811,7 +1829,8 @@ MK_VIII::IOHandler::tie_input (SGPropertyNode *node,
                               bool *input,
                               bool *feed)
 {
-  mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(), RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
+  mk->properties_handler.tie(node, (string("inputs/discretes/") + name).c_str(),
+          FGVoicePlayer::RawValueMethodsData<MK_VIII::IOHandler, bool, bool *>(*this, input, &MK_VIII::IOHandler::get_discrete_input, &MK_VIII::IOHandler::set_discrete_input));
   if (feed)
     mk->properties_handler.tie(node, (string("input-feeders/discretes/") + name).c_str(), SGRawValuePointer<bool>(feed));
 }
@@ -2049,7 +2068,7 @@ MK_VIII::IOHandler::present_status ()
            "TCF INPUTS INVALID"
          };
 
-         for (int i = 0; i < n_elements(fault_names); i++)
+         for (size_t i = 0; i < n_elements(fault_names); i++)
            if (mk->fault_handler.faults[i])
              present_status_subitem(fault_names[i]);
        }
@@ -2075,7 +2094,7 @@ MK_VIII::IOHandler::present_status ()
     "VOLUME SELECT"
   };
 
-  for (int i = 0; i < n_elements(category_names); i++)
+  for (size_t i = 0; i < n_elements(category_names); i++)
     {
       std::ostringstream value;
       value << "= " << mk->configuration_module.effective_categories[i];
@@ -2097,285 +2116,50 @@ MK_VIII::IOHandler::set_present_status (bool value)
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// VoicePlayer ////////////////////////////////////////////////////////////////
+// MK_VIII::VoicePlayer ///////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
-void
-MK_VIII::VoicePlayer::Speaker::bind (SGPropertyNode *node)
-{
-  // uses xmlsound property names
-  tie(node, "volume", &volume);
-  tie(node, "pitch", &pitch);
-  tie(node, "position/x", &position[0]);
-  tie(node, "position/y", &position[1]);
-  tie(node, "position/z", &position[2]);
-  tie(node, "orientation/x", &orientation[0]);
-  tie(node, "orientation/y", &orientation[1]);
-  tie(node, "orientation/z", &orientation[2]);
-  tie(node, "orientation/inner-cone", &inner_cone);
-  tie(node, "orientation/outer-cone", &outer_cone);
-  tie(node, "reference-dist", &reference_dist);
-  tie(node, "max-dist", &max_dist);
-}
-
-void
-MK_VIII::VoicePlayer::Speaker::update_configuration ()
-{
-  map<string, SGSoundSample *>::iterator iter;
-  for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
-    {
-      SGSoundSample *sample = (*iter).second;
-
-      sample->set_pitch(pitch);
-      sample->set_offset_pos(position);
-      sample->set_orientation(orientation,
-                             inner_cone,
-                             outer_cone,
-                             outer_gain);
-      sample->set_reference_dist(reference_dist);
-      sample->set_max_dist(max_dist);
-    }
-
-  if (player->voice)
-    player->voice->volume_changed();
-}
-
-MK_VIII::VoicePlayer::Voice::~Voice ()
-{
-  for (iter = elements.begin(); iter != elements.end(); iter++)
-    delete *iter;              // we owned the element
-  elements.clear();
-}
-
-void
-MK_VIII::VoicePlayer::Voice::play ()
-{
-  iter = elements.begin();
-  element = *iter;
-
-  element->play(get_volume());
-}
-
-void
-MK_VIII::VoicePlayer::Voice::stop (bool now)
-{
-  if (element)
-    {
-      if (now || element->silence)
-       {
-         element->stop();
-         element = NULL;
-       }
-      else
-       iter = elements.end() - 1; // stop after the current element finishes
-    }
-}
-
-void
-MK_VIII::VoicePlayer::Voice::set_volume (double _volume)
-{
-  volume = _volume;
-  volume_changed();
-}
-
-void
-MK_VIII::VoicePlayer::Voice::volume_changed ()
-{
-  if (element)
-    element->set_volume(get_volume());
-}
-
-void
-MK_VIII::VoicePlayer::Voice::update ()
-{
-  if (element)
-    {
-      if (! element->is_playing())
-       {
-         if (++iter == elements.end())
-           element = NULL;
-         else
-           {
-             element = *iter;
-             element->play(get_volume());
-           }
-       }
-    }
-}
-
-MK_VIII::VoicePlayer::~VoicePlayer ()
-{
-  vector<Voice *>::iterator iter1;
-  for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
-    delete *iter1;
-  _voices.clear();
-
-/* sound mgr already destroyed - samples already deleted
-  map<string, SGSoundSample *>::iterator iter2;
-  for (iter2 = samples.begin(); iter2 != samples.end(); iter2++)
-    {
-      bool status = globals->get_soundmgr()->remove((*iter2).first);
-      assert(status);
-    }
-*/
-  samples.clear();
-}
-
 void
 MK_VIII::VoicePlayer::init ()
 {
-#define STDPAUSE 0.75  // [SPEC] 6.4.4: "the standard 0.75 second delay"
-
-  make_voice(&voices.application_data_base_failed, "application-data-base-failed");
-  make_voice(&voices.bank_angle, "bank-angle");
-  make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
-  make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
-  make_voice(&voices.bank_angle_inop, "bank-angle-inop");
-  make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
-  make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
-  make_voice(&voices.callouts_inop, "callouts-inop");
-  make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
-  make_voice(&voices.dont_sink, "dont-sink");
-  make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
-  make_voice(&voices.five_hundred_above, "500-above");
-  make_voice(&voices.glideslope, "glideslope");
-  make_voice(&voices.glideslope_inop, "glideslope-inop");
-  make_voice(&voices.gpws_inop, "gpws-inop");
-  make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
-  make_voice(&voices.minimums, "minimums");
-  make_voice(&voices.minimums_minimums, "minimums", "minimums");
-  make_voice(&voices.pull_up, "pull-up");
-  make_voice(&voices.sink_rate, "sink-rate");
-  make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
-  make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
-  make_voice(&voices.terrain, "terrain");
-  make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
-  make_voice(&voices.too_low_flaps, "too-low-flaps");
-  make_voice(&voices.too_low_gear, "too-low-gear");
-  make_voice(&voices.too_low_terrain, "too-low-terrain");
-
-  for (int i = 0; i < n_altitude_callouts; i++)
-    {
-      std::ostringstream name;
-      name << "altitude-" << mk->mode6_handler.altitude_callout_definitions[i];
-      make_voice(&voices.altitude_callouts[i], name.str().c_str());
-    }
-
-  speaker.update_configuration();
-}
-
-SGSoundSample *
-MK_VIII::VoicePlayer::get_sample (const char *name)
-{
-  std::ostringstream refname;
-  refname << mk->name << "[" << mk->num << "]" << "/" << name;
-
-  SGSoundMgr *soundmgr = globals->get_soundmgr();
-  if (soundmgr->is_working() == false)
-    {
-      return NULL;
-    }
-
-  SGSoundSample *sample = soundmgr->find(refname.str());
-  if (! sample)
-    {
-      SGPath sample_path(globals->get_fg_root());
-      sample_path.append("Sounds/mk-viii");
-
-      string filename = string(name) + ".wav";
-      try
-       {
-         sample = new SGSoundSample(sample_path.c_str(), filename.c_str());
-       }
-      catch (const sg_exception &e)
-       {
-         SG_LOG(SG_INSTR, SG_ALERT, "Error loading MK VIII sound sample \"" + filename + "\": " + e.getFormattedMessage());
-         exit(1);
-       }
-
-      soundmgr->add(sample, refname.str());
-      samples[refname.str()] = sample;
-    }
-
-  return sample;
-}
-
-void
-MK_VIII::VoicePlayer::play (Voice *_voice, unsigned int flags)
-{
-  if (test_bits(flags, PLAY_NOW) || ! voice || voice->element->silence)
-    {
-      if (voice)
-       voice->stop(true);
-
-      voice = _voice;
-      looped = test_bits(flags, PLAY_LOOPED);
-
-      next_voice = NULL;
-      next_looped = false;
-
-      voice->play();
-    }
-  else
-    {
-      next_voice = _voice;
-      next_looped = test_bits(flags, PLAY_LOOPED);
-    }
-}
-
-void
-MK_VIII::VoicePlayer::stop (unsigned int flags)
-{
-  if (voice)
-    {
-      voice->stop(test_bits(flags, STOP_NOW));
-      if (voice->element)
-       looped = false;
-      else
-       voice = NULL;
-      next_voice = NULL;
-    }
-}
-
-void
-MK_VIII::VoicePlayer::set_volume (double _volume)
-{
-  volume = _volume;
-  if (voice)
-    voice->volume_changed();
-}
-
-void
-MK_VIII::VoicePlayer::update ()
-{
-  if (voice)
-    {
-      voice->update();
-
-      if (next_voice)
-       {
-         if (! voice->element || voice->element->silence)
-           {
-             voice = next_voice;
-             looped = next_looped;
-
-             next_voice = NULL;
-             next_looped = false;
-
-             voice->play();
-           }
-       }
-      else
-       {
-         if (! voice->element)
-           {
-             if (looped)
-               voice->play();
-             else
-               voice = NULL;
-           }
-       }
-    }
+    FGVoicePlayer::init();
+
+#define STDPAUSE 0.75   // [SPEC] 6.4.4: "the standard 0.75 second delay"
+    make_voice(&voices.application_data_base_failed, "application-data-base-failed");
+    make_voice(&voices.bank_angle, "bank-angle");
+    make_voice(&voices.bank_angle_bank_angle, "bank-angle", "bank-angle");
+    make_voice(&voices.bank_angle_bank_angle_3, "bank-angle", "bank-angle", 3.0);
+    make_voice(&voices.bank_angle_inop, "bank-angle-inop");
+    make_voice(&voices.bank_angle_pause_bank_angle, "bank-angle", STDPAUSE, "bank-angle");
+    make_voice(&voices.bank_angle_pause_bank_angle_3, "bank-angle", STDPAUSE, "bank-angle", 3.0);
+    make_voice(&voices.callouts_inop, "callouts-inop");
+    make_voice(&voices.configuration_type_invalid, "configuration-type-invalid");
+    make_voice(&voices.dont_sink, "dont-sink");
+    make_voice(&voices.dont_sink_pause_dont_sink, "dont-sink", STDPAUSE, "dont-sink");
+    make_voice(&voices.five_hundred_above, "500-above");
+    make_voice(&voices.glideslope, "glideslope");
+    make_voice(&voices.glideslope_inop, "glideslope-inop");
+    make_voice(&voices.gpws_inop, "gpws-inop");
+    make_voice(&voices.hard_glideslope, "glideslope", "glideslope", 3.0);
+    make_voice(&voices.minimums, "minimums");
+    make_voice(&voices.minimums_minimums, "minimums", "minimums");
+    make_voice(&voices.pull_up, "pull-up");
+    make_voice(&voices.sink_rate, "sink-rate");
+    make_voice(&voices.sink_rate_pause_sink_rate, "sink-rate", STDPAUSE, "sink-rate");
+    make_voice(&voices.soft_glideslope, new Voice::SampleElement(get_sample("glideslope"), modify_amplitude(1.0, -6)));
+    make_voice(&voices.terrain, "terrain");
+    make_voice(&voices.terrain_pause_terrain, "terrain", STDPAUSE, "terrain");
+    make_voice(&voices.too_low_flaps, "too-low-flaps");
+    make_voice(&voices.too_low_gear, "too-low-gear");
+    make_voice(&voices.too_low_terrain, "too-low-terrain");
+
+    for (unsigned i = 0; i < n_altitude_callouts; i++)
+      {
+        std::ostringstream name;
+        name << "altitude-" << MK_VIII::Mode6Handler::altitude_callout_definitions[i];
+        make_voice(&voices.altitude_callouts[i], name.str().c_str());
+      }
+    speaker.update_configuration();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2587,7 +2371,7 @@ MK_VIII::SelfTestHandler::run ()
       if (mk->mode6_handler.conf.above_field_voice)
        return play(mk->mode6_handler.conf.above_field_voice);
     }
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     if (! was_here_offset(i))
       {
        if (mk->mode6_handler.conf.altitude_callouts_enabled[i])
@@ -2652,6 +2436,8 @@ MK_VIII::SelfTestHandler::stop ()
 
       button_pressed = false;
       state = STATE_NONE;
+      // reset self-test handler position
+      current=0;
     }
 }
 
@@ -2876,6 +2662,7 @@ MK_VIII::AlertHandler::update ()
        {
          assert(altitude_callout_voice != NULL);
          mk->voice_player.play(altitude_callout_voice);
+         altitude_callout_voice = NULL;
        }
     }
   else if (select_voice_alerts(ALERT_MODE4_TOO_LOW_GEAR))
@@ -2959,11 +2746,11 @@ MK_VIII::AlertHandler::update ()
        mk->voice_player.play(mk_voice(bank_angle_pause_bank_angle));
     }
 
-  // set new state
-
-  old_alerts = alerts;
+  // remember all alerts voiced so far...
+  old_alerts |= voice_alerts;
+  // ... forget those no longer active
+  old_alerts &= alerts;
   repeated_alerts = 0;
-  altitude_callout_voice = NULL;
 }
 
 void
@@ -2983,6 +2770,8 @@ MK_VIII::AlertHandler::unset_alerts (unsigned int _alerts)
 {
   alerts &= ~_alerts;
   repeated_alerts &= ~_alerts;
+  if (_alerts & ALERT_MODE6_ALTITUDE_CALLOUT)
+    altitude_callout_voice = NULL;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -3598,9 +3387,16 @@ MK_VIII::Mode3Handler::max_alt_loss (double _bias)
 double
 MK_VIII::Mode3Handler::get_bias (double initial_bias, double alt_loss)
 {
-  if (mk_data(radio_altitude).get() > 0)
-    while (alt_loss > max_alt_loss(initial_bias))
+  // do not repeat altitude-loss alerts below 30ft agl
+  if (mk_data(radio_altitude).get() > 30)
+  {
+    if (initial_bias < 0.0) // sanity check
+      initial_bias = 0.0;
+    // mk-viii spec: repeat alerts whenever losing 20% of initial altitude
+    while ((alt_loss > max_alt_loss(initial_bias))&&
+           (initial_bias < 1.0))
       initial_bias += 0.2;
+  }
 
   return initial_bias;
 }
@@ -3732,8 +3528,15 @@ MK_VIII::Mode4Handler::get_ab_envelope ()
 double
 MK_VIII::Mode4Handler::get_bias (double initial_bias, double min_agl)
 {
-  while (mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)
-    initial_bias += 0.2;
+  // do not repeat terrain/gear/flap alerts below 30ft agl
+  if (mk_data(radio_altitude).get() > 30.0)
+  {
+    if (initial_bias < 0.0) // sanity check
+      initial_bias = 0.0;
+    while ((mk_data(radio_altitude).get() < min_agl - min_agl * initial_bias)&&
+           (initial_bias < 1.0))
+      initial_bias += 0.2;
+  }
 
   return initial_bias;
 }
@@ -3792,6 +3595,7 @@ MK_VIII::Mode4Handler::update_ab ()
     }
 
   mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
+  ab_bias=0.0;
 }
 
 void
@@ -3817,6 +3621,7 @@ MK_VIII::Mode4Handler::update_ab_expanded ()
     }
 
   mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
+  ab_expanded_bias=0.0;
 }
 
 void
@@ -3833,7 +3638,10 @@ MK_VIII::Mode4Handler::update_c ()
       && mk_data(radio_altitude).get() < mk_data(terrain_clearance).get())
     handle_alert(mk_alert(MODE4C_TOO_LOW_TERRAIN), mk_data(terrain_clearance).get(), &c_bias);
   else
+  {
     mk_unset_alerts(mk_alert(MODE4C_TOO_LOW_TERRAIN));
+    c_bias=0.0;
+  }
 }
 
 void
@@ -3873,6 +3681,7 @@ MK_VIII::Mode5Handler::is_hard ()
 bool
 MK_VIII::Mode5Handler::is_soft (double bias)
 {
+  // do not repeat glide-slope alerts below 30ft agl
   if (mk_data(radio_altitude).get() > 30)
     {
       double bias_dots = 1.3 * bias;
@@ -3911,7 +3720,10 @@ MK_VIII::Mode5Handler::is_soft (double bias)
 double
 MK_VIII::Mode5Handler::get_soft_bias (double initial_bias)
 {
-  while (is_soft(initial_bias))
+  if (initial_bias < 0.0) // sanity check
+    initial_bias = 0.0;
+  while ((is_soft(initial_bias))&&
+         (initial_bias < 1.0))
     initial_bias += 0.2;
 
   return initial_bias;
@@ -4011,14 +3823,14 @@ MK_VIII::Mode6Handler::reset_minimums ()
 void
 MK_VIII::Mode6Handler::reset_altitude_callouts ()
 {
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     altitude_callouts_issued[i] = false;
 }
 
 bool
 MK_VIII::Mode6Handler::is_playing_altitude_callout ()
 {
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     if (mk->voice_player.voice == mk_altitude_voice(i)
        || mk->voice_player.next_voice == mk_altitude_voice(i))
       return true;
@@ -4092,7 +3904,7 @@ MK_VIII::Mode6Handler::boot ()
   last_radio_altitude.set(&mk_data(radio_altitude));
 
   // [SPEC] 6.4.2
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     altitude_callouts_issued[i] = ! mk_data(radio_altitude).ncd
       && mk_data(radio_altitude).get() <= altitude_callout_definitions[i];
 
@@ -4130,11 +3942,11 @@ MK_VIII::Mode6Handler::leave_takeoff ()
 }
 
 void
-MK_VIII::Mode6Handler::set_volume (double volume)
+MK_VIII::Mode6Handler::set_volume (float volume)
 {
   mk_voice(minimums_minimums)->set_volume(volume);
   mk_voice(five_hundred_above)->set_volume(volume);
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     mk_altitude_voice(i)->set_volume(volume);
 }
 
@@ -4144,7 +3956,7 @@ MK_VIII::Mode6Handler::altitude_callouts_enabled ()
   if (conf.minimums_enabled || conf.smart_500_enabled || conf.above_field_voice)
     return true;
 
-  for (int i = 0; i < n_altitude_callouts; i++)
+  for (unsigned i = 0; i < n_altitude_callouts; i++)
     if (conf.altitude_callouts_enabled[i])
       return true;
 
@@ -4197,7 +4009,7 @@ MK_VIII::Mode6Handler::update_altitude_callouts ()
   if (! mk->io_handler.gpws_inhibit()
       && ! mk->state_handler.ground // [1]
       && ! mk_data(radio_altitude).ncd)
-    for (int i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
+    for (unsigned i = 0; i < n_altitude_callouts && mk_data(radio_altitude).get() <= altitude_callout_definitions[i]; i++)
       if ((conf.altitude_callouts_enabled[i]
           || (altitude_callout_definitions[i] == 500
               && conf.smart_500_enabled))
@@ -4206,7 +4018,7 @@ MK_VIII::Mode6Handler::update_altitude_callouts ()
              || last_radio_altitude.get() > altitude_callout_definitions[i]))
        {
          // lock out all callouts superior or equal to this one
-         for (int j = 0; j <= i; j++)
+         for (unsigned j = 0; j <= i; j++)
            altitude_callouts_issued[j] = true;
 
          altitude_callouts_issued[i] = true;
@@ -4237,9 +4049,7 @@ MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
     SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
   
   // get distance to threshold
-  double distance, az1, az2;
-  SGGeodesy::inverse(pos, _runway->threshold(), az1, az2, distance);
-  return distance * SG_METER_TO_NM <= 5;
+  return SGGeodesy::distanceNm(pos, _runway->threshold()) <= 5;
 }
 
 bool
@@ -4254,13 +4064,9 @@ MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
   return false;
 }
 
-bool MK_VIII::Mode6Handler::AirportFilter::pass(FGPositioned* a) const
+bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
 {
-  if (a->type() != FGPositioned::AIRPORT) {
-    return false;
-  }
-    
-  bool ok = self->test_airport(static_cast<FGAirport*>(a));
+  bool ok = self->test_airport(a);
   return ok;
 }
 
@@ -4443,68 +4249,9 @@ MK_VIII::Mode6Handler::update ()
 // TCFHandler /////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
-// Gets the difference between the azimuth from @from_lat,@from_lon to
-// @to_lat,@to_lon, and @to_heading, in degrees.
-double
-MK_VIII::TCFHandler::get_azimuth_difference (double from_lat,
-                                            double from_lon,
-                                            double to_lat,
-                                            double to_lon,
-                                            double to_heading)
-{
-  double az1, az2, distance;
-  geo_inverse_wgs_84(0, from_lat, from_lon, to_lat, to_lon, &az1, &az2, &distance);
-  return get_heading_difference(az1, to_heading);
-}
-
-// Gets the difference between the azimuth from the current GPS
-// position to the center of @_runway, and the heading of @_runway, in
-// degrees.
-double
-MK_VIII::TCFHandler::get_azimuth_difference (const FGRunway *_runway)
+bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
 {
-  return get_azimuth_difference(mk_data(gps_latitude).get(),
-                               mk_data(gps_longitude).get(),
-                               _runway->latitude(),
-                               _runway->longitude(),
-                               _runway->headingDeg());
-}
-
-// Selects the most likely intended destination runway of @airport,
-// and returns it in @_runway. For each runway, the difference between
-// the azimuth from the current GPS position to the center of the
-// runway and its heading is computed. The runway having the smallest
-// difference wins.
-//
-// This selection algorithm is not specified in [SPEC], but
-// http://www.egpws.com/general_information/description/runway_select.htm
-// talks about automatic runway selection.
-FGRunway*
-MK_VIII::TCFHandler::select_runway (const FGAirport *airport)
-{
-  FGRunway* _runway = 0;
-  double min_diff = 360;
-  
-  for (unsigned int r=0; r<airport->numRunways(); ++r) {
-    FGRunway* rwy(airport->getRunwayByIndex(r));
-    double diff = get_azimuth_difference(rwy);
-    if (diff < min_diff)
-         {
-      min_diff = diff;
-      _runway = rwy;
-    }
-  } // of airport runways iteration
-  return _runway;
-}
-
-bool MK_VIII::TCFHandler::AirportFilter::pass(FGPositioned* aPos) const
-{
-  if (aPos->type() != FGPositioned::AIRPORT) {
-    return false;
-  }
-  
-  FGAirport* apt = static_cast<FGAirport*>(aPos);
-  return apt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
+  return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
 }
    
 void
@@ -4521,107 +4268,39 @@ MK_VIII::TCFHandler::update_runway ()
   // large airports, which may have a runway located far away from
   // the airport's reference point.
   AirportFilter filter(mk);
-  FGAirport* apt = FGAirport::findClosest(
-    SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
-    30.0, &filter);
+  SGGeod apos = SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get());
+  FGAirport* apt = FGAirport::findClosest(apos, 30.0, &filter);
       
   if (!apt) return;
   
+         FGRunway* _runway = apt->findBestRunwayForPos(apos).get();
+    
+  if (!_runway) return;
+
          has_runway = true;
 
-         FGRunway* _runway = select_runway(apt);
-    
-         runway.center.latitude = _runway->latitude();
-         runway.center.longitude = _runway->longitude();
+         runway.center = _runway->pointOnCenterline(_runway->lengthM() * 0.5);
 
          runway.elevation = apt->elevation();
 
+          runway.half_width_m = _runway->widthM() * 0.5;
          double half_length_m = _runway->lengthM() * 0.5;
          runway.half_length = half_length_m * SG_METER_TO_NM;
 
-         //        b3 ________________ b0
-         //          |                |
-         //    h1>>> |  e1<<<<<<<<e0  | <<<h0
-         //          |________________|
-         //        b2                  b1
-
-         // get heading to runway threshold (h0) and end (h1)
-         runway.edges[0].heading = _runway->headingDeg();
-         runway.edges[1].heading = get_reciprocal_heading(_runway->headingDeg());
+         // _________
+         //          |
+         //  <<<<e0  | <<<h0
+         // _________|
 
-         double az;
+         // get heading of runway end (h0)
+         runway.edge.heading = _runway->headingDeg();
 
          // get position of runway threshold (e0)
-         geo_direct_wgs_84(0,
-                           runway.center.latitude,
-                           runway.center.longitude,
-                           runway.edges[1].heading,
-                           half_length_m,
-                           &runway.edges[0].position.latitude,
-                           &runway.edges[0].position.longitude,
-                           &az);
-
-         // get position of runway end (e1)
-         geo_direct_wgs_84(0,
-                           runway.center.latitude,
-                           runway.center.longitude,
-                           runway.edges[0].heading,
-                           half_length_m,
-                           &runway.edges[1].position.latitude,
-                           &runway.edges[1].position.longitude,
-                           &az);
-
-         double half_width_m = _runway->widthM() * 0.5;
-
-         // get position of threshold bias area edges (b0 and b1)
-         get_bias_area_edges(&runway.edges[0].position,
-                             runway.edges[1].heading,
-                             half_width_m,
-                             &runway.bias_area[0],
-                             &runway.bias_area[1]);
-
-         // get position of end bias area edges (b2 and b3)
-         get_bias_area_edges(&runway.edges[1].position,
-                             runway.edges[0].heading,
-                             half_width_m,
-                             &runway.bias_area[2],
-                             &runway.bias_area[3]);
-}
+          runway.edge.position = _runway->begin();
 
-void
-MK_VIII::TCFHandler::get_bias_area_edges (Position *edge,
-                                         double reciprocal,
-                                         double half_width_m,
-                                         Position *bias_edge1,
-                                         Position *bias_edge2)
-{
-  double half_bias_width_m = k * SG_NM_TO_METER + half_width_m;
-  double tmp_latitude, tmp_longitude, az;
-
-  geo_direct_wgs_84(0,
-                   edge->latitude,
-                   edge->longitude,
-                   reciprocal,
-                   k * SG_NM_TO_METER,
-                   &tmp_latitude,
-                   &tmp_longitude,
-                   &az);
-  geo_direct_wgs_84(0,
-                   tmp_latitude,
-                   tmp_longitude,
-                   heading_substract(reciprocal, 90),
-                   half_bias_width_m,
-                   &bias_edge1->latitude,
-                   &bias_edge1->longitude,
-                   &az);
-  geo_direct_wgs_84(0,
-                   tmp_latitude,
-                   tmp_longitude,
-                   heading_add(reciprocal, 90),
-                   half_bias_width_m,
-                   &bias_edge2->latitude,
-                   &bias_edge2->longitude,
-                   &az);
+          // get cartesian coordinates of both runway ends
+          runway.bias_points[0] = _runway->cart();
+          runway.bias_points[1] = _runway->reciprocalRunway()->cart();
 }
 
 // Returns true if the current GPS position is inside the edge
@@ -4633,11 +4312,10 @@ MK_VIII::TCFHandler::get_bias_area_edges (Position *edge,
 bool
 MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
 {
-  return get_azimuth_difference(mk_data(gps_latitude).get(),
-                               mk_data(gps_longitude).get(),
-                               edge->position.latitude,
-                               edge->position.longitude,
-                               edge->heading) <= 45;
+  double az = SGGeodesy::courseDeg( SGGeod::fromDeg(mk_data(gps_longitude).get(),
+                                                    mk_data(gps_latitude).get()),
+                                    edge->position);
+  return get_heading_difference(az, edge->heading) <= 45;
 }
 
 // Returns true if the current GPS position is inside the bias area of
@@ -4645,105 +4323,65 @@ MK_VIII::TCFHandler::is_inside_edge_triangle (RunwayEdge *edge)
 bool
 MK_VIII::TCFHandler::is_inside_bias_area ()
 {
-  double az1[4];
-  double angles_sum = 0;
-
-  for (int i = 0; i < 4; i++)
-    {
-      double az2, distance;
-      geo_inverse_wgs_84(0,
-                        mk_data(gps_latitude).get(),
-                        mk_data(gps_longitude).get(),
-                        runway.bias_area[i].latitude,
-                        runway.bias_area[i].longitude,
-                        &az1[i], &az2, &distance);
-    }
-
-  for (int i = 0; i < 4; i++)
-    {
-      double angle = az1[i == 3 ? 0 : i + 1] - az1[i];
-      if (angle < -180)
-       angle += 360;
-
-      angles_sum += angle;
-    }
-
-  return angles_sum > 180;
+  double half_bias_width_m = k * SG_NM_TO_METER + runway.half_width_m;
+  SGVec3d cpos = SGVec3d::fromGeod(  SGGeod::fromDegFt(mk_data(gps_longitude).get(),
+                                                       mk_data(gps_latitude).get(),
+                                                       runway.elevation) );
+  SGLineSegmentd bias_line = SGLineSegmentd(runway.bias_points[0], runway.bias_points[1]);
+  return dist(cpos, bias_line) < half_bias_width_m;
 }
 
 bool
 MK_VIII::TCFHandler::is_tcf ()
 {
   if (mk_data(radio_altitude).get() > 10)
+  {
+    if (has_runway)
     {
-      if (has_runway)
-       {
-         double distance, az1, az2;
-
-         geo_inverse_wgs_84(0,
-                            mk_data(gps_latitude).get(),
-                            mk_data(gps_longitude).get(),
-                            runway.center.latitude,
-                            runway.center.longitude,
-                            &az1, &az2, &distance);
+      double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
+                                                                 mk_data(gps_latitude).get(),
+                                                                 runway.elevation),
+                                               runway.center);
 
-         distance *= SG_METER_TO_NM;
+      // distance to the inner envelope edge
+      double edge_distance = distance - runway.half_length - k;
 
-         // distance to the inner envelope edge
-         double edge_distance = distance - runway.half_length - k;
-
-         if (edge_distance >= 15)
-           {
-             if (mk_data(radio_altitude).get() < 700)
-               return true;
-           }
-         else if (edge_distance >= 12)
-           {
-             if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
-               return true;
-           }
-         else if (edge_distance >= 4)
-           {
-             if (mk_data(radio_altitude).get() < 400)
-               return true;
-           }
-         else if (edge_distance >= 2.45)
-           {
-             if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
-               return true;
-           }
-         else
-           {
-             if (is_inside_edge_triangle(&runway.edges[0]) || is_inside_edge_triangle(&runway.edges[1]))
-               {
-                 if (edge_distance >= 1)
-                   {
-                     if (mk_data(radio_altitude).get() < 66.666667 * edge_distance + 133.33333)
-                       return true;
-                   }
-                 else if (edge_distance >= 0.05)
-                   {
-                     if (mk_data(radio_altitude).get() < 200 * edge_distance)
-                       return true;
-                   }
-               }
-             else
-               {
-                 if (! is_inside_bias_area())
-                   {
-                     if (mk_data(radio_altitude).get() < 245)
-                       return true;
-                   }
-               }
-           }
-       }
-      else
-       {
-         if (mk_data(radio_altitude).get() < 700)
-           return true;
-       }
+      if (edge_distance > 15)
+      {
+        if (mk_data(radio_altitude).get() < 700)
+          return true;
+      }
+      else if (edge_distance > 12)
+      {
+        if (mk_data(radio_altitude).get() < 100 * edge_distance - 800)
+          return true;
+      }
+      else if (edge_distance > 4)
+      {
+        if (mk_data(radio_altitude).get() < 400)
+          return true;
+      }
+      else if (edge_distance > 2.45)
+      {
+        if (mk_data(radio_altitude).get() < 100 * edge_distance)
+          return true;
+      }
+      else if ( is_inside_edge_triangle(&runway.edge) && (edge_distance > 0.01) )
+      {
+        if (mk_data(radio_altitude).get() < 100 * edge_distance)
+          return true;
+      }
+      else if (! is_inside_bias_area())
+      {
+        if (mk_data(radio_altitude).get() < 245)
+          return true;
+      }
     }
-
+    else if (mk_data(radio_altitude).get() < 700)
+    {
+      return true;
+    }
+  }
   return false;
 }
 
@@ -4751,34 +4389,25 @@ bool
 MK_VIII::TCFHandler::is_rfcf ()
 {
   if (has_runway)
-    {
-      double distance, az1, az2;
-      geo_inverse_wgs_84(0,
-                        mk_data(gps_latitude).get(),
-                        mk_data(gps_longitude).get(),
-                        runway.center.latitude,
-                        runway.center.longitude,
-                        &az1, &az2, &distance);
+  {
+    double distance = SGGeodesy::distanceNm( SGGeod::fromDegFt(mk_data(gps_longitude).get(),
+                                                               mk_data(gps_latitude).get(),
+                                                               runway.elevation),
+                                             runway.center);
+    distance -= runway.half_length;
 
+    if (distance < 5.0)
+    {
       double krf = k + mk_data(gps_vertical_figure_of_merit).get() / 200;
-      distance = distance * SG_METER_TO_NM - runway.half_length - krf;
-
-      if (distance <= 5)
-       {
-         double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
+      distance -= krf;
+      double altitude_above_field = mk_data(geometric_altitude).get() - runway.elevation;
 
-         if (distance >= 1.5)
-           {
-             if (altitude_above_field < 300)
-               return true;
-           }
-         else if (distance >= 0)
-           {
-             if (altitude_above_field < 200 * distance)
-               return true;
-           }
-       }
+      if ( (distance > 1.5) && (altitude_above_field < 300.0) )
+        return true;
+      else if ( (distance > 0.0) && (altitude_above_field < 200 * distance) )
+        return true;
     }
+  }
 
   return false;
 }
@@ -4820,8 +4449,15 @@ MK_VIII::TCFHandler::update ()
          if (mk_test_alert(TCF_TOO_LOW_TERRAIN))
            {
              double new_bias = bias;
-             while (*reference < initial_value - initial_value * new_bias)
-               new_bias += 0.2;
+             // do not repeat terrain alerts below 30ft agl
+             if (mk_data(radio_altitude).get() > 30)
+             {
+               if (new_bias < 0.0) // sanity check
+                 new_bias = 0.0;
+               while ((*reference < initial_value - initial_value * new_bias)&&
+                      (new_bias < 1.0))
+                 new_bias += 0.2;
+             }
 
              if (new_bias > bias)
                {
@@ -4849,10 +4485,9 @@ MK_VIII::TCFHandler::update ()
 ///////////////////////////////////////////////////////////////////////////////
 
 MK_VIII::MK_VIII (SGPropertyNode *node)
-  : name("mk-viii"),
+  : properties_handler(this),
+    name("mk-viii"),
     num(0),
-
-    properties_handler(this),
     power_handler(this),
     system_handler(this),
     configuration_module(this),
@@ -4904,7 +4539,7 @@ MK_VIII::bind ()
   configuration_module.bind(node);
   power_handler.bind(node);
   io_handler.bind(node);
-  voice_player.bind(node);
+  voice_player.bind(node, "Sounds/mk-viii/");
 }
 
 void