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