1
0
Fork 0
flightgear/utils/fgcom/fgcom.cxx

1276 lines
33 KiB
C++

/*
* 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.
*
* For more information read: http://squonk.abacab.org/dokuwiki/fgcom
*
* (c) H. Wirtz <dcoredump@gmail.com>
*
* 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.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <simgear/debug/logstream.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/timing/timestamp.hxx>
#include "fgcom.hxx"
#include "fgcom_init.hxx"
#include "utils.hxx"
#include "version.h"
/* Global variables */
int exitcode = 0;
int initialized = 0;
int connected = 0;
int reg_id;
const char* dialstring;
char airport[5];
int codec = DEFAULT_IAX_CODEC;
simgear::Socket sgSocket;
/* Variables declared as static */
static double selected_frequency = 0.0;
static char mode = 0;
static char rep_buf[MX_REPORT_BUF+2]; /* report output buffer - used on iax callback thread - note +2 to ensure null termination */
static char num_buf[1024]; /* number generation buffer - used on main thread */
static char states[256]; /* buffer to hold ascii states */
static char delim = '\t'; /* output field delimiter */
static int last_state = 0; /* previous state of the channel */
static const char *map[] = {
"unknown", "active", "outgoing", "ringing", "complete", "selected",
"busy", "transfer", NULL
};
static const char *radio_map[] = {"COM1", "COM2"};
char icao[5];
double special_frq[] = {
910.000,
911.000,
700.000,
123.450,
122.750,
121.500,
123.500,
-1.0 };
double *special_frequencies;
double previous_com_frequency = 0.0;
int previous_ptt = 0;
float previous_vol = 0.0;
float previous_thd = 0.0;
int com_select = 0;
int max_com_instruments = 2;
struct airport *airportlist;
struct fgdata data;
char *prog; //program name
/* configuration values */
static bool debug = false;
static int port;
static const char *voipserver;
static const char *fgserver;
static const char *airport_option;
static double frequency = -1.0;
static const char *audio_in;
static const char *audio_out;
static double level_in = 0.7;
static double level_out = 0.7;
static double silence_thd = -35.0;
static bool mic_boost;
static char codec_option;
static const char *callsign;
static const char *username;
static const char *password;
static bool list_audio;
static char *positions_file;
static char *frequency_file;
static const OptionEntry fgcomOptionArray[] = {
{"debug", 'd', false, OPTION_NONE, &debug, 0, "show debugging information",
0},
{"voipserver", 'S', true, OPTION_STRING, &voipserver, 0,
"voip server to connect to", &DEFAULT_VOIP_SERVER},
{"fgserver", 's', true, OPTION_STRING, &fgserver, 0, "fg to connect to ",
&DEFAULT_FG_SERVER},
{"port", 'p', true, OPTION_INT, &port, 0, "where we should listen to FG", &port}, // hm, good idea? don't think so...
{"airport", 'a', true, OPTION_STRING, &airport_option, 0,
"airport-id (ICAO) for ATC-mode", 0},
{"frequency", 'f', true, OPTION_DOUBLE, &frequency, 0,
"frequency for ATC-mode", 0},
{"callsign", 'C', true, OPTION_STRING, &callsign, 0,
"callsign to use", &DEFAULT_USER},
{"user", 'U', true, OPTION_STRING, &username, 0,
"username for VoIP account", &DEFAULT_USER},
{"password", 'P', true, OPTION_STRING, &password, 0,
"password for VoIP account", &DEFAULT_PASSWORD},
{"mic", 'i', true, OPTION_DOUBLE, &level_in, 0,
"mic input level (0.0 - 1.0)", 0},
{"speaker", 'o', true, OPTION_DOUBLE, &level_out, 0,
"speaker output level (0.0 - 1.0)", 0},
{"mic-boost", 'b', false, OPTION_BOOL, &mic_boost, 0, "enable mic boost",
0},
{"silence-threshold", 't', false, OPTION_DOUBLE, &silence_thd, 0, "set silence threshold (-60.0 - 0.0)",
0},
{"list-audio", 'l', false, OPTION_BOOL, &list_audio, 0,
"list audio devices", 0},
{"set-audio-in", 'r', true, OPTION_STRING, &audio_in, 0,
"use <devicename> as audio input", 0},
{"set-audio-out", 'k', true, OPTION_STRING, &audio_out, 0,
"use <devicename> as audio output", 0},
{"codec", 'c', true, OPTION_CHAR, &codec_option, 0,
"use codec <codec> as transfer codec", &codec_option},
{"positions", 'T', true, OPTION_STRING, &positions_file, 0,
"location positions file", &DEFAULT_POSITIONS_FILE},
{"special", 'Q', true, OPTION_STRING, &frequency_file, 0,
"location spl. frequency file (opt)", &SPECIAL_FREQUENCIES_FILE},
{NULL}
};
void
process_packet (char *buf)
{
/* cut off ending \n */
buf[strlen (buf) - 1] = '\0';
/* parse the data into a struct */
parse_fgdata (&data, buf);
/* get the selected frequency */
if (com_select == 0 && data.COM1_SRV == 1)
selected_frequency = data.COM1_FRQ;
else if (com_select == 1 && data.COM2_SRV == 1)
selected_frequency = data.COM2_FRQ;
/* Check for com frequency changes */
if (previous_com_frequency != selected_frequency)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Selected frequency: " << selected_frequency );
/* remark the new frequency */
previous_com_frequency = selected_frequency;
if (connected == 1)
{
/* hangup call, if connected */
iaxc_dump_call ();
iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
connected = 0;
}
strcpy (icao,
icaobypos (airportlist, selected_frequency, data.LAT,
data.LON, DEFAULT_RANGE));
icao2number (icao, selected_frequency, num_buf);
SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
do_iaxc_call (username, password, voipserver, num_buf);
connected = 1;
}
/* Check for pressed PTT key */
if (previous_ptt != data.PTT)
{
if (data.PTT == 2)
{
/* select the next com equipment */
com_select = (com_select + 1) % 2;
SG_LOG( SG_GENERAL, SG_ALERT, "Radio selected is " << radio_map[com_select] );
}
else if (connected == 1)
{
ptt (data.PTT);
}
previous_ptt = data.PTT;
}
/* Check for output volume change */
if (previous_vol != data.OUTPUT_VOL)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Set speaker volume to " << data.OUTPUT_VOL );
iaxc_output_level_set( data.OUTPUT_VOL );
previous_vol = data.OUTPUT_VOL;
}
/* Check for silence threshold change */
if (previous_thd != data.SILENCE_THD)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Set silent threshold to " << data.SILENCE_THD );
iaxc_set_silence_threshold( data.SILENCE_THD );
previous_thd = data.SILENCE_THD;
}
/* Check for callsign change */
if (strcmp(callsign, data.CALLSIGN) != 0)
{
iaxc_dump_call ();
callsign = data.CALLSIGN;
SG_LOG( SG_GENERAL, SG_ALERT, "FGCom will restart now with callsign " << callsign );
std::string app = "FGCOM-";
app += FGCOM_VERSION;
iaxc_set_callerid ( callsign, app.c_str() );
}
}
static char *base_name( char *name )
{
char *bn = name;
size_t len = strlen(name);
size_t i;
int c;
for ( i = 0; i < len; i++ ) {
c = name[i];
if (( c == '/' ) || ( c == '\\'))
bn = &name[i+1];
}
return bn;
}
// if default FAILS, for OSX and WIN try EXE path
int fix_input_files()
{
int iret = 0;
char *def_freq = (char *) SPECIAL_FREQUENCIES_FILE;
char *def_pos = (char *) DEFAULT_POSITIONS_FILE;
char exepath[MX_PATH_SIZE+2];
exepath[0] = 0;
if (strcmp( frequency_file,def_freq) == 0) {
/* ok is default value - do some fixes */
if (is_file_or_directory( frequency_file ) != 1) {
exepath[0] = 0;
iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
strcat(exepath,def_freq);
frequency_file = strdup(exepath);
}
}
if (strcmp( positions_file,def_pos) == 0) {
// if default FAILS, for OSX and WIN try EXE path
if (is_file_or_directory( positions_file ) != 1) {
exepath[0] = 0;
iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
strcat(exepath,def_pos);
positions_file = strdup(exepath);
}
}
return iret;
}
int
main (int argc, char *argv[])
{
int numbytes;
static char pkt_buf[MAXBUFLEN+2];
simgear::requestConsole();
sglog().setLogLevels( SG_ALL, SG_ALERT );
prog = strdup( base_name(argv[0]) );
/* program header */
SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
SG_LOG( SG_GENERAL, SG_ALERT, "" );
/* init values */
voipserver = DEFAULT_VOIP_SERVER;
fgserver = DEFAULT_FG_SERVER;
port = DEFAULT_FG_PORT;
username = DEFAULT_USER;
password = DEFAULT_PASSWORD;
codec_option = DEFAULT_CODEC;
mode = 0; // 0 = ATC mode, 1 = FG mode
positions_file = (char *) DEFAULT_POSITIONS_FILE;
frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
#ifndef _WIN32
/* catch signals */
signal (SIGINT, quit);
signal (SIGQUIT, quit);
signal (SIGTERM, quit);
#endif
/* setup iax */
#ifdef _MSC_VER
/* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom
can be an offset to a jump table, making a compare of the current address to the address of
the actual imported function fail. So here ensure they are the same. */
iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
#endif // _MSC_VER
if (iaxc_initialize (DEFAULT_MAX_CALLS))
fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
initialized = 1;
// option parser
fgcomInitOptions (fgcomOptionArray, argc, argv);
if( debug )
sglog().setLogLevels( SG_ALL, SG_DEBUG );
// codec
if (codec_option)
{
switch (codec_option)
{
case 'u':
codec = IAXC_FORMAT_ULAW;
break;
case 'a':
codec = IAXC_FORMAT_ALAW;
break;
case 'g':
codec = IAXC_FORMAT_GSM;
break;
case '7':
codec = IAXC_FORMAT_G726;
break;
case 's':
codec = IAXC_FORMAT_SPEEX;
break;
}
}
// airport
if (airport_option)
{
strtoupper (airport_option, airport, sizeof (airport));
}
// input level
if (level_in > 1.0)
{
level_in = 1.0;
}
if (level_in < 0.0)
{
level_in = 0.0;
}
// output level
if (level_out > 1.0)
{
level_out = 1.0;
}
if (level_out < 0.0)
{
level_out = 0.0;
}
// microphone boost
if (mic_boost)
{
iaxc_mic_boost_set (1);
}
if (list_audio)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
iaxc_shutdown ();
exit (1);
}
if (audio_in)
{
set_device (audio_in, 0);
}
if (audio_out)
{
set_device (audio_out, 1);
}
/* checking consistency of arguments */
if (frequency > 0.0 && frequency < 1000.0)
{
if (strlen (airport) == 0 || strlen (airport) > 4)
{
strcpy (airport, "ZZZZ");
}
/* airport and frequency are given => ATC mode */
mode = 0;
}
else
{
/* no airport => FG mode */
mode = 1;
}
/* Read special frequencies file (if exists).
* If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
* are used and are hard coded.
*/
if (fix_input_files()) { /* adjust default input per OS */
fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
}
if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
special_frequencies = special_frq;
} else {
SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
}
/* read airport frequencies and positions */
airportlist = read_airports (positions_file); /* never returns if fail! */
/* preconfigure iax */
SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
std::string app = "FGCOM-";
app += FGCOM_VERSION;
if( !callsign )
callsign = DEFAULT_USER;
iaxc_set_callerid ( callsign, 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(silence_thd);
iaxc_set_event_callback (iaxc_callback);
iaxc_start_processing_thread ();
if (username && password && voipserver)
{
reg_id =
iaxc_register (const_cast < char *>(username),
const_cast < char *>(password),
const_cast < char *>(voipserver));
SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
exitcode = 130;
quit (0);
}
iaxc_millisleep (DEFAULT_MILLISLEEP);
/* main loop */
if (mode == 1)
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
/* only in FG mode */
simgear::Socket::initSockets();
sgSocket.open (false);
sgSocket.bind (fgserver, port);
/* mute mic, speaker on */
iaxc_input_level_set (0);
iaxc_output_level_set (level_out);
SGTimeStamp sg_clock;
sg_clock.stamp();
double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
/* get data from flightgear */
while (1)
{
sg_clock.stamp();
double sg_wait = sg_next_update - sg_clock.toSecs();
if (sg_wait > 0.001)
{
simgear::Socket *readSockets[2] = { &sgSocket, 0 };
if (sgSocket.select (readSockets, readSockets + 1,
(int) (sg_wait * 1000)) == 1)
{
simgear::IPAddress their_addr;
numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
if (numbytes == -1)
{
perror ("recvfrom");
exit (1);
}
pkt_buf[numbytes] = '\0';
SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
process_packet (pkt_buf);
}
}
else
{
alarm_handler (0);
sg_clock.stamp();
sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
}
}
}
else
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
/* mic on, speaker on */
iaxc_input_level_set (1.0);
iaxc_output_level_set (1.0);
icao2atisnumber (airport, frequency, num_buf);
SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
do_iaxc_call (username, password, voipserver, num_buf);
while (1)
{
/* sleep endless */
SGTimeStamp::sleepForMSec(3600000);
}
}
/* should never be reached */
exitcode = 999;
quit (0);
}
void
quit (int signal)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
if (initialized)
iaxc_shutdown ();
if (reg_id)
iaxc_unregister (reg_id);
exit (exitcode);
}
void
alarm_handler (int signal)
{
/* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
if (check_special_frq (selected_frequency))
{
strcpy (icao, "ZZZZ");
}
else
{
strcpy (icao,
icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
DEFAULT_RANGE));
}
/* Check if we are out of range */
if (strlen (icao) == 0 && connected == 1)
{
/* Yes, we are out of range so hangup */
iaxc_dump_call ();
iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
connected = 0;
}
/* Check if we are now in range */
else if (strlen (icao) != 0 && connected == 0)
{
icao2number (icao, selected_frequency, num_buf);
SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
do_iaxc_call (username, password, voipserver, num_buf);
connected = 1;
}
}
void
strtoupper (const char *str, char *buf, size_t len)
{
unsigned int i;
for (i = 0; str[i] && i < len - 1; i++)
{
buf[i] = toupper (str[i]);
}
buf[i++] = '\0';
}
void
fatal_error (const char *err)
{
SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
if (initialized)
iaxc_shutdown ();
exit (1);
}
int
iaxc_callback (iaxc_event e)
{
switch (e.type)
{
case IAXC_EVENT_LEVELS:
event_level (e.ev.levels.input, e.ev.levels.output);
break;
case IAXC_EVENT_TEXT:
event_text (e.ev.text.type, e.ev.text.message);
break;
case IAXC_EVENT_STATE:
event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
e.ev.call.local, e.ev.call.local_context);
break;
case IAXC_EVENT_NETSTAT:
event_netstats (e.ev.netstats);
case IAXC_EVENT_REGISTRATION:
event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
break;
default:
event_unknown (e.type);
break;
}
return 1;
}
void
event_state (int state, char *remote, char *remote_name,
char *local, char *local_context)
{
last_state = state;
/* This is needed for auto-reconnect */
if (state == 0)
{
connected = 0;
/* FIXME: we should wake up the main thread somehow */
/* in fg mode the next incoming packet will do that anyway */
}
snprintf (rep_buf, MX_REPORT_BUF,
"S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
delim, map_state (state), delim, remote, delim, remote_name,
delim, local, delim, local_context);
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
void
event_text (int type, char *message)
{
snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
SG_LOG( SG_GENERAL, SG_ALERT, message );
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
void
event_register (int id, int reply, int count)
{
const char *reason;
switch (reply)
{
case IAXC_REGISTRATION_REPLY_ACK:
reason = "accepted";
break;
case IAXC_REGISTRATION_REPLY_REJ:
reason = "denied";
if (strcmp (username, "guest") != 0)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
}
break;
case IAXC_REGISTRATION_REPLY_TIMEOUT:
reason = "timeout";
break;
default:
reason = "unknown";
}
snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
reason, delim, count);
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
void
event_netstats (struct iaxc_ev_netstats stat)
{
struct iaxc_netstat local = stat.local;
struct iaxc_netstat remote = stat.remote;
snprintf (rep_buf, MX_REPORT_BUF,
"N%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d",
delim, stat.callNo, delim, stat.rtt,
delim, local.jitter, delim, local.losspct, delim,
local.losscnt, delim, local.packets, delim, local.delay,
delim, local.dropped, delim, local.ooo, delim,
remote.jitter, delim, remote.losspct, delim, remote.losscnt,
delim, remote.packets, delim, remote.delay, delim,
remote.dropped, delim, remote.ooo);
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
void
event_level (double in, double out)
{
snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
const char *
map_state (int state)
{
int i, j;
int next = 0;
*states = '\0';
if (state == 0)
{
return "free";
}
for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
{
if (state & j)
{
if (next)
strcat (states, ",");
strcat (states, map[i]);
next = 1;
}
}
return states;
}
void
event_unknown (int type)
{
snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
report (rep_buf);
}
void
report (char *text)
{
if (debug)
{
SG_LOG( SG_GENERAL, SG_ALERT, text );
fflush (stdout);
}
}
void
ptt (int mode)
{
if (mode == 1)
{
/* mic is muted so unmute and mute speaker */
iaxc_input_level_set (level_in);
iaxc_output_level_set (0.0);
SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
}
else
{
/* mic is unmuted so mute and unmute speaker */
iaxc_input_level_set (0.0);
iaxc_output_level_set (level_out);
SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
}
}
int
split (char *string, char *fields[], int nfields, const char *sep)
{
register char *p = string;
register char c; /* latest character */
register char sepc = sep[0];
register char sepc2;
register int fn;
register char **fp = fields;
register const char *sepp;
register int trimtrail;
/* white space */
if (sepc == '\0')
{
while ((c = *p++) == ' ' || c == '\t')
continue;
p--;
trimtrail = 1;
sep = " \t"; /* note, code below knows this is 2 long */
sepc = ' ';
}
else
trimtrail = 0;
sepc2 = sep[1]; /* now we can safely pick this up */
/* catch empties */
if (*p == '\0')
return (0);
/* single separator */
if (sepc2 == '\0')
{
fn = nfields;
for (;;)
{
*fp++ = p;
fn--;
if (fn == 0)
break;
while ((c = *p++) != sepc)
if (c == '\0')
return (nfields - fn);
*(p - 1) = '\0';
}
/* we have overflowed the fields vector -- just count them */
fn = nfields;
for (;;)
{
while ((c = *p++) != sepc)
if (c == '\0')
return (fn);
fn++;
}
/* not reached */
}
/* two separators */
if (sep[2] == '\0')
{
fn = nfields;
for (;;)
{
*fp++ = p;
fn--;
while ((c = *p++) != sepc && c != sepc2)
if (c == '\0')
{
if (trimtrail && **(fp - 1) == '\0')
fn++;
return (nfields - fn);
}
if (fn == 0)
break;
*(p - 1) = '\0';
while ((c = *p++) == sepc || c == sepc2)
continue;
p--;
}
/* we have overflowed the fields vector -- just count them */
fn = nfields;
while (c != '\0')
{
while ((c = *p++) == sepc || c == sepc2)
continue;
p--;
fn++;
while ((c = *p++) != '\0' && c != sepc && c != sepc2)
continue;
}
/* might have to trim trailing white space */
if (trimtrail)
{
p--;
while ((c = *--p) == sepc || c == sepc2)
continue;
p++;
if (*p != '\0')
{
if (fn == nfields + 1)
*p = '\0';
fn--;
}
}
return (fn);
}
/* n separators */
fn = 0;
for (;;)
{
if (fn < nfields)
*fp++ = p;
fn++;
for (;;)
{
c = *p++;
if (c == '\0')
return (fn);
sepp = sep;
while ((sepc = *sepp++) != '\0' && sepc != c)
continue;
if (sepc != '\0') /* it was a separator */
break;
}
if (fn < nfields)
*(p - 1) = '\0';
for (;;)
{
c = *p++;
sepp = sep;
while ((sepc = *sepp++) != '\0' && sepc != c)
continue;
if (sepc == '\0') /* it wasn't a separator */
break;
}
p--;
}
/* not reached */
}
/**
*
* \fn double *read_special_frequencies(const char *file)
*
* \brief Reads the file "special_frequencies.txt" if it exists.
* If no file exists, then no special frequencies are useable.
*
* \param file pointer on the filename.
*
* \return Returns the pointer on an array containing doubles if file
* has been successfully opened and read, otherwise returns NULL.
*
*/
double *read_special_frequencies(const char *file)
{
double *l_pfrq = NULL;
double l_value;
int l_count;
int l_allocated = 0;
int l_new_size;
if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
{
l_allocated += ALLOC_CHUNK_SIZE;
if(FGC_SUCCESS(parser_init(file)))
{
l_count = 0;
while(FGC_SUCCESS(parser_get_next_value(&l_value)))
{
if(l_count >= l_allocated)
{
l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
l_allocated += ALLOC_CHUNK_SIZE;
}
l_pfrq[l_count] = l_value;
l_count++;
}
/* Last value of the array must be -1.0 which is the terminator. */
if(l_count == l_allocated)
l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
l_pfrq[l_count] = -1.0;
} else {
// failed to open file
parser_exit();
free(l_pfrq);
return 0;
}
}
parser_exit();
return(l_pfrq);
}
struct airport *
read_airports (const char *file)
{
FILE *fp;
int ret;
struct airport airport_tmp;
struct airport *first = NULL;
struct airport *my_airport = NULL;
struct airport *previous_airport = NULL;
size_t counter = 0;
SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
if ((fp = fopen (file, "rt")) == NULL)
{
SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
perror ("fopen");
exitcode = 120;
quit (0);
}
airport_tmp.next = NULL;
while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
airport_tmp.icao, &airport_tmp.frequency,
&airport_tmp.lat, &airport_tmp.lon,
airport_tmp.type, airport_tmp.text)) == 6)
{
counter++;
if ((my_airport =
(struct airport *) malloc (sizeof (struct airport))) == NULL)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
exitcode = 900;
quit (0);
}
if (first == NULL)
first = my_airport;
memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
if (previous_airport != NULL)
{
previous_airport->next = my_airport;
}
previous_airport = my_airport;
}
fclose (fp);
if (ret != EOF)
{
SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
exitcode = 900;
quit (0);
}
SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
return (first);
}
char *
report_devices (int in)
{
struct iaxc_audio_device *devs; //audio devices
int ndevs; //audio dedvice count
int input, output, ring; //audio device id's
int current, i;
int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
current = in ? input : output;
snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
for (i = 0; i < ndevs; i++)
{
if (devs[i].capabilities & flag && i != current)
{
snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
devs[i].name);
}
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
if (strlen(rep_buf) >= MX_REPORT_BUF)
break;
}
rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
return rep_buf;
}
int
set_device (const char *name, int out)
{
struct iaxc_audio_device *devs; /* audio devices */
int ndevs; /* audio dedvice count */
int input, output, ring; /* audio device id's */
int i;
iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
for (i = 0; i < ndevs; i++)
{
if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
strcmp (name, devs[i].name) == 0)
{
if (out)
{
output = devs[i].devID;
}
else
{
input = devs[i].devID;
}
fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
devs[i].devID);
iaxc_audio_devices_set (input, output, ring);
return 1;
}
}
return 0;
}
void
parse_fgdata (struct fgdata *data, char *buf)
{
char *data_pair = NULL;
char *fields[2];
fields[0] = NULL;
fields[1] = NULL;
SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
/* Parse data from FG */
data_pair = strtok (buf, ",");
while (data_pair != NULL)
{
split (data_pair, fields, 2, "=");
if (strcmp (fields[0], "COM1_FRQ") == 0)
{
data->COM1_FRQ = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
}
else if (strcmp (fields[0], "COM2_FRQ") == 0)
{
data->COM2_FRQ = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
}
else if (strcmp (fields[0], "NAV1_FRQ") == 0)
{
data->NAV1_FRQ = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
}
else if (strcmp (fields[0], "NAV2_FRQ") == 0)
{
data->NAV2_FRQ = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
}
else if (strcmp (fields[0], "PTT") == 0)
{
data->PTT = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
}
else if (strcmp (fields[0], "TRANSPONDER") == 0)
{
data->TRANSPONDER = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
}
else if (strcmp (fields[0], "IAS") == 0)
{
data->IAS = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
}
else if (strcmp (fields[0], "GS") == 0)
{
data->GS = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
}
else if (strcmp (fields[0], "LON") == 0)
{
data->LON = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
}
else if (strcmp (fields[0], "LAT") == 0)
{
data->LAT = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
}
else if (strcmp (fields[0], "ALT") == 0)
{
data->ALT = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
}
else if (strcmp (fields[0], "HEAD") == 0)
{
data->HEAD = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
}
else if (strcmp (fields[0], "COM1_SRV") == 0)
{
data->COM1_SRV = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
}
else if (strcmp (fields[0], "COM2_SRV") == 0)
{
data->COM2_SRV = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
}
else if (strcmp (fields[0], "NAV1_SRV") == 0)
{
data->NAV1_SRV = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
}
else if (strcmp (fields[0], "NAV2_SRV") == 0)
{
data->NAV2_SRV = atoi (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
}
else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
{
data->OUTPUT_VOL = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
}
else if (strcmp (fields[0], "SILENCE_THD") == 0)
{
data->SILENCE_THD = atof (fields[1]);
SG_LOG( SG_GENERAL, SG_DEBUG, "SILENCE_THD=" << data->SILENCE_THD );
}
else if (strcmp (fields[0], "CALLSIGN") == 0)
{
data->CALLSIGN = fields[1];
SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
}
else
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
}
data_pair = strtok (NULL, ",");
}
SG_LOG( SG_GENERAL, SG_DEBUG, "" );
}
/**
*
* \fn int check_special_frq (double frq)
*
* \brief Check to see if specified frequency is a special frequency.
*
* \param frq frequency to check against special frequencies
*
* \return Returns 1 if successful, otherwise returns 0.
*
*/
int check_special_frq (double frq)
{
int i = 0;
frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
while (special_frequencies[i] >= 0.0)
{
if (frq == special_frequencies[i])
{
SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
return (1);
}
i++;
}
return (0);
}
void
do_iaxc_call (const char *username, const char *password,
const char *voipserver, char *number)
{
char dest[256];
if( strcmp(number, "9990909090910000") == 0)
number = (char *)"0190909090910000";
snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
voipserver, number);
iaxc_call (dest);
iaxc_millisleep (DEFAULT_MILLISLEEP);
}
/* eof - fgcom.cpp */