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