]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/dclgps.cxx
NavDisplay: ignore case when matching symbol types in rules.
[flightgear.git] / src / Instrumentation / dclgps.cxx
index eb90804c1e237a11367b4d08d9600b4448e90623..42157d7a1836ffcde8db0f510c4b7c97977b24eb 100644 (file)
@@ -5,7 +5,7 @@
 //
 // Written by David Luff, started 2005.
 //
-// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+// Copyright (C) 2005 - David C Luff:  daveluff --AT-- ntlworld --D0T-- com
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
 #include "dclgps.hxx"
 
 #include <simgear/sg_inlines.h>
-#include <simgear/structure/commands.hxx>
-#include <Main/fg_props.hxx>
-#include <iostream>
-SG_USING_STD(cout);
-
-//using namespace std;
-
-// Command callbacks for FlightGear
-
-static bool do_kln89_msg_pressed(const SGPropertyNode* arg) {
-       //cout << "do_kln89_msg_pressed called!\n";
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->MsgPressed();
-       return(true);
-}
-
-static bool do_kln89_obs_pressed(const SGPropertyNode* arg) {
-       //cout << "do_kln89_obs_pressed called!\n";
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->OBSPressed();
-       return(true);
-}
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/timing/sg_time.hxx>
+#include <simgear/magvar/magvar.hxx>
+#include <simgear/structure/exception.hxx>
 
-static bool do_kln89_alt_pressed(const SGPropertyNode* arg) {
-       //cout << "do_kln89_alt_pressed called!\n";
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->AltPressed();
-       return(true);
-}
-
-static bool do_kln89_nrst_pressed(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->NrstPressed();
-       return(true);
-}
-
-static bool do_kln89_dto_pressed(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->DtoPressed();
-       return(true);
-}
-
-static bool do_kln89_clr_pressed(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->ClrPressed();
-       return(true);
-}
-
-static bool do_kln89_ent_pressed(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->EntPressed();
-       return(true);
-}
-
-static bool do_kln89_crsr_pressed(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->CrsrPressed();
-       return(true);
-}
-
-static bool do_kln89_knob1left1(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->Knob1Left1();
-       return(true);
-}
-
-static bool do_kln89_knob1right1(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->Knob1Right1();
-       return(true);
-}
-
-static bool do_kln89_knob2left1(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->Knob2Left1();
-       return(true);
-}
+#include <Main/fg_props.hxx>
+#include <Navaids/fix.hxx>
+#include <Navaids/navrecord.hxx>
+#include <Airports/simple.hxx>
+#include <Airports/runways.hxx>
 
-static bool do_kln89_knob2right1(const SGPropertyNode* arg) {
-       DCLGPS* gps = (DCLGPS*)globals->get_subsystem("kln89");
-       gps->Knob2Right1();
-       return(true);
-}
+#include <fstream>
+#include <iostream>
 
-// End command callbacks
+using namespace std;
 
 GPSWaypoint::GPSWaypoint() {
     appType = GPS_APP_NONE;
 }
 
+GPSWaypoint::GPSWaypoint(const std::string& aIdent, float aLat, float aLon, GPSWpType aType) :
+  id(aIdent),
+  lat(aLat),
+  lon(aLon),
+  type(aType),
+  appType(GPS_APP_NONE)
+{
+}
+
 GPSWaypoint::~GPSWaypoint() {}
 
 string GPSWaypoint::GetAprId() {
@@ -126,6 +65,45 @@ string GPSWaypoint::GetAprId() {
        else return(id);
 }
 
+static GPSWpType
+GPSWpTypeFromFGPosType(FGPositioned::Type aType)
+{
+  switch (aType) {
+  case FGPositioned::AIRPORT:
+  case FGPositioned::SEAPORT:
+  case FGPositioned::HELIPORT:
+    return GPS_WP_APT;
+  
+  case FGPositioned::VOR:
+    return GPS_WP_VOR;
+  
+  case FGPositioned::NDB:
+    return GPS_WP_NDB;
+  
+  case FGPositioned::WAYPOINT:
+    return GPS_WP_USR;
+  
+  case FGPositioned::FIX:
+    return GPS_WP_INT;
+  
+  default:
+    return GPS_WP_USR;
+  }
+}
+
+GPSWaypoint* GPSWaypoint::createFromPositioned(const FGPositioned* aPos)
+{
+  if (!aPos) {
+    return NULL; // happens if find returns no match
+  }
+  
+  return new GPSWaypoint(aPos->ident(), 
+    aPos->latitude() * SG_DEGREES_TO_RADIANS,
+    aPos->longitude() * SG_DEGREES_TO_RADIANS,
+    GPSWpTypeFromFGPosType(aPos->type())
+  );
+}
+
 ostream& operator << (ostream& os, GPSAppWpType type) {
        switch(type) {
                case(GPS_IAF):       return(os << "IAF");
@@ -152,65 +130,28 @@ FGNPIAP::FGNPIAP() {
 FGNPIAP::~FGNPIAP() {
 }
 
-GPSPage::GPSPage(DCLGPS* parent) {
-       _parent = parent;
-       _subPage = 0;
-}
-
-GPSPage::~GPSPage() {
+ClockTime::ClockTime() {
+    _hr = 0;
+    _min = 0;
 }
 
-void GPSPage::Update(double dt) {}
-
-void GPSPage::Knob1Left1() {}
-void GPSPage::Knob1Right1() {}
-
-void GPSPage::Knob2Left1() {
-       _parent->_activePage->LooseFocus();
-       _subPage--;
-       if(_subPage < 0) _subPage = _nSubPages - 1;
+ClockTime::ClockTime(int hr, int min) {
+    while(hr < 0) { hr += 24; }
+    _hr = hr % 24;
+    while(min < 0) { min += 60; }
+    while(min > 60) { min -= 60; }
+       _min = min;
 }
 
-void GPSPage::Knob2Right1() {
-       _parent->_activePage->LooseFocus();
-       _subPage++;
-       if(_subPage >= _nSubPages) _subPage = 0;
+ClockTime::~ClockTime() {
 }
 
-void GPSPage::CrsrPressed() {}
-void GPSPage::EntPressed() {}
-void GPSPage::ClrPressed() {}
-void GPSPage::DtoPressed() {}
-void GPSPage::NrstPressed() {}
-void GPSPage::AltPressed() {}
-void GPSPage::OBSPressed() {}
-void GPSPage::MsgPressed() {}
-
-string GPSPage::GPSitoa(int n) {
-       char buf[4];
-       // TODO - sanity check n!
-       sprintf(buf, "%i", n);
-       string s = buf;
-       return(s);
-}
-
-void GPSPage::CleanUp() {}
-void GPSPage::LooseFocus() {}
-void GPSPage::SetId(const string& s) {}
-
 // ------------------------------------------------------------------------------------- //
 
 DCLGPS::DCLGPS(RenderArea2D* instrument) {
        _instrument = instrument;
        _nFields = 1;
        _maxFields = 2;
-       _pages.clear();
-       
-       // Units - lets default to US units - FG can set them to other units from config during startup if desired.
-       _altUnits = GPS_ALT_UNITS_FT;
-       _baroUnits = GPS_PRES_UNITS_IN;
-       _velUnits = GPS_VEL_UNITS_KT;
-       _distUnits = GPS_DIST_UNITS_NM;
 
        _lon_node = fgGetNode("/instrumentation/gps/indicated-longitude-deg", true);
        _lat_node = fgGetNode("/instrumentation/gps/indicated-latitude-deg", true);
@@ -256,12 +197,14 @@ DCLGPS::DCLGPS(RenderArea2D* instrument) {
        _departed = false;
        _departureTimeString = "----";
        _elapsedTime = 0.0;
+       _powerOnTime.set_hr(0);
+       _powerOnTime.set_min(0);
+       _powerOnTimerSet = false;
+       _alarmSet = false;
        
        // Configuration Initialisation
        // Should this be in kln89.cxx ?
        _turnAnticipationEnabled = false;
-       _suaAlertEnabled = false;
-       _altAlertEnabled = false;
         
        _time = new SGTime;
        
@@ -278,379 +221,36 @@ DCLGPS::DCLGPS(RenderArea2D* instrument) {
 
 DCLGPS::~DCLGPS() {
        delete _time;
-       for(gps_waypoint_map_iterator itr = _waypoints.begin(); itr != _waypoints.end(); ++itr) {
-               for(unsigned int i = 0; i < (*itr).second.size(); ++i) {
-                       delete(((*itr).second)[i]);
-               }
-       }
-       delete _approachFP;             // Don't need to delete the waypoints inside since they point to
+  delete _approachFP;          // Don't need to delete the waypoints inside since they point to
                                                        // the waypoints in the approach database.
        // TODO - may need to delete the approach database!!
 }
 
-void DCLGPS::draw() {
-       //cout << "draw called!\n";
-       _instrument->draw();
+void DCLGPS::draw(osg::State& state) {
+       _instrument->Draw(state);
 }
 
 void DCLGPS::init() {
-       globals->get_commands()->addCommand("kln89_msg_pressed", do_kln89_msg_pressed);
-       globals->get_commands()->addCommand("kln89_obs_pressed", do_kln89_obs_pressed);
-       globals->get_commands()->addCommand("kln89_alt_pressed", do_kln89_alt_pressed);
-       globals->get_commands()->addCommand("kln89_nrst_pressed", do_kln89_nrst_pressed);
-       globals->get_commands()->addCommand("kln89_dto_pressed", do_kln89_dto_pressed);
-       globals->get_commands()->addCommand("kln89_clr_pressed", do_kln89_clr_pressed);
-       globals->get_commands()->addCommand("kln89_ent_pressed", do_kln89_ent_pressed);
-       globals->get_commands()->addCommand("kln89_crsr_pressed", do_kln89_crsr_pressed);
-       globals->get_commands()->addCommand("kln89_knob1left1", do_kln89_knob1left1);
-       globals->get_commands()->addCommand("kln89_knob1right1", do_kln89_knob1right1);
-       globals->get_commands()->addCommand("kln89_knob2left1", do_kln89_knob2left1);
-       globals->get_commands()->addCommand("kln89_knob2right1", do_kln89_knob2right1);
-       
-       // Build the GPS-specific databases.
-       // TODO - consider splitting into real life GPS database regions - eg Americas, Europe etc.
-       // Note that this needs to run after FG's airport and nav databases are up and running
-       _waypoints.clear();
-       const airport_list* apts = globals->get_airports()->getAirportList();
-       for(unsigned int i = 0; i < apts->size(); ++i) {
-               FGAirport* a = (*apts)[i];
-               GPSWaypoint* w = new GPSWaypoint;
-               w->id = a->getId();
-               w->lat = a->getLatitude() * SG_DEGREES_TO_RADIANS;
-               w->lon = a->getLongitude() * SG_DEGREES_TO_RADIANS;
-               w->type = GPS_WP_APT;
-               gps_waypoint_map_iterator wtr = _waypoints.find(a->getId());
-               if(wtr == _waypoints.end()) {
-                       gps_waypoint_array arr;
-                       arr.push_back(w);
-                       _waypoints[w->id] = arr;
-               } else {
-                       wtr->second.push_back(w);
-               }
-       }
-       nav_map_type navs = globals->get_navlist()->get_navaids();
-       for(nav_map_iterator itr = navs.begin(); itr != navs.end(); ++itr) {
-               nav_list_type nlst = itr->second;
-               for(unsigned int i = 0; i < nlst.size(); ++i) {
-                       FGNavRecord* n = nlst[i];
-                       if(n->get_fg_type() == FG_NAV_VOR || n->get_fg_type() == FG_NAV_NDB) {  // We don't bother with ILS etc.
-                               GPSWaypoint* w = new GPSWaypoint;
-                               w->id = n->get_ident();
-                               w->lat = n->get_lat() * SG_DEGREES_TO_RADIANS;
-                               w->lon = n->get_lon() * SG_DEGREES_TO_RADIANS;
-                               w->type = (n->get_fg_type() == FG_NAV_VOR ? GPS_WP_VOR : GPS_WP_NDB);
-                               gps_waypoint_map_iterator wtr = _waypoints.find(n->get_ident());
-                               if(wtr == _waypoints.end()) {
-                                       gps_waypoint_array arr;
-                                       arr.push_back(w);
-                                       _waypoints[w->id] = arr;
-                               } else {
-                                       wtr->second.push_back(w);
-                               }
-                       }
-               }
-       }
-       const fix_map_type* fixes = globals->get_fixlist()->getFixList();
-       for(fix_map_const_iterator itr = fixes->begin(); itr != fixes->end(); ++itr) {
-               FGFix f = itr->second;
-               GPSWaypoint* w = new GPSWaypoint;
-               w->id = f.get_ident();
-               w->lat = f.get_lat() * SG_DEGREES_TO_RADIANS;
-               w->lon = f.get_lon() * SG_DEGREES_TO_RADIANS;
-               w->type = GPS_WP_INT;
-               gps_waypoint_map_iterator wtr = _waypoints.find(f.get_ident());
-               if(wtr == _waypoints.end()) {
-                       gps_waypoint_array arr;
-                       arr.push_back(w);
-                       _waypoints[w->id] = arr;
-               } else {
-                       wtr->second.push_back(w);
-               }
-       }
-       // TODO - add USR waypoints as well.
-       
+               
        // Not sure if this should be here, but OK for now.
        CreateDefaultFlightPlans();
-       
-       // Hack - hardwire some instrument approaches for testing.
-       // TODO - read these from file - either all at startup or as needed.
-       FGNPIAP* iap = new FGNPIAP;
-       iap->_id = "KHWD";
-       iap->_name = "VOR/DME OR GPS-B";
-       iap->_abbrev = "VOR/D";
-       iap->_rwyStr = "B";
-       iap->_IAF.clear();
-       iap->_IAP.clear();
-       iap->_MAP.clear();
-       // -------
-       GPSWaypoint* wp = new GPSWaypoint;
-       wp->id = "SUNOL";
-       bool multi;
-       // Nasty using the find any function here, but it saves converting data from FGFix etc. 
-       const GPSWaypoint* fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "MABRY";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "IMPLY";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAP;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "DECOT";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_FAF;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "MAPVV";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAP;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "OAK";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAHP;
-       iap->_MAP.push_back(wp);
-       // -------
-       _np_iap[iap->_id].push_back(iap);
-       // -----------------------
-       // -----------------------
-       iap = new FGNPIAP;
-       iap->_id = "KHWD";
-       iap->_name = "VOR OR GPS-A";
-       iap->_abbrev = "VOR-";
-       iap->_rwyStr = "A";
-       iap->_IAF.clear();
-       iap->_IAP.clear();
-       iap->_MAP.clear();
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "SUNOL";
-       // Nasty using the find any function here, but it saves converting data from FGFix etc. 
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "MABRY";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "IMPLY";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAP;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "DECOT";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_FAF;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "MAPVV";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAP;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "OAK";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAHP;
-       iap->_MAP.push_back(wp);
-       // -------
-       _np_iap[iap->_id].push_back(iap);
-       // ------------------
-       // ------------------
-       /*
-       // Ugh - don't load this one - the waypoints required aren't in fix.dat.gz - result: program crash!
-       // TODO - make the IAP loader robust to absent waypoints.
-       iap = new FGNPIAP;
-       iap->_id = "KHWD";
-       iap->_name = "GPS RWY 28L";
-       iap->_abbrev = "GPS";
-       iap->_rwyStr = "28L";
-       iap->_IAF.clear();
-       iap->_IAP.clear();
-       iap->_MAP.clear();
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "SUNOL";
-       // Nasty using the find any function here, but it saves converting data from FGFix etc. 
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "SJC";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAF;
-       iap->_IAF.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "JOCPI";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_IAP;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "SUDGE";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_FAF;
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "RW28L";
-       wp->appType = GPS_MAP;
-       if(wp->id.substr(0, 2) == "RW" && wp->appType == GPS_MAP) {
-               // Assume that this is a missed-approach point based on the runway number
-               // Get the runway threshold location etc
-       } else {
-               fp = FindFirstById(wp->id, multi, true);
-               if(fp == NULL) {
-                       cout << "Failed to find waypoint " << wp->id << " in database...\n";
-               } else {
-                       *wp = *fp;
-               }
-       }
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "OAK";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAHP;
-       iap->_MAP.push_back(wp);
-       // -------
-       _np_iap[iap->_id].push_back(iap);
-       */
-       iap = new FGNPIAP;
-       iap->_id = "C83";
-       iap->_name = "GPS RWY 30";
-       iap->_abbrev = "GPS";
-       iap->_rwyStr = "30";
-       iap->_IAF.clear();
-       iap->_IAP.clear();
-       iap->_MAP.clear();
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "MAXNI";
-       // Nasty using the find any function here, but it saves converting data from FGFix etc. 
-       fp = FindFirstById(wp->id, multi, true);
-       if(fp) {
-               *wp = *fp;
-               wp->appType = GPS_IAF;
-               iap->_IAF.push_back(wp);
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "PATYY";
-       fp = FindFirstById(wp->id, multi, true);
-       if(fp) {
-               *wp = *fp;
-               wp->appType = GPS_IAF;
-               iap->_IAF.push_back(wp);
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "TRACY";
-       fp = FindFirstById(wp->id, multi, true);
-       if(fp) {
-               *wp = *fp;
-               wp->appType = GPS_IAF;
-               iap->_IAF.push_back(wp);
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "TRACY";
-       fp = FindFirstById(wp->id, multi, true);
-       if(fp) {
-               *wp = *fp;
-               wp->appType = GPS_IAP;
-               iap->_IAP.push_back(wp);
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "BABPI";
-       fp = FindFirstById(wp->id, multi, true);
-       if(fp) {
-               *wp = *fp;
-               wp->appType = GPS_FAF;
-               iap->_IAP.push_back(wp);
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "AMOSY";
-       wp->appType = GPS_MAP;
-       if(wp->id.substr(0, 2) == "RW" && wp->appType == GPS_MAP) {
-               // Assume that this is a missed-approach point based on the runway number
-               // TODO: Get the runway threshold location etc
-               cout << "TODO - implement missed-approach point based on rwy no.\n";
-       } else {
-               fp = FindFirstById(wp->id, multi, true);
-               if(fp == NULL) {
-                       cout << "Failed to find waypoint " << wp->id << " in database...\n";
-               } else {
-                       *wp = *fp;
-                       wp->appType = GPS_MAP;
-               }
-       }
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "HAIRE";
-       fp = FindFirstById(wp->id, multi, true); 
-       *wp = *fp;
-       wp->appType = GPS_MAHP;
-       iap->_MAP.push_back(wp);
-       // -------
-       _np_iap[iap->_id].push_back(iap);
+
+       LoadApproachData();
 }
 
 void DCLGPS::bind() {
-       fgTie("/instrumentation/gps/waypoint-alert", this, &DCLGPS::GetWaypointAlert);
-       fgTie("/instrumentation/gps/leg-mode", this, &DCLGPS::GetLegMode);
-       fgTie("/instrumentation/gps/obs-mode", this, &DCLGPS::GetOBSMode);
-       fgTie("/instrumentation/gps/approach-arm", this, &DCLGPS::GetApproachArm);
-       fgTie("/instrumentation/gps/approach-active", this, &DCLGPS::GetApproachActive);
-       fgTie("/instrumentation/gps/cdi-deflection", this, &DCLGPS::GetCDIDeflection);
-       fgTie("/instrumentation/gps/to-flag", this, &DCLGPS::GetToFlag);
+       _tiedProperties.setRoot(fgGetNode("/instrumentation/gps", true));
+       _tiedProperties.Tie("waypoint-alert",  this, &DCLGPS::GetWaypointAlert);
+       _tiedProperties.Tie("leg-mode",        this, &DCLGPS::GetLegMode);
+       _tiedProperties.Tie("obs-mode",        this, &DCLGPS::GetOBSMode);
+       _tiedProperties.Tie("approach-arm",    this, &DCLGPS::GetApproachArm);
+       _tiedProperties.Tie("approach-active", this, &DCLGPS::GetApproachActive);
+       _tiedProperties.Tie("cdi-deflection",  this, &DCLGPS::GetCDIDeflection);
+       _tiedProperties.Tie("to-flag",         this, &DCLGPS::GetToFlag);
 }
 
 void DCLGPS::unbind() {
-       fgUntie("/instrumentation/gps/waypoint-alert");
-       fgUntie("/instrumentation/gps/leg-mode");
-       fgUntie("/instrumentation/gps/obs-mode");
-       fgUntie("/instrumentation/gps/approach-arm");
-       fgUntie("/instrumentation/gps/approach-active");
-       fgUntie("/instrumentation/gps/cdi-deflection");
+       _tiedProperties.Untie();
 }
 
 void DCLGPS::update(double dt) {
@@ -674,6 +274,20 @@ void DCLGPS::update(double dt) {
        _checkLon = _gpsLon;
        _checkLat = _gpsLat;
        
+       // TODO - check for unit power before running this.
+       if(!_powerOnTimerSet) {
+               SetPowerOnTimer();
+       } 
+       
+       // Check if an alarm timer has expired
+       if(_alarmSet) {
+               if(_alarmTime.hr() == atoi(fgGetString("/instrumentation/clock/indicated-hour"))
+               && _alarmTime.min() == atoi(fgGetString("/instrumentation/clock/indicated-min"))) {
+                       _messageStack.push_back("*Timer Expired");
+                       _alarmSet = false;
+               }
+       }
+       
        if(!_departed) {
                if(_groundSpeed_kts > 30.0) {
                        _departed = true;
@@ -859,14 +473,13 @@ void DCLGPS::update(double dt) {
                                                // Do nothing
                                        } else if(_activeWaypoint.appType == GPS_MAP) {
                                                // Don't sequence beyond the missed approach point
-                                               cout << "ACTIVE WAYPOINT is MAP - not sequencing!!!!!\n";
+                                               //cout << "ACTIVE WAYPOINT is MAP - not sequencing!!!!!\n";
                                        } else {
-                                               cout << "Sequencing...\n";
+                                               //cout << "Sequencing...\n";
                                                _fromWaypoint = _activeWaypoint;
                                                _activeWaypoint = *_activeFP->waypoints[idx + 1];
                                                _dto = false;
-                                               // TODO - course alteration message format is dependent on whether we are slaved HSI/CDI indicator or not.
-                                               // For now assume we are not.
+                                               // Course alteration message format is dependent on whether we are slaved HSI/CDI indicator or not.
                                                string s;
                                                if(fgGetBool("/instrumentation/nav[0]/slaved-to-gps")) {
                                                        // TODO - avoid the hardwiring on nav[0]
@@ -901,27 +514,496 @@ void DCLGPS::update(double dt) {
        }
 }
 
+/* 
+       Expand a SIAP ident to the full procedure name (as shown on the approach chart).
+       NOTE: Some of this is inferred from data, some is from documentation.
+       
+       Example expansions from ARINC 424-18 [and the airport they're taken from]:
+       "R10LY" <--> "RNAV (GPS) Y RWY 10 L"    [KBOI]
+       "R10-Z" <--> "RNAV (GPS) Z RWY 10"              [KHTO]
+       "S25"   <--> "VOR or GPS RWY 25"                [KHHR]
+       "P20"   <--> "GPS RWY 20"                               [KDAN]
+       "NDB-B" <--> "NDB or GPS-B"                             [KDAW]
+       "NDBC"  <--> "NDB or GPS-C"                             [KEMT]
+       "VDMA"  <--> "VOR/DME or GPS-A"                 [KDAW]
+       "VDM-A" <--> "VOR/DME or GPS-A"                 [KEAG]
+       "VDMB"  <--> "VOR/DME or GPS-B"                 [KDKX]
+       "VORA"  <--> "VOR or GPS-A"                             [KEMT]
+       
+       It seems that there are 2 basic types of expansions; those that include
+       the runway and those that don't.  Of those that don't, it seems that 2
+       different positions within the string to encode the identifying letter
+       are used, i.e. with a dash and without.
+*/
+string DCLGPS::ExpandSIAPIdent(const string& ident) {
+       string name;
+       bool has_rwy = false;
+       
+       switch(ident[0]) {
+       case 'N': name = "NDB or GPS"; has_rwy = false; break;
+       case 'P': name = "GPS"; has_rwy = true; break;
+       case 'R': name = "RNAV (GPS)"; has_rwy = true; break;
+       case 'S': name = "VOR or GPS"; has_rwy = true; break;
+       case 'V':
+               if(ident[1] == 'D') name = "VOR/DME or GPS";
+               else name = "VOR or GPS";
+               has_rwy = false;
+               break;
+       default: // TODO output a log message
+               break;
+       }
+       
+       if(has_rwy) {
+               // Add the identifying letter if present
+               if(ident.size() == 5) {
+                       name += ' ';
+                       name += ident[4];
+               }
+               
+               // Add the runway
+               name += " RWY ";
+               name += ident.substr(1, 2);
+               
+               // Add a left/right/centre indication if present.
+               if(ident.size() > 3) {
+                       if((ident[3] != '-') && (ident[3] != ' ')) {    // Early versions of the spec allowed a blank instead of a dash so check for both
+                               name += ' ';
+                               name += ident[3];
+                       }
+               }
+       } else {
+               // Add the identifying letter, which I *think* should always be present, but seems to be inconsistent as to whether a dash is used.
+               if(ident.size() == 5) {
+                       name += '-';
+                       name += ident[4];
+               } else if(ident.size() == 4) {
+                       name += '-';
+                       name += ident[3];
+               } else {
+                       // No suffix letter
+               }
+       }
+       
+       return(name);
+}
+
+/*
+       Load instrument approaches from an ARINC 424 file.
+       Tested on ARINC 424-18.
+       Known / current best guess at the format:
+       Col 1:          Always 'S'.  If it isn't, ditch it.
+       Col 2-4:        "Customer area" code, eg "USA", "CAN".  I think that CAN is used for Alaska.
+       Col 5:          Section code.  Used in conjunction with sub-section code.  Definitions are with sub-section code.
+       Col 6:          Always blank.
+       Col 7-10:       ICAO (or FAA) airport ident. Left justified if < 4 chars.
+       Col 11-12:      Based on ICAO geographical region.
+       Col 13:         Sub-section code.  Used in conjunction with section code.
+                               "HD/E/F" => Helicopter record.
+                               "HS" => Helicopter minimum safe altitude.
+                               "PA" => Airport record.
+                               "PF" => Approach segment.
+                               "PG" => Runway record.
+                               "PP" => Path point record.      ???
+                               "PS" => MSA record (minimum safe altitude).
+                               
+       ------ The following is for "PF", approach segment -------
+                               
+       Col 14-19:      SIAP ident for this approach (left justified).  This is a standardised abbreviated approach name.
+                               e.g. "R10LZ" expands to "RNAV (GPS) Z RWY 10 L".  See the comment block for ExpandSIAPIdent for full details.
+       Col 20:         Route type.  This is tricky - I don't have full documentation and am having to guess a bit.
+                               'A'     => Arrival route?       This seems to be used to encode arrival routes from the IAF to the approach proper.
+                                                                               Note that the final fix of the arrival route is duplicated in the approach proper.
+                               'D'     => VOR/DME or GPS 
+                               'N' => NDB or GPS
+                               'P'     => GPS (ARINC 424-18), GPS and RNAV (GPS) (ARINC 424-15 and before).
+                               'R' => RNAV (GPS) (ARINC 424-18).
+                               'S'     => VOR or GPS
+       Col 21-25:      Transition identifier.  AFAICT, this is the ident of the IAF for this initial approach route, and left blank for the final approach course.  See col 30-34 for the actual fix ident.
+       Col 26:         BLANK
+       Col 27-29:      Sequence number - position within the route segment.  Rule: 10-20-30 etc.
+       Col 30-34:      Fix identifer.  The ident of the waypoint.
+       Col 35-36:      ICAO geographical region code.  I think we can ignore this for now.
+       Col 37:         Section code - ??? I don't know what this means
+       Col 38          Subsection code - ??? ditto - no idea!
+       Col 40:         Waypoint type.
+                               'A' => Airport as waypoint
+                               'E' => Essential waypoint (e.g. change of heading at this waypoint, etc).
+                               'G' => Runway or helipad as waypoint
+                               'H' => Heliport as waypoint
+                               'N' => NDB as waypoint
+                               'P' => Phantom waypoint (not sure if this is used in rev 18?)
+                               'V' => VOR as waypoint
+       Col 41:         Waypoint type.
+                               'B' => Flyover, approach transition, or final approach.
+                               'E' => end of route segment (transition waypoint). (Actually "End of terminal procedure route type" in the docs).
+                               'N' => ??? I've also seen 'N' in this column, but don't know what it indicates.
+                               'Y' => Flyover.
+       Col 43:         Waypoint type.  May also be blank when none of the below.
+                               'A' => Initial approach fix (IAF)
+                               'F' => Final approach fix
+                               'H' => Holding fix
+                               'I' => Final approach course fix
+                               'M' => Missed approach point
+                               'P' => ??? This is present, but I don't know what this means and it wasn't in the FAA docs that I found the above in!
+                                          ??? Possibly procedure turn?
+                               'C' => ??? This is also present in the data, but missing from the docs.  Is at airport 00R.
+       Col 107-111     MSA center fix.  We can ignore this.
+*/
+void DCLGPS::LoadApproachData() {
+       FGNPIAP* iap = NULL;
+       GPSWaypoint* wp;
+       GPSFlightPlan* fp;
+       const GPSWaypoint* cwp;
+       
+       std::ifstream fin;
+       SGPath path = globals->get_fg_root();
+       path.append("Navaids/rnav.dat");
+       fin.open(path.c_str(), ios::in);
+       if(!fin) {
+               //cout << "Unable to open input file " << path.c_str() << '\n';
+               return;
+       } else {
+               //cout << "Opened " << path.c_str() << " for reading\n";
+       }
+       char tmp[256];
+       string s;
+       
+       string apt_ident;    // This gets set to the ICAO code of the current airport being processed.
+       string iap_ident;    // The abbreviated name of the current approach being processed.
+       string wp_ident;     // The ident of the waypoint of the current line
+       string last_apt_ident;
+       string last_iap_ident;
+       string last_wp_ident;
+       // There is no need to save the full name - it can be generated on the fly from the abbreviated name as and when needed.
+       bool apt_in_progress = false;    // Set true whilst loading all the approaches for a given airport.
+       bool iap_in_progress = false;    // Set true whilst loading a given approach.
+       bool iap_error = false;                  // Set true if there is an error loading a given approach.
+       bool route_in_progress = false;  // Set true when we are loading a "route" segment of the approach.
+       int last_sequence_number = 0;    // Position within the route, rule (rev 18): 10, 20, 30 etc.
+       int sequence_number;
+       char last_route_type = 0;
+       char route_type;
+       char waypoint_fix_type;  // This is the waypoint type from col 43, i.e. the type of fix.  May be blank.
+       
+       int j;
+       
+       // Debugging info
+       unsigned int nLoaded = 0;
+       unsigned int nErrors = 0;
+       
+       //for(i=0; i<64; ++i) {
+       while(!fin.eof()) {
+               fin.getline(tmp, 256);
+               //s = Fake_rnav_dat[i];
+               s = tmp;
+               if(s.size() < 132) continue;
+               if(s[0] == 'S') {    // Valid line
+                       string country_code = s.substr(1, 3);
+                       if(country_code == "USA") {    // For now we'll stick to US procedures in case there are unknown gotchas with others
+                               if(s[4] == 'P') {    // Includes approaches.
+                                       if(s[12] == 'A') {      // Airport record
+                                               apt_ident = s.substr(6, 4);
+                                               // Trim any whitespace from the ident.  The ident is left justified,
+                                               // so any space will be at the end.
+                                               if(apt_ident[3] == ' ') apt_ident = apt_ident.substr(0, 3);
+                                               // I think that all idents are either 3 or 4 chars - could check this though!
+                                               if(!apt_in_progress) {
+                                                       last_apt_ident = apt_ident;
+                                                       apt_in_progress = 1;
+                                               } else {
+                                                       if(last_apt_ident != apt_ident) {
+                                                               if(iap_in_progress) {
+                                                                       if(iap_error) {
+                                                                               //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+                                                                               nErrors++;
+                                                                       } else {
+                                                                               _np_iap[iap->_aptIdent].push_back(iap);
+                                                                               //cout << "** Loaded " << iap->_aptIdent << "\t" << iap->_ident << '\n';
+                                                                               nLoaded++;
+                                                                       }
+                                                                       iap_in_progress = false;
+                                                               }
+                                                       }
+                                                       last_apt_ident = apt_ident;
+                                               }
+                                               iap_in_progress = 0;
+                                       } else if(s[12] == 'F') {       // Approach segment
+                                               if(apt_in_progress) {
+                                                       iap_ident = s.substr(13, 6);
+                                                       // Trim any whitespace from the RH end.
+                                                       for(j=0; j<6; ++j) {
+                                                               if(iap_ident[5-j] == ' ') {
+                                                                       iap_ident = iap_ident.substr(0, 5-j);
+                                                               } else {
+                                                                       // It's important to break here, since earlier versions of ARINC 424 allowed spaces in the ident.
+                                                                       break;
+                                                               }
+                                                       }
+                                                       if(iap_in_progress) {
+                                                               if(iap_ident != last_iap_ident) {
+                                                                       // This is a new approach - store the last one and trigger
+                                                                       // starting afresh by setting the in progress flag to false.
+                                                                       if(iap_error) {
+                                                                               //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+                                                                               nErrors++;
+                                                                       } else {
+                                                                               _np_iap[iap->_aptIdent].push_back(iap);
+                                                                               //cout << "Loaded " << iap->_aptIdent << "\t" << iap->_ident << '\n';
+                                                                               nLoaded++;
+                                                                       }
+                                                                       iap_in_progress = false;
+                                                               }
+                                                       }
+                                                       if(!iap_in_progress) {
+                                                               iap = new FGNPIAP;
+                                                               iap->_aptIdent = apt_ident;
+                                                               iap->_ident = iap_ident;
+                                                               iap->_name = ExpandSIAPIdent(iap_ident); // I suspect that it's probably better to just store idents, and to expand the names as needed.
+                                                               // Note, we haven't set iap->_rwyStr yet.
+                                                               last_iap_ident = iap_ident;
+                                                               iap_in_progress = true;
+                                                               iap_error = false;
+                                                       }
+                                                       
+                                                       // Route type
+                                                       route_type = s[19];
+                                                       sequence_number = atoi(s.substr(26,3).c_str());
+                                                       wp_ident = s.substr(29, 5);
+                                                       waypoint_fix_type = s[42];
+                                                       // Trim any whitespace from the RH end
+                                                       for(j=0; j<5; ++j) {
+                                                               if(wp_ident[4-j] == ' ') {
+                                                                       wp_ident = wp_ident.substr(0, 4-j);
+                                                               } else {
+                                                                       break;
+                                                               }
+                                                       }
+                                                       
+                                                       // Ignore lines with no waypoint ID for now - these are normally part of the
+                                                       // missed approach procedure, and we don't use them in the KLN89.
+                                                       if(!wp_ident.empty()) {
+                                                               // Make a local copy of the waypoint for now, since we're not yet sure if we'll be using it
+                                                               GPSWaypoint w;
+                                                               w.id = wp_ident;
+                                                               bool wp_error = false;
+                                                               if(w.id.substr(0, 2) == "RW" && waypoint_fix_type == 'M') {
+                                                                       // Assume that this is a missed-approach point based on the runway number, which appears to be standard for most approaches.
+                                                                       // Note: Currently fgFindAirportID returns NULL on error, but getRunwayByIdent throws an exception.
+                                                                       const FGAirport* apt = fgFindAirportID(iap->_aptIdent);
+                                                                       if(apt) {
+                                                                               string rwystr;
+                                                                               try {
+                                                                                       rwystr = w.id.substr(2, 2);
+                                                                                       // TODO - sanity check the rwystr at this point to ensure we have a double digit number
+                                                                                       if(w.id.size() > 4) {
+                                                                                               if(w.id[4] == 'L' || w.id[4] == 'C' || w.id[4] == 'R') {
+                                                                                                       rwystr += w.id[4];
+                                                                                               }
+                                                                                       }
+                                                                                       FGRunway* rwy = apt->getRunwayByIdent(rwystr);
+                                                                                       w.lat = rwy->begin().getLatitudeRad();
+                                                                                       w.lon = rwy->begin().getLongitudeRad();
+                                                                               } catch(const sg_exception&) {
+                                                                                       SG_LOG(SG_INSTR, SG_WARN, "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent);
+                                                                                       //cout << "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent << " ( w.id = " << w.id << ", rwystr = " << rwystr << " )\n";
+                                                                                       wp_error = true;
+                                                                               }
+                                                                       } else {
+                                                                               wp_error = true;
+                                                                       }
+                                                               } else {
+                                                                       cwp = FindFirstByExactId(w.id);
+                                                                       if(cwp) {
+                                                                               w = *cwp;
+                                                                       } else {
+                                                                               wp_error = true;
+                                                                       }
+                                                               }
+                                                               switch(waypoint_fix_type) {
+                                                               case 'A': w.appType = GPS_IAF; break;
+                                                               case 'F': w.appType = GPS_FAF; break;
+                                                               case 'H': w.appType = GPS_MAHP; break;
+                                                               case 'I': w.appType = GPS_IAP; break;
+                                                               case 'M': w.appType = GPS_MAP; break;
+                                                               case ' ': w.appType = GPS_APP_NONE; break;
+                                                               //default: cout << "Unknown waypoint_fix_type: \'" << waypoint_fix_type << "\' [" << apt_ident << ", " << iap_ident << "]\n";
+                                                               }
+                                                               
+                                                               if(wp_error) {
+                                                                       //cout << "Unable to find waypoint " << w.id << " [" << apt_ident << ", " << iap_ident << "]\n";
+                                                                       iap_error = true;
+                                                               }
+                                                       
+                                                               if(!wp_error) {
+                                                                       if(route_in_progress) {
+                                                                               if(sequence_number > last_sequence_number) {
+                                                                                       // TODO - add a check for runway numbers
+                                                                                       // Check for the waypoint ID being the same as the previous line.
+                                                                                       // This is often the case for the missed approach holding point.
+                                                                                       if(wp_ident == last_wp_ident) {
+                                                                                               if(waypoint_fix_type == 'H') {
+                                                                                                       if(!iap->_IAP.empty()) {
+                                                                                                               if(iap->_IAP[iap->_IAP.size() - 1]->appType == GPS_APP_NONE) {
+                                                                                                                       iap->_IAP[iap->_IAP.size() - 1]->appType = GPS_MAHP;
+                                                                                                               } else {
+                                                                                                                       //cout << "Waypoint is MAHP and another type! " << w.id << " [" << apt_ident << ", " << iap_ident << "]\n";
+                                                                                                               }
+                                                                                                       }
+                                                                                               }
+                                                                                       } else {
+                                                                                               // Create a new waypoint on the heap, copy the local copy into it, and push it onto the approach.
+                                                                                               wp = new GPSWaypoint;
+                                                                                               *wp = w;
+                                                                                               if(route_type == 'A') {
+                                                                                                       fp->waypoints.push_back(wp);
+                                                                                               } else {
+                                                                                                       iap->_IAP.push_back(wp);
+                                                                                               }
+                                                                                       }
+                                                                               } else if(sequence_number == last_sequence_number) {
+                                                                                       // This seems to happen once per final approach route - one of the waypoints
+                                                                                       // is duplicated with the same sequence number - I'm not sure what information
+                                                                                       // the second line give yet so ignore it for now.
+                                                                                       // TODO - figure this out!
+                                                                               } else {
+                                                                                       // Finalise the current route and start a new one
+                                                                                       //
+                                                                                       // Finalise the current route
+                                                                                       if(last_route_type == 'A') {
+                                                                                               // Push the flightplan onto the approach
+                                                                                               iap->_approachRoutes.push_back(fp);
+                                                                                       } else {
+                                                                                               // All the waypoints get pushed individually - don't need to do it.
+                                                                                       }
+                                                                                       // Start a new one
+                                                                                       // There are basically 2 possibilities here - either it's one of the arrival transitions,
+                                                                                       // or it's the core final approach course.
+                                                                                       wp = new GPSWaypoint;
+                                                                                       *wp = w;
+                                                                                       if(route_type == 'A') { // It's one of the arrival transition(s)
+                                                                                               fp = new GPSFlightPlan;
+                                                                                               fp->waypoints.push_back(wp);
+                                                                                       } else {
+                                                                                               iap->_IAP.push_back(wp);
+                                                                                       }
+                                                                                       route_in_progress = true;
+                                                                               }
+                                                                       } else {
+                                                                               // Start a new route.
+                                                                               // There are basically 2 possibilities here - either it's one of the arrival transitions,
+                                                                               // or it's the core final approach course.
+                                                                               wp = new GPSWaypoint;
+                                                                               *wp = w;
+                                                                               if(route_type == 'A') { // It's one of the arrival transition(s)
+                                                                                       fp = new GPSFlightPlan;
+                                                                                       fp->waypoints.push_back(wp);
+                                                                               } else {
+                                                                                       iap->_IAP.push_back(wp);
+                                                                               }
+                                                                               route_in_progress = true;
+                                                                       }
+                                                                       last_route_type = route_type;
+                                                                       last_wp_ident = wp_ident;
+                                                                       last_sequence_number = sequence_number;
+                                                               }
+                                                       }
+                                               } else {
+                                                       // ERROR - no airport record read.
+                                               }
+                                       }
+                               } else {
+                                       // Check and finalise any approaches in progress
+                                       // TODO - sanity check that the approach has all the required elements
+                                       if(iap_in_progress) {
+                                               // This is a new approach - store the last one and trigger
+                                               // starting afresh by setting the in progress flag to false.
+                                               if(iap_error) {
+                                                       //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+                                                       nErrors++;
+                                               } else {
+                                                       _np_iap[iap->_aptIdent].push_back(iap);
+                                                       //cout << "* Loaded " << iap->_aptIdent << "\t" << iap->_ident << '\n';
+                                                       nLoaded++;
+                                               }
+                                               iap_in_progress = false;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       // If we get to the end of the file, load any approach that is still in progress
+       // TODO - sanity check that the approach has all the required elements
+       if(iap_in_progress) {
+               if(iap_error) {
+                       //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+                       nErrors++;
+               } else {
+                       _np_iap[iap->_aptIdent].push_back(iap);
+                       //cout << "*** Loaded " << iap->_aptIdent << "\t" << iap->_ident << '\n';
+                       nLoaded++;
+               }
+       }
+       
+       //cout << "Done loading approach database\n";
+       //cout << "Loaded: " << nLoaded << '\n';
+       //cout << "Failed: " << nErrors << '\n';
+       
+       fin.close();
+}
+
+GPSWaypoint* DCLGPS::GetActiveWaypoint() { 
+       return &_activeWaypoint; 
+}
+       
+// Returns meters
+float DCLGPS::GetDistToActiveWaypoint() { 
+       return _dist2Act;
+}
+
+// I don't yet fully understand all the gotchas about where to source time from.
+// This function sets the initial timer before the clock exports properties
+// and the one below uses the clock to be consistent with the rest of the code.
+// It might change soonish...
+void DCLGPS::SetPowerOnTimer() {
+       struct tm *t = globals->get_time_params()->getGmt();
+       _powerOnTime.set_hr(t->tm_hour);
+       _powerOnTime.set_min(t->tm_min);
+       _powerOnTimerSet = true;
+}
+
+void DCLGPS::ResetPowerOnTimer() {
+       _powerOnTime.set_hr(atoi(fgGetString("/instrumentation/clock/indicated-hour")));
+       _powerOnTime.set_min(atoi(fgGetString("/instrumentation/clock/indicated-min")));
+       _powerOnTimerSet = true;
+}
+
 double DCLGPS::GetCDIDeflection() const {
        double xtd = CalcCrossTrackDeviation(); //nm
        return((xtd / _currentCdiScale) * 5.0 * 2.5 * -1.0);
 }
 
 void DCLGPS::DtoInitiate(const string& s) {
-       //cout << "DtoInitiate, s = " << s << '\n';
-       bool multi;
-       const GPSWaypoint* wp = FindFirstById(s, multi, true);
+       const GPSWaypoint* wp = FindFirstByExactId(s);
        if(wp) {
-               //cout << "Waypoint found, starting dto operation!\n";
+               // TODO - Currently we start DTO operation unconditionally, regardless of which mode we are in.
+               // In fact, the following rules apply:
+               // In LEG mode, start DTO as we currently do.
+               // In OBS mode, set the active waypoint to the requested waypoint, and then:
+               // If the KLN89 is not connected to an external HSI or CDI, set the OBS course to go direct to the waypoint.
+               // If the KLN89 *is* connected to an external HSI or CDI, it cannot set the course itself, and will display
+               // a scratchpad message with the course to set manually on the HSI/CDI.
+               // In both OBS cases, leave _dto false, since we don't need the virtual waypoint created.
                _dto = true;
                _activeWaypoint = *wp;
                _fromWaypoint.lat = _gpsLat;
                _fromWaypoint.lon = _gpsLon;
                _fromWaypoint.type = GPS_WP_VIRT;
-               _fromWaypoint.id = "DTOWP";
+               _fromWaypoint.id = "_DTOWP_";
+               delete wp;
        } else {
-               //cout << "Waypoint not found, ignoring dto request\n";
-               // Should bring up the user waypoint page, but we're not implementing that yet.
-               _dto = false;   // TODO - implement this some day.
+               // TODO - Should bring up the user waypoint page.
+               _dto = false;
        }
 }
 
@@ -934,23 +1016,40 @@ void DCLGPS::DtoCancel() {
        _dto = false;
 }
 
-void DCLGPS::Knob1Left1() {}
-void DCLGPS::Knob1Right1() {}
-void DCLGPS::Knob2Left1() {}
-void DCLGPS::Knob2Right1() {}
-void DCLGPS::CrsrPressed() { _activePage->CrsrPressed(); }
-void DCLGPS::EntPressed() { _activePage->EntPressed(); }
-void DCLGPS::ClrPressed() { _activePage->ClrPressed(); }
-void DCLGPS::DtoPressed() {}
-void DCLGPS::NrstPressed() {}
-void DCLGPS::AltPressed() {}
-
-void DCLGPS::OBSPressed() { 
+void DCLGPS::ToggleOBSMode() {
        _obsMode = !_obsMode;
        if(_obsMode) {
-               if(!_activeWaypoint.id.empty()) {
-                       _obsHeading = static_cast<int>(_dtkMag);
+               // We need to set the OBS heading.  The rules for this are:
+               //
+               // If the KLN89 is connected to an external HSI or CDI, then the OBS heading must be set
+               // to the OBS radial on the external indicator, and cannot be changed in the KLN89.
+               //
+               // If the KLN89 is not connected to an external indicator, then:
+               //              If there is an active waypoint, the OBS heading is set such that the
+               //              deviation indicator remains at the same deviation (i.e. set to DTK, 
+               //              although there may be some small difference).
+               //
+               //              If there is not an active waypoint, I am not sure what value should be
+               //              set.
+               //
+               if(fgGetBool("/instrumentation/nav/slaved-to-gps")) {
+                       _obsHeading = static_cast<int>(fgGetDouble("/instrumentation/nav/radials/selected-deg") + 0.5);
+               } else if(!_activeWaypoint.id.empty()) {
+                       // NOTE: I am not sure if this is completely correct.  There are sometimes small differences
+                       // between DTK and default OBS heading in the simulator (~ 1 or 2 degrees).  It might also
+                       // be that OBS heading should be stored as float and only displayed as int, in order to maintain
+                       // the initial bar deviation?
+                       _obsHeading = static_cast<int>(_dtkMag + 0.5);
+                       //cout << "_dtkMag = " << _dtkMag << ", _dtkTrue = " << _dtkTrue << ", _obsHeading = " << _obsHeading << '\n';
+               } else {
+                       // TODO - what should we really do here?
+                       _obsHeading = 0;
                }
+               
+               // Valid OBS heading values are 0 -> 359 degrees inclusive (from kln89 simulator).
+               if(_obsHeading > 359) _obsHeading -= 360;
+               if(_obsHeading < 0) _obsHeading += 360;
+               
                // TODO - the _fromWaypoint location will change as the OBS heading changes.
                // Might need to store the OBS initiation position somewhere in case it is needed again.
                SetOBSFromWaypoint();
@@ -964,11 +1063,10 @@ void DCLGPS::SetOBSFromWaypoint() {
        
        // TODO - base the 180 deg correction on the to/from flag.
        _fromWaypoint = GetPositionOnMagRadial(_activeWaypoint, 10, _obsHeading + 180.0);
-       _fromWaypoint.id = "OBSWP";
+       _fromWaypoint.type = GPS_WP_VIRT;
+       _fromWaypoint.id = "_OBSWP_";
 }
 
-void DCLGPS::MsgPressed() {}
-
 void DCLGPS::CDIFSDIncrease() {
        if(_currentCdiScaleIndex == 0) {
                _currentCdiScaleIndex = _cdiScales.size() - 1;
@@ -990,16 +1088,6 @@ void DCLGPS::DrawChar(char c, int field, int px, int py, bool bold) {
 void DCLGPS::DrawText(const string& s, int field, int px, int py, bool bold) {
 }
 
-void DCLGPS::SetBaroUnits(int n, bool wrap) {
-       if(n < 1) {
-               _baroUnits = (GPSPressureUnits)(wrap ? 3 : 1);
-       } else if(n > 3) {
-               _baroUnits = (GPSPressureUnits)(wrap ? 1 : 3);
-       } else {
-               _baroUnits = (GPSPressureUnits)n;
-       }
-}
-
 void DCLGPS::CreateDefaultFlightPlans() {}
 
 // Get the time to the active waypoint in seconds.
@@ -1051,10 +1139,10 @@ double DCLGPS::GetTimeToWaypoint(const string& id) {
        } else if(id == _activeWaypoint.id) {
                return(_eta);
        } else {
-               bool multi;
-               const GPSWaypoint* wp = FindFirstById(id, multi, true);
+               const GPSWaypoint* wp = FindFirstByExactId(id);
                if(wp == NULL) return(-1.0);
                double distm = GetGreatCircleDistance(_gpsLat, _gpsLon, wp->lat, wp->lon);
+    delete wp;
                return(distm / _groundSpeed_ms);
        }
        return(-1.0);   // Hopefully we never get here!
@@ -1252,353 +1340,93 @@ void DCLGPS::CreateFlightPlan(GPSFlightPlan* fp, vector<string> ids, vector<GPSW
                case GPS_WP_USR:
                        // TODO
                        break;
+               case GPS_WP_VIRT:
+                       // TODO
+                       break;
                }
        }
 }
 
 /***************************************/
 
-const GPSWaypoint* DCLGPS::ActualFindFirstById(const string& id, bool exact) {
-    gps_waypoint_map_const_iterator itr;
-    if(exact) {
-        itr = _waypoints.find(id);
-    } else {
-        itr = _waypoints.lower_bound(id);
+class DCLGPSFilter : public FGPositioned::Filter
+{
+public:
+  virtual bool pass(const FGPositioned* aPos) const {
+    switch (aPos->type()) {
+    case FGPositioned::AIRPORT:
+    // how about heliports and seaports?
+    case FGPositioned::NDB:
+    case FGPositioned::VOR:
+    case FGPositioned::WAYPOINT:
+    case FGPositioned::FIX:
+      break;
+    default: return false; // reject all other types
     }
-    if(itr == _waypoints.end()) {
-        return(NULL);
-    } else {
-               // TODO - don't just return the first one - either return all or the nearest one.
-        return((itr->second)[0]);
-    }
-}
+    return true;
+  }
+};
 
-const GPSWaypoint* DCLGPS::FindFirstById(const string& id, bool &multi, bool exact) {
-       multi = false;
-       if(exact) return(ActualFindFirstById(id, exact));
-       
-       // OK, that was the easy case, now the fuzzy case
-       const GPSWaypoint* w1 = ActualFindFirstById(id);
-       if(w1 == NULL) return(w1);
-       
-       // The non-trivial code from here to the end of the function is all to deal with the fact that
-       // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters).
-       string id2 = id;
-       //string id3 = id+'0';
-       string id4 = id+'A';
-       // Increment the last char to provide the boundary.  Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters
-       //bool alfa = isalpha(id2[id2.size() - 1]);
-       id2[id2.size() - 1] = id2[id2.size() - 1] + 1;
-       const GPSWaypoint* w2 = ActualFindFirstById(id2);
-       //FGAirport* a3 = globals->get_airports()->findFirstById(id3);
-       const GPSWaypoint* w4 = ActualFindFirstById(id4);
-       //cout << "Strings sent were " << id << ", " << id2 << " and " << id4 << '\n';
-       //cout << "Airports returned were (a1, a2, a4): " << a1->getId() << ", " << a2->getId() << ", " << a4->getId() << '\n';
-       //cout << "Pointers were " << a1 << ", " << a2 << ", " << a4 << '\n';
-       
-       // TODO - the below handles the imediately following char OK
-       // eg id = "KD" returns "KDAA" instead of "KD5"
-       // but it doesn't handle numbers / letters further down the string,
-       // eg - id = "I" returns "IA01" instead of "IAN"
-       // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string.
-       // (Currenly fixed with recursion).
-       
-       if(w4 != w2) { // A-Z match - preferred
-               //cout << "A-Z match!\n";
-               if(w4->id.size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<w4->id.size(); ++i) {
-                               if(!isalpha(w4->id[i])) {
-                                       //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstById(w4->id.substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(w4);
-       } else if(w1 != w2) {  // 0-9 match
-               //cout << "0-9 match!\n";
-               if(w1->id.size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<w1->id.size(); ++i) {
-                               if(!isalpha(w1->id[i])) {
-                                       //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstById(w1->id.substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(w1);
-       } else {  // No match
-               return(NULL);
-       }
-       return NULL;
-}
-
-// Host specific lookup functions
-// TODO - add the ASCII / alphabetical stuff from the Atlas version
-FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set.
-       multi = false;
-       //if(exact) return(_overlays->FindFirstVorById(id, exact));
-       
-       nav_list_type nav = globals->get_navlist()->findFirstByIdent(id, FG_NAV_VOR, exact);
-       
-       if(nav.size() > 1) multi = true;
-       //return(nav.empty() ? NULL : *(nav.begin()));
-       
-       // The above is sort of what we want - unfortunately we can't guarantee no NDB/ILS at the moment
-       if(nav.empty()) return(NULL);
-       
-       for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
-               if((*it)->get_type() == 3) return(*it);
-       }
-       return(NULL);   // Shouldn't get here!
-}
-#if 0
-Overlays::NAV* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set.
-       multi = false;
-       if(exact) return(_overlays->FindFirstVorById(id, exact));
-       
-       // OK, that was the easy case, now the fuzzy case
-       Overlays::NAV* n1 = _overlays->FindFirstVorById(id);
-       if(n1 == NULL) return(n1);
-       
-       string id2 = id;
-       string id3 = id+'0';
-       string id4 = id+'A';
-       // Increment the last char to provide the boundary.  Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters
-       bool alfa = isalpha(id2[id2.size() - 1]);
-       id2[id2.size() - 1] = id2[id2.size() - 1] + 1;
-       Overlays::NAV* n2 = _overlays->FindFirstVorById(id2);
-       //Overlays::NAV* n3 = _overlays->FindFirstVorById(id3);
-       //Overlays::NAV* n4 = _overlays->FindFirstVorById(id4);
-       //cout << "Strings sent were " << id << ", " << id2 << ", " << id3 << ", " << id4 << '\n';
-       
-       
-       if(alfa) {
-               if(n1 != n2) { // match
-                       return(n1);
-               } else {
-                       return(NULL);
-               }
-       }
-       
-       /*
-       if(n1 != n2) {
-               // Something matches - the problem is the number/letter preference order is reversed between the GPS and the STL
-               if(n4 != n2) {
-                       // There's a letter match - return that
-                       return(n4);
-               } else {
-                       // By definition we must have a number match
-                       if(n3 == n2) cout << "HELP - LOGIC FLAW in find VOR!\n";
-                       return(n3);
-               }
-       } else {
-               // No match
-               return(NULL);
-       }
-       */
-       return NULL;
+GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
+{
+  DCLGPSFilter filter;
+  FGPositionedRef result = FGPositioned::findNextWithPartialId(NULL, id, &filter);
+  return GPSWaypoint::createFromPositioned(result);
 }
-#endif //0
 
-// TODO - add the ASCII / alphabetical stuff from the Atlas version
-FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set.
-       multi = false;
-       //if(exact) return(_overlays->FindFirstVorById(id, exact));
-       
-       nav_list_type nav = globals->get_navlist()->findFirstByIdent(id, FG_NAV_NDB, exact);
-       
-       if(nav.size() > 1) multi = true;
-       //return(nav.empty() ? NULL : *(nav.begin()));
-       
-       // The above is sort of what we want - unfortunately we can't guarantee no NDB/ILS at the moment
-       if(nav.empty()) return(NULL);
-       
-       for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
-               if((*it)->get_type() == 2) return(*it);
-       }
-       return(NULL);   // Shouldn't get here!
-}
-#if 0
-Overlays::NAV* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set.
-       multi = false;
-       if(exact) return(_overlays->FindFirstNDBById(id, exact));
-       
-       // OK, that was the easy case, now the fuzzy case
-       Overlays::NAV* n1 = _overlays->FindFirstNDBById(id);
-       if(n1 == NULL) return(n1);
-       
-       string id2 = id;
-       string id3 = id+'0';
-       string id4 = id+'A';
-       // Increment the last char to provide the boundary.  Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters
-       bool alfa = isalpha(id2[id2.size() - 1]);
-       id2[id2.size() - 1] = id2[id2.size() - 1] + 1;
-       Overlays::NAV* n2 = _overlays->FindFirstNDBById(id2);
-       //Overlays::NAV* n3 = _overlays->FindFirstNDBById(id3);
-       //Overlays::NAV* n4 = _overlays->FindFirstNDBById(id4);
-       //cout << "Strings sent were " << id << ", " << id2 << ", " << id3 << ", " << id4 << '\n';
-       
-       
-       if(alfa) {
-               if(n1 != n2) { // match
-                       return(n1);
-               } else {
-                       return(NULL);
-               }
-       }
-       
-       /*
-       if(n1 != n2) {
-               // Something matches - the problem is the number/letter preference order is reversed between the GPS and the STL
-               if(n4 != n2) {
-                       // There's a letter match - return that
-                       return(n4);
-               } else {
-                       // By definition we must have a number match
-                       if(n3 == n2) cout << "HELP - LOGIC FLAW in find VOR!\n";
-                       return(n3);
-               }
-       } else {
-               // No match
-               return(NULL);
-       }
-       */
-       return NULL;
+GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const
+{
+  SGGeod pos(SGGeod::fromRad(_lon, _lat));
+  FGPositionedRef result = FGPositioned::findClosestWithIdent(id, pos);
+  return GPSWaypoint::createFromPositioned(result);
 }
-#endif //0
 
 // TODO - add the ASCII / alphabetical stuff from the Atlas version
-const FGFix* DCLGPS::FindFirstIntById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set, and indeed can't be
-       // since FG can only map one Fix per ID at the moment.
-       multi = false;
-       if(exact) return(globals->get_fixlist()->findFirstByIdent(id, exact));
-       
-       const FGFix* f1 = globals->get_fixlist()->findFirstByIdent(id, exact);
-       if(f1 == NULL) return(f1);
-       
-       // The non-trivial code from here to the end of the function is all to deal with the fact that
-       // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters).
-       // It is copied from the airport version which is definately needed, but at present I'm not actually 
-       // sure if any fixes in FG or real-life have numbers in them!
-       string id2 = id;
-       //string id3 = id+'0';
-       string id4 = id+'A';
-       // Increment the last char to provide the boundary.  Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters
-       //bool alfa = isalpha(id2[id2.size() - 1]);
-       id2[id2.size() - 1] = id2[id2.size() - 1] + 1;
-       const FGFix* f2 = globals->get_fixlist()->findFirstByIdent(id2);
-       //const FGFix* a3 = globals->get_fixlist()->findFirstByIdent(id3);
-       const FGFix* f4 = globals->get_fixlist()->findFirstByIdent(id4);
-       
-       // TODO - the below handles the imediately following char OK
-       // eg id = "KD" returns "KDAA" instead of "KD5"
-       // but it doesn't handle numbers / letters further down the string,
-       // eg - id = "I" returns "IA01" instead of "IAN"
-       // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string.
-       // (Currenly fixed with recursion).
-       
-       if(f4 != f2) { // A-Z match - preferred
-               //cout << "A-Z match!\n";
-               if(f4->get_ident().size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<f4->get_ident().size(); ++i) {
-                               if(!isalpha(f4->get_ident()[i])) {
-                                       //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstIntById(f4->get_ident().substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(f4);
-       } else if(f1 != f2) {  // 0-9 match
-               //cout << "0-9 match!\n";
-               if(f1->get_ident().size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<f1->get_ident().size(); ++i) {
-                               if(!isalpha(f1->get_ident()[i])) {
-                                       //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstIntById(f1->get_ident().substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(f1);
-       } else {  // No match
-               return(NULL);
-       }
-               
-       return NULL;    // Don't think we can ever get here.
-}
-
-const FGAirport* DCLGPS::FindFirstAptById(const string& id, bool &multi, bool exact) {
-       // NOTE - at the moment multi is never set.
-       //cout << "FindFirstAptById, id = " << id << '\n';
-       multi = false;
-       if(exact) return(globals->get_airports()->findFirstById(id, exact));
-       
-       // OK, that was the easy case, now the fuzzy case
-       const FGAirport* a1 = globals->get_airports()->findFirstById(id);
-       if(a1 == NULL) return(a1);
-       
-       // The non-trivial code from here to the end of the function is all to deal with the fact that
-       // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters).
-       string id2 = id;
-       //string id3 = id+'0';
-       string id4 = id+'A';
-       // Increment the last char to provide the boundary.  Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters
-       //bool alfa = isalpha(id2[id2.size() - 1]);
-       id2[id2.size() - 1] = id2[id2.size() - 1] + 1;
-       const FGAirport* a2 = globals->get_airports()->findFirstById(id2);
-       //FGAirport* a3 = globals->get_airports()->findFirstById(id3);
-       const FGAirport* a4 = globals->get_airports()->findFirstById(id4);
-       //cout << "Strings sent were " << id << ", " << id2 << " and " << id4 << '\n';
-       //cout << "Airports returned were (a1, a2, a4): " << a1->getId() << ", " << a2->getId() << ", " << a4->getId() << '\n';
-       //cout << "Pointers were " << a1 << ", " << a2 << ", " << a4 << '\n';
-       
-       // TODO - the below handles the imediately following char OK
-       // eg id = "KD" returns "KDAA" instead of "KD5"
-       // but it doesn't handle numbers / letters further down the string,
-       // eg - id = "I" returns "IA01" instead of "IAN"
-       // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string.
-       // (Currenly fixed with recursion).
-       
-       if(a4 != a2) { // A-Z match - preferred
-               //cout << "A-Z match!\n";
-               if(a4->getId().size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<a4->getId().size(); ++i) {
-                               if(!isalpha(a4->getId()[i])) {
-                                       //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstAptById(a4->getId().substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(a4);
-       } else if(a1 != a2) {  // 0-9 match
-               //cout << "0-9 match!\n";
-               if(a1->getId().size() - id.size() > 2) {
-                       // Check for numbers further on
-                       for(unsigned int i=id.size(); i<a1->getId().size(); ++i) {
-                               if(!isalpha(a1->getId()[i])) {
-                                       //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n';
-                                       return(FindFirstAptById(a1->getId().substr(0, i), multi, exact));
-                               }
-                       }
-               }
-               return(a1);
-       } else {  // No match
-               return(NULL);
-       }
-               
-       return NULL;
-}
-
-FGNavRecord* DCLGPS::FindClosestVor(double lat_rad, double lon_rad) {
-       return(globals->get_navlist()->findClosest(lon_rad, lat_rad, 0.0, FG_NAV_VOR));
+FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty, bool &multi, bool exact)
+{
+  multi = false;
+  FGPositioned::TypeFilter filter(ty);
+  
+  if (exact) {
+    FGPositioned::List matches = 
+      FGPositioned::findAllWithIdent(id, &filter);
+    FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
+    multi = (matches.size() > 1);
+    return matches.empty() ? NULL : matches.front().ptr();
+  }
+  
+  return FGPositioned::findNextWithPartialId(NULL, id, &filter);
+}
+
+FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact)
+{
+  return dynamic_cast<FGNavRecord*>(FindTypedFirstById(id, FGPositioned::VOR, multi, exact));
+}
+
+FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact)
+{
+  return dynamic_cast<FGNavRecord*>(FindTypedFirstById(id, FGPositioned::NDB, multi, exact));
+}
+
+const FGFix* DCLGPS::FindFirstIntById(const string& id, bool &multi, bool exact)
+{
+  return dynamic_cast<FGFix*>(FindTypedFirstById(id, FGPositioned::FIX, multi, exact));
+}
+
+const FGAirport* DCLGPS::FindFirstAptById(const string& id, bool &multi, bool exact)
+{
+  return dynamic_cast<FGAirport*>(FindTypedFirstById(id, FGPositioned::AIRPORT, multi, exact));
+}
+
+FGNavRecord* DCLGPS::FindClosestVor(double lat_rad, double lon_rad) {  
+  FGPositioned::TypeFilter filter(FGPositioned::VOR);
+  double cutoff = 1000; // nautical miles
+  FGPositionedRef v = FGPositioned::findClosest(SGGeod::fromRad(lon_rad, lat_rad), cutoff, &filter);
+  if (!v) {
+    return NULL;
+  }
+  
+  return dynamic_cast<FGNavRecord*>(v.ptr());
 }
 
 //----------------------------------------------------------------------------------------------------------