// fgcom.cxx -- FGCom: Voice communication
//
-// Written by Clement de l'Hamaide, started Mai 2013.
+// Written by Clement de l'Hamaide, started May 2013.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
#include <Airports/airport.hxx>
#include <Navaids/navlist.hxx>
-#include <utils/iaxclient/lib/iaxclient.h>
+#include <3rdparty/iaxclient/lib/iaxclient.h>
#define NUM_CALLS 4
+#define MAX_GND_RANGE 10.0
+#define MAX_TWR_RANGE 50.0
#define MAX_RANGE 100.0
#define MIN_RANGE 20.0
+#define MIN_GNDTWR_RANGE 0.0
#define DEFAULT_SERVER "fgcom.flightgear.org"
#define IAX_DELAY 300 // delay between calls in milliseconds
#define TEST_FREQ 910.00
#define NULL_ICAO "ZZZZ"
const int special_freq[] = { // Define some freq who need to be used with NULL_ICAO
+ 910000,
911000,
700000,
123450,
121000,
723340 };
+static FGCom* static_instance = NULL;
+
+
+
+static int iaxc_callback( iaxc_event e )
+{
+ switch( e.type )
+ {
+ case IAXC_EVENT_TEXT:
+ static_instance->iaxTextEvent(e.ev.text);
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+
+void FGCom::iaxTextEvent(struct iaxc_ev_text text)
+{
+ if( (text.type == IAXC_TEXT_TYPE_STATUS ||
+ text.type == IAXC_TEXT_TYPE_IAX) &&
+ _showMessages_node->getBoolValue() )
+ {
+ _text_node->setStringValue(text.message);
+ }
+}
+
+
FGCom::FGCom() :
- _register(true)
+ _register(true),
+ _enabled(false),
+ _initialized(false),
+ _listener_active(0)
{
- _listener_active = 0;
}
_speakerLevel_node = node->getChild( "speaker-level", 0, true );
_selectedInput_node = node->getChild( "device-input", 0, true );
_selectedOutput_node = node->getChild( "device-output", 0, true );
+ _showMessages_node = node->getChild( "show-messages", 0, true );
SGPropertyNode *reg_node = node->getChild("register", 0, true);
_register_node = reg_node->getChild( "enabled", 0, true );
_username_node = reg_node->getChild( "username", 0, true );
_password_node = reg_node->getChild( "password", 0, true );
- //_nav0_node = fgGetNode("/instrumentation/nav[0]/frequencies/selected-mhz", true);
- //_nav1_node = fgGetNode("/instrumentation/nav[1]/frequencies/selected-mhz", true);
_comm0_node = fgGetNode("/instrumentation/comm[0]/frequencies/selected-mhz", true);
- //_comm1_node = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true);
+ _comm1_node = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true);
_ptt0_node = fgGetNode("/instrumentation/comm[0]/ptt", true); //FIXME: what about /instrumentation/comm[1]/ptt ?
_callsign_node = fgGetNode("/sim/multiplay/callsign", true);
+ _text_node = fgGetNode("/sim/messages/atc", true );
+ _version_node = fgGetNode("/sim/version/flightgear", true );
// Set default values if not provided
if ( !_enabled_node->hasValue() )
if ( !_password_node->hasValue() )
_password_node->setStringValue("guest");
+ if ( !_showMessages_node->hasValue() )
+ _showMessages_node->setBoolValue(false);
+
_selectedOutput_node->addChangeListener(this);
_selectedInput_node->addChangeListener(this);
_speakerLevel_node->addChangeListener(this);
_micLevel_node->addChangeListener(this);
_enabled_node->addChangeListener(this);
_comm0_node->addChangeListener(this);
- //_comm1_node->addChangeListener(this);
- //_nav0_node->addChangeListener(this);
- //_nav1_node->addChangeListener(this);
+ _comm1_node->addChangeListener(this);
_ptt0_node->addChangeListener(this);
_test_node->addChangeListener(this);
}
_register = _register_node->getBoolValue();
_username = _username_node->getStringValue();
_password = _password_node->getStringValue();
+ _selectedComm = 0;
_currentComm0 = _comm0_node->getDoubleValue();
- //_currentComm1 = _comm1_node->getDoubleValue();
- //_currentNav0 = _nav0_node->getDoubleValue();
- //_currentNav1 = _nav1_node->getDoubleValue();
+ _currentComm1 = _comm1_node->getDoubleValue();
_comm0Changed = false;
- //_comm1Changed = false;
- //_nav0Changed = false;
- //_nav1Changed = false;
+ _comm1Changed = false;
+
+ _maxRange = MAX_RANGE;
+ _minRange = MIN_RANGE;
}
//WARNING: this _must_ be executed after sound system is totally initialized !
if( iaxc_initialize(NUM_CALLS) ) {
- SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot initialize iaxclient!");
+ SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot initialize iaxclient");
_enabled = false;
return;
}
+
+ assert( static_instance == NULL );
+ static_instance = this;
+ iaxc_set_event_callback( iaxc_callback );
// FIXME: To be implemented in IAX audio driver
//iaxc_mic_boost_set( _micBoost_node->getIntValue() );
- iaxc_set_formats( IAXC_FORMAT_GSM, IAXC_FORMAT_GSM );
+ std::string app = "FGFS-";
+ app += _version_node->getStringValue();
+
+ iaxc_set_callerid( _callsign_node->getStringValue(), app.c_str() );
+ iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_SPEEX);
+ iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
+ iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
+ iaxc_set_silence_threshold(-20.0);
iaxc_start_processing_thread ();
+
+ // Now IAXClient is initialized
+ _initialized = true;
if ( _register ) {
_regId = iaxc_register( const_cast<char*>(_username.c_str()),
const_cast<char*>(_password.c_str()),
const_cast<char*>(_server.c_str()) );
if( _regId == -1 ) {
- SG_LOG(SG_IO, SG_INFO, "FGCom: cannot register iaxclient!");
+ SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot register iaxclient");
return;
}
}
_selectedOutput_node->setIntValue(devs[i].devID);
}
+ // Mute the mic and set speaker at start
+ iaxc_input_level_set( 0.0 );
+ iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
+
iaxc_millisleep(50);
// Do the first call at start
const double freq = _comm0_node->getDoubleValue();
+ _currentFreqKhz = 10 * static_cast<int>(freq * 100 + 0.25);
std::string num = computePhoneNumber(freq, getAirportCode(freq));
- if( num.size() > 0 ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom comm[0] number=" << num );
+ if( !num.empty() ) {
_callComm0 = iaxc_call(num.c_str());
}
if( _callComm0 == -1 )
- SG_LOG( SG_IO, SG_ALERT, "FGCom cannot call selected freq" );
+ SG_LOG( SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str() );
}
void FGCom::updateCall(bool& changed, int& callNo, double freqMHz)
{
+
+ _currentFreqKhz = 10 * static_cast<int>(freqMHz * 100 + 0.25);
+
if (!changed) {
if( !isInRange(freqMHz) ) {
iaxc_dump_call_number(callNo);
return;
}
}
-
- SG_LOG( SG_IO, SG_INFO, "FGCom manage change" );
+
changed = false; // FIXME, out-params are confusing
if( callNo != -1 ) {
if( !isInRange(freqMHz) )
return;
if( !num.empty() ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom number=" << num );
- callNo = iaxc_call_ex(num.c_str(), _callsign.c_str(), NULL, 0 /* no video */);
+ callNo = iaxc_call(num.c_str());
if( callNo == -1 )
- SG_LOG( SG_IO, SG_ALERT, "FGCom cannot call selected freq" );
+ SG_LOG( SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str() );
}
} else {
changed = true;
void FGCom::update(double dt)
{
- if ( !_enabled ) {
+ if ( !_enabled || !_initialized ) {
return;
}
+
// For now we manage FGCom for only one freq because IAXClient
// is not able to handle multiple calls at same time.
- updateCall(_comm0Changed, _callComm0, _comm0_node->getDoubleValue());
- // updateCall(_comm1Changed, _callComm1, _comm1_node->getDoubleValue());
- // updateCall(_nav0Changed, _callNav0, _nav0_node->getDoubleValue());
- // updateCall(_nav1Changed, _callNav1, _nav1_node->getDoubleValue());
+ if( _selectedComm == 0) {
+ updateCall(_comm0Changed, _callComm0, _comm0_node->getDoubleValue());
+ } else {
+ updateCall(_comm1Changed, _callComm0, _comm1_node->getDoubleValue());
+ }
}
void FGCom::shutdown()
{
- SG_LOG( SG_IO, SG_INFO, "FGCom shutdown()" );
+ if( !_enabled ) {
+ return;
+ }
+
+ _initialized = false;
_enabled = false;
+ iaxc_set_event_callback(NULL);
iaxc_unregister(_regId);
iaxc_stop_processing_thread();
iaxc_shutdown();
+
+ assert( static_instance == this );
+ static_instance = NULL;
}
void FGCom::valueChanged(SGPropertyNode *prop)
{
if (prop == _enabled_node) {
- SG_LOG( SG_IO, SG_INFO, "FGCom enabled= " << prop->getBoolValue() );
- if( prop->getBoolValue() ) {
+ bool isEnabled = prop->getBoolValue();
+ if (_enabled == isEnabled) {
+ return;
+ }
+
+ if( isEnabled ) {
init();
postinit();
} else {
}
if (prop == _ptt0_node && _enabled) {
+ if( _ptt0_node->getIntValue() == 2 ) {
+ if( _selectedComm == 0 ) {
+ SG_LOG( SG_IO, SG_INFO, "FGCom: change comm source to comm[1]" );
+ _comm1Changed = true;
+ _selectedComm = 1;
+ } else {
+ SG_LOG( SG_IO, SG_INFO, "FGCom: change comm source to comm[0]" );
+ _comm0Changed = true;
+ _selectedComm = 0;
+ }
+ return;
+ }
if( _ptt0_node->getBoolValue() ) {
- iaxc_input_level_set( _micLevel_node->getFloatValue() ); //0.0 = min , 1.0 = max
iaxc_output_level_set( 0.0 );
+ iaxc_input_level_set( _micLevel_node->getFloatValue() ); //0.0 = min , 1.0 = max
} else {
- iaxc_input_level_set( 0.0 );
iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
+ iaxc_input_level_set( 0.0 );
}
}
if (prop == _test_node) {
- SG_LOG( SG_IO, SG_INFO, "FGCom test= " << prop->getBoolValue() );
testMode( prop->getBoolValue() );
return;
}
//FIXME: not implemented in IAX audio driver (audio_openal.c)
- if (prop == _micBoost_node && _enabled) {
+ if (prop == _micBoost_node && _initialized) {
int micBoost = prop->getIntValue();
- SG_LOG( SG_IO, SG_INFO, "FGCom mic-boost= " << micBoost );
SG_CLAMP_RANGE<int>( micBoost, 0, 1 );
iaxc_mic_boost_set( micBoost ) ; // 0 = enabled , 1 = disabled
return;
}
//FIXME: not implemented in IAX audio driver (audio_openal.c)
- if ((prop == _selectedInput_node || prop == _selectedOutput_node) && _enabled) {
+ if ((prop == _selectedInput_node || prop == _selectedOutput_node) && _initialized) {
int selectedInput = _selectedInput_node->getIntValue();
int selectedOutput = _selectedOutput_node->getIntValue();
- SG_LOG( SG_IO, SG_INFO, "FGCom selected-input= " << selectedInput );
- SG_LOG( SG_IO, SG_INFO, "FGCom selected-output= " << selectedOutput );
iaxc_audio_devices_set(selectedInput, selectedOutput, 0);
return;
}
if (prop == _speakerLevel_node && _enabled) {
float speakerLevel = prop->getFloatValue();
- SG_LOG( SG_IO, SG_INFO, "FGCom speaker-level= " << speakerLevel );
SG_CLAMP_RANGE<float>( speakerLevel, 0.0, 1.0 );
_speakerLevel_node->setFloatValue(speakerLevel);
- iaxc_output_level_set(speakerLevel);
+ //iaxc_output_level_set(speakerLevel);
}
if (prop == _micLevel_node && _enabled) {
float micLevel = prop->getFloatValue();
- SG_LOG( SG_IO, SG_INFO, "FGCom mic-level= " << micLevel );
SG_CLAMP_RANGE<float>( micLevel, 0.0, 1.0 );
_micLevel_node->setFloatValue(micLevel);
- iaxc_input_level_set(micLevel);
+ //iaxc_input_level_set(micLevel);
}
if (prop == _comm0_node) {
if( _currentComm0 != prop->getDoubleValue() ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom comm[0]/freq= " << prop->getDoubleValue() );
_currentComm0 = prop->getDoubleValue();
_p.stamp();
_comm0Changed = true;
}
}
-/*
+
if (prop == _comm1_node) {
if( _currentComm1 != prop->getDoubleValue() ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom comm[1]/freq= " << prop->getDoubleValue() );
_currentComm1 = prop->getDoubleValue();
_p.stamp();
_comm1Changed = true;
}
}
- if (prop == _nav0_node) {
- if( _currentNav0 != prop->getDoubleValue() ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom nav[0]/freq= " << prop->getDoubleValue() );
- _currentNav0 = prop->getDoubleValue();
- _nav0Changed = true;
- }
- }
-
- if (prop == _nav1_node) {
- if( _currentNav1 != prop->getDoubleValue() ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom nav[1]/freq= " << prop->getDoubleValue() );
- _currentNav1 = prop->getDoubleValue();
- _nav1Changed = true;
- }
- }
-*/
-
_listener_active--;
}
void FGCom::testMode(bool testMode)
{
- if(testMode) {
+ if(testMode && _initialized) {
_enabled = false;
- iaxc_dump_call_number(_callComm0);
- iaxc_input_level_set( _micLevel_node->getFloatValue() );
+ iaxc_dump_all_calls();
+ iaxc_input_level_set( 1.0 );
iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
std::string num = computePhoneNumber(TEST_FREQ, NULL_ICAO);
if( num.size() > 0 ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom test mode =" << num );
iaxc_millisleep(IAX_DELAY);
_callComm0 = iaxc_call(num.c_str());
}
if( _callComm0 == -1 )
- SG_LOG( SG_IO, SG_ALERT, "FGCom cannot call selected freq (test mode)" );
+ SG_LOG( SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str() );
} else {
- iaxc_dump_call_number(_callComm0);
- iaxc_millisleep(IAX_DELAY);
- _callComm0 = -1;
- _enabled = true;
+ if( _initialized ) {
+ iaxc_dump_all_calls();
+ iaxc_millisleep(IAX_DELAY);
+ iaxc_input_level_set( 0.0 );
+ iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
+ _callComm0 = -1;
+ _enabled = true;
+ }
}
}
{
SGGeod aircraftPos = globals->get_aircraft_position();
- int freqKhz = 10 * static_cast<int>(freq * 100 + 0.25);
-
for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
- if(special_freq[i] == freqKhz) {
- SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: " << freqKhz << " is specially associated to " << NULL_ICAO );
+ if(special_freq[i] == _currentFreqKhz) {
return NULL_ICAO;
}
}
- flightgear::CommStation* apt = flightgear::CommStation::findByFreq(freqKhz, aircraftPos);
+ flightgear::CommStation* apt = flightgear::CommStation::findByFreq(_currentFreqKhz, aircraftPos);
if( !apt ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: not found" );
return std::string();
}
- SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: found " << apt->airport()->ident() );
+
+ if( apt->type() == FGPositioned::FREQ_TOWER ) {
+ _maxRange = MAX_TWR_RANGE;
+ _minRange = MIN_GNDTWR_RANGE;
+ } else if( apt->type() == FGPositioned::FREQ_GROUND ) {
+ _maxRange = MAX_GND_RANGE;
+ _minRange = MIN_GNDTWR_RANGE;
+ } else {
+ _maxRange = MAX_RANGE;
+ _minRange = MIN_RANGE;
+ }
_aptPos = apt->geod();
return apt->airport()->ident();
-/*
- \param freq The requested frequency e.g 112.7
- \return The ICAO code as string e.g ITS
-*/
-/*
-std::string FGCom::getVorCode(const double& freq) const
-{
- SGGeod aircraftPos = globals->get_aircraft_position();
- FGNavList::TypeFilter filter(FGPositioned::VOR);
-
- FGNavRecord* vor = FGNavList::findByFreq( freq, aircraftPos, &filter);
- if( !vor ) {
- SG_LOG( SG_IO, SG_INFO, "FGCom getVorCode: not found" );
- return std::string();
- }
- SG_LOG( SG_IO, SG_INFO, "FGCom getVorCode: found " << vor->get_ident(); );
-
- return vor->get_ident();
-}
-*/
-
-
/*
\param freq The requested frequency e.g 120.825
\param iaco The associated ICAO code e.g LFMV
bool FGCom::isInRange(const double &freq) const
{
+ for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
+ if( (special_freq[i]) == _currentFreqKhz ) {
+ return 1;
+ }
+ }
+
SGGeod acftPos = globals->get_aircraft_position();
double distNm = SGGeodesy::distanceNm(_aptPos, acftPos);
double delta_elevation_ft = fabs(acftPos.getElevationFt() - _aptPos.getElevationFt());
double rangeNm = 1.23 * sqrt(delta_elevation_ft);
- if (rangeNm > MAX_RANGE) rangeNm = MAX_RANGE;
- if (rangeNm < MIN_RANGE) rangeNm = MIN_RANGE;
+ if (rangeNm > _maxRange) rangeNm = _maxRange;
+ if (rangeNm < _minRange) rangeNm = _minRange;
if( distNm > rangeNm ) return 0;
return 1;
}