]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/radiostack.cxx
- fixed typos relating to nav frequency properties
[flightgear.git] / src / Cockpit / radiostack.cxx
1 // radiostack.cxx -- class to manage an instance of the radio stack
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29 #include <simgear/math/sg_random.h>
30
31 #include <Aircraft/aircraft.hxx>
32 #include <Navaids/ilslist.hxx>
33 #include <Navaids/mkrbeacons.hxx>
34 #include <Navaids/navlist.hxx>
35 #include <Time/event.hxx>
36
37 #include "radiostack.hxx"
38
39 static int nav1_play_count = 0;
40 static int nav2_play_count = 0;
41 static int adf_play_count = 0;
42 static time_t nav1_last_time = 0;
43 static time_t nav2_last_time = 0;
44 static time_t adf_last_time = 0;
45
46
47 FGRadioStack *current_radiostack;
48
49
50 /**
51  * Boy, this is ugly!  Make the VOR range vary by altitude difference.
52  */
53 static double kludgeRange ( double stationElev, double aircraftElev,
54                             double nominalRange)
55 {
56                                 // Assume that the nominal range (usually
57                                 // 50nm) applies at a 5,000 ft difference.
58                                 // Just a wild guess!
59   double factor = ((aircraftElev*SG_METER_TO_FEET) - stationElev) / 5000.0;
60   double range = fabs(nominalRange * factor);
61
62                                 // Clamp the range to keep it sane; for
63                                 // now, never less than 25% or more than
64                                 // 500% of nominal range.
65   if (range < nominalRange/4.0) {
66     range = nominalRange/4.0;
67   } else if (range > nominalRange*5.0) {
68     range = nominalRange*5.0;
69   }
70
71   return range;
72 }
73
74
75 // periodic radio station search wrapper
76 static void fgRadioSearch( void ) {
77     current_radiostack->search();
78 }
79
80 // Constructor
81 FGRadioStack::FGRadioStack() :
82     lon_node(fgGetNode("/position/longitude-deg", true)),
83     lat_node(fgGetNode("/position/latitude-deg", true)),
84     alt_node(fgGetNode("/position/altitude-ft", true)),
85     need_update(true),
86     nav1_radial(0.0),
87     nav1_dme_dist(0.0),
88     nav2_radial(0.0),
89     nav2_dme_dist(0.0)
90 {
91     SGPath path( globals->get_fg_root() );
92     SGPath term = path;
93     term.append( "Navaids/range.term" );
94     SGPath low = path;
95     low.append( "Navaids/range.low" );
96     SGPath high = path;
97     high.append( "Navaids/range.high" );
98
99     term_tbl = new SGInterpTable( term.str() );
100     low_tbl = new SGInterpTable( low.str() );
101     high_tbl = new SGInterpTable( high.str() );
102 }
103
104
105 // Destructor
106 FGRadioStack::~FGRadioStack() 
107 {
108     unbind();                   // FIXME: should be called externally
109
110     delete term_tbl;
111     delete low_tbl;
112     delete high_tbl;
113 }
114
115
116 void
117 FGRadioStack::init ()
118 {
119     morse.init();
120     beacon.init();
121     blink.stamp();
122
123     search();
124     update();
125
126     // Search radio database once per second
127     global_events.Register( "fgRadioSearch()", fgRadioSearch,
128                             fgEVENT::FG_EVENT_READY, 1000);
129 }
130
131 void
132 FGRadioStack::bind ()
133 {
134                                 // User inputs
135     fgTie("/radios/nav[0]/frequencies/selected-mhz", this,
136           &FGRadioStack::get_nav1_freq, &FGRadioStack::set_nav1_freq);
137     fgSetArchivable("/radios/nav[0]/frequencies/selected-mhz");
138     fgTie("/radios/nav[0]/frequencies/standby-mhz", this,
139           &FGRadioStack::get_nav1_alt_freq, &FGRadioStack::set_nav1_alt_freq);
140     fgSetArchivable("/radios/nav[0]/frequencies/standby-mhz");
141     fgTie("/radios/nav[0]/radials/selected-deg", this,
142           &FGRadioStack::get_nav1_sel_radial,
143           &FGRadioStack::set_nav1_sel_radial);
144     fgSetArchivable("/radios/nav[0]/radials/selected-deg");
145     fgTie("/radios/nav[0]/volume", this,
146           &FGRadioStack::get_nav1_vol_btn,
147           &FGRadioStack::set_nav1_vol_btn);
148     fgSetArchivable("/radios/nav[0]/volume");
149     fgTie("/radios/nav[0]/ident", this,
150           &FGRadioStack::get_nav1_ident_btn,
151           &FGRadioStack::set_nav1_ident_btn);
152     fgSetArchivable("/radios/nav[0]/ident");
153
154                                 // Radio outputs
155     fgTie("/radios/nav[0]/radials/actual-deg", this,
156           &FGRadioStack::get_nav1_radial);
157     fgTie("/radios/nav[0]/to-flag", this, &FGRadioStack::get_nav1_to_flag);
158     fgTie("/radios/nav[0]/from-flag", this, &FGRadioStack::get_nav1_from_flag);
159     fgTie("/radios/nav[0]/in-range", this, &FGRadioStack::get_nav1_inrange);
160     fgTie("/radios/nav[0]/dme/distance-nm", this,
161           &FGRadioStack::get_nav1_dme_dist);
162     fgTie("/radios/nav[0]/dme/in-range", this,
163           &FGRadioStack::get_nav1_dme_inrange);
164     fgTie("/radios/nav[0]/heading-needle-deflection", this,
165           &FGRadioStack::get_nav1_heading_needle_deflection);
166     fgTie("/radios/nav[0]/gs-needle-deflection", this,
167           &FGRadioStack::get_nav1_gs_needle_deflection);
168
169                                 // User inputs
170     fgTie("/radios/nav[1]/frequencies/selected-mhz", this,
171           &FGRadioStack::get_nav2_freq, &FGRadioStack::set_nav2_freq);
172     fgSetArchivable("/radios/nav[1]/frequencies/selected-mhz");
173     fgTie("/radios/nav[1]/frequencies/standby-mhz", this,
174           &FGRadioStack::get_nav2_alt_freq, &FGRadioStack::set_nav2_alt_freq);
175     fgSetArchivable("/radios/nav[1]/frequencies/standby-mhz");
176     fgTie("/radios/nav[1]/radials/selected-deg", this,
177           &FGRadioStack::get_nav2_sel_radial,
178           &FGRadioStack::set_nav2_sel_radial);
179     fgSetArchivable("/radios/nav[1]/radials/selected-deg");
180     fgTie("/radios/nav[1]/volume", this,
181           &FGRadioStack::get_nav2_vol_btn,
182           &FGRadioStack::set_nav2_vol_btn);
183     fgSetArchivable("/radios/nav[1]/volume");
184     fgTie("/radios/nav[1]/ident", this,
185           &FGRadioStack::get_nav2_ident_btn,
186           &FGRadioStack::set_nav2_ident_btn);
187     fgSetArchivable("/radios/nav[1]/ident");
188
189                                 // Radio outputs
190     fgTie("/radios/nav[1]/radials/actual-deg", this,
191           &FGRadioStack::get_nav2_radial);
192     fgTie("/radios/nav[1]/to-flag", this, &FGRadioStack::get_nav2_to_flag);
193     fgTie("/radios/nav[1]/from-flag", this, &FGRadioStack::get_nav2_from_flag);
194     fgTie("/radios/nav[1]/in-range", this, &FGRadioStack::get_nav2_inrange);
195     fgTie("/radios/nav[1]/dme/distance-nm", this,
196           &FGRadioStack::get_nav2_dme_dist);
197     fgTie("/radios/nav[1]/dme/in-range", this,
198           &FGRadioStack::get_nav2_dme_inrange);
199     fgTie("/radios/nav[1]/heading-needle-deflection", this,
200           &FGRadioStack::get_nav2_heading_needle_deflection);
201     fgTie("/radios/nav[1]/gs-needle-deflection", this,
202           &FGRadioStack::get_nav2_gs_needle_deflection);
203
204                                 // User inputs
205     fgTie("/radios/adf/frequencies/selected-khz", this,
206           &FGRadioStack::get_adf_freq, &FGRadioStack::set_adf_freq);
207     fgSetArchivable("/radios/adf/frequencies/selected-khz");
208     fgTie("/radios/adf/frequencies/standby-khz", this,
209           &FGRadioStack::get_adf_alt_freq, &FGRadioStack::set_adf_alt_freq);
210     fgSetArchivable("/radios/adf/frequencies/standby-khz");
211     fgTie("/radios/adf/rotation-deg", this,
212           &FGRadioStack::get_adf_rotation, &FGRadioStack::set_adf_rotation);
213     fgSetArchivable("/radios/adf/rotation-deg");
214     fgTie("/radios/adf/volume", this,
215           &FGRadioStack::get_adf_vol_btn,
216           &FGRadioStack::set_adf_vol_btn);
217     fgSetArchivable("/radios/adf/volume");
218     fgTie("/radios/adf/ident", this,
219           &FGRadioStack::get_adf_ident_btn,
220           &FGRadioStack::set_adf_ident_btn);
221     fgSetArchivable("/radios/adf/ident");
222
223     fgTie("/radios/marker-beacon/inner", this,
224           &FGRadioStack::get_inner_blink);
225     fgTie("/radios/marker-beacon/middle", this,
226           &FGRadioStack::get_middle_blink);
227     fgTie("/radios/marker-beacon/outer", this,
228           &FGRadioStack::get_outer_blink);
229 }
230
231 void
232 FGRadioStack::unbind ()
233 {
234     fgUntie("/radios/nav[0]/frequencies/selected-mhz");
235     fgUntie("/radios/nav[0]/frequencies/standby-mhz");
236     fgUntie("/radios/nav[0]/radials/actual-deg");
237     fgUntie("/radios/nav[0]/radials/selected-deg");
238     fgUntie("/radios/nav[0]/on");
239     fgUntie("/radios/nav[0]/ident");
240     fgUntie("/radios/nav[0]/to-flag");
241     fgUntie("/radios/nav[0]/from-flag");
242     fgUntie("/radios/nav[0]/in-range");
243     fgUntie("/radios/nav[0]/dme/distance-nm");
244     fgUntie("/radios/nav[0]/dme/in-range");
245     fgUntie("/radios/nav[0]/heading-needle-deflection");
246     fgUntie("/radios/nav[0]/gs-needle-deflection");
247
248     fgUntie("/radios/nav[1]/frequencies/selected-mhz");
249     fgUntie("/radios/nav[1]/frequencies/standby-mhz");
250     fgUntie("/radios/nav[1]/radials/actual-deg");
251     fgUntie("/radios/nav[1]/radials/selected-deg");
252     fgUntie("/radios/nav[1]/on");
253     fgUntie("/radios/nav[1]/ident");
254     fgUntie("/radios/nav[1]/to-flag");
255     fgUntie("/radios/nav[1]/from-flag");
256     fgUntie("/radios/nav[1]/in-range");
257     fgUntie("/radios/nav[1]/dme/distance-nm");
258     fgUntie("/radios/nav[1]/dme/in-range");
259     fgUntie("/radios/nav[1]/heading-needle-deflection");
260     fgUntie("/radios/nav[1]/gs-needle-deflection");
261
262     fgUntie("/radios/adf/frequencies/selected-khz");
263     fgUntie("/radios/adf/frequencies/standby-khz");
264     fgUntie("/radios/adf/rotation-deg");
265     fgUntie("/radios/adf/on");
266     fgUntie("/radios/adf/ident");
267
268     fgUntie("/radios/marker-beacon/inner");
269     fgUntie("/radios/marker-beacon/middle");
270     fgUntie("/radios/marker-beacon/outer");
271 }
272
273
274 // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
275 double FGRadioStack::adjustNavRange( double stationElev, double aircraftElev,
276                               double nominalRange )
277 {
278     // extend out actual usable range to be 1.3x the published safe range
279     const double usability_factor = 1.3;
280
281     // assumptions we model the standard service volume, plus
282     // ... rather than specifying a cylinder, we model a cone that
283     // contains the cylinder.  Then we put an upside down cone on top
284     // to model diminishing returns at too-high altitudes.
285
286     // altitude difference
287     double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
288     // cout << "aircraft elev = " << aircraftElev * SG_METER_TO_FEET
289     //      << " station elev = " << stationElev << endl;
290
291     if ( nominalRange < 25.0 + SG_EPSILON ) {
292         // Standard Terminal Service Volume
293         return term_tbl->interpolate( alt ) * usability_factor;
294     } else if ( nominalRange < 50.0 + SG_EPSILON ) {
295         // Standard Low Altitude Service Volume
296         // table is based on range of 40, scale to actual range
297         return low_tbl->interpolate( alt ) * nominalRange / 40.0
298             * usability_factor;
299     } else {
300         // Standard High Altitude Service Volume
301         // table is based on range of 130, scale to actual range
302         return high_tbl->interpolate( alt ) * nominalRange / 130.0
303             * usability_factor;
304     }
305 }
306
307
308 // model standard ILS service volumes as per AIM 1-1-9
309 double FGRadioStack::adjustILSRange( double stationElev, double aircraftElev,
310                                      double offsetDegrees, double distance )
311 {
312     // assumptions we model the standard service volume, plus
313
314     // altitude difference
315     // double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
316     double offset = fabs( offsetDegrees );
317
318     if ( offset < 10 ) {
319         return FG_ILS_DEFAULT_RANGE;
320     } else if ( offset < 35 ) {
321         return 10 + (35 - offset) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
322     } else if ( offset < 45 ) {
323         return (45 - offset);
324     } else {
325         return 0;
326     }
327 }
328
329
330 // Update the various nav values based on position and valid tuned in navs
331 void 
332 FGRadioStack::update() 
333 {
334     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
335     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
336     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
337
338     need_update = false;
339
340     Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
341     Point3D station;
342     double az1, az2, s;
343
344     if ( nav1_valid ) {
345         station = Point3D( nav1_x, nav1_y, nav1_z );
346         nav1_loc_dist = aircraft.distance3D( station );
347
348         if ( nav1_has_dme ) {
349             // staightline distance
350             station = Point3D( nav1_dme_x, nav1_dme_y, nav1_dme_z );
351             nav1_dme_dist = aircraft.distance3D( station );
352         } else {
353             nav1_dme_dist = 0.0;
354         }
355
356         if ( nav1_has_gs ) {
357             station = Point3D( nav1_gs_x, nav1_gs_y, nav1_gs_z );
358             nav1_gs_dist = aircraft.distance3D( station );
359         } else {
360             nav1_gs_dist = 0.0;
361         }
362         
363         // wgs84 heading
364         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
365                             nav1_loclat, nav1_loclon,
366                             &az1, &az2, &s );
367         // cout << "az1 = " << az1 << " magvar = " << nav1_magvar << endl;
368         nav1_heading = az1 - nav1_magvar;
369         // cout << " heading = " << nav1_heading
370         //      << " dist = " << nav1_dist << endl;
371
372         if ( nav1_loc ) {
373             double offset = nav1_heading - nav1_radial;
374             while ( offset < -180.0 ) { offset += 360.0; }
375             while ( offset > 180.0 ) { offset -= 360.0; }
376             // cout << "ils offset = " << offset << endl;
377             nav1_effective_range = adjustILSRange(nav1_elev, elev, offset,
378                                                   nav1_loc_dist * SG_METER_TO_NM );
379         } else {
380             nav1_effective_range = adjustNavRange(nav1_elev, elev, nav1_range);
381         }
382         // cout << "nav1 range = " << nav1_effective_range
383         //      << " (" << nav1_range << ")" << endl;
384
385         if ( nav1_loc_dist < nav1_effective_range * SG_NM_TO_METER ) {
386             nav1_inrange = true;
387         } else if ( nav1_loc_dist < 2 * nav1_effective_range * SG_NM_TO_METER ) {
388             nav1_inrange = sg_random() < 
389                 ( 2 * nav1_effective_range * SG_NM_TO_METER - nav1_loc_dist ) /
390                 (nav1_effective_range * SG_NM_TO_METER);
391         } else {
392             nav1_inrange = false;
393         }
394
395         if ( !nav1_loc ) {
396             nav1_radial = nav1_sel_radial;
397         }
398     } else {
399         nav1_inrange = false;
400         nav1_dme_dist = 0.0;
401         // cout << "not picking up vor. :-(" << endl;
402     }
403
404 #ifdef ENABLE_AUDIO_SUPPORT
405     if ( nav1_valid && nav1_inrange ) {
406         // play station ident via audio system if on + ident,
407         // otherwise turn it off
408         if ( nav1_vol_btn > 0.1 && nav1_ident_btn ) {
409             FGSimpleSound *sound;
410             sound = globals->get_soundmgr()->find( "nav1-vor-ident" );
411             sound->set_volume( nav1_vol_btn * 0.3 );
412             sound = globals->get_soundmgr()->find( "nav1-dme-ident" );
413             sound->set_volume( nav1_vol_btn * 0.3 );
414             if ( nav1_last_time <
415                  globals->get_time_params()->get_cur_time() - 30 ) {
416                 nav1_last_time = globals->get_time_params()->get_cur_time();
417                 nav1_play_count = 0;
418             }
419             if ( nav1_play_count < 4 ) {
420                 // play VOR ident
421                 if ( !globals->get_soundmgr()->is_playing("nav1-vor-ident") ) {
422                     globals->get_soundmgr()->play_once( "nav1-vor-ident" );
423                     ++nav1_play_count;
424                 }
425             } else if ( nav1_play_count < 5 && nav1_has_dme ) {
426                 // play DME ident
427                 if ( !globals->get_soundmgr()->is_playing("nav1-vor-ident") &&
428                      !globals->get_soundmgr()->is_playing("nav1-dme-ident") ) {
429                     globals->get_soundmgr()->play_once( "nav1-dme-ident" );
430                     ++nav1_play_count;
431                 }
432             }
433         } else {
434             globals->get_soundmgr()->stop( "nav1-vor-ident" );
435             globals->get_soundmgr()->stop( "nav1-dme-ident" );
436         }
437     }
438 #endif
439
440     if ( nav2_valid ) {
441         station = Point3D( nav2_x, nav2_y, nav2_z );
442         nav2_loc_dist = aircraft.distance3D( station );
443
444         if ( nav2_has_dme ) {
445             // staightline distance
446             station = Point3D( nav2_dme_x, nav2_dme_y, nav2_dme_z );
447             nav2_dme_dist = aircraft.distance3D( station );
448         } else {
449             nav2_dme_dist = 0.0;
450         }
451
452         if ( nav2_has_gs ) {
453             station = Point3D( nav2_gs_x, nav2_gs_y, nav2_gs_z );
454             nav2_gs_dist = aircraft.distance3D( station );
455         } else {
456             nav2_gs_dist = 0.0;
457         }
458
459         // wgs84 heading
460         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
461                             nav2_loclat, nav2_loclon,
462                             &az1, &az2, &s );
463         nav2_heading = az1 - nav2_magvar;
464         // cout << " heading = " << nav2_heading
465         //      << " dist = " << nav2_dist << endl;
466
467         if ( nav2_loc ) {
468             double offset = nav2_heading - nav2_radial;
469             while ( offset < -180.0 ) { offset += 360.0; }
470             while ( offset > 180.0 ) { offset -= 360.0; }
471             // cout << "ils offset = " << offset << endl;
472             nav2_effective_range = adjustILSRange(nav2_elev, elev, offset,
473                                                   nav2_loc_dist * SG_METER_TO_NM );
474         } else {
475             nav2_effective_range = adjustNavRange(nav2_elev, elev, nav2_range);
476         }
477         // cout << "nav2 range = " << nav2_effective_range
478         //      << " (" << nav2_range << ")" << endl;
479
480         if ( nav2_loc_dist < nav2_effective_range * SG_NM_TO_METER ) {
481             nav2_inrange = true;
482         } else if ( nav2_loc_dist < 2 * nav2_effective_range * SG_NM_TO_METER ) {
483             nav2_inrange = sg_random() < 
484                 ( 2 * nav2_effective_range * SG_NM_TO_METER - nav2_loc_dist ) /
485                 (nav2_effective_range * SG_NM_TO_METER);
486         } else {
487             nav2_inrange = false;
488         }
489
490         if ( !nav2_loc ) {
491             nav2_radial = nav2_sel_radial;
492         }
493     } else {
494         nav2_inrange = false;
495         nav2_dme_dist = 0.0;
496         // cout << "not picking up vor. :-(" << endl;
497     }
498
499 #ifdef ENABLE_AUDIO_SUPPORT
500     if ( nav2_valid && nav2_inrange ) {
501         // play station ident via audio system if on + ident,
502         // otherwise turn it off
503         if ( nav2_vol_btn > 0.1 && nav2_ident_btn ) {
504             FGSimpleSound *sound;
505             sound = globals->get_soundmgr()->find( "nav2-vor-ident" );
506             sound->set_volume( nav2_vol_btn * 0.3 );
507             sound = globals->get_soundmgr()->find( "nav2-dme-ident" );
508             sound->set_volume( nav2_vol_btn * 0.3 );
509             if ( nav2_last_time <
510                  globals->get_time_params()->get_cur_time() - 30 ) {
511                 nav2_last_time = globals->get_time_params()->get_cur_time();
512                 nav2_play_count = 0;
513             }
514             if ( nav2_play_count < 4 ) {
515                 // play VOR ident
516                 if ( !globals->get_soundmgr()->is_playing("nav2-vor-ident") ) {
517                     globals->get_soundmgr()->play_once( "nav2-vor-ident" );
518                     ++nav2_play_count;
519                 }
520             } else if ( nav2_play_count < 5 && nav2_has_dme ) {
521                 // play DME ident
522                 if ( !globals->get_soundmgr()->is_playing("nav2-vor-ident") &&
523                      !globals->get_soundmgr()->is_playing("nav2-dme-ident") ) {
524                     globals->get_soundmgr()->play_once( "nav2-dme-ident" );
525                     ++nav2_play_count;
526                 }
527             }
528         } else {
529             globals->get_soundmgr()->stop( "nav2-vor-ident" );
530             globals->get_soundmgr()->stop( "nav2-dme-ident" );
531         }
532     }
533 #endif
534
535     // adf
536     if ( adf_valid ) {
537         // staightline distance
538         station = Point3D( adf_x, adf_y, adf_z );
539         adf_dist = aircraft.distance3D( station );
540
541         // wgs84 heading
542         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
543                             adf_lat, adf_lon,
544                             &az1, &az2, &s );
545         adf_heading = az1;
546         // cout << " heading = " << nav2_heading
547         //      << " dist = " << nav2_dist << endl;
548
549         adf_effective_range = kludgeRange(adf_elev, elev, adf_range);
550         if ( adf_dist < adf_effective_range * SG_NM_TO_METER ) {
551             adf_inrange = true;
552         } else if ( adf_dist < 2 * adf_effective_range * SG_NM_TO_METER ) {
553             adf_inrange = sg_random() < 
554                 ( 2 * adf_effective_range * SG_NM_TO_METER - adf_dist ) /
555                 (adf_effective_range * SG_NM_TO_METER);
556         } else {
557             adf_inrange = false;
558         }
559     } else {
560         adf_inrange = false;
561     }
562
563 #ifdef ENABLE_AUDIO_SUPPORT
564     if ( adf_valid && adf_inrange ) {
565         // play station ident via audio system if on + ident,
566         // otherwise turn it off
567         if ( adf_vol_btn > 0.1 && adf_ident_btn ) {
568             FGSimpleSound *sound;
569             sound = globals->get_soundmgr()->find( "adf-ident" );
570             sound->set_volume( adf_vol_btn * 0.3 );
571             if ( adf_last_time <
572                  globals->get_time_params()->get_cur_time() - 30 ) {
573                 adf_last_time = globals->get_time_params()->get_cur_time();
574                 adf_play_count = 0;
575             }
576             if ( adf_play_count < 4 ) {
577                 // play ADF ident
578                 if ( !globals->get_soundmgr()->is_playing("adf-ident") ) {
579                     globals->get_soundmgr()->play_once( "adf-ident" );
580                     ++adf_play_count;
581                 }
582             }
583         } else {
584             globals->get_soundmgr()->stop( "adf-ident" );
585         }
586     }
587 #endif
588
589     // marker beacon blinking
590     bool light_on = ( outer_blink || middle_blink || inner_blink );
591     SGTimeStamp current;
592     current.stamp();
593
594     if ( light_on && (current - blink > 400000) ) {
595         light_on = false;
596         blink.stamp();
597     } else if ( !light_on && (current - blink > 100000) ) {
598         light_on = true;
599         blink.stamp();
600     }
601
602     if ( outer_marker ) {
603         outer_blink = light_on;
604     } else {
605         outer_blink = false;
606     }
607
608     if ( middle_marker ) {
609         middle_blink = light_on;
610     } else {
611         middle_blink = false;
612     }
613
614     if ( inner_marker ) {
615         inner_blink = light_on;
616     } else {
617         inner_blink = false;
618     }
619
620     // cout << outer_blink << " " << middle_blink << " " << inner_blink << endl;
621 }
622
623
624 // Update current nav/adf radio stations based on current postition
625 void FGRadioStack::search() 
626 {
627     static FGMkrBeacon::fgMkrBeacType last_beacon = FGMkrBeacon::NOBEACON;
628
629     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
630     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
631     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
632
633     // nav1
634     FGILS ils;
635     FGNav nav;
636
637     static string last_nav1_ident = "";
638     static string last_nav2_ident = "";
639     static string last_adf_ident = "";
640     static bool last_nav1_vor = false;
641     static bool last_nav2_vor = false;
642     if ( current_ilslist->query( lon, lat, elev, nav1_freq, &ils ) ) {
643         nav1_ident = ils.get_locident();
644         nav1_valid = true;
645         if ( last_nav1_ident != nav1_ident || last_nav1_vor ) {
646             nav1_trans_ident = ils.get_trans_ident();
647             last_nav1_ident = nav1_ident;
648             last_nav1_vor = false;
649             nav1_loc = true;
650             nav1_has_dme = ils.get_has_dme();
651             nav1_has_gs = ils.get_has_gs();
652
653             nav1_loclon = ils.get_loclon();
654             nav1_loclat = ils.get_loclat();
655             nav1_gslon = ils.get_gslon();
656             nav1_gslat = ils.get_gslat();
657             nav1_dmelon = ils.get_dmelon();
658             nav1_dmelat = ils.get_dmelat();
659             nav1_elev = ils.get_gselev();
660             nav1_magvar = 0;
661             nav1_range = FG_ILS_DEFAULT_RANGE;
662             nav1_effective_range = nav1_range;
663             nav1_target_gs = ils.get_gsangle();
664             nav1_radial = ils.get_locheading();
665             while ( nav1_radial <   0.0 ) { nav1_radial += 360.0; }
666             while ( nav1_radial > 360.0 ) { nav1_radial -= 360.0; }
667             nav1_x = ils.get_x();
668             nav1_y = ils.get_y();
669             nav1_z = ils.get_z();
670             nav1_gs_x = ils.get_gs_x();
671             nav1_gs_y = ils.get_gs_y();
672             nav1_gs_z = ils.get_gs_z();
673             nav1_dme_x = ils.get_dme_x();
674             nav1_dme_y = ils.get_dme_y();
675             nav1_dme_z = ils.get_dme_z();
676
677 #ifdef ENABLE_AUDIO_SUPPORT
678             if ( globals->get_soundmgr()->exists( "nav1-vor-ident" ) ) {
679                 globals->get_soundmgr()->remove( "nav1-vor-ident" );
680             }
681             FGSimpleSound *sound;
682             sound = morse.make_ident( nav1_trans_ident, LO_FREQUENCY );
683             sound->set_volume( 0.3 );
684             globals->get_soundmgr()->add( sound, "nav1-vor-ident" );
685
686             if ( globals->get_soundmgr()->exists( "nav1-dme-ident" ) ) {
687                 globals->get_soundmgr()->remove( "nav1-dme-ident" );
688             }
689             sound = morse.make_ident( nav1_trans_ident, HI_FREQUENCY );
690             sound->set_volume( 0.3 );
691             globals->get_soundmgr()->add( sound, "nav1-dme-ident" );
692
693             int offset = (int)(sg_random() * 30.0);
694             nav1_play_count = offset / 4;
695             nav1_last_time = globals->get_time_params()->get_cur_time() -
696                 offset;
697             // cout << "offset = " << offset << " play_count = "
698             //      << nav1_play_count
699             //      << " nav1_last_time = " << nav1_last_time
700             //      << " current time = "
701             //      << globals->get_time_params()->get_cur_time() << endl;
702 #endif
703
704             // cout << "Found an ils station in range" << endl;
705             // cout << " id = " << ils.get_locident() << endl;
706         }
707     } else if ( current_navlist->query( lon, lat, elev, nav1_freq, &nav ) ) {
708         nav1_ident = nav.get_ident();
709         nav1_valid = true;
710         if ( last_nav1_ident != nav1_ident || !last_nav1_vor ) {
711             last_nav1_ident = nav1_ident;
712             last_nav1_vor = true;
713             nav1_trans_ident = nav.get_trans_ident();
714             nav1_loc = false;
715             nav1_has_dme = nav.get_has_dme();
716             nav1_has_gs = false;
717             nav1_loclon = nav.get_lon();
718             nav1_loclat = nav.get_lat();
719             nav1_elev = nav.get_elev();
720             nav1_magvar = nav.get_magvar();
721             nav1_range = nav.get_range();
722             nav1_effective_range = adjustNavRange(nav1_elev, elev, nav1_range);
723             nav1_target_gs = 0.0;
724             nav1_radial = nav1_sel_radial;
725             nav1_x = nav1_dme_x = nav.get_x();
726             nav1_y = nav1_dme_y = nav.get_y();
727             nav1_z = nav1_dme_z = nav.get_z();
728
729 #ifdef ENABLE_AUDIO_SUPPORT
730             if ( globals->get_soundmgr()->exists( "nav1-vor-ident" ) ) {
731                 globals->get_soundmgr()->remove( "nav1-vor-ident" );
732             }
733             FGSimpleSound *sound;
734             sound = morse.make_ident( nav1_trans_ident, LO_FREQUENCY );
735             sound->set_volume( 0.3 );
736             globals->get_soundmgr()->add( sound, "nav1-vor-ident" );
737
738             if ( globals->get_soundmgr()->exists( "nav1-dme-ident" ) ) {
739                 globals->get_soundmgr()->remove( "nav1-dme-ident" );
740             }
741             sound = morse.make_ident( nav1_trans_ident, HI_FREQUENCY );
742             sound->set_volume( 0.3 );
743             globals->get_soundmgr()->add( sound, "nav1-dme-ident" );
744
745             int offset = (int)(sg_random() * 30.0);
746             nav1_play_count = offset / 4;
747             nav1_last_time = globals->get_time_params()->get_cur_time() -
748                 offset;
749             // cout << "offset = " << offset << " play_count = "
750             //      << nav1_play_count << " nav1_last_time = "
751             //      << nav1_last_time << " current time = "
752             //      << globals->get_time_params()->get_cur_time() << endl;
753 #endif
754
755             // cout << "Found a vor station in range" << endl;
756             // cout << " id = " << nav.get_ident() << endl;
757         }
758     } else {
759         nav1_valid = false;
760         nav1_ident = "";
761         nav1_radial = 0;
762         nav1_dme_dist = 0;
763         nav1_trans_ident = "";
764         last_nav1_ident = "";
765 #ifdef ENABLE_AUDIO_SUPPORT
766         globals->get_soundmgr()->remove( "nav1-vor-ident" );
767         globals->get_soundmgr()->remove( "nav1-dme-ident" );
768 #endif
769         // cout << "not picking up vor1. :-(" << endl;
770     }
771
772     if ( current_ilslist->query( lon, lat, elev, nav2_freq, &ils ) ) {
773         nav2_ident = ils.get_locident();
774         nav2_valid = true;
775         if ( last_nav2_ident != nav2_ident || last_nav2_vor ) {
776             last_nav2_ident = nav2_ident;
777             last_nav2_vor = false;
778             nav2_trans_ident = ils.get_trans_ident();
779             nav2_loc = true;
780             nav2_has_dme = ils.get_has_dme();
781             nav2_has_gs = ils.get_has_gs();
782
783             nav2_loclon = ils.get_loclon();
784             nav2_loclat = ils.get_loclat();
785             nav2_elev = ils.get_gselev();
786             nav2_magvar = 0;
787             nav2_range = FG_ILS_DEFAULT_RANGE;
788             nav2_effective_range = nav2_range;
789             nav2_target_gs = ils.get_gsangle();
790             nav2_radial = ils.get_locheading();
791             while ( nav2_radial <   0.0 ) { nav2_radial += 360.0; }
792             while ( nav2_radial > 360.0 ) { nav2_radial -= 360.0; }
793             nav2_x = ils.get_x();
794             nav2_y = ils.get_y();
795             nav2_z = ils.get_z();
796             nav2_gs_x = ils.get_gs_x();
797             nav2_gs_y = ils.get_gs_y();
798             nav2_gs_z = ils.get_gs_z();
799             nav2_dme_x = ils.get_dme_x();
800             nav2_dme_y = ils.get_dme_y();
801             nav2_dme_z = ils.get_dme_z();
802
803 #ifdef ENABLE_AUDIO_SUPPORT
804             if ( globals->get_soundmgr()->exists( "nav2-vor-ident" ) ) {
805                 globals->get_soundmgr()->remove( "nav2-vor-ident" );
806             }
807             FGSimpleSound *sound;
808             sound = morse.make_ident( nav2_trans_ident, LO_FREQUENCY );
809             sound->set_volume( 0.3 );
810             globals->get_soundmgr()->add( sound, "nav2-vor-ident" );
811
812             if ( globals->get_soundmgr()->exists( "nav2-dme-ident" ) ) {
813                 globals->get_soundmgr()->remove( "nav2-dme-ident" );
814             }
815             sound = morse.make_ident( nav2_trans_ident, HI_FREQUENCY );
816             sound->set_volume( 0.3 );
817             globals->get_soundmgr()->add( sound, "nav2-dme-ident" );
818
819             int offset = (int)(sg_random() * 30.0);
820             nav2_play_count = offset / 4;
821             nav2_last_time = globals->get_time_params()->get_cur_time() -
822                 offset;
823             // cout << "offset = " << offset << " play_count = "
824             //      << nav2_play_count << " nav2_last_time = "
825             //      << nav2_last_time << " current time = "
826             //      << globals->get_time_params()->get_cur_time() << endl;
827 #endif
828
829             // cout << "Found an ils station in range" << endl;
830             // cout << " id = " << ils.get_locident() << endl;
831         }
832     } else if ( current_navlist->query( lon, lat, elev, nav2_freq, &nav ) ) {
833         nav2_ident = nav.get_ident();
834         nav2_valid = true;
835         if ( last_nav2_ident != nav2_ident || !last_nav2_vor ) {
836             last_nav2_ident = nav2_ident;
837             last_nav2_vor = true;
838             nav2_trans_ident = nav.get_trans_ident();
839             nav2_loc = false;
840             nav2_has_dme = nav.get_has_dme();
841             nav2_has_dme = false;
842             nav2_loclon = nav.get_lon();
843             nav2_loclat = nav.get_lat();
844             nav2_elev = nav.get_elev();
845             nav2_magvar = nav.get_magvar();
846             nav2_range = nav.get_range();
847             nav2_effective_range = adjustNavRange(nav2_elev, elev, nav2_range);
848             nav2_target_gs = 0.0;
849             nav2_radial = nav2_sel_radial;
850             nav2_x = nav2_dme_x = nav.get_x();
851             nav2_y = nav2_dme_y = nav.get_y();
852             nav2_z = nav2_dme_z = nav.get_z();
853
854 #ifdef ENABLE_AUDIO_SUPPORT
855             if ( globals->get_soundmgr()->exists( "nav2-vor-ident" ) ) {
856                 globals->get_soundmgr()->remove( "nav2-vor-ident" );
857             }
858             FGSimpleSound *sound;
859             sound = morse.make_ident( nav2_trans_ident, LO_FREQUENCY );
860             sound->set_volume( 0.3 );
861             globals->get_soundmgr()->add( sound, "nav2-vor-ident" );
862
863             if ( globals->get_soundmgr()->exists( "nav2-dme-ident" ) ) {
864                 globals->get_soundmgr()->remove( "nav2-dme-ident" );
865             }
866             sound = morse.make_ident( nav2_trans_ident, HI_FREQUENCY );
867             sound->set_volume( 0.3 );
868             globals->get_soundmgr()->add( sound, "nav2-dme-ident" );
869
870             int offset = (int)(sg_random() * 30.0);
871             nav2_play_count = offset / 4;
872             nav2_last_time = globals->get_time_params()->get_cur_time() -
873                 offset;
874             // cout << "offset = " << offset << " play_count = "
875             //      << nav2_play_count << " nav2_last_time = "
876             //      << nav2_last_time << " current time = "
877             //      << globals->get_time_params()->get_cur_time() << endl;
878 #endif
879
880             // cout << "Found a vor station in range" << endl;
881             // cout << " id = " << nav.get_ident() << endl;
882         }
883     } else {
884         nav2_valid = false;
885         nav2_ident = "";
886         nav2_radial = 0;
887         nav2_dme_dist = 0;
888         nav2_trans_ident = "";
889         last_nav2_ident = "";
890 #ifdef ENABLE_AUDIO_SUPPORT
891         globals->get_soundmgr()->remove( "nav2-vor-ident" );
892         globals->get_soundmgr()->remove( "nav2-dme-ident" );
893 #endif
894         // cout << "not picking up vor1. :-(" << endl;
895     }
896
897     FGMkrBeacon::fgMkrBeacType beacon_type
898         = current_beacons->query( lon * SGD_RADIANS_TO_DEGREES,
899                                   lat * SGD_RADIANS_TO_DEGREES, elev );
900
901     outer_marker = middle_marker = inner_marker = false;
902
903     if ( beacon_type == FGMkrBeacon::OUTER ) {
904         outer_marker = true;
905         // cout << "OUTER MARKER" << endl;
906 #ifdef ENABLE_AUDIO_SUPPORT
907         if ( last_beacon != FGMkrBeacon::OUTER ) {
908             if ( ! globals->get_soundmgr()->exists( "outer-marker" ) ) {
909                 FGSimpleSound *sound = beacon.get_outer();
910                 sound->set_volume( 0.3 );
911                 globals->get_soundmgr()->add( sound, "outer-marker" );
912             }
913             if ( !globals->get_soundmgr()->is_playing("outer-marker") ) {
914                 globals->get_soundmgr()->play_looped( "outer-marker" );
915             }
916         }
917 #endif
918     } else if ( beacon_type == FGMkrBeacon::MIDDLE ) {
919         middle_marker = true;
920         // cout << "MIDDLE MARKER" << endl;
921 #ifdef ENABLE_AUDIO_SUPPORT
922         if ( last_beacon != FGMkrBeacon::MIDDLE ) {
923             if ( ! globals->get_soundmgr()->exists( "middle-marker" ) ) {
924                 FGSimpleSound *sound = beacon.get_middle();
925                 sound->set_volume( 0.3 );
926                 globals->get_soundmgr()->add( sound, "middle-marker" );
927             }
928             if ( !globals->get_soundmgr()->is_playing("middle-marker") ) {
929                 globals->get_soundmgr()->play_looped( "middle-marker" );
930             }
931         }
932 #endif
933     } else if ( beacon_type == FGMkrBeacon::INNER ) {
934         inner_marker = true;
935         // cout << "INNER MARKER" << endl;
936 #ifdef ENABLE_AUDIO_SUPPORT
937         if ( last_beacon != FGMkrBeacon::INNER ) {
938             if ( ! globals->get_soundmgr()->exists( "inner-marker" ) ) {
939                 FGSimpleSound *sound = beacon.get_inner();
940                 sound->set_volume( 0.3 );
941                 globals->get_soundmgr()->add( sound, "inner-marker" );
942             }
943             if ( !globals->get_soundmgr()->is_playing("inner-marker") ) {
944                 globals->get_soundmgr()->play_looped( "inner-marker" );
945             }
946         }
947 #endif
948     } else {
949         // cout << "no marker" << endl;
950 #ifdef ENABLE_AUDIO_SUPPORT
951         globals->get_soundmgr()->stop( "outer-marker" );
952         globals->get_soundmgr()->stop( "middle-marker" );
953         globals->get_soundmgr()->stop( "inner-marker" );
954 #endif
955     }
956     last_beacon = beacon_type;
957
958     // adf
959     if ( current_navlist->query( lon, lat, elev, adf_freq, &nav ) ) {
960         char freq[128];
961 #if defined( _MSC_VER )
962         _snprintf( freq, 10, "%.0f", adf_freq );
963 #else
964         snprintf( freq, 10, "%.0f", adf_freq );
965 #endif
966         adf_ident = freq;
967         adf_ident += nav.get_ident();
968         // cout << "adf ident = " << adf_ident << endl;
969         adf_valid = true;
970         if ( last_adf_ident != adf_ident ) {
971             last_adf_ident = adf_ident;
972
973             adf_trans_ident = nav.get_trans_ident();
974             adf_lon = nav.get_lon();
975             adf_lat = nav.get_lat();
976             adf_elev = nav.get_elev();
977             adf_range = nav.get_range();
978             adf_effective_range = kludgeRange(adf_elev, elev, adf_range);
979             adf_x = nav.get_x();
980             adf_y = nav.get_y();
981             adf_z = nav.get_z();
982
983 #ifdef ENABLE_AUDIO_SUPPORT
984             if ( globals->get_soundmgr()->exists( "adf-ident" ) ) {
985                 globals->get_soundmgr()->remove( "adf-ident" );
986             }
987             FGSimpleSound *sound;
988             sound = morse.make_ident( adf_trans_ident, LO_FREQUENCY );
989             sound->set_volume( 0.3 );
990             globals->get_soundmgr()->add( sound, "adf-ident" );
991
992             int offset = (int)(sg_random() * 30.0);
993             adf_play_count = offset / 4;
994             adf_last_time = globals->get_time_params()->get_cur_time() -
995                 offset;
996             // cout << "offset = " << offset << " play_count = "
997             //      << adf_play_count << " adf_last_time = "
998             //      << adf_last_time << " current time = "
999             //      << globals->get_time_params()->get_cur_time() << endl;
1000 #endif
1001
1002             // cout << "Found an adf station in range" << endl;
1003             // cout << " id = " << nav.get_ident() << endl;
1004         }
1005     } else {
1006         adf_valid = false;
1007         adf_ident = "";
1008         adf_trans_ident = "";
1009 #ifdef ENABLE_AUDIO_SUPPORT
1010         globals->get_soundmgr()->remove( "adf-ident" );
1011 #endif
1012         last_adf_ident = "";
1013         // cout << "not picking up adf. :-(" << endl;
1014     }
1015 }
1016
1017
1018 // return the amount of heading needle deflection, returns a value
1019 // clamped to the range of ( -10 , 10 )
1020 double FGRadioStack::get_nav1_heading_needle_deflection() const {
1021     double r;
1022
1023     if ( nav1_inrange ) {
1024         r = nav1_heading - nav1_radial;
1025         // cout << "Radial = " << nav1_radial 
1026         //      << "  Bearing = " << nav1_heading << endl;
1027     
1028         while ( r >  180.0 ) { r -= 360.0;}
1029         while ( r < -180.0 ) { r += 360.0;}
1030         if ( fabs(r) > 90.0 ) {
1031             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
1032             if ( nav1_loc ) {
1033                 r = -r;
1034             }
1035         }
1036
1037         // According to Robin Peel, the ILS is 4x more sensitive than a vor
1038         if ( nav1_loc ) { r *= 4.0; }
1039         if ( r < -10.0 ) { r = -10.0; }
1040         if ( r >  10.0 ) { r = 10.0; }
1041     } else {
1042         r = 0.0;
1043     }
1044
1045     return r;
1046 }
1047
1048 // return the amount of heading needle deflection, returns a value
1049 // clamped to the range of ( -10 , 10 )
1050 double FGRadioStack::get_nav2_heading_needle_deflection() const {
1051     double r;
1052
1053     if ( nav2_inrange ) {
1054         r = nav2_heading - nav2_radial;
1055         // cout << "Radial = " << nav2_radial 
1056         //      << "  Bearing = " << nav2_heading << endl;
1057     
1058         while (r> 180.0) r-=360.0;
1059         while (r<-180.0) r+=360.0;
1060         if ( fabs(r) > 90.0 )
1061             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
1062         // According to Robin Peel, the ILS is 4x more sensitive than a vor
1063         if ( nav2_loc ) r *= 4.0;
1064         if ( r < -10.0 ) r = -10.0;
1065         if ( r > 10.0 ) r = 10.0;
1066     } else {
1067         r = 0.0;
1068     }
1069
1070     return r;
1071 }
1072
1073 // return the amount of glide slope needle deflection (.i.e. the
1074 // number of degrees we are off the glide slope * 5.0
1075 double FGRadioStack::get_nav1_gs_needle_deflection() const {
1076     if ( nav1_inrange && nav1_has_gs ) {
1077         double x = nav1_gs_dist;
1078         double y = (fgGetDouble("/position/altitude-ft") - nav1_elev)
1079             * SG_FEET_TO_METER;
1080         double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
1081         return (nav1_target_gs - angle) * 5.0;
1082     } else {
1083         return 0.0;
1084     }
1085 }
1086
1087
1088 // return the amount of glide slope needle deflection (.i.e. the
1089 // number of degrees we are off the glide slope * 5.0
1090 double FGRadioStack::get_nav2_gs_needle_deflection() const {
1091     if ( nav2_inrange && nav2_has_gs ) {
1092         double x = nav2_gs_dist;
1093         double y = (fgGetDouble("/position/altitude-ft") - nav2_elev)
1094             * SG_FEET_TO_METER;
1095         double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
1096         return (nav2_target_gs - angle) * 5.0;
1097     } else {
1098         return 0.0;
1099     }
1100 }
1101
1102
1103 /**
1104  * Return true if the NAV1 TO flag should be active.
1105  */
1106 bool 
1107 FGRadioStack::get_nav1_to_flag () const
1108 {
1109   if (nav1_inrange) {
1110     double offset = fabs(nav1_heading - nav1_radial);
1111     if (nav1_loc)
1112       return true;
1113     else
1114       return (offset <= 90.0 || offset >= 270.0);
1115   } else {
1116     return false;
1117   }
1118 }
1119
1120
1121 /**
1122  * Return true if the NAV1 FROM flag should be active.
1123  */
1124 bool
1125 FGRadioStack::get_nav1_from_flag () const
1126 {
1127   if (nav1_inrange) {
1128     double offset = fabs(nav1_heading - nav1_radial);
1129     if (nav1_loc)
1130       return false;
1131     else
1132       return (offset > 90.0 && offset < 270.0);
1133   } else {
1134     return false;
1135   }
1136 }
1137
1138
1139 /**
1140  * Return true if the NAV2 TO flag should be active.
1141  */
1142 bool 
1143 FGRadioStack::get_nav2_to_flag () const
1144 {
1145   if (nav2_inrange) {
1146     double offset = fabs(nav2_heading - nav2_radial);
1147     if (nav2_loc)
1148       return true;
1149     else
1150       return (offset <= 90.0 || offset >= 270.0);
1151   } else {
1152     return false;
1153   }
1154 }
1155
1156
1157 /**
1158  * Return true if the NAV2 FROM flag should be active.
1159  */
1160 bool
1161 FGRadioStack::get_nav2_from_flag () const
1162 {
1163   if (nav2_inrange) {
1164     double offset = fabs(nav2_heading - nav2_radial);
1165     if (nav2_loc)
1166       return false;
1167     else
1168       return (offset > 90.0 && offset < 270.0);
1169   } else {
1170     return false;
1171   }
1172 }
1173