2 * fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
4 * This program realizes the usage of the VoIP infractructure based
5 * on flight data which is send from FlightGear with an external
6 * protocol to this application.
8 * For more information read: http://squonk.abacab.org/dokuwiki/fgcom
10 * (c) H. Wirtz <dcoredump@gmail.com>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/io/raw_socket.hxx>
35 #include <simgear/timing/timestamp.hxx>
38 #include "fgcom_init.hxx"
42 /* Global variables */
47 const char* dialstring;
49 int codec = DEFAULT_IAX_CODEC;
50 simgear::Socket sgSocket;
52 /* Variables declared as static */
53 static double selected_frequency = 0.0;
55 static char rep_buf[MX_REPORT_BUF+2]; /* report output buffer - used on iax callback thread - note +2 to ensure null termination */
56 static char num_buf[1024]; /* number generation buffer - used on main thread */
57 static char states[256]; /* buffer to hold ascii states */
58 static char delim = '\t'; /* output field delimiter */
59 static int last_state = 0; /* previous state of the channel */
61 static const char *map[] = {
62 "unknown", "active", "outgoing", "ringing", "complete", "selected",
63 "busy", "transfer", NULL
66 static const char *radio_map[] = {"COM1", "COM2"};
69 double special_frq[] = {
78 double *special_frequencies;
80 double previous_com_frequency = 0.0;
82 float previous_vol = 0.0;
83 float previous_thd = 0.0;
85 int max_com_instruments = 2;
86 struct airport *airportlist;
88 char *prog; //program name
90 /* configuration values */
91 static bool debug = false;
93 static const char *voipserver;
94 static const char *fgserver;
95 static const char *airport_option;
96 static double frequency = -1.0;
97 static const char *audio_in;
98 static const char *audio_out;
99 static double level_in = 0.7;
100 static double level_out = 0.7;
101 static double silence_thd = -20.0;
102 static bool mic_boost;
103 static char codec_option;
104 static const char *callsign;
105 static const char *username;
106 static const char *password;
107 static bool list_audio;
108 static char *positions_file;
109 static char *frequency_file;
111 static const OptionEntry fgcomOptionArray[] = {
112 {"debug", 'd', false, OPTION_NONE, &debug, 0, "show debugging information",
114 {"voipserver", 'S', true, OPTION_STRING, &voipserver, 0,
115 "voip server to connect to", &DEFAULT_VOIP_SERVER},
116 {"fgserver", 's', true, OPTION_STRING, &fgserver, 0, "fg to connect to ",
118 {"port", 'p', true, OPTION_INT, &port, 0, "where we should listen to FG", &port}, // hm, good idea? don't think so...
119 {"airport", 'a', true, OPTION_STRING, &airport_option, 0,
120 "airport-id (ICAO) for ATC-mode", 0},
121 {"frequency", 'f', true, OPTION_DOUBLE, &frequency, 0,
122 "frequency for ATC-mode", 0},
123 {"callsign", 'C', true, OPTION_STRING, &callsign, 0,
124 "callsign to use", &DEFAULT_USER},
125 {"user", 'U', true, OPTION_STRING, &username, 0,
126 "username for VoIP account", &DEFAULT_USER},
127 {"password", 'P', true, OPTION_STRING, &password, 0,
128 "password for VoIP account", &DEFAULT_PASSWORD},
129 {"mic", 'i', true, OPTION_DOUBLE, &level_in, 0,
130 "mic input level (0.0 - 1.0)", 0},
131 {"speaker", 'o', true, OPTION_DOUBLE, &level_out, 0,
132 "speaker output level (0.0 - 1.0)", 0},
133 {"mic-boost", 'b', false, OPTION_BOOL, &mic_boost, 0, "enable mic boost",
135 {"silence-threshold", 't', false, OPTION_DOUBLE, &silence_thd, 0, "set silence threshold (-60.0 - 0.0)",
137 {"list-audio", 'l', false, OPTION_BOOL, &list_audio, 0,
138 "list audio devices", 0},
139 {"set-audio-in", 'r', true, OPTION_STRING, &audio_in, 0,
140 "use <devicename> as audio input", 0},
141 {"set-audio-out", 'k', true, OPTION_STRING, &audio_out, 0,
142 "use <devicename> as audio output", 0},
143 {"codec", 'c', true, OPTION_CHAR, &codec_option, 0,
144 "use codec <codec> as transfer codec", &codec_option},
145 {"positions", 'T', true, OPTION_STRING, &positions_file, 0,
146 "location positions file", &DEFAULT_POSITIONS_FILE},
147 {"special", 'Q', true, OPTION_STRING, &frequency_file, 0,
148 "location spl. frequency file (opt)", &SPECIAL_FREQUENCIES_FILE},
153 process_packet (char *buf)
156 /* cut off ending \n */
157 buf[strlen (buf) - 1] = '\0';
159 /* parse the data into a struct */
160 parse_fgdata (&data, buf);
162 /* get the selected frequency */
163 if (com_select == 0 && data.COM1_SRV == 1)
164 selected_frequency = data.COM1_FRQ;
165 else if (com_select == 1 && data.COM2_SRV == 1)
166 selected_frequency = data.COM2_FRQ;
168 /* Check for com frequency changes */
169 if (previous_com_frequency != selected_frequency)
171 SG_LOG( SG_GENERAL, SG_ALERT, "Selected frequency: " << selected_frequency );
173 /* remark the new frequency */
174 previous_com_frequency = selected_frequency;
178 /* hangup call, if connected */
180 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
185 icaobypos (airportlist, selected_frequency, data.LAT,
186 data.LON, DEFAULT_RANGE));
187 icao2number (icao, selected_frequency, num_buf);
189 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
190 do_iaxc_call (username, password, voipserver, num_buf);
195 /* Check for pressed PTT key */
196 if (previous_ptt != data.PTT)
200 /* select the next com equipment */
201 com_select = (com_select + 1) % 2;
202 SG_LOG( SG_GENERAL, SG_ALERT, "Radio selected is " << radio_map[com_select] );
204 else if (connected == 1)
208 previous_ptt = data.PTT;
211 /* Check for output volume change */
212 if (previous_vol != data.OUTPUT_VOL)
214 SG_LOG( SG_GENERAL, SG_ALERT, "Set speaker volume to " << data.OUTPUT_VOL );
216 iaxc_output_level_set( data.OUTPUT_VOL );
217 previous_vol = data.OUTPUT_VOL;
220 /* Check for silence threshold change */
221 if (previous_thd != data.SILENCE_THD)
223 SG_LOG( SG_GENERAL, SG_ALERT, "Set silent threshold to " << data.SILENCE_THD );
225 iaxc_set_silence_threshold( data.SILENCE_THD );
226 previous_thd = data.SILENCE_THD;
229 /* Check for callsign change */
230 if (strcmp(callsign, data.CALLSIGN) != 0)
233 callsign = data.CALLSIGN;
234 SG_LOG( SG_GENERAL, SG_ALERT, "FGCom will restart now with callsign " << callsign );
235 std::string app = "FGCOM-";
236 app += FGCOM_VERSION;
237 iaxc_set_callerid ( callsign, app.c_str() );
241 static char *base_name( char *name )
244 size_t len = strlen(name);
247 for ( i = 0; i < len; i++ ) {
249 if (( c == '/' ) || ( c == '\\'))
255 // if default FAILS, for OSX and WIN try EXE path
256 int fix_input_files()
259 char *def_freq = (char *) SPECIAL_FREQUENCIES_FILE;
260 char *def_pos = (char *) DEFAULT_POSITIONS_FILE;
261 char exepath[MX_PATH_SIZE+2];
263 if (strcmp( frequency_file,def_freq) == 0) {
264 /* ok is default value - do some fixes */
265 if (is_file_or_directory( frequency_file ) != 1) {
267 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
268 strcat(exepath,def_freq);
269 frequency_file = strdup(exepath);
272 if (strcmp( positions_file,def_pos) == 0) {
273 // if default FAILS, for OSX and WIN try EXE path
274 if (is_file_or_directory( positions_file ) != 1) {
276 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
277 strcat(exepath,def_pos);
278 positions_file = strdup(exepath);
285 main (int argc, char *argv[])
288 static char pkt_buf[MAXBUFLEN+2];
290 simgear::requestConsole();
291 sglog().setLogLevels( SG_ALL, SG_ALERT );
293 prog = strdup( base_name(argv[0]) );
296 SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
297 SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
298 SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
299 SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
300 SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
301 SG_LOG( SG_GENERAL, SG_ALERT, "" );
304 voipserver = DEFAULT_VOIP_SERVER;
305 fgserver = DEFAULT_FG_SERVER;
306 port = DEFAULT_FG_PORT;
307 username = DEFAULT_USER;
308 password = DEFAULT_PASSWORD;
309 codec_option = DEFAULT_CODEC;
310 mode = 0; // 0 = ATC mode, 1 = FG mode
311 positions_file = (char *) DEFAULT_POSITIONS_FILE;
312 frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
316 signal (SIGINT, quit);
317 signal (SIGQUIT, quit);
318 signal (SIGTERM, quit);
323 /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom
324 can be an offset to a jump table, making a compare of the current address to the address of
325 the actual imported function fail. So here ensure they are the same. */
326 iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
329 if (iaxc_initialize (DEFAULT_MAX_CALLS))
330 fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
335 fgcomInitOptions (fgcomOptionArray, argc, argv);
338 sglog().setLogLevels( SG_ALL, SG_DEBUG );
343 switch (codec_option)
346 codec = IAXC_FORMAT_ULAW;
349 codec = IAXC_FORMAT_ALAW;
352 codec = IAXC_FORMAT_GSM;
355 codec = IAXC_FORMAT_G726;
358 codec = IAXC_FORMAT_SPEEX;
366 strtoupper (airport_option, airport, sizeof (airport));
392 iaxc_mic_boost_set (1);
397 SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
398 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
400 SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
401 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
410 set_device (audio_in, 0);
415 set_device (audio_out, 1);
418 /* checking consistency of arguments */
419 if (frequency > 0.0 && frequency < 1000.0)
421 if (strlen (airport) == 0 || strlen (airport) > 4)
423 strcpy (airport, "ZZZZ");
425 /* airport and frequency are given => ATC mode */
430 /* no airport => FG mode */
434 /* Read special frequencies file (if exists).
435 * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
436 * are used and are hard coded.
439 if (fix_input_files()) { /* adjust default input per OS */
440 fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
443 if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
444 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
445 SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
446 special_frequencies = special_frq;
448 SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
451 /* read airport frequencies and positions */
452 airportlist = read_airports (positions_file); /* never returns if fail! */
454 /* preconfigure iax */
455 SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
457 std::string app = "FGCOM-";
458 app += FGCOM_VERSION;
461 callsign = DEFAULT_USER;
463 iaxc_set_callerid ( callsign, app.c_str() );
464 iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
465 iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
466 iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
467 iaxc_set_silence_threshold(silence_thd);
468 iaxc_set_event_callback (iaxc_callback);
470 iaxc_start_processing_thread ();
472 if (username && password && voipserver)
475 iaxc_register (const_cast < char *>(username),
476 const_cast < char *>(password),
477 const_cast < char *>(voipserver));
478 SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
482 SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
483 SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
488 iaxc_millisleep (DEFAULT_MILLISLEEP);
493 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
494 /* only in FG mode */
495 simgear::Socket::initSockets();
496 sgSocket.open (false);
497 sgSocket.bind (fgserver, port);
499 /* mute mic, speaker on */
500 iaxc_input_level_set (0);
501 iaxc_output_level_set (level_out);
503 SGTimeStamp sg_clock;
506 double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
507 /* get data from flightgear */
511 double sg_wait = sg_next_update - sg_clock.toSecs();
514 simgear::Socket *readSockets[2] = { &sgSocket, 0 };
515 if (sgSocket.select (readSockets, readSockets + 1,
516 (int) (sg_wait * 1000)) == 1)
518 simgear::IPAddress their_addr;
519 numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
525 pkt_buf[numbytes] = '\0';
526 SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
527 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
528 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
529 process_packet (pkt_buf);
536 sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
542 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
543 /* mic on, speaker on */
544 iaxc_input_level_set (1.0);
545 iaxc_output_level_set (1.0);
547 icao2atisnumber (airport, frequency, num_buf);
548 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
549 do_iaxc_call (username, password, voipserver, num_buf);
554 SGTimeStamp::sleepForMSec(3600000);
558 /* should never be reached */
566 SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
571 iaxc_unregister (reg_id);
577 alarm_handler (int signal)
579 /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
580 if (check_special_frq (selected_frequency))
582 strcpy (icao, "ZZZZ");
587 icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
591 /* Check if we are out of range */
592 if (strlen (icao) == 0 && connected == 1)
594 /* Yes, we are out of range so hangup */
596 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
600 /* Check if we are now in range */
601 else if (strlen (icao) != 0 && connected == 0)
603 icao2number (icao, selected_frequency, num_buf);
604 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
605 do_iaxc_call (username, password, voipserver, num_buf);
611 strtoupper (const char *str, char *buf, size_t len)
614 for (i = 0; str[i] && i < len - 1; i++)
616 buf[i] = toupper (str[i]);
623 fatal_error (const char *err)
625 SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
632 iaxc_callback (iaxc_event e)
636 case IAXC_EVENT_LEVELS:
637 event_level (e.ev.levels.input, e.ev.levels.output);
639 case IAXC_EVENT_TEXT:
640 event_text (e.ev.text.type, e.ev.text.message);
642 case IAXC_EVENT_STATE:
643 event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
644 e.ev.call.local, e.ev.call.local_context);
646 case IAXC_EVENT_NETSTAT:
647 event_netstats (e.ev.netstats);
648 case IAXC_EVENT_REGISTRATION:
649 event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
652 event_unknown (e.type);
659 event_state (int state, char *remote, char *remote_name,
660 char *local, char *local_context)
663 /* This is needed for auto-reconnect */
667 /* FIXME: we should wake up the main thread somehow */
668 /* in fg mode the next incoming packet will do that anyway */
671 snprintf (rep_buf, MX_REPORT_BUF,
672 "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
673 delim, map_state (state), delim, remote, delim, remote_name,
674 delim, local, delim, local_context);
675 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
680 event_text (int type, char *message)
682 snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
683 SG_LOG( SG_GENERAL, SG_ALERT, message );
684 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
689 event_register (int id, int reply, int count)
694 case IAXC_REGISTRATION_REPLY_ACK:
697 case IAXC_REGISTRATION_REPLY_REJ:
699 if (strcmp (username, "guest") != 0)
701 SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
704 case IAXC_REGISTRATION_REPLY_TIMEOUT:
710 snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
711 reason, delim, count);
712 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
717 event_netstats (struct iaxc_ev_netstats stat)
719 struct iaxc_netstat local = stat.local;
720 struct iaxc_netstat remote = stat.remote;
721 snprintf (rep_buf, MX_REPORT_BUF,
722 "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",
723 delim, stat.callNo, delim, stat.rtt,
724 delim, local.jitter, delim, local.losspct, delim,
725 local.losscnt, delim, local.packets, delim, local.delay,
726 delim, local.dropped, delim, local.ooo, delim,
727 remote.jitter, delim, remote.losspct, delim, remote.losscnt,
728 delim, remote.packets, delim, remote.delay, delim,
729 remote.dropped, delim, remote.ooo);
730 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
735 event_level (double in, double out)
737 snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
738 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
743 map_state (int state)
752 for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
757 strcat (states, ",");
758 strcat (states, map[i]);
766 event_unknown (int type)
768 snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
769 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
778 SG_LOG( SG_GENERAL, SG_ALERT, text );
788 /* mic is muted so unmute and mute speaker */
789 iaxc_input_level_set (level_in);
790 iaxc_output_level_set (0.0);
791 SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
795 /* mic is unmuted so mute and unmute speaker */
796 iaxc_input_level_set (0.0);
797 iaxc_output_level_set (level_out);
798 SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
803 split (char *string, char *fields[], int nfields, const char *sep)
805 register char *p = string;
806 register char c; /* latest character */
807 register char sepc = sep[0];
810 register char **fp = fields;
811 register const char *sepp;
812 register int trimtrail;
816 while ((c = *p++) == ' ' || c == '\t')
820 sep = " \t"; /* note, code below knows this is 2 long */
825 sepc2 = sep[1]; /* now we can safely pick this up */
829 /* single separator */
839 while ((c = *p++) != sepc)
841 return (nfields - fn);
844 /* we have overflowed the fields vector -- just count them */
848 while ((c = *p++) != sepc)
864 while ((c = *p++) != sepc && c != sepc2)
867 if (trimtrail && **(fp - 1) == '\0')
869 return (nfields - fn);
874 while ((c = *p++) == sepc || c == sepc2)
878 /* we have overflowed the fields vector -- just count them */
882 while ((c = *p++) == sepc || c == sepc2)
886 while ((c = *p++) != '\0' && c != sepc && c != sepc2)
889 /* might have to trim trailing white space */
893 while ((c = *--p) == sepc || c == sepc2)
898 if (fn == nfields + 1)
919 while ((sepc = *sepp++) != '\0' && sepc != c)
921 if (sepc != '\0') /* it was a separator */
930 while ((sepc = *sepp++) != '\0' && sepc != c)
932 if (sepc == '\0') /* it wasn't a separator */
943 * \fn double *read_special_frequencies(const char *file)
945 * \brief Reads the file "special_frequencies.txt" if it exists.
946 * If no file exists, then no special frequencies are useable.
948 * \param file pointer on the filename.
950 * \return Returns the pointer on an array containing doubles if file
951 * has been successfully opened and read, otherwise returns NULL.
954 double *read_special_frequencies(const char *file)
956 double *l_pfrq = NULL;
962 if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
964 l_allocated += ALLOC_CHUNK_SIZE;
966 if(FGC_SUCCESS(parser_init(file)))
970 while(FGC_SUCCESS(parser_get_next_value(&l_value)))
972 if(l_count >= l_allocated)
974 l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
975 l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
976 l_allocated += ALLOC_CHUNK_SIZE;
979 l_pfrq[l_count] = l_value;
984 /* Last value of the array must be -1.0 which is the terminator. */
985 if(l_count == l_allocated)
986 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
987 l_pfrq[l_count] = -1.0;
989 // failed to open file
1003 read_airports (const char *file)
1007 struct airport airport_tmp;
1008 struct airport *first = NULL;
1009 struct airport *my_airport = NULL;
1010 struct airport *previous_airport = NULL;
1013 SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
1015 if ((fp = fopen (file, "rt")) == NULL)
1017 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1023 airport_tmp.next = NULL;
1024 while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1025 airport_tmp.icao, &airport_tmp.frequency,
1026 &airport_tmp.lat, &airport_tmp.lon,
1027 airport_tmp.type, airport_tmp.text)) == 6)
1031 (struct airport *) malloc (sizeof (struct airport))) == NULL)
1033 SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1040 memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1041 if (previous_airport != NULL)
1043 previous_airport->next = my_airport;
1045 previous_airport = my_airport;
1051 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1056 SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1061 report_devices (int in)
1063 struct iaxc_audio_device *devs; //audio devices
1064 int ndevs; //audio dedvice count
1065 int input, output, ring; //audio device id's
1067 int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1068 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1069 current = in ? input : output;
1070 snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1071 for (i = 0; i < ndevs; i++)
1073 if (devs[i].capabilities & flag && i != current)
1075 snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1078 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1079 if (strlen(rep_buf) >= MX_REPORT_BUF)
1082 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1087 set_device (const char *name, int out)
1089 struct iaxc_audio_device *devs; /* audio devices */
1090 int ndevs; /* audio dedvice count */
1091 int input, output, ring; /* audio device id's */
1093 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1094 for (i = 0; i < ndevs; i++)
1096 if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1097 strcmp (name, devs[i].name) == 0)
1101 output = devs[i].devID;
1105 input = devs[i].devID;
1107 fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1109 iaxc_audio_devices_set (input, output, ring);
1117 parse_fgdata (struct fgdata *data, char *buf)
1119 char *data_pair = NULL;
1123 SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1124 /* Parse data from FG */
1125 data_pair = strtok (buf, ",");
1126 while (data_pair != NULL)
1128 split (data_pair, fields, 2, "=");
1129 if (strcmp (fields[0], "COM1_FRQ") == 0)
1131 data->COM1_FRQ = atof (fields[1]);
1132 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1134 else if (strcmp (fields[0], "COM2_FRQ") == 0)
1136 data->COM2_FRQ = atof (fields[1]);
1137 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1139 else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1141 data->NAV1_FRQ = atof (fields[1]);
1142 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1144 else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1146 data->NAV2_FRQ = atof (fields[1]);
1147 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1149 else if (strcmp (fields[0], "PTT") == 0)
1151 data->PTT = atoi (fields[1]);
1152 SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1154 else if (strcmp (fields[0], "TRANSPONDER") == 0)
1156 data->TRANSPONDER = atoi (fields[1]);
1157 SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1159 else if (strcmp (fields[0], "IAS") == 0)
1161 data->IAS = atof (fields[1]);
1162 SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1164 else if (strcmp (fields[0], "GS") == 0)
1166 data->GS = atof (fields[1]);
1167 SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1169 else if (strcmp (fields[0], "LON") == 0)
1171 data->LON = atof (fields[1]);
1172 SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1174 else if (strcmp (fields[0], "LAT") == 0)
1176 data->LAT = atof (fields[1]);
1177 SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1179 else if (strcmp (fields[0], "ALT") == 0)
1181 data->ALT = atoi (fields[1]);
1182 SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1184 else if (strcmp (fields[0], "HEAD") == 0)
1186 data->HEAD = atof (fields[1]);
1187 SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1189 else if (strcmp (fields[0], "COM1_SRV") == 0)
1191 data->COM1_SRV = atoi (fields[1]);
1192 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1194 else if (strcmp (fields[0], "COM2_SRV") == 0)
1196 data->COM2_SRV = atoi (fields[1]);
1197 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1199 else if (strcmp (fields[0], "NAV1_SRV") == 0)
1201 data->NAV1_SRV = atoi (fields[1]);
1202 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1204 else if (strcmp (fields[0], "NAV2_SRV") == 0)
1206 data->NAV2_SRV = atoi (fields[1]);
1207 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1209 else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1211 data->OUTPUT_VOL = atof (fields[1]);
1212 SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1214 else if (strcmp (fields[0], "SILENCE_THD") == 0)
1216 data->SILENCE_THD = atof (fields[1]);
1217 SG_LOG( SG_GENERAL, SG_DEBUG, "SILENCE_THD=" << data->SILENCE_THD );
1219 else if (strcmp (fields[0], "CALLSIGN") == 0)
1221 data->CALLSIGN = fields[1];
1222 SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1226 SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1229 data_pair = strtok (NULL, ",");
1231 SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1236 * \fn int check_special_frq (double frq)
1238 * \brief Check to see if specified frequency is a special frequency.
1240 * \param frq frequency to check against special frequencies
1242 * \return Returns 1 if successful, otherwise returns 0.
1245 int check_special_frq (double frq)
1248 frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1249 while (special_frequencies[i] >= 0.0)
1251 if (frq == special_frequencies[i])
1253 SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1262 do_iaxc_call (const char *username, const char *password,
1263 const char *voipserver, char *number)
1267 if( strcmp(number, "9990909090910000") == 0)
1268 number = (char *)"0190909090910000";
1270 snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1271 voipserver, number);
1273 iaxc_millisleep (DEFAULT_MILLISLEEP);
1276 /* eof - fgcom.cpp */