FlightGear next
kr_87.cxx
Go to the documentation of this file.
1// kr-87.cxx -- class to impliment the King KR 87 Digital ADF
2//
3// Written by Curtis Olson, started April 2002.
4//
5// Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20//
21
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27#include <stdio.h> // snprintf
28
29#include <simgear/compiler.h>
30#include <simgear/math/sg_random.hxx>
31#include <simgear/math/sg_geodesy.hxx>
32#include <simgear/timing/sg_time.hxx>
33#include <simgear/sound/sample_group.hxx>
34
35#include <Navaids/navlist.hxx>
36
37#include "kr_87.hxx"
38
39#include <Sound/morse.hxx>
40#include <string>
41using std::string;
42
43static int play_count = 0;
44static time_t last_time = 0;
45
46
50static double kludgeRange ( double stationElev, double aircraftElev,
51 double nominalRange)
52{
53 // Assume that the nominal range (usually
54 // 50nm) applies at a 5,000 ft difference.
55 // Just a wild guess!
56 double factor = (aircraftElev - stationElev)*SG_METER_TO_FEET / 5000.0;
57 double range = fabs(nominalRange * factor);
58
59 // Clamp the range to keep it sane; for
60 // now, never less than 50% or more than
61 // 500% of nominal range.
62 if (range < nominalRange/2.0) {
63 range = nominalRange/2.0;
64 } else if (range > nominalRange*5.0) {
65 range = nominalRange*5.0;
66 }
67
68 return range;
69}
70
71
72// Constructor
73FGKR_87::FGKR_87( SGPropertyNode *node ) :
74 bus_power(fgGetNode("/systems/electrical/outputs/adf", true)),
75 serviceable(fgGetNode("/instrumentation/adf/serviceable", true)),
76 need_update(true),
77 valid(false),
78 inrange(false),
79 dist(0.0),
80 heading(0.0),
81 goal_needle_deg(0.0),
82 et_flash_time(0.0),
83 ant_mode(0),
84 stby_mode(0),
85 timer_mode(0),
86 count_mode(0),
87 rotation(0),
88 power_btn(true),
89 audio_btn(true),
90 vol_btn(0.5),
91 adf_btn(true),
92 bfo_btn(false),
93 frq_btn(false),
94 last_frq_btn(false),
95 flt_et_btn(false),
96 last_flt_et_btn(false),
97 set_rst_btn(false),
98 last_set_rst_btn(false),
99 freq(0),
100 stby_freq(0),
101 needle_deg(0.0),
102 flight_timer(0.0),
103 elapsed_timer(0.0),
104 tmp_timer(0.0),
105 _time_before_search_sec(0),
106 _sgr(NULL)
107{
108}
109
110
111// Destructor
114
115
117 auto smgr = globals->get_subsystem<SGSoundMgr>();
118 _sgr = smgr->find("avionics", true);
119 _sgr->tie_to_listener();
120}
121
123 _time_before_search_sec = 0;
124}
125
127 _tiedProperties.setRoot(fgGetNode("/instrumentation/kr-87", true));
128 // internal values
129 _tiedProperties.Tie("internal/valid", this, &FGKR_87::get_valid);
130 _tiedProperties.Tie("internal/inrange", this,
132 _tiedProperties.Tie("internal/dist", this,
134 _tiedProperties.Tie("internal/heading", this,
136
137 // modes
138 _tiedProperties.Tie("modes/ant", this,
140 _tiedProperties.Tie("modes/stby", this,
142 _tiedProperties.Tie("modes/timer", this,
144 _tiedProperties.Tie("modes/count", this,
146
147 // input and buttons
148 _tiedProperties.Tie("inputs/rotation-deg", this,
150 fgSetArchivable("/instrumentation/kr-87/inputs/rotation-deg");
151 _tiedProperties.Tie("inputs/power-btn", this,
154 fgSetArchivable("/instrumentation/kr-87/inputs/power-btn");
155 _tiedProperties.Tie("inputs/audio-btn", this,
158 fgSetArchivable("/instrumentation/kr-87/inputs/audio-btn");
159 _tiedProperties.Tie("inputs/volume", this,
162 fgSetArchivable("/instrumentation/kr-87/inputs/volume");
163 _tiedProperties.Tie("inputs/adf-btn", this,
166 _tiedProperties.Tie("inputs/bfo-btn", this,
169 _tiedProperties.Tie("inputs/frq-btn", this,
172 _tiedProperties.Tie("inputs/flt-et-btn", this,
175 _tiedProperties.Tie("inputs/set-rst-btn", this,
178
179 // outputs
180 _tiedProperties.Tie("outputs/selected-khz", this,
182 fgSetArchivable("/instrumentation/kr-87/outputs/selected-khz");
183 _tiedProperties.Tie("outputs/standby-khz", this,
185 fgSetArchivable("/instrumentation/kr-87/outputs/standby-khz");
186 _tiedProperties.Tie("outputs/needle-deg", this,
188 _tiedProperties.Tie("outputs/flight-timer", this,
190 _tiedProperties.Tie("outputs/elapsed-timer", this,
193
194 // annunciators
195 _tiedProperties.Tie("annunciators/ant", this,
197 _tiedProperties.Tie("annunciators/adf", this,
199 _tiedProperties.Tie("annunciators/bfo", this,
201 _tiedProperties.Tie("annunciators/frq", this,
203 _tiedProperties.Tie("annunciators/flt", this,
205 _tiedProperties.Tie("annunciators/et", this,
207}
208
209
211 _tiedProperties.Untie();
212}
213
214
215// Update the various nav values based on position and valid tuned in navs
216void FGKR_87::update( double dt_sec ) {
217 SGGeod acft = globals->get_aircraft_position();
218
219 need_update = false;
220
221 double az1, az2, s;
222
223 // On timeout, scan again
224 _time_before_search_sec -= dt_sec;
225 if ( _time_before_search_sec < 0 ) {
226 search();
227 }
228
230 // Radio
232
233 if ( has_power() && serviceable->getBoolValue() ) {
234 // buttons
235 if ( adf_btn == 0 ) {
236 ant_mode = 1;
237 } else {
238 ant_mode = 0;
239 }
240 // cout << "ant_mode = " << ant_mode << endl;
241
242 if ( frq_btn && frq_btn != last_frq_btn && stby_mode == 0 ) {
243 int tmp = freq;
244 freq = stby_freq;
245 stby_freq = tmp;
246 } else if ( frq_btn ) {
247 stby_mode = 0;
248 count_mode = 0;
249 }
250 last_frq_btn = frq_btn;
251
252 if ( flt_et_btn && flt_et_btn != last_flt_et_btn ) {
253 if ( stby_mode == 0 ) {
254 timer_mode = 0;
255 } else {
256 timer_mode = !timer_mode;
257 }
258 stby_mode = 1;
259 }
260 last_flt_et_btn = flt_et_btn;
261
262 if ( set_rst_btn == 1 && set_rst_btn != last_set_rst_btn ) {
263 // button depressed
264 tmp_timer = 0.0;
265 }
266 if ( set_rst_btn == 1 && set_rst_btn == last_set_rst_btn ) {
267 // button depressed and was last iteration too
268 tmp_timer += dt_sec;
269 // cout << "tmp_timer = " << tmp_timer << endl;
270 if ( tmp_timer > 2.0 ) {
271 // button held depressed for 2 seconds
272 // cout << "entering elapsed count down mode" << endl;
273 timer_mode = 1;
274 count_mode = 2;
275 elapsed_timer = 0.0;
276 }
277 }
278 if ( set_rst_btn == 0 && set_rst_btn != last_set_rst_btn ) {
279 // button released
280 if ( tmp_timer > 2.0 ) {
281 // button held depressed for 2 seconds, don't adjust
282 // mode, just exit
283 } else if ( count_mode == 2 ) {
284 count_mode = 1;
285 } else {
286 count_mode = 0;
287 elapsed_timer = 0.0;
288 }
289 }
290 last_set_rst_btn = set_rst_btn;
291
292 // timers
293 flight_timer += dt_sec;
294
295 if ( set_rst_btn == 0 ) {
296 // only count if set/rst button not depressed
297 if ( count_mode == 0 ) {
298 elapsed_timer += dt_sec;
299 } else if ( count_mode == 1 ) {
300 elapsed_timer -= dt_sec;
301 if ( elapsed_timer < 1.0 ) {
302 count_mode = 0;
303 elapsed_timer = 0.0;
304 }
305 }
306 }
307
308 // annunciators
309 ant_ann = !adf_btn;
310 adf_ann = adf_btn;
311 bfo_ann = bfo_btn;
312 frq_ann = !stby_mode;
313 flt_ann = stby_mode && !timer_mode;
314 if ( count_mode < 2 ) {
315 et_ann = stby_mode && timer_mode;
316 } else {
317 et_flash_time += dt_sec;
318 if ( et_ann && et_flash_time > 0.5 ) {
319 et_ann = false;
320 et_flash_time -= 0.5;
321 } else if ( !et_ann && et_flash_time > 0.2 ) {
322 et_ann = true;
323 et_flash_time -= 0.2;
324 }
325 }
326
327 if ( valid ) {
328 // cout << "adf is valid" << endl;
329 // staightline distance
330 // What a hack, dist is a class local variable
331 dist = sqrt(distSqr(SGVec3d::fromGeod(acft), xyz));
332
333 // wgs84 heading
334 geo_inverse_wgs_84( acft, SGGeod::fromDeg(stn_lon, stn_lat),
335 &az1, &az2, &s );
336 heading = az1;
337 // cout << " heading = " << heading
338 // << " dist = " << dist << endl;
339
340 effective_range = kludgeRange(stn_elev, acft.getElevationFt(), range);
341 if ( dist < effective_range * SG_NM_TO_METER ) {
342 inrange = true;
343 } else if ( dist < 2 * effective_range * SG_NM_TO_METER ) {
344 inrange = sg_random() <
345 ( 2 * effective_range * SG_NM_TO_METER - dist ) /
346 (effective_range * SG_NM_TO_METER);
347 } else {
348 inrange = false;
349 }
350
351 // cout << "inrange = " << inrange << endl;
352
353 if ( inrange ) {
354 goal_needle_deg = heading
355 - fgGetDouble("/orientation/heading-deg");
356 }
357 } else {
358 inrange = false;
359 }
360
361 if ( ant_mode ) {
362 goal_needle_deg = 90.0;
363 }
364 } else {
365 // unit turned off
366 goal_needle_deg = 0.0;
367 flight_timer = 0.0;
368 elapsed_timer = 0.0;
369 ant_ann = false;
370 adf_ann = false;
371 bfo_ann = false;
372 frq_ann = false;
373 flt_ann = false;
374 et_ann = false;
375 }
376
377 // formatted timer
378 double time;
379 int hours, min, sec;
380 if ( timer_mode == 0 ) {
381 time = flight_timer;
382 } else {
383 time = elapsed_timer;
384 }
385 // cout << time << endl;
386 hours = (int)(time / 3600.0);
387 time -= hours * 3600.00;
388 min = (int)(time / 60.0);
389 time -= min * 60.0;
390 sec = (int)time;
391 int big, little;
392 if ( hours > 0 ) {
393 big = hours;
394 if ( big > 99 ) {
395 big = 99;
396 }
397 little = min;
398 } else {
399 big = min;
400 little = sec;
401 }
402 if ( big > 99 ) {
403 big = 99;
404 }
405
406 char formatted_timer[24];
407 // cout << big << ":" << little << endl;
408 snprintf(formatted_timer, 24, "%02d:%02d", big, little);
409 fgSetString( "/instrumentation/kr-87/outputs/timer-string",
410 formatted_timer );
411
412 while ( goal_needle_deg < 0.0 ) { goal_needle_deg += 360.0; }
413 while ( goal_needle_deg >= 360.0 ) { goal_needle_deg -= 360.0; }
414
415 double diff = goal_needle_deg - needle_deg;
416 while ( diff < -180.0 ) { diff += 360.0; }
417 while ( diff > 180.0 ) { diff -= 360.0; }
418
419 needle_deg += diff * dt_sec * 4;
420 while ( needle_deg < 0.0 ) { needle_deg += 360.0; }
421 while ( needle_deg >= 360.0 ) { needle_deg -= 360.0; }
422
423 // cout << "goal = " << goal_needle_deg << " actual = " << needle_deg
424 // << endl;
425 // cout << "flt = " << flight_timer << " et = " << elapsed_timer
426 // << " needle = " << needle_deg << endl;
427
428 if ( valid && inrange && serviceable->getBoolValue() ) {
429 // play station ident via audio system if on + ant mode,
430 // otherwise turn it off
431 if ( vol_btn >= 0.01 && audio_btn ) {
432 SGSoundSample *sound;
433 sound = _sgr->find( "adf-ident" );
434 if ( sound != NULL ) {
435 if ( !adf_btn ) {
436 sound->set_volume( vol_btn );
437 } else {
438 sound->set_volume( vol_btn / 4.0 );
439 }
440 } else {
441 SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find adf-ident sound" );
442 }
443 if ( last_time <
444 globals->get_time_params()->get_cur_time() - 30 ) {
445 last_time = globals->get_time_params()->get_cur_time();
446 play_count = 0;
447 }
448 if ( play_count < 4 ) {
449 // play ADF ident
450 if ( !_sgr->is_playing("adf-ident") && (vol_btn > 0.05) ) {
451 _sgr->play_once( "adf-ident" );
452 ++play_count;
453 }
454 }
455 } else {
456 _sgr->stop( "adf-ident" );
457 }
458 }
459}
460
461
462// Update current nav/adf radio stations based on current postition
464 SGGeod pos = globals->get_aircraft_position();
465
466 // FIXME: the panel should handle this
467 static string last_ident = "";
468
469 // reset search time
470 _time_before_search_sec = 1.0;
471
473 // ADF.
475
476
478 FGNavRecord *adf = FGNavList::findByFreq( freq, pos, &filter);
479 if ( adf != NULL ) {
480 char sfreq[128];
481 snprintf( sfreq, 10, "%d", freq );
482 ident = sfreq;
483 ident += adf->get_ident();
484 // cout << "adf ident = " << ident << endl;
485 valid = true;
486 if ( last_ident != ident ) {
487 last_ident = ident;
488
489 trans_ident = adf->get_trans_ident();
490 stn_lon = adf->get_lon();
491 stn_lat = adf->get_lat();
492 stn_elev = adf->get_elev_ft();
493 range = adf->get_range();
494 effective_range = kludgeRange(stn_elev, pos.getElevationM(), range);
495 xyz = adf->cart();
496
497 if ( _sgr->exists( "adf-ident" ) ) {
498 // stop is required! -- remove alone wouldn't stop immediately
499 _sgr->stop( "adf-ident" );
500 _sgr->remove( "adf-ident" );
501 }
502 SGSoundSample *sound;
503 sound = FGMorse::instance()->make_ident( trans_ident, FGMorse::LO_FREQUENCY );
504 sound->set_volume( 0.3 );
505 _sgr->add( sound, "adf-ident" );
506
507 int offset = (int)(sg_random() * 30.0);
508 play_count = offset / 4;
509 last_time = globals->get_time_params()->get_cur_time() -
510 offset;
511 // cout << "offset = " << offset << " play_count = "
512 // << play_count << " last_time = "
513 // << last_time << " current time = "
514 // << globals->get_time_params()->get_cur_time() << endl;
515
516 // cout << "Found an adf station in range" << endl;
517 // cout << " id = " << nav->get_ident() << endl;
518 }
519 } else {
520 valid = false;
521 ident = "";
522 trans_ident = "";
523 _sgr->remove( "adf-ident" );
524 last_ident = "";
525 // cout << "not picking up adf. :-(" << endl;
526 }
527}
528
529
531 if ( stby_mode == 0 ) {
532 return stby_freq;
533 } else {
534 if ( timer_mode == 0 ) {
535 return (int)flight_timer;
536 } else {
537 return (int)elapsed_timer;
538 }
539 }
540}
541
542
543// Register the subsystem.
544#if 0
545SGSubsystemMgr::InstancedRegistrant<FGKR_87> registrantFGKR_87(
546 SGSubsystemMgr::FDM,
547 {{"instrumentation", SGSubsystemMgr::Dependency::HARD},
548 {"sound", SGSubsystemMgr::Dependency::HARD}});
549#endif
#define min(X, Y)
void set_set_rst_btn(bool val)
Definition kr_87.hxx:171
void set_elapsed_timer(double val)
Definition kr_87.hxx:184
void reinit() override
Definition kr_87.cxx:122
void set_frq_btn(bool val)
Definition kr_87.hxx:167
int get_timer_mode() const
Definition kr_87.hxx:142
void unbind() override
Definition kr_87.cxx:210
bool get_adf_btn() const
Definition kr_87.hxx:162
bool get_frq_ann() const
Definition kr_87.hxx:190
bool get_audio_btn() const
Definition kr_87.hxx:152
void set_bfo_btn(bool val)
Definition kr_87.hxx:165
void init() override
Definition kr_87.cxx:116
int get_ant_mode() const
Definition kr_87.hxx:140
void set_stby_freq(int f)
Definition kr_87.hxx:180
int get_stby_mode() const
Definition kr_87.hxx:141
double get_rotation() const
Definition kr_87.hxx:146
bool get_set_rst_btn() const
Definition kr_87.hxx:170
double get_needle_deg() const
Definition kr_87.hxx:181
double get_dist() const
Definition kr_87.hxx:133
bool get_power_btn() const
Definition kr_87.hxx:148
FGKR_87(SGPropertyNode *node)
Definition kr_87.cxx:73
bool get_flt_et_btn() const
Definition kr_87.hxx:168
bool get_et_ann() const
Definition kr_87.hxx:192
void set_rotation(double rot)
Definition kr_87.hxx:147
bool get_bfo_ann() const
Definition kr_87.hxx:189
bool get_inrange() const
Definition kr_87.hxx:130
~FGKR_87()
Definition kr_87.cxx:112
bool get_flt_ann() const
Definition kr_87.hxx:191
void update(double dt_sec) override
Definition kr_87.cxx:216
void set_audio_btn(bool val)
Definition kr_87.hxx:153
double get_flight_timer() const
Definition kr_87.hxx:182
void bind() override
Definition kr_87.cxx:126
void set_power_btn(bool val)
Definition kr_87.hxx:149
double get_vol_btn() const
Definition kr_87.hxx:156
bool get_valid() const
Definition kr_87.hxx:129
void set_vol_btn(double val)
Definition kr_87.hxx:157
bool get_bfo_btn() const
Definition kr_87.hxx:164
int get_stby_freq() const
Definition kr_87.cxx:530
void set_adf_btn(bool val)
Definition kr_87.hxx:163
bool has_power() const
Definition kr_87.hxx:135
bool get_ant_ann() const
Definition kr_87.hxx:187
void set_freq(int f)
Definition kr_87.hxx:175
bool get_adf_ann() const
Definition kr_87.hxx:188
void set_flt_et_btn(bool val)
Definition kr_87.hxx:169
bool get_frq_btn() const
Definition kr_87.hxx:166
void search()
Definition kr_87.cxx:463
int get_freq() const
Definition kr_87.hxx:174
double get_heading() const
Definition kr_87.hxx:134
double get_elapsed_timer() const
Definition kr_87.hxx:183
int get_count_mode() const
Definition kr_87.hxx:143
static const int LO_FREQUENCY
Definition morse.hxx:107
SGSoundSample * make_ident(const std::string &id, const int freq=LO_FREQUENCY)
Definition morse.cxx:141
static FGMorse * instance()
Definition morse.cxx:250
static FGNavRecordRef findByFreq(double freq, const SGGeod &position, TypeFilter *filter=nullptr)
Query the database for the specified station.
Definition navlist.cxx:187
int get_range() const
Definition navrecord.hxx:77
const char * get_ident() const
Definition navrecord.hxx:80
double get_lat() const
Definition navrecord.hxx:73
double get_elev_ft() const
Definition navrecord.hxx:74
const char * get_trans_ident() const
Definition navrecord.hxx:83
double get_lon() const
Definition navrecord.hxx:72
virtual const SGVec3d & cart() const
The cartesian position associated with this object.
void fgSetArchivable(const char *name, bool state)
Set the state of the archive attribute for a property.
Definition fg_props.cxx:598
FGGlobals * globals
Definition globals.cxx:142
static time_t last_time
Definition kr_87.cxx:44
static int play_count
Definition kr_87.cxx:43
static double kludgeRange(double stationElev, double aircraftElev, double nominalRange)
Boy, this is ugly!
Definition kr_87.cxx:50
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27