Files
flightgear/src/Network/fgcom.cxx

894 lines
25 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// fgcom.cxx -- FGCom: Voice communication
//
// 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
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//#include <stdlib.h>
#include <config.h>
#include "fgcom.hxx"
// standard library includes
#include <cstdio>
// simgear includes
#include <simgear/compiler.h>
#include <simgear/sg_inlines.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/timing/timestamp.hxx>
// flightgear includes
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <ATC/CommStation.hxx>
#include <Airports/airport.hxx>
#include <Navaids/navlist.hxx>
#include <Main/sentryIntegration.hxx>
#include <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.jingweiht.com"
#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 };
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;
}
//zhongjin
SG_LOG(SG_SOUND, SG_ALERT, "FCCom iaxc_callback:" << e.type);
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);
}
auto level = SG_INFO;
if ((text.type == IAXC_TEXT_TYPE_ERROR) || (text.type == IAXC_TEXT_TYPE_FATALERROR)) {
level = SG_ALERT;
}
SG_LOG(SG_SOUND, level, std::string{"FCCom IAX:"} + text.message);
}
FGCom::FGCom()
{
_maxRange = MAX_RANGE;
_minRange = MIN_RANGE;
}
FGCom::~FGCom()
{
}
void FGCom::bind()
{
SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
_test_node = node->getChild( "test", 0, true );
_server_node = node->getChild( "server", 0, true );
_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", 1, 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 );
_ptt_node = fgGetNode("/controls/radios/comm-ptt", true);
_selected_comm_node = fgGetNode("/controls/radios/comm-radio-selected", true);
_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() )
_enabled_node->setBoolValue(true);
if ( !_test_node->hasValue() )
_test_node->setBoolValue(false);
if ( !_micBoost_node->hasValue() )
_micBoost_node->setIntValue(1);
if ( !_server_node->hasValue() )
_server_node->setStringValue(DEFAULT_SERVER);
if ( !_speakerLevel_node->hasValue() )
_speakerLevel_node->setFloatValue(1.0);
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);
if ( !_username_node->hasValue() )
_username_node->setStringValue("guest");
if ( !_password_node->hasValue() )
_password_node->setStringValue("guest");
if ( !_showMessages_node->hasValue() )
_showMessages_node->setBoolValue(false);
_mpTransmitFrequencyNode = fgGetNode("sim/multiplay/comm-transmit-frequency-hz", 0, true);
_mpTransmitPowerNode = fgGetNode("sim/multiplay/comm-transmit-power-norm", 0, true);
_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);
_ptt_node->addChangeListener(this);
_selected_comm_node->addChangeListener(this);
_test_node->addChangeListener(this);
// if you listen to more properties, be sure to remove
// the listener in unbind() as well
}
void FGCom::unbind()
{
_selectedOutput_node->removeChangeListener(this);
_selectedInput_node->removeChangeListener(this);
_speakerLevel_node->removeChangeListener(this);
_silenceThd_node->removeChangeListener(this);
_micBoost_node->removeChangeListener(this);
_micLevel_node->removeChangeListener(this);
_enabled_node->removeChangeListener(this);
_ptt_node->removeChangeListener(this);
_selected_comm_node->removeChangeListener(this);
_test_node->removeChangeListener(this);
}
void FGCom::init()
{
_enabled = _enabled_node->getBoolValue();
_server = _server_node->getStringValue();
_register = _register_node->getBoolValue();
_username = _username_node->getStringValue();
_password = _password_node->getStringValue();
_connected = false;
_currentCommFrequency = 0.0;
_maxRange = MAX_RANGE;
_minRange = MIN_RANGE;
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: init");
}
void FGCom::postinit()
{
//SG_LOG(SG_SOUND, SG_ALERT, "FGCom: postinit() 11111111111111111111111111");
if( !_enabled ) {
return;
}
//WARNING: this _must_ be executed after sound system is totally initialized !
if( iaxc_initialize(NUM_CALLS) ) {
SG_LOG(SG_SOUND, 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() );
std::string app = "FGFS-";
app += _version_node->getStringValue();
iaxc_set_callerid( _callsign_node->getStringValue().c_str(), app.c_str() );
//iaxc_set_callerid("guest","FGCOM");
iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
//iaxc_set_formats (IAXC_FORMAT_GSM, IAXC_FORMAT_ULAW|IAXC_FORMAT_GSM);
//iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
iaxc_set_speex_settings(1,-1,-1,0,8000,3);
/*
void iaxc_set_speex_settings(int decode_enhance, float quality,
int bitrate, int vbr, int abr, int complexity)
*/
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_SOUND, SG_ALERT, "FGCom: cannot register iaxclient");
return;
}
}
/*
Here we will create the list of available audio devices
Each audio device has a name, an ID, and a list of capabilities
If an audio device can output sound, available-output=true
If an audio device can input sound, available-input=true
/sim/fgcom/selected-input (int)
/sim/fgcom/selected-output (int)
/sim/fgcom/device[n]/id (int)
/sim/fgcom/device[n]/name (string)
/sim/fgcom/device[n]/available-input (bool)
/sim/fgcom/device[n]/available-output (bool)
*/
//FIXME: OpenAL driver use an hard-coded device
// so all following is unused finally until someone
// implement "multi-device" support in IAX audio driver
SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
struct iaxc_audio_device *devs;
int nDevs, input, output, ring;
iaxc_audio_devices_get(&devs,&nDevs, &input, &output, &ring);
//zhongjin
SG_LOG(SG_SOUND, SG_INFO, "设备总数:" << nDevs);
for(int i=0; i<nDevs; i++ ) {
SGPropertyNode *in_node = node->getChild("device", i, true);
//zhongjin
SG_LOG(SG_SOUND, SG_INFO, "设备:" << i);
// devID
_deviceID_node[i] = in_node->getChild("id", 0, true);
_deviceID_node[i]->setIntValue(devs[i].devID);
// name
_deviceName_node[i] = in_node->getChild("name", 0, true);
_deviceName_node[i]->setStringValue(devs[i].name);
// input capability
_deviceInput_node[i] = in_node->getChild("available-input", 0, true);
if( devs[i].capabilities & IAXC_AD_INPUT )
_deviceInput_node[i]->setBoolValue(true);
else
_deviceInput_node[i]->setBoolValue(false);
// output capability
_deviceOutput_node[i] = in_node->getChild("available-output", 0, true);
if( devs[i].capabilities & IAXC_AD_OUTPUT )
_deviceOutput_node[i]->setBoolValue(true);
else
_deviceOutput_node[i]->setBoolValue(false);
// use default device at start
if( devs[i].capabilities & IAXC_AD_INPUT_DEFAULT )
_selectedInput_node->setIntValue(devs[i].devID);
if( devs[i].capabilities & IAXC_AD_OUTPUT_DEFAULT )
_selectedOutput_node->setIntValue(devs[i].devID);
}
iaxc_input_level_set(0.1);
iaxc_output_level_set(0.8);
iaxc_millisleep(300);
// Do the first call at start
setupCommFrequency();
connectToCommFrequency();
//iaxc_call("guest:guest@fgcom.jingweiht.com/01118300");
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: postinit 初始化完成");
}
double FGCom::getCurrentCommVolume() const {
double rv = 0.8;
if (_speakerLevel_node) {
rv = rv * _speakerLevel_node->getFloatValue();
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: _speakerLevel_node " << _speakerLevel_node->getFloatValue());
}
if (_commVolumeNode) {
//暂时关闭 _commVolumeNode
//rv = rv * _commVolumeNode->getFloatValue();
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: _commVolumeNode " << _commVolumeNode->getFloatValue());
}
return rv;
}
double FGCom::getCurrentFrequencyKhz() const {
return 10 * static_cast<int>(_currentCommFrequency * 100 + 0.25);
}
void FGCom::setupCommFrequency(int channel) {
// SG_LOG(SG_SOUND, SG_ALERT, "setupCommFrequency1 channel=" << channel);
if (channel < 1) {
if (_selected_comm_node != nullptr) {
channel = _selected_comm_node->getIntValue();
}
}
// SG_LOG(SG_SOUND, SG_ALERT, "setupCommFrequency2 channel=" << channel);
// disconnect if channel set to 0
if (channel < 1) {
if (_currentCallIdent != -1) {
iaxc_dump_call_number(_currentCallIdent);
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: disconnect as channel 0 " << _currentCallIdent);
_currentCallIdent = -1;
}
_currentCommFrequency = 0.0;
return;
}
if (channel > 0)
{
channel--; // adjust back to zero based index.
SGPropertyNode *commRadioNode = fgGetNode("/instrumentation/")->getChild("comm", channel, false);
if (commRadioNode) {
SGPropertyNode *frequencyNode = commRadioNode->getChild("frequencies");
if (_commVolumeNode)
_commVolumeNode->removeChangeListener(this);
_commVolumeNode = commRadioNode->getChild("volume");
if (frequencyNode) {
frequencyNode = frequencyNode->getChild("selected-mhz");
if (frequencyNode) {
if (_commFrequencyNode != frequencyNode) {
if (_commFrequencyNode)
_commFrequencyNode->removeChangeListener(this);
_commFrequencyNode = frequencyNode;
_commFrequencyNode->addChangeListener(this);
_commVolumeNode->addChangeListener(this);
}
_currentCommFrequency = frequencyNode->getDoubleValue();
return;
}
}
}
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: setupCommFrequency node listener failed: channel " << channel);
}
SG_LOG(SG_SOUND, SG_ALERT, "setupCommFrequency3 _commFrequencyNode=" << _commFrequencyNode);
if (_commFrequencyNode)
_commFrequencyNode->removeChangeListener(this);
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: setupCommFrequency invalid channel " << channel);
_currentCommFrequency = 0.0;
}
void FGCom::connectToCommFrequency() {
// ensure that the current comm is still in range
//zhongjin
//SG_LOG(SG_SOUND, SG_INFO, "FGCom: connectToCommFrequency()");
//SG_LOG(SG_SOUND, SG_INFO, "FGCom: _currentCallFrequency=" << _currentCallFrequency);
if ((_currentCallFrequency > 0.0) && !isInRange(_currentCallFrequency)) {
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: call out of range of: " << _currentCallFrequency);
_currentCallFrequency = 0.0;
}
// don't connected (and disconnect if already connected) when tuned freq is 0
if (_currentCommFrequency < 1.0) {
if (_currentCallIdent != -1) {
iaxc_dump_call_number(_currentCallIdent);
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: disconnect as freq 0: current call " << _currentCallIdent);
_currentCallIdent = -1;
}
return;
}
if (_currentCallFrequency != _currentCommFrequency || _currentCallIdent == -1) {
if (_currentCallIdent != -1) {
iaxc_dump_call_number(_currentCallIdent);
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom:iaxc_dump_call_number:" << _currentCallIdent);
_currentCallIdent = -1;
}
if (_currentCallIdent == -1)
{
// std::string num = computePhoneNumber(_currentCommFrequency, getAirportCode(_currentCommFrequency));
std::string num = computePhoneNumber(_currentCommFrequency, "ZGHA");
_processingTimer.stamp();
if (!isInRange(_currentCommFrequency)) {
if (_currentCallIdent != -1) {
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: disconnect call as not in range " << _currentCallIdent);
if (_currentCallIdent != -1) {
iaxc_dump_call_number(_currentCallIdent);
_currentCallIdent = -1;
}
}
return;
}
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: connectToCommFrequency num=" << num);
if (!num.empty()) {
lib_hangup();
_currentCallIdent = iaxc_call(num.c_str());
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: iaxc_call(" << num << ") _currentCallIdent=" << _currentCallIdent);
if (_currentCallIdent == -1)
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: cannot call " << num.c_str());
else {
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: call established " << num.c_str() << " Freq: " << _currentCommFrequency);
_currentCallFrequency = _currentCommFrequency;
_connected = true;
}
}
else
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: frequency " << _currentCommFrequency << " does not map to valid IAX address");
}
}
}
void FGCom::updateCall()
{
//SG_LOG(SG_SOUND,SG_ALERT,"updateCall()");
if (_processingTimer.elapsedMSec() > IAX_DELAY) {
_processingTimer.stamp();
connectToCommFrequency();
}
}
void FGCom::update(double dt)
{
if (!_enabled || !_initialized) {
return;
}
updateCall();
}
void FGCom::shutdown()
{
if( !_enabled ) {
return;
}
_initialized = false;
_enabled = false;
_connected = false;
iaxc_set_event_callback(NULL);
iaxc_unregister(_regId);
iaxc_stop_processing_thread();
iaxc_shutdown();
// added to help debugging lingering IAX thread after fgOSCloseWindow
flightgear::addSentryBreadcrumb("Did shutdown FGCom", "info");
assert( static_instance == this );
static_instance = NULL;
}
void FGCom::valueChanged(SGPropertyNode *prop)
{
//return;
if (prop == _enabled_node) {
bool isEnabled = prop->getBoolValue();
if (_enabled == isEnabled) {
return;
}
if( isEnabled ) {
init();
postinit();
} else {
shutdown();
}
return;
}
// avoid crash when properties change before FGCom::postinit
// https://sourceforge.net/p/flightgear/codetickets/2574/
if (!_initialized) {
return;
}
if (prop == _commVolumeNode && _enabled) {
if (_ptt_node->getIntValue()) {
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: ignoring change comm volume as PTT pressed");
}
else
{
iaxc_input_level_set(0.0);
// iaxc_output_level_set(getCurrentCommVolume());
iaxc_output_level_set(1.0);
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: change comm volume=" << _commVolumeNode->getFloatValue());
}
}
if (prop == _selected_comm_node && _enabled) {
setupCommFrequency();
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: change comm frequency (selected node): set to " << _currentCommFrequency);
}
if (prop == _commFrequencyNode && _enabled) {
setupCommFrequency();
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: change comm frequency (property updated): set to " << _currentCommFrequency);
}
if (prop == _ptt_node && _enabled) {
if (_ptt_node->getIntValue()) {
// ensure that we are on the right channel by calling setupCommFrequency
setupCommFrequency();
iaxc_output_level_set(0.0);
iaxc_input_level_set(_micLevel_node->getFloatValue()); //0.0 = min , 1.0 = max
_mpTransmitFrequencyNode->setValue(_currentCallFrequency * 1000000);
_mpTransmitPowerNode->setValue(1.0);
SGPath soundpath( globals->get_fg_root() + "/Sounds/click.wav" );
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: PTT active: " << soundpath);
std::string urlcmd = "ffplay -nodisp -autoexit -loglevel quiet -i " + soundpath;
SG_LOG(SG_SOUND, SG_INFO, "CMD :" << urlcmd );
system(urlcmd.c_str());
}
else {
// iaxc_output_level_set(getCurrentCommVolume());
iaxc_output_level_set(1.0);
iaxc_input_level_set(0.0);
// SG_LOG(SG_SOUND, SG_ALERT, "FGCom: PTT release: " << _currentCallFrequency << " vol=" << getCurrentCommVolume());
_mpTransmitFrequencyNode->setValue(0);
_mpTransmitPowerNode->setValue(0);
}
}
if (prop == _test_node) {
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 && _initialized) {
int micBoost = prop->getIntValue();
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) && _initialized) {
int selectedInput = _selectedInput_node->getIntValue();
int selectedOutput = _selectedOutput_node->getIntValue();
iaxc_audio_devices_set(selectedInput, selectedOutput, 0);
return;
}
if (_listener_active)
return;
_listener_active++;
if (prop == _speakerLevel_node && _enabled) {
float speakerLevel = prop->getFloatValue();
SG_CLAMP_RANGE<float>( speakerLevel, 0.0, 1.0 );
speakerLevel=1.0;
_speakerLevel_node->setFloatValue(speakerLevel);
iaxc_output_level_set(speakerLevel);
}
if (prop == _micLevel_node && _enabled) {
float micLevel = prop->getFloatValue();
SG_CLAMP_RANGE<float>( micLevel, 0.0, 1.0 );
_micLevel_node->setFloatValue(micLevel);
//iaxc_input_level_set(micLevel);
}
_listener_active--;
}
void FGCom::testMode(bool testMode)
{
if(testMode && _initialized) {
_enabled = false;
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 ) {
iaxc_millisleep(IAX_DELAY);
_currentCallIdent = iaxc_call(num.c_str());
}
if( _currentCallIdent == -1 )
SG_LOG( SG_SOUND, SG_ALERT, "FGCom: cannot call " << num.c_str() );
} else {
if( _initialized ) {
iaxc_dump_all_calls();
iaxc_millisleep(IAX_DELAY);
iaxc_input_level_set( 0.0 );
//iaxc_output_level_set(getCurrentCommVolume());
iaxc_output_level_set(1.0);
_currentCallIdent = -1;
_enabled = true;
}
}
}
/*
\param freq The requested frequency e.g 120.825
\return The ICAO code as string e.g LFMV
*/
std::string FGCom::getAirportCode(const double& freq)
{
SGGeod aircraftPos = globals->get_aircraft_position();
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] == getCurrentFrequencyKhz()) {
return NULL_ICAO;
}
}
flightgear::CommStation* apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz(), aircraftPos);
if( !apt ) {
apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz() -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;
}
_aptPos = apt->geod();
return apt->airport()->ident();
}
/*
\param freq The requested frequency e.g 120.825
\param iaco The associated ICAO code e.g LFMV
\return The phone number as string i.e username:password@fgcom.flightgear.org/0176707786120825
*/
std::string FGCom::computePhoneNumber(const double& freq, const std::string& icao) const
{
if( icao.empty() )
return std::string();
char phoneNumber[256];
char exten[32];
char tmp[5];
/*Convert ICAO to ASCII */
sprintf( tmp, "%4s", icao.c_str() );
/*Built the phone number */
sprintf( exten,
"%02d%02d%02d%02d%02d%06d",
01,
tmp[0],
tmp[1],
tmp[2],
tmp[3],
(int) (freq * 1000 + 0.5) );
exten[16] = '\0';
// zhongjin 测试环境,去掉机场编号
sprintf( exten,
"%02d%06d",
01,
(int) (freq * 1000 + 0.5) );
exten[8] = '\0';
snprintf( phoneNumber,
sizeof (phoneNumber),
"%s:%s@%s/%s",
_username.c_str(),
_password.c_str(),
_server.c_str(),
exten);
SG_LOG(SG_SOUND, SG_ALERT, "FGCom: computePhoneNumber:" << phoneNumber << " icao:" << icao);
return phoneNumber;
}
/*
\return A boolean value, 1=in range, 0=out of range
*/
bool FGCom::isInRange(const double &freq) const
{
// zhongjin 测试环境始终返回1
return 1;
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]) == getCurrentFrequencyKhz()) {
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 > _maxRange) rangeNm = _maxRange;
if (rangeNm < _minRange) rangeNm = _minRange;
if( distNm > rangeNm ) return 0;
return 1;
}
bool FGCom::lib_hangup()
{
if(!_connected)
return false;
SG_LOG(SG_GENERAL, SG_DEBUG, "Request hangup");
iaxc_dump_all_calls();
iaxc_millisleep(300);
_currentCallIdent = -1;
return false;
}
// Register the subsystem.
SGSubsystemMgr::Registrant<FGCom> registrantFGCom;