FlightGear next
navlist.cxx
Go to the documentation of this file.
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 <algorithm>
30
31#include <simgear/debug/logstream.hxx>
32#include <simgear/math/sg_geodesy.hxx>
33#include <simgear/sg_inlines.h>
34
35#include "navlist.hxx"
36
37#include <Airports/runways.hxx>
39#include <Navaids/navrecord.hxx>
40
41using std::string;
42
43namespace { // anonymous
44
45class NavRecordDistanceSortPredicate
46{
47public:
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 }
56private:
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.
64bool 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//------------------------------------------------------------------------------
104
105//------------------------------------------------------------------------------
112
113//------------------------------------------------------------------------------
114bool FGNavList::TypeFilter::fromTypeString(const std::string& type)
115{
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
137{
138public:
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
162
164{
165 static TypeFilter tf(FGPositioned::NDB);
166 return &tf;
167}
168
174
176{
177 static TacanFilter tf;
178 return &tf;
179}
180
186
188 const SGGeod& position,
189 TypeFilter* filter )
190{
192 int freqKhz = static_cast<int>(freq * 100 + 0.5);
193 PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, position, filter));
194 if (stations.empty()) {
195 return NULL;
196 }
197
198// now walk the (sorted) results list to find a usable, in-range navaid
199 SGVec3d acCart(SGVec3d::fromGeod(position));
200 double min_dist
201 = FG_NAV_MAX_RANGE*SG_NM_TO_METER*FG_NAV_MAX_RANGE*SG_NM_TO_METER;
202
203 for (auto id : stations) {
205 if (filter && !filter->pass(station)) {
206 continue;
207 }
208
209 double d2 = distSqr(station->cart(), acCart);
210 if (d2 > min_dist) {
211 // since results are sorted by proximity, as soon as we pass the
212 // distance cutoff we're done - fall out and return NULL
213 break;
214 }
215
216 if (navidUsable(station, position)) {
217 return station;
218 }
219 }
220
221// fell out of the loop, no usable match
222 return NULL;
223}
224
226{
228 int freqKhz = static_cast<int>(freq * 100 + 0.5);
229 PositionedIDVec stations(cache->findNavaidsByFreq(freqKhz, filter));
230 if (stations.empty()) {
231 return NULL;
232 }
233
234 for (auto id : stations) {
236 if (filter->pass(station)) {
237 return station;
238 }
239 }
240
241 return NULL;
242}
243
244nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position,
245 TypeFilter* filter)
246{
247 nav_list_type stations;
248
250 // note this frequency is passed in 'database units', which depend on the
251 // type of navaid being requested
252 int f = static_cast<int>(freq * 100 + 0.5);
253 PositionedIDVec ids(cache->findNavaidsByFreq(f, position, filter));
254
255 for (PositionedID id : ids) {
257 if (!filter->pass(station)) {
258 continue;
259 }
260
261 stations.push_back(station);
262 }
263
264 return stations;
265}
266
267nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq,
268 TypeFilter* filter)
269{
270 nav_list_type reply;
271 int f = (int)(freq*100.0 + 0.5);
272
273 FGPositionedList stations = FGPositioned::findAllWithIdent(ident, filter);
274 for (auto ref : stations) {
275 FGNavRecord* nav = static_cast<FGNavRecord*>(ref.ptr());
276 if ( f <= 0.0 || nav->get_freq() == f) {
277 reply.push_back( nav );
278 }
279 }
280
281 return reply;
282}
283
284// Given an Ident and optional frequency and type ,
285// return a list of matching stations sorted by distance to the given position
287 const std::string& ident, const double freq,
288 TypeFilter* filter)
289{
290 nav_list_type reply = findByIdentAndFreq( ident, freq, filter );
291 NavRecordDistanceSortPredicate sortPredicate( position );
292 std::sort( reply.begin(), reply.end(), sortPredicate );
293
294 return reply;
295}
296
297// FGTACANList ----------------------------------------------------------------
298
300{
301}
302
303
307
308
310{
311 return true;
312}
313
314
315// add an entry to the lists
317{
318 ident_channels[c->get_channel()].push_back(c);
319 return true;
320}
321
322/*
323 * ref: 070031b3c01f64a44433e8cce742373f4c76b7ac
324 * The ground reply frequencies are used to keep channels 1-16 and 60-69 out of the VOR frequency range as those channels
325 * don't have VORs associated with them.
326 * - this method will therefore only be able to locate channels 17 to 59.
327 */
329{
330 //029Y 10925 (encoded) = 109.25 = 109250khz - so we divide the input by 10
331 int tfreq = frequency_kHz / 10;
332 for (tacan_ident_map_type::iterator it = ident_channels.begin(); it != ident_channels.end(); it++)
333 {
334 for (tacan_list_type::iterator lit = it->second.begin(); lit != it->second.end(); lit++)
335 if ((*lit)->get_freq() == tfreq)
336 return (*lit);
337 }
338 return nullptr;
339}
340// Given a TACAN Channel return the first matching frequency
342{
343 const tacan_list_type& stations = ident_channels[channel];
344 SG_LOG( SG_NAVAID, SG_DEBUG, "findByChannel " << channel<< " size " << stations.size() );
345
346 if (!stations.empty()) {
347 return stations[0];
348 }
349 return NULL;
350}
Model a DME radio.
Definition dme.hxx:38
virtual FGPositioned::Type minType() const
Definition navlist.hxx:63
TypeFilter(const FGPositioned::Type type=FGPositioned::INVALID)
Definition navlist.cxx:95
virtual FGPositioned::Type maxType() const
Definition navlist.hxx:64
FGPositioned::Type _mintype
Definition navlist.hxx:67
bool fromTypeString(const std::string &type)
Construct from string containing type.
Definition navlist.cxx:114
FGPositioned::Type _maxtype
Definition navlist.hxx:68
static TypeFilter * locFilter()
filter matching ILS/LOC transmitter
Definition navlist.cxx:157
static FGNavRecordRef findByFreq(double freq, const SGGeod &position, TypeFilter *filter=nullptr)
Query the database for the specified station.
Definition navlist.cxx:187
static TypeFilter * ndbFilter()
Definition navlist.cxx:163
static TypeFilter * navFilter()
filter matching VOR & ILS/LOC transmitters
Definition navlist.cxx:169
static nav_list_type findAllByFreq(double freq, const SGGeod &position, TypeFilter *filter=NULL)
Definition navlist.cxx:244
static nav_list_type findByIdentAndFreq(const std::string &ident, const double freq, TypeFilter *filter=NULL)
static TypeFilter * mobileTacanFilter()
Definition navlist.cxx:181
static TypeFilter * tacanFilter()
Filter returning TACANs and VORTACs.
Definition navlist.cxx:175
int get_freq() const
Definition navrecord.hxx:76
FGRunwayRef runway() const
Retrieve the runway this navaid is associated with (for ILS/LOC/GS)
Definition navrecord.cxx:58
virtual bool pass(FGPositioned *aPos) const
Over-rideable filter method.
virtual const std::string & name() const
Return the name of this positioned.
static SGSharedPtr< T > loadById(PositionedID id)
@ DME
important that DME & TACAN are adjacent to keep the TacanFilter efficient - DMEs are proxies for TACA...
static FGPositionedList findAllWithIdent(const std::string &aIdent, Filter *aFilter=NULL, bool aExact=true)
Find all items with the specified ident.
Type type() const
bool init()
Definition navlist.cxx:309
bool add(FGTACANRecord *r)
Definition navlist.cxx:316
FGTACANRecord * findByFrequency(int frequency_kHz)
Definition navlist.cxx:328
FGTACANRecord * findByChannel(const std::string &channel)
Definition navlist.cxx:341
const std::string & get_channel() const
Model a TACAN radio.
Definition tacan.hxx:41
Filter returning Tacan stations.
Definition navlist.cxx:137
virtual bool pass(FGPositioned *pos) const
Over-rideable filter method.
Definition navlist.cxx:144
static NavDataCache * instance()
PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod &pos, FGPositioned::Filter *filt)
Find all navaids matching a particular frequency, sorted by range from the supplied position.
SGSharedPtr< FGNavRecord > FGNavRecordRef
std::vector< nav_rec_ptr > nav_list_type
Definition navlist.hxx:44
std::vector< tacan_rec_ptr > tacan_list_type
Definition navlist.hxx:127
SGSharedPtr< FGNavRecord > nav_rec_ptr
Definition navlist.hxx:43
const double FG_NAV_MAX_RANGE
Definition navrecord.hxx:40
std::vector< PositionedID > PositionedIDVec
std::vector< FGPositionedRef > FGPositionedList
int64_t PositionedID