From a650cf42395e78f73630eec9f13226c5950a6c68 Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 14 Sep 2009 21:53:55 +0000 Subject: [PATCH 1/6] Fix the has-gs flag when switching to a VOR (thanks Torsten), and expose GS validity via a new gs-in-range boolean prop, as suggested by John Denker. --- src/Instrumentation/navradio.cxx | 21 +++++++++++---------- src/Instrumentation/navradio.hxx | 1 + 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 3e779bbcd..cf05bc764 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -120,6 +120,7 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) : gs_deflection_norm_node(NULL), gs_rate_of_climb_node(NULL), gs_dist_node(NULL), + gs_inrange_node(NULL), nav_id_node(NULL), id_c1_node(NULL), id_c2_node(NULL), @@ -232,6 +233,8 @@ FGNavRadio::init () gs_deflection_norm_node = node->getChild("gs-needle-deflection-norm", 0, true); gs_rate_of_climb_node = node->getChild("gs-rate-of-climb", 0, true); gs_dist_node = node->getChild("gs-distance", 0, true); + gs_inrange_node = node->getChild("gs-in-range", 0, true); + nav_id_node = node->getChild("nav-id", 0, true); id_c1_node = node->getChild("nav-id_asc1", 0, true); id_c2_node = node->getChild("nav-id_asc2", 0, true); @@ -255,20 +258,12 @@ FGNavRadio::init () void FGNavRadio::bind () { - std::ostringstream temp; - string branch; - temp << _num; - branch = "/instrumentation/" + _name + "[" + temp.str() + "]"; } void FGNavRadio::unbind () { - std::ostringstream temp; - string branch; - temp << _num; - branch = "/instrumentation/" + _name + "[" + temp.str() + "]"; } @@ -382,6 +377,7 @@ void FGNavRadio::clearOutputs() gs_deflection_node->setDoubleValue( 0.0 ); gs_deflection_deg_node->setDoubleValue(0.0); gs_deflection_norm_node->setDoubleValue(0.0); + gs_inrange_node->setBoolValue( false ); to_flag_node->setBoolValue( false ); from_flag_node->setBoolValue( false ); @@ -532,12 +528,16 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig _gsNeedleDeflection = 0.0; if (!_gs || !inrange_node->getBoolValue()) { gs_dist_node->setDoubleValue( 0.0 ); + gs_inrange_node->setBoolValue(false); return; } double gsDist = dist(aircraft, _gsCart); gs_dist_node->setDoubleValue(gsDist); - if (gsDist > (_gs->get_range() * SG_NM_TO_METER)) { + bool gsInRange = (gsDist < (_gs->get_range() * SG_NM_TO_METER)); + gs_inrange_node->setBoolValue(gsInRange); + + if (!gsInRange) { return; } @@ -800,10 +800,11 @@ void FGNavRadio::search() if (nav->type() == FGPositioned::VOR) { target_radial = sel_radial_node->getDoubleValue(); _gs = NULL; + has_gs_node->setBoolValue(false); } else { // ILS or LOC _gs = globals->get_gslist()->findByFreq(freq, pos); - _localizerWidth = localizerWidth(nav); has_gs_node->setBoolValue(_gs != NULL); + _localizerWidth = localizerWidth(nav); twist = 0.0; effective_range = nav->get_range(); diff --git a/src/Instrumentation/navradio.hxx b/src/Instrumentation/navradio.hxx index 116a90ce7..7ef57f7bd 100644 --- a/src/Instrumentation/navradio.hxx +++ b/src/Instrumentation/navradio.hxx @@ -101,6 +101,7 @@ class FGNavRadio : public SGSubsystem SGPropertyNode_ptr gs_deflection_norm_node; SGPropertyNode_ptr gs_rate_of_climb_node; SGPropertyNode_ptr gs_dist_node; + SGPropertyNode_ptr gs_inrange_node; SGPropertyNode_ptr nav_id_node; SGPropertyNode_ptr id_c1_node; SGPropertyNode_ptr id_c2_node; From c1598361ca7d7337d192375e99c8cdf8fcdb81ed Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 14 Sep 2009 22:41:29 +0000 Subject: [PATCH 2/6] navradio: fix crash spotted by Torsten, when nav range is unspecified. --- src/Instrumentation/navradio.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index cf05bc764..19e29a226 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -271,6 +271,10 @@ FGNavRadio::unbind () double FGNavRadio::adjustNavRange( double stationElev, double aircraftElev, double nominalRange ) { + if (nominalRange < 0.0) { + nominalRange = FG_NAV_DEFAULT_RANGE; + } + // extend out actual usable range to be 1.3x the published safe range const double usability_factor = 1.3; @@ -454,7 +458,7 @@ void FGNavRadio::updateReceiver(double dt) effective_range = adjustNavRange( nav_elev, pos.getElevationM(), _navaid->get_range() ); } - + double effective_range_m = effective_range * SG_NM_TO_METER; ////////////////////////////////////////////////////////// From ebf5dcb828cbf0776b78d6d730f6df5fdbbc31ba Mon Sep 17 00:00:00 2001 From: jmt Date: Tue, 15 Sep 2009 10:15:42 +0000 Subject: [PATCH 3/6] Work around the penaltyForNav broken-ness, by ensuring GS behaves sanely even if non-matches LOC and GS transmitters are selected. Which happens. Yuck. --- src/Instrumentation/navradio.cxx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 19e29a226..744ce74ee 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -552,7 +552,7 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig double dot_v = dot(pos, _gsVertical); double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES; double deflectionAngle = target_gs - angle; - + // Construct false glideslopes. The scale factor of 1.5 // in the sawtooth gives a period of 6 degrees. // There will be zeros at 3, 6r, 9, 12r et cetera @@ -819,14 +819,22 @@ void FGNavRadio::search() int tmp = (int)(_gs->get_multiuse() / 1000.0); target_gs = (double)tmp / 100.0; + // until penaltyForNav goes away, we cannot assume we always pick + // paired LOC/GS trasmsitters. As we pass over a runway threshold, we + // often end up picking the 'wrong' LOC, but the correct GS. To avoid + // breaking the basis computation, ensure we use the GS radial and not + // the (potentially reversed) LOC radial. + double gs_radial = fmod(_gs->get_multiuse(), 1000.0); + SG_NORMALIZE_RANGE(gs_radial, 0.0, 360.0); + // GS axis unit tangent vector // (along the runway) _gsCart = _gs->cart(); - _gsAxis = tangentVector(_gs->geod(), _gsCart, target_radial); + _gsAxis = tangentVector(_gs->geod(), _gsCart, gs_radial); // GS baseline unit tangent vector // (perpendicular to the runay along the ground) - SGVec3d baseline = tangentVector(_gs->geod(), _gsCart, target_radial + 90.0); + SGVec3d baseline = tangentVector(_gs->geod(), _gsCart, gs_radial + 90.0); _gsVertical = cross(baseline, _gsAxis); } // of have glideslope } // of found LOC or ILS From bf830f681d1641aa2b9cf5fbc439f41c22bb4511 Mon Sep 17 00:00:00 2001 From: jmt Date: Tue, 15 Sep 2009 15:34:36 +0000 Subject: [PATCH 4/6] One more divide by zero fix from Torsten. --- src/Instrumentation/navradio.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 744ce74ee..37e4c6101 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -271,7 +271,7 @@ FGNavRadio::unbind () double FGNavRadio::adjustNavRange( double stationElev, double aircraftElev, double nominalRange ) { - if (nominalRange < 0.0) { + if (nominalRange <= 0.0) { nominalRange = FG_NAV_DEFAULT_RANGE; } From 5947f3fcd5d1ec608168539553d0f38e1b7e3868 Mon Sep 17 00:00:00 2001 From: jmt Date: Wed, 16 Sep 2009 10:12:50 +0000 Subject: [PATCH 5/6] NavRadio DME in-range flag, and audio code improvements. By John Denker, adapted to current trunk by me. --- src/Instrumentation/navradio.cxx | 86 ++++++++++++++++++++++---------- src/Instrumentation/navradio.hxx | 8 ++- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 37e4c6101..042cdbdf0 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -78,6 +78,17 @@ static SGVec3d tangentVector(const SGGeod& tail, const SGVec3d& tail_xyz, return (head_xyz - tail_xyz) * (1.0/fudge); } +// Create a "serviceable" node with a default value of "true" +SGPropertyNode_ptr createServiceableProp(SGPropertyNode* aParent, const char* aName) +{ + SGPropertyNode_ptr n = (aParent->getChild(aName, 0, true)->getChild("serviceable", 0, true)); + simgear::props::Type typ = n->getType(); + if ((typ == simgear::props::NONE) || (typ == simgear::props::UNSPECIFIED)) { + n->setBoolValue(true); + } + return n; +} + // Constructor FGNavRadio::FGNavRadio(SGPropertyNode *node) : lon_node(fgGetNode("/position/longitude-deg", true)), @@ -96,6 +107,7 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) : cdi_serviceable_node(NULL), gs_serviceable_node(NULL), tofrom_serviceable_node(NULL), + dme_serviceable_node(NULL), fmt_freq_node(NULL), fmt_alt_freq_node(NULL), heading_node(NULL), @@ -190,14 +202,13 @@ FGNavRadio::init () audio_btn_node->setBoolValue( true ); backcourse_node = node->getChild("back-course-btn", 0, true); backcourse_node->setBoolValue( false ); + nav_serviceable_node = node->getChild("serviceable", 0, true); - cdi_serviceable_node = (node->getChild("cdi", 0, true)) - ->getChild("serviceable", 0, true); - gs_serviceable_node = (node->getChild("gs", 0, true)) - ->getChild("serviceable"); - tofrom_serviceable_node = (node->getChild("to-from", 0, true)) - ->getChild("serviceable", 0, true); - + cdi_serviceable_node = createServiceableProp(node, "cdi"); + gs_serviceable_node = createServiceableProp(node, "gs"); + tofrom_serviceable_node = createServiceableProp(node, "to-from"); + dme_serviceable_node = createServiceableProp(node, "dme"); + // frequencies SGPropertyNode *subnode = node->getChild("frequencies", 0, true); freq_node = subnode->getChild("selected-mhz", 0, true); @@ -241,6 +252,8 @@ FGNavRadio::init () id_c3_node = node->getChild("nav-id_asc3", 0, true); id_c4_node = node->getChild("nav-id_asc4", 0, true); + node->tie("dme-in-range", SGRawValuePointer(&_dmeInRange)); + // gps slaving support nav_slaved_to_gps_node = node->getChild("slaved-to-gps", 0, true); gps_cdi_deflection_node = fgGetNode("/instrumentation/gps/cdi-deflection", true); @@ -258,6 +271,7 @@ FGNavRadio::init () void FGNavRadio::bind () { + } @@ -385,6 +399,8 @@ void FGNavRadio::clearOutputs() to_flag_node->setBoolValue( false ); from_flag_node->setBoolValue( false ); + + _dmeInRange = false; } void FGNavRadio::updateReceiver(double dt) @@ -523,6 +539,7 @@ void FGNavRadio::updateReceiver(double dt) _cdiCrossTrackErrorM = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS); updateGlideSlope(dt, aircraft, signal_quality_norm); + updateDME(aircraft); last_loc_dist = loc_dist; } @@ -594,6 +611,17 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig * horiz_vel * SG_METER_TO_FEET ); } +void FGNavRadio::updateDME(const SGVec3d& aircraft) +{ + if (!_dme || !dme_serviceable_node->getBoolValue()) { + _dmeInRange = false; + return; + } + + double dme_distance = dist(aircraft, _dme->cart()); + _dmeInRange = (dme_distance < _dme->get_range() * SG_NM_TO_METER); +} + void FGNavRadio::updateGPSSlaved() { has_gs_node->setBoolValue(gps_has_gs_node->getBoolValue()); @@ -743,25 +771,34 @@ void FGNavRadio::updateAudio() } else { SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-dme-ident sound" ); } + + const int NUM_IDENT_SLOTS = 5; + const time_t SLOT_LENGTH = 5; // seconds - if ( last_time < globals->get_time_params()->get_cur_time() - 30 ) { - last_time = globals->get_time_params()->get_cur_time(); - play_count = 0; + // There are N slots numbered 0 through (NUM_IDENT_SLOTS-1) inclusive. + // Each slot is 5 seconds long. + // Slots 0 is for DME + // the rest are for azimuth. + time_t now = globals->get_time_params()->get_cur_time(); + if ((now >= last_time) && (now < last_time + SLOT_LENGTH)) { + return; // wait longer } - if ( play_count < 4 ) { - // play VOR ident - if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) { - globals->get_soundmgr()->play_once( nav_fx_name ); - ++play_count; + last_time = now; + play_count = ++play_count % NUM_IDENT_SLOTS; + + // Previous ident is out of time; if still playing, cut it off: + globals->get_soundmgr()->stop( nav_fx_name ); + globals->get_soundmgr()->stop( dme_fx_name ); + if (play_count == 0) { // the DME slot + if (_dmeInRange && dme_serviceable_node->getBoolValue()) { + // play DME ident + globals->get_soundmgr()->play_once( dme_fx_name ); + } + } else { // NAV slot + if (inrange_node->getBoolValue() && nav_serviceable_node->getBoolValue()) { + globals->get_soundmgr()->play_once(nav_fx_name); } - } else if ( play_count < 5 && has_dme) { - // play DME ident - if ( !globals->get_soundmgr()->is_playing(nav_fx_name) && - !globals->get_soundmgr()->is_playing(dme_fx_name) ) { - globals->get_soundmgr()->play_once( dme_fx_name ); - ++play_count; - } } } @@ -791,8 +828,7 @@ void FGNavRadio::search() _navaid = nav; char identBuffer[5] = " "; if (nav) { - FGNavRecord* dme = globals->get_dmelist()->findByFreq(freq, pos); - has_dme = (dme != NULL); + _dme = globals->get_dmelist()->findByFreq(freq, pos); nav_id_node->setStringValue(nav->get_ident()); strncpy(identBuffer, nav->ident().c_str(), 5); @@ -842,8 +878,8 @@ void FGNavRadio::search() audioNavidChanged(); } else { // found nothing _gs = NULL; + _dme = NULL; nav_id_node->setStringValue(""); - has_dme = false; globals->get_soundmgr()->remove( nav_fx_name ); globals->get_soundmgr()->remove( dme_fx_name ); } diff --git a/src/Instrumentation/navradio.hxx b/src/Instrumentation/navradio.hxx index 7ef57f7bd..47e232512 100644 --- a/src/Instrumentation/navradio.hxx +++ b/src/Instrumentation/navradio.hxx @@ -66,7 +66,8 @@ class FGNavRadio : public SGSubsystem SGPropertyNode_ptr cdi_serviceable_node; SGPropertyNode_ptr gs_serviceable_node; SGPropertyNode_ptr tofrom_serviceable_node; - + SGPropertyNode_ptr dme_serviceable_node; + // property outputs SGPropertyNode_ptr fmt_freq_node; // formated frequency SGPropertyNode_ptr fmt_alt_freq_node; // formated alternate frequency @@ -125,7 +126,6 @@ class FGNavRadio : public SGSubsystem string nav_fx_name; string dme_fx_name; - bool has_dme; double target_radial; SGTimeStamp prev_time; SGTimeStamp curr_time; @@ -146,6 +146,9 @@ class FGNavRadio : public SGSubsystem SGVec3d _gsCart, _gsAxis, _gsVertical; + FGNavRecordPtr _dme; + bool _dmeInRange; + // CDI properties bool _toFlag, _fromFlag; double _cdiDeflection; @@ -167,6 +170,7 @@ class FGNavRadio : public SGSubsystem void audioNavidChanged(); void updateReceiver(double dt); + void updateDME(const SGVec3d& aircraft); void updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm); void updateGPSSlaved(); void updateCDI(double dt); From a683b5e0ed39743c42403988802b11de6a3b34fb Mon Sep 17 00:00:00 2001 From: jmt Date: Thu, 17 Sep 2009 09:30:19 +0000 Subject: [PATCH 6/6] NavRadio: By way of an experiment, make false courses/lobes disable-able via a property. --- src/Instrumentation/navradio.cxx | 62 +++++++++++++++++++++----------- src/Instrumentation/navradio.hxx | 3 ++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 042cdbdf0..ee217d6c9 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -153,7 +153,8 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) : _localizerWidth(5.0), _name(node->getStringValue("name", "nav")), _num(node->getIntValue("number", 0)), - _time_before_search_sec(-1.0) + _time_before_search_sec(-1.0), + _falseCoursesEnabled(true) { SGPath path( globals->get_fg_root() ); SGPath term = path; @@ -209,6 +210,9 @@ FGNavRadio::init () tofrom_serviceable_node = createServiceableProp(node, "to-from"); dme_serviceable_node = createServiceableProp(node, "dme"); + globals->get_props()->tie("sim/realism/false-radio-courses-enabled", + SGRawValuePointer(&_falseCoursesEnabled)); + // frequencies SGPropertyNode *subnode = node->getChild("frequencies", 0, true); freq_node = subnode->getChild("selected-mhz", 0, true); @@ -518,12 +522,28 @@ void FGNavRadio::updateReceiver(double dt) SG_NORMALIZE_RANGE(r, -180.0, 180.0); if ( is_loc ) { - // The factor of 30.0 gives a period of 120 which gives us 3 cycles and six - // zeros i.e. six courses: one front course, one back course, and four - // false courses. Three of the six are reverse sensing. - _cdiDeflection = 30.0 * sawtooth(r / 30.0); + if (_falseCoursesEnabled) { + // The factor of 30.0 gives a period of 120 which gives us 3 cycles and six + // zeros i.e. six courses: one front course, one back course, and four + // false courses. Three of the six are reverse sensing. + _cdiDeflection = 30.0 * sawtooth(r / 30.0); + } else { + // no false courses, but we do need to create a back course + if (fabs(r) > 90.0) { // front course + _cdiDeflection = r - copysign(180.0, r); + } else { + _cdiDeflection = r; // back course + } + + _cdiDeflection = -_cdiDeflection; // reverse for outbound radial + } // of false courses disabled + const double VOR_FULL_ARC = 20.0; // VOR is -10 .. 10 degree swing _cdiDeflection *= VOR_FULL_ARC / _localizerWidth; // increased localiser sensitivity + + if (backcourse_node->getBoolValue()) { + _cdiDeflection = -_cdiDeflection; + } } else { // handle the TO side of the VOR if (fabs(r) > 90.0) { @@ -569,21 +589,23 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig double dot_v = dot(pos, _gsVertical); double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES; double deflectionAngle = target_gs - angle; - - // Construct false glideslopes. The scale factor of 1.5 - // in the sawtooth gives a period of 6 degrees. - // There will be zeros at 3, 6r, 9, 12r et cetera - // where "r" indicates reverse sensing. - // This is is consistent with conventional pilot lore - // e.g. http://www.allstar.fiu.edu/aerojava/ILS.htm - // but inconsistent with - // http://www.freepatentsonline.com/3757338.html - // - // It may be that some of each exist. - if (deflectionAngle < 0) { - deflectionAngle = 1.5 * sawtooth(deflectionAngle / 1.5); - } else { - // no false GS below the true GS + + if (_falseCoursesEnabled) { + // Construct false glideslopes. The scale factor of 1.5 + // in the sawtooth gives a period of 6 degrees. + // There will be zeros at 3, 6r, 9, 12r et cetera + // where "r" indicates reverse sensing. + // This is is consistent with conventional pilot lore + // e.g. http://www.allstar.fiu.edu/aerojava/ILS.htm + // but inconsistent with + // http://www.freepatentsonline.com/3757338.html + // + // It may be that some of each exist. + if (deflectionAngle < 0) { + deflectionAngle = 1.5 * sawtooth(deflectionAngle / 1.5); + } else { + // no false GS below the true GS + } } _gsNeedleDeflection = deflectionAngle * 5.0; diff --git a/src/Instrumentation/navradio.hxx b/src/Instrumentation/navradio.hxx index 47e232512..3ee3e4155 100644 --- a/src/Instrumentation/navradio.hxx +++ b/src/Instrumentation/navradio.hxx @@ -156,6 +156,9 @@ class FGNavRadio : public SGSubsystem double _gsNeedleDeflection; double _gsNeedleDeflectionNorm; + // realism setting, are false courses and GS lobes enabled? + bool _falseCoursesEnabled; + bool updateWithPower(double aDt); // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8