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 simgear::requestConsole();
279 sglog().setLogLevels( SG_ALL, SG_ALERT );
281 prog = strdup( base_name(argv[0]) );
284 SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
285 SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
286 SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
287 SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
288 SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
289 SG_LOG( SG_GENERAL, SG_ALERT, "" );
292 voipserver = DEFAULT_VOIP_SERVER;
293 fgserver = DEFAULT_FG_SERVER;
294 port = DEFAULT_FG_PORT;
295 username = DEFAULT_USER;
296 password = DEFAULT_PASSWORD;
297 codec_option = DEFAULT_CODEC;
298 mode = 0; // 0 = ATC mode, 1 = FG mode
299 positions_file = (char *) DEFAULT_POSITIONS_FILE;
300 frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
304 signal (SIGINT, quit);
305 signal (SIGQUIT, quit);
306 signal (SIGTERM, quit);
311 /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom
312 can be an offset to a jump table, making a compare of the current address to the address of
313 the actual imported function fail. So here ensure they are the same. */
314 iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
317 if (iaxc_initialize (DEFAULT_MAX_CALLS))
318 fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
323 fgcomInitOptions (fgcomOptionArray, argc, argv);
326 sglog().setLogLevels( SG_ALL, SG_DEBUG );
331 switch (codec_option)
334 codec = IAXC_FORMAT_ULAW;
337 codec = IAXC_FORMAT_ALAW;
340 codec = IAXC_FORMAT_GSM;
343 codec = IAXC_FORMAT_G726;
346 codec = IAXC_FORMAT_SPEEX;
354 strtoupper (airport_option, airport, sizeof (airport));
380 iaxc_mic_boost_set (1);
385 SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
386 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
388 SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
389 SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
398 set_device (audio_in, 0);
403 set_device (audio_out, 1);
406 /* checking consistency of arguments */
407 if (frequency > 0.0 && frequency < 1000.0)
409 if (strlen (airport) == 0 || strlen (airport) > 4)
411 strcpy (airport, "ZZZZ");
413 /* airport and frequency are given => ATC mode */
418 /* no airport => FG mode */
422 /* Read special frequencies file (if exists).
423 * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
424 * are used and are hard coded.
427 if (fix_input_files()) { /* adjust default input per OS */
428 fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
431 if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
432 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
433 SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
434 special_frequencies = special_frq;
436 SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
439 /* read airport frequencies and positions */
440 airportlist = read_airports (positions_file); /* never returns if fail! */
442 /* preconfigure iax */
443 SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
445 std::string app = "FGCOM-";
446 app += FGCOM_VERSION;
449 callsign = DEFAULT_USER;
451 iaxc_set_callerid ( callsign, app.c_str() );
452 iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
453 iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
454 iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
455 iaxc_set_silence_threshold(-20.0);
456 iaxc_set_event_callback (iaxc_callback);
458 iaxc_start_processing_thread ();
460 if (username && password && voipserver)
463 iaxc_register (const_cast < char *>(username),
464 const_cast < char *>(password),
465 const_cast < char *>(voipserver));
466 SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
470 SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
471 SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
476 iaxc_millisleep (DEFAULT_MILLISLEEP);
481 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
482 /* only in FG mode */
483 simgear::Socket::initSockets();
484 sgSocket.open (false);
485 sgSocket.bind (fgserver, port);
487 /* mute mic, speaker on */
488 iaxc_input_level_set (0);
489 iaxc_output_level_set (level_out);
491 SGTimeStamp sg_clock;
494 double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
495 /* get data from flightgear */
499 double sg_wait = sg_next_update - sg_clock.toSecs();
502 simgear::Socket *readSockets[2] = { &sgSocket, 0 };
503 if (sgSocket.select (readSockets, readSockets + 1,
504 (int) (sg_wait * 1000)) == 1)
506 simgear::IPAddress their_addr;
507 numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
513 pkt_buf[numbytes] = '\0';
514 SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
515 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
516 SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
517 process_packet (pkt_buf);
524 sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
530 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
531 /* mic on, speaker on */
532 iaxc_input_level_set (1.0);
533 iaxc_output_level_set (1.0);
535 icao2atisnumber (airport, frequency, num_buf);
536 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
537 do_iaxc_call (username, password, voipserver, num_buf);
542 SGTimeStamp::sleepForMSec(3600000);
546 /* should never be reached */
554 SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
559 iaxc_unregister (reg_id);
565 alarm_handler (int signal)
567 /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
568 if (check_special_frq (selected_frequency))
570 strcpy (icao, "ZZZZ");
575 icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
579 /* Check if we are out of range */
580 if (strlen (icao) == 0 && connected == 1)
582 /* Yes, we are out of range so hangup */
584 iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
588 /* Check if we are now in range */
589 else if (strlen (icao) != 0 && connected == 0)
591 icao2number (icao, selected_frequency, num_buf);
592 SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
593 do_iaxc_call (username, password, voipserver, num_buf);
599 strtoupper (const char *str, char *buf, size_t len)
602 for (i = 0; str[i] && i < len - 1; i++)
604 buf[i] = toupper (str[i]);
611 fatal_error (const char *err)
613 SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
620 iaxc_callback (iaxc_event e)
624 case IAXC_EVENT_LEVELS:
625 event_level (e.ev.levels.input, e.ev.levels.output);
627 case IAXC_EVENT_TEXT:
628 event_text (e.ev.text.type, e.ev.text.message);
630 case IAXC_EVENT_STATE:
631 event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
632 e.ev.call.local, e.ev.call.local_context);
634 case IAXC_EVENT_NETSTAT:
635 event_netstats (e.ev.netstats);
636 case IAXC_EVENT_REGISTRATION:
637 event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
640 event_unknown (e.type);
647 event_state (int state, char *remote, char *remote_name,
648 char *local, char *local_context)
651 /* This is needed for auto-reconnect */
655 /* FIXME: we should wake up the main thread somehow */
656 /* in fg mode the next incoming packet will do that anyway */
659 snprintf (rep_buf, MX_REPORT_BUF,
660 "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
661 delim, map_state (state), delim, remote, delim, remote_name,
662 delim, local, delim, local_context);
663 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
668 event_text (int type, char *message)
670 snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
671 SG_LOG( SG_GENERAL, SG_ALERT, message );
672 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
677 event_register (int id, int reply, int count)
682 case IAXC_REGISTRATION_REPLY_ACK:
685 case IAXC_REGISTRATION_REPLY_REJ:
687 if (strcmp (username, "guest") != 0)
689 SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
692 case IAXC_REGISTRATION_REPLY_TIMEOUT:
698 snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
699 reason, delim, count);
700 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
705 event_netstats (struct iaxc_ev_netstats stat)
707 struct iaxc_netstat local = stat.local;
708 struct iaxc_netstat remote = stat.remote;
709 snprintf (rep_buf, MX_REPORT_BUF,
710 "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",
711 delim, stat.callNo, delim, stat.rtt,
712 delim, local.jitter, delim, local.losspct, delim,
713 local.losscnt, delim, local.packets, delim, local.delay,
714 delim, local.dropped, delim, local.ooo, delim,
715 remote.jitter, delim, remote.losspct, delim, remote.losscnt,
716 delim, remote.packets, delim, remote.delay, delim,
717 remote.dropped, delim, remote.ooo);
718 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
723 event_level (double in, double out)
725 snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
726 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
731 map_state (int state)
740 for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
745 strcat (states, ",");
746 strcat (states, map[i]);
754 event_unknown (int type)
756 snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
757 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
766 SG_LOG( SG_GENERAL, SG_ALERT, text );
776 /* mic is muted so unmute and mute speaker */
777 iaxc_input_level_set (level_in);
778 iaxc_output_level_set (0.0);
779 SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
783 /* mic is unmuted so mute and unmute speaker */
784 iaxc_input_level_set (0.0);
785 iaxc_output_level_set (level_out);
786 SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
791 split (char *string, char *fields[], int nfields, const char *sep)
793 register char *p = string;
794 register char c; /* latest character */
795 register char sepc = sep[0];
798 register char **fp = fields;
799 register const char *sepp;
800 register int trimtrail;
804 while ((c = *p++) == ' ' || c == '\t')
808 sep = " \t"; /* note, code below knows this is 2 long */
813 sepc2 = sep[1]; /* now we can safely pick this up */
817 /* single separator */
827 while ((c = *p++) != sepc)
829 return (nfields - fn);
832 /* we have overflowed the fields vector -- just count them */
836 while ((c = *p++) != sepc)
852 while ((c = *p++) != sepc && c != sepc2)
855 if (trimtrail && **(fp - 1) == '\0')
857 return (nfields - fn);
862 while ((c = *p++) == sepc || c == sepc2)
866 /* we have overflowed the fields vector -- just count them */
870 while ((c = *p++) == sepc || c == sepc2)
874 while ((c = *p++) != '\0' && c != sepc && c != sepc2)
877 /* might have to trim trailing white space */
881 while ((c = *--p) == sepc || c == sepc2)
886 if (fn == nfields + 1)
907 while ((sepc = *sepp++) != '\0' && sepc != c)
909 if (sepc != '\0') /* it was a separator */
918 while ((sepc = *sepp++) != '\0' && sepc != c)
920 if (sepc == '\0') /* it wasn't a separator */
931 * \fn double *read_special_frequencies(const char *file)
933 * \brief Reads the file "special_frequencies.txt" if it exists.
934 * If no file exists, then no special frequencies are useable.
936 * \param file pointer on the filename.
938 * \return Returns the pointer on an array containing doubles if file
939 * has been successfully opened and read, otherwise returns NULL.
942 double *read_special_frequencies(const char *file)
944 double *l_pfrq = NULL;
950 if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
952 l_allocated += ALLOC_CHUNK_SIZE;
954 if(FGC_SUCCESS(parser_init(file)))
958 while(FGC_SUCCESS(parser_get_next_value(&l_value)))
960 if(l_count >= l_allocated)
962 l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
963 l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
964 l_allocated += ALLOC_CHUNK_SIZE;
967 l_pfrq[l_count] = l_value;
972 /* Last value of the array must be -1.0 which is the terminator. */
973 if(l_count == l_allocated)
974 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
975 l_pfrq[l_count] = -1.0;
977 // failed to open file
991 read_airports (const char *file)
995 struct airport airport_tmp;
996 struct airport *first = NULL;
997 struct airport *my_airport = NULL;
998 struct airport *previous_airport = NULL;
1001 SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
1003 if ((fp = fopen (file, "rt")) == NULL)
1005 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1011 airport_tmp.next = NULL;
1012 while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1013 airport_tmp.icao, &airport_tmp.frequency,
1014 &airport_tmp.lat, &airport_tmp.lon,
1015 airport_tmp.type, airport_tmp.text)) == 6)
1019 (struct airport *) malloc (sizeof (struct airport))) == NULL)
1021 SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1028 memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1029 if (previous_airport != NULL)
1031 previous_airport->next = my_airport;
1033 previous_airport = my_airport;
1039 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1044 SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1049 report_devices (int in)
1051 struct iaxc_audio_device *devs; //audio devices
1052 int ndevs; //audio dedvice count
1053 int input, output, ring; //audio device id's
1055 int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1056 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1057 current = in ? input : output;
1058 snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1059 for (i = 0; i < ndevs; i++)
1061 if (devs[i].capabilities & flag && i != current)
1063 snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1066 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1067 if (strlen(rep_buf) >= MX_REPORT_BUF)
1070 rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1075 set_device (const char *name, int out)
1077 struct iaxc_audio_device *devs; /* audio devices */
1078 int ndevs; /* audio dedvice count */
1079 int input, output, ring; /* audio device id's */
1081 iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1082 for (i = 0; i < ndevs; i++)
1084 if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1085 strcmp (name, devs[i].name) == 0)
1089 output = devs[i].devID;
1093 input = devs[i].devID;
1095 fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1097 iaxc_audio_devices_set (input, output, ring);
1105 parse_fgdata (struct fgdata *data, char *buf)
1107 char *data_pair = NULL;
1111 SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1112 /* Parse data from FG */
1113 data_pair = strtok (buf, ",");
1114 while (data_pair != NULL)
1116 split (data_pair, fields, 2, "=");
1117 if (strcmp (fields[0], "COM1_FRQ") == 0)
1119 data->COM1_FRQ = atof (fields[1]);
1120 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1122 else if (strcmp (fields[0], "COM2_FRQ") == 0)
1124 data->COM2_FRQ = atof (fields[1]);
1125 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1127 else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1129 data->NAV1_FRQ = atof (fields[1]);
1130 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1132 else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1134 data->NAV2_FRQ = atof (fields[1]);
1135 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1137 else if (strcmp (fields[0], "PTT") == 0)
1139 data->PTT = atoi (fields[1]);
1140 SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1142 else if (strcmp (fields[0], "TRANSPONDER") == 0)
1144 data->TRANSPONDER = atoi (fields[1]);
1145 SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1147 else if (strcmp (fields[0], "IAS") == 0)
1149 data->IAS = atof (fields[1]);
1150 SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1152 else if (strcmp (fields[0], "GS") == 0)
1154 data->GS = atof (fields[1]);
1155 SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1157 else if (strcmp (fields[0], "LON") == 0)
1159 data->LON = atof (fields[1]);
1160 SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1162 else if (strcmp (fields[0], "LAT") == 0)
1164 data->LAT = atof (fields[1]);
1165 SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1167 else if (strcmp (fields[0], "ALT") == 0)
1169 data->ALT = atoi (fields[1]);
1170 SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1172 else if (strcmp (fields[0], "HEAD") == 0)
1174 data->HEAD = atof (fields[1]);
1175 SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1177 else if (strcmp (fields[0], "COM1_SRV") == 0)
1179 data->COM1_SRV = atoi (fields[1]);
1180 SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1182 else if (strcmp (fields[0], "COM2_SRV") == 0)
1184 data->COM2_SRV = atoi (fields[1]);
1185 SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1187 else if (strcmp (fields[0], "NAV1_SRV") == 0)
1189 data->NAV1_SRV = atoi (fields[1]);
1190 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1192 else if (strcmp (fields[0], "NAV2_SRV") == 0)
1194 data->NAV2_SRV = atoi (fields[1]);
1195 SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1197 else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1199 data->OUTPUT_VOL = atof (fields[1]);
1200 SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1202 else if (strcmp (fields[0], "CALLSIGN") == 0)
1204 data->CALLSIGN = fields[1];
1205 SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1209 SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1212 data_pair = strtok (NULL, ",");
1214 SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1219 * \fn int check_special_frq (double frq)
1221 * \brief Check to see if specified frequency is a special frequency.
1223 * \param frq frequency to check against special frequencies
1225 * \return Returns 1 if successful, otherwise returns 0.
1228 int check_special_frq (double frq)
1231 frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1232 while (special_frequencies[i] >= 0.0)
1234 if (frq == special_frequencies[i])
1236 SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1245 do_iaxc_call (const char *username, const char *password,
1246 const char *voipserver, char *number)
1249 size_t len = strlen(number);
1251 if( strcmp(voipserver, "delta384.server4you.de") == 0 ) {
1252 if( number[len-1] == '5' ) {
1253 number[len-1] = '0';
1257 if( strcmp(number, "9990909090910000") == 0)
1258 number = (char *)"0190909090910000";
1260 snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1261 voipserver, number);
1263 iaxc_millisleep (DEFAULT_MILLISLEEP);
1266 /* eof - fgcom.cpp */