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[] = {
67 "COM1", "NAV1", "COM2", "NAV2"
71 double special_frq[] =
72 { 999.999, 910.0, 123.45, 122.75, 123.5, 121.5, 732.34, -1.0 };
73 double *special_frequencies;
75 double previous_com_frequency = 0.0;
77 float previous_vol = 0.0;
79 int max_com_instruments = 2;
80 struct airport *airportlist;
82 char *prog; //program name
84 /* configuration values */
85 static bool debug = false;
87 static const char *voipserver;
88 static const char *fgserver;
89 static const char *airport_option;
90 static double frequency = -1.0;
91 static const char *audio_in;
92 static const char *audio_out;
93 static double level_in = 0.7;
94 static double level_out = 0.7;
95 static bool mic_boost;
96 static char codec_option;
97 static const char *callsign;
98 static const char *username;
99 static const char *password;
100 static bool list_audio;
101 static char *positions_file;
102 static char *frequency_file;
104 static const OptionEntry fgcomOptionArray[] = {
105 {"debug", 'd', false, OPTION_NONE, &debug, 0, "show debugging information",
107 {"voipserver", 'S', true, OPTION_STRING, &voipserver, 0,
108 "voip server to connect to", &DEFAULT_VOIP_SERVER},
109 {"fgserver", 's', true, OPTION_STRING, &fgserver, 0, "fg to connect to ",
111 {"port", 'p', true, OPTION_INT, &port, 0, "where we should listen to FG", &port}, // hm, good idea? don't think so...
112 {"airport", 'a', true, OPTION_STRING, &airport_option, 0,
113 "airport-id (ICAO) for ATC-mode", 0},
114 {"frequency", 'f', true, OPTION_DOUBLE, &frequency, 0,
115 "frequency for ATC-mode", 0},
116 {"callsign", 'C', true, OPTION_STRING, &callsign, 0,
117 "callsign to use", &DEFAULT_USER},
118 {"user", 'U', true, OPTION_STRING, &username, 0,
119 "username for VoIP account", &DEFAULT_USER},
120 {"password", 'P', true, OPTION_STRING, &password, 0,
121 "password for VoIP account", &DEFAULT_PASSWORD},
122 {"mic", 'i', true, OPTION_DOUBLE, &level_in, 0,
123 "mic input level (0.0 - 1.0)", 0},
124 {"speaker", 'o', true, OPTION_DOUBLE, &level_out, 0,
125 "speaker output level (0.0 - 1.0)", 0},
126 {"mic-boost", 'b', false, OPTION_BOOL, &mic_boost, 0, "enable mic boost",
128 {"list-audio", 'l', false, OPTION_BOOL, &list_audio, 0,
129 "list audio devices", 0},
130 {"set-audio-in", 'r', true, OPTION_STRING, &audio_in, 0,
131 "use <devicename> as audio input", 0},
132 {"set-audio-out", 'k', true, OPTION_STRING, &audio_out, 0,
133 "use <devicename> as audio output", 0},
134 {"codec", 'c', true, OPTION_CHAR, &codec_option, 0,
135 "use codec <codec> as transfer codec", &codec_option},
136 {"positions", 'T', true, OPTION_STRING, &positions_file, 0,
137 "location positions file", &DEFAULT_POSITIONS_FILE},
138 {"special", 'Q', true, OPTION_STRING, &frequency_file, 0,
139 "location spl. frequency file (opt)", &SPECIAL_FREQUENCIES_FILE},
144 process_packet (char *buf)
147 /* cut off ending \n */
148 buf[strlen (buf) - 1] = '\0';
150 /* parse the data into a struct */
151 parse_fgdata (&data, buf);
153 /* get the selected frequency */
154 if (com_select == 0 && data.COM1_SRV == 1)
155 selected_frequency = data.COM1_FRQ;
156 else if (com_select == 1 && data.NAV1_SRV == 1)
157 selected_frequency = data.NAV1_FRQ;
158 else if (com_select == 2 && data.COM2_SRV == 1)
159 selected_frequency = data.COM2_FRQ;
160 else if (com_select == 3 && data.NAV2_SRV == 1)
161 selected_frequency = data.NAV2_FRQ;
163 /* Check for com frequency changes */
164 if (previous_com_frequency != selected_frequency)
166 SG_LOG( SG_GENERAL, SG_ALERT, "Selected frequency: " << selected_frequency );
168 /* remark the new frequency */
169 previous_com_frequency = selected_frequency;
173 /* hangup call, if connected */
175 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
180 icaobypos (airportlist, selected_frequency, data.LAT,
181 data.LON, DEFAULT_RANGE));
182 icao2number (icao, selected_frequency, num_buf);
184 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
185 do_iaxc_call (username, password, voipserver, num_buf);
190 /* Check for pressed PTT key */
191 if (previous_ptt != data.PTT)
195 /* select the next com equipment */
196 com_select = (com_select + 1) % 4;
197 SG_LOG( SG_GENERAL, SG_ALERT, "Radio selected is " << radio_map[com_select] );
199 else if (connected == 1)
203 previous_ptt = data.PTT;
206 /* Check for output volume change */
207 if (previous_vol != data.OUTPUT_VOL)
209 SG_LOG( SG_GENERAL, SG_ALERT, "Set speaker volume to " << data.OUTPUT_VOL );
211 iaxc_output_level_set( data.OUTPUT_VOL );
212 previous_vol = data.OUTPUT_VOL;
215 /* Check for callsign change */
216 if (strcmp(callsign, data.CALLSIGN) != 0)
219 callsign = data.CALLSIGN;
220 SG_LOG( SG_GENERAL, SG_ALERT, "FGCom will restart now with callsign " << callsign );
221 std::string app = "FGCOM-";
222 app += FGCOM_VERSION;
223 iaxc_set_callerid ( callsign, app.c_str() );
227 static char *base_name( char *name )
230 size_t len = strlen(name);
233 for ( i = 0; i < len; i++ ) {
235 if (( c == '/' ) || ( c == '\\'))
241 // if default FAILS, for OSX and WIN try EXE path
242 int fix_input_files()
245 char *def_freq = (char *) SPECIAL_FREQUENCIES_FILE;
246 char *def_pos = (char *) DEFAULT_POSITIONS_FILE;
247 char exepath[MX_PATH_SIZE+2];
249 if (strcmp( frequency_file,def_freq) == 0) {
250 /* ok is default value - do some fixes */
251 if (is_file_or_directory( frequency_file ) != 1) {
253 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
254 strcat(exepath,def_freq);
255 frequency_file = strdup(exepath);
258 if (strcmp( positions_file,def_pos) == 0) {
259 // if default FAILS, for OSX and WIN try EXE path
260 if (is_file_or_directory( positions_file ) != 1) {
262 iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
263 strcat(exepath,def_pos);
264 positions_file = strdup(exepath);
271 main (int argc, char *argv[])
274 static char pkt_buf[MAXBUFLEN+2];
276 sglog().setLogLevels( SG_ALL, SG_ALERT );
278 prog = strdup( base_name(argv[0]) );
281 SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
282 SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
283 SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
284 SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
285 SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
286 SG_LOG( SG_GENERAL, SG_ALERT, "" );
289 voipserver = DEFAULT_VOIP_SERVER;
290 fgserver = DEFAULT_FG_SERVER;
291 port = DEFAULT_FG_PORT;
292 username = DEFAULT_USER;
293 password = DEFAULT_PASSWORD;
294 codec_option = DEFAULT_CODEC;
295 mode = 0; // 0 = ATC mode, 1 = FG mode
296 positions_file = (char *) DEFAULT_POSITIONS_FILE;
297 frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
301 signal (SIGINT, quit);
302 signal (SIGQUIT, quit);
303 signal (SIGTERM, quit);
308 /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom
309 can be an offset to a jump table, making a compare of the current address to the address of
310 the actual imported function fail. So here ensure they are the same. */
311 iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
314 if (iaxc_initialize (DEFAULT_MAX_CALLS))
315 fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
320 fgcomInitOptions (fgcomOptionArray, argc, argv);
323 sglog().setLogLevels( SG_ALL, SG_DEBUG );
328 switch (codec_option)
331 codec = IAXC_FORMAT_ULAW;
334 codec = IAXC_FORMAT_ALAW;
337 codec = IAXC_FORMAT_GSM;
340 codec = IAXC_FORMAT_G726;
343 codec = IAXC_FORMAT_SPEEX;
351 strtoupper (airport_option, airport, sizeof (airport));
377 iaxc_mic_boost_set (1);
382 SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
383 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
385 SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
386 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
395 set_device (audio_in, 0);
400 set_device (audio_out, 1);
403 /* checking consistency of arguments */
404 if (frequency > 0.0 && frequency < 1000.0)
406 if (strlen (airport) == 0 || strlen (airport) > 4)
408 strcpy (airport, "ZZZZ");
410 /* airport and frequency are given => ATC mode */
415 /* no airport => FG mode */
419 /* Read special frequencies file (if exists).
420 * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
421 * are used and are hard coded.
424 if (fix_input_files()) { /* adjust default input per OS */
425 fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
428 if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
429 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
430 SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
431 special_frequencies = special_frq;
433 SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
436 /* read airport frequencies and positions */
437 airportlist = read_airports (positions_file); /* never returns if fail! */
439 /* preconfigure iax */
440 SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
442 std::string app = "FGCOM-";
443 app += FGCOM_VERSION;
446 callsign = DEFAULT_USER;
448 iaxc_set_callerid ( callsign, app.c_str() );
449 iaxc_set_formats (codec,
450 IAXC_FORMAT_ULAW | IAXC_FORMAT_GSM | IAXC_FORMAT_SPEEX);
451 iaxc_set_event_callback (iaxc_callback);
453 iaxc_start_processing_thread ();
455 if (username && password && voipserver)
458 iaxc_register (const_cast < char *>(username),
459 const_cast < char *>(password),
460 const_cast < char *>(voipserver));
461 SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
465 SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
466 SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
471 iaxc_millisleep (DEFAULT_MILLISLEEP);
476 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
477 /* only in FG mode */
478 simgear::Socket::initSockets();
479 sgSocket.open (false);
480 sgSocket.bind (fgserver, port);
482 /* mute mic, speaker on */
483 iaxc_input_level_set (0);
484 iaxc_output_level_set (level_out);
486 SGTimeStamp sg_clock;
489 double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
490 /* get data from flightgear */
494 double sg_wait = sg_next_update - sg_clock.toSecs();
497 simgear::Socket *readSockets[2] = { &sgSocket, 0 };
498 if (sgSocket.select (readSockets, readSockets + 1,
499 (int) (sg_wait * 1000)) == 1)
501 simgear::IPAddress their_addr;
502 numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
508 pkt_buf[numbytes] = '\0';
509 SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
510 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
511 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
512 process_packet (pkt_buf);
519 sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
525 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
526 /* mic on, speaker on */
527 iaxc_input_level_set (1.0);
528 iaxc_output_level_set (1.0);
530 icao2atisnumber (airport, frequency, num_buf);
531 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
532 do_iaxc_call (username, password, voipserver, num_buf);
537 SGTimeStamp::sleepForMSec(3600000);
541 /* should never be reached */
549 SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
554 iaxc_unregister (reg_id);
560 alarm_handler (int signal)
562 /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
563 if (check_special_frq (selected_frequency))
565 strcpy (icao, "ZZZZ");
570 icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
574 /* Check if we are out of range */
575 if (strlen (icao) == 0 && connected == 1)
577 /* Yes, we are out of range so hangup */
579 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
583 /* Check if we are now in range */
584 else if (strlen (icao) != 0 && connected == 0)
586 icao2number (icao, selected_frequency, num_buf);
587 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
588 do_iaxc_call (username, password, voipserver, num_buf);
594 strtoupper (const char *str, char *buf, size_t len)
597 for (i = 0; str[i] && i < len - 1; i++)
599 buf[i] = toupper (str[i]);
606 fatal_error (const char *err)
608 SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
615 iaxc_callback (iaxc_event e)
619 case IAXC_EVENT_LEVELS:
620 event_level (e.ev.levels.input, e.ev.levels.output);
622 case IAXC_EVENT_TEXT:
623 event_text (e.ev.text.type, e.ev.text.message);
625 case IAXC_EVENT_STATE:
626 event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
627 e.ev.call.local, e.ev.call.local_context);
629 case IAXC_EVENT_NETSTAT:
630 event_netstats (e.ev.netstats);
631 case IAXC_EVENT_REGISTRATION:
632 event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
635 event_unknown (e.type);
642 event_state (int state, char *remote, char *remote_name,
643 char *local, char *local_context)
646 /* This is needed for auto-reconnect */
650 /* FIXME: we should wake up the main thread somehow */
651 /* in fg mode the next incoming packet will do that anyway */
654 snprintf (rep_buf, MX_REPORT_BUF,
655 "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
656 delim, map_state (state), delim, remote, delim, remote_name,
657 delim, local, delim, local_context);
658 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
663 event_text (int type, char *message)
665 snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
666 SG_LOG( SG_GENERAL, SG_ALERT, message );
667 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
672 event_register (int id, int reply, int count)
677 case IAXC_REGISTRATION_REPLY_ACK:
680 case IAXC_REGISTRATION_REPLY_REJ:
682 if (strcmp (username, "guest") != 0)
684 SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
687 case IAXC_REGISTRATION_REPLY_TIMEOUT:
693 snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
694 reason, delim, count);
695 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
700 event_netstats (struct iaxc_ev_netstats stat)
702 struct iaxc_netstat local = stat.local;
703 struct iaxc_netstat remote = stat.remote;
704 snprintf (rep_buf, MX_REPORT_BUF,
705 "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",
706 delim, stat.callNo, delim, stat.rtt,
707 delim, local.jitter, delim, local.losspct, delim,
708 local.losscnt, delim, local.packets, delim, local.delay,
709 delim, local.dropped, delim, local.ooo, delim,
710 remote.jitter, delim, remote.losspct, delim, remote.losscnt,
711 delim, remote.packets, delim, remote.delay, delim,
712 remote.dropped, delim, remote.ooo);
713 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
718 event_level (double in, double out)
720 snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
721 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
726 map_state (int state)
735 for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
740 strcat (states, ",");
741 strcat (states, map[i]);
749 event_unknown (int type)
751 snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
752 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
761 SG_LOG( SG_GENERAL, SG_ALERT, text );
771 /* mic is muted so unmute and mute speaker */
772 iaxc_input_level_set (level_in);
773 if (!check_special_frq (selected_frequency))
775 iaxc_output_level_set (0.0);
776 SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
780 SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic" );
785 /* mic is unmuted so mute and unmute speaker */
786 iaxc_input_level_set (0.0);
787 if (!check_special_frq (selected_frequency))
789 iaxc_output_level_set (level_out);
790 SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
794 SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic" );
800 split (char *string, char *fields[], int nfields, const char *sep)
802 register char *p = string;
803 register char c; /* latest character */
804 register char sepc = sep[0];
807 register char **fp = fields;
808 register const char *sepp;
809 register int trimtrail;
813 while ((c = *p++) == ' ' || c == '\t')
817 sep = " \t"; /* note, code below knows this is 2 long */
822 sepc2 = sep[1]; /* now we can safely pick this up */
826 /* single separator */
836 while ((c = *p++) != sepc)
838 return (nfields - fn);
841 /* we have overflowed the fields vector -- just count them */
845 while ((c = *p++) != sepc)
861 while ((c = *p++) != sepc && c != sepc2)
864 if (trimtrail && **(fp - 1) == '\0')
866 return (nfields - fn);
871 while ((c = *p++) == sepc || c == sepc2)
875 /* we have overflowed the fields vector -- just count them */
879 while ((c = *p++) == sepc || c == sepc2)
883 while ((c = *p++) != '\0' && c != sepc && c != sepc2)
886 /* might have to trim trailing white space */
890 while ((c = *--p) == sepc || c == sepc2)
895 if (fn == nfields + 1)
916 while ((sepc = *sepp++) != '\0' && sepc != c)
918 if (sepc != '\0') /* it was a separator */
927 while ((sepc = *sepp++) != '\0' && sepc != c)
929 if (sepc == '\0') /* it wasn't a separator */
940 * \fn double *read_special_frequencies(const char *file)
942 * \brief Reads the file "special_frequencies.txt" if it exists.
943 * If no file exists, then no special frequencies are useable.
945 * \param file pointer on the filename.
947 * \return Returns the pointer on an array containing doubles if file
948 * has been successfully opened and read, otherwise returns NULL.
951 double *read_special_frequencies(const char *file)
953 double *l_pfrq = NULL;
959 if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
961 l_allocated += ALLOC_CHUNK_SIZE;
963 if(FGC_SUCCESS(parser_init(file)))
967 while(FGC_SUCCESS(parser_get_next_value(&l_value)))
969 if(l_count >= l_allocated)
971 l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
972 l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
973 l_allocated += ALLOC_CHUNK_SIZE;
976 l_pfrq[l_count] = l_value;
981 /* Last value of the array must be -1.0 which is the terminator. */
982 if(l_count == l_allocated)
983 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
984 l_pfrq[l_count] = -1.0;
986 // failed to open file
1000 read_airports (const char *file)
1004 struct airport airport_tmp;
1005 struct airport *first = NULL;
1006 struct airport *my_airport = NULL;
1007 struct airport *previous_airport = NULL;
1010 SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
1012 if ((fp = fopen (file, "rt")) == NULL)
1014 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1020 airport_tmp.next = NULL;
1021 while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1022 airport_tmp.icao, &airport_tmp.frequency,
1023 &airport_tmp.lat, &airport_tmp.lon,
1024 airport_tmp.type, airport_tmp.text)) == 6)
1028 (struct airport *) malloc (sizeof (struct airport))) == NULL)
1030 SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1037 memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1038 if (previous_airport != NULL)
1040 previous_airport->next = my_airport;
1042 previous_airport = my_airport;
1048 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1053 SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1058 report_devices (int in)
1060 struct iaxc_audio_device *devs; //audio devices
1061 int ndevs; //audio dedvice count
1062 int input, output, ring; //audio device id's
1064 int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1065 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1066 current = in ? input : output;
1067 snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1068 for (i = 0; i < ndevs; i++)
1070 if (devs[i].capabilities & flag && i != current)
1072 snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1075 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1076 if (strlen(rep_buf) >= MX_REPORT_BUF)
1079 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1084 set_device (const char *name, int out)
1086 struct iaxc_audio_device *devs; /* audio devices */
1087 int ndevs; /* audio dedvice count */
1088 int input, output, ring; /* audio device id's */
1090 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1091 for (i = 0; i < ndevs; i++)
1093 if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1094 strcmp (name, devs[i].name) == 0)
1098 output = devs[i].devID;
1102 input = devs[i].devID;
1104 fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1106 iaxc_audio_devices_set (input, output, ring);
1114 parse_fgdata (struct fgdata *data, char *buf)
1116 char *data_pair = NULL;
1120 SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1121 /* Parse data from FG */
1122 data_pair = strtok (buf, ",");
1123 while (data_pair != NULL)
1125 split (data_pair, fields, 2, "=");
1126 if (strcmp (fields[0], "COM1_FRQ") == 0)
1128 data->COM1_FRQ = atof (fields[1]);
1129 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1131 else if (strcmp (fields[0], "COM2_FRQ") == 0)
1133 data->COM2_FRQ = atof (fields[1]);
1134 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1136 else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1138 data->NAV1_FRQ = atof (fields[1]);
1139 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1141 else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1143 data->NAV2_FRQ = atof (fields[1]);
1144 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1146 else if (strcmp (fields[0], "PTT") == 0)
1148 data->PTT = atoi (fields[1]);
1149 SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1151 else if (strcmp (fields[0], "TRANSPONDER") == 0)
1153 data->TRANSPONDER = atoi (fields[1]);
1154 SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1156 else if (strcmp (fields[0], "IAS") == 0)
1158 data->IAS = atof (fields[1]);
1159 SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1161 else if (strcmp (fields[0], "GS") == 0)
1163 data->GS = atof (fields[1]);
1164 SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1166 else if (strcmp (fields[0], "LON") == 0)
1168 data->LON = atof (fields[1]);
1169 SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1171 else if (strcmp (fields[0], "LAT") == 0)
1173 data->LAT = atof (fields[1]);
1174 SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1176 else if (strcmp (fields[0], "ALT") == 0)
1178 data->ALT = atoi (fields[1]);
1179 SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1181 else if (strcmp (fields[0], "HEAD") == 0)
1183 data->HEAD = atof (fields[1]);
1184 SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1186 else if (strcmp (fields[0], "COM1_SRV") == 0)
1188 data->COM1_SRV = atoi (fields[1]);
1189 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1191 else if (strcmp (fields[0], "COM2_SRV") == 0)
1193 data->COM2_SRV = atoi (fields[1]);
1194 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1196 else if (strcmp (fields[0], "NAV1_SRV") == 0)
1198 data->NAV1_SRV = atoi (fields[1]);
1199 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1201 else if (strcmp (fields[0], "NAV2_SRV") == 0)
1203 data->NAV2_SRV = atoi (fields[1]);
1204 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1206 else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1208 data->OUTPUT_VOL = atof (fields[1]);
1209 SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1211 else if (strcmp (fields[0], "CALLSIGN") == 0)
1213 data->CALLSIGN = fields[1];
1214 SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1218 SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1221 data_pair = strtok (NULL, ",");
1223 SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1228 * \fn int check_special_frq (double frq)
1230 * \brief Check to see if specified frequency is a special frequency.
1232 * \param frq frequency to check against special frequencies
1234 * \return Returns 1 if successful, otherwise returns 0.
1237 int check_special_frq (double frq)
1240 frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1241 while (special_frequencies[i] >= 0.0)
1243 if (frq == special_frequencies[i])
1245 SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1254 do_iaxc_call (const char *username, const char *password,
1255 const char *voipserver, char *number)
1258 size_t len = strlen(number);
1260 if( strcmp(voipserver, "delta384.server4you.de") == 0 ) {
1261 if( number[len-1] == '5' ) {
1262 number[len-1] = '0';
1266 if( strcmp(number, "9990909090910000") == 0)
1267 number = (char *)"0190909090910000";
1269 snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1270 voipserver, number);
1272 iaxc_millisleep (DEFAULT_MILLISLEEP);
1275 /* eof - fgcom.cpp */