]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/radiostack.cxx
Removed some depricated code snippets.
[flightgear.git] / src / Cockpit / radiostack.cxx
1 // radiostack.cxx -- class to manage an instance of the radio stack
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000  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
33 #include <Aircraft/aircraft.hxx>
34 #include <Navaids/ilslist.hxx>
35 #include <Navaids/mkrbeacons.hxx>
36 #include <Navaids/navlist.hxx>
37 #include <Time/FGEventMgr.hxx>
38
39 #include "radiostack.hxx"
40
41 #include <string>
42 SG_USING_STD(string);
43
44
45 FGRadioStack *current_radiostack;
46
47
48 /**
49  * Boy, this is ugly!  Make the VOR range vary by altitude difference.
50  */
51 static double kludgeRange ( double stationElev, double aircraftElev,
52                             double nominalRange)
53 {
54                                 // Assume that the nominal range (usually
55                                 // 50nm) applies at a 5,000 ft difference.
56                                 // Just a wild guess!
57   double factor = ((aircraftElev*SG_METER_TO_FEET) - stationElev) / 5000.0;
58   double range = fabs(nominalRange * factor);
59
60                                 // Clamp the range to keep it sane; for
61                                 // now, never less than 25% or more than
62                                 // 500% of nominal range.
63   if (range < nominalRange/4.0) {
64     range = nominalRange/4.0;
65   } else if (range > nominalRange*5.0) {
66     range = nominalRange*5.0;
67   }
68
69   return range;
70 }
71
72
73 // Constructor
74 FGRadioStack::FGRadioStack() :
75     lon_node(fgGetNode("/position/longitude-deg", true)),
76     lat_node(fgGetNode("/position/latitude-deg", true)),
77     alt_node(fgGetNode("/position/altitude-ft", true)),
78     need_update(true),
79     dme_freq(0.0),
80     dme_dist(0.0),
81     dme_prev_dist(0.0),
82     dme_spd(0.0),
83     dme_ete(0.0),
84     outer_blink(false),
85     middle_blink(false),
86     inner_blink(false)
87 {
88     SGPath path( globals->get_fg_root() );
89     SGPath term = path;
90     term.append( "Navaids/range.term" );
91     SGPath low = path;
92     low.append( "Navaids/range.low" );
93     SGPath high = path;
94     high.append( "Navaids/range.high" );
95
96     term_tbl = new SGInterpTable( term.str() );
97     low_tbl = new SGInterpTable( low.str() );
98     high_tbl = new SGInterpTable( high.str() );
99     dme_last_time.stamp();
100 }
101
102
103 // Destructor
104 FGRadioStack::~FGRadioStack() 
105 {
106     navcom1.unbind();
107     navcom2.unbind();
108     adf.unbind();
109     xponder.unbind();
110     unbind();                   // FIXME: should be called externally
111
112     delete term_tbl;
113     delete low_tbl;
114     delete high_tbl;
115 }
116
117
118 void
119 FGRadioStack::init ()
120 {
121     navcom1.set_bind_index( 0 );
122     navcom1.init();
123
124     navcom2.set_bind_index( 1 );
125     navcom2.init();
126
127     adf.init();
128     xponder.init();
129
130     morse.init();
131     beacon.init();
132     blink.stamp();
133
134     search();
135     navcom1.search();
136     navcom2.search();
137     adf.search();
138     xponder.search();
139
140     update(0);                  // FIXME: use dt
141     navcom1.update(0);
142     navcom2.update(0);
143     adf.update(0);
144     xponder.update(0);
145
146     // Search radio database once per second
147     global_events.Register( "fgRadioSearch()",
148                             current_radiostack, &FGRadioStack::search,
149                             1000 );
150 }
151
152 void
153 FGRadioStack::bind ()
154 {
155
156                                 // User inputs
157     fgTie("/radios/dme/frequencies/selected-khz", this,
158           &FGRadioStack::get_dme_freq, &FGRadioStack::set_dme_freq);
159
160                                 // Radio outputs
161     fgTie("/radios/dme/in-range", this, &FGRadioStack::get_dme_inrange);
162
163     fgTie("/radios/dme/distance-nm", this, &FGRadioStack::get_dme_dist);
164
165     fgTie("/radios/dme/speed-kt", this, &FGRadioStack::get_dme_spd);
166
167     fgTie("/radios/dme/ete-min", this, &FGRadioStack::get_dme_ete);
168
169     fgTie("/radios/marker-beacon/inner", this,
170           &FGRadioStack::get_inner_blink);
171
172     fgTie("/radios/marker-beacon/middle", this,
173           &FGRadioStack::get_middle_blink);
174
175     fgTie("/radios/marker-beacon/outer", this,
176           &FGRadioStack::get_outer_blink);
177
178     navcom1.set_bind_index( 0 );
179     navcom1.bind();
180     navcom2.set_bind_index( 1 );
181     navcom2.bind();
182     adf.bind();
183     xponder.bind();
184 }
185
186 void
187 FGRadioStack::unbind ()
188 {
189     fgUntie("/radios/dme/frequencies/selected-khz");
190
191                                 // Radio outputs
192     fgUntie("/radios/dme/in-range");
193     fgUntie("/radios/dme/distance-nm");
194     fgUntie("/radios/dme/speed-kt");
195     fgUntie("/radios/dme/ete-min");
196
197     fgUntie("/radios/marker-beacon/inner");
198     fgUntie("/radios/marker-beacon/middle");
199     fgUntie("/radios/marker-beacon/outer");
200
201     navcom1.unbind();
202     navcom2.unbind();
203     adf.unbind();
204     xponder.unbind();
205 }
206
207
208 // Update the various nav values based on position and valid tuned in navs
209 void 
210 FGRadioStack::update(double dt) 
211 {
212     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
213     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
214     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
215
216     need_update = false;
217
218     Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
219     Point3D station;
220
221     navcom1.update( dt );
222     navcom2.update( dt );
223
224     ////////////////////////////////////////////////////////////////////////
225     // DME.
226     ////////////////////////////////////////////////////////////////////////
227
228     if ( dme_valid ) {
229         station = Point3D( dme_x, dme_y, dme_z );
230         dme_dist = aircraft.distance3D( station ) * SG_METER_TO_NM;
231         dme_effective_range = kludgeRange(dme_elev, elev, dme_range);
232         if (dme_dist < dme_effective_range * SG_NM_TO_METER) {
233             dme_inrange = true;
234         } else if (dme_dist < 2 * dme_effective_range * SG_NM_TO_METER) {
235             dme_inrange = sg_random() <
236                 (2 * dme_effective_range * SG_NM_TO_METER - dme_dist) /
237                 (dme_effective_range * SG_NM_TO_METER);
238         } else {
239             dme_inrange = false;
240         }
241         if ( dme_inrange ) {
242             SGTimeStamp current_time;
243             station = Point3D( dme_x, dme_y, dme_z );
244             dme_dist = aircraft.distance3D( station ) * SG_METER_TO_NM;
245             current_time.stamp();
246             long dMs = (current_time - dme_last_time) / 1000;
247                                 // Update every second
248             if (dMs >= 1000) {
249                 double dDist = dme_dist - dme_prev_dist;
250                 dme_spd = fabs((dDist/dMs) * 3600000);
251                                 // FIXME: the panel should be able to
252                                 // handle this!!!
253                 if (dme_spd > 999.0)
254                     dme_spd = 999.0;
255                 dme_ete = fabs((dme_dist/dme_spd) * 60.0);
256                                 // FIXME: the panel should be able to
257                                 // handle this!!!
258                 if (dme_ete > 99.0)
259                     dme_ete = 99.0;
260                 dme_prev_dist = dme_dist;
261                 dme_last_time.stamp();
262             }
263         }
264     } else {
265         dme_inrange = false;
266         dme_dist = 0.0;
267         dme_prev_dist = 0.0;
268         dme_spd = 0.0;
269         dme_ete = 0.0;
270     }
271
272     // marker beacon blinking
273     bool light_on = ( outer_blink || middle_blink || inner_blink );
274     SGTimeStamp current;
275     current.stamp();
276
277     if ( light_on && (current - blink > 400000) ) {
278         light_on = false;
279         blink.stamp();
280     } else if ( !light_on && (current - blink > 100000) ) {
281         light_on = true;
282         blink.stamp();
283     }
284
285     if ( outer_marker ) {
286         outer_blink = light_on;
287     } else {
288         outer_blink = false;
289     }
290
291     if ( middle_marker ) {
292         middle_blink = light_on;
293     } else {
294         middle_blink = false;
295     }
296
297     if ( inner_marker ) {
298         inner_blink = light_on;
299     } else {
300         inner_blink = false;
301     }
302
303     // cout << outer_blink << " " << middle_blink << " " << inner_blink << endl;
304
305     adf.update( dt );
306     xponder.update( dt );
307 }
308
309
310 // Update current nav/adf radio stations based on current postition
311 void FGRadioStack::search() 
312 {
313     static FGMkrBeacon::fgMkrBeacType last_beacon = FGMkrBeacon::NOBEACON;
314
315     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
316     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
317     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
318
319                                 // FIXME: the panel should handle this
320                                 // don't worry about overhead for now,
321                                 // since this is handled only periodically
322     int dme_switch_pos = fgGetInt("/radios/dme/switch-position");
323     if ( dme_switch_pos == 1 && navcom1.get_power_btn() ) {
324         if ( dme_freq != navcom1.get_nav_freq() ) {
325             dme_freq = navcom1.get_nav_freq();
326             need_update = true;
327         }
328     } else if ( dme_switch_pos == 3 && navcom2.get_power_btn() ) {
329         if ( dme_freq != navcom2.get_nav_freq() ) {
330             dme_freq = navcom2.get_nav_freq();
331             need_update = true;
332         }
333     } else {
334         dme_freq = 0;
335         dme_inrange = false;
336     }
337
338     FGILS ils;
339     FGNav nav;
340
341     navcom1.search();
342     navcom2.search();
343
344     ////////////////////////////////////////////////////////////////////////
345     // DME
346     ////////////////////////////////////////////////////////////////////////
347
348     if ( current_ilslist->query( lon, lat, elev, dme_freq, &ils ) ) {
349       if (ils.get_has_dme()) {
350         dme_valid = true;
351         dme_lon = ils.get_loclon();
352         dme_lat = ils.get_loclat();
353         dme_elev = ils.get_gselev();
354         dme_range = FG_ILS_DEFAULT_RANGE;
355         dme_effective_range = kludgeRange(dme_elev, elev, dme_range);
356         dme_x = ils.get_dme_x();
357         dme_y = ils.get_dme_y();
358         dme_z = ils.get_dme_z();
359       }
360     } else if ( current_navlist->query( lon, lat, elev, dme_freq, &nav ) ) {
361       if (nav.get_has_dme()) {
362         dme_valid = true;
363         dme_lon = nav.get_lon();
364         dme_lat = nav.get_lat();
365         dme_elev = nav.get_elev();
366         dme_range = nav.get_range();
367         dme_effective_range = kludgeRange(dme_elev, elev, dme_range);
368         dme_x = nav.get_x();
369         dme_y = nav.get_y();
370         dme_z = nav.get_z();
371       }
372     } else {
373       dme_valid = false;
374       dme_dist = 0;
375     }
376
377
378     ////////////////////////////////////////////////////////////////////////
379     // Beacons.
380     ////////////////////////////////////////////////////////////////////////
381
382     FGMkrBeacon::fgMkrBeacType beacon_type
383         = current_beacons->query( lon * SGD_RADIANS_TO_DEGREES,
384                                   lat * SGD_RADIANS_TO_DEGREES, elev );
385
386     outer_marker = middle_marker = inner_marker = false;
387
388     if ( beacon_type == FGMkrBeacon::OUTER ) {
389         outer_marker = true;
390         // cout << "OUTER MARKER" << endl;
391 #ifdef ENABLE_AUDIO_SUPPORT
392         if ( last_beacon != FGMkrBeacon::OUTER ) {
393             if ( ! globals->get_soundmgr()->exists( "outer-marker" ) ) {
394                 FGSimpleSound *sound = beacon.get_outer();
395                 sound->set_volume( 0.3 );
396                 globals->get_soundmgr()->add( sound, "outer-marker" );
397             }
398             if ( !globals->get_soundmgr()->is_playing("outer-marker") ) {
399                 globals->get_soundmgr()->play_looped( "outer-marker" );
400             }
401         }
402 #endif
403     } else if ( beacon_type == FGMkrBeacon::MIDDLE ) {
404         middle_marker = true;
405         // cout << "MIDDLE MARKER" << endl;
406 #ifdef ENABLE_AUDIO_SUPPORT
407         if ( last_beacon != FGMkrBeacon::MIDDLE ) {
408             if ( ! globals->get_soundmgr()->exists( "middle-marker" ) ) {
409                 FGSimpleSound *sound = beacon.get_middle();
410                 sound->set_volume( 0.3 );
411                 globals->get_soundmgr()->add( sound, "middle-marker" );
412             }
413             if ( !globals->get_soundmgr()->is_playing("middle-marker") ) {
414                 globals->get_soundmgr()->play_looped( "middle-marker" );
415             }
416         }
417 #endif
418     } else if ( beacon_type == FGMkrBeacon::INNER ) {
419         inner_marker = true;
420         // cout << "INNER MARKER" << endl;
421 #ifdef ENABLE_AUDIO_SUPPORT
422         if ( last_beacon != FGMkrBeacon::INNER ) {
423             if ( ! globals->get_soundmgr()->exists( "inner-marker" ) ) {
424                 FGSimpleSound *sound = beacon.get_inner();
425                 sound->set_volume( 0.3 );
426                 globals->get_soundmgr()->add( sound, "inner-marker" );
427             }
428             if ( !globals->get_soundmgr()->is_playing("inner-marker") ) {
429                 globals->get_soundmgr()->play_looped( "inner-marker" );
430             }
431         }
432 #endif
433     } else {
434         // cout << "no marker" << endl;
435 #ifdef ENABLE_AUDIO_SUPPORT
436         globals->get_soundmgr()->stop( "outer-marker" );
437         globals->get_soundmgr()->stop( "middle-marker" );
438         globals->get_soundmgr()->stop( "inner-marker" );
439 #endif
440     }
441     last_beacon = beacon_type;
442
443     navcom1.search();
444     navcom2.search();
445     adf.search();
446     xponder.search();
447 }