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