]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/navdb.cxx
Reduce severity of a failure to create a marker beacon due to an unknown runway from...
[flightgear.git] / src / Navaids / navdb.cxx
1 // navdb.cxx -- top level navaids management routines
2 //
3 // Written by Curtis Olson, started May 2004.
4 //
5 // Copyright (C) 2004  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <simgear/compiler.h>
28
29 #include <string>
30
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/math/sg_geodesy.hxx>
33 #include <simgear/misc/strutils.hxx>
34 #include <simgear/misc/sg_path.hxx>
35 #include <simgear/structure/exception.hxx>
36 #include <simgear/misc/sgstream.hxx>
37 #include <simgear/props/props_io.hxx>
38
39 #include "navrecord.hxx"
40 #include "navlist.hxx"
41 #include "navdb.hxx"
42 #include <Main/globals.hxx>
43 #include <Navaids/markerbeacon.hxx>
44 #include <Airports/simple.hxx>
45 #include <Airports/runways.hxx>
46 #include <Airports/xmlloader.hxx>
47 #include <Main/fg_props.hxx>
48
49 using std::string;
50 using std::vector;
51
52 typedef std::map<FGAirport*, SGPropertyNode_ptr> AirportPropertyMap;
53
54 static AirportPropertyMap static_airportIlsData;
55
56 static FGPositioned::Type
57 mapRobinTypeToFGPType(int aTy)
58 {
59   switch (aTy) {
60  // case 1:
61   case 2: return FGPositioned::NDB;
62   case 3: return FGPositioned::VOR;
63   case 4: return FGPositioned::ILS;
64   case 5: return FGPositioned::LOC;
65   case 6: return FGPositioned::GS;
66   case 12:
67   case 13: return FGPositioned::DME;
68   case 99: return FGPositioned::INVALID; // end-of-file code
69   default:
70     throw sg_range_exception("Got a nav.dat type we don't recognize", "FGNavRecord::createFromStream");
71   }
72 }
73
74 static FGNavRecord* createNavFromStream(std::istream& aStream)
75 {
76   int rawType;
77   aStream >> rawType;
78   if (aStream.eof() || (rawType == 99)) {
79     return NULL; // happens with, eg, carrier_nav.dat
80   }
81   
82   double lat, lon, elev_ft, multiuse;
83   int freq, range;
84   std::string name, ident;
85   aStream >> lat >> lon >> elev_ft >> freq >> range >> multiuse >> ident;
86   getline(aStream, name);
87   
88   SGGeod pos(SGGeod::fromDegFt(lon, lat, elev_ft));
89   name = simgear::strutils::strip(name);
90   
91   if ((rawType >= 7) && (rawType <= 9)) {
92     // marker beacons use a different run-time class now
93      FGMarkerBeaconRecord::create(rawType, name, pos);
94      return NULL; // not a nav-record, but that's okay
95   }
96   
97   FGPositioned::Type type = mapRobinTypeToFGPType(rawType);
98   if (type == FGPositioned::INVALID) {
99     return NULL;
100   }
101   
102   // silently multiply adf frequencies by 100 so that adf
103   // vs. nav/loc frequency lookups can use the same code.
104   if (type == FGPositioned::NDB) {
105     freq *= 100;
106   }
107   
108   return new FGNavRecord(type, ident, name, pos,
109     freq, range, multiuse);
110 }
111
112 // load and initialize the navigational databases
113 bool fgNavDBInit( FGNavList *navlist, FGNavList *loclist, FGNavList *gslist,
114                   FGNavList *dmelist, 
115                   FGNavList *tacanlist, FGNavList *carrierlist,
116                   FGTACANList *channellist)
117 {
118     SG_LOG(SG_GENERAL, SG_INFO, "Loading Navaid Databases");
119
120     SGPath path( globals->get_fg_root() );
121     path.append( "Navaids/nav.dat" );
122
123     sg_gzifstream in( path.str() );
124     if ( !in.is_open() ) {
125         SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
126         exit(-1);
127     }
128
129     // skip first two lines
130     in >> skipeol;
131     in >> skipeol;
132
133     while (!in.eof()) {
134       FGNavRecord *r = createNavFromStream(in);
135       if (!r) {
136         continue;
137       }
138       
139       switch (r->type()) {
140       case FGPositioned::NDB:
141       case FGPositioned::VOR:
142         navlist->add(r);
143         break;
144         
145       case FGPositioned::ILS:
146       case FGPositioned::LOC:
147         loclist->add(r);
148         break;
149         
150       case FGPositioned::GS:
151         gslist->add(r);
152         break;
153       
154       case FGPositioned::DME:
155       {
156         dmelist->add(r);
157         string::size_type loc1= r->name().find( "TACAN", 0 );
158         string::size_type loc2 = r->name().find( "VORTAC", 0 );
159                        
160         if( loc1 != string::npos || loc2 != string::npos) {
161           tacanlist->add(r);
162         }
163
164         break;
165       }
166       
167       default:
168         throw sg_range_exception("got unsupported NavRecord type", "fgNavDBInit");
169       }
170
171       in >> skipcomment;
172     } // of stream data loop
173
174 // load the carrier navaids file
175     
176     string file, name;
177     path = globals->get_fg_root() ;
178     path.append( "Navaids/carrier_nav.dat" );
179     
180     file = path.str();
181     SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
182     
183     sg_gzifstream incarrier( path.str() );
184     
185     if ( !incarrier.is_open() ) {
186         SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
187         exit(-1);
188     }
189     
190     // skip first two lines
191     //incarrier >> skipeol;
192     //incarrier >> skipeol;
193     
194     while ( ! incarrier.eof() ) {
195       FGNavRecord *r = createNavFromStream(incarrier);
196       if (!r) {
197         continue;
198       }
199       
200       carrierlist->add (r);
201     } // end while
202
203 // end loading the carrier navaids file
204
205 // load the channel/freqency file
206     string channel, freq;
207     path="";
208     path = globals->get_fg_root();
209     path.append( "Navaids/TACAN_freq.dat" );
210     
211     SG_LOG( SG_GENERAL, SG_INFO, "opening file: " << path.str() );
212         
213     sg_gzifstream inchannel( path.str() );
214     
215     if ( !inchannel.is_open() ) {
216         SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
217         exit(-1);
218     }
219     
220     // skip first line
221     inchannel >> skipeol;
222     while ( ! inchannel.eof() ) {
223         FGTACANRecord *r = new FGTACANRecord;
224         inchannel >> (*r);
225         channellist->add ( r );
226         //cout << "channel = " << r->get_channel() ;
227         //cout << " freq = " << r->get_freq() << endl;
228         
229     } // end while
230
231  
232  // end ReadChanFile
233
234
235   // flush all the parsed ils.xml data, we don't need it anymore,
236   // since it's been meregd into the FGNavRecords
237     static_airportIlsData.clear();
238
239     return true;
240 }
241
242 SGPropertyNode* ilsDataForRunwayAndNavaid(FGRunway* aRunway, const std::string& aNavIdent)
243 {
244   if (!aRunway) {
245     return NULL;
246   }
247   
248   FGAirport* apt = aRunway->airport();
249 // find (or load) the airprot ILS data
250   AirportPropertyMap::iterator it = static_airportIlsData.find(apt);
251   if (it == static_airportIlsData.end()) {
252     SGPath path;
253     if (!XMLLoader::findAirportData(apt->ident(), "ils", path)) {
254     // no ils.xml file for this airpot, insert a NULL entry so we don't
255     // check again
256       static_airportIlsData.insert(it, std::make_pair(apt, SGPropertyNode_ptr()));
257       return NULL;
258     }
259     
260     SGPropertyNode_ptr rootNode = new SGPropertyNode;
261     readProperties(path.str(), rootNode);
262     it = static_airportIlsData.insert(it, std::make_pair(apt, rootNode));
263   } // of ils.xml file not loaded
264   
265   if (!it->second) {
266     return NULL;
267   }
268   
269 // find the entry matching the runway
270   SGPropertyNode* runwayNode, *ilsNode;
271   for (int i=0; (runwayNode = it->second->getChild("runway", i)) != NULL; ++i) {
272     for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) {
273       // must match on both nav-ident and runway ident, to support the following:
274       // - runways with multiple distinct ILS installations (KEWD, for example)
275       // - runways where both ends share the same nav ident (LFAT, for example)
276       if ((ilsNode->getStringValue("nav-id") == aNavIdent) &&
277           (ilsNode->getStringValue("rwy") == aRunway->ident())) 
278       {
279         return ilsNode;
280       }
281     } // of ILS iteration
282   } // of runway iteration
283
284   return NULL;
285 }
286
287 FGRunway* getRunwayFromName(const std::string& aName)
288 {
289   vector<string> parts = simgear::strutils::split(aName);
290   if (parts.size() < 2) {
291     SG_LOG(SG_GENERAL, SG_WARN, "getRunwayFromName: malformed name:" << aName);
292     return NULL;
293   }
294   
295   const FGAirport* apt = fgFindAirportID(parts[0]);
296   if (!apt) {
297     SG_LOG(SG_GENERAL, SG_DEBUG, "navaid " << aName << " associated with bogus airport ID:" << parts[0]);
298     return NULL;
299   }
300   
301   if (!apt->hasRunwayWithIdent(parts[1])) {
302     SG_LOG(SG_GENERAL, SG_DEBUG, "navaid " << aName << " associated with bogus runway ID:" << parts[1]);
303     return NULL;
304   }
305
306   return apt->getRunwayByIdent(parts[1]);
307 }