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