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