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