]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/navlist.cxx
Interim windows build fix
[flightgear.git] / src / Navaids / navlist.cxx
1 // navlist.cxx -- navaids management class
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000  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
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <cassert>
29 #include <boost/foreach.hpp>
30 #include <algorithm>
31
32 #include <simgear/debug/logstream.hxx>
33 #include <simgear/math/sg_geodesy.hxx>
34 #include <simgear/sg_inlines.h>
35
36 #include "navlist.hxx"
37
38 #include <Airports/runways.hxx>
39 #include <Navaids/NavDataCache.hxx>
40 #include <Navaids/navrecord.hxx>
41
42 using std::string;
43
44 namespace { // anonymous
45
46 class NavRecordDistanceSortPredicate
47 {
48 public:
49   NavRecordDistanceSortPredicate( const SGGeod & position ) :
50   _position(SGVec3d::fromGeod(position)) {}
51   
52   bool operator()( const nav_rec_ptr & n1, const nav_rec_ptr & n2 )
53   {
54     if( n1 == NULL || n2 == NULL ) return false;
55     return distSqr(n1->cart(), _position) < distSqr(n2->cart(), _position);
56   }
57 private:
58   SGVec3d _position;
59   
60 };
61
62 // discount navids if they conflict with another on the same frequency
63 // this only applies to navids associated with opposite ends of a runway,
64 // with matching frequencies.
65 bool navidUsable(FGNavRecord* aNav, const SGGeod &aircraft)
66 {
67   FGRunway* r(aNav->runway());
68   if (!r || !r->reciprocalRunway()) {
69     return true;
70   }
71   
72   // check if the runway frequency is paired
73   FGNavRecord* locA = r->ILS();
74   FGNavRecord* locB = r->reciprocalRunway()->ILS();
75   
76   if (!locA || !locB || (locA->get_freq() != locB->get_freq())) {
77     return true; // not paired, ok
78   }
79   
80   // okay, both ends have an ILS, and they're paired. We need to select based on
81   // aircraft position. What we're going to use is *runway* (not navid) position,
82   // ie whichever runway end we are closer too. This makes back-course / missed
83   // approach behaviour incorrect, but that's the price we accept.
84   double crs = SGGeodesy::courseDeg(aircraft, r->geod());
85   double hdgDiff = crs - r->headingDeg();
86   SG_NORMALIZE_RANGE(hdgDiff, -180.0, 180.0);
87   return (fabs(hdgDiff) < 90.0);
88 }
89   
90 } // of anonymous namespace
91
92 // FGNavList ------------------------------------------------------------------
93
94
95 //------------------------------------------------------------------------------
96 FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
97 {
98   if (type == FGPositioned::INVALID) {
99     _mintype = FGPositioned::NDB;
100     _maxtype = FGPositioned::GS;
101   } else {
102     _mintype = _maxtype = type;
103   }
104 }
105
106 //------------------------------------------------------------------------------
107 FGNavList::TypeFilter::TypeFilter( const FGPositioned::Type minType,
108                                    const FGPositioned::Type maxType ):
109   _mintype(minType),
110   _maxtype(maxType)
111 {
112 }
113
114 //------------------------------------------------------------------------------
115 bool FGNavList::TypeFilter::fromTypeString(const std::string& type)
116 {
117   FGPositioned::Type t;
118   if(      type == "any"  ) t = FGPositioned::INVALID;
119   else if( type == "fix"  ) t = FGPositioned::FIX;
120   else if( type == "vor"  ) t = FGPositioned::VOR;
121   else if( type == "ndb"  ) t = FGPositioned::NDB;
122   else if( type == "ils"  ) t = FGPositioned::ILS;
123   else if( type == "dme"  ) t = FGPositioned::DME;
124   else if( type == "tacan") t = FGPositioned::TACAN;
125   else                      return false;
126
127   _mintype = _maxtype = t;
128
129   return true;
130 }
131
132 /**
133  * Filter returning Tacan stations. Checks for both pure TACAN stations
134  * but also co-located VORTACs. This is done by searching for DMEs whose
135  * name indicates they are a TACAN or VORTAC; not a great solution.
136  */
137 class TacanFilter : public FGNavList::TypeFilter
138 {
139 public:
140   TacanFilter() :
141     TypeFilter(FGPositioned::DME, FGPositioned::TACAN)
142   {
143   }
144   
145   virtual bool pass(FGPositioned* pos) const
146   {
147     if (pos->type() == FGPositioned::TACAN) {
148       return true;
149     }
150     
151     assert(pos->type() == FGPositioned::DME);
152     string::size_type loc1 = pos->name().find( "TACAN" );
153     string::size_type loc2 = pos->name().find( "VORTAC" );
154     return (loc1 != string::npos) || (loc2 != string::npos);
155   }
156 };
157
158 FGNavList::TypeFilter* FGNavList::locFilter()
159 {
160   static TypeFilter tf(FGPositioned::ILS, FGPositioned::LOC);
161   return &tf;
162 }
163
164 FGNavList::TypeFilter* FGNavList::ndbFilter()
165 {
166   static TypeFilter tf(FGPositioned::NDB);
167   return &tf;
168 }
169
170 FGNavList::TypeFilter* FGNavList::navFilter()
171 {
172   static TypeFilter tf(FGPositioned::VOR, FGPositioned::LOC);
173   return &tf;
174 }
175
176 FGNavList::TypeFilter* FGNavList::tacanFilter()
177 {
178   static TacanFilter tf;
179   return &tf;
180 }
181
182 FGNavList::TypeFilter* FGNavList::mobileTacanFilter()
183 {
184   static TypeFilter tf(FGPositioned::MOBILE_TACAN);
185   return &tf;
186 }
187
188 FGNavRecordRef FGNavList::findByFreq( double freq,
189                                       const SGGeod& position,
190                                       TypeFilter* filter )
191 {
192   flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
193   int freqKhz = static_cast<int>(freq * 100 + 0.5);
194   PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, position, filter));
195   if (stations.empty()) {
196     return NULL;
197   }
198   
199 // now walk the (sorted) results list to find a usable, in-range navaid
200   SGVec3d acCart(SGVec3d::fromGeod(position));
201   double min_dist
202     = FG_NAV_MAX_RANGE*SG_NM_TO_METER*FG_NAV_MAX_RANGE*SG_NM_TO_METER;
203     
204   BOOST_FOREACH(PositionedID id, stations) {
205     FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
206     if (!filter->pass(station)) {
207       continue;
208     }
209     
210     double d2 = distSqr(station->cart(), acCart);
211     if (d2 > min_dist) {
212     // since results are sorted by proximity, as soon as we pass the
213     // distance cutoff we're done - fall out and return NULL
214       break;
215     }
216     
217     if (navidUsable(station, position)) {
218       return station;
219     }
220   }
221     
222 // fell out of the loop, no usable match
223   return NULL;
224 }
225
226 FGNavRecordRef FGNavList::findByFreq(double freq, TypeFilter* filter)
227 {
228   flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
229   int freqKhz = static_cast<int>(freq * 100 + 0.5);
230   PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, filter));
231   if (stations.empty()) {
232     return NULL;
233   }
234
235   BOOST_FOREACH(PositionedID id, stations) {
236     FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
237     if (filter->pass(station)) {
238       return station;
239     }
240   }
241
242   return NULL;
243 }
244
245 nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position,
246                                        TypeFilter* filter)
247 {
248   nav_list_type stations;
249   
250   flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
251   int freqKhz = static_cast<int>(freq * 1000 + 0.5);
252   PositionedIDVec ids(cache->findNavaidsByFreq(freqKhz, position, filter));
253   
254   BOOST_FOREACH(PositionedID id, ids) {
255     FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
256     if (!filter->pass(station)) {
257       continue;
258     }
259
260     stations.push_back(station);
261   }
262   
263   return stations;
264 }
265
266 nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq,
267                                             TypeFilter* filter)
268 {
269   nav_list_type reply;
270   int f = (int)(freq*100.0 + 0.5);
271   
272   FGPositionedList stations = FGPositioned::findAllWithIdent(ident, filter);
273   BOOST_FOREACH(FGPositionedRef ref, stations) {
274     FGNavRecord* nav = static_cast<FGNavRecord*>(ref.ptr());
275     if ( f <= 0.0 || nav->get_freq() == f) {
276       reply.push_back( nav );
277     }
278   }
279
280   return reply;
281 }
282
283 // Given an Ident and optional frequency and type ,
284 // return a list of matching stations sorted by distance to the given position
285 nav_list_type FGNavList::findByIdentAndFreq( const SGGeod & position,
286         const std::string& ident, const double freq,
287                                             TypeFilter* filter)
288 {
289     nav_list_type reply = findByIdentAndFreq( ident, freq, filter );
290     NavRecordDistanceSortPredicate sortPredicate( position );
291     std::sort( reply.begin(), reply.end(), sortPredicate );
292
293     return reply;
294 }
295
296 // FGTACANList ----------------------------------------------------------------
297
298 FGTACANList::FGTACANList( void )
299 {
300 }
301
302
303 FGTACANList::~FGTACANList( void )
304 {
305 }
306
307
308 bool FGTACANList::init()
309 {
310     return true;
311 }
312
313
314 // add an entry to the lists
315 bool FGTACANList::add( FGTACANRecord *c )
316 {
317     ident_channels[c->get_channel()].push_back(c);
318     return true;
319 }
320
321
322 // Given a TACAN Channel return the first matching frequency
323 FGTACANRecord *FGTACANList::findByChannel( const string& channel )
324 {
325     const tacan_list_type& stations = ident_channels[channel];
326     SG_LOG( SG_NAVAID, SG_DEBUG, "findByChannel " << channel<< " size " << stations.size() );
327
328     if (!stations.empty()) {
329         return stations[0];
330     }
331     return NULL;
332 }
333
334