]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/navcom.cxx
Don't scale elevator by 0.5.
[flightgear.git] / src / Cockpit / navcom.cxx
1 // navcom.cxx -- class to manage a navcom instance
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000 - 2002  Curtis L. Olson - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <stdio.h>      // snprintf
29
30 #include <simgear/compiler.h>
31 #include <simgear/math/sg_random.h>
32 #include <simgear/math/vector.hxx>
33
34 #include <Aircraft/aircraft.hxx>
35 #include <Navaids/ilslist.hxx>
36 #include <Navaids/navlist.hxx>
37
38 #include "navcom.hxx"
39
40 #include <string>
41 SG_USING_STD(string);
42
43
44 // Constructor
45 FGNavCom::FGNavCom() :
46     lon_node(fgGetNode("/position/longitude-deg", true)),
47     lat_node(fgGetNode("/position/latitude-deg", true)),
48     alt_node(fgGetNode("/position/altitude-ft", true)),
49     last_nav_id(""),
50     last_nav_vor(false),
51     nav_play_count(0),
52     nav_last_time(0),
53     need_update(true),
54     power_btn(true),
55     audio_btn(true),
56     comm_freq(0.0),
57     comm_alt_freq(0.0),
58     comm_vol_btn(0.0),
59     nav_freq(0.0),
60     nav_alt_freq(0.0),
61     nav_target_radial(0.0),
62     nav_vol_btn(0.0),
63     nav_ident_btn(true)
64 {
65     SGPath path( globals->get_fg_root() );
66     SGPath term = path;
67     term.append( "Navaids/range.term" );
68     SGPath low = path;
69     low.append( "Navaids/range.low" );
70     SGPath high = path;
71     high.append( "Navaids/range.high" );
72
73     term_tbl = new SGInterpTable( term.str() );
74     low_tbl = new SGInterpTable( low.str() );
75     high_tbl = new SGInterpTable( high.str() );
76
77 }
78
79
80 // Destructor
81 FGNavCom::~FGNavCom() 
82 {
83     delete term_tbl;
84     delete low_tbl;
85     delete high_tbl;
86 }
87
88
89 void
90 FGNavCom::init ()
91 {
92     morse.init();
93
94     // We assume that index is valid now (it must be set before init()
95     // is called.)
96     char propname[256];
97     // FIXME: Get rid of snprintf
98
99     snprintf(propname, 256, "/systems/electrical/outputs/navcom[%d]", index);
100     // default to true in case no electrical system defined.
101     fgSetDouble( propname, 60.0 );
102     bus_power = fgGetNode( propname, true );
103
104     snprintf(propname, 256, "/instrumentation/comm[%d]/serviceable", index);
105     com_serviceable = fgGetNode( propname, true );
106     com_serviceable->setBoolValue( true );
107
108     snprintf(propname, 256, "/instrumentation/nav[%d]/serviceable", index);
109     nav_serviceable = fgGetNode( propname, true );
110     nav_serviceable->setBoolValue( true );
111
112     snprintf(propname, 256, "/instrumentation/vor[%d]/cdi/serviceable", index);
113     cdi_serviceable = fgGetNode( propname, true );
114     cdi_serviceable->setBoolValue( true );
115
116     snprintf(propname, 256, "/instrumentation/vor[%d]/gs/serviceable", index);
117     gs_serviceable = fgGetNode( propname, true );
118     gs_serviceable->setBoolValue( true );
119
120     snprintf(propname, 256, "/instrumentation/vor[%d]/to-from/serviceable", index);
121     tofrom_serviceable = fgGetNode( propname, true );
122     tofrom_serviceable->setBoolValue( true );
123 }
124
125 void
126 FGNavCom::bind ()
127 {
128     char propname[256];
129     // FIXME: Get rid of snprintf
130
131                                 // User inputs
132     snprintf(propname, 256, "/radios/comm[%d]/inputs/power-btn", index);
133     fgTie( propname, this,
134            &FGNavCom::get_power_btn, &FGNavCom::set_power_btn );
135     fgSetArchivable( propname );
136
137     snprintf(propname, 256, "/radios/comm[%d]/frequencies/selected-mhz", index);
138     fgTie( propname, this, &FGNavCom::get_comm_freq, &FGNavCom::set_comm_freq );
139     fgSetArchivable( propname );
140
141     snprintf(propname, 256, "/radios/comm[%d]/frequencies/standby-mhz", index);
142     fgTie( propname, this,
143            &FGNavCom::get_comm_alt_freq, &FGNavCom::set_comm_alt_freq );
144     fgSetArchivable( propname );
145
146     snprintf(propname, 256, "/radios/comm[%d]/volume", index);
147     fgTie( propname, this,
148            &FGNavCom::get_comm_vol_btn, &FGNavCom::set_comm_vol_btn );
149     fgSetArchivable( propname );
150
151     snprintf(propname, 256, "/radios/nav[%d]/frequencies/selected-mhz", index);
152     fgTie( propname, this,
153           &FGNavCom::get_nav_freq, &FGNavCom::set_nav_freq );
154     fgSetArchivable( propname );
155
156     snprintf(propname, 256, "/radios/nav[%d]/frequencies/standby-mhz", index);
157     fgTie( propname , this,
158            &FGNavCom::get_nav_alt_freq, &FGNavCom::set_nav_alt_freq);
159     fgSetArchivable( propname );
160
161     snprintf(propname, 256, "/radios/nav[%d]/radials/selected-deg", index);
162     fgTie( propname, this,
163            &FGNavCom::get_nav_sel_radial, &FGNavCom::set_nav_sel_radial );
164     fgSetArchivable( propname );
165
166     snprintf(propname, 256, "/radios/nav[%d]/volume", index);
167     fgTie( propname, this,
168            &FGNavCom::get_nav_vol_btn, &FGNavCom::set_nav_vol_btn );
169     fgSetArchivable( propname );
170
171     snprintf(propname, 256, "/radios/nav[%d]/ident", index);
172     fgTie( propname, this,
173            &FGNavCom::get_nav_ident_btn, &FGNavCom::set_nav_ident_btn );
174     fgSetArchivable( propname );
175
176                                 // Radio outputs
177     snprintf(propname, 256, "/radios/nav[%d]/audio-btn", index);
178     fgTie( propname, this,
179            &FGNavCom::get_audio_btn, &FGNavCom::set_audio_btn );
180     fgSetArchivable( propname );
181
182     snprintf(propname, 256, "/radios/nav[%d]/radials/actual-deg", index);
183     fgTie( propname,  this, &FGNavCom::get_nav_radial );
184
185     snprintf(propname, 256, "/radios/nav[%d]/to-flag", index);
186     fgTie( propname, this, &FGNavCom::get_nav_to_flag );
187
188     snprintf(propname, 256, "/radios/nav[%d]/from-flag", index);
189     fgTie( propname, this, &FGNavCom::get_nav_from_flag );
190
191     snprintf(propname, 256, "/radios/nav[%d]/in-range", index);
192     fgTie( propname, this, &FGNavCom::get_nav_inrange );
193
194     snprintf(propname, 256, "/radios/nav[%d]/heading-needle-deflection", index);
195     fgTie( propname, this, &FGNavCom::get_nav_cdi_deflection );
196
197     snprintf(propname, 256, "/radios/nav[%d]/has-gs", index);
198     fgTie( propname, this, &FGNavCom::get_nav_has_gs );
199
200     snprintf(propname, 256, "/radios/nav[%d]/nav-loc", index);
201     fgTie( propname, this, &FGNavCom::get_nav_loc );
202
203     snprintf(propname, 256, "/radios/nav[%d]/gs-needle-deflection", index);
204     fgTie( propname, this, &FGNavCom::get_nav_gs_deflection );
205
206     snprintf(propname, 256, "/radios/nav[%d]/nav-id", index);
207     fgTie( propname, this, &FGNavCom::get_nav_id );
208
209     // put nav_id characters into seperate properties for instrument displays
210     snprintf(propname, 256, "/radios/nav[%d]/nav-id_asc1", index);
211     fgTie( propname, this, &FGNavCom::get_nav_id_c1 );
212
213     snprintf(propname, 256, "/radios/nav[%d]/nav-id_asc2", index);
214     fgTie( propname, this, &FGNavCom::get_nav_id_c2 );
215
216     snprintf(propname, 256, "/radios/nav[%d]/nav-id_asc3", index);
217     fgTie( propname, this, &FGNavCom::get_nav_id_c3 );
218
219     snprintf(propname, 256, "/radios/nav[%d]/nav-id_asc4", index);
220     fgTie( propname, this, &FGNavCom::get_nav_id_c4 );
221
222     // end of binding
223 }
224
225
226 void
227 FGNavCom::unbind ()
228 {
229     char propname[256];
230     // FIXME: Get rid of snprintf
231
232     snprintf(propname, 256, "/radios/comm[%d]/inputs/power-btn", index);
233     fgUntie( propname );
234     snprintf(propname, 256, "/radios/comm[%d]/frequencies/selected-mhz", index);
235     fgUntie( propname );
236     snprintf(propname, 256, "/radios/comm[%d]/frequencies/standby-mhz", index);
237     fgUntie( propname );
238
239     snprintf(propname, 256, "/radios/nav[%d]/frequencies/selected-mhz", index);
240     fgUntie( propname );
241     snprintf(propname, 256, "/radios/nav[%d]/frequencies/standby-mhz", index);
242     fgUntie( propname );
243     snprintf(propname, 256, "/radios/nav[%d]/radials/actual-deg", index);
244     fgUntie( propname );
245     snprintf(propname, 256, "/radios/nav[%d]/radials/selected-deg", index);
246     fgUntie( propname );
247     snprintf(propname, 256, "/radios/nav[%d]/ident", index);
248     fgUntie( propname );
249     snprintf(propname, 256, "/radios/nav[%d]/to-flag", index);
250     fgUntie( propname );
251     snprintf(propname, 256, "/radios/nav[%d]/from-flag", index);
252     fgUntie( propname );
253     snprintf(propname, 256, "/radios/nav[%d]/in-range", index);
254     fgUntie( propname );
255     snprintf(propname, 256, "/radios/nav[%d]/heading-needle-deflection", index);
256     fgUntie( propname );
257     snprintf(propname, 256, "/radios/nav[%d]/gs-needle-deflection", index);
258     fgUntie( propname );
259 }
260
261
262 // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
263 double FGNavCom::adjustNavRange( double stationElev, double aircraftElev,
264                                  double nominalRange )
265 {
266     // extend out actual usable range to be 1.3x the published safe range
267     const double usability_factor = 1.3;
268
269     // assumptions we model the standard service volume, plus
270     // ... rather than specifying a cylinder, we model a cone that
271     // contains the cylinder.  Then we put an upside down cone on top
272     // to model diminishing returns at too-high altitudes.
273
274     // altitude difference
275     double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
276     // cout << "aircraft elev = " << aircraftElev * SG_METER_TO_FEET
277     //      << " station elev = " << stationElev << endl;
278
279     if ( nominalRange < 25.0 + SG_EPSILON ) {
280         // Standard Terminal Service Volume
281         return term_tbl->interpolate( alt ) * usability_factor;
282     } else if ( nominalRange < 50.0 + SG_EPSILON ) {
283         // Standard Low Altitude Service Volume
284         // table is based on range of 40, scale to actual range
285         return low_tbl->interpolate( alt ) * nominalRange / 40.0
286             * usability_factor;
287     } else {
288         // Standard High Altitude Service Volume
289         // table is based on range of 130, scale to actual range
290         return high_tbl->interpolate( alt ) * nominalRange / 130.0
291             * usability_factor;
292     }
293 }
294
295
296 // model standard ILS service volumes as per AIM 1-1-9
297 double FGNavCom::adjustILSRange( double stationElev, double aircraftElev,
298                                      double offsetDegrees, double distance )
299 {
300     // assumptions we model the standard service volume, plus
301
302     // altitude difference
303     // double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
304 //     double offset = fabs( offsetDegrees );
305
306 //     if ( offset < 10 ) {
307 //      return FG_ILS_DEFAULT_RANGE;
308 //     } else if ( offset < 35 ) {
309 //      return 10 + (35 - offset) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
310 //     } else if ( offset < 45 ) {
311 //      return (45 - offset);
312 //     } else if ( offset > 170 ) {
313 //         return FG_ILS_DEFAULT_RANGE;
314 //     } else if ( offset > 145 ) {
315 //      return 10 + (offset - 145) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
316 //     } else if ( offset > 135 ) {
317 //         return (offset - 135);
318 //     } else {
319 //      return 0;
320 //     }
321     return FG_ILS_DEFAULT_RANGE;
322 }
323
324
325 // Update the various nav values based on position and valid tuned in navs
326 void 
327 FGNavCom::update(double dt) 
328 {
329     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
330     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
331     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
332
333     need_update = false;
334
335     Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
336     Point3D station;
337     double az1, az2, s;
338
339     ////////////////////////////////////////////////////////////////////////
340     // Nav.
341     ////////////////////////////////////////////////////////////////////////
342
343     if ( nav_valid && power_btn && (bus_power->getDoubleValue() > 1.0)
344          && nav_serviceable->getBoolValue() )
345     {
346         station = Point3D( nav_x, nav_y, nav_z );
347         nav_loc_dist = aircraft.distance3D( station );
348
349         if ( nav_has_gs ) {
350             // find closest distance to the gs base line
351             sgdVec3 p;
352             sgdSetVec3( p, aircraft.x(), aircraft.y(), aircraft.z() );
353             sgdVec3 p0;
354             sgdSetVec3( p0, nav_gs_x, nav_gs_y, nav_gs_z );
355             double dist = sgdClosestPointToLineDistSquared( p, p0,
356                                                             gs_base_vec );
357             nav_gs_dist = sqrt( dist );
358             // cout << nav_gs_dist;
359
360             // Point3D tmp( nav_gs_x, nav_gs_y, nav_gs_z );
361             // cout << " (" << aircraft.distance3D( tmp ) << ")" << endl;
362
363             // wgs84 heading to glide slope (to determine sign of distance)
364             geo_inverse_wgs_84( elev,
365                                 lat * SGD_RADIANS_TO_DEGREES,
366                                 lon * SGD_RADIANS_TO_DEGREES, 
367                                 nav_gslat, nav_gslon,
368                                 &az1, &az2, &s );
369             double r = az1 - nav_target_radial;
370             while ( r >  180.0 ) { r -= 360.0;}
371             while ( r < -180.0 ) { r += 360.0;}
372             if ( r >= -90.0 && r <= 90.0 ) {
373                 nav_gs_dist_signed = nav_gs_dist;
374             } else {
375                 nav_gs_dist_signed = -nav_gs_dist;
376             }
377             /* cout << "Target Radial = " << nav_target_radial 
378                  << "  Bearing = " << az1
379                  << "  dist (signed) = " << nav_gs_dist_signed
380                  << endl; */
381             
382         } else {
383             nav_gs_dist = 0.0;
384         }
385         
386         // wgs84 heading to localizer
387         geo_inverse_wgs_84( elev,
388                             lat * SGD_RADIANS_TO_DEGREES,
389                             lon * SGD_RADIANS_TO_DEGREES, 
390                             nav_loclat, nav_loclon,
391                             &az1, &az2, &s );
392         // cout << "az1 = " << az1 << " magvar = " << nav_magvar << endl;
393         nav_radial = az2 - nav_twist;
394         // cout << " heading = " << nav_heading
395         //      << " dist = " << nav_dist << endl;
396
397         if ( nav_loc ) {
398             double offset = nav_radial - nav_target_radial;
399             while ( offset < -180.0 ) { offset += 360.0; }
400             while ( offset > 180.0 ) { offset -= 360.0; }
401             // cout << "ils offset = " << offset << endl;
402             nav_effective_range = adjustILSRange(nav_elev, elev, offset,
403                                                   nav_loc_dist * SG_METER_TO_NM );
404         } else {
405             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
406         }
407         // cout << "nav range = " << nav_effective_range
408         //      << " (" << nav_range << ")" << endl;
409
410         if ( nav_loc_dist < nav_effective_range * SG_NM_TO_METER ) {
411             nav_inrange = true;
412         } else if ( nav_loc_dist < 2 * nav_effective_range * SG_NM_TO_METER ) {
413             nav_inrange = sg_random() < 
414                 ( 2 * nav_effective_range * SG_NM_TO_METER - nav_loc_dist ) /
415                 (nav_effective_range * SG_NM_TO_METER);
416         } else {
417             nav_inrange = false;
418         }
419
420         if ( !nav_loc ) {
421             nav_target_radial = nav_sel_radial;
422         }
423     } else {
424         nav_inrange = false;
425         // cout << "not picking up vor. :-(" << endl;
426     }
427
428     if ( nav_valid && nav_inrange && nav_serviceable->getBoolValue() ) {
429         // play station ident via audio system if on + ident,
430         // otherwise turn it off
431         if ( power_btn && (bus_power->getDoubleValue() > 1.0)
432              && nav_ident_btn && audio_btn )
433         {
434             SGSimpleSound *sound;
435             sound = globals->get_soundmgr()->find( nav_fx_name );
436             if ( sound != NULL ) {
437                 sound->set_volume( nav_vol_btn );
438             } else {
439                 SG_LOG( SG_COCKPIT, SG_ALERT,
440                         "Can't find nav-vor-ident sound" );
441             }
442             sound = globals->get_soundmgr()->find( dme_fx_name );
443             if ( sound != NULL ) {
444                 sound->set_volume( nav_vol_btn );
445             } else {
446                 SG_LOG( SG_COCKPIT, SG_ALERT,
447                         "Can't find nav-dme-ident sound" );
448             }
449             // cout << "nav_last_time = " << nav_last_time << " ";
450             // cout << "cur_time = "
451             //      << globals->get_time_params()->get_cur_time();
452             if ( nav_last_time <
453                  globals->get_time_params()->get_cur_time() - 30 ) {
454                 nav_last_time = globals->get_time_params()->get_cur_time();
455                 nav_play_count = 0;
456             }
457             // cout << " nav_play_count = " << nav_play_count << endl;
458             // cout << "playing = "
459             //      << globals->get_soundmgr()->is_playing(nav_fx_name)
460             //      << endl;
461             if ( nav_play_count < 4 ) {
462                 // play VOR ident
463                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) {
464                     globals->get_soundmgr()->play_once( nav_fx_name );
465                     ++nav_play_count;
466                 }
467             } else if ( nav_play_count < 5 && nav_has_dme ) {
468                 // play DME ident
469                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) &&
470                      !globals->get_soundmgr()->is_playing(dme_fx_name) ) {
471                     globals->get_soundmgr()->play_once( dme_fx_name );
472                     ++nav_play_count;
473                 }
474             }
475         } else {
476             globals->get_soundmgr()->stop( nav_fx_name );
477             globals->get_soundmgr()->stop( dme_fx_name );
478         }
479     }
480 }
481
482
483 // Update current nav/adf radio stations based on current postition
484 void FGNavCom::search() 
485 {
486     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
487     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
488     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
489
490     FGILS *ils;
491     FGNav *nav;
492
493     ////////////////////////////////////////////////////////////////////////
494     // Nav.
495     ////////////////////////////////////////////////////////////////////////
496
497     if ( (ils = current_ilslist->findByFreq(nav_freq, lon, lat, elev)) != NULL ) {
498         nav_id = ils->get_locident();
499         nav_valid = true;
500         if ( last_nav_id != nav_id || last_nav_vor ) {
501             nav_trans_ident = ils->get_trans_ident();
502             last_nav_id = nav_id;
503             last_nav_vor = false;
504             nav_loc = true;
505             nav_has_dme = ils->get_has_dme();
506             nav_has_gs = ils->get_has_gs();
507
508             nav_loclon = ils->get_loclon();
509             nav_loclat = ils->get_loclat();
510             nav_gslon = ils->get_gslon();
511             nav_gslat = ils->get_gslat();
512             nav_elev = ils->get_gselev();
513             nav_twist = 0;
514             nav_range = FG_ILS_DEFAULT_RANGE;
515             nav_effective_range = nav_range;
516             nav_target_gs = ils->get_gsangle();
517             nav_target_radial = ils->get_locheading();
518             while ( nav_target_radial <   0.0 ) { nav_target_radial += 360.0; }
519             while ( nav_target_radial > 360.0 ) { nav_target_radial -= 360.0; }
520             nav_x = ils->get_x();
521             nav_y = ils->get_y();
522             nav_z = ils->get_z();
523             nav_gs_x = ils->get_gs_x();
524             nav_gs_y = ils->get_gs_y();
525             nav_gs_z = ils->get_gs_z();
526
527             // derive GS baseline
528             double tlon, tlat, taz;
529             geo_direct_wgs_84 ( 0.0, nav_gslat, nav_gslon, nav_target_radial + 90,  
530                                 100.0, &tlat, &tlon, &taz );
531             // cout << nav_gslon << "," << nav_gslat << "  "
532             //      << tlon << "," << tlat << "  (" << nav_elev << ")" << endl;
533             Point3D p1 = sgGeodToCart( Point3D(tlon*SGD_DEGREES_TO_RADIANS,
534                                                tlat*SGD_DEGREES_TO_RADIANS,
535                                                nav_elev*SG_FEET_TO_METER) );
536             // cout << nav_gs_x << "," << nav_gs_y << "," << nav_gs_z << endl;
537             // cout << p1 << endl;
538             sgdSetVec3( gs_base_vec,
539                         p1.x()-nav_gs_x, p1.y()-nav_gs_y, p1.z()-nav_gs_z );
540             // cout << gs_base_vec[0] << "," << gs_base_vec[1] << ","
541             //      << gs_base_vec[2] << endl;
542
543             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
544                 globals->get_soundmgr()->remove( nav_fx_name );
545             }
546             SGSimpleSound *sound;
547             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
548             sound->set_volume( 0.3 );
549             globals->get_soundmgr()->add( sound, nav_fx_name );
550
551             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
552                 globals->get_soundmgr()->remove( dme_fx_name );
553             }
554             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
555             sound->set_volume( 0.3 );
556             globals->get_soundmgr()->add( sound, dme_fx_name );
557
558             int offset = (int)(sg_random() * 30.0);
559             nav_play_count = offset / 4;
560             nav_last_time = globals->get_time_params()->get_cur_time() -
561                 offset;
562             // cout << "offset = " << offset << " play_count = "
563             //      << nav_play_count
564             //      << " nav_last_time = " << nav_last_time
565             //      << " current time = "
566             //      << globals->get_time_params()->get_cur_time() << endl;
567
568             // cout << "Found an ils station in range" << endl;
569             // cout << " id = " << ils->get_locident() << endl;
570         }
571     } else if ( (nav = current_navlist->findByFreq(nav_freq, lon, lat, elev)) != NULL ) {
572         nav_id = nav->get_ident();
573         nav_valid = (nav->get_type() == 'V');
574         if ( last_nav_id != nav_id || !last_nav_vor ) {
575             last_nav_id = nav_id;
576             last_nav_vor = true;
577             nav_trans_ident = nav->get_trans_ident();
578             nav_loc = false;
579             nav_has_dme = nav->get_has_dme();
580             nav_has_gs = false;
581             nav_loclon = nav->get_lon();
582             nav_loclat = nav->get_lat();
583             nav_elev = nav->get_elev();
584             nav_twist = nav->get_magvar();
585             nav_range = nav->get_range();
586             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
587             nav_target_gs = 0.0;
588             nav_target_radial = nav_sel_radial;
589             nav_x = nav->get_x();
590             nav_y = nav->get_y();
591             nav_z = nav->get_z();
592
593             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
594                 globals->get_soundmgr()->remove( nav_fx_name );
595             }
596             SGSimpleSound *sound;
597             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
598             sound->set_volume( 0.3 );
599             if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) {
600                 // cout << "Added nav-vor-ident sound" << endl;
601             } else {
602                 SG_LOG(SG_COCKPIT, SG_WARN, "Failed to add v1-vor-ident sound");
603             }
604
605             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
606                 globals->get_soundmgr()->remove( dme_fx_name );
607             }
608             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
609             sound->set_volume( 0.3 );
610             globals->get_soundmgr()->add( sound, dme_fx_name );
611
612             int offset = (int)(sg_random() * 30.0);
613             nav_play_count = offset / 4;
614             nav_last_time = globals->get_time_params()->get_cur_time() -
615                 offset;
616             // cout << "offset = " << offset << " play_count = "
617             //      << nav_play_count << " nav_last_time = "
618             //      << nav_last_time << " current time = "
619             //      << globals->get_time_params()->get_cur_time() << endl;
620
621             // cout << "Found a vor station in range" << endl;
622             // cout << " id = " << nav->get_ident() << endl;
623         }
624     } else {
625         nav_valid = false;
626         nav_id = "";
627         nav_target_radial = 0;
628         nav_trans_ident = "";
629         last_nav_id = "";
630         if ( ! globals->get_soundmgr()->remove( nav_fx_name ) ) {
631             SG_LOG(SG_COCKPIT, SG_WARN, "Failed to remove nav-vor-ident sound");
632         }
633         globals->get_soundmgr()->remove( dme_fx_name );
634         // cout << "not picking up vor1. :-(" << endl;
635     }
636 }
637
638
639 // return the amount of heading needle deflection, returns a value
640 // clamped to the range of ( -10 , 10 )
641 double FGNavCom::get_nav_cdi_deflection() const {
642     double r;
643
644     if ( nav_inrange
645          && nav_serviceable->getBoolValue() && cdi_serviceable->getBoolValue() )
646     {
647         r = nav_radial - nav_target_radial;
648         // cout << "Target radial = " << nav_target_radial 
649         //      << "  Actual radial = " << nav_radial << endl;
650     
651         while ( r >  180.0 ) { r -= 360.0;}
652         while ( r < -180.0 ) { r += 360.0;}
653         if ( fabs(r) > 90.0 )
654             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
655
656         // According to Robin Peel, the ILS is 4x more sensitive than a vor
657         r = -r;                 // reverse, since radial is outbound
658         if ( nav_loc ) { r *= 4.0; }
659         if ( r < -10.0 ) { r = -10.0; }
660         if ( r >  10.0 ) { r = 10.0; }
661     } else {
662         r = 0.0;
663     }
664
665     return r;
666 }
667
668
669 // return the amount of glide slope needle deflection (.i.e. the
670 // number of degrees we are off the glide slope * 5.0
671 double FGNavCom::get_nav_gs_deflection() const {
672     if ( nav_inrange && nav_has_gs
673          && nav_serviceable->getBoolValue() && gs_serviceable->getBoolValue() )
674     {
675         double x = nav_gs_dist;
676         double y = (fgGetDouble("/position/altitude-ft") - nav_elev)
677             * SG_FEET_TO_METER;
678         // cout << "dist = " << x << " height = " << y << endl;
679         double angle = asin( y / x ) * SGD_RADIANS_TO_DEGREES;
680         return (nav_target_gs - angle) * 5.0;
681     } else {
682         return 0.0;
683     }
684 }
685
686
687 /**
688  * Return true if the NAV TO flag should be active.
689  */
690 bool 
691 FGNavCom::get_nav_to_flag () const
692 {
693     if ( nav_inrange
694          && nav_serviceable->getBoolValue()
695          && tofrom_serviceable->getBoolValue() )
696     {
697         double offset = fabs(nav_radial - nav_target_radial);
698         if (nav_loc) {
699             return true;
700         } else {
701             return !(offset <= 90.0 || offset >= 270.0);
702         }
703     } else {
704         return false;
705     }
706 }
707
708
709 /**
710  * Return true if the NAV FROM flag should be active.
711  */
712 bool
713 FGNavCom::get_nav_from_flag () const
714 {
715     if ( nav_inrange
716          && nav_serviceable->getBoolValue()
717          && tofrom_serviceable->getBoolValue() ) {
718         double offset = fabs(nav_radial - nav_target_radial);
719         if (nav_loc) {
720             return false;
721         } else {
722           return !(offset > 90.0 && offset < 270.0);
723         }
724     } else {
725         return false;
726     }
727 }
728
729
730 /**
731  * Return the current radial.
732  */
733 double
734 FGNavCom::get_nav_radial () const
735 {
736     return nav_radial;
737 }
738
739 double
740 FGNavCom::get_nav_reciprocal_radial () const
741 {
742     double recip = nav_radial + 180;
743     if ( recip >= 360 ) {
744         recip -= 360;
745     }
746     return recip;
747 }