]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/navradio.cxx
Make hardcoded error values configurable.
[flightgear.git] / src / Instrumentation / navradio.cxx
index fe586b3e945f59193cbae98c6637a5696554769c..b4455be10013b15952ecf0d3823739e4c52e19a8 100644 (file)
@@ -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$
 
 #  include <config.h>
 #endif
 
-#include <iostream>
-#include <string>
 #include <sstream>
 
-#include <simgear/compiler.h>
 #include <simgear/sg_inlines.h>
-#include <simgear/math/sg_random.h>
+#include <simgear/timing/sg_time.hxx>
 #include <simgear/math/vector.hxx>
+#include <simgear/math/sg_random.h>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/structure/exception.hxx>
 
-#include <Aircraft/aircraft.hxx>
 #include <Navaids/navlist.hxx>
-
+#include <Main/util.hxx>
 #include "navradio.hxx"
 
-#include <string>
-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),
-    _time_before_search_sec(0.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;
@@ -82,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 );
-            }
-        }
-    }
-
 }
 
 
@@ -119,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();
 }
 
@@ -145,104 +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-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() + "]";
 }
 
 
@@ -251,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() + "]";
 }
 
 
@@ -330,184 +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 );
-        // cout << "station = " << station << " dist = " << nav_loc_dist
-        //      << endl;
+        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 );
+           effective_range
+                = adjustILSRange( nav_elev, pos.getElevationM(), offset,
+                                  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);
-       } 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 );
+            }
+
+            r = -r;             // reverse, since radial is outbound
 
-        // 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;
+            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;
-    }
-
-    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;
     }
-}
 
+    is_valid_node->setBoolValue( is_valid );
 
-/**
- * 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] );
 }