]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/navcom.cxx
Stabalize cloud shading.
[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/mkrbeacons.hxx>
36 #include <Navaids/navlist.hxx>
37 #include <Time/FGEventMgr.hxx>
38
39 #include "navcom.hxx"
40
41 #include <string>
42 SG_USING_STD(string);
43
44
45 /**
46  * Boy, this is ugly!  Make the VOR range vary by altitude difference.
47  */
48 static double kludgeRange ( double stationElev, double aircraftElev,
49                             double nominalRange)
50 {
51     // Assume that the nominal range (usually 50nm) applies at a 5,000
52     // ft difference.  Just a wild guess!
53     double factor = ((aircraftElev*SG_METER_TO_FEET) - stationElev) / 5000.0;
54     double range = fabs(nominalRange * factor);
55
56     // Clamp the range to keep it sane; for now, never less than 25%
57     // or more than 500% of nominal range.
58     if (range < nominalRange/4.0) {
59         range = nominalRange/4.0;
60     } else if (range > nominalRange*5.0) {
61         range = nominalRange*5.0;
62     }
63
64     return range;
65 }
66
67
68 // Constructor
69 FGNavCom::FGNavCom() :
70     lon_node(fgGetNode("/position/longitude-deg", true)),
71     lat_node(fgGetNode("/position/latitude-deg", true)),
72     alt_node(fgGetNode("/position/altitude-ft", true)),
73     last_nav_ident(""),
74     last_nav_vor(false),
75     nav_play_count(0),
76     nav_last_time(0),
77     need_update(true),
78     comm_freq(0.0),
79     comm_alt_freq(0.0),
80     comm_vol_btn(0.0),
81     nav_freq(0.0),
82     nav_alt_freq(0.0),
83     nav_radial(0.0),
84     nav_vol_btn(0.0)
85 {
86     SGPath path( globals->get_fg_root() );
87     SGPath term = path;
88     term.append( "Navaids/range.term" );
89     SGPath low = path;
90     low.append( "Navaids/range.low" );
91     SGPath high = path;
92     high.append( "Navaids/range.high" );
93
94     term_tbl = new SGInterpTable( term.str() );
95     low_tbl = new SGInterpTable( low.str() );
96     high_tbl = new SGInterpTable( high.str() );
97
98 }
99
100
101 // Destructor
102 FGNavCom::~FGNavCom() 
103 {
104     delete term_tbl;
105     delete low_tbl;
106     delete high_tbl;
107 }
108
109
110 void
111 FGNavCom::init ()
112 {
113     morse.init();
114     beacon.init();
115
116     search();
117
118     update(0);                  // FIXME: use dt
119 }
120
121 void
122 FGNavCom::bind ()
123 {
124     char propname[256];
125
126                                 // User inputs
127     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
128     fgTie( propname, this, &FGNavCom::get_comm_freq, &FGNavCom::set_comm_freq );
129     fgSetArchivable( propname );
130
131     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
132     fgTie( propname, this,
133            &FGNavCom::get_comm_alt_freq, &FGNavCom::set_comm_alt_freq );
134     fgSetArchivable( propname );
135
136     sprintf( propname, "/radios/comm[%d]/volume", index );
137     fgTie( propname, this,
138            &FGNavCom::get_comm_vol_btn, &FGNavCom::set_comm_vol_btn );
139     fgSetArchivable( propname );
140
141     sprintf( propname, "/radios/comm[%d]/ident", index );
142     fgTie( propname, this,
143           &FGNavCom::get_comm_ident_btn, &FGNavCom::set_comm_ident_btn );
144     fgSetArchivable( propname );
145
146     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
147     fgTie( propname, this,
148           &FGNavCom::get_nav_freq, &FGNavCom::set_nav_freq );
149     fgSetArchivable( propname );
150
151     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
152     fgTie( propname , this,
153            &FGNavCom::get_nav_alt_freq, &FGNavCom::set_nav_alt_freq);
154     fgSetArchivable( propname );
155
156     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
157     fgTie( propname, this,
158            &FGNavCom::get_nav_sel_radial, &FGNavCom::set_nav_sel_radial );
159     fgSetArchivable( propname );
160
161     sprintf( propname, "/radios/nav[%d]/volume", index );
162     fgTie( propname, this,
163            &FGNavCom::get_nav_vol_btn, &FGNavCom::set_nav_vol_btn );
164     fgSetArchivable( propname );
165
166     sprintf( propname, "/radios/nav[%d]/ident", index );
167     fgTie( propname, this,
168            &FGNavCom::get_nav_ident_btn, &FGNavCom::set_nav_ident_btn );
169     fgSetArchivable( propname );
170
171                                 // Radio outputs
172     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
173     fgTie( propname,  this, &FGNavCom::get_nav_radial );
174
175     sprintf( propname, "/radios/nav[%d]/to-flag", index );
176     fgTie( propname, this, &FGNavCom::get_nav_to_flag );
177
178     sprintf( propname, "/radios/nav[%d]/from-flag", index );
179     fgTie( propname, this, &FGNavCom::get_nav_from_flag );
180
181     sprintf( propname, "/radios/nav[%d]/in-range", index );
182     fgTie( propname, this, &FGNavCom::get_nav_inrange );
183
184     sprintf( propname, "/radios/nav[%d]/heading-needle-deflection", index );
185     fgTie( propname, this, &FGNavCom::get_nav_heading_needle_deflection );
186
187     sprintf( propname, "/radios/nav[%d]/gs-needle-deflection", index );
188     fgTie( propname, this, &FGNavCom::get_nav_gs_needle_deflection );
189 }
190
191
192 void
193 FGNavCom::unbind ()
194 {
195     char propname[256];
196
197     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
198     fgUntie( propname );
199     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
200     fgUntie( propname );
201     sprintf( propname, "/radios/comm[%d]/on", index );
202     fgUntie( propname );
203     sprintf( propname, "/radios/comm[%d]/ident", index );
204     fgUntie( propname );
205
206     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
207     fgUntie( propname );
208     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
209     fgUntie( propname );
210     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
211     fgUntie( propname );
212     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
213     fgUntie( propname );
214     sprintf( propname, "/radios/nav[%d]/on", 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 ) {
312         station = Point3D( nav_x, nav_y, nav_z );
313         nav_loc_dist = aircraft.distance3D( station );
314
315         if ( nav_has_gs ) {
316             station = Point3D( nav_gs_x, nav_gs_y, nav_gs_z );
317             nav_gs_dist = aircraft.distance3D( station );
318         } else {
319             nav_gs_dist = 0.0;
320         }
321         
322         // wgs84 heading
323         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
324                             nav_loclat, nav_loclon,
325                             &az1, &az2, &s );
326         // cout << "az1 = " << az1 << " magvar = " << nav_magvar << endl;
327         nav_heading = az1 - nav_magvar;
328         // cout << " heading = " << nav_heading
329         //      << " dist = " << nav_dist << endl;
330
331         if ( nav_loc ) {
332             double offset = nav_heading - nav_radial;
333             while ( offset < -180.0 ) { offset += 360.0; }
334             while ( offset > 180.0 ) { offset -= 360.0; }
335             // cout << "ils offset = " << offset << endl;
336             nav_effective_range = adjustILSRange(nav_elev, elev, offset,
337                                                   nav_loc_dist * SG_METER_TO_NM );
338         } else {
339             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
340         }
341         // cout << "nav range = " << nav_effective_range
342         //      << " (" << nav_range << ")" << endl;
343
344         if ( nav_loc_dist < nav_effective_range * SG_NM_TO_METER ) {
345             nav_inrange = true;
346         } else if ( nav_loc_dist < 2 * nav_effective_range * SG_NM_TO_METER ) {
347             nav_inrange = sg_random() < 
348                 ( 2 * nav_effective_range * SG_NM_TO_METER - nav_loc_dist ) /
349                 (nav_effective_range * SG_NM_TO_METER);
350         } else {
351             nav_inrange = false;
352         }
353
354         if ( !nav_loc ) {
355             nav_radial = nav_sel_radial;
356         }
357     } else {
358         nav_inrange = false;
359         // cout << "not picking up vor. :-(" << endl;
360     }
361
362 #ifdef ENABLE_AUDIO_SUPPORT
363     if ( nav_valid && nav_inrange ) {
364         // play station ident via audio system if on + ident,
365         // otherwise turn it off
366         if ( nav_vol_btn > 0.1 && nav_ident_btn ) {
367             FGSimpleSound *sound;
368             sound = globals->get_soundmgr()->find( nav_fx_name );
369             if ( sound != NULL ) {
370                 sound->set_volume( nav_vol_btn );
371             } else {
372                 SG_LOG( SG_COCKPIT, SG_ALERT,
373                         "Can't find nav-vor-ident sound" );
374             }
375             sound = globals->get_soundmgr()->find( dme_fx_name );
376             if ( sound != NULL ) {
377                 sound->set_volume( nav_vol_btn );
378             } else {
379                 SG_LOG( SG_COCKPIT, SG_ALERT,
380                         "Can't find nav-dme-ident sound" );
381             }
382             cout << "nav_last_time = " << nav_last_time << " ";
383             cout << "cur_time = " << globals->get_time_params()->get_cur_time();
384             if ( nav_last_time <
385                  globals->get_time_params()->get_cur_time() - 30 ) {
386                 nav_last_time = globals->get_time_params()->get_cur_time();
387                 nav_play_count = 0;
388             }
389             cout << " nav_play_count = " << nav_play_count << endl;
390             cout << "playing = "
391                  << globals->get_soundmgr()->is_playing(nav_fx_name)
392                  << endl;
393             if ( nav_play_count < 4 ) {
394                 // play VOR ident
395                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) {
396                     globals->get_soundmgr()->play_once( nav_fx_name );
397                     ++nav_play_count;
398                 }
399             } else if ( nav_play_count < 5 && nav_has_dme ) {
400                 // play DME ident
401                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) &&
402                      !globals->get_soundmgr()->is_playing(dme_fx_name) ) {
403                     globals->get_soundmgr()->play_once( dme_fx_name );
404                     ++nav_play_count;
405                 }
406             }
407         } else {
408             globals->get_soundmgr()->stop( nav_fx_name );
409             globals->get_soundmgr()->stop( dme_fx_name );
410         }
411     }
412 #endif
413
414 }
415
416
417 // Update current nav/adf radio stations based on current postition
418 void FGNavCom::search() 
419 {
420     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
421     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
422     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
423
424     FGILS ils;
425     FGNav nav;
426
427     ////////////////////////////////////////////////////////////////////////
428     // Nav.
429     ////////////////////////////////////////////////////////////////////////
430
431     if ( current_ilslist->query( lon, lat, elev, nav_freq, &ils ) ) {
432         nav_ident = ils.get_locident();
433         nav_valid = true;
434         if ( last_nav_ident != nav_ident || last_nav_vor ) {
435             nav_trans_ident = ils.get_trans_ident();
436             last_nav_ident = nav_ident;
437             last_nav_vor = false;
438             nav_loc = true;
439             nav_has_dme = ils.get_has_dme();
440             nav_has_gs = ils.get_has_gs();
441
442             nav_loclon = ils.get_loclon();
443             nav_loclat = ils.get_loclat();
444             nav_gslon = ils.get_gslon();
445             nav_gslat = ils.get_gslat();
446             nav_elev = ils.get_gselev();
447             nav_magvar = 0;
448             nav_range = FG_ILS_DEFAULT_RANGE;
449             nav_effective_range = nav_range;
450             nav_target_gs = ils.get_gsangle();
451             nav_radial = ils.get_locheading();
452             while ( nav_radial <   0.0 ) { nav_radial += 360.0; }
453             while ( nav_radial > 360.0 ) { nav_radial -= 360.0; }
454             nav_x = ils.get_x();
455             nav_y = ils.get_y();
456             nav_z = ils.get_z();
457             nav_gs_x = ils.get_gs_x();
458             nav_gs_y = ils.get_gs_y();
459             nav_gs_z = ils.get_gs_z();
460
461 #ifdef ENABLE_AUDIO_SUPPORT
462             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
463                 globals->get_soundmgr()->remove( nav_fx_name );
464             }
465             FGSimpleSound *sound;
466             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
467             sound->set_volume( 0.3 );
468             globals->get_soundmgr()->add( sound, nav_fx_name );
469
470             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
471                 globals->get_soundmgr()->remove( dme_fx_name );
472             }
473             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
474             sound->set_volume( 0.3 );
475             globals->get_soundmgr()->add( sound, dme_fx_name );
476
477             int offset = (int)(sg_random() * 30.0);
478             nav_play_count = offset / 4;
479             nav_last_time = globals->get_time_params()->get_cur_time() -
480                 offset;
481             cout << "offset = " << offset << " play_count = "
482                  << nav_play_count
483                  << " nav_last_time = " << nav_last_time
484                  << " current time = "
485                  << globals->get_time_params()->get_cur_time() << endl;
486 #endif
487
488             // cout << "Found an ils station in range" << endl;
489             // cout << " id = " << ils.get_locident() << endl;
490         }
491     } else if ( current_navlist->query( lon, lat, elev, nav_freq, &nav ) ) {
492         nav_ident = nav.get_ident();
493         nav_valid = true;
494         if ( last_nav_ident != nav_ident || !last_nav_vor ) {
495             last_nav_ident = nav_ident;
496             last_nav_vor = true;
497             nav_trans_ident = nav.get_trans_ident();
498             nav_loc = false;
499             nav_has_dme = nav.get_has_dme();
500             nav_has_gs = false;
501             nav_loclon = nav.get_lon();
502             nav_loclat = nav.get_lat();
503             nav_elev = nav.get_elev();
504             nav_magvar = nav.get_magvar();
505             nav_range = nav.get_range();
506             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
507             nav_target_gs = 0.0;
508             nav_radial = nav_sel_radial;
509             nav_x = nav.get_x();
510             nav_y = nav.get_y();
511             nav_z = nav.get_z();
512
513 #ifdef ENABLE_AUDIO_SUPPORT
514             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
515                 globals->get_soundmgr()->remove( nav_fx_name );
516             }
517             FGSimpleSound *sound;
518             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
519             sound->set_volume( 0.3 );
520             if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) {
521                 cout << "Added nav-vor-ident sound" << endl;
522             } else {
523                 cout << "Failed to add v1-vor-ident sound" << endl;
524             }
525
526             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
527                 globals->get_soundmgr()->remove( dme_fx_name );
528             }
529             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
530             sound->set_volume( 0.3 );
531             globals->get_soundmgr()->add( sound, dme_fx_name );
532
533             int offset = (int)(sg_random() * 30.0);
534             nav_play_count = offset / 4;
535             nav_last_time = globals->get_time_params()->get_cur_time() -
536                 offset;
537             cout << "offset = " << offset << " play_count = "
538                  << nav_play_count << " nav_last_time = "
539                  << nav_last_time << " current time = "
540                  << globals->get_time_params()->get_cur_time() << endl;
541 #endif
542
543             // cout << "Found a vor station in range" << endl;
544             // cout << " id = " << nav.get_ident() << endl;
545         }
546     } else {
547         nav_valid = false;
548         nav_ident = "";
549         nav_radial = 0;
550         nav_trans_ident = "";
551         last_nav_ident = "";
552 #ifdef ENABLE_AUDIO_SUPPORT
553         if ( ! globals->get_soundmgr()->remove( nav_fx_name ) ) {
554             cout << "Failed to remove nav-vor-ident sound" << endl;
555         }
556         globals->get_soundmgr()->remove( dme_fx_name );
557 #endif
558         // cout << "not picking up vor1. :-(" << endl;
559     }
560 }
561
562
563 // return the amount of heading needle deflection, returns a value
564 // clamped to the range of ( -10 , 10 )
565 double FGNavCom::get_nav_heading_needle_deflection() const {
566     double r;
567
568     if ( nav_inrange ) {
569         r = nav_heading - nav_radial;
570         // cout << "Radial = " << nav_radial 
571         //      << "  Bearing = " << nav_heading << endl;
572     
573         while ( r >  180.0 ) { r -= 360.0;}
574         while ( r < -180.0 ) { r += 360.0;}
575         if ( fabs(r) > 90.0 ) {
576             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
577             if ( nav_loc ) {
578                 r = -r;
579             }
580         }
581
582         // According to Robin Peel, the ILS is 4x more sensitive than a vor
583         if ( nav_loc ) { r *= 4.0; }
584         if ( r < -10.0 ) { r = -10.0; }
585         if ( r >  10.0 ) { r = 10.0; }
586     } else {
587         r = 0.0;
588     }
589
590     return r;
591 }
592
593
594 // return the amount of glide slope needle deflection (.i.e. the
595 // number of degrees we are off the glide slope * 5.0
596 double FGNavCom::get_nav_gs_needle_deflection() const {
597     if ( nav_inrange && nav_has_gs ) {
598         double x = nav_gs_dist;
599         double y = (fgGetDouble("/position/altitude-ft") - nav_elev)
600             * SG_FEET_TO_METER;
601         double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
602         return (nav_target_gs - angle) * 5.0;
603     } else {
604         return 0.0;
605     }
606 }
607
608
609 /**
610  * Return true if the NAV TO flag should be active.
611  */
612 bool 
613 FGNavCom::get_nav_to_flag () const
614 {
615   if (nav_inrange) {
616     double offset = fabs(nav_heading - nav_radial);
617     if (nav_loc)
618       return true;
619     else
620       return (offset <= 90.0 || offset >= 270.0);
621   } else {
622     return false;
623   }
624 }
625
626
627 /**
628  * Return true if the NAV FROM flag should be active.
629  */
630 bool
631 FGNavCom::get_nav_from_flag () const
632 {
633   if (nav_inrange) {
634     double offset = fabs(nav_heading - nav_radial);
635     if (nav_loc)
636       return false;
637     else
638       return (offset > 90.0 && offset < 270.0);
639   } else {
640     return false;
641   }
642 }