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[] = {
79 double *special_frequencies;
81 double previous_com_frequency = 0.0;
83 float previous_vol = 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 bool mic_boost;
102 static char codec_option;
103 static const char *callsign;
104 static const char *username;
105 static const char *password;
106 static bool list_audio;
107 static char *positions_file;
108 static char *frequency_file;
110 static const OptionEntry fgcomOptionArray[] = {
111 {"debug", 'd', false, OPTION_NONE, &debug, 0, "show debugging information",
113 {"voipserver", 'S', true, OPTION_STRING, &voipserver, 0,
114 "voip server to connect to", &DEFAULT_VOIP_SERVER},
115 {"fgserver", 's', true, OPTION_STRING, &fgserver, 0, "fg to connect to ",
117 {"port", 'p', true, OPTION_INT, &port, 0, "where we should listen to FG", &port}, // hm, good idea? don't think so...
118 {"airport", 'a', true, OPTION_STRING, &airport_option, 0,
119 "airport-id (ICAO) for ATC-mode", 0},
120 {"frequency", 'f', true, OPTION_DOUBLE, &frequency, 0,
121 "frequency for ATC-mode", 0},
122 {"callsign", 'C', true, OPTION_STRING, &callsign, 0,
123 "callsign to use", &DEFAULT_USER},
124 {"user", 'U', true, OPTION_STRING, &username, 0,
125 "username for VoIP account", &DEFAULT_USER},
126 {"password", 'P', true, OPTION_STRING, &password, 0,
127 "password for VoIP account", &DEFAULT_PASSWORD},
128 {"mic", 'i', true, OPTION_DOUBLE, &level_in, 0,
129 "mic input level (0.0 - 1.0)", 0},
130 {"speaker", 'o', true, OPTION_DOUBLE, &level_out, 0,
131 "speaker output level (0.0 - 1.0)", 0},
132 {"mic-boost", 'b', false, OPTION_BOOL, &mic_boost, 0, "enable mic boost",
134 {"list-audio", 'l', false, OPTION_BOOL, &list_audio, 0,
135 "list audio devices", 0},
136 {"set-audio-in", 'r', true, OPTION_STRING, &audio_in, 0,
137 "use <devicename> as audio input", 0},
138 {"set-audio-out", 'k', true, OPTION_STRING, &audio_out, 0,
139 "use <devicename> as audio output", 0},
140 {"codec", 'c', true, OPTION_CHAR, &codec_option, 0,
141 "use codec <codec> as transfer codec", &codec_option},
142 {"positions", 'T', true, OPTION_STRING, &positions_file, 0,
143 "location positions file", &DEFAULT_POSITIONS_FILE},
144 {"special", 'Q', true, OPTION_STRING, &frequency_file, 0,
145 "location spl. frequency file (opt)", &SPECIAL_FREQUENCIES_FILE},
150 process_packet (char *buf)
153 /* cut off ending \n */
154 buf[strlen (buf) - 1] = '\0';
156 /* parse the data into a struct */
157 parse_fgdata (&data, buf);
159 /* get the selected frequency */
160 if (com_select == 0 && data.COM1_SRV == 1)
161 selected_frequency = data.COM1_FRQ;
162 else if (com_select == 1 && data.COM2_SRV == 1)
163 selected_frequency = data.COM2_FRQ;
165 /* Check for com frequency changes */
166 if (previous_com_frequency != selected_frequency)
168 SG_LOG( SG_GENERAL, SG_ALERT, "Selected frequency: " << selected_frequency );
170 /* remark the new frequency */
171 previous_com_frequency = selected_frequency;
175 /* hangup call, if connected */
177 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
182 icaobypos (airportlist, selected_frequency, data.LAT,
183 data.LON, DEFAULT_RANGE));
184 icao2number (icao, selected_frequency, num_buf);
186 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
187 do_iaxc_call (username, password, voipserver, num_buf);
192 /* Check for pressed PTT key */
193 if (previous_ptt != data.PTT)
197 /* select the next com equipment */
198 com_select = (com_select + 1) % 2;
199 SG_LOG( SG_GENERAL, SG_ALERT, "Radio selected is " << radio_map[com_select] );
201 else if (connected == 1)
205 previous_ptt = data.PTT;
208 /* Check for output volume change */
209 if (previous_vol != data.OUTPUT_VOL)
211 SG_LOG( SG_GENERAL, SG_ALERT, "Set speaker volume to " << data.OUTPUT_VOL );
213 iaxc_output_level_set( data.OUTPUT_VOL );
214 previous_vol = data.OUTPUT_VOL;
217 /* Check for callsign change */
218 if (strcmp(callsign, data.CALLSIGN) != 0)
221 callsign = data.CALLSIGN;
222 SG_LOG( SG_GENERAL, SG_ALERT, "FGCom will restart now with callsign " << callsign );
223 std::string app = "FGCOM-";
224 app += FGCOM_VERSION;
225 iaxc_set_callerid ( callsign, app.c_str() );
229 static char *base_name( char *name )
232 size_t len = strlen(name);
235 for ( i = 0; i < len; i++ ) {
237 if (( c == '/' ) || ( c == '\\'))
243 // if default FAILS, for OSX and WIN try EXE path
244 int fix_input_files()
247 char *def_freq = (char *) SPECIAL_FREQUENCIES_FILE;
248 char *def_pos = (char *) DEFAULT_POSITIONS_FILE;
249 char exepath[MX_PATH_SIZE+2];
251 if (strcmp( frequency_file,def_freq) == 0) {
252 /* ok is default value - do some fixes */
253 if (is_file_or_directory( frequency_file ) != 1) {
255 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
256 strcat(exepath,def_freq);
257 frequency_file = strdup(exepath);
260 if (strcmp( positions_file,def_pos) == 0) {
261 // if default FAILS, for OSX and WIN try EXE path
262 if (is_file_or_directory( positions_file ) != 1) {
264 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
265 strcat(exepath,def_pos);
266 positions_file = strdup(exepath);
273 main (int argc, char *argv[])
276 static char pkt_buf[MAXBUFLEN+2];
278 sglog().setLogLevels( SG_ALL, SG_ALERT );
280 prog = strdup( base_name(argv[0]) );
283 SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
284 SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
285 SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
286 SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
287 SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
288 SG_LOG( SG_GENERAL, SG_ALERT, "" );
291 voipserver = DEFAULT_VOIP_SERVER;
292 fgserver = DEFAULT_FG_SERVER;
293 port = DEFAULT_FG_PORT;
294 username = DEFAULT_USER;
295 password = DEFAULT_PASSWORD;
296 codec_option = DEFAULT_CODEC;
297 mode = 0; // 0 = ATC mode, 1 = FG mode
298 positions_file = (char *) DEFAULT_POSITIONS_FILE;
299 frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
303 signal (SIGINT, quit);
304 signal (SIGQUIT, quit);
305 signal (SIGTERM, quit);
310 /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom
311 can be an offset to a jump table, making a compare of the current address to the address of
312 the actual imported function fail. So here ensure they are the same. */
313 iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
316 if (iaxc_initialize (DEFAULT_MAX_CALLS))
317 fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
322 fgcomInitOptions (fgcomOptionArray, argc, argv);
325 sglog().setLogLevels( SG_ALL, SG_DEBUG );
330 switch (codec_option)
333 codec = IAXC_FORMAT_ULAW;
336 codec = IAXC_FORMAT_ALAW;
339 codec = IAXC_FORMAT_GSM;
342 codec = IAXC_FORMAT_G726;
345 codec = IAXC_FORMAT_SPEEX;
353 strtoupper (airport_option, airport, sizeof (airport));
379 iaxc_mic_boost_set (1);
384 SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
385 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
387 SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
388 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
397 set_device (audio_in, 0);
402 set_device (audio_out, 1);
405 /* checking consistency of arguments */
406 if (frequency > 0.0 && frequency < 1000.0)
408 if (strlen (airport) == 0 || strlen (airport) > 4)
410 strcpy (airport, "ZZZZ");
412 /* airport and frequency are given => ATC mode */
417 /* no airport => FG mode */
421 /* Read special frequencies file (if exists).
422 * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
423 * are used and are hard coded.
426 if (fix_input_files()) { /* adjust default input per OS */
427 fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
430 if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
431 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
432 SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
433 special_frequencies = special_frq;
435 SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
438 /* read airport frequencies and positions */
439 airportlist = read_airports (positions_file); /* never returns if fail! */
441 /* preconfigure iax */
442 SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
444 std::string app = "FGCOM-";
445 app += FGCOM_VERSION;
448 callsign = DEFAULT_USER;
450 iaxc_set_callerid ( callsign, app.c_str() );
451 iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_SPEEX);
452 iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
453 iaxc_set_event_callback (iaxc_callback);
455 iaxc_start_processing_thread ();
457 if (username && password && voipserver)
460 iaxc_register (const_cast < char *>(username),
461 const_cast < char *>(password),
462 const_cast < char *>(voipserver));
463 SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
467 SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
468 SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
473 iaxc_millisleep (DEFAULT_MILLISLEEP);
478 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
479 /* only in FG mode */
480 simgear::Socket::initSockets();
481 sgSocket.open (false);
482 sgSocket.bind (fgserver, port);
484 /* mute mic, speaker on */
485 iaxc_input_level_set (0);
486 iaxc_output_level_set (level_out);
488 SGTimeStamp sg_clock;
491 double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
492 /* get data from flightgear */
496 double sg_wait = sg_next_update - sg_clock.toSecs();
499 simgear::Socket *readSockets[2] = { &sgSocket, 0 };
500 if (sgSocket.select (readSockets, readSockets + 1,
501 (int) (sg_wait * 1000)) == 1)
503 simgear::IPAddress their_addr;
504 numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
510 pkt_buf[numbytes] = '\0';
511 SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
512 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
513 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
514 process_packet (pkt_buf);
521 sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
527 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
528 /* mic on, speaker on */
529 iaxc_input_level_set (1.0);
530 iaxc_output_level_set (1.0);
532 icao2atisnumber (airport, frequency, num_buf);
533 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
534 do_iaxc_call (username, password, voipserver, num_buf);
539 SGTimeStamp::sleepForMSec(3600000);
543 /* should never be reached */
551 SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
556 iaxc_unregister (reg_id);
562 alarm_handler (int signal)
564 /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
565 if (check_special_frq (selected_frequency))
567 strcpy (icao, "ZZZZ");
572 icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
576 /* Check if we are out of range */
577 if (strlen (icao) == 0 && connected == 1)
579 /* Yes, we are out of range so hangup */
581 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
585 /* Check if we are now in range */
586 else if (strlen (icao) != 0 && connected == 0)
588 icao2number (icao, selected_frequency, num_buf);
589 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
590 do_iaxc_call (username, password, voipserver, num_buf);
596 strtoupper (const char *str, char *buf, size_t len)
599 for (i = 0; str[i] && i < len - 1; i++)
601 buf[i] = toupper (str[i]);
608 fatal_error (const char *err)
610 SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
617 iaxc_callback (iaxc_event e)
621 case IAXC_EVENT_LEVELS:
622 event_level (e.ev.levels.input, e.ev.levels.output);
624 case IAXC_EVENT_TEXT:
625 event_text (e.ev.text.type, e.ev.text.message);
627 case IAXC_EVENT_STATE:
628 event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
629 e.ev.call.local, e.ev.call.local_context);
631 case IAXC_EVENT_NETSTAT:
632 event_netstats (e.ev.netstats);
633 case IAXC_EVENT_REGISTRATION:
634 event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
637 event_unknown (e.type);
644 event_state (int state, char *remote, char *remote_name,
645 char *local, char *local_context)
648 /* This is needed for auto-reconnect */
652 /* FIXME: we should wake up the main thread somehow */
653 /* in fg mode the next incoming packet will do that anyway */
656 snprintf (rep_buf, MX_REPORT_BUF,
657 "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
658 delim, map_state (state), delim, remote, delim, remote_name,
659 delim, local, delim, local_context);
660 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
665 event_text (int type, char *message)
667 snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
668 SG_LOG( SG_GENERAL, SG_ALERT, message );
669 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
674 event_register (int id, int reply, int count)
679 case IAXC_REGISTRATION_REPLY_ACK:
682 case IAXC_REGISTRATION_REPLY_REJ:
684 if (strcmp (username, "guest") != 0)
686 SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
689 case IAXC_REGISTRATION_REPLY_TIMEOUT:
695 snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
696 reason, delim, count);
697 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
702 event_netstats (struct iaxc_ev_netstats stat)
704 struct iaxc_netstat local = stat.local;
705 struct iaxc_netstat remote = stat.remote;
706 snprintf (rep_buf, MX_REPORT_BUF,
707 "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",
708 delim, stat.callNo, delim, stat.rtt,
709 delim, local.jitter, delim, local.losspct, delim,
710 local.losscnt, delim, local.packets, delim, local.delay,
711 delim, local.dropped, delim, local.ooo, delim,
712 remote.jitter, delim, remote.losspct, delim, remote.losscnt,
713 delim, remote.packets, delim, remote.delay, delim,
714 remote.dropped, delim, remote.ooo);
715 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
720 event_level (double in, double out)
722 snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
723 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
728 map_state (int state)
737 for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
742 strcat (states, ",");
743 strcat (states, map[i]);
751 event_unknown (int type)
753 snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
754 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
763 SG_LOG( SG_GENERAL, SG_ALERT, text );
773 /* mic is muted so unmute and mute speaker */
774 iaxc_input_level_set (level_in);
775 iaxc_output_level_set (0.0);
776 SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
780 /* mic is unmuted so mute and unmute speaker */
781 iaxc_input_level_set (0.0);
782 iaxc_output_level_set (level_out);
783 SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
788 split (char *string, char *fields[], int nfields, const char *sep)
790 register char *p = string;
791 register char c; /* latest character */
792 register char sepc = sep[0];
795 register char **fp = fields;
796 register const char *sepp;
797 register int trimtrail;
801 while ((c = *p++) == ' ' || c == '\t')
805 sep = " \t"; /* note, code below knows this is 2 long */
810 sepc2 = sep[1]; /* now we can safely pick this up */
814 /* single separator */
824 while ((c = *p++) != sepc)
826 return (nfields - fn);
829 /* we have overflowed the fields vector -- just count them */
833 while ((c = *p++) != sepc)
849 while ((c = *p++) != sepc && c != sepc2)
852 if (trimtrail && **(fp - 1) == '\0')
854 return (nfields - fn);
859 while ((c = *p++) == sepc || c == sepc2)
863 /* we have overflowed the fields vector -- just count them */
867 while ((c = *p++) == sepc || c == sepc2)
871 while ((c = *p++) != '\0' && c != sepc && c != sepc2)
874 /* might have to trim trailing white space */
878 while ((c = *--p) == sepc || c == sepc2)
883 if (fn == nfields + 1)
904 while ((sepc = *sepp++) != '\0' && sepc != c)
906 if (sepc != '\0') /* it was a separator */
915 while ((sepc = *sepp++) != '\0' && sepc != c)
917 if (sepc == '\0') /* it wasn't a separator */
928 * \fn double *read_special_frequencies(const char *file)
930 * \brief Reads the file "special_frequencies.txt" if it exists.
931 * If no file exists, then no special frequencies are useable.
933 * \param file pointer on the filename.
935 * \return Returns the pointer on an array containing doubles if file
936 * has been successfully opened and read, otherwise returns NULL.
939 double *read_special_frequencies(const char *file)
941 double *l_pfrq = NULL;
947 if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
949 l_allocated += ALLOC_CHUNK_SIZE;
951 if(FGC_SUCCESS(parser_init(file)))
955 while(FGC_SUCCESS(parser_get_next_value(&l_value)))
957 if(l_count >= l_allocated)
959 l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
960 l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
961 l_allocated += ALLOC_CHUNK_SIZE;
964 l_pfrq[l_count] = l_value;
969 /* Last value of the array must be -1.0 which is the terminator. */
970 if(l_count == l_allocated)
971 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
972 l_pfrq[l_count] = -1.0;
974 // failed to open file
988 read_airports (const char *file)
992 struct airport airport_tmp;
993 struct airport *first = NULL;
994 struct airport *my_airport = NULL;
995 struct airport *previous_airport = NULL;
998 SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
1000 if ((fp = fopen (file, "rt")) == NULL)
1002 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1008 airport_tmp.next = NULL;
1009 while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1010 airport_tmp.icao, &airport_tmp.frequency,
1011 &airport_tmp.lat, &airport_tmp.lon,
1012 airport_tmp.type, airport_tmp.text)) == 6)
1016 (struct airport *) malloc (sizeof (struct airport))) == NULL)
1018 SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1025 memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1026 if (previous_airport != NULL)
1028 previous_airport->next = my_airport;
1030 previous_airport = my_airport;
1036 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1041 SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1046 report_devices (int in)
1048 struct iaxc_audio_device *devs; //audio devices
1049 int ndevs; //audio dedvice count
1050 int input, output, ring; //audio device id's
1052 int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1053 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1054 current = in ? input : output;
1055 snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1056 for (i = 0; i < ndevs; i++)
1058 if (devs[i].capabilities & flag && i != current)
1060 snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1063 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1064 if (strlen(rep_buf) >= MX_REPORT_BUF)
1067 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1072 set_device (const char *name, int out)
1074 struct iaxc_audio_device *devs; /* audio devices */
1075 int ndevs; /* audio dedvice count */
1076 int input, output, ring; /* audio device id's */
1078 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1079 for (i = 0; i < ndevs; i++)
1081 if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1082 strcmp (name, devs[i].name) == 0)
1086 output = devs[i].devID;
1090 input = devs[i].devID;
1092 fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1094 iaxc_audio_devices_set (input, output, ring);
1102 parse_fgdata (struct fgdata *data, char *buf)
1104 char *data_pair = NULL;
1108 SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1109 /* Parse data from FG */
1110 data_pair = strtok (buf, ",");
1111 while (data_pair != NULL)
1113 split (data_pair, fields, 2, "=");
1114 if (strcmp (fields[0], "COM1_FRQ") == 0)
1116 data->COM1_FRQ = atof (fields[1]);
1117 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1119 else if (strcmp (fields[0], "COM2_FRQ") == 0)
1121 data->COM2_FRQ = atof (fields[1]);
1122 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1124 else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1126 data->NAV1_FRQ = atof (fields[1]);
1127 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1129 else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1131 data->NAV2_FRQ = atof (fields[1]);
1132 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1134 else if (strcmp (fields[0], "PTT") == 0)
1136 data->PTT = atoi (fields[1]);
1137 SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1139 else if (strcmp (fields[0], "TRANSPONDER") == 0)
1141 data->TRANSPONDER = atoi (fields[1]);
1142 SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1144 else if (strcmp (fields[0], "IAS") == 0)
1146 data->IAS = atof (fields[1]);
1147 SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1149 else if (strcmp (fields[0], "GS") == 0)
1151 data->GS = atof (fields[1]);
1152 SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1154 else if (strcmp (fields[0], "LON") == 0)
1156 data->LON = atof (fields[1]);
1157 SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1159 else if (strcmp (fields[0], "LAT") == 0)
1161 data->LAT = atof (fields[1]);
1162 SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1164 else if (strcmp (fields[0], "ALT") == 0)
1166 data->ALT = atoi (fields[1]);
1167 SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1169 else if (strcmp (fields[0], "HEAD") == 0)
1171 data->HEAD = atof (fields[1]);
1172 SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1174 else if (strcmp (fields[0], "COM1_SRV") == 0)
1176 data->COM1_SRV = atoi (fields[1]);
1177 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1179 else if (strcmp (fields[0], "COM2_SRV") == 0)
1181 data->COM2_SRV = atoi (fields[1]);
1182 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1184 else if (strcmp (fields[0], "NAV1_SRV") == 0)
1186 data->NAV1_SRV = atoi (fields[1]);
1187 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1189 else if (strcmp (fields[0], "NAV2_SRV") == 0)
1191 data->NAV2_SRV = atoi (fields[1]);
1192 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1194 else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1196 data->OUTPUT_VOL = atof (fields[1]);
1197 SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1199 else if (strcmp (fields[0], "CALLSIGN") == 0)
1201 data->CALLSIGN = fields[1];
1202 SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1206 SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1209 data_pair = strtok (NULL, ",");
1211 SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1216 * \fn int check_special_frq (double frq)
1218 * \brief Check to see if specified frequency is a special frequency.
1220 * \param frq frequency to check against special frequencies
1222 * \return Returns 1 if successful, otherwise returns 0.
1225 int check_special_frq (double frq)
1228 frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1229 while (special_frequencies[i] >= 0.0)
1231 if (frq == special_frequencies[i])
1233 SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1242 do_iaxc_call (const char *username, const char *password,
1243 const char *voipserver, char *number)
1246 size_t len = strlen(number);
1248 if( strcmp(voipserver, "delta384.server4you.de") == 0 ) {
1249 if( number[len-1] == '5' ) {
1250 number[len-1] = '0';
1254 if( strcmp(number, "9990909090910000") == 0)
1255 number = (char *)"0190909090910000";
1257 snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1258 voipserver, number);
1260 iaxc_millisleep (DEFAULT_MILLISLEEP);
1263 /* eof - fgcom.cpp */