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