1 // radiostack.cxx -- class to manage an instance of the radio stack
3 // Written by Curtis Olson, started April 2000.
5 // Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
28 #include <stdio.h> // snprintf
30 #include <simgear/compiler.h>
31 #include <simgear/math/sg_random.h>
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>
39 #include "radiostack.hxx"
45 FGRadioStack *current_radiostack;
49 * Boy, this is ugly! Make the VOR range vary by altitude difference.
51 static double kludgeRange ( double stationElev, double aircraftElev,
54 // Assume that the nominal range (usually
55 // 50nm) applies at a 5,000 ft difference.
57 double factor = ((aircraftElev*SG_METER_TO_FEET) - stationElev) / 5000.0;
58 double range = fabs(nominalRange * factor);
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;
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)),
88 SGPath path( globals->get_fg_root() );
90 term.append( "Navaids/range.term" );
92 low.append( "Navaids/range.low" );
94 high.append( "Navaids/range.high" );
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();
104 FGRadioStack::~FGRadioStack()
110 unbind(); // FIXME: should be called externally
119 FGRadioStack::init ()
121 navcom1.set_bind_index( 0 );
124 navcom2.set_bind_index( 1 );
140 update(0); // FIXME: use dt
146 // Search radio database once per second
147 global_events.Register( "fgRadioSearch()",
148 current_radiostack, &FGRadioStack::search,
153 FGRadioStack::bind ()
157 fgTie("/radios/dme/frequencies/selected-khz", this,
158 &FGRadioStack::get_dme_freq, &FGRadioStack::set_dme_freq);
161 fgTie("/radios/dme/in-range", this, &FGRadioStack::get_dme_inrange);
163 fgTie("/radios/dme/distance-nm", this, &FGRadioStack::get_dme_dist);
165 fgTie("/radios/dme/speed-kt", this, &FGRadioStack::get_dme_spd);
167 fgTie("/radios/dme/ete-min", this, &FGRadioStack::get_dme_ete);
169 fgTie("/radios/marker-beacon/inner", this,
170 &FGRadioStack::get_inner_blink);
172 fgTie("/radios/marker-beacon/middle", this,
173 &FGRadioStack::get_middle_blink);
175 fgTie("/radios/marker-beacon/outer", this,
176 &FGRadioStack::get_outer_blink);
178 navcom1.set_bind_index( 0 );
180 navcom2.set_bind_index( 1 );
187 FGRadioStack::unbind ()
189 fgUntie("/radios/dme/frequencies/selected-khz");
192 fgUntie("/radios/dme/in-range");
193 fgUntie("/radios/dme/distance-nm");
194 fgUntie("/radios/dme/speed-kt");
195 fgUntie("/radios/dme/ete-min");
197 fgUntie("/radios/marker-beacon/inner");
198 fgUntie("/radios/marker-beacon/middle");
199 fgUntie("/radios/marker-beacon/outer");
208 // Update the various nav values based on position and valid tuned in navs
210 FGRadioStack::update(double dt)
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;
218 Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
221 navcom1.update( dt );
222 navcom2.update( dt );
224 ////////////////////////////////////////////////////////////////////////
226 ////////////////////////////////////////////////////////////////////////
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) {
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);
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
249 double dDist = dme_dist - dme_prev_dist;
250 dme_spd = fabs((dDist/dMs) * 3600000);
251 // FIXME: the panel should be able to
255 dme_ete = fabs((dme_dist/dme_spd) * 60.0);
256 // FIXME: the panel should be able to
260 dme_prev_dist = dme_dist;
261 dme_last_time.stamp();
272 // marker beacon blinking
273 bool light_on = ( outer_blink || middle_blink || inner_blink );
277 if ( light_on && (current - blink > 400000) ) {
280 } else if ( !light_on && (current - blink > 100000) ) {
285 if ( outer_marker ) {
286 outer_blink = light_on;
291 if ( middle_marker ) {
292 middle_blink = light_on;
294 middle_blink = false;
297 if ( inner_marker ) {
298 inner_blink = light_on;
303 // cout << outer_blink << " " << middle_blink << " " << inner_blink << endl;
306 xponder.update( dt );
310 // Update current nav/adf radio stations based on current postition
311 void FGRadioStack::search()
313 static FGMkrBeacon::fgMkrBeacType last_beacon = FGMkrBeacon::NOBEACON;
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;
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();
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();
344 ////////////////////////////////////////////////////////////////////////
346 ////////////////////////////////////////////////////////////////////////
348 if ( current_ilslist->query( lon, lat, elev, dme_freq, &ils ) ) {
349 if (ils.get_has_dme()) {
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();
360 } else if ( current_navlist->query( lon, lat, elev, dme_freq, &nav ) ) {
361 if (nav.get_has_dme()) {
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);
378 ////////////////////////////////////////////////////////////////////////
380 ////////////////////////////////////////////////////////////////////////
382 FGMkrBeacon::fgMkrBeacType beacon_type
383 = current_beacons->query( lon * SGD_RADIANS_TO_DEGREES,
384 lat * SGD_RADIANS_TO_DEGREES, elev );
386 outer_marker = middle_marker = inner_marker = false;
388 if ( beacon_type == FGMkrBeacon::OUTER ) {
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" );
398 if ( !globals->get_soundmgr()->is_playing("outer-marker") ) {
399 globals->get_soundmgr()->play_looped( "outer-marker" );
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" );
413 if ( !globals->get_soundmgr()->is_playing("middle-marker") ) {
414 globals->get_soundmgr()->play_looped( "middle-marker" );
418 } else if ( beacon_type == FGMkrBeacon::INNER ) {
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" );
428 if ( !globals->get_soundmgr()->is_playing("inner-marker") ) {
429 globals->get_soundmgr()->play_looped( "inner-marker" );
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" );
441 last_beacon = beacon_type;