first commit
This commit is contained in:
32
utils/fgcom/CMakeLists.txt
Normal file
32
utils/fgcom/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
set(name fgcom)
|
||||
|
||||
# Copy positions.txt content in const char* _positionsData[];
|
||||
file(READ utils/positions.txt POSITIONS_DATA)
|
||||
string(REGEX REPLACE "\n" "\"%
|
||||
\"" POSITIONS_DATA ${POSITIONS_DATA})
|
||||
string(REGEX REPLACE "%" "," POSITIONS_DATA ${POSITIONS_DATA})
|
||||
|
||||
set(out_file ${CMAKE_BINARY_DIR}/positions.hxx)
|
||||
|
||||
file(WRITE ${out_file} "const char* _positionsData[] = {\n\"")
|
||||
file(APPEND ${out_file} ${POSITIONS_DATA})
|
||||
file(APPEND ${out_file} "\"\n};")
|
||||
|
||||
if(MSVC)
|
||||
set(RESOURCE_FILE fgcom.rc)
|
||||
endif(MSVC)
|
||||
|
||||
set(SOURCES fgcom_external.cxx)
|
||||
set(HEADERS fgcom_external.hxx ${out_file})
|
||||
|
||||
add_executable(${name}
|
||||
${SOURCES}
|
||||
${HEADERS}
|
||||
${RESOURCE_FILE}
|
||||
)
|
||||
|
||||
target_link_Libraries(${name}
|
||||
iaxclient_lib
|
||||
SimGearCore)
|
||||
|
||||
install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
BIN
utils/fgcom/fgcom.ico
Normal file
BIN
utils/fgcom/fgcom.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
1
utils/fgcom/fgcom.rc
Normal file
1
utils/fgcom/fgcom.rc
Normal file
@@ -0,0 +1 @@
|
||||
FGCOM ICON "fgcom.ico"
|
||||
644
utils/fgcom/fgcom_external.cxx
Normal file
644
utils/fgcom/fgcom_external.cxx
Normal file
@@ -0,0 +1,644 @@
|
||||
/*
|
||||
* fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
|
||||
*
|
||||
* This program realizes the usage of the VoIP infractructure based
|
||||
* on flight data which is send from FlightGear with an external
|
||||
* protocol to this application.
|
||||
*
|
||||
* Clement de l'Hamaide - Jan 2014
|
||||
* Re-writting of FGCom standalone
|
||||
*
|
||||
* 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 "config.h"
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/io/raw_socket.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
#include <iaxclient.h>
|
||||
|
||||
#include "fgcom_external.hxx"
|
||||
#include "positions.hxx" // provides _positionsData[];
|
||||
|
||||
int _port = 16661;
|
||||
int _callId = -1;
|
||||
int _currentFreqKhz = -1;
|
||||
int _maxRange = 100;
|
||||
int _minRange = 10;
|
||||
int _registrationId = -1;
|
||||
bool _libInitialized = false;
|
||||
bool _running = true;
|
||||
bool _debug = false;
|
||||
bool _connected = false;
|
||||
double _frequency = -1;
|
||||
double _atis = -1;
|
||||
double _silenceThd = -35.0;
|
||||
std::string _app = "FGCOM-";
|
||||
std::string _host = "127.0.0.1";
|
||||
std::string _server = "fgcom.flightgear.org";
|
||||
std::string _airport = "ZZZZ";
|
||||
std::string _callsign = "guest";
|
||||
std::string _username = "guest";
|
||||
std::string _password = "guest";
|
||||
|
||||
SGGeod _airportPos;
|
||||
SGTimeStamp _p;
|
||||
std::multimap<int, Airport> _airportsData;
|
||||
|
||||
const int special_freq[] = { // Define some freq who need to be used with icao = ZZZZ
|
||||
910000,
|
||||
911000,
|
||||
700000,
|
||||
123450,
|
||||
122750,
|
||||
121500,
|
||||
123500 };
|
||||
|
||||
//
|
||||
// Main loop
|
||||
//
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
signal(SIGINT, quit);
|
||||
signal(SIGTERM, quit);
|
||||
|
||||
simgear::requestConsole(true);
|
||||
sglog().setLogLevels(SG_ALL, SG_INFO);
|
||||
_app += FGCOM_VERSION;
|
||||
Modes mode = PILOT;
|
||||
std::string num = "";
|
||||
|
||||
for(int count = 1; count < argc; count++) {
|
||||
std::string item = argv[count];
|
||||
std::string option = item.substr(2, item.find("=")-2);
|
||||
std::string value = item.substr(item.find("=")+1, item.size());
|
||||
if(option == "server") _server = value;
|
||||
if(option == "host") _host = value;
|
||||
if(option == "port") _port = atoi(value.c_str());
|
||||
if(option == "callsign") _callsign = value;
|
||||
if(option == "frequency") _frequency = atof(value.c_str());
|
||||
if(option == "atis") _atis = atof(value.c_str());
|
||||
if(option == "airport") _airport = simgear::strutils::uppercase(value);
|
||||
if(option == "username") _username = value;
|
||||
if(option == "password") _password = value;
|
||||
if(option == "silence-threshold") _silenceThd = atof(value.c_str());
|
||||
if(option == "debug") sglog().setLogLevels(SG_ALL, SG_DEBUG);
|
||||
if(option == "help") return usage();
|
||||
if(option == "version") return version();
|
||||
}
|
||||
|
||||
if(_frequency == 910.000)
|
||||
mode = TEST;
|
||||
if(_frequency <= 136.975 && _frequency >= 118.000)
|
||||
mode = OBS;
|
||||
if(_atis <= 136.975 && _atis >= 118.000 && _airport != "ZZZZ")
|
||||
mode = ATC;
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
|
||||
<< ", at " << __TIME__ );
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "For help usage, use --help");
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Starting FGCom session as " << _username << ":xxxxxxxxx@" << _server);
|
||||
|
||||
if( !(_libInitialized = lib_init()) )
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (_username != "guest" && _password != "guest")
|
||||
_registrationId = lib_registration();
|
||||
|
||||
if(mode == PILOT) {
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode PILOT" );
|
||||
|
||||
simgear::Socket::initSockets();
|
||||
simgear::Socket sgSocket;
|
||||
sgSocket.open(false);
|
||||
sgSocket.bind(_host.c_str(), _port);
|
||||
sgSocket.setBlocking(false);
|
||||
lib_setVolume(0.0, 1.0);
|
||||
static char currentPacket[MAXBUFLEN+2], previousPacket[MAXBUFLEN+2];
|
||||
struct Data currentData, previousData, previousPosData;
|
||||
double currentFreq = -1, previousFreq = -1;
|
||||
std::string currentIcao = "";
|
||||
ActiveComm activeComm = COM1;
|
||||
|
||||
_airportsData = getAirportsData();
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "");
|
||||
|
||||
while(_running) {
|
||||
int bytes = sgSocket.recv(currentPacket, sizeof(currentPacket)-1, 0);
|
||||
if (bytes == -1) {
|
||||
SGTimeStamp::sleepForMSec(1); // Prevent full CPU usage (loop)
|
||||
continue;
|
||||
}
|
||||
|
||||
currentPacket[bytes] = '\0';
|
||||
if( strcmp(currentPacket, previousPacket) != 0 ) {
|
||||
std::string packet(currentPacket);
|
||||
std::vector<std::string> properties = simgear::strutils::split(packet, ",");
|
||||
for(size_t i=0; i < properties.size(); i++) {
|
||||
std::vector<std::string> prop = simgear::strutils::split(properties[i], "=");
|
||||
if(prop[0] == "PTT") currentData.ptt = atoi(prop[1].c_str());
|
||||
if(prop[0] == "LAT") currentData.lat = atof(prop[1].c_str());
|
||||
if(prop[0] == "LON") currentData.lon = atof(prop[1].c_str());
|
||||
if(prop[0] == "ALT") currentData.alt = atof(prop[1].c_str());
|
||||
if(prop[0] == "COM1_FRQ") currentData.com1 = atof(prop[1].c_str());
|
||||
if(prop[0] == "COM2_FRQ") currentData.com2 = atof(prop[1].c_str());
|
||||
if(prop[0] == "OUTPUT_VOL") currentData.outputVol = atof(prop[1].c_str());
|
||||
if(prop[0] == "SILENCE_THD") currentData.silenceThd = atof(prop[1].c_str());
|
||||
if(prop[0] == "CALLSIGN") currentData.callsign = prop[1];
|
||||
}
|
||||
|
||||
if(currentData.ptt != previousData.ptt) {
|
||||
if(currentData.ptt == 2) {
|
||||
if(activeComm == COM1) {
|
||||
activeComm = COM2;
|
||||
currentFreq = currentData.com2;
|
||||
} else {
|
||||
activeComm = COM1;
|
||||
currentFreq = currentData.com1;
|
||||
}
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Select radio " << activeComm << " on " << currentFreq << " MHz" );
|
||||
} else if(currentData.ptt) {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "[SPEAK] unmute mic, mute speaker" );
|
||||
lib_setVolume(1.0, 0.0);
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "[LISTEN] mute mic, unmute speaker" );
|
||||
lib_setVolume(0.0, currentData.outputVol);
|
||||
}
|
||||
}
|
||||
|
||||
if(currentData.outputVol != previousData.outputVol)
|
||||
lib_setVolume(0.0, currentData.outputVol);
|
||||
|
||||
if(currentData.silenceThd != previousData.silenceThd)
|
||||
lib_setSilenceThreshold(currentData.silenceThd);
|
||||
|
||||
if(currentData.callsign != previousData.callsign)
|
||||
lib_setCallerId(currentData.callsign);
|
||||
|
||||
if(currentData.com1 != previousData.com1 && activeComm == COM1) {
|
||||
currentFreq = currentData.com1;
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
|
||||
}
|
||||
|
||||
if(currentData.com2 != previousData.com2 && activeComm == COM2) {
|
||||
currentFreq = currentData.com2;
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
|
||||
}
|
||||
|
||||
if(previousFreq != currentFreq || currentData.callsign != previousData.callsign) {
|
||||
_currentFreqKhz = 10 * static_cast<int>(currentFreq * 100 + 0.25);
|
||||
currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
|
||||
|
||||
if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
|
||||
_connected = lib_call(currentIcao, currentFreq);
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
|
||||
} else {
|
||||
if(_connected) {
|
||||
_connected = lib_hangup();
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( currentData.lat <= previousPosData.lat - 0.05 ||
|
||||
currentData.lon <= previousPosData.lon - 0.05 ||
|
||||
currentData.alt <= previousPosData.alt - 50.0 ||
|
||||
currentData.lat >= previousPosData.lat + 0.05 ||
|
||||
currentData.lon >= previousPosData.lon + 0.05 ||
|
||||
currentData.alt >= previousPosData.alt + 50.0) {
|
||||
|
||||
currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
|
||||
if(_connected) {
|
||||
if(!isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
|
||||
_connected = lib_hangup();
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
|
||||
}
|
||||
} else {
|
||||
if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
|
||||
_connected = lib_call(currentIcao, currentFreq);
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
|
||||
}
|
||||
}
|
||||
previousPosData = currentData;
|
||||
}
|
||||
previousFreq = currentFreq;
|
||||
previousData = currentData;
|
||||
}
|
||||
strcpy(previousPacket, currentPacket);
|
||||
} // while()
|
||||
sgSocket.close();
|
||||
} else { // if(mode == PILOT)
|
||||
int sessionDuration = 1000;
|
||||
_p.stamp();
|
||||
if(mode == OBS) {
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode OBS (max duration: 6 hours)" );
|
||||
sessionDuration *= 2160; // 6 hours for OBS mode
|
||||
lib_setVolume(0.0, 1.0);
|
||||
lib_setCallerId("::OBS::");
|
||||
num = computePhoneNumber(_frequency, _airport);
|
||||
} else {
|
||||
lib_setVolume(1.0, 1.0);
|
||||
if(mode == TEST) {
|
||||
sessionDuration *= 65; // 65 seconds for TEST mode
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode TEST (max duration: 65 seconds)" );
|
||||
_airport = "ZZZZ";
|
||||
num = computePhoneNumber(_frequency, _airport);
|
||||
} else if(mode == ATC) {
|
||||
sessionDuration *= 45; // 45 seconds for ATC mode
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC (max duration: 45 seconds)" );
|
||||
num = computePhoneNumber(_atis, _airport, true);
|
||||
}
|
||||
}
|
||||
_connected = lib_directCall(_airport, _frequency, num);
|
||||
|
||||
while (_p.elapsedMSec() <= sessionDuration && _running){
|
||||
SGTimeStamp::sleepForMSec(2000);
|
||||
}
|
||||
}
|
||||
|
||||
if(!lib_shutdown())
|
||||
return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// function: getAirportsData
|
||||
// action: parse positionsData.hxx then build multimap
|
||||
|
||||
std::multimap<int, Airport> getAirportsData()
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
std::multimap<int, Airport> aptData;
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Loading airports information...");
|
||||
|
||||
for(size_t i=0; i < sizeof(_positionsData)/sizeof(*_positionsData); i++) { // _positionsData is provided by positions.hxx
|
||||
std::vector<std::string> entries = simgear::strutils::split(_positionsData[i], ",");
|
||||
if(entries.size() == 6) {
|
||||
// [0]=ICAO, [1]=Frequency, [2]=Latitude, [3]=Longitude, [4]=ID/Type, [5]=Name
|
||||
std::string entryIcao = entries[0];
|
||||
double entryFreq = atof(entries[1].c_str());
|
||||
double entryLat = atof(entries[2].c_str());
|
||||
double entryLon = atof(entries[3].c_str());
|
||||
std::string entryType = entries[4];
|
||||
std::string entryName = entries[5];
|
||||
|
||||
int aptFreqKhz = 10 * static_cast<int>(entryFreq * 100 + 0.25);
|
||||
Airport apt;
|
||||
apt.icao = entryIcao;
|
||||
apt.frequency = entryFreq;
|
||||
apt.latitude = entryLat;
|
||||
apt.longitude = entryLon;
|
||||
apt.type = entryType;
|
||||
apt.name = entryName;
|
||||
aptData.insert( std::pair<int, Airport>(aptFreqKhz, apt) );
|
||||
}
|
||||
}
|
||||
return aptData;
|
||||
}
|
||||
|
||||
// function: orderByDistanceNm
|
||||
// action: sort airportsInRange vector by distanceNm ASC in getClosestAirportForFreq()
|
||||
|
||||
bool orderByDistanceNm(Airport a, Airport b)
|
||||
{
|
||||
return a.distanceNm < b.distanceNm;
|
||||
}
|
||||
|
||||
// function: gestClosestAircraftForFreq
|
||||
// action: return ICAO of closest airport with given frequency and define his position
|
||||
|
||||
std::string getClosestAirportForFreq(double freq, double acftLat, double acftLon, double acftAlt)
|
||||
{
|
||||
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 std::string("ZZZZ");
|
||||
}
|
||||
|
||||
std::string icao = "";
|
||||
double aptLon = 0;
|
||||
double aptLat = 0;
|
||||
int freqKhz = 10 * static_cast<int>(freq * 100 + 0.25);
|
||||
SGGeod acftPos = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
|
||||
std::vector<Airport> airportsInRange;
|
||||
|
||||
std::pair <std::multimap<int, Airport>::iterator, std::multimap<int, Airport>::iterator> ret;
|
||||
ret = _airportsData.equal_range(freqKhz);
|
||||
for (std::multimap<int, Airport>::iterator it=ret.first; it!=ret.second; ++it) {
|
||||
SGGeod aptPos = SGGeod::fromDeg(it->second.longitude, it->second.latitude);
|
||||
double distNm = SGGeodesy::distanceNm(aptPos, acftPos);
|
||||
if(distNm <= _maxRange){
|
||||
it->second.distanceNm = distNm;
|
||||
airportsInRange.push_back(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
if(!airportsInRange.size())
|
||||
return icao;
|
||||
|
||||
std::sort(airportsInRange.begin(), airportsInRange.end(), orderByDistanceNm);
|
||||
|
||||
aptLon = airportsInRange[0].longitude;
|
||||
aptLat = airportsInRange[0].latitude;
|
||||
icao = airportsInRange[0].icao;
|
||||
_airportPos = SGGeod::fromDeg(aptLon, aptLat);
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Airport " << airportsInRange[0].icao << " " << airportsInRange[0].name << " - "
|
||||
<< airportsInRange[0].type << " on " << airportsInRange[0].frequency
|
||||
<< " - is in range " << airportsInRange[0].distanceNm << "nm ("
|
||||
<< (SG_NM_TO_METER*airportsInRange[0].distanceNm)/1000 <<"km)");
|
||||
return icao;
|
||||
}
|
||||
|
||||
// function: isInRange
|
||||
// action: return TRUE if airport/freq is in range, else return FALSE
|
||||
|
||||
bool isInRange(std::string icao, double acftLat, double acftLon, double acftAlt)
|
||||
{
|
||||
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 true;
|
||||
}
|
||||
|
||||
if(icao.empty())
|
||||
return false;
|
||||
|
||||
SGGeod acftPos = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
|
||||
double distNm = SGGeodesy::distanceNm(_airportPos, acftPos);
|
||||
double delta_elevation_ft = fabs(acftPos.getElevationFt() - _airportPos.getElevationFt());
|
||||
double rangeNm = 1.23 * sqrt(delta_elevation_ft);
|
||||
|
||||
if (rangeNm > _maxRange) rangeNm = _maxRange;
|
||||
if (rangeNm < _minRange) rangeNm = _minRange;
|
||||
if( distNm > rangeNm ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// function: quit
|
||||
// action: set _running flag to false
|
||||
|
||||
void quit(int state)
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Exiting FGCom" );
|
||||
_running = false;
|
||||
#ifdef _WIN32
|
||||
lib_shutdown();
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "You can close the terminal now");
|
||||
#endif
|
||||
}
|
||||
|
||||
// function: usage
|
||||
// action: display FGCom usage then quit
|
||||
|
||||
int usage()
|
||||
{
|
||||
std::cout << "FGCom " << FGCOM_VERSION << " usage:" << std::endl;
|
||||
std::cout << " --server=fgcom.flightgear.org - Server to connect" << std::endl;
|
||||
std::cout << " --host=127.0.0.1 - Host to listen i.e where FG is running" << std::endl;
|
||||
std::cout << " --port=16661 - Port to use" << std::endl;
|
||||
std::cout << " --callsign=guest - Callsign during session e.g F-ELYD" << std::endl;
|
||||
std::cout << " --frequency=xxx.xxx - Frequency e.g 120.500" << std::endl;
|
||||
std::cout << " --airport=YYYY - ICAO of airport e.g KSFO" << std::endl;
|
||||
std::cout << " --username=guest - Username for registration" << std::endl;
|
||||
std::cout << " --password=guest - Password for registration" << std::endl;
|
||||
std::cout << " --silence-threshold=-35 - Silence threshold in dB (-60 < range < 0 )" << std::endl;
|
||||
std::cout << " --debug - Enable debug output" << std::endl;
|
||||
std::cout << " --help - Show this message" << std::endl;
|
||||
std::cout << " --version - Show version" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << " None of these options are required, you can simply start FGCom without option at all: it works" << std::endl;
|
||||
std::cout << " For further information, please visit: http://wiki.flightgear.org/FGCom_3.0" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << " About silence-threshold:" << std::endl;
|
||||
std::cout << " This is the limit, in dB, when FGCom consider no voice in your microphone." << std::endl;
|
||||
std::cout << " --silence-threshold=-60 is similar to micro always ON" << std::endl;
|
||||
std::cout << " --silence-threshold=0 is similar to micro always OFF" << std::endl;
|
||||
std::cout << " Default value is -35.0 dB" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << " In order to make an echo-test, you have to start FGCom like:" << std::endl;
|
||||
std::cout << " fgcom --frequency=910" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << " In order to listen a frequency, you have to start FGCom like:" << std::endl;
|
||||
std::cout << " fgcom --frequency=xxx.xxx --airport=YYYY" << std::endl;
|
||||
std::cout << " where xxx.xxx is the frequency of the ICAO airport YYYY that you want to listen to" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << " In order to record an ATIS message, you have to start FGCom like:" << std::endl;
|
||||
std::cout << " fgcom --atis=xxx.xxx --airport=YYYY" << std::endl;
|
||||
std::cout << " where xxx.xxx is the ATIS frequency of the ICAO airport YYYY" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// function: version
|
||||
// action: display FGCom version then quit
|
||||
|
||||
int version()
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
|
||||
<< ", at " << __TIME__ );
|
||||
std::cout << "" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// function: computePhoneNumber
|
||||
// action: return phone number
|
||||
|
||||
std::string computePhoneNumber(double freq, std::string icao, bool atis)
|
||||
{
|
||||
if(icao.empty())
|
||||
return std::string();
|
||||
|
||||
char phoneNumber[256];
|
||||
char exten[32];
|
||||
char tmp[5];
|
||||
int prefix = atis ? 99 : 01;
|
||||
|
||||
sprintf( tmp, "%4s", icao.c_str() );
|
||||
|
||||
sprintf( exten,
|
||||
"%02d%02d%02d%02d%02d%06d",
|
||||
prefix,
|
||||
tmp[0],
|
||||
tmp[1],
|
||||
tmp[2],
|
||||
tmp[3],
|
||||
(int) (freq * 1000 + 0.5) );
|
||||
exten[16] = '\0';
|
||||
|
||||
snprintf( phoneNumber,
|
||||
sizeof(phoneNumber),
|
||||
"%s:%s@%s/%s",
|
||||
_username.c_str(),
|
||||
_password.c_str(),
|
||||
_server.c_str(),
|
||||
exten);
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
// function: lib_setVolume
|
||||
// action: set input/output volume
|
||||
|
||||
void lib_setVolume(double input, double output)
|
||||
{
|
||||
SG_CLAMP_RANGE<double>(input, 0.0, 1.0);
|
||||
SG_CLAMP_RANGE<double>(output, 0.0, 1.0);
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Set volume input=" << input << " , output=" << output);
|
||||
iaxc_input_level_set(input);
|
||||
iaxc_output_level_set(output);
|
||||
}
|
||||
|
||||
// function: lib_setSilenceThreshold
|
||||
// action: set silence threshold
|
||||
|
||||
void lib_setSilenceThreshold(double thd)
|
||||
{
|
||||
SG_CLAMP_RANGE<double>(thd, -60, 0);
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Set silence threshold=" << thd);
|
||||
iaxc_set_silence_threshold(thd);
|
||||
}
|
||||
|
||||
// function: lib_setCallerId
|
||||
// action: set caller id for the session
|
||||
|
||||
void lib_setCallerId(std::string callsign)
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Set caller ID=" << callsign);
|
||||
iaxc_set_callerid (callsign.c_str(), _app.c_str());
|
||||
}
|
||||
|
||||
// function: lib_init
|
||||
// action: init the library
|
||||
|
||||
bool lib_init()
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Initializing IAX library");
|
||||
#ifdef _MSC_VER
|
||||
iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
|
||||
#endif
|
||||
if (iaxc_initialize(4)) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Error: cannot initialize IAXClient !\nHINT: Have you checked the mic and speakers ?" );
|
||||
return false;
|
||||
}
|
||||
|
||||
iaxc_set_callerid( _callsign.c_str(), _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_event_callback(iaxc_callback);
|
||||
iaxc_start_processing_thread ();
|
||||
lib_setSilenceThreshold(_silenceThd);
|
||||
return true;
|
||||
}
|
||||
|
||||
// function: lib_shutdown
|
||||
// action: stop the library
|
||||
|
||||
bool lib_shutdown()
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Shutdown IAX library");
|
||||
lib_hangup();
|
||||
if(_registrationId != -1)
|
||||
iaxc_unregister(_registrationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// function: lib_call
|
||||
// action: register a user on remote server then return the registration ID
|
||||
|
||||
int lib_registration()
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Request registration");
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " username: " << _username);
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " password: xxxxxxxx");
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " server: " << _server);
|
||||
int regId = iaxc_register( _username.c_str(), _password.c_str(), _server.c_str());
|
||||
if(regId == -1) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot register '" << _username << "' at '" << _server );
|
||||
}
|
||||
return regId;
|
||||
}
|
||||
|
||||
// function: lib_call
|
||||
// action: kill current call then do a new call
|
||||
|
||||
bool lib_call(std::string icao, double freq)
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " icao: " << icao);
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " freq: " << freq);
|
||||
lib_hangup();
|
||||
iaxc_millisleep(300);
|
||||
std::string num = computePhoneNumber(freq, icao);
|
||||
if(num.empty())
|
||||
return false;
|
||||
_callId = iaxc_call(num.c_str());
|
||||
if(_callId == -1) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lib_directCall(std::string icao, double freq, std::string num)
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " icao: " << icao);
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, " freq: " << freq);
|
||||
lib_hangup();
|
||||
iaxc_millisleep(300);
|
||||
if(num.empty())
|
||||
return false;
|
||||
_callId = iaxc_call(num.c_str());
|
||||
if(_callId == -1) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// function: lib_hangup
|
||||
// action: kill current call
|
||||
|
||||
bool lib_hangup()
|
||||
{
|
||||
if(!_connected)
|
||||
return false;
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Request hangup");
|
||||
iaxc_dump_all_calls();
|
||||
_callId = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// function: iaxc_callback
|
||||
// action: parse IAX event then call event handler
|
||||
|
||||
int iaxc_callback(iaxc_event e)
|
||||
{
|
||||
switch (e.type) {
|
||||
case IAXC_EVENT_TEXT:
|
||||
if(e.ev.text.type == IAXC_TEXT_TYPE_STATUS ||
|
||||
e.ev.text.type == IAXC_TEXT_TYPE_IAX)
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Message: " << e.ev.text.message );
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// eof
|
||||
123
utils/fgcom/fgcom_external.hxx
Normal file
123
utils/fgcom/fgcom_external.hxx
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
|
||||
*
|
||||
* This program realizes the usage of the VoIP infractructure based
|
||||
* on flight data which is send from FlightGear with an external
|
||||
* protocol to this application.
|
||||
*
|
||||
* Clement de l'Hamaide - Jan 2014
|
||||
* Re-writting of FGCom standalone
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FGCOM_H__
|
||||
#define __FGCOM_H__
|
||||
|
||||
// avoid name clash with winerror.h
|
||||
#define FGC_SUCCESS(__x__) (__x__ == 0)
|
||||
#define FGC_FAILED(__x__) (__x__ < 0)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#ifdef WIN64
|
||||
typedef __int64 ssize_t;
|
||||
#else
|
||||
typedef int ssize_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef FGCOM_VERSION
|
||||
#ifdef FLIGHTGEAR_VERSION
|
||||
#define FGCOM_VERSION FLIGHTGEAR_VERSION
|
||||
#else
|
||||
#define FGCOM_VERSION "unknown"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAXBUFLEN 1024
|
||||
|
||||
enum Modes {
|
||||
ATC,
|
||||
PILOT,
|
||||
OBS,
|
||||
TEST
|
||||
};
|
||||
|
||||
enum ActiveComm {
|
||||
COM1,
|
||||
COM2
|
||||
};
|
||||
|
||||
struct Data
|
||||
{
|
||||
int ptt;
|
||||
float com1;
|
||||
float com2;
|
||||
double lon;
|
||||
double lat;
|
||||
double alt;
|
||||
float outputVol;
|
||||
float silenceThd;
|
||||
std::string callsign;
|
||||
};
|
||||
|
||||
struct Airport
|
||||
{
|
||||
double frequency;
|
||||
double latitude;
|
||||
double longitude;
|
||||
double distanceNm;
|
||||
std::string icao;
|
||||
std::string type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Internal functions
|
||||
int usage();
|
||||
int version();
|
||||
void quit(int state);
|
||||
bool isInRange(std::string icao, double acftLat, double acftLon, double acftAlt);
|
||||
std::string computePhoneNumber(double freq, std::string icao, bool atis = false);
|
||||
std::string getClosestAirportForFreq(double freq, double acftLat, double acftLon, double acftAlt);
|
||||
std::multimap<int, Airport> getAirportsData();
|
||||
|
||||
// Library functions
|
||||
bool lib_init();
|
||||
bool lib_hangup();
|
||||
bool lib_shutdown();
|
||||
bool lib_call(std::string icao, double freq);
|
||||
bool lib_directCall(std::string icao, double freq, std::string num);
|
||||
|
||||
int lib_registration();
|
||||
int iaxc_callback(iaxc_event e);
|
||||
|
||||
void lib_setSilenceThreshold(double thd);
|
||||
void lib_setCallerId(std::string callsign);
|
||||
void lib_setVolume(double input, double output);
|
||||
|
||||
#endif
|
||||
47174
utils/fgcom/positions.hxx
Executable file
47174
utils/fgcom/positions.hxx
Executable file
File diff suppressed because it is too large
Load Diff
50
utils/fgcom/utils/README
Normal file
50
utils/fgcom/utils/README
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
# README for fgcom/utils folder content
|
||||
#
|
||||
# 26 sept, 2013 - Clément de l'Hamaide
|
||||
|
||||
|
||||
============================================
|
||||
==== fgcom.conf =====
|
||||
============================================
|
||||
|
||||
This file is the dialplan as of 26/09/2013 generated by gen_phonebook.pl
|
||||
If you are looking for setup an Asterisk server for FGCom you should
|
||||
add this file in your /etc/asterisk folder
|
||||
|
||||
|
||||
============================================
|
||||
==== gen_phonebook.pl =====
|
||||
============================================
|
||||
|
||||
This file is used to parse apt.dat.gz and nav.dat.gz
|
||||
and create fgcom.conf and positions.txt as result.
|
||||
|
||||
To use this script you should put apt.dat.gz and nav.dat.gz
|
||||
belong gen_phonebook.pl then run the script like:
|
||||
|
||||
./gen_phonebook.pl
|
||||
|
||||
For details use:
|
||||
|
||||
./gen_phonebook.pl --help
|
||||
|
||||
============================================
|
||||
==== build_fgcom_server =====
|
||||
============================================
|
||||
|
||||
This file is used to build an FGCom server FOR DEBIAN BASED OPERATING SYSTEM ONLY
|
||||
You just need to run the script like:
|
||||
|
||||
./build_fgcom_server.sh
|
||||
|
||||
What does the script ?
|
||||
- install Asterisk and related/dependencies
|
||||
- download apt.dat.gz and nav.data.gz
|
||||
- download gen_phonebook.pl script
|
||||
- run gen_phonebook.pl
|
||||
- configure Asterisk
|
||||
- start Asterisk and show the CLI
|
||||
|
||||
|
||||
|
||||
122
utils/fgcom/utils/build_fgcom_server.sh
Normal file
122
utils/fgcom/utils/build_fgcom_server.sh
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
PHONEBOOK_SCRIPT="http://clemaez.fr/flightgear/gen_phonebook.pl.txt"
|
||||
APTNAV_DATA="http://dev.x-plane.com/update/data/AptNav201304XP1000.zip"
|
||||
DAHDI_SRC="http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz"
|
||||
LOGSEP="###########################################"
|
||||
GETMETAR_SCRIPT="#!/bin/bash
|
||||
#curl https://tgftp.nws.noaa.gov/data/observations/metar/stations/$1.TXT
|
||||
echo \"Hello World !\""
|
||||
|
||||
ROOT=$PWD
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Install Asterisk & Festival & dependances"
|
||||
echo ""
|
||||
sudo apt-get install asterisk asterisk-dahdi festival gzip unzip perl libfile-slurp-perl libdatetime-perl
|
||||
echo ""
|
||||
echo ""
|
||||
wget $DAHDI_SRC -O dahdi_src.tar.gz
|
||||
tar -zxvf dahdi_src.tar.gz
|
||||
mv dahdi*/ dahdi_src/
|
||||
cd dahdi_src
|
||||
make
|
||||
sudo make install
|
||||
sudo make config
|
||||
echo ""
|
||||
echo ""
|
||||
echo "$GETMETAR_SCRIPT" > getmetar
|
||||
chmod +x getmetar
|
||||
sudo mv getmetar /usr/bin/getmetar
|
||||
cd $ROOT
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Download APT & NAV data"
|
||||
echo ""
|
||||
wget $APTNAV_DATA -O aptnav_data.zip
|
||||
mkdir aptnav && cd aptnav
|
||||
unzip ../aptnav_data.zip
|
||||
mv earth_nav.dat nav.dat
|
||||
gzip apt.dat && mv apt.dat.gz ../apt.dat.gz
|
||||
gzip nav.dat && mv nav.dat.gz ../nav.dat.gz
|
||||
cd .. && rm -rf aptnav/
|
||||
rm aptnav_data.zip
|
||||
cd $ROOT
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Download PhoneBook script"
|
||||
echo ""
|
||||
wget $PHONEBOOK_SCRIPT -O gen_phonebook.pl
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Run PhoneBook script"
|
||||
echo ""
|
||||
chmod +x gen_phonebook.pl
|
||||
./gen_phonebook.pl
|
||||
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Configure Asterisk"
|
||||
echo ""
|
||||
|
||||
echo "[general]
|
||||
static=yes
|
||||
writeprotect=yes
|
||||
;
|
||||
[default]
|
||||
#include \"fgcom.conf\"
|
||||
include => fgcom" > extensions.conf
|
||||
|
||||
cd /etc/asterisk
|
||||
|
||||
if [ -f iax.conf.bak ]
|
||||
then
|
||||
sudo rm iax.conf
|
||||
sudo mv iax.conf.bak iax.conf
|
||||
fi
|
||||
sudo cp iax.conf iax.conf.bak
|
||||
sudo sed -i '/callerid="Guest IAX User"/a requirecalltoken=no' iax.conf
|
||||
sudo sed -i '/^;calltokenoptional=/c calltokenoptional=0.0.0.0/0.0.0.0' iax.conf
|
||||
sudo sed -i '/^;authdebug=no/c authdebug=no' iax.conf
|
||||
|
||||
if [ ! -f extensions.conf.bak ]
|
||||
then
|
||||
sudo cp extensions.conf extensions.conf.bak
|
||||
fi
|
||||
sudo rm extensions.conf
|
||||
sudo mv $ROOT/extensions.conf extensions.conf
|
||||
sudo mv $ROOT/fgcom.conf fgcom.conf
|
||||
|
||||
sudo chown asterisk:asterisk extensions.conf
|
||||
sudo chown asterisk:asterisk extensions.conf.bak
|
||||
sudo chown asterisk:asterisk iax.conf
|
||||
sudo chown asterisk:asterisk iax.conf.bak
|
||||
sudo chown asterisk:asterisk fgcom.conf
|
||||
sudo mkdir /var/fgcom-server
|
||||
sudo chown asterisk:asterisk /var/fgcom-server
|
||||
sudo mkdir /var/fgcom-server/atis
|
||||
sudo chown asterisk:asterisk /var/fgcom-server/atis
|
||||
cd $ROOT
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Restart Asterisk"
|
||||
echo ""
|
||||
sudo service asterisk restart
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Run Festival"
|
||||
echo ""
|
||||
nohup festival --server > /dev/null 2> /dev/null < /dev/null &
|
||||
|
||||
|
||||
echo $LOGSEP
|
||||
echo "Enter in Asterisk CLI"
|
||||
echo ""
|
||||
sudo asterisk -r -vvvvvvvvvv
|
||||
138836
utils/fgcom/utils/fgcom.conf
Normal file
138836
utils/fgcom/utils/fgcom.conf
Normal file
File diff suppressed because it is too large
Load Diff
608
utils/fgcom/utils/gen_phonebook.pl
Executable file
608
utils/fgcom/utils/gen_phonebook.pl
Executable file
@@ -0,0 +1,608 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# gen_phonebook.pl
|
||||
# Version 1.04 - 26 Sept 2013
|
||||
# Version 1.03 - 04 Sept 2013
|
||||
# Version 1.02 - 01 Sept 2013
|
||||
#
|
||||
# Modified by Clément de l'Hamaide - 20130926
|
||||
# * Disable phonebook.txt generation
|
||||
#
|
||||
# Modified by Geoff R. McLane - 20130904
|
||||
# * Added strict and warnings, fixing all variables to comply
|
||||
# * Added command line interface and 'help', but kept same defaults
|
||||
# * Removed numerous substr($txt,0,-1) which truncated some string
|
||||
# * Change 'chop' to the safer 'chomp'
|
||||
# * Added heliport(16) and seaport(17) under an option switch
|
||||
# * Added output of some stats of what was collected, and skipped
|
||||
# * Added $trim_to_20 option to align the phonebook if desired
|
||||
# * Replaced all tabs with ' ' to better align the code for readability
|
||||
# * Added a trim_all($txt) to removed some unwanted line endings
|
||||
# * Is compatible with FG 810 AND XP 1000 apt.dat files
|
||||
#
|
||||
# Modified by Clement de l'Hamaide - 20130901
|
||||
#
|
||||
use IO::Zlib;
|
||||
use IO::File;
|
||||
use File::Slurp;
|
||||
use Data::Dumper;
|
||||
use DateTime;
|
||||
use Encode;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $VERS = "1.04 - 26 Sept 2013";
|
||||
# default locations
|
||||
my $FG_AIRPORTS = "./apt.dat.gz";
|
||||
my $FG_NAVAIDS = "./nav.dat.gz";
|
||||
my $out_fil1 = "fgcom.conf";
|
||||
my $out_pos = "positions.txt";
|
||||
#my $out_phon = "phonebook.txt";
|
||||
|
||||
# options
|
||||
my $trim_to_20 = 0;
|
||||
my $add_heliports = 1;
|
||||
my $add_seaports = 1;
|
||||
|
||||
my $phonebook_post="ZZZZ 910.000 0190909090910000 Echo-Box
|
||||
ZZZZ 911.000 0190909090911000 Music-Box
|
||||
ZZZZ 700.000 0190909090700000 Radio-Box
|
||||
ZZZZ 123.450 0190909090123450 Air2Air
|
||||
ZZZZ 122.750 0190909090122750 Air2Air
|
||||
ZZZZ 121.500 0190909090121500 Air2Air
|
||||
ZZZZ 123.500 0190909090123500 Air2Air
|
||||
ZZZZ 121.000 0190909090121000 Emergency
|
||||
ZZZZ 723.340 0190909090723340 French Air Patrol
|
||||
";
|
||||
|
||||
my $extensions_pre="[globals]
|
||||
ATIS_RECORDINGS=/var/fgcom-server/atis
|
||||
RADIO_FILE=/var/fgcom-server/radio
|
||||
;Morse tone, 1020Hz for VOR and ILS but 1350Hz for DME, in Hz
|
||||
MORSETONE=1020
|
||||
;Dit length for morse code, in ms
|
||||
;MORSEDITLEN=300
|
||||
|
||||
[macro-com]
|
||||
exten => s,1,Answer()
|
||||
exten => s,n,NoCDR
|
||||
exten => s,n,MeetMe(\${MACRO_EXTEN},qd)
|
||||
exten => s,n,Hangup()
|
||||
|
||||
[macro-echo]
|
||||
exten => s,1,Answer()
|
||||
exten => s,n,NoCDR
|
||||
exten => s,n,Echo()
|
||||
exten => s,n,Hangup()
|
||||
|
||||
[macro-atis]
|
||||
exten => s,1,Answer()
|
||||
; Check if audio file exists
|
||||
exten => s,n,TrySystem(ls \${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2}*)
|
||||
exten => s,n,Goto(\${SYSTEMSTATUS})
|
||||
; If audio file exists, play it
|
||||
exten => s,n(SUCCESS),While(\$[1])
|
||||
exten => s,n,Playback(\${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2})
|
||||
exten => s,n,Wait(3)
|
||||
exten => s,n,EndWhile
|
||||
exten => s,n,Hangup()
|
||||
; If audio doesn't exist or TrySystem failed (why?), go to festival macro
|
||||
;exten => s,n,(APPERROR),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
|
||||
;exten => s,n,(FAILURE),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
|
||||
exten => s,n,(APPERROR),Hangup()
|
||||
exten => s,n,(FAILURE),Hangup()
|
||||
|
||||
[macro-record-atis]
|
||||
exten => s,1,Answer()
|
||||
exten => s,n,SendText(Record begin in 3s)
|
||||
exten => s,n,Wait(1)
|
||||
exten => s,n,SendText(Record begin in 2s)
|
||||
exten => s,n,Wait(1)
|
||||
exten => s,n,SendText(Record begin in 1s)
|
||||
exten => s,n,Wait(1)
|
||||
exten => s,n,Record(\${ATIS_RECORDINGS}/\${MACRO_EXTEN}:gsm,,90,k)
|
||||
exten => s,n,Wait(2)
|
||||
exten => s,n,Playback(\${ATIS_RECORDINGS}/\${MACRO_EXTEN})
|
||||
exten => s,n,Hangup()
|
||||
|
||||
[macro-festival]
|
||||
exten => s,1,Set(metar=\"\${SHELL(getmetar \$[1]):0:-1}\");
|
||||
exten => s,n,While(\$[1])
|
||||
exten => s,n,Festival(\${metar:1:-1})
|
||||
exten => s,n,Wait(3)
|
||||
exten => s,n,EndWhile
|
||||
|
||||
[macro-vor]
|
||||
exten => s,1,Answer()
|
||||
exten => s,n,While(\$[1])
|
||||
exten => s,n,Morsecode(\${ARG1})
|
||||
exten => s,n,Wait(2)
|
||||
exten => s,n,EndWhile
|
||||
exten => s,n,Hangup()
|
||||
|
||||
[macro-radio]
|
||||
exten => s,1,Answer()
|
||||
exten => s,n,Playback(\${RADIO_FILE})
|
||||
exten => s,n,Hangup()
|
||||
|
||||
[fgcom]
|
||||
; 910.000 Echo-Box
|
||||
exten => 0190909090910000,1,SendText(Echo Box - For testing FGCOM)
|
||||
exten => 0190909090910000,n,Macro(echo)
|
||||
; 911.000 Music-Box
|
||||
exten => 0190909090911000,1,Answer
|
||||
exten => 0190909090911000,n,SendText(Music On Hold Box - For testing FGCOM)
|
||||
exten => 0190909090911000,n,MusicOnHold(default)
|
||||
; Radio Station: 700.000 MHz
|
||||
exten => 0190909090700000,1,SendText(Radio Station - For testing FGCOM)
|
||||
exten => 0190909090700000,n,Macro(radio)
|
||||
|
||||
; 121.500 Air2Air
|
||||
exten => 0190909090121500,1,SendText(121.500 Auto-information frequency)
|
||||
exten => 0190909090121500,n,Macro(com)
|
||||
; 123.450 Air2Air
|
||||
exten => 0190909090123450,1,SendText(123.450 Auto-information frequency)
|
||||
exten => 0190909090123450,n,Macro(com)
|
||||
; 123.500 Air2Air
|
||||
exten => 0190909090123500,1,SendText(123.500 Auto-information frequency)
|
||||
exten => 0190909090123500,n,Macro(com)
|
||||
; 122.750 Air2Air
|
||||
exten => 0190909090122750,1,SendText(122.750 Auto-information frequency)
|
||||
exten => 0190909090122750,n,Macro(com)
|
||||
|
||||
; 121.000 emergency
|
||||
exten => 0190909090121000,1,SendText(121.000 Emergency frequency)
|
||||
exten => 0190909090121000,n,Macro(com)
|
||||
|
||||
; 723.340 Franch Air Patrol
|
||||
exten => 0190909090723340,1,SendText(723.340 French Air Patrol frequency)
|
||||
exten => 0190909090723340,n,Macro(com)
|
||||
|
||||
";
|
||||
|
||||
my $dt = DateTime->now;
|
||||
my $pgmname = $0;
|
||||
if ($pgmname =~ /(\\|\/)/) {
|
||||
my @tmpsp = split(/(\\|\/)/,$pgmname);
|
||||
$pgmname = $tmpsp[-1];
|
||||
}
|
||||
my $numberOfFrequenciesParsed = 0;
|
||||
my $numberOfFrequenciesComputed = 0;
|
||||
my $numberOfFrequenciesWritten = 0;
|
||||
my $numberOfFrequenciesSkipped = 0;
|
||||
my %APT = ();
|
||||
my %NAV = ();
|
||||
my %frq = ();
|
||||
my ($fh,$z,$icao,$lat,$lon,$latW,$lonW,$com,$code,$text);
|
||||
my ($vor,$icao_number,$f,$positions,$extensions,$phonebook);
|
||||
my ($i,$tmp,$airport,$nav,$freq,$ssf,$type);
|
||||
|
||||
my $land_apcnt = 0;
|
||||
my $heli_apcnt = 0;
|
||||
my $sea_apcnt = 0;
|
||||
my $no_latloncnt = 0;
|
||||
my $no_freqcnt = 0;
|
||||
my $airportcount = 0;
|
||||
my $navlinecount = 0;
|
||||
my $vordmeadded = 0;
|
||||
|
||||
sub prt($) { print shift; }
|
||||
|
||||
sub icao2number($) {
|
||||
my ($icao) = @_;
|
||||
my ($number,$n);
|
||||
$icao = " ".$icao while(length($icao) < 4);
|
||||
$number = '';
|
||||
for ($i = 0; $i< length($icao); $i++) {
|
||||
$n = ord(substr($icao,$i,1));
|
||||
$number .= sprintf("%02d",$n);
|
||||
}
|
||||
return($number);
|
||||
}
|
||||
|
||||
sub trim_tailing($) {
|
||||
my ($ln) = shift;
|
||||
$ln = substr($ln,0, length($ln) - 1) while ($ln =~ /\s$/g); # remove all TRAILING space
|
||||
return $ln;
|
||||
}
|
||||
|
||||
sub trim_leading($) {
|
||||
my ($ln) = shift;
|
||||
$ln = substr($ln,1) while ($ln =~ /^\s/); # remove all LEADING space
|
||||
return $ln;
|
||||
}
|
||||
|
||||
sub trim_ends($) {
|
||||
my ($ln) = shift;
|
||||
$ln = trim_tailing($ln); # remove all TRAINING space
|
||||
$ln = trim_leading($ln); # remove all LEADING space
|
||||
return $ln;
|
||||
}
|
||||
|
||||
sub trim_all {
|
||||
my ($ln) = shift;
|
||||
$ln =~ s/\n/ /gm; # replace CR (\n)
|
||||
$ln =~ s/\r/ /gm; # replace LF (\r)
|
||||
$ln =~ s/\t/ /g; # TAB(s) to a SPACE
|
||||
$ln = trim_ends($ln);
|
||||
$ln =~ s/\s{2}/ /g while ($ln =~ /\s{2}/); # all double space to SINGLE
|
||||
return $ln;
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Main program
|
||||
##############################################################################
|
||||
parse_args(@ARGV);
|
||||
# read airport data in hash
|
||||
$fh = new IO::Zlib;
|
||||
if ($fh->open($FG_AIRPORTS, "r")) {
|
||||
my $hadlines = 0;
|
||||
printf("Parsing ".$FG_AIRPORTS." ...\n");
|
||||
while ($z = <$fh>) {
|
||||
chomp($z);
|
||||
if ($z =~ /^\s*$/) {
|
||||
if ($icao) {
|
||||
if (scalar(keys(%frq)) > 0) {
|
||||
if (!$lat && !$lon) {
|
||||
if ($latW && $lonW) {
|
||||
$lat = $latW;
|
||||
$lon = $lonW;
|
||||
} else {
|
||||
print($icao." :: LAT/LON not found\n");
|
||||
}
|
||||
}
|
||||
if ($lat && $lon) {
|
||||
$APT{$icao}{'text'} = $text;
|
||||
$APT{$icao}{'lat'} = $lat;
|
||||
$APT{$icao}{'lon'} = $lon;
|
||||
foreach $f (keys(%frq)) {
|
||||
if (length($frq{$f}) <= 1) {
|
||||
$frq{$f} = "TWR";
|
||||
}
|
||||
$APT{$icao}{'com'}{$f} = $frq{$f};
|
||||
}
|
||||
$airportcount++;
|
||||
} else {
|
||||
$no_latloncnt++;
|
||||
}
|
||||
} else {
|
||||
$no_freqcnt++;
|
||||
}
|
||||
} elsif ($hadlines) {
|
||||
$numberOfFrequenciesSkipped++;
|
||||
}
|
||||
|
||||
undef($icao);
|
||||
undef($text);
|
||||
undef($lon);
|
||||
undef($lat);
|
||||
undef($com);
|
||||
%frq=();
|
||||
$hadlines = 0;
|
||||
next;
|
||||
}
|
||||
elsif ($z=~/^1\s+-?\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
|
||||
{
|
||||
# Airport Header
|
||||
$icao = $1;
|
||||
$text = trim_all($2);
|
||||
$land_apcnt++;
|
||||
}
|
||||
elsif ($z=~/^16\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
|
||||
{
|
||||
$heli_apcnt++;
|
||||
# Heliport Header
|
||||
if ($add_seaports) {
|
||||
$icao = $1;
|
||||
$text = trim_all($2);
|
||||
}
|
||||
}
|
||||
elsif ($z=~/^17\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
|
||||
{
|
||||
# Seaport Header
|
||||
$sea_apcnt++;
|
||||
if ($add_seaports) {
|
||||
$icao = $1;
|
||||
$text = trim_all($2);
|
||||
}
|
||||
}
|
||||
elsif ($z=~/^14\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
|
||||
{
|
||||
# TWR Position
|
||||
$lat=$1;
|
||||
$lon=$2;
|
||||
}
|
||||
elsif ($z=~/^19\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
|
||||
{
|
||||
# WS Position
|
||||
$latW=$1;
|
||||
$lonW=$2;
|
||||
}
|
||||
elsif ($z=~/^5[0-6]\s+(\d{5})\d*\s+(.+)\s*$/)
|
||||
{
|
||||
# COM data
|
||||
$freq = $1;
|
||||
$type = trim_all($2);
|
||||
###prt("$freq $type\n");
|
||||
$ssf = substr($freq, -1);
|
||||
if ( $ssf == 2 || $ssf == 7) {
|
||||
$com = sprintf("%3.2f5", $freq/100);
|
||||
} else {
|
||||
$com = sprintf("%3.3f", $freq/100);
|
||||
}
|
||||
$frq{$com} = $type;
|
||||
}
|
||||
$hadlines++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
die("Cannot open $FG_AIRPORTS :$!\n");
|
||||
}
|
||||
$fh->close;
|
||||
prt("Got $airportcount airports, $land_apcnt land(1), $heli_apcnt heliports(16), and $sea_apcnt seaports(17), skipped $no_latloncnt no lat/lon, $no_freqcnt no freqs\n");
|
||||
|
||||
# read nav data in hash
|
||||
$nav=new IO::Zlib;
|
||||
if($nav->open($FG_NAVAIDS, "r"))
|
||||
{
|
||||
|
||||
printf("Parsing ".$FG_NAVAIDS." ...\n");
|
||||
while ($z=<$nav>) {
|
||||
|
||||
chomp($z);
|
||||
last if ($z =~ /^99/);
|
||||
$navlinecount++;
|
||||
|
||||
if ($z =~ /^3\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)\s+\d+\s+(\d+)\s+\d+\s+-?\d+\.\d+\s+([A-Z]+)\s+(.*)\s*$/) {
|
||||
# VOR/DME Nav
|
||||
$lat=$1;
|
||||
$lon=$2;
|
||||
$freq=sprintf("%3.3f",$3/100);
|
||||
$code=$4;
|
||||
$text = trim_all($5);
|
||||
|
||||
$NAV{$code}{'lat'}=$lat;
|
||||
$NAV{$code}{'lon'}=$lon;
|
||||
$NAV{$code}{'frq'}=$freq;
|
||||
$NAV{$code}{'text'}=$text;
|
||||
$vordmeadded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
die("Cannot open $FG_NAVAIDS :$!\n");
|
||||
}
|
||||
|
||||
$nav->close;
|
||||
prt("Done $navlinecount nav.dat lines, adding $vordmeadded VOR/DME(3) records\n");
|
||||
|
||||
# get output files open
|
||||
|
||||
# open positions file
|
||||
$positions = new IO::File;
|
||||
$positions->open($out_pos, "w") || die("Cannot open $out_pos for writing: $!\n");
|
||||
|
||||
# open phonebook file
|
||||
#$phonebook = new IO::File;
|
||||
#$phonebook->open($out_phon, "w") || die("Cannot open $out_phon for writing: $!\n");
|
||||
|
||||
# open fgcom.conf
|
||||
$extensions = new IO::File;
|
||||
$extensions->open($out_fil1, "w") || die("Cannot open $out_fil1 for writing: $!\n");
|
||||
|
||||
|
||||
#print $phonebook "File generated by $pgmname - ".join ' ', $dt->ymd, $dt->hms." UTC\n";
|
||||
#print $phonebook keys(%APT)." airports and ".keys(%NAV)." navaids are present in this file (".$numberOfFrequenciesSkipped." freq skipped) \n\n";
|
||||
#print $phonebook "ICAO Decription FRQ Phone no. Name\n";
|
||||
#print $phonebook "-" x 79,"\n";
|
||||
|
||||
# read pre data for fgcom.conf;
|
||||
print $extensions $extensions_pre;
|
||||
|
||||
printf("Writing airports data ...\n");
|
||||
|
||||
# Print all known airports
|
||||
foreach $airport (sort(keys(%APT))) {
|
||||
foreach $f (keys(%{$APT{$airport}{'com'}})) {
|
||||
$ssf = substr($f, -2);
|
||||
$type = $APT{$airport}{'com'}{$f};
|
||||
if ($trim_to_20 && (length($type) > 20)) {
|
||||
$type = substr($type,0,20);
|
||||
}
|
||||
if ($ssf == 30 || $ssf == 80) {
|
||||
next;
|
||||
printf("Found a 8.33KHz freq !!! $airport\n");
|
||||
}
|
||||
|
||||
$icao_number = icao2number($airport);
|
||||
|
||||
# write positions APT
|
||||
print $positions Encode::encode( "utf8", $airport.",".$f.",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
|
||||
# write phonebook
|
||||
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$airport,$type,$f,"01".$icao_number.$f*1000,$APT{$airport}{'text'});
|
||||
#print $phonebook Encode::encode( "utf8", $tmp);
|
||||
|
||||
# write extensions.conf
|
||||
$tmp = "; $airport $type $f - ".$APT{$airport}{'text'}."\n";
|
||||
###prt($tmp);
|
||||
print $extensions Encode::encode( "utf8", $tmp);
|
||||
if ( $APT{$airport}{'com'}{$f} =~ /ATIS$/ ) {
|
||||
# ATIS playback extension
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(atis)\n");
|
||||
if ($ssf == 00) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif ($ssf == 25) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif ($ssf == 50) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif($ssf == 75) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
}
|
||||
|
||||
# ATIS record extension
|
||||
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f Record-".$APT{$airport}{'com'}{$f}.")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",n,Macro(record-atis)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(com)\n");
|
||||
if ($ssf == 00) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif ($ssf == 25) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif ($ssf == 50) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
} elsif ($ssf == 75) {
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
print $extensions ";\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf("Writing navaids data ...\n");
|
||||
# write VORs to files
|
||||
foreach $vor (sort(keys(%NAV))) {
|
||||
|
||||
$icao_number = icao2number($vor);
|
||||
|
||||
# write positions NAV
|
||||
print $positions Encode::encode( "utf8", $vor.",".$NAV{$vor}{'frq'}.",".$NAV{$vor}{'lat'}.",".$NAV{$vor}{'lon'}.",VOR,".$NAV{$vor}{'text'}."\n");
|
||||
|
||||
# write phonebook
|
||||
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$vor,"",$NAV{$vor}{'frq'},"01".$icao_number.$NAV{$vor}{'frq'}*1000,$NAV{$vor}{'text'});
|
||||
#print $phonebook Encode::encode( "utf8", $tmp);
|
||||
|
||||
# write extensions.conf
|
||||
print $extensions Encode::encode( "utf8", "; VOR $vor $NAV{$vor}{'frq'} - $NAV{$vor}{'text'}\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",1,SendText($vor ".$NAV{$vor}{'text'}." ".$NAV{$vor}{'frq'}.")\n");
|
||||
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",n,Macro(vor,$vor)\n");
|
||||
}
|
||||
|
||||
# close positions
|
||||
$positions->close;
|
||||
|
||||
# close extensions
|
||||
$extensions->close;
|
||||
|
||||
# close phonebook
|
||||
#print $phonebook $phonebook_post;
|
||||
#$phonebook->close;
|
||||
|
||||
prt("Done file outputs... $out_fil1 and $out_pos...\n");
|
||||
exit(0);
|
||||
|
||||
###############################################
|
||||
sub give_help() {
|
||||
prt("\n");
|
||||
prt("$pgmname, version $VERS\n");
|
||||
prt("Usage:\n");
|
||||
prt(" --help (-h, -?) = This help and exit(2)\n");
|
||||
prt("\n");
|
||||
prt("Input files:\n");
|
||||
prt(" --air <file> (-a) = Name of airports file. (def=$FG_AIRPORTS)\n");
|
||||
prt(" --nav <file> (-n) = Name of navaids file. (def=$FG_NAVAIDS)\n");
|
||||
prt("\n");
|
||||
prt("Output files:\n");
|
||||
prt(" --conf <file> (-c) = Name of conf file. (def=$out_fil1)\n");
|
||||
prt(" --pos <file> (-p) = Name of position file. (def=$out_pos)\n");
|
||||
#prt(" --book <file> (-b) = Name of phonebook file. (def=$out_phon)\n");
|
||||
prt("\n");
|
||||
prt("Purpose:\n");
|
||||
prt("Read the input files, and output the extensions (conf),\n");
|
||||
prt("used to configure the asterisk voip server, and output the positions file,\n");
|
||||
prt("used by standalone fgcom to establish the location of the caller.\n");
|
||||
prt("\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
sub need_arg {
|
||||
my ($arg,@av) = @_;
|
||||
die("ERROR: [$arg] must have a following argument!\n") if (!@av);
|
||||
}
|
||||
|
||||
sub parse_args {
|
||||
my @av = @_;
|
||||
my ($arg,$sarg);
|
||||
my $bad = 0;
|
||||
while (@av) {
|
||||
$arg = $av[0];
|
||||
### prt("ARG [$arg]\n");
|
||||
if ($arg =~ /^-/) {
|
||||
$sarg = substr($arg,1);
|
||||
$sarg = substr($sarg,1) while ($sarg =~ /^-/);
|
||||
if ($sarg =~ /^a/) {
|
||||
need_arg(@av);
|
||||
shift @av;
|
||||
$sarg = $av[0];
|
||||
$FG_AIRPORTS = $sarg;
|
||||
} elsif ($sarg =~ /^n/) {
|
||||
need_arg(@av);
|
||||
shift @av;
|
||||
$sarg = $av[0];
|
||||
$FG_NAVAIDS = $sarg;
|
||||
} elsif ($sarg =~ /^c/) {
|
||||
need_arg(@av);
|
||||
shift @av;
|
||||
$sarg = $av[0];
|
||||
$out_fil1 = $sarg;
|
||||
} elsif ($sarg =~ /^p/) {
|
||||
need_arg(@av);
|
||||
shift @av;
|
||||
$sarg = $av[0];
|
||||
$out_pos = $sarg;
|
||||
# } elsif ($sarg =~ /^b/) {
|
||||
# need_arg(@av);
|
||||
# shift @av;
|
||||
# $sarg = $av[0];
|
||||
# $out_phon = $sarg;
|
||||
} elsif (($sarg =~ /^h/)||($sarg eq '?')) {
|
||||
give_help();
|
||||
} else {
|
||||
die("ERROR: Unknown argument [$arg]\n");
|
||||
}
|
||||
} else {
|
||||
die("ERROR: Unknown argument [$arg]\n");
|
||||
}
|
||||
shift @av;
|
||||
}
|
||||
$arg = '';
|
||||
if (! -f $FG_AIRPORTS ) {
|
||||
$arg .= "Error: Unable to locate [$FG_AIRPORTS]!\n";
|
||||
$bad++;
|
||||
}
|
||||
if (! -f $FG_NAVAIDS ) {
|
||||
$arg .= "Error: Unable to locate [$FG_NAVAIDS]!\n";
|
||||
$bad++;
|
||||
}
|
||||
die("Invalid input file!\n$arg") if ($bad);
|
||||
|
||||
}
|
||||
|
||||
# eof
|
||||
47171
utils/fgcom/utils/positions.txt
Normal file
47171
utils/fgcom/utils/positions.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user