1 // navlist.cxx -- navaids management class
3 // Written by Curtis Olson, started April 2000.
5 // Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
29 #include <boost/foreach.hpp>
32 #include <simgear/debug/logstream.hxx>
33 #include <simgear/math/sg_geodesy.hxx>
34 #include <simgear/sg_inlines.h>
36 #include "navlist.hxx"
38 #include <Airports/runways.hxx>
39 #include <Navaids/NavDataCache.hxx>
40 #include <Navaids/navrecord.hxx>
44 namespace { // anonymous
46 class NavRecordDistanceSortPredicate
49 NavRecordDistanceSortPredicate( const SGGeod & position ) :
50 _position(SGVec3d::fromGeod(position)) {}
52 bool operator()( const nav_rec_ptr & n1, const nav_rec_ptr & n2 )
54 if( n1 == NULL || n2 == NULL ) return false;
55 return distSqr(n1->cart(), _position) < distSqr(n2->cart(), _position);
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)
67 FGRunway* r(aNav->runway());
68 if (!r || !r->reciprocalRunway()) {
72 // check if the runway frequency is paired
73 FGNavRecord* locA = r->ILS();
74 FGNavRecord* locB = r->reciprocalRunway()->ILS();
76 if (!locA || !locB || (locA->get_freq() != locB->get_freq())) {
77 return true; // not paired, ok
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);
90 } // of anonymous namespace
92 // FGNavList ------------------------------------------------------------------
95 //------------------------------------------------------------------------------
96 FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
98 if (type == FGPositioned::INVALID) {
99 _mintype = FGPositioned::NDB;
100 _maxtype = FGPositioned::GS;
102 _mintype = _maxtype = type;
106 //------------------------------------------------------------------------------
107 FGNavList::TypeFilter::TypeFilter( const FGPositioned::Type minType,
108 const FGPositioned::Type maxType ):
114 //------------------------------------------------------------------------------
115 bool FGNavList::TypeFilter::fromTypeString(const std::string& type)
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;
127 _mintype = _maxtype = t;
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.
137 class TacanFilter : public FGNavList::TypeFilter
141 TypeFilter(FGPositioned::DME, FGPositioned::TACAN)
145 virtual bool pass(FGPositioned* pos) const
147 if (pos->type() == FGPositioned::TACAN) {
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);
158 FGNavList::TypeFilter* FGNavList::locFilter()
160 static TypeFilter tf(FGPositioned::ILS, FGPositioned::LOC);
164 FGNavList::TypeFilter* FGNavList::ndbFilter()
166 static TypeFilter tf(FGPositioned::NDB);
170 FGNavList::TypeFilter* FGNavList::navFilter()
172 static TypeFilter tf(FGPositioned::VOR, FGPositioned::LOC);
176 FGNavList::TypeFilter* FGNavList::tacanFilter()
178 static TacanFilter tf;
182 FGNavList::TypeFilter* FGNavList::mobileTacanFilter()
184 static TypeFilter tf(FGPositioned::MOBILE_TACAN);
188 FGNavRecordRef FGNavList::findByFreq( double freq,
189 const SGGeod& position,
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()) {
199 // now walk the (sorted) results list to find a usable, in-range navaid
200 SGVec3d acCart(SGVec3d::fromGeod(position));
202 = FG_NAV_MAX_RANGE*SG_NM_TO_METER*FG_NAV_MAX_RANGE*SG_NM_TO_METER;
204 BOOST_FOREACH(PositionedID id, stations) {
205 FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
206 if (!filter->pass(station)) {
210 double d2 = distSqr(station->cart(), acCart);
212 // since results are sorted by proximity, as soon as we pass the
213 // distance cutoff we're done - fall out and return NULL
217 if (navidUsable(station, position)) {
222 // fell out of the loop, no usable match
226 FGNavRecordRef FGNavList::findByFreq(double freq, TypeFilter* filter)
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()) {
235 BOOST_FOREACH(PositionedID id, stations) {
236 FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
237 if (filter->pass(station)) {
245 nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position,
248 nav_list_type stations;
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));
254 BOOST_FOREACH(PositionedID id, ids) {
255 FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
256 if (!filter->pass(station)) {
260 stations.push_back(station);
266 nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq,
270 int f = (int)(freq*100.0 + 0.5);
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 );
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,
289 nav_list_type reply = findByIdentAndFreq( ident, freq, filter );
290 NavRecordDistanceSortPredicate sortPredicate( position );
291 std::sort( reply.begin(), reply.end(), sortPredicate );
296 // FGTACANList ----------------------------------------------------------------
298 FGTACANList::FGTACANList( void )
303 FGTACANList::~FGTACANList( void )
308 bool FGTACANList::init()
314 // add an entry to the lists
315 bool FGTACANList::add( FGTACANRecord *c )
317 ident_channels[c->get_channel()].push_back(c);
322 // Given a TACAN Channel return the first matching frequency
323 FGTACANRecord *FGTACANList::findByChannel( const string& channel )
325 const tacan_list_type& stations = ident_channels[channel];
326 SG_LOG( SG_NAVAID, SG_DEBUG, "findByChannel " << channel<< " size " << stations.size() );
328 if (!stations.empty()) {