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