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