FlightGear next
fgcom.cxx
Go to the documentation of this file.
1// fgcom.cxx -- FGCom: Voice communication
2//
3// Written by Clement de l'Hamaide, started May 2013.
4//
5// This program is free software; you can redistribute it and/or
6// modify it under the terms of the GNU General Public License as
7// published by the Free Software Foundation; either version 2 of the
8// License, or (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful, but
11// WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13// General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19# include <config.h>
20
21#include "fgcom.hxx"
22
23// standard library includes
24#include <cstdio>
25
26// simgear includes
27#include <simgear/compiler.h>
28#include <simgear/sg_inlines.h>
29#include <simgear/debug/logstream.hxx>
30#include <simgear/math/sg_geodesy.hxx>
31#include <simgear/timing/timestamp.hxx>
32
33// flightgear includes
34#include <Main/fg_props.hxx>
35#include <Main/globals.hxx>
36#include <ATC/CommStation.hxx>
37#include <Airports/airport.hxx>
38#include <Navaids/navlist.hxx>
40
41#include <iaxclient.h>
42
43
44#define NUM_CALLS 4
45#define MAX_GND_RANGE 10.0
46#define MAX_TWR_RANGE 50.0
47#define MAX_RANGE 100.0
48#define MIN_RANGE 20.0
49#define MIN_GNDTWR_RANGE 0.0
50#define DEFAULT_SERVER "fgcom.flightgear.org"
51#define IAX_DELAY 300 // delay between calls in milliseconds
52#define TEST_FREQ 910.00
53#define NULL_ICAO "ZZZZ"
54
55const int special_freq[] = { // Define some freq who need to be used with NULL_ICAO
56 910000,
57 911000,
58 700000,
59 123450,
60 122750,
61 121500,
62 123500 };
63
64static FGCom* static_instance = NULL;
65
66
67
68static int iaxc_callback( iaxc_event e )
69{
70 switch( e.type )
71 {
72 case IAXC_EVENT_TEXT:
73 static_instance->iaxTextEvent(e.ev.text);
74 break;
75 default:
76 return 0;
77 }
78 return 1;
79}
80
81
82
83void FGCom::iaxTextEvent(struct iaxc_ev_text text)
84{
85 if( (text.type == IAXC_TEXT_TYPE_STATUS ||
86 text.type == IAXC_TEXT_TYPE_IAX) &&
87 _showMessages_node->getBoolValue() )
88 {
89 _text_node->setStringValue(text.message);
90 }
91
92 auto level = SG_INFO;
93 if ((text.type == IAXC_TEXT_TYPE_ERROR) || (text.type == IAXC_TEXT_TYPE_FATALERROR)) {
94 level = SG_ALERT;
95 }
96
97 SG_LOG(SG_SOUND, level, std::string{"FCCom IAX:"} + text.message);
98}
99
100
101
103{
104 _maxRange = MAX_RANGE;
105 _minRange = MIN_RANGE;
106}
107
108
109
111{
112}
113
114
115
117{
118 SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
119 _test_node = node->getChild( "test", 0, true );
120 _server_node = node->getChild( "server", 0, true );
121 _enabled_node = node->getChild( "enabled", 0, true );
122 _micBoost_node = node->getChild( "mic-boost", 0, true );
123 _micLevel_node = node->getChild( "mic-level", 0, true );
124 _silenceThd_node = node->getChild( "silence-threshold", 0, true );
125 _speakerLevel_node = node->getChild( "speaker-level", 0, true );
126 _selectedInput_node = node->getChild( "device-input", 0, true );
127 _selectedOutput_node = node->getChild( "device-output", 0, true );
128 _showMessages_node = node->getChild( "show-messages", 0, true );
129
130 SGPropertyNode *reg_node = node->getChild("register", 0, true);
131 _register_node = reg_node->getChild( "enabled", 0, true );
132 _username_node = reg_node->getChild( "username", 0, true );
133 _password_node = reg_node->getChild( "password", 0, true );
134
135 _ptt_node = fgGetNode("/controls/radios/comm-ptt", true);
136 _selected_comm_node = fgGetNode("/controls/radios/comm-radio-selected", true);
137 _callsign_node = fgGetNode("/sim/multiplay/callsign", true);
138 _text_node = fgGetNode("/sim/messages/atc", true );
139 _version_node = fgGetNode("/sim/version/flightgear", true );
140
141 // Set default values if not provided
142 if ( !_enabled_node->hasValue() )
143 _enabled_node->setBoolValue(true);
144
145 if ( !_test_node->hasValue() )
146 _test_node->setBoolValue(false);
147
148 if ( !_micBoost_node->hasValue() )
149 _micBoost_node->setIntValue(1);
150
151 if ( !_server_node->hasValue() )
152 _server_node->setStringValue(DEFAULT_SERVER);
153
154 if ( !_speakerLevel_node->hasValue() )
155 _speakerLevel_node->setFloatValue(1.0);
156
157 if ( !_micLevel_node->hasValue() )
158 _micLevel_node->setFloatValue(1.0);
159
160 if ( !_silenceThd_node->hasValue() )
161 _silenceThd_node->setFloatValue(-35.0);
162
163 if ( !_register_node->hasValue() )
164 _register_node->setBoolValue(false);
165
166 if ( !_username_node->hasValue() )
167 _username_node->setStringValue("guest");
168
169 if ( !_password_node->hasValue() )
170 _password_node->setStringValue("guest");
171
172 if ( !_showMessages_node->hasValue() )
173 _showMessages_node->setBoolValue(false);
174
175 _mpTransmitFrequencyNode = fgGetNode("sim/multiplay/comm-transmit-frequency-hz", 0, true);
176 _mpTransmitPowerNode = fgGetNode("sim/multiplay/comm-transmit-power-norm", 0, true);
177
178 _selectedOutput_node->addChangeListener(this);
179 _selectedInput_node->addChangeListener(this);
180 _speakerLevel_node->addChangeListener(this);
181 _silenceThd_node->addChangeListener(this);
182 _micBoost_node->addChangeListener(this);
183 _micLevel_node->addChangeListener(this);
184 _enabled_node->addChangeListener(this);
185 _ptt_node->addChangeListener(this);
186 _selected_comm_node->addChangeListener(this);
187 _test_node->addChangeListener(this);
188 // if you listen to more properties, be sure to remove
189 // the listener in unbind() as well
190}
191
192
193
195{
196 _selectedOutput_node->removeChangeListener(this);
197 _selectedInput_node->removeChangeListener(this);
198 _speakerLevel_node->removeChangeListener(this);
199 _silenceThd_node->removeChangeListener(this);
200 _micBoost_node->removeChangeListener(this);
201 _micLevel_node->removeChangeListener(this);
202 _enabled_node->removeChangeListener(this);
203 _ptt_node->removeChangeListener(this);
204 _selected_comm_node->removeChangeListener(this);
205 _test_node->removeChangeListener(this);
206}
207
208
209
211{
212 _enabled = _enabled_node->getBoolValue();
213 _server = _server_node->getStringValue();
214 _register = _register_node->getBoolValue();
215 _username = _username_node->getStringValue();
216 _password = _password_node->getStringValue();
217
218 _currentCommFrequency = 0.0;
219
220 _maxRange = MAX_RANGE;
221 _minRange = MIN_RANGE;
222}
223
224
225
227{
228 if( !_enabled ) {
229 return;
230 }
231
232 //WARNING: this _must_ be executed after sound system is totally initialized !
233 if( iaxc_initialize(NUM_CALLS) ) {
234 SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot initialize iaxclient");
235 _enabled = false;
236 return;
237 }
238
239 assert( static_instance == NULL );
240 static_instance = this;
241 iaxc_set_event_callback( iaxc_callback );
242
243 // FIXME: To be implemented in IAX audio driver
244 //iaxc_mic_boost_set( _micBoost_node->getIntValue() );
245 std::string app = "FGFS-";
246 app += _version_node->getStringValue();
247
248 iaxc_set_callerid( _callsign_node->getStringValue().c_str(), app.c_str() );
249 iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
250 iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
251 iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
252 iaxc_set_silence_threshold(_silenceThd_node->getFloatValue());
253 iaxc_start_processing_thread ();
254
255 // Now IAXClient is initialized
256 _initialized = true;
257
258 if ( _register ) {
259 _regId = iaxc_register( const_cast<char*>(_username.c_str()),
260 const_cast<char*>(_password.c_str()),
261 const_cast<char*>(_server.c_str()) );
262 if( _regId == -1 ) {
263 SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot register iaxclient");
264 return;
265 }
266 }
267
268 /*
269 Here we will create the list of available audio devices
270 Each audio device has a name, an ID, and a list of capabilities
271 If an audio device can output sound, available-output=true
272 If an audio device can input sound, available-input=true
273
274 /sim/fgcom/selected-input (int)
275 /sim/fgcom/selected-output (int)
276
277 /sim/fgcom/device[n]/id (int)
278 /sim/fgcom/device[n]/name (string)
279 /sim/fgcom/device[n]/available-input (bool)
280 /sim/fgcom/device[n]/available-output (bool)
281 */
282
283 //FIXME: OpenAL driver use an hard-coded device
284 // so all following is unused finally until someone
285 // implement "multi-device" support in IAX audio driver
286 SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
287
288 struct iaxc_audio_device *devs;
289 int nDevs, input, output, ring;
290
291 iaxc_audio_devices_get(&devs,&nDevs, &input, &output, &ring);
292
293 for(int i=0; i<nDevs; i++ ) {
294 SGPropertyNode *in_node = node->getChild("device", i, true);
295
296 // devID
297 _deviceID_node[i] = in_node->getChild("id", 0, true);
298 _deviceID_node[i]->setIntValue(devs[i].devID);
299
300 // name
301 _deviceName_node[i] = in_node->getChild("name", 0, true);
302 _deviceName_node[i]->setStringValue(devs[i].name);
303
304 // input capability
305 _deviceInput_node[i] = in_node->getChild("available-input", 0, true);
306 if( devs[i].capabilities & IAXC_AD_INPUT )
307 _deviceInput_node[i]->setBoolValue(true);
308 else
309 _deviceInput_node[i]->setBoolValue(false);
310
311 // output capability
312 _deviceOutput_node[i] = in_node->getChild("available-output", 0, true);
313 if( devs[i].capabilities & IAXC_AD_OUTPUT )
314 _deviceOutput_node[i]->setBoolValue(true);
315 else
316 _deviceOutput_node[i]->setBoolValue(false);
317
318 // use default device at start
319 if( devs[i].capabilities & IAXC_AD_INPUT_DEFAULT )
320 _selectedInput_node->setIntValue(devs[i].devID);
321 if( devs[i].capabilities & IAXC_AD_OUTPUT_DEFAULT )
322 _selectedOutput_node->setIntValue(devs[i].devID);
323 }
324
325 // Mute the mic and set speaker at start
326 iaxc_input_level_set( 0.0 );
327 iaxc_output_level_set(getCurrentCommVolume());
328
329 iaxc_millisleep(50);
330
331 // Do the first call at start
332 setupCommFrequency();
333 connectToCommFrequency();
334}
335
336double FGCom::getCurrentCommVolume() const {
337 double rv = 1.0;
338
339 if (_speakerLevel_node)
340 rv = _speakerLevel_node->getFloatValue();
341
342 if (_commVolumeNode)
343 rv = rv * _commVolumeNode->getFloatValue();
344
345 return rv;
346}
347double FGCom::getCurrentFrequencyKhz() const {
348 return 10 * static_cast<int>(_currentCommFrequency * 100 + 0.25);
349}
350
351void FGCom::setupCommFrequency(int channel) {
352 if (channel < 1) {
353 if (_selected_comm_node != nullptr) {
354 channel = _selected_comm_node->getIntValue();
355 }
356 }
357 // disconnect if channel set to 0
358 if (channel < 1) {
359 if (_currentCallIdent != -1) {
360 iaxc_dump_call_number(_currentCallIdent);
361 SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect as channel 0 " << _currentCallIdent);
362 _currentCallIdent = -1;
363 }
364 _currentCommFrequency = 0.0;
365 return;
366 }
367
368 if (channel > 0)
369 {
370 channel--; // adjust back to zero based index.
371 SGPropertyNode *commRadioNode = fgGetNode("/instrumentation/")->getChild("comm", channel, false);
372 if (commRadioNode) {
373 SGPropertyNode *frequencyNode = commRadioNode->getChild("frequencies");
374 if (_commVolumeNode)
375 _commVolumeNode->removeChangeListener(this);
376 _commVolumeNode = commRadioNode->getChild("volume");
377 if (frequencyNode) {
378 frequencyNode = frequencyNode->getChild("selected-mhz");
379 if (frequencyNode) {
380 if (_commFrequencyNode != frequencyNode) {
381 if (_commFrequencyNode)
382 _commFrequencyNode->removeChangeListener(this);
383 _commFrequencyNode = frequencyNode;
384 _commFrequencyNode->addChangeListener(this);
385 _commVolumeNode->addChangeListener(this);
386 }
387 _currentCommFrequency = frequencyNode->getDoubleValue();
388 return;
389 }
390 }
391 }
392 SG_LOG(SG_IO, SG_INFO, "FGCom: setupCommFrequency node listener failed: channel " << channel);
393 }
394
395 if (_commFrequencyNode)
396 _commFrequencyNode->removeChangeListener(this);
397 SG_LOG(SG_IO, SG_INFO, "FGCom: setupCommFrequency invalid channel " << channel);
398
399 _currentCommFrequency = 0.0;
400}
401
402void FGCom::connectToCommFrequency() {
403 // ensure that the current comm is still in range
404 if ((_currentCallFrequency > 0.0) && !isInRange(_currentCallFrequency)) {
405 SG_LOG(SG_IO, SG_WARN, "FGCom: call out of range of: " << _currentCallFrequency);
406 _currentCallFrequency = 0.0;
407 }
408
409 // don't connected (and disconnect if already connected) when tuned freq is 0
410 if (_currentCommFrequency < 1.0) {
411 if (_currentCallIdent != -1) {
412 iaxc_dump_call_number(_currentCallIdent);
413 SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect as freq 0: current call " << _currentCallIdent);
414 _currentCallIdent = -1;
415 }
416 return;
417 }
418
419 if (_currentCallFrequency != _currentCommFrequency || _currentCallIdent == -1) {
420 if (_currentCallIdent != -1) {
421 iaxc_dump_call_number(_currentCallIdent);
422 SG_LOG(SG_IO, SG_INFO, "FGCom: dump_call_number " << _currentCallIdent);
423 _currentCallIdent = -1;
424 }
425
426 if (_currentCallIdent == -1)
427 {
428 std::string num = computePhoneNumber(_currentCommFrequency, getAirportCode(_currentCommFrequency));
429 _processingTimer.stamp();
430 if (!isInRange(_currentCommFrequency)) {
431 if (_currentCallIdent != -1) {
432 SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect call as not in range " << _currentCallIdent);
433 if (_currentCallIdent != -1) {
434 iaxc_dump_call_number(_currentCallIdent);
435 _currentCallIdent = -1;
436 }
437 }
438 return;
439 }
440 if (!num.empty()) {
441 _currentCallIdent = iaxc_call(num.c_str());
442 if (_currentCallIdent == -1)
443 SG_LOG(SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str());
444 else {
445 SG_LOG(SG_IO, SG_DEBUG, "FGCom: call established " << num.c_str() << " Freq: " << _currentCommFrequency);
446 _currentCallFrequency = _currentCommFrequency;
447 }
448 }
449 else
450 SG_LOG(SG_IO, SG_WARN, "FGCom: frequency " << _currentCommFrequency << " does not map to valid IAX address");
451 }
452 }
453}
454
455void FGCom::updateCall()
456{
457 if (_processingTimer.elapsedMSec() > IAX_DELAY) {
458 _processingTimer.stamp();
459 connectToCommFrequency();
460 }
461}
462
463
464
465void FGCom::update(double dt)
466{
467 if (!_enabled || !_initialized) {
468 return;
469 }
470
471 updateCall();
472}
473
474
475
477{
478 if( !_enabled ) {
479 return;
480 }
481
482 _initialized = false;
483 _enabled = false;
484
485 iaxc_set_event_callback(NULL);
486 iaxc_unregister(_regId);
487 iaxc_stop_processing_thread();
488 iaxc_shutdown();
489
490 // added to help debugging lingering IAX thread after fgOSCloseWindow
491 flightgear::addSentryBreadcrumb("Did shutdown FGCom", "info");
492
493 assert( static_instance == this );
494 static_instance = NULL;
495}
496
497void FGCom::valueChanged(SGPropertyNode *prop)
498{
499 if (prop == _enabled_node) {
500 bool isEnabled = prop->getBoolValue();
501 if (_enabled == isEnabled) {
502 return;
503 }
504
505 if( isEnabled ) {
506 init();
507 postinit();
508 } else {
509 shutdown();
510 }
511 return;
512 }
513
514 // avoid crash when properties change before FGCom::postinit
515 // https://sourceforge.net/p/flightgear/codetickets/2574/
516 if (!_initialized) {
517 return;
518 }
519
520 if (prop == _commVolumeNode && _enabled) {
521 if (_ptt_node->getIntValue()) {
522 SG_LOG(SG_IO, SG_INFO, "FGCom: ignoring change comm volume as PTT pressed");
523 }
524 else
525 {
526 iaxc_input_level_set(0.0);
527 iaxc_output_level_set(getCurrentCommVolume());
528 SG_LOG(SG_IO, SG_INFO, "FGCom: change comm volume=" << _commVolumeNode->getFloatValue());
529 }
530 }
531
532 if (prop == _selected_comm_node && _enabled) {
533 setupCommFrequency();
534 SG_LOG(SG_IO, SG_INFO, "FGCom: change comm frequency (selected node): set to " << _currentCommFrequency);
535 }
536
537 if (prop == _commFrequencyNode && _enabled) {
538 setupCommFrequency();
539 SG_LOG(SG_IO, SG_INFO, "FGCom: change comm frequency (property updated): set to " << _currentCommFrequency);
540 }
541
542 if (prop == _ptt_node && _enabled) {
543 if (_ptt_node->getIntValue()) {
544 // ensure that we are on the right channel by calling setupCommFrequency
545 setupCommFrequency();
546 iaxc_output_level_set(0.0);
547 iaxc_input_level_set(_micLevel_node->getFloatValue()); //0.0 = min , 1.0 = max
548 _mpTransmitFrequencyNode->setValue(_currentCallFrequency * 1000000);
549 _mpTransmitPowerNode->setValue(1.0);
550 SG_LOG(SG_IO, SG_INFO, "FGCom: PTT active: " << _currentCallFrequency);
551 }
552 else {
553 iaxc_output_level_set(getCurrentCommVolume());
554 iaxc_input_level_set(0.0);
555 SG_LOG(SG_IO, SG_INFO, "FGCom: PTT release: " << _currentCallFrequency << " vol=" << getCurrentCommVolume());
556 _mpTransmitFrequencyNode->setValue(0);
557 _mpTransmitPowerNode->setValue(0);
558 }
559 }
560
561 if (prop == _test_node) {
562 testMode( prop->getBoolValue() );
563 return;
564 }
565
566 if (prop == _silenceThd_node && _initialized) {
567 float silenceThd = prop->getFloatValue();
568 SG_CLAMP_RANGE<float>( silenceThd, -60, 0 );
569 iaxc_set_silence_threshold( silenceThd );
570 return;
571 }
572
573 //FIXME: not implemented in IAX audio driver (audio_openal.c)
574 if (prop == _micBoost_node && _initialized) {
575 int micBoost = prop->getIntValue();
576 SG_CLAMP_RANGE<int>( micBoost, 0, 1 );
577 iaxc_mic_boost_set( micBoost ) ; // 0 = enabled , 1 = disabled
578 return;
579 }
580
581 //FIXME: not implemented in IAX audio driver (audio_openal.c)
582 if ((prop == _selectedInput_node || prop == _selectedOutput_node) && _initialized) {
583 int selectedInput = _selectedInput_node->getIntValue();
584 int selectedOutput = _selectedOutput_node->getIntValue();
585 iaxc_audio_devices_set(selectedInput, selectedOutput, 0);
586 return;
587 }
588
589 if (_listener_active)
590 return;
591
592 _listener_active++;
593
594 if (prop == _speakerLevel_node && _enabled) {
595 float speakerLevel = prop->getFloatValue();
596 SG_CLAMP_RANGE<float>( speakerLevel, 0.0, 1.0 );
597 _speakerLevel_node->setFloatValue(speakerLevel);
598 iaxc_output_level_set(speakerLevel);
599 }
600
601 if (prop == _micLevel_node && _enabled) {
602 float micLevel = prop->getFloatValue();
603 SG_CLAMP_RANGE<float>( micLevel, 0.0, 1.0 );
604 _micLevel_node->setFloatValue(micLevel);
605 //iaxc_input_level_set(micLevel);
606 }
607
608 _listener_active--;
609}
610
611
612
613void FGCom::testMode(bool testMode)
614{
615 if(testMode && _initialized) {
616 _enabled = false;
617 iaxc_dump_all_calls();
618 iaxc_input_level_set( 1.0 );
619 iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
620 std::string num = computePhoneNumber(TEST_FREQ, NULL_ICAO);
621 if( num.size() > 0 ) {
622 iaxc_millisleep(IAX_DELAY);
623 _currentCallIdent = iaxc_call(num.c_str());
624 }
625 if( _currentCallIdent == -1 )
626 SG_LOG( SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str() );
627 } else {
628 if( _initialized ) {
629 iaxc_dump_all_calls();
630 iaxc_millisleep(IAX_DELAY);
631 iaxc_input_level_set( 0.0 );
632 iaxc_output_level_set(getCurrentCommVolume());
633 _currentCallIdent = -1;
634 _enabled = true;
635 }
636 }
637}
638
639
640
641/*
642 \param freq The requested frequency e.g 120.825
643 \return The ICAO code as string e.g LFMV
644*/
645
646std::string FGCom::getAirportCode(const double& freq)
647{
648 SGGeod aircraftPos = globals->get_aircraft_position();
649
650 for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
651 if(special_freq[i] == getCurrentFrequencyKhz()) {
652 return NULL_ICAO;
653 }
654 }
655
656 flightgear::CommStation* apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz(), aircraftPos);
657 if( !apt ) {
658 apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz() -10, aircraftPos); // Check for 8.33KHz
659 if( !apt ) {
660 return std::string();
661 }
662 }
663
664 if( apt->type() == FGPositioned::FREQ_TOWER ) {
665 _maxRange = MAX_TWR_RANGE;
666 _minRange = MIN_GNDTWR_RANGE;
667 } else if( apt->type() == FGPositioned::FREQ_GROUND ) {
668 _maxRange = MAX_GND_RANGE;
669 _minRange = MIN_GNDTWR_RANGE;
670 } else {
671 _maxRange = MAX_RANGE;
672 _minRange = MIN_RANGE;
673 }
674
675 _aptPos = apt->geod();
676 return apt->airport()->ident();
677}
678
679
680
681/*
682 \param freq The requested frequency e.g 120.825
683 \param iaco The associated ICAO code e.g LFMV
684 \return The phone number as string i.e username:password@fgcom.flightgear.org/0176707786120825
685*/
686
687std::string FGCom::computePhoneNumber(const double& freq, const std::string& icao) const
688{
689 if( icao.empty() )
690 return std::string();
691
692 char phoneNumber[256];
693 char exten[32];
694 char tmp[5];
695
696 /*Convert ICAO to ASCII */
697 snprintf(tmp, 5, "%4s", icao.c_str());
698
699 /*Built the phone number */
700 snprintf(exten, 32,
701 "%02d%02d%02d%02d%02d%06d",
702 01,
703 tmp[0],
704 tmp[1],
705 tmp[2],
706 tmp[3],
707 (int)(freq * 1000 + 0.5));
708 exten[16] = '\0';
709
710 snprintf( phoneNumber,
711 sizeof (phoneNumber),
712 "%s:%s@%s/%s",
713 _username.c_str(),
714 _password.c_str(),
715 _server.c_str(),
716 exten);
717
718 return phoneNumber;
719}
720
721
722
723/*
724 \return A boolean value, 1=in range, 0=out of range
725*/
726
727bool FGCom::isInRange(const double &freq) const
728{
729 for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
730 if( (special_freq[i]) == getCurrentFrequencyKhz()) {
731 return 1;
732 }
733 }
734
735 SGGeod acftPos = globals->get_aircraft_position();
736 double distNm = SGGeodesy::distanceNm(_aptPos, acftPos);
737 double delta_elevation_ft = fabs(acftPos.getElevationFt() - _aptPos.getElevationFt());
738 double rangeNm = 1.23 * sqrt(delta_elevation_ft);
739
740 if (rangeNm > _maxRange) rangeNm = _maxRange;
741 if (rangeNm < _minRange) rangeNm = _minRange;
742 if( distNm > rangeNm ) return 0;
743 return 1;
744}
745
746
747// Register the subsystem.
748SGSubsystemMgr::Registrant<FGCom> registrantFGCom(
749 SGSubsystemMgr::SOUND);
static std::unique_ptr< FavouriteAircraftData > static_instance
#define i(x)
FGCom()
Definition fgcom.cxx:102
void unbind() override
Definition fgcom.cxx:194
void postinit() override
Definition fgcom.cxx:226
virtual ~FGCom()
Definition fgcom.cxx:110
void update(double dt) override
Definition fgcom.cxx:465
void iaxTextEvent(struct iaxc_ev_text text)
Definition fgcom.cxx:83
void bind() override
Definition fgcom.cxx:116
void shutdown() override
Definition fgcom.cxx:476
virtual void valueChanged(SGPropertyNode *prop)
Definition fgcom.cxx:497
void init() override
Definition fgcom.cxx:210
SGGeod get_aircraft_position() const
Definition globals.cxx:611
virtual const SGGeod & geod() const
Type type() const
FGAirportRef airport() const
static CommStationRef findByFreq(int freqKhz, const SGGeod &pos, FGPositioned::Filter *filt=NULL)
const char * name
#define MIN_GNDTWR_RANGE
Definition fgcom.cxx:49
static int iaxc_callback(iaxc_event e)
Definition fgcom.cxx:68
const int special_freq[]
Definition fgcom.cxx:55
#define DEFAULT_SERVER
Definition fgcom.cxx:50
SGSubsystemMgr::Registrant< FGCom > registrantFGCom(SGSubsystemMgr::SOUND)
#define NULL_ICAO
Definition fgcom.cxx:53
#define MIN_RANGE
Definition fgcom.cxx:48
#define IAX_DELAY
Definition fgcom.cxx:51
#define MAX_GND_RANGE
Definition fgcom.cxx:45
#define NUM_CALLS
Definition fgcom.cxx:44
#define MAX_RANGE
Definition fgcom.cxx:47
#define TEST_FREQ
Definition fgcom.cxx:52
#define MAX_TWR_RANGE
Definition fgcom.cxx:46
FGGlobals * globals
Definition globals.cxx:142
void addSentryBreadcrumb(const std::string &, const std::string &)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27