//
// 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/sound/soundmgr_openal.hxx>
#include <simgear/structure/exception.hxx>
-SG_USING_STD(string);
+using std::string;
-#include "Airports/runways.hxx"
-#include "Airports/simple.hxx"
-#ifndef _MSC_VER
-# include "Include/version.h"
+#include <Airports/runways.hxx>
+#include <Airports/simple.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"
return heading_add(h, 180);
}
-// Searches for the closest airport whose Manhattan distance to
-// @lat,@lon is inferior to @min_manhattan_distance (expressed in
-// degrees) and for which @test_airport returns true. Returns NULL if
-// no airport was found.
-template <class C>
-static const FGAirport *
-get_closest_airport (double lat,
- double lon,
- double min_manhattan_distance,
- C &obj,
- bool (C::*test_airport) (const FGAirport *))
-{
- const FGAirport *airport = NULL;
- const airport_list *airport_list = globals->get_airports()->getAirportList();
-
- for (size_t i = 0; i < airport_list->size(); i++)
- {
- const FGAirport *a = (*airport_list)[i];
- double dist = fabs(lat - a->getLatitude()) + fabs(lon - a->getLongitude());
- if (dist < min_manhattan_distance && (obj.*test_airport)(a))
- {
- airport = a;
- min_manhattan_distance = dist;
- }
- }
-
- return airport;
-}
-
///////////////////////////////////////////////////////////////////////////////
// PropertiesHandler //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
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);
mk_node(vs) = fgGetNode("/velocities/vertical-speed-fps", true);
}
-void
-MK_VIII::PropertiesHandler::unbind ()
-{
- vector<SGPropertyNode *>::iterator iter;
-
- for (iter = tied_properties.begin(); iter != tied_properties.end(); iter++)
- (*iter)->untie();
-
- tied_properties.clear();
-}
-
///////////////////////////////////////////////////////////////////////////////
// PowerHandler ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
if (replay_state != last_replay_state)
{
mk->alert_handler.reposition();
+ mk->io_handler.reposition();
last_replay_state = replay_state;
state = STATE_REPOSITION;
{ 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;
{ 101, { FIELD_500_ABOVE, 0 } }
};
- int i;
+ unsigned i;
mk->mode6_handler.conf.minimums_enabled = false;
mk->mode6_handler.conf.smart_500_enabled = false;
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;
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);
}
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;
}
{ 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;
{ 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));
// [PILOT] page 20 specifies that the terrain clearance is equal to
// 75% of the radio altitude, averaged over the previous 15 seconds.
- deque< Sample<double> >::iterator iter;
+ // 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
for (iter = samples.begin(); iter != samples.end() && globals->get_sim_time_sec() - (*iter).timestamp >= 15; iter = samples.begin())
double new_value = 0;
if (samples.size() > 0)
{
+ // 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;
{
samples.clear();
value = 0;
+ last_update = -1.0;
}
MK_VIII::IOHandler::IOHandler (MK_VIII *device)
mk_doutput(glideslope_cancel) = power_saved.glideslope_cancel;
altitude_samples.clear();
+ reset_terrain_clearance();
}
void
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))
}
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()
// Erase everything from the beginning of the list up to the sample
// preceding the most recent sample whose age is >= 1 second.
- deque< Sample< Parameter<double> > >::iterator erase_last = altitude_samples.begin();
- deque< Sample< Parameter<double> > >::iterator iter;
+ altitude_samples_type::iterator erase_last = altitude_samples.begin();
+ altitude_samples_type::iterator iter;
for (iter = altitude_samples.begin(); iter != altitude_samples.end(); iter++)
if (globals->get_sim_time_sec() - (*iter).timestamp >= 1)
update_terrain_clearance();
}
+void
+MK_VIII::IOHandler::reposition ()
+{
+ reset_terrain_clearance();
+}
+
void
MK_VIII::IOHandler::handle_input_fault (bool test, FaultHandler::Fault fault)
{
{ 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;
{ 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;
}
{ 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;
{ 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;
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));
}
"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]);
}
"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];
///////////////////////////////////////////////////////////////////////////////
-// 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();
}
///////////////////////////////////////////////////////////////////////////////
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])
button_pressed = false;
state = STATE_NONE;
+ // reset self-test handler position
+ current=0;
}
}
{
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))
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
{
alerts &= ~_alerts;
repeated_alerts &= ~_alerts;
+ if (_alerts & ALERT_MODE6_ALTITUDE_CALLOUT)
+ altitude_callout_voice = NULL;
}
///////////////////////////////////////////////////////////////////////////////
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;
}
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;
}
}
mk_unset_alerts(mk_alert(MODE4_TOO_LOW_FLAPS) | mk_alert(MODE4_TOO_LOW_GEAR));
+ ab_bias=0.0;
}
void
}
mk_unset_alerts(mk_alert(MODE4AB_TOO_LOW_TERRAIN));
+ ab_expanded_bias=0.0;
}
void
&& 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
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;
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;
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;
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];
}
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);
}
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;
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))
|| 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;
bool
MK_VIII::Mode6Handler::test_runway (const FGRunway *_runway)
{
- if (_runway->_length < mk->conf.runway_database)
+ if (_runway->lengthFt() < mk->conf.runway_database)
return false;
- // get position of threshold
- double latitude, longitude, az;
- geo_direct_wgs_84(0,
- _runway->_lat,
- _runway->_lon,
- get_reciprocal_heading(_runway->_heading),
- _runway->_length / 2 * SG_FEET_TO_METER,
- &latitude,
- &longitude,
- &az);
-
+ SGGeod pos(
+ SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()));
+
// get distance to threshold
double distance, az1, az2;
- geo_inverse_wgs_84(0,
- mk_data(gps_latitude).get(),
- mk_data(gps_longitude).get(),
- latitude,
- longitude,
- &az1, &az2, &distance);
-
+ SGGeodesy::inverse(pos, _runway->threshold(), az1, az2, distance);
return distance * SG_METER_TO_NM <= 5;
}
bool
MK_VIII::Mode6Handler::test_airport (const FGAirport *airport)
{
- FGRunway r;
- if (globals->get_runways()->search(airport->getId(), &r))
- do
- {
- if (test_runway(&r))
- return true;
-
- // reciprocal runway
- r._heading = get_reciprocal_heading(r._heading);
- if (test_runway(&r))
- return true;
- }
- while (globals->get_runways()->next(&r) && r._id == airport->getId());
+ for (unsigned int r=0; r<airport->numRunways(); ++r) {
+ FGRunway* rwy(airport->getRunwayByIndex(r));
+
+ if (test_runway(rwy)) return true;
+ }
return false;
}
+bool MK_VIII::Mode6Handler::AirportFilter::passAirport(FGAirport* a) const
+{
+ bool ok = self->test_airport(a);
+ return ok;
+}
+
void
MK_VIII::Mode6Handler::update_runway ()
{
- if (! mk_data(gps_latitude).ncd && ! mk_data(gps_longitude).ncd)
- {
- // Search for the closest runway threshold in range 5
- // nm. Passing 0.5 degrees (approximatively 30 nm) to
- // get_closest_airport() provides enough margin for large
- // airports, which may have a runway located far away from the
- // airport's reference point.
-
- const FGAirport *airport = get_closest_airport(mk_data(gps_latitude).get(),
- mk_data(gps_longitude).get(),
- 0.5,
- *this,
- &MK_VIII::Mode6Handler::test_airport);
-
- if (airport)
- runway.elevation = airport->getElevation();
-
- has_runway.set(airport != NULL);
- }
- else
- has_runway.unset();
+
+ if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
+ has_runway.unset();
+ return;
+ }
+
+ // Search for the closest runway threshold in range 5
+ // nm. Passing 30nm to
+ // get_closest_airport() provides enough margin for large
+ // airports, which may have a runway located far away from the
+ // airport's reference point.
+ AirportFilter filter(this);
+ FGPositionedRef apt = FGPositioned::findClosest(
+ SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()),
+ 30.0, &filter);
+ if (apt) {
+ runway.elevation = apt->elevation();
+ }
+
+ has_runway.set(apt != NULL);
}
void
{
return get_azimuth_difference(mk_data(gps_latitude).get(),
mk_data(gps_longitude).get(),
- _runway->_lat,
- _runway->_lon,
- _runway->_heading);
+ _runway->latitude(),
+ _runway->longitude(),
+ _runway->headingDeg());
}
// Selects the most likely intended destination runway of @airport,
// This selection algorithm is not specified in [SPEC], but
// http://www.egpws.com/general_information/description/runway_select.htm
// talks about automatic runway selection.
-void
-MK_VIII::TCFHandler::select_runway (const FGAirport *airport,
- FGRunway *_runway)
+FGRunway*
+MK_VIII::TCFHandler::select_runway (const FGAirport *airport)
{
- FGRunway r;
- bool status = globals->get_runways()->search(airport->getId(), &r);
- assert(status);
-
+ FGRunway* _runway = 0;
double min_diff = 360;
- do
- {
- double diff;
-
- diff = get_azimuth_difference(&r);
- if (diff < min_diff)
- {
- min_diff = diff;
- *_runway = r;
- }
-
- // reciprocal runway
- r._heading = get_reciprocal_heading(r._heading);
- diff = get_azimuth_difference(&r);
- if (diff < min_diff)
- {
- min_diff = diff;
- *_runway = r;
- }
+
+ 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;
}
- while (globals->get_runways()->next(&r) && r._id == airport->getId());
+ } // of airport runways iteration
+ return _runway;
}
-bool
-MK_VIII::TCFHandler::test_airport (const FGAirport *airport)
+bool MK_VIII::TCFHandler::AirportFilter::passAirport(FGAirport* aApt) const
{
- FGRunway r;
- if (globals->get_runways()->search(airport->getId(), &r))
- do
- {
- if (r._length >= mk->conf.runway_database)
- return true;
- }
- while (globals->get_runways()->next(&r) && r._id == airport->getId());
-
- return false;
+ return aApt->hasHardRunwayOfLengthFt(mk->conf.runway_database);
}
-
+
void
MK_VIII::TCFHandler::update_runway ()
{
has_runway = false;
- if (! mk_data(gps_latitude).ncd && ! mk_data(gps_longitude).ncd)
- {
- // Search for the intended destination runway of the closest
- // airport in range 15 nm. Passing 0.5 degrees (approximatively
- // 30 nm) to get_closest_airport() provides enough margin for
- // large airports, which may have a runway located far away from
- // the airport's reference point.
-
- const FGAirport *airport = get_closest_airport(mk_data(gps_latitude).get(),
- mk_data(gps_longitude).get(),
- 0.5,
- *this,
- &MK_VIII::TCFHandler::test_airport);
+ if (mk_data(gps_latitude).ncd || mk_data(gps_longitude).ncd) {
+ return;
+ }
+
+ // Search for the intended destination runway of the closest
+ // airport in range 15 nm. Passing 30nm to get_closest_airport()
+ // provides enough margin for
+ // 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);
+
+ if (!apt) return;
+
+ FGRunway* _runway = select_runway(apt);
+
+ if (!_runway) return;
- if (airport)
- {
has_runway = true;
- FGRunway _runway;
- select_runway(airport, &_runway);
-
- runway.center.latitude = _runway._lat;
- runway.center.longitude = _runway._lon;
+ runway.center.latitude = _runway->latitude();
+ runway.center.longitude = _runway->longitude();
- runway.elevation = airport->getElevation();
+ runway.elevation = apt->elevation();
- double half_length_m = _runway._length / 2 * SG_FEET_TO_METER;
+ double half_length_m = _runway->lengthM() * 0.5;
runway.half_length = half_length_m * SG_METER_TO_NM;
// b3 ________________ b0
// b2 b1
// get heading to runway threshold (h0) and end (h1)
- runway.edges[0].heading = _runway._heading;
- runway.edges[1].heading = get_reciprocal_heading(_runway._heading);
+ runway.edges[0].heading = _runway->headingDeg();
+ runway.edges[1].heading = get_reciprocal_heading(_runway->headingDeg());
double az;
&runway.edges[1].position.longitude,
&az);
- double half_width_m = _runway._width / 2 * SG_FEET_TO_METER;
+ 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,
half_width_m,
&runway.bias_area[2],
&runway.bias_area[3]);
- }
- }
}
void
Position *bias_edge2)
{
double half_bias_width_m = k * SG_NM_TO_METER + half_width_m;
- double tmp_latitude, tmp_longitude, az;
+ double tmp_latitude = 0.0, tmp_longitude = 0.0, az = 0.0;
geo_direct_wgs_84(0,
edge->latitude,
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)
{
///////////////////////////////////////////////////////////////////////////////
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),
configuration_module.bind(node);
power_handler.bind(node);
io_handler.bind(node);
- voice_player.bind(node);
+ voice_player.bind(node, "Sounds/mk-viii/");
}
void