X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FInstrumentation%2Fnavradio.cxx;h=b4455be10013b15952ecf0d3823739e4c52e19a8;hb=43b300fe46d8013a90009ec8e1a923ec53a396b5;hp=6a6be3707fadd46bfb0681782ff9ccfd7621fe29;hpb=0f8d8bbf05ece495fe8991ce508943502cbe6156;p=flightgear.git diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 6a6be3707..b4455be10 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -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 Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -25,50 +25,83 @@ # include #endif -#include -#include #include -#include #include -#include +#include #include +#include +#include +#include +#include -#include #include - +#include
#include "navradio.hxx" -#include -SG_USING_STD(string); - +using std::string; // Constructor FGNavRadio::FGNavRadio(SGPropertyNode *node) : lon_node(fgGetNode("/position/longitude-deg", true)), lat_node(fgGetNode("/position/latitude-deg", true)), alt_node(fgGetNode("/position/altitude-ft", true)), + is_valid_node(NULL), + power_btn_node(NULL), + freq_node(NULL), + alt_freq_node(NULL), + sel_radial_node(NULL), + vol_btn_node(NULL), + ident_btn_node(NULL), + audio_btn_node(NULL), + backcourse_node(NULL), + nav_serviceable_node(NULL), + cdi_serviceable_node(NULL), + gs_serviceable_node(NULL), + tofrom_serviceable_node(NULL), + fmt_freq_node(NULL), + fmt_alt_freq_node(NULL), + heading_node(NULL), + radial_node(NULL), + recip_radial_node(NULL), + target_radial_true_node(NULL), + target_auto_hdg_node(NULL), + time_to_intercept(NULL), + to_flag_node(NULL), + from_flag_node(NULL), + inrange_node(NULL), + signal_quality_norm_node(NULL), + cdi_deflection_node(NULL), + cdi_xtrack_error_node(NULL), + cdi_xtrack_hdg_err_node(NULL), + has_gs_node(NULL), + loc_node(NULL), + loc_dist_node(NULL), + gs_deflection_node(NULL), + gs_rate_of_climb_node(NULL), + gs_dist_node(NULL), + nav_id_node(NULL), + id_c1_node(NULL), + id_c2_node(NULL), + id_c3_node(NULL), + id_c4_node(NULL), + nav_slaved_to_gps_node(NULL), + gps_cdi_deflection_node(NULL), + gps_to_flag_node(NULL), + gps_from_flag_node(NULL), last_nav_id(""), last_nav_vor(false), - nav_play_count(0), - nav_last_time(0), - need_update(true), - power_btn(true), - audio_btn(true), - nav_freq(0.0), - nav_alt_freq(0.0), - nav_heading(0.0), - nav_radial(0.0), - nav_target_radial(0.0), - nav_target_radial_true(0.0), - nav_target_auto_hdg(0.0), - nav_gs_rate_of_climb(0.0), - nav_vol_btn(0.0), - nav_ident_btn(true), + play_count(0), + last_time(0), + radial(0.0), + target_radial(0.0), horiz_vel(0.0), last_x(0.0), - name("nav"), - num(0) + last_loc_dist(0.0), + last_xtrack_error(0.0), + _name(node->getStringValue("name", "nav")), + _num(node->getIntValue("number", 0)), + _time_before_search_sec(-1.0) { SGPath path( globals->get_fg_root() ); SGPath term = path; @@ -81,25 +114,6 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) : term_tbl = new SGInterpTable( term.str() ); low_tbl = new SGInterpTable( low.str() ); high_tbl = new SGInterpTable( high.str() ); - - int i; - for ( i = 0; i < node->nChildren(); ++i ) { - SGPropertyNode *child = node->getChild(i); - string cname = child->getName(); - string cval = child->getStringValue(); - if ( cname == "name" ) { - name = cval; - } else if ( cname == "number" ) { - num = child->getIntValue(); - } else { - SG_LOG( SG_INSTR, SG_WARN, - "Error in nav radio config logic" ); - if ( name.length() ) { - SG_LOG( SG_INSTR, SG_WARN, "Section = " << name ); - } - } - } - } @@ -118,24 +132,80 @@ FGNavRadio::init () morse.init(); string branch; - branch = "/instrumentation/" + name; - - SGPropertyNode *node = fgGetNode(branch.c_str(), num, true ); - - bus_power = - fgGetNode(("/systems/electrical/outputs/" + name).c_str(), true); - nav_serviceable = node->getChild("serviceable", 0, true); - cdi_serviceable = (node->getChild("cdi", 0, true)) + branch = "/instrumentation/" + _name; + + SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true ); + + bus_power_node = + fgGetNode(("/systems/electrical/outputs/" + _name).c_str(), true); + + // inputs + is_valid_node = node->getChild("data-is-valid", 0, true); + power_btn_node = node->getChild("power-btn", 0, true); + power_btn_node->setBoolValue( true ); + vol_btn_node = node->getChild("volume", 0, true); + ident_btn_node = node->getChild("ident", 0, true); + ident_btn_node->setBoolValue( true ); + audio_btn_node = node->getChild("audio-btn", 0, true); + 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->getChild("gs", 0, true)) + gs_serviceable_node = (node->getChild("gs", 0, true)) ->getChild("serviceable"); - tofrom_serviceable = (node->getChild("to-from", 0, true)) + tofrom_serviceable_node = (node->getChild("to-from", 0, true)) ->getChild("serviceable", 0, true); + // frequencies + SGPropertyNode *subnode = node->getChild("frequencies", 0, true); + freq_node = subnode->getChild("selected-mhz", 0, true); + alt_freq_node = subnode->getChild("standby-mhz", 0, true); + fmt_freq_node = subnode->getChild("selected-mhz-fmt", 0, true); + fmt_alt_freq_node = subnode->getChild("standby-mhz-fmt", 0, true); + + // radials + subnode = node->getChild("radials", 0, true); + sel_radial_node = subnode->getChild("selected-deg", 0, true); + radial_node = subnode->getChild("actual-deg", 0, true); + recip_radial_node = subnode->getChild("reciprocal-radial-deg", 0, true); + target_radial_true_node = subnode->getChild("target-radial-deg", 0, true); + target_auto_hdg_node = subnode->getChild("target-auto-hdg-deg", 0, true); + + // outputs + heading_node = node->getChild("heading-deg", 0, true); + time_to_intercept = node->getChild("time-to-intercept-sec", 0, true); + to_flag_node = node->getChild("to-flag", 0, true); + from_flag_node = node->getChild("from-flag", 0, true); + inrange_node = node->getChild("in-range", 0, true); + signal_quality_norm_node = node->getChild("signal-quality-norm", 0, true); + cdi_deflection_node = node->getChild("heading-needle-deflection", 0, true); + cdi_xtrack_error_node = node->getChild("crosstrack-error-m", 0, true); + cdi_xtrack_hdg_err_node + = node->getChild("crosstrack-heading-error-deg", 0, true); + has_gs_node = node->getChild("has-gs", 0, true); + loc_node = node->getChild("nav-loc", 0, true); + loc_dist_node = node->getChild("nav-distance", 0, true); + gs_deflection_node = node->getChild("gs-needle-deflection", 0, true); + gs_rate_of_climb_node = node->getChild("gs-rate-of-climb", 0, true); + gs_dist_node = node->getChild("gs-distance", 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); + id_c3_node = node->getChild("nav-id_asc3", 0, true); + id_c4_node = node->getChild("nav-id_asc4", 0, true); + + // 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); + gps_to_flag_node = fgGetNode("/instrumentation/gps/to-flag", true); + gps_from_flag_node = fgGetNode("/instrumentation/gps/from-flag", true); + std::ostringstream temp; - temp << name << "nav-ident" << num; + temp << _name << "nav-ident" << _num; nav_fx_name = temp.str(); - temp << name << "dme-ident" << num; + temp << _name << "dme-ident" << _num; dme_fx_name = temp.str(); } @@ -144,107 +214,8 @@ FGNavRadio::bind () { std::ostringstream temp; string branch; - temp << num; - branch = "/instrumentation/" + name + "[" + temp.str() + "]"; - - // User inputs - fgTie( (branch + "power-btn").c_str(), this, - &FGNavRadio::get_power_btn, &FGNavRadio::set_power_btn ); - fgSetArchivable( (branch + "power-btn").c_str() ); - - fgTie( (branch + "/frequencies/selected-mhz").c_str() , this, - &FGNavRadio::get_nav_freq, &FGNavRadio::set_nav_freq ); - fgSetArchivable( (branch + "/frequencies/selected-mhz").c_str() ); - - fgTie( (branch + "/frequencies/standby-mhz").c_str() , this, - &FGNavRadio::get_nav_alt_freq, &FGNavRadio::set_nav_alt_freq); - fgSetArchivable( (branch + "/frequencies/standby-mhz").c_str() ); - - fgTie( (branch + "/radials/selected-deg").c_str() , this, - &FGNavRadio::get_nav_sel_radial, &FGNavRadio::set_nav_sel_radial ); - fgSetArchivable((branch + "/radials/selected-deg").c_str() ); - - fgTie( (branch + "/volume").c_str() , this, - &FGNavRadio::get_nav_vol_btn, &FGNavRadio::set_nav_vol_btn ); - fgSetArchivable( (branch + "/volume").c_str() ); - - fgTie( (branch + "/ident").c_str(), this, - &FGNavRadio::get_nav_ident_btn, &FGNavRadio::set_nav_ident_btn ); - fgSetArchivable( (branch + "/ident").c_str() ); - - // Radio outputs - fgTie( (branch + "/audio-btn").c_str(), this, - &FGNavRadio::get_audio_btn, &FGNavRadio::set_audio_btn ); - fgSetArchivable( (branch + "/audio-btn").c_str() ); - - fgTie( (branch + "/heading-deg").c_str(), - this, &FGNavRadio::get_nav_heading ); - - fgTie( (branch + "/radials/actual-deg").c_str(), - this, &FGNavRadio::get_nav_radial ); - - fgTie( (branch + "/radials/target-radial-deg").c_str(), - this, &FGNavRadio::get_nav_target_radial_true ); - - fgTie( (branch + "/radials/reciprocal-radial-deg").c_str(), - this, &FGNavRadio::get_nav_reciprocal_radial ); - - fgTie( (branch + "/radials/target-radial2-deg").c_str(), - this, &FGNavRadio::get_nav_target_radial ); - - fgTie( (branch + "/radials/target-auto-hdg-deg").c_str(), - this, &FGNavRadio::get_nav_target_auto_hdg ); - - fgTie( (branch + "/to-flag").c_str(), - this, &FGNavRadio::get_nav_to_flag ); - - fgTie( (branch + "/from-flag").c_str(), - this, &FGNavRadio::get_nav_from_flag ); - - fgTie( (branch + "/in-range").c_str(), - this, &FGNavRadio::get_nav_inrange ); - - fgTie( (branch + "/heading-needle-deflection").c_str(), - this, &FGNavRadio::get_nav_cdi_deflection ); - - fgTie( (branch + "/crosstrack-error-m").c_str(), - this, &FGNavRadio::get_nav_cdi_xtrack_error ); - - fgTie( (branch + "/has-gs").c_str(), - this, &FGNavRadio::get_nav_has_gs ); - - fgTie( (branch + "/nav-loc").c_str(), - this, &FGNavRadio::get_nav_loc ); - - fgTie( (branch + "/gs-rate-of-climb").c_str(), - this, &FGNavRadio::get_nav_gs_rate_of_climb ); - - fgTie( (branch + "/gs-needle-deflection").c_str(), - this, &FGNavRadio::get_nav_gs_deflection ); - - fgTie( (branch + "/gs-distance").c_str(), - this, &FGNavRadio::get_nav_gs_dist_signed ); - - fgTie( (branch + "/nav-distance").c_str(), - this, &FGNavRadio::get_nav_loc_dist ); - - fgTie( (branch + "/nav-id").c_str(), - this, &FGNavRadio::get_nav_id ); - - // put nav_id characters into seperate properties for instrument displays - fgTie( (branch + "/nav-id_asc1").c_str(), - this, &FGNavRadio::get_nav_id_c1 ); - - fgTie( (branch + "/nav-id_asc2").c_str(), - this, &FGNavRadio::get_nav_id_c2 ); - - fgTie( (branch + "/nav-id_asc3").c_str(), - this, &FGNavRadio::get_nav_id_c3 ); - - fgTie( (branch + "/nav-id_asc4").c_str(), - this, &FGNavRadio::get_nav_id_c4 ); - - // end of binding + temp << _num; + branch = "/instrumentation/" + _name + "[" + temp.str() + "]"; } @@ -253,19 +224,8 @@ FGNavRadio::unbind () { std::ostringstream temp; string branch; - temp << num; - branch = "/instrumentation/" + name + "[" + temp.str() + "]"; - - fgUntie( (branch + "/frequencies/selected-mhz").c_str() ); - fgUntie( (branch + "/frequencies/standby-mhz").c_str() ); - fgUntie( (branch + "/radials/actual-deg").c_str() ); - fgUntie( (branch + "/radials/selected-deg").c_str() ); - fgUntie( (branch + "/ident").c_str() ); - fgUntie( (branch + "/to-flag").c_str() ); - fgUntie( (branch + "/from-flag").c_str() ); - fgUntie( (branch + "/in-range").c_str() ); - fgUntie( (branch + "/heading-needle-deflection").c_str() ); - fgUntie( (branch + "/gs-needle-deflection").c_str() ); + temp << _num; + branch = "/instrumentation/" + _name + "[" + temp.str() + "]"; } @@ -332,182 +292,340 @@ double FGNavRadio::adjustILSRange( double stationElev, double aircraftElev, } +////////////////////////////////////////////////////////////////////////// // Update the various nav values based on position and valid tuned in navs +////////////////////////////////////////////////////////////////////////// void FGNavRadio::update(double dt) { - double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS; - double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS; - double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER; - - need_update = false; - - Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) ); - Point3D station; - double az1, az2, s; - - // On timeout, scan again + // Do a nav station search only once a second to reduce + // unnecessary work. (Also, make sure to do this before caching + // any values!) _time_before_search_sec -= dt; if ( _time_before_search_sec < 0 ) { search(); } - //////////////////////////////////////////////////////////////////////// - // Nav. - //////////////////////////////////////////////////////////////////////// + // cache a few strategic values locally for speed + SGGeod pos = SGGeod::fromDegFt(lon_node->getDoubleValue(), + lat_node->getDoubleValue(), + alt_node->getDoubleValue()); + bool power_btn = power_btn_node->getBoolValue(); + bool nav_serviceable = nav_serviceable_node->getBoolValue(); + bool cdi_serviceable = cdi_serviceable_node->getBoolValue(); + bool tofrom_serviceable = tofrom_serviceable_node->getBoolValue(); + bool inrange = inrange_node->getBoolValue(); + bool has_gs = has_gs_node->getBoolValue(); + bool is_loc = loc_node->getBoolValue(); + double loc_dist = loc_dist_node->getDoubleValue(); + double effective_range_m; + double signal_quality_norm = signal_quality_norm_node->getDoubleValue(); + + double az1, az2, s; + + // Create "formatted" versions of the nav frequencies for + // instrument displays. + char tmp[16]; + sprintf( tmp, "%.2f", freq_node->getDoubleValue() ); + fmt_freq_node->setStringValue(tmp); + sprintf( tmp, "%.2f", alt_freq_node->getDoubleValue() ); + fmt_alt_freq_node->setStringValue(tmp); - // cout << "nav_valid = " << nav_valid + // cout << "is_valid = " << is_valid // << " power_btn = " << power_btn - // << " bus_power = " << bus_power->getDoubleValue() - // << " nav_serviceable = " << nav_serviceable->getBoolValue() + // << " bus_power = " << bus_power_node->getDoubleValue() + // << " nav_serviceable = " << nav_serviceable // << endl; - if ( nav_valid && power_btn && (bus_power->getDoubleValue() > 1.0) - && nav_serviceable->getBoolValue() ) + if ( is_valid && power_btn && (bus_power_node->getDoubleValue() > 1.0) + && nav_serviceable ) { - station = Point3D( nav_x, nav_y, nav_z ); - nav_loc_dist = aircraft.distance3D( station ); + SGVec3d aircraft = SGVec3d::fromGeod(pos); + loc_dist = dist(aircraft, nav_xyz); + loc_dist_node->setDoubleValue( loc_dist ); + // cout << "dt = " << dt << " dist = " << loc_dist << endl; - if ( nav_has_gs ) { + if ( has_gs ) { // find closest distance to the gs base line - sgdVec3 p; - sgdSetVec3( p, aircraft.x(), aircraft.y(), aircraft.z() ); - sgdVec3 p0; - sgdSetVec3( p0, nav_gs_x, nav_gs_y, nav_gs_z ); - double dist = sgdClosestPointToLineDistSquared( p, p0, - gs_base_vec ); - nav_gs_dist = sqrt( dist ); - // cout << "nav_gs_dist = " << nav_gs_dist << endl; - - Point3D tmp( nav_gs_x, nav_gs_y, nav_gs_z ); - // cout << " (" << aircraft.distance3D( tmp ) << ")" << endl; + SGVec3d p = aircraft; + double dist = sgdClosestPointToLineDistSquared(p.sg(), gs_xyz.sg(), + gs_base_vec.sg()); + gs_dist_node->setDoubleValue( sqrt( dist ) ); + // cout << "gs_dist = " << gs_dist_node->getDoubleValue() + // << endl; // wgs84 heading to glide slope (to determine sign of distance) - geo_inverse_wgs_84( elev, - lat * SGD_RADIANS_TO_DEGREES, - lon * SGD_RADIANS_TO_DEGREES, - nav_gslat, nav_gslon, + geo_inverse_wgs_84( pos, SGGeod::fromDeg(gs_lon, gs_lat), &az1, &az2, &s ); - double r = az1 - nav_target_radial; + double r = az1 - target_radial; while ( r > 180.0 ) { r -= 360.0;} while ( r < -180.0 ) { r += 360.0;} if ( r >= -90.0 && r <= 90.0 ) { - nav_gs_dist_signed = nav_gs_dist; + gs_dist_signed = gs_dist_node->getDoubleValue(); } else { - nav_gs_dist_signed = -nav_gs_dist; + gs_dist_signed = -gs_dist_node->getDoubleValue(); } - /* cout << "Target Radial = " << nav_target_radial + /* cout << "Target Radial = " << target_radial << " Bearing = " << az1 - << " dist (signed) = " << nav_gs_dist_signed + << " dist (signed) = " << gs_dist_signed << endl; */ } else { - nav_gs_dist = 0.0; + gs_dist_node->setDoubleValue( 0.0 ); } - // wgs84 heading to localizer - geo_inverse_wgs_84( elev, - lat * SGD_RADIANS_TO_DEGREES, - lon * SGD_RADIANS_TO_DEGREES, - nav_loclat, nav_loclon, - &nav_heading, &az2, &s ); + ////////////////////////////////////////////////////////// + // compute forward and reverse wgs84 headings to localizer + ////////////////////////////////////////////////////////// + double hdg; + geo_inverse_wgs_84( pos, SGGeod::fromDeg(loc_lon, loc_lat), + &hdg, &az2, &s ); // cout << "az1 = " << az1 << " magvar = " << nav_magvar << endl; - nav_radial = az2 - nav_twist; - // cout << " heading = " << nav_heading + heading_node->setDoubleValue( hdg ); + radial = az2 - twist; + double recip = radial + 180.0; + if ( recip >= 360.0 ) { recip -= 360.0; } + radial_node->setDoubleValue( radial ); + recip_radial_node->setDoubleValue( recip ); + // cout << " heading = " << heading_node->getDoubleValue() // << " dist = " << nav_dist << endl; - if ( nav_loc ) { - double offset = nav_radial - nav_target_radial; + ////////////////////////////////////////////////////////// + // compute the target/selected radial in "true" heading + ////////////////////////////////////////////////////////// + double trtrue = 0.0; + if ( is_loc ) { + // ILS localizers radials are already "true" in our + // database + trtrue = target_radial; + } else { + // VOR radials need to have that vor's offset added in + trtrue = target_radial + twist; + } + + while ( trtrue < 0.0 ) { trtrue += 360.0; } + while ( trtrue > 360.0 ) { trtrue -= 360.0; } + target_radial_true_node->setDoubleValue( trtrue ); + + ////////////////////////////////////////////////////////// + // adjust reception range for altitude + // FIXME: make sure we are using the navdata range now that + // it is valid in the data file + ////////////////////////////////////////////////////////// + if ( is_loc ) { + double offset = radial - target_radial; while ( offset < -180.0 ) { offset += 360.0; } while ( offset > 180.0 ) { offset -= 360.0; } // cout << "ils offset = " << offset << endl; - nav_effective_range - = adjustILSRange( nav_elev, elev, offset, - nav_loc_dist * SG_METER_TO_NM ); - } else { - nav_effective_range = adjustNavRange( nav_elev, elev, nav_range ); - } - // cout << "nav range = " << nav_effective_range - // << " (" << nav_range << ")" << endl; - - if ( nav_loc_dist < nav_effective_range * SG_NM_TO_METER ) { - nav_inrange = true; - } else if ( nav_loc_dist < 2 * nav_effective_range * SG_NM_TO_METER ) { - nav_inrange = sg_random() < - ( 2 * nav_effective_range * SG_NM_TO_METER - nav_loc_dist ) / - (nav_effective_range * SG_NM_TO_METER); + effective_range + = adjustILSRange( nav_elev, pos.getElevationM(), offset, + loc_dist * SG_METER_TO_NM ); } else { - nav_inrange = false; + effective_range + = adjustNavRange( nav_elev, pos.getElevationM(), range ); } - if ( !nav_loc ) { - nav_target_radial = nav_sel_radial; - } + effective_range_m = effective_range * SG_NM_TO_METER; - // Calculate some values for the nav/ils hold autopilot + // cout << "nav range = " << effective_range + // << " (" << range << ")" << endl; - double cur_radial = get_nav_reciprocal_radial(); - if ( nav_loc ) { - // ILS localizers radials are already "true" in our - // database - } else { - cur_radial += nav_twist; - } - if ( get_nav_from_flag() ) { - cur_radial += 180.0; - while ( cur_radial >= 360.0 ) { cur_radial -= 360.0; } + ////////////////////////////////////////////////////////// + // compute signal quality + // 100% within effective_range + // decreases 1/x^2 further out + ////////////////////////////////////////////////////////// + { + double last_signal_quality_norm = signal_quality_norm; + + if ( loc_dist < effective_range_m ) { + signal_quality_norm = 1.0; + } else { + double range_exceed_norm = loc_dist/effective_range_m; + signal_quality_norm = 1/(range_exceed_norm*range_exceed_norm); + } + + signal_quality_norm = fgGetLowPass( last_signal_quality_norm, + signal_quality_norm, dt ); } - - // AUTOPILOT HELPERS + signal_quality_norm_node->setDoubleValue( signal_quality_norm ); + inrange = signal_quality_norm > 0.2; + inrange_node->setBoolValue( inrange ); - // determine the target radial in "true" heading - nav_target_radial_true = nav_target_radial; - if ( nav_loc ) { - // ILS localizers radials are already "true" in our - // database + if ( !is_loc ) { + target_radial = sel_radial_node->getDoubleValue(); + } + + ////////////////////////////////////////////////////////// + // compute to/from flag status + ////////////////////////////////////////////////////////// + bool value = false; + double offset = fabs(radial - target_radial); + if ( tofrom_serviceable ) { + if ( nav_slaved_to_gps_node->getBoolValue() ) { + value = gps_to_flag_node->getBoolValue(); + } else if ( inrange ) { + if ( is_loc ) { + value = true; + } else { + value = !(offset <= 90.0 || offset >= 270.0); + } + } } else { - // VOR radials need to have that vor's offset added in - nav_target_radial_true += nav_twist; + value = false; } - - while ( nav_target_radial_true < 0.0 ) { - nav_target_radial_true += 360.0; + to_flag_node->setBoolValue( value ); + + value = false; + if ( tofrom_serviceable ) { + if ( nav_slaved_to_gps_node->getBoolValue() ) { + value = gps_from_flag_node->getBoolValue(); + } else if ( inrange ) { + if ( is_loc ) { + value = false; + } else { + value = !(offset > 90.0 && offset < 270.0); + } + } + } else { + value = false; } - while ( nav_target_radial_true > 360.0 ) { - nav_target_radial_true -= 360.0; + from_flag_node->setBoolValue( value ); + + ////////////////////////////////////////////////////////// + // compute the deflection of the CDI needle, clamped to the range + // of ( -10 , 10 ) + ////////////////////////////////////////////////////////// + double r = 0.0; + bool loc_backside = false; // an in-code flag indicating that we are + // on a localizer backcourse. + if ( cdi_serviceable ) { + if ( nav_slaved_to_gps_node->getBoolValue() ) { + r = gps_cdi_deflection_node->getDoubleValue(); + // We want +- 5 dots deflection for the gps, so clamp + // to -12.5/12.5 + SG_CLAMP_RANGE( r, -12.5, 12.5 ); + } else if ( inrange ) { + r = radial - target_radial; + // cout << "Target radial = " << target_radial + // << " Actual radial = " << radial << endl; + + while ( r > 180.0 ) { r -= 360.0;} + while ( r < -180.0 ) { r += 360.0;} + if ( fabs(r) > 90.0 ) { + r = ( r<0.0 ? -r-180.0 : -r+180.0 ); + } else { + if ( is_loc ) { + loc_backside = true; + } + } + + r = -r; // reverse, since radial is outbound + if ( is_loc ) { + // According to Robin Peel, the ILS is 4x more + // sensitive than a vor + r *= 4.0; + } + SG_CLAMP_RANGE( r, -10.0, 10.0 ); + r *= signal_quality_norm; + } } + cdi_deflection_node->setDoubleValue( r ); + + ////////////////////////////////////////////////////////// + // compute the amount of cross track distance error in meters + ////////////////////////////////////////////////////////// + double xtrack_error = 0.0; + if ( inrange && nav_serviceable && cdi_serviceable ) { + r = radial - target_radial; + // cout << "Target radial = " << target_radial + // << " Actual radial = " << radial + // << " r = " << r << endl; + + while ( r > 180.0 ) { r -= 360.0;} + while ( r < -180.0 ) { r += 360.0;} + if ( fabs(r) > 90.0 ) { + r = ( r<0.0 ? -r-180.0 : -r+180.0 ); + } - // determine the heading adjustment needed. - // over 8km scale by 3.0 - // (3 is chosen because max deflection is 10 - // and 30 is clamped angle to radial) - // under 8km scale by 10.0 - // because the overstated error helps drive it to the radial in a - // moderate cross wind. - double adjustment = 0.0; - if (nav_loc_dist > 8000) { - adjustment = get_nav_cdi_deflection() * 3.0; + r = -r; // reverse, since radial is outbound + + xtrack_error = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS); } else { - adjustment = get_nav_cdi_deflection() * 10.0; + xtrack_error = 0.0; } - SG_CLAMP_RANGE( adjustment, -30.0, 30.0 ); - - // determine the target heading to fly to intercept the - // tgt_radial - nav_target_auto_hdg = nav_target_radial_true + adjustment; - while ( nav_target_auto_hdg < 0.0 ) { nav_target_auto_hdg += 360.0; } - while ( nav_target_auto_hdg > 360.0 ) { nav_target_auto_hdg -= 360.0; } - - // cross track error - // ???? + cdi_xtrack_error_node->setDoubleValue( xtrack_error ); + + ////////////////////////////////////////////////////////// + // compute an approximate ground track heading error + ////////////////////////////////////////////////////////// + double hdg_error = 0.0; + if ( inrange && cdi_serviceable ) { + double vn = fgGetDouble( "/velocities/speed-north-fps" ); + double ve = fgGetDouble( "/velocities/speed-east-fps" ); + double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES; + if ( gnd_trk_true < 0.0 ) { gnd_trk_true += 360.0; } + + SGPropertyNode *true_hdg + = fgGetNode("/orientation/heading-deg", true); + hdg_error = gnd_trk_true - true_hdg->getDoubleValue(); + + // cout << "ground track = " << gnd_trk_true + // << " orientation = " << true_hdg->getDoubleValue() << endl; + } + cdi_xtrack_hdg_err_node->setDoubleValue( hdg_error ); + + ////////////////////////////////////////////////////////// + // compute the time to intercept selected radial (based on + // current and last cross track errors and dt + ////////////////////////////////////////////////////////// + double t = 0.0; + if ( inrange && cdi_serviceable ) { + double xrate_ms = (last_xtrack_error - xtrack_error) / dt; + if ( fabs(xrate_ms) > 0.00001 ) { + t = xtrack_error / xrate_ms; + } else { + t = 9999.9; + } + } + time_to_intercept->setDoubleValue( t ); + + ////////////////////////////////////////////////////////// + // compute the amount of glide slope needle deflection + // (.i.e. the number of degrees we are off the glide slope * 5.0 + // + // CLO - 13 Mar 2006: The glide slope needle should peg at + // +/-0.7 degrees off the ideal glideslope. I'm not sure why + // we compute the factor the way we do (5*gs_error), but we + // need to compensate for our 'odd' number in the glideslope + // needle animation. This means that the needle should peg + // when this values is +/-3.5. + ////////////////////////////////////////////////////////// + r = 0.0; + if ( has_gs && gs_serviceable_node->getBoolValue() ) { + if ( nav_slaved_to_gps_node->getBoolValue() ) { + // FIXME/FINISHME, what should be set here? + } else if ( inrange ) { + double x = gs_dist_node->getDoubleValue(); + double y = (fgGetDouble("/position/altitude-ft") - nav_elev) + * SG_FEET_TO_METER; + // cout << "dist = " << x << " height = " << y << endl; + double angle = asin( y / x ) * SGD_RADIANS_TO_DEGREES; + r = (target_gs - angle) * 5.0; + r *= signal_quality_norm; + } + } + gs_deflection_node->setDoubleValue( r ); + ////////////////////////////////////////////////////////// // Calculate desired rate of climb for intercepting the GS - double x = nav_gs_dist; + ////////////////////////////////////////////////////////// + double x = gs_dist_node->getDoubleValue(); double y = (alt_node->getDoubleValue() - nav_elev) * SG_FEET_TO_METER; double current_angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES; - double target_angle = nav_target_gs; + double target_angle = target_gs; double gs_diff = target_angle - current_angle; // convert desired vertical path angle into a climb rate @@ -526,59 +644,107 @@ FGNavRadio::update(double dt) // double horiz_vel = airspeed_node->getFloatValue() // * SG_FEET_TO_METER * 60.0; - nav_gs_rate_of_climb = -sin( des_angle * SGD_DEGREES_TO_RADIANS ) - * horiz_vel * SG_METER_TO_FEET; + gs_rate_of_climb_node + ->setDoubleValue( -sin( des_angle * SGD_DEGREES_TO_RADIANS ) + * horiz_vel * SG_METER_TO_FEET ); } - } else { - nav_inrange = false; + + ////////////////////////////////////////////////////////// + // Calculate a suggested target heading to smoothly intercept + // a nav/ils radial. + ////////////////////////////////////////////////////////// + + // Now that we have cross track heading adjustment built in, + // we shouldn't need to overdrive the heading angle within 8km + // of the station. + // + // The cdi deflection should be +/-10 for a full range of deflection + // so multiplying this by 3 gives us +/- 30 degrees heading + // compensation. + double adjustment = cdi_deflection_node->getDoubleValue() * 3.0; + SG_CLAMP_RANGE( adjustment, -30.0, 30.0 ); + + // determine the target heading to fly to intercept the + // tgt_radial = target radial (true) + cdi offset adjustmest - + // xtrack heading error adjustment + double nta_hdg; + if ( is_loc && backcourse_node->getBoolValue() ) { + // tuned to a localizer and backcourse mode activated + trtrue += 180.0; // reverse the target localizer heading + while ( trtrue > 360.0 ) { trtrue -= 360.0; } + nta_hdg = trtrue - adjustment - hdg_error; + } else { + nta_hdg = trtrue + adjustment - hdg_error; + } + + while ( nta_hdg < 0.0 ) { nta_hdg += 360.0; } + while ( nta_hdg >= 360.0 ) { nta_hdg -= 360.0; } + target_auto_hdg_node->setDoubleValue( nta_hdg ); + + last_xtrack_error = xtrack_error; + } else { + inrange_node->setBoolValue( false ); + cdi_deflection_node->setDoubleValue( 0.0 ); + cdi_xtrack_error_node->setDoubleValue( 0.0 ); + cdi_xtrack_hdg_err_node->setDoubleValue( 0.0 ); + time_to_intercept->setDoubleValue( 0.0 ); + gs_deflection_node->setDoubleValue( 0.0 ); + to_flag_node->setBoolValue( false ); + from_flag_node->setBoolValue( false ); // cout << "not picking up vor. :-(" << endl; } - if ( nav_valid && nav_inrange && nav_serviceable->getBoolValue() ) { + // audio effects + if ( is_valid && inrange && nav_serviceable ) { // play station ident via audio system if on + ident, // otherwise turn it off - if ( power_btn && (bus_power->getDoubleValue() > 1.0) - && nav_ident_btn && audio_btn ) + if ( power_btn + && (bus_power_node->getDoubleValue() > 1.0) + && ident_btn_node->getBoolValue() + && audio_btn_node->getBoolValue() ) { SGSoundSample *sound; sound = globals->get_soundmgr()->find( nav_fx_name ); + double vol = vol_btn_node->getDoubleValue(); + if ( vol < 0.0 ) { vol = 0.0; } + if ( vol > 1.0 ) { vol = 1.0; } if ( sound != NULL ) { - sound->set_volume( nav_vol_btn ); + sound->set_volume( vol ); } else { SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-vor-ident sound" ); } sound = globals->get_soundmgr()->find( dme_fx_name ); if ( sound != NULL ) { - sound->set_volume( nav_vol_btn ); + sound->set_volume( vol ); } else { SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-dme-ident sound" ); } - // cout << "nav_last_time = " << nav_last_time << " "; + // cout << "last_time = " << last_time << " "; // cout << "cur_time = " // << globals->get_time_params()->get_cur_time(); - if ( nav_last_time < + if ( last_time < globals->get_time_params()->get_cur_time() - 30 ) { - nav_last_time = globals->get_time_params()->get_cur_time(); - nav_play_count = 0; + last_time = globals->get_time_params()->get_cur_time(); + play_count = 0; } - // cout << " nav_play_count = " << nav_play_count << endl; + // cout << " play_count = " << play_count << endl; // cout << "playing = " // << globals->get_soundmgr()->is_playing(nav_fx_name) // << endl; - if ( nav_play_count < 4 ) { + if ( play_count < 4 ) { // play VOR ident if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) { globals->get_soundmgr()->play_once( nav_fx_name ); - ++nav_play_count; + ++play_count; } - } else if ( nav_play_count < 5 && nav_has_dme ) { + } 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 ); - ++nav_play_count; + ++play_count; } } } else { @@ -586,6 +752,8 @@ FGNavRadio::update(double dt) globals->get_soundmgr()->stop( dme_fx_name ); } } + + last_loc_dist = loc_dist; } @@ -596,10 +764,9 @@ void FGNavRadio::search() // reset search time _time_before_search_sec = 1.0; - double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS; - double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS; - double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER; - + // cache values locally for speed + SGGeod pos = SGGeod::fromDegFt(lon_node->getDoubleValue(), + lat_node->getDoubleValue(), alt_node->getDoubleValue()); FGNavRecord *nav = NULL; FGNavRecord *loc = NULL; FGNavRecord *dme = NULL; @@ -609,93 +776,90 @@ void FGNavRadio::search() // Nav. //////////////////////////////////////////////////////////////////////// - nav = globals->get_navlist()->findByFreq(nav_freq, lon, lat, elev); - dme = globals->get_dmelist()->findByFreq(nav_freq, lon, lat, elev); + double freq = freq_node->getDoubleValue(); + nav = globals->get_navlist()->findByFreq(freq, pos); + dme = globals->get_dmelist()->findByFreq(freq, pos); if ( nav == NULL ) { - loc = globals->get_loclist()->findByFreq(nav_freq, lon, lat, elev); - gs = globals->get_gslist()->findByFreq(nav_freq, lon, lat, elev); + loc = globals->get_loclist()->findByFreq(freq, pos); + gs = globals->get_gslist()->findByFreq(freq, pos); } - + + string nav_id = ""; + if ( loc != NULL ) { - nav_id = loc->get_ident(); - // cout << "localizer = " << nav_id << endl; - nav_valid = true; + nav_id = loc->get_ident(); + nav_id_node->setStringValue( nav_id.c_str() ); + // cout << "localizer = " << nav_id_node->getStringValue() << endl; + is_valid = true; if ( last_nav_id != nav_id || last_nav_vor ) { - nav_trans_ident = loc->get_trans_ident(); - nav_target_radial = loc->get_multiuse(); - while ( nav_target_radial < 0.0 ) { nav_target_radial += 360.0; } - while ( nav_target_radial > 360.0 ) { nav_target_radial -= 360.0; } - nav_loclon = loc->get_lon(); - nav_loclat = loc->get_lat(); - nav_x = loc->get_x(); - nav_y = loc->get_y(); - nav_z = loc->get_z(); + trans_ident = loc->get_trans_ident(); + target_radial = loc->get_multiuse(); + while ( target_radial < 0.0 ) { target_radial += 360.0; } + while ( target_radial > 360.0 ) { target_radial -= 360.0; } + loc_lon = loc->get_lon(); + loc_lat = loc->get_lat(); + nav_xyz = loc->cart(); last_nav_id = nav_id; last_nav_vor = false; - nav_loc = true; - nav_has_dme = (dme != NULL); - nav_has_gs = (gs != NULL); - if ( nav_has_gs ) { - nav_gslon = gs->get_lon(); - nav_gslat = gs->get_lat(); + loc_node->setBoolValue( true ); + has_dme = (dme != NULL); + if ( gs != NULL ) { + has_gs_node->setBoolValue( true ); + gs_lon = gs->get_lon(); + gs_lat = gs->get_lat(); nav_elev = gs->get_elev_ft(); int tmp = (int)(gs->get_multiuse() / 1000.0); - nav_target_gs = (double)tmp / 100.0; - nav_gs_x = gs->get_x(); - nav_gs_y = gs->get_y(); - nav_gs_z = gs->get_z(); + target_gs = (double)tmp / 100.0; + gs_xyz = gs->cart(); // derive GS baseline (perpendicular to the runay // along the ground) double tlon, tlat, taz; - geo_direct_wgs_84 ( 0.0, nav_gslat, nav_gslon, - nav_target_radial + 90, + geo_direct_wgs_84 ( 0.0, gs_lat, gs_lon, + target_radial + 90, 100.0, &tlat, &tlon, &taz ); - // cout << "nav_target_radial = " << nav_target_radial << endl; - // cout << "nav_loc = " << nav_loc << endl; - // cout << nav_gslon << "," << nav_gslat << " " + // cout << "target_radial = " << target_radial << endl; + // cout << "nav_loc = " << loc_node->getBoolValue() << endl; + // cout << gs_lon << "," << gs_lat << " " // << tlon << "," << tlat << " (" << nav_elev << ")" // << endl; - Point3D p1 = sgGeodToCart( Point3D(tlon*SGD_DEGREES_TO_RADIANS, - tlat*SGD_DEGREES_TO_RADIANS, - nav_elev*SG_FEET_TO_METER) - ); - // cout << nav_gs_x << "," << nav_gs_y << "," << nav_gs_z - // << endl; + SGGeod tpos = SGGeod::fromDegFt(tlon, tlat, nav_elev); + SGVec3d p1 = SGVec3d::fromGeod(tpos); + + // cout << gs_xyz << endl; // cout << p1 << endl; - sgdSetVec3( gs_base_vec, - p1.x()-nav_gs_x, p1.y()-nav_gs_y, p1.z()-nav_gs_z ); - // cout << gs_base_vec[0] << "," << gs_base_vec[1] << "," - // << gs_base_vec[2] << endl; + gs_base_vec = p1 - gs_xyz; + // cout << gs_base_vec << endl; } else { + has_gs_node->setBoolValue( false ); nav_elev = loc->get_elev_ft(); } - nav_twist = 0; - nav_range = FG_LOC_DEFAULT_RANGE; - nav_effective_range = nav_range; + twist = 0; + range = FG_LOC_DEFAULT_RANGE; + effective_range = range; if ( globals->get_soundmgr()->exists( nav_fx_name ) ) { globals->get_soundmgr()->remove( nav_fx_name ); } SGSoundSample *sound; - sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY ); + sound = morse.make_ident( trans_ident, LO_FREQUENCY ); sound->set_volume( 0.3 ); globals->get_soundmgr()->add( sound, nav_fx_name ); if ( globals->get_soundmgr()->exists( dme_fx_name ) ) { globals->get_soundmgr()->remove( dme_fx_name ); } - sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY ); + sound = morse.make_ident( trans_ident, HI_FREQUENCY ); sound->set_volume( 0.3 ); globals->get_soundmgr()->add( sound, dme_fx_name ); int offset = (int)(sg_random() * 30.0); - nav_play_count = offset / 4; - nav_last_time = globals->get_time_params()->get_cur_time() - + play_count = offset / 4; + last_time = globals->get_time_params()->get_cur_time() - offset; // cout << "offset = " << offset << " play_count = " - // << nav_play_count - // << " nav_last_time = " << nav_last_time + // << play_count + // << " last_time = " << last_time // << " current time = " // << globals->get_time_params()->get_cur_time() << endl; @@ -703,217 +867,77 @@ void FGNavRadio::search() // cout << " id = " << loc->get_locident() << endl; } } else if ( nav != NULL ) { - nav_id = nav->get_ident(); + nav_id = nav->get_ident(); + nav_id_node->setStringValue( nav_id.c_str() ); // cout << "nav = " << nav_id << endl; - nav_valid = true; + is_valid = true; if ( last_nav_id != nav_id || !last_nav_vor ) { last_nav_id = nav_id; last_nav_vor = true; - nav_trans_ident = nav->get_trans_ident(); - nav_loc = false; - nav_has_dme = (dme != NULL); - nav_has_gs = false; - nav_loclon = nav->get_lon(); - nav_loclat = nav->get_lat(); + trans_ident = nav->get_trans_ident(); + loc_node->setBoolValue( false ); + has_dme = (dme != NULL); + has_gs_node->setBoolValue( false ); + loc_lon = nav->get_lon(); + loc_lat = nav->get_lat(); nav_elev = nav->get_elev_ft(); - nav_twist = nav->get_multiuse(); - nav_range = nav->get_range(); - nav_effective_range = adjustNavRange(nav_elev, elev, nav_range); - nav_target_gs = 0.0; - nav_target_radial = nav_sel_radial; - nav_x = nav->get_x(); - nav_y = nav->get_y(); - nav_z = nav->get_z(); + twist = nav->get_multiuse(); + range = nav->get_range(); + effective_range = adjustNavRange(nav_elev, pos.getElevationM(), range); + target_gs = 0.0; + target_radial = sel_radial_node->getDoubleValue(); + nav_xyz = nav->cart(); if ( globals->get_soundmgr()->exists( nav_fx_name ) ) { globals->get_soundmgr()->remove( nav_fx_name ); } - SGSoundSample *sound; - sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY ); - sound->set_volume( 0.3 ); - if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) { - // cout << "Added nav-vor-ident sound" << endl; - } else { - SG_LOG(SG_COCKPIT, SG_WARN, "Failed to add v1-vor-ident sound"); - } - - if ( globals->get_soundmgr()->exists( dme_fx_name ) ) { - globals->get_soundmgr()->remove( dme_fx_name ); - } - sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY ); - sound->set_volume( 0.3 ); - globals->get_soundmgr()->add( sound, dme_fx_name ); - - int offset = (int)(sg_random() * 30.0); - nav_play_count = offset / 4; - nav_last_time = globals->get_time_params()->get_cur_time() - - offset; - // cout << "offset = " << offset << " play_count = " - // << nav_play_count << " nav_last_time = " - // << nav_last_time << " current time = " - // << globals->get_time_params()->get_cur_time() << endl; + try { + SGSoundSample *sound; + sound = morse.make_ident( trans_ident, LO_FREQUENCY ); + sound->set_volume( 0.3 ); + if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) { + // cout << "Added nav-vor-ident sound" << endl; + } else { + SG_LOG(SG_COCKPIT, SG_WARN, "Failed to add v1-vor-ident sound"); + } - // cout << "Found a vor station in range" << endl; - // cout << " id = " << nav->get_ident() << endl; + if ( globals->get_soundmgr()->exists( dme_fx_name ) ) { + globals->get_soundmgr()->remove( dme_fx_name ); + } + sound = morse.make_ident( trans_ident, HI_FREQUENCY ); + sound->set_volume( 0.3 ); + globals->get_soundmgr()->add( sound, dme_fx_name ); + + int offset = (int)(sg_random() * 30.0); + play_count = offset / 4; + last_time = globals->get_time_params()->get_cur_time() - offset; + // cout << "offset = " << offset << " play_count = " + // << play_count << " last_time = " + // << last_time << " current time = " + // << globals->get_time_params()->get_cur_time() << endl; + + // cout << "Found a vor station in range" << endl; + // cout << " id = " << nav->get_ident() << endl; + } catch ( sg_io_exception &e ) { + SG_LOG(SG_GENERAL, SG_ALERT, e.getFormattedMessage()); + } } } else { - nav_valid = false; - nav_id = ""; - nav_target_radial = 0; - nav_trans_ident = ""; + is_valid = false; + nav_id_node->setStringValue( "" ); + target_radial = 0; + trans_ident = ""; last_nav_id = ""; - if ( ! globals->get_soundmgr()->remove( nav_fx_name ) ) { - SG_LOG(SG_COCKPIT, SG_WARN, "Failed to remove nav-vor-ident sound"); - } + globals->get_soundmgr()->remove( nav_fx_name ); globals->get_soundmgr()->remove( dme_fx_name ); - // cout << "not picking up vor1. :-(" << endl; } -} - -// return the amount of heading needle deflection, returns a value -// clamped to the range of ( -10 , 10 ) -double FGNavRadio::get_nav_cdi_deflection() const { - double r; - - if ( nav_inrange - && nav_serviceable->getBoolValue() && cdi_serviceable->getBoolValue() ) - { - r = nav_radial - nav_target_radial; - // cout << "Target radial = " << nav_target_radial - // << " Actual radial = " << nav_radial << endl; - - while ( r > 180.0 ) { r -= 360.0;} - while ( r < -180.0 ) { r += 360.0;} - if ( fabs(r) > 90.0 ) - r = ( r<0.0 ? -r-180.0 : -r+180.0 ); - - // According to Robin Peel, the ILS is 4x more sensitive than a vor - r = -r; // reverse, since radial is outbound - if ( nav_loc ) { r *= 4.0; } - if ( r < -10.0 ) { r = -10.0; } - if ( r > 10.0 ) { r = 10.0; } - } else { - r = 0.0; - } + is_valid_node->setBoolValue( is_valid ); - return r; -} - -// return the amount of cross track distance error, returns a meters -double FGNavRadio::get_nav_cdi_xtrack_error() const { - double r, m; - - if ( nav_inrange - && nav_serviceable->getBoolValue() && cdi_serviceable->getBoolValue() ) - { - r = nav_radial - nav_target_radial; - // cout << "Target radial = " << nav_target_radial - // << " Actual radial = " << nav_radial - // << " r = " << r << endl; - - while ( r > 180.0 ) { r -= 360.0;} - while ( r < -180.0 ) { r += 360.0;} - if ( fabs(r) > 90.0 ) - r = ( r<0.0 ? -r-180.0 : -r+180.0 ); - - r = -r; // reverse, since radial is outbound - - m = nav_loc_dist * sin(r * SGD_DEGREES_TO_RADIANS); - - } else { - m = 0.0; - } - - return m; -} - -// return the amount of glide slope needle deflection (.i.e. the -// number of degrees we are off the glide slope * 5.0 -double FGNavRadio::get_nav_gs_deflection() const { - if ( nav_inrange && nav_has_gs - && nav_serviceable->getBoolValue() && gs_serviceable->getBoolValue() ) - { - double x = nav_gs_dist; - double y = (fgGetDouble("/position/altitude-ft") - nav_elev) - * SG_FEET_TO_METER; - // cout << "dist = " << x << " height = " << y << endl; - double angle = asin( y / x ) * SGD_RADIANS_TO_DEGREES; - return (nav_target_gs - angle) * 5.0; - } else { - return 0.0; - } -} - - -/** - * Return true if the NAV TO flag should be active. - */ -bool -FGNavRadio::get_nav_to_flag () const -{ - if ( nav_inrange - && nav_serviceable->getBoolValue() - && tofrom_serviceable->getBoolValue() ) - { - double offset = fabs(nav_radial - nav_target_radial); - if (nav_loc) { - return true; - } else { - return !(offset <= 90.0 || offset >= 270.0); - } - } else { - return false; - } -} - - -/** - * Return true if the NAV FROM flag should be active. - */ -bool -FGNavRadio::get_nav_from_flag () const -{ - if ( nav_inrange - && nav_serviceable->getBoolValue() - && tofrom_serviceable->getBoolValue() ) { - double offset = fabs(nav_radial - nav_target_radial); - if (nav_loc) { - return false; - } else { - return !(offset > 90.0 && offset < 270.0); - } - } else { - return false; - } -} - - -/** - * Return the true heading to station - */ -double -FGNavRadio::get_nav_heading () const -{ - return nav_heading; -} - - -/** - * Return the current radial. - */ -double -FGNavRadio::get_nav_radial () const -{ - return nav_radial; -} - -double -FGNavRadio::get_nav_reciprocal_radial () const -{ - double recip = nav_radial + 180; - if ( recip >= 360 ) { - recip -= 360; - } - return recip; + char tmpid[5]; + strncpy( tmpid, nav_id.c_str(), 5 ); + id_c1_node->setIntValue( (int)tmpid[0] ); + id_c2_node->setIntValue( (int)tmpid[1] ); + id_c3_node->setIntValue( (int)tmpid[2] ); + id_c4_node->setIntValue( (int)tmpid[3] ); }