]> git.mxchange.org Git - flightgear.git/commitdiff
KLN89: Remove hardwired instrument approach, and add initial support for loading...
authorDave Luff <daveluff@ntlworld.com>
Sat, 4 Dec 2010 16:55:26 +0000 (16:55 +0000)
committerDave Luff <daveluff@ntlworld.com>
Sat, 4 Dec 2010 16:55:26 +0000 (16:55 +0000)
src/Instrumentation/dclgps.cxx
src/Instrumentation/dclgps.hxx

index 4ceb2b023de7ea87cf1f6fd6f1cbed88795259d4..92dcb21325bb442152701d02cf6d311005f281ed 100644 (file)
 #include "dclgps.hxx"
 
 #include <simgear/sg_inlines.h>
+#include <simgear/misc/sg_path.hxx>
 #include <simgear/timing/sg_time.hxx>
 #include <simgear/magvar/magvar.hxx>
+#include <simgear/structure/exception.hxx>
 
 #include <Main/fg_props.hxx>
 #include <Navaids/fix.hxx>
@@ -35,6 +37,7 @@
 #include <Airports/simple.hxx>
 #include <Airports/runways.hxx>
 
+#include <fstream>
 #include <iostream>
 
 using namespace std;
@@ -240,95 +243,7 @@ void DCLGPS::init() {
        // Not sure if this should be here, but OK for now.
        CreateDefaultFlightPlans();
 
-       // Hack - hardwire some instrument approaches for development.
-       // These will shortly be replaced by a routine to read ARINC data from file instead.
-       FGNPIAP* iap;
-       GPSWaypoint* wp;
-       GPSFlightPlan* fp;
-       const GPSWaypoint* cwp;
-       
-       iap = new FGNPIAP;
-       iap->_aptIdent = "KHAF";
-       iap->_ident = "R12-Y";
-       iap->_name = ExpandSIAPIdent(iap->_ident);
-       iap->_rwyStr = "12";
-       iap->_approachRoutes.clear();
-       iap->_IAP.clear();
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "GOBBS";
-       // Nasty using the find any function here, but it saves converting data from FGFix etc. 
-       cwp = FindFirstByExactId(wp->id);
-       if(cwp) {
-               *wp = *cwp;
-               wp->appType = GPS_IAF;
-               fp = new GPSFlightPlan;
-               fp->waypoints.push_back(wp);
-       } else {
-               //cout << "Unable to find waypoint " << wp->id << '\n';
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "FUJCE";
-       cwp = FindFirstByExactId(wp->id);
-       if(cwp) {
-               *wp = *cwp;
-               wp->appType = GPS_IAP;
-               fp->waypoints.push_back(wp);
-               iap->_approachRoutes.push_back(fp);
-               iap->_IAP.push_back(wp);
-       } else {
-               //cout << "Unable to find waypoint " << wp->id << '\n';
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "JEVXY";
-       cwp = FindFirstByExactId(wp->id);
-       if(cwp) {
-               *wp = *cwp;
-               wp->appType = GPS_FAF;
-               iap->_IAP.push_back(wp);
-       } else {
-               //cout << "Unable to find waypoint " << wp->id << '\n';
-       }
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "RW12";
-       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, which appears to be standard for most approaches.
-               const FGAirport* apt = fgFindAirportID(iap->_aptIdent);
-               if(apt) {
-                       // TODO - sanity check the waypoint ID to ensure we have a double digit number
-                       FGRunway* rwy = apt->getRunwayByIdent(wp->id.substr(2, 2));
-                       if(rwy) {
-                               wp->lat = rwy->begin().getLatitudeRad();
-                               wp->lon = rwy->begin().getLongitudeRad();
-                       }
-               }
-       } else {
-               cwp = FindFirstByExactId(wp->id);
-               if(cwp) {
-                       *wp = *cwp;
-                       wp->appType = GPS_MAP;
-               } else {
-                       //cout << "Unable to find waypoint " << wp->id << '\n';
-               }
-       }
-       iap->_IAP.push_back(wp);
-       // -------
-       wp = new GPSWaypoint;
-       wp->id = "SEEMS";
-       cwp = FindFirstByExactId(wp->id);
-       if(cwp) {
-               *wp = *cwp;
-               wp->appType = GPS_MAHP;
-               iap->_IAP.push_back(wp);
-       } else {
-               //cout << "Unable to find waypoint " << wp->id << '\n';
-       }
-       // -------
-       _np_iap[iap->_aptIdent].push_back(iap);
+       LoadApproachData();
 }
 
 void DCLGPS::bind() {
@@ -685,6 +600,362 @@ string DCLGPS::ExpandSIAPIdent(const string& ident) {
        return(name);
 }
 
+/*
+       Load instrument approaches from an ARINC 424-18 file.
+       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;
+       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) {
+                                                                               try {
+                                                                                       // TODO - sanity check the waypoint ID to ensure we have a double digit number
+                                                                                       FGRunway* rwy = apt->getRunwayByIdent(w.id.substr(2, 2));
+                                                                                       w.lat = rwy->begin().getLatitudeRad();
+                                                                                       w.lon = rwy->begin().getLongitudeRad();
+                                                                               } catch(const sg_exception&) {
+                                                                                       SG_LOG(SG_GENERAL, SG_WARN, "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent);
+                                                                                       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; 
 }
@@ -735,6 +1006,7 @@ void DCLGPS::DtoInitiate(const string& s) {
                _fromWaypoint.id = "DTOWP";
                delete wp;
        } else {
+               // TODO - Should bring up the user waypoint page.
                _dto = false;
        }
 }
index 295a7ad6436d8402db30461a5e09592e9b430b52..9164dbad2665360be6c1172fb4cf30bacf4c7938 100644 (file)
@@ -342,6 +342,8 @@ protected:
 
 
 protected:
+       void LoadApproachData();
+
        // Find first of any type of waypoint by id.  (TODO - Possibly we should return multiple waypoints here).
        GPSWaypoint* FindFirstById(const string& id) const;
        GPSWaypoint* FindFirstByExactId(const string& id) const;