]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/navcom.cxx
Additional failure modeling.
[flightgear.git] / src / Cockpit / navcom.cxx
1 // navcom.cxx -- class to manage a navcom instance
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000 - 2002  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 <stdio.h>      // snprintf
29
30 #include <simgear/compiler.h>
31 #include <simgear/math/sg_random.h>
32
33 #include <Aircraft/aircraft.hxx>
34 #include <Navaids/ilslist.hxx>
35 #include <Navaids/navlist.hxx>
36 #include <Time/FGEventMgr.hxx>
37
38 #include "navcom.hxx"
39
40 #include <string>
41 SG_USING_STD(string);
42
43
44 // Constructor
45 FGNavCom::FGNavCom() :
46     lon_node(fgGetNode("/position/longitude-deg", true)),
47     lat_node(fgGetNode("/position/latitude-deg", true)),
48     alt_node(fgGetNode("/position/altitude-ft", true)),
49     last_nav_id(""),
50     last_nav_vor(false),
51     nav_play_count(0),
52     nav_last_time(0),
53     need_update(true),
54     power_btn(true),
55     comm_freq(0.0),
56     comm_alt_freq(0.0),
57     comm_vol_btn(0.0),
58     nav_freq(0.0),
59     nav_alt_freq(0.0),
60     nav_radial(0.0),
61     nav_vol_btn(0.0),
62     nav_ident_btn(true)
63 {
64     SGPath path( globals->get_fg_root() );
65     SGPath term = path;
66     term.append( "Navaids/range.term" );
67     SGPath low = path;
68     low.append( "Navaids/range.low" );
69     SGPath high = path;
70     high.append( "Navaids/range.high" );
71
72     term_tbl = new SGInterpTable( term.str() );
73     low_tbl = new SGInterpTable( low.str() );
74     high_tbl = new SGInterpTable( high.str() );
75
76 }
77
78
79 // Destructor
80 FGNavCom::~FGNavCom() 
81 {
82     delete term_tbl;
83     delete low_tbl;
84     delete high_tbl;
85 }
86
87
88 void
89 FGNavCom::init ()
90 {
91     morse.init();
92
93     // We assume that index is valid now (it must be set before init()
94     // is called.)
95     char propname[256];
96
97     sprintf( propname, "/systems/electrical/outputs/navcom[%d]", index );
98     // default to true in case no electrical system defined.
99     fgSetDouble( propname, 60.0 );
100     bus_power = fgGetNode( propname, true );
101
102     sprintf( propname, "/instrumentation/comm[%d]/servicable", index );
103     com_servicable = fgGetNode( propname, true );
104     com_servicable->setBoolValue( true );
105
106     sprintf( propname, "/instrumentation/nav[%d]/servicable", index );
107     nav_servicable = fgGetNode( propname, true );
108     nav_servicable->setBoolValue( true );
109
110     sprintf( propname, "/instrumentation/vor[%d]/cdi/servicable", index );
111     cdi_servicable = fgGetNode( propname, true );
112     cdi_servicable->setBoolValue( true );
113
114     sprintf( propname, "/instrumentation/vor[%d]/gs/servicable", index );
115     gs_servicable = fgGetNode( propname, true );
116     gs_servicable->setBoolValue( true );
117
118     sprintf( propname, "/instrumentation/vor[%d]/to-from/servicable", index );
119     tofrom_servicable = fgGetNode( propname, true );
120     tofrom_servicable->setBoolValue( true );
121 }
122
123 void
124 FGNavCom::bind ()
125 {
126     char propname[256];
127
128                                 // User inputs
129     sprintf( propname, "/radios/comm[%d]/inputs/power-btn", index );
130     fgTie( propname, this,
131            &FGNavCom::get_power_btn, &FGNavCom::set_power_btn );
132     fgSetArchivable( propname );
133
134     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
135     fgTie( propname, this, &FGNavCom::get_comm_freq, &FGNavCom::set_comm_freq );
136     fgSetArchivable( propname );
137
138     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
139     fgTie( propname, this,
140            &FGNavCom::get_comm_alt_freq, &FGNavCom::set_comm_alt_freq );
141     fgSetArchivable( propname );
142
143     sprintf( propname, "/radios/comm[%d]/volume", index );
144     fgTie( propname, this,
145            &FGNavCom::get_comm_vol_btn, &FGNavCom::set_comm_vol_btn );
146     fgSetArchivable( propname );
147
148     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
149     fgTie( propname, this,
150           &FGNavCom::get_nav_freq, &FGNavCom::set_nav_freq );
151     fgSetArchivable( propname );
152
153     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
154     fgTie( propname , this,
155            &FGNavCom::get_nav_alt_freq, &FGNavCom::set_nav_alt_freq);
156     fgSetArchivable( propname );
157
158     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
159     fgTie( propname, this,
160            &FGNavCom::get_nav_sel_radial, &FGNavCom::set_nav_sel_radial );
161     fgSetArchivable( propname );
162
163     sprintf( propname, "/radios/nav[%d]/volume", index );
164     fgTie( propname, this,
165            &FGNavCom::get_nav_vol_btn, &FGNavCom::set_nav_vol_btn );
166     fgSetArchivable( propname );
167
168     sprintf( propname, "/radios/nav[%d]/ident", index );
169     fgTie( propname, this,
170            &FGNavCom::get_nav_ident_btn, &FGNavCom::set_nav_ident_btn );
171     fgSetArchivable( propname );
172
173                                 // Radio outputs
174     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
175     fgTie( propname,  this, &FGNavCom::get_nav_radial );
176
177     sprintf( propname, "/radios/nav[%d]/to-flag", index );
178     fgTie( propname, this, &FGNavCom::get_nav_to_flag );
179
180     sprintf( propname, "/radios/nav[%d]/from-flag", index );
181     fgTie( propname, this, &FGNavCom::get_nav_from_flag );
182
183     sprintf( propname, "/radios/nav[%d]/in-range", index );
184     fgTie( propname, this, &FGNavCom::get_nav_inrange );
185
186     sprintf( propname, "/radios/nav[%d]/heading-needle-deflection", index );
187     fgTie( propname, this, &FGNavCom::get_nav_heading_needle_deflection );
188
189     sprintf( propname, "/radios/nav[%d]/gs-needle-deflection", index );
190     fgTie( propname, this, &FGNavCom::get_nav_gs_needle_deflection );
191
192     // end of binding
193 }
194
195
196 void
197 FGNavCom::unbind ()
198 {
199     char propname[256];
200
201     sprintf( propname, "/radios/comm[%d]/inputs/power-btn", index );
202     fgUntie( propname );
203     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
204     fgUntie( propname );
205     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
206     fgUntie( propname );
207
208     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
209     fgUntie( propname );
210     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
211     fgUntie( propname );
212     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
213     fgUntie( propname );
214     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
215     fgUntie( propname );
216     sprintf( propname, "/radios/nav[%d]/ident", index );
217     fgUntie( propname );
218     sprintf( propname, "/radios/nav[%d]/to-flag", index );
219     fgUntie( propname );
220     sprintf( propname, "/radios/nav[%d]/from-flag", index );
221     fgUntie( propname );
222     sprintf( propname, "/radios/nav[%d]/in-range", index );
223     fgUntie( propname );
224     sprintf( propname, "/radios/nav[%d]/heading-needle-deflection", index );
225     fgUntie( propname );
226     sprintf( propname, "/radios/nav[%d]/gs-needle-deflection", index );
227     fgUntie( propname );
228 }
229
230
231 // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
232 double FGNavCom::adjustNavRange( double stationElev, double aircraftElev,
233                                  double nominalRange )
234 {
235     // extend out actual usable range to be 1.3x the published safe range
236     const double usability_factor = 1.3;
237
238     // assumptions we model the standard service volume, plus
239     // ... rather than specifying a cylinder, we model a cone that
240     // contains the cylinder.  Then we put an upside down cone on top
241     // to model diminishing returns at too-high altitudes.
242
243     // altitude difference
244     double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
245     // cout << "aircraft elev = " << aircraftElev * SG_METER_TO_FEET
246     //      << " station elev = " << stationElev << endl;
247
248     if ( nominalRange < 25.0 + SG_EPSILON ) {
249         // Standard Terminal Service Volume
250         return term_tbl->interpolate( alt ) * usability_factor;
251     } else if ( nominalRange < 50.0 + SG_EPSILON ) {
252         // Standard Low Altitude Service Volume
253         // table is based on range of 40, scale to actual range
254         return low_tbl->interpolate( alt ) * nominalRange / 40.0
255             * usability_factor;
256     } else {
257         // Standard High Altitude Service Volume
258         // table is based on range of 130, scale to actual range
259         return high_tbl->interpolate( alt ) * nominalRange / 130.0
260             * usability_factor;
261     }
262 }
263
264
265 // model standard ILS service volumes as per AIM 1-1-9
266 double FGNavCom::adjustILSRange( double stationElev, double aircraftElev,
267                                      double offsetDegrees, double distance )
268 {
269     // assumptions we model the standard service volume, plus
270
271     // altitude difference
272     // double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
273     double offset = fabs( offsetDegrees );
274
275     if ( offset < 10 ) {
276         return FG_ILS_DEFAULT_RANGE;
277     } else if ( offset < 35 ) {
278         return 10 + (35 - offset) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
279     } else if ( offset < 45 ) {
280         return (45 - offset);
281     } else if ( offset > 170 ) {
282         return FG_ILS_DEFAULT_RANGE;
283     } else if ( offset > 145 ) {
284         return 10 + (offset - 145) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
285     } else if ( offset > 135 ) {
286         return (offset - 135);
287     } else {
288         return 0;
289     }
290 }
291
292
293 // Update the various nav values based on position and valid tuned in navs
294 void 
295 FGNavCom::update(double dt) 
296 {
297     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
298     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
299     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
300
301     need_update = false;
302
303     Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
304     Point3D station;
305     double az1, az2, s;
306
307     ////////////////////////////////////////////////////////////////////////
308     // Nav.
309     ////////////////////////////////////////////////////////////////////////
310
311     if ( nav_valid && power_btn && (bus_power->getDoubleValue() > 1.0)
312          && nav_servicable->getBoolValue() )
313     {
314         station = Point3D( nav_x, nav_y, nav_z );
315         nav_loc_dist = aircraft.distance3D( station );
316
317         if ( nav_has_gs ) {
318             station = Point3D( nav_gs_x, nav_gs_y, nav_gs_z );
319             nav_gs_dist = aircraft.distance3D( station );
320         } else {
321             nav_gs_dist = 0.0;
322         }
323         
324         // wgs84 heading
325         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
326                             nav_loclat, nav_loclon,
327                             &az1, &az2, &s );
328         // cout << "az1 = " << az1 << " magvar = " << nav_magvar << endl;
329         nav_heading = az1 - nav_magvar;
330         // cout << " heading = " << nav_heading
331         //      << " dist = " << nav_dist << endl;
332
333         if ( nav_loc ) {
334             double offset = nav_heading - nav_radial;
335             while ( offset < -180.0 ) { offset += 360.0; }
336             while ( offset > 180.0 ) { offset -= 360.0; }
337             // cout << "ils offset = " << offset << endl;
338             nav_effective_range = adjustILSRange(nav_elev, elev, offset,
339                                                   nav_loc_dist * SG_METER_TO_NM );
340         } else {
341             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
342         }
343         // cout << "nav range = " << nav_effective_range
344         //      << " (" << nav_range << ")" << endl;
345
346         if ( nav_loc_dist < nav_effective_range * SG_NM_TO_METER ) {
347             nav_inrange = true;
348         } else if ( nav_loc_dist < 2 * nav_effective_range * SG_NM_TO_METER ) {
349             nav_inrange = sg_random() < 
350                 ( 2 * nav_effective_range * SG_NM_TO_METER - nav_loc_dist ) /
351                 (nav_effective_range * SG_NM_TO_METER);
352         } else {
353             nav_inrange = false;
354         }
355
356         if ( !nav_loc ) {
357             nav_radial = nav_sel_radial;
358         }
359     } else {
360         nav_inrange = false;
361         // cout << "not picking up vor. :-(" << endl;
362     }
363
364 #ifdef ENABLE_AUDIO_SUPPORT
365     if ( nav_valid && nav_inrange && nav_servicable->getBoolValue() ) {
366         // play station ident via audio system if on + ident,
367         // otherwise turn it off
368         if ( power_btn && (bus_power->getDoubleValue() > 1.0)
369              && nav_ident_btn )
370         {
371             FGSimpleSound *sound;
372             sound = globals->get_soundmgr()->find( nav_fx_name );
373             if ( sound != NULL ) {
374                 sound->set_volume( nav_vol_btn );
375             } else {
376                 SG_LOG( SG_COCKPIT, SG_ALERT,
377                         "Can't find nav-vor-ident sound" );
378             }
379             sound = globals->get_soundmgr()->find( dme_fx_name );
380             if ( sound != NULL ) {
381                 sound->set_volume( nav_vol_btn );
382             } else {
383                 SG_LOG( SG_COCKPIT, SG_ALERT,
384                         "Can't find nav-dme-ident sound" );
385             }
386             // cout << "nav_last_time = " << nav_last_time << " ";
387             // cout << "cur_time = "
388             //      << globals->get_time_params()->get_cur_time();
389             if ( nav_last_time <
390                  globals->get_time_params()->get_cur_time() - 30 ) {
391                 nav_last_time = globals->get_time_params()->get_cur_time();
392                 nav_play_count = 0;
393             }
394             // cout << " nav_play_count = " << nav_play_count << endl;
395             // cout << "playing = "
396             //      << globals->get_soundmgr()->is_playing(nav_fx_name)
397             //      << endl;
398             if ( nav_play_count < 4 ) {
399                 // play VOR ident
400                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) {
401                     globals->get_soundmgr()->play_once( nav_fx_name );
402                     ++nav_play_count;
403                 }
404             } else if ( nav_play_count < 5 && nav_has_dme ) {
405                 // play DME ident
406                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) &&
407                      !globals->get_soundmgr()->is_playing(dme_fx_name) ) {
408                     globals->get_soundmgr()->play_once( dme_fx_name );
409                     ++nav_play_count;
410                 }
411             }
412         } else {
413             globals->get_soundmgr()->stop( nav_fx_name );
414             globals->get_soundmgr()->stop( dme_fx_name );
415         }
416     }
417 #endif
418
419 }
420
421
422 // Update current nav/adf radio stations based on current postition
423 void FGNavCom::search() 
424 {
425     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
426     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
427     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
428
429     FGILS ils;
430     FGNav nav;
431
432     ////////////////////////////////////////////////////////////////////////
433     // Nav.
434     ////////////////////////////////////////////////////////////////////////
435
436     if ( current_ilslist->query( lon, lat, elev, nav_freq, &ils ) ) {
437         nav_id = ils.get_locident();
438         nav_valid = true;
439         if ( last_nav_id != nav_id || last_nav_vor ) {
440             nav_trans_ident = ils.get_trans_ident();
441             last_nav_id = nav_id;
442             last_nav_vor = false;
443             nav_loc = true;
444             nav_has_dme = ils.get_has_dme();
445             nav_has_gs = ils.get_has_gs();
446
447             nav_loclon = ils.get_loclon();
448             nav_loclat = ils.get_loclat();
449             nav_gslon = ils.get_gslon();
450             nav_gslat = ils.get_gslat();
451             nav_elev = ils.get_gselev();
452             nav_magvar = 0;
453             nav_range = FG_ILS_DEFAULT_RANGE;
454             nav_effective_range = nav_range;
455             nav_target_gs = ils.get_gsangle();
456             nav_radial = ils.get_locheading();
457             while ( nav_radial <   0.0 ) { nav_radial += 360.0; }
458             while ( nav_radial > 360.0 ) { nav_radial -= 360.0; }
459             nav_x = ils.get_x();
460             nav_y = ils.get_y();
461             nav_z = ils.get_z();
462             nav_gs_x = ils.get_gs_x();
463             nav_gs_y = ils.get_gs_y();
464             nav_gs_z = ils.get_gs_z();
465
466 #ifdef ENABLE_AUDIO_SUPPORT
467             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
468                 globals->get_soundmgr()->remove( nav_fx_name );
469             }
470             FGSimpleSound *sound;
471             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
472             sound->set_volume( 0.3 );
473             globals->get_soundmgr()->add( sound, nav_fx_name );
474
475             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
476                 globals->get_soundmgr()->remove( dme_fx_name );
477             }
478             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
479             sound->set_volume( 0.3 );
480             globals->get_soundmgr()->add( sound, dme_fx_name );
481
482             int offset = (int)(sg_random() * 30.0);
483             nav_play_count = offset / 4;
484             nav_last_time = globals->get_time_params()->get_cur_time() -
485                 offset;
486             // cout << "offset = " << offset << " play_count = "
487             //      << nav_play_count
488             //      << " nav_last_time = " << nav_last_time
489             //      << " current time = "
490             //      << globals->get_time_params()->get_cur_time() << endl;
491 #endif
492
493             // cout << "Found an ils station in range" << endl;
494             // cout << " id = " << ils.get_locident() << endl;
495         }
496     } else if ( current_navlist->query( lon, lat, elev, nav_freq, &nav ) ) {
497         nav_id = nav.get_ident();
498         nav_valid = true;
499         if ( last_nav_id != nav_id || !last_nav_vor ) {
500             last_nav_id = nav_id;
501             last_nav_vor = true;
502             nav_trans_ident = nav.get_trans_ident();
503             nav_loc = false;
504             nav_has_dme = nav.get_has_dme();
505             nav_has_gs = false;
506             nav_loclon = nav.get_lon();
507             nav_loclat = nav.get_lat();
508             nav_elev = nav.get_elev();
509             nav_magvar = nav.get_magvar();
510             nav_range = nav.get_range();
511             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
512             nav_target_gs = 0.0;
513             nav_radial = nav_sel_radial;
514             nav_x = nav.get_x();
515             nav_y = nav.get_y();
516             nav_z = nav.get_z();
517
518 #ifdef ENABLE_AUDIO_SUPPORT
519             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
520                 globals->get_soundmgr()->remove( nav_fx_name );
521             }
522             FGSimpleSound *sound;
523             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
524             sound->set_volume( 0.3 );
525             if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) {
526                 // cout << "Added nav-vor-ident sound" << endl;
527             } else {
528                 cout << "Failed to add v1-vor-ident sound" << endl;
529             }
530
531             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
532                 globals->get_soundmgr()->remove( dme_fx_name );
533             }
534             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
535             sound->set_volume( 0.3 );
536             globals->get_soundmgr()->add( sound, dme_fx_name );
537
538             int offset = (int)(sg_random() * 30.0);
539             nav_play_count = offset / 4;
540             nav_last_time = globals->get_time_params()->get_cur_time() -
541                 offset;
542             // cout << "offset = " << offset << " play_count = "
543             //      << nav_play_count << " nav_last_time = "
544             //      << nav_last_time << " current time = "
545             //      << globals->get_time_params()->get_cur_time() << endl;
546 #endif
547
548             // cout << "Found a vor station in range" << endl;
549             // cout << " id = " << nav.get_ident() << endl;
550         }
551     } else {
552         nav_valid = false;
553         nav_id = "";
554         nav_radial = 0;
555         nav_trans_ident = "";
556         last_nav_id = "";
557 #ifdef ENABLE_AUDIO_SUPPORT
558         if ( ! globals->get_soundmgr()->remove( nav_fx_name ) ) {
559             cout << "Failed to remove nav-vor-ident sound" << endl;
560         }
561         globals->get_soundmgr()->remove( dme_fx_name );
562 #endif
563         // cout << "not picking up vor1. :-(" << endl;
564     }
565 }
566
567
568 // return the amount of heading needle deflection, returns a value
569 // clamped to the range of ( -10 , 10 )
570 double FGNavCom::get_nav_heading_needle_deflection() const {
571     double r;
572
573     if ( nav_inrange
574          && nav_servicable->getBoolValue() && cdi_servicable->getBoolValue() )
575     {
576         r = nav_heading - nav_radial;
577         // cout << "Radial = " << nav_radial 
578         //      << "  Bearing = " << nav_heading << endl;
579     
580         while ( r >  180.0 ) { r -= 360.0;}
581         while ( r < -180.0 ) { r += 360.0;}
582         if ( fabs(r) > 90.0 )
583             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
584
585         // According to Robin Peel, the ILS is 4x more sensitive than a vor
586         if ( nav_loc ) { r *= 4.0; }
587         if ( r < -10.0 ) { r = -10.0; }
588         if ( r >  10.0 ) { r = 10.0; }
589     } else {
590         r = 0.0;
591     }
592
593     return r;
594 }
595
596
597 // return the amount of glide slope needle deflection (.i.e. the
598 // number of degrees we are off the glide slope * 5.0
599 double FGNavCom::get_nav_gs_needle_deflection() const {
600     if ( nav_inrange && nav_has_gs
601          && nav_servicable->getBoolValue() && gs_servicable->getBoolValue() )
602     {
603         double x = nav_gs_dist;
604         double y = (fgGetDouble("/position/altitude-ft") - nav_elev)
605             * SG_FEET_TO_METER;
606         double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
607         return (nav_target_gs - angle) * 5.0;
608     } else {
609         return 0.0;
610     }
611 }
612
613
614 /**
615  * Return true if the NAV TO flag should be active.
616  */
617 bool 
618 FGNavCom::get_nav_to_flag () const
619 {
620     if ( nav_inrange
621          && nav_servicable->getBoolValue()
622          && tofrom_servicable->getBoolValue() )
623     {
624         double offset = fabs(nav_heading - nav_radial);
625         if (nav_loc) {
626             return true;
627         } else {
628             return (offset <= 90.0 || offset >= 270.0);
629         }
630     } else {
631         return false;
632     }
633 }
634
635
636 /**
637  * Return true if the NAV FROM flag should be active.
638  */
639 bool
640 FGNavCom::get_nav_from_flag () const
641 {
642     if ( nav_inrange
643          && nav_servicable->getBoolValue()
644          && tofrom_servicable->getBoolValue() ) {
645         double offset = fabs(nav_heading - nav_radial);
646         if (nav_loc) {
647             return false;
648         } else {
649           return (offset > 90.0 && offset < 270.0);
650         }
651     } else {
652         return false;
653     }
654 }