1 // kr-87.cxx -- class to impliment the King KR 87 Digital ADF
3 // Written by Curtis Olson, started April 2002.
5 // Copyright (C) 2002 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/navlist.hxx>
35 #include <Time/FGEventMgr.hxx>
42 static int adf_play_count = 0;
43 static time_t adf_last_time = 0;
47 * Boy, this is ugly! Make the VOR range vary by altitude difference.
49 static double kludgeRange ( double stationElev, double aircraftElev,
52 // Assume that the nominal range (usually
53 // 50nm) applies at a 5,000 ft difference.
55 double factor = ((aircraftElev*SG_METER_TO_FEET) - stationElev) / 5000.0;
56 double range = fabs(nominalRange * factor);
58 // Clamp the range to keep it sane; for
59 // now, never less than 25% or more than
60 // 500% of nominal range.
61 if (range < nominalRange/4.0) {
62 range = nominalRange/4.0;
63 } else if (range > nominalRange*5.0) {
64 range = nominalRange*5.0;
73 lon_node(fgGetNode("/position/longitude-deg", true)),
74 lat_node(fgGetNode("/position/latitude-deg", true)),
75 alt_node(fgGetNode("/position/altitude-ft", true)),
81 SGPath path( globals->get_fg_root() );
83 term.append( "Navaids/range.term" );
85 low.append( "Navaids/range.low" );
87 high.append( "Navaids/range.high" );
89 term_tbl = new SGInterpTable( term.str() );
90 low_tbl = new SGInterpTable( low.str() );
91 high_tbl = new SGInterpTable( high.str() );
109 update(0); // FIXME: use dt
116 fgTie("/radios/adf/frequencies/selected-khz", this,
117 &FGKR_87::get_adf_freq, &FGKR_87::set_adf_freq);
118 fgSetArchivable("/radios/adf/frequencies/selected-khz");
119 fgTie("/radios/adf/frequencies/standby-khz", this,
120 &FGKR_87::get_adf_alt_freq, &FGKR_87::set_adf_alt_freq);
121 fgSetArchivable("/radios/adf/frequencies/standby-khz");
122 fgTie("/radios/adf/rotation-deg", this,
123 &FGKR_87::get_adf_rotation, &FGKR_87::set_adf_rotation);
124 fgSetArchivable("/radios/adf/rotation-deg");
125 fgTie("/radios/adf/volume", this,
126 &FGKR_87::get_adf_vol_btn,
127 &FGKR_87::set_adf_vol_btn);
128 fgSetArchivable("/radios/adf/volume");
129 fgTie("/radios/adf/ident", this,
130 &FGKR_87::get_adf_ident_btn,
131 &FGKR_87::set_adf_ident_btn);
132 fgSetArchivable("/radios/adf/ident");
135 fgTie("/radios/adf/inrange", this, &FGKR_87::get_adf_inrange);
136 fgTie("/radios/adf/heading", this, &FGKR_87::get_adf_heading);
142 fgUntie("/radios/adf/frequencies/selected-khz");
143 fgUntie("/radios/adf/frequencies/standby-khz");
144 fgUntie("/radios/adf/rotation-deg");
145 fgUntie("/radios/adf/on");
146 fgUntie("/radios/adf/ident");
147 fgUntie("/radios/adf/inrange");
148 fgUntie("/radios/adf/heading");
152 // Update the various nav values based on position and valid tuned in navs
154 FGKR_87::update(double dt)
156 double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
157 double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
158 double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
162 Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
166 ////////////////////////////////////////////////////////////////////////
168 ////////////////////////////////////////////////////////////////////////
171 // staightline distance
172 station = Point3D( adf_x, adf_y, adf_z );
173 adf_dist = aircraft.distance3D( station );
176 geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES,
177 lon * SGD_RADIANS_TO_DEGREES,
181 // cout << " heading = " << adf_heading
182 // << " dist = " << adf_dist << endl;
184 adf_effective_range = kludgeRange(adf_elev, elev, adf_range);
185 if ( adf_dist < adf_effective_range * SG_NM_TO_METER ) {
187 } else if ( adf_dist < 2 * adf_effective_range * SG_NM_TO_METER ) {
188 adf_inrange = sg_random() <
189 ( 2 * adf_effective_range * SG_NM_TO_METER - adf_dist ) /
190 (adf_effective_range * SG_NM_TO_METER);
198 #ifdef ENABLE_AUDIO_SUPPORT
199 if ( adf_valid && adf_inrange ) {
200 // play station ident via audio system if on + ident,
201 // otherwise turn it off
202 if ( adf_vol_btn > 0.1 && adf_ident_btn ) {
203 FGSimpleSound *sound;
204 sound = globals->get_soundmgr()->find( "adf-ident" );
205 if ( sound != NULL ) {
206 sound->set_volume( adf_vol_btn );
208 SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find adf-ident sound" );
211 globals->get_time_params()->get_cur_time() - 30 ) {
212 adf_last_time = globals->get_time_params()->get_cur_time();
215 if ( adf_play_count < 4 ) {
217 if ( !globals->get_soundmgr()->is_playing("adf-ident") ) {
218 globals->get_soundmgr()->play_once( "adf-ident" );
223 globals->get_soundmgr()->stop( "adf-ident" );
230 // Update current nav/adf radio stations based on current postition
231 void FGKR_87::search()
233 double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
234 double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
235 double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
237 // FIXME: the panel should handle this
240 static string last_adf_ident = "";
242 ////////////////////////////////////////////////////////////////////////
244 ////////////////////////////////////////////////////////////////////////
246 if ( current_navlist->query( lon, lat, elev, adf_freq, &nav ) ) {
248 snprintf( freq, 10, "%.0f", adf_freq );
250 adf_ident += nav.get_ident();
251 // cout << "adf ident = " << adf_ident << endl;
253 if ( last_adf_ident != adf_ident ) {
254 last_adf_ident = adf_ident;
256 adf_trans_ident = nav.get_trans_ident();
257 adf_lon = nav.get_lon();
258 adf_lat = nav.get_lat();
259 adf_elev = nav.get_elev();
260 adf_range = nav.get_range();
261 adf_effective_range = kludgeRange(adf_elev, elev, adf_range);
266 #ifdef ENABLE_AUDIO_SUPPORT
267 if ( globals->get_soundmgr()->exists( "adf-ident" ) ) {
268 globals->get_soundmgr()->remove( "adf-ident" );
270 FGSimpleSound *sound;
271 sound = morse.make_ident( adf_trans_ident, LO_FREQUENCY );
272 sound->set_volume( 0.3 );
273 globals->get_soundmgr()->add( sound, "adf-ident" );
275 int offset = (int)(sg_random() * 30.0);
276 adf_play_count = offset / 4;
277 adf_last_time = globals->get_time_params()->get_cur_time() -
279 // cout << "offset = " << offset << " play_count = "
280 // << adf_play_count << " adf_last_time = "
281 // << adf_last_time << " current time = "
282 // << globals->get_time_params()->get_cur_time() << endl;
285 // cout << "Found an adf station in range" << endl;
286 // cout << " id = " << nav.get_ident() << endl;
291 adf_trans_ident = "";
292 #ifdef ENABLE_AUDIO_SUPPORT
293 globals->get_soundmgr()->remove( "adf-ident" );
296 // cout << "not picking up adf. :-(" << endl;