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