]> git.mxchange.org Git - flightgear.git/blobdiff - src/Network/fgcom.cxx
Fix for bug 1304 - crash loading XML route
[flightgear.git] / src / Network / fgcom.cxx
index 5d9319c2053139b9eaf2d7a94354ec2858200205..8d1bfc96c8bb332af58b23ac6677891247fb75f3 100644 (file)
@@ -1,6 +1,6 @@
 // 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,
        122750,
        121500,
-       123500,
-       121000,
-       723340 };
+       123500 };
+
+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;
 }
 
 
@@ -83,21 +117,23 @@ void FGCom::bind()
   _enabled_node            = node->getChild( "enabled", 0, true );
   _micBoost_node           = node->getChild( "mic-boost", 0, true );
   _micLevel_node           = node->getChild( "mic-level", 0, true );
+  _silenceThd_node         = node->getChild( "silence-threshold", 0, true );
   _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() )
@@ -118,6 +154,9 @@ void FGCom::bind()
   if ( !_micLevel_node->hasValue() )
       _micLevel_node->setFloatValue(1.0);
 
+  if ( !_silenceThd_node->hasValue() )
+      _silenceThd_node->setFloatValue(-35.0);
+
   if ( !_register_node->hasValue() )
       _register_node->setBoolValue(false);
 
@@ -127,16 +166,18 @@ void FGCom::bind()
   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);
+  _silenceThd_node->addChangeListener(this);
   _micBoost_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);
 }
@@ -156,16 +197,16 @@ void FGCom::init()
   _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;
 }
 
 
@@ -178,22 +219,36 @@ void FGCom::postinit()
     
     //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_ULAW|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(_silenceThd_node->getFloatValue());
     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;
         }
     }
@@ -255,6 +310,10 @@ void FGCom::postinit()
         _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
@@ -262,11 +321,10 @@ void FGCom::postinit()
     _currentFreqKhz = 10 * static_cast<int>(freq * 100 + 0.25);
     std::string num = computePhoneNumber(freq, getAirportCode(freq));
     if( !num.empty() ) {
-      SG_LOG( SG_IO, SG_INFO, "FGCom comm[0] number=" << num );
       _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() );
 }
 
 
@@ -286,8 +344,7 @@ void FGCom::updateCall(bool& changed, int& callNo, double freqMHz)
                 return;
         }
     }
-    
-    SG_LOG( SG_IO, SG_INFO, "FGCom manage change" );
+
     changed = false; // FIXME, out-params are confusing
 
     if( callNo != -1 ) {
@@ -300,11 +357,10 @@ void FGCom::updateCall(bool& changed, int& callNo, double freqMHz)
         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;
@@ -315,27 +371,37 @@ void FGCom::updateCall(bool& changed, int& callNo, double freqMHz)
 
 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;
 }
 
 
@@ -343,8 +409,12 @@ void FGCom::shutdown()
 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 {
@@ -354,36 +424,51 @@ void FGCom::valueChanged(SGPropertyNode *prop)
   }
 
   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;
   }
 
+  if (prop == _silenceThd_node && _initialized) {
+    float silenceThd = prop->getFloatValue();
+    SG_CLAMP_RANGE<float>( silenceThd, -60, 0 );
+    iaxc_set_silence_threshold( silenceThd );
+    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;
   }
@@ -395,7 +480,6 @@ void FGCom::valueChanged(SGPropertyNode *prop)
 
   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);
@@ -403,47 +487,27 @@ void FGCom::valueChanged(SGPropertyNode *prop)
 
   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--;
 }
 
@@ -451,24 +515,27 @@ void FGCom::valueChanged(SGPropertyNode *prop)
 
 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;
+    }
   }
 }
 
@@ -485,17 +552,28 @@ std::string FGCom::getAirportCode(const double& freq)
 
   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) {
-      SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: " << freq << " is specially associated to " << NULL_ICAO );
       return NULL_ICAO;
     }
   }
 
   flightgear::CommStation* apt = flightgear::CommStation::findByFreq(_currentFreqKhz, aircraftPos);
   if( !apt ) {
-    SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: not found" );
-    return std::string();
+    apt = flightgear::CommStation::findByFreq(_currentFreqKhz-10, aircraftPos); // Check for 8.33KHz
+    if( !apt ) {
+      return std::string();
+    }
+  }
+
+  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;
   }
-  SG_LOG( SG_IO, SG_INFO, "FGCom getAirportCode: found " << apt->airport()->ident() );
 
   _aptPos = apt->geod();
   return apt->airport()->ident();
@@ -503,28 +581,6 @@ std::string FGCom::getAirportCode(const double& freq)
 
 
 
-/*
-  \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
@@ -584,8 +640,8 @@ bool FGCom::isInRange(const double &freq) const
     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;
 }