/* freebeacon.c David Rowe VK5DGR Hamlib PTT and 700C support Bob VK4YA 700E Alan VK2ZIW FreeDV Beacon. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #endif #include #include #include "sndfile.h" #include "portaudio.h" #include "codec2_fifo.h" #include "modem_stats.h" #include "freedv_api.h" #include "hamlib/rig.h" #define MAX_CHAR 80 #define FS8 8000 // codec audio sample rate fixed at 8 kHz #define FS48 48000 // 48 kHz sampling rate rec. as we can trust accuracy of sound card #define SYNC_TIMER 2.0 // seconds of valid rx sync we need to see to change state #define UNSYNC_TIMER 2.0 // seconds of lost sync we need to see to change state #define COM_HANDLE_INVALID -1 #define LOG_TIMER 1.0 #define SNR_MIN 3.0 // 700E, sync is problematic without SQ enabled #define SQ_LEVEL 1.0 /* globals used to communicate with async events and callback functions */ volatile int keepRunning, runListener, recordAny; char txtMsg[MAX_CHAR], *ptxtMsg, triggerString[MAX_CHAR]; int triggered; float snr_est, snr_sample; int com_handle, verbose; /* state machine defines */ #define SRX_IDLE 0 /* listening but no FreeDV signal */ #define SRX_MAYBE_SYNC 1 /* We have sync but lets see if it goes away */ #define SRX_SYNC 2 /* We have sync on a valid FreeDV signal */ #define SRX_MAYBE_UNSYNC 3 /* We have lost sync but lets see if it's really gone */ #define STX 4 /* transmitting reply */ char *state_str[] = { "Rx Idle", "Rx Maybe Sync", "Rx Sync", "Rx Maybe UnSync", "Tx" }; int openComPort(const char *name); void closeComPort(void); void raiseDTR(void); void lowerDTR(void); void raiseRTS(void); void lowerRTS(void); pthread_t start_udp_listener_thread(void); /* hamlib static vars */ hamlib_port_t myport; RIG *my_rig; rig_model_t myrig_model; // int int retcode; /*--------------------------------------------------------------------------------------------------------*\ FUNCTIONS \*--------------------------------------------------------------------------------------------------------*/ /* Called on Ctrl-C */ void intHandler(int dummy) { keepRunning = 0; fprintf(stderr,"\nShutting Down ......\n"); } /* returns number of output samples generated by resampling */ int resample(SRC_STATE *src, short output_short[], short input_short[], int output_sample_rate, int input_sample_rate, int length_output_short, // maximum output array length in samples int length_input_short ) { SRC_DATA src_data; float input[length_input_short]; float output[length_output_short]; assert(src != NULL); src_short_to_float_array(input_short, input, length_input_short); src_data.data_in = input; src_data.data_out = output; src_data.input_frames = length_input_short; src_data.output_frames = length_output_short; src_data.end_of_input = 0; src_data.src_ratio = (float)output_sample_rate/input_sample_rate; //printf("%d %d src_ratio: %f \n", length_input_short, length_output_short, src_data.src_ratio); src_process(src, &src_data); assert(src_data.output_frames_gen <= length_output_short); src_float_to_short_array(output, output_short, src_data.output_frames_gen); return src_data.output_frames_gen; } void listAudioDevices(void) { const PaDeviceInfo *deviceInfo = NULL; int numDevices, devn; numDevices = Pa_GetDeviceCount(); printf("Num Name API InCh OutCh DefFs\n"); printf("====================================================================================\n"); for (devn = 0; devnname, Pa_GetHostApiInfo(deviceInfo->hostApi)->name, deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels, (int)deviceInfo->defaultSampleRate); } } void printHelp(const struct option* long_options, int num_opts, char* argv[]) { int i; char *option_parameters = NULL; fprintf(stderr, "\nFreeBeacon - FreeDV Beacon\n" "usage: %s [OPTIONS]\n\n" "Options:\n" "\t-b (Enable beacon)\n" "\t-c (comm port for Tx or CAT PTT)\n" "\t-r (Hamlib CAT model number [use rigctl -l to see list])\n" "\t-l --list (audio devices)\n" "\t-m --mode 1600|700C|700D|700E\n" "\t-t (tx on start up, useful for testing)\n" "\t-u (Start UDP listener)\n" "\t-v (verbose)\n", argv[0]); for(i=0; i= MAX_CHAR)) ptxtMsg = txtMsg; //fprintf(stderr, "TX txtMsg: %d %c\n", (int)*ptxtMsg, *ptxtMsg); return *ptxtMsg++; } SNDFILE *openPlayFile(char fileName[], int *sfFs) { SF_INFO sfInfo; SNDFILE *sfPlayFile; sfInfo.format = 0; sfPlayFile = sf_open(fileName, SFM_READ, &sfInfo); if(sfPlayFile == NULL) { const char *strErr = sf_strerror(NULL); fprintf(stderr, " %s Couldn't open: %s\n", strErr, fileName); } *sfFs = sfInfo.samplerate; return sfPlayFile; } SNDFILE *openRecFile(char fileName[], int sfFs) { SF_INFO sfInfo; SNDFILE *sfRecFile; sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; sfInfo.channels = 1; sfInfo.samplerate = sfFs; sfRecFile = sf_open(fileName, SFM_WRITE, &sfInfo); if(sfRecFile == NULL) { const char *strErr = sf_strerror(NULL); fprintf(stderr, " %s Couldn't open: %s\n", strErr, fileName); } return sfRecFile; } /* Use the Linux /sys/class/gpio system to access the RPis GPIOs */ void sys_gpio(char filename[], char s[]) { FILE *fgpio = fopen(filename, "wt"); //fprintf(stderr,"%s %s\n",filename, s); if (fgpio == NULL) { fprintf(stderr, "\nProblem opening %s\n", filename); exit(1); } fprintf(fgpio,"%s",s); fclose(fgpio); } void getTimeStr(char timeStr[]) { time_t ltime; /* calendar time */ struct tm *loctime; ltime=time(NULL); /* get current cal time */ loctime = localtime (<ime); strftime(timeStr, MAX_CHAR, "%F-%H-%M-%S",loctime); } void hamlib_ptt_on() { printf("Freebeacon: Setting rig PTT ON.\n"); retcode = rig_set_ptt(my_rig, RIG_VFO_A, RIG_PTT_ON); if (retcode != RIG_OK) { printf("rig_set_ptt: error = %s \n", rigerror(retcode)); } } void hamlib_ptt_off() { printf("Freebeacon: Setting rig PTT OFF.\n"); retcode = rig_set_ptt(my_rig, RIG_VFO_A, RIG_PTT_OFF); if (retcode != RIG_OK) { printf("rig_set_ptt: error = %s \n", rigerror(retcode)); } } int hamlib_init(rig_model_t myrig_model, char *commport) { // rig_load_all_backends(); // rig_set_debug(RIG_DEBUG_VERBOSE); // rig_set_debug(RIG_DEBUG_TRACE); rig_set_debug(RIG_DEBUG_ERR); fprintf(stderr,"Freebeacon: Calling Rig Init\n"); my_rig = rig_init(myrig_model); if (!my_rig) { fprintf(stderr,"Freebeacon: Hamlib Rig Init FAILED\n"); return(1); } strncpy(my_rig->state.rigport.pathname, commport, FILPATHLEN - 1); retcode = rig_open(my_rig); if (retcode != RIG_OK) { fprintf(stderr,"Freebeacon: Hamlib rig_open: error = %s\n", rigerror(retcode)); return(2); } fprintf(stderr,"Freebeacon: Hamlib Rig opened okay\n"); return(0); } /*--------------------------------------------------------------------------------------------------------* \ MAIN \*--------------------------------------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { struct freedv *f2; PaError err; PaStreamParameters inputParameters, outputParameters; const PaDeviceInfo *deviceInfo = NULL; PaStream *stream = NULL; int j, src_error, inputChannels, nin, devNum; int outputChannels; int state, next_state; SRC_STATE *rxsrc, *txsrc; SRC_STATE *playsrc; struct FIFO *fifo; char txFileName[MAX_CHAR]; SNDFILE *sfPlayFile, *sfRecFileFromRadio, *sfRecFileDecAudio; int sfFs; int fssc; int triggerf, txfilenamef, callsignf, sampleratef, wavefilepathf, rpigpiof, rpigpioalivef; int statuspagef; int sync, haveRecording, beaconTimer; char commport[MAX_CHAR]; char callsign[MAX_CHAR]; //FILE *ftmp; float syncTimer, logTimer; unsigned int tnout=0,mnout; short peak = 0; char waveFileWritePath[MAX_CHAR]; char rpigpio[MAX_CHAR], rpigpio_path[MAX_CHAR], rpigpioalive[MAX_CHAR], rpigpioalive_path[MAX_CHAR]; char statusPageFileName[MAX_CHAR]; FILE *fstatus; int gpioAliveState = 0; int freedv_mode; int ident_en, udp_en; struct MODEM_STATS g_stats; /* debug raw file */ //ftmp = fopen("t.raw", "wb"); //assert(ftmp != NULL); /* Defaults -------------------------------------------------------------------------------*/ devNum = 0; fssc = FS48; sprintf(triggerString, "FreeBeacon"); sprintf(txFileName, "txaudio.wav"); sprintf(callsign, "PARROT"); verbose = 0; com_handle = COM_HANDLE_INVALID; mnout = 60*FS8; state = SRX_IDLE; *txtMsg = 0; sfRecFileFromRadio = NULL; sfRecFileDecAudio = NULL; sfPlayFile = NULL; strcpy(waveFileWritePath, "."); *rpigpio = 0; *rpigpioalive = 0; *statusPageFileName = 0; freedv_mode = FREEDV_MODE_700E; recordAny = 1; myrig_model = 0; my_rig = NULL; beaconTimer = 0; ident_en = 1; udp_en = 0; if (Pa_Initialize()) { fprintf(stderr, "Port Audio failed to initialize"); exit(1); } /* Process command line options -----------------------------------------------------------*/ char* opt_string = "hlvc:u:tm:"; struct option long_options[] = { { "dev", required_argument, &devNum, 1 }, { "trigger", required_argument, &triggerf, 1 }, { "txfilename", required_argument, &txfilenamef, 1 }, { "callsign", required_argument, &callsignf, 1 }, { "samplerate", required_argument, &sampleratef, 1 }, { "wavefilewritepath", required_argument, &wavefilepathf, 1 }, { "statuspagefile", required_argument, &statuspagef, 1 }, { "rpigpio", required_argument, &rpigpiof, 1 }, { "rpigpioalive", required_argument, &rpigpioalivef, 1 }, { "list", no_argument, NULL, 'l' }, { "mode", required_argument, NULL, 'm'}, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; int num_opts=sizeof(long_options)/sizeof(struct option); while(1) { int option_index = 0; int opt = getopt_long(argc, argv, opt_string, long_options, &option_index); if (opt == -1) break; switch (opt) { case 0: if (strcmp(long_options[option_index].name, "dev") == 0) { devNum = atoi(optarg); } else if(strcmp(long_options[option_index].name, "trigger") == 0) { strcpy(triggerString, optarg); } else if(strcmp(long_options[option_index].name, "txfilename") == 0) { strcpy(txFileName, optarg); } else if(strcmp(long_options[option_index].name, "callsign") == 0) { strcpy(callsign, optarg); } else if (strcmp(long_options[option_index].name, "samplerate") == 0) { fssc = atoi(optarg); } else if (strcmp(long_options[option_index].name, "wavefilewritepath") == 0) { strcpy(waveFileWritePath, optarg); } else if (strcmp(long_options[option_index].name, "statuspagefile") == 0) { strcpy(statusPageFileName, optarg); } else if (strcmp(long_options[option_index].name, "rpigpio") == 0) { strcpy(rpigpio, optarg); sys_gpio("/sys/class/gpio/unexport", rpigpio); sys_gpio("/sys/class/gpio/export", rpigpio); usleep(100*1000); /* short delay so OS can create the next device */ char tmp[MAX_CHAR]; sprintf(tmp,"/sys/class/gpio/gpio%s/direction", rpigpio); sys_gpio(tmp, "out"); sprintf(rpigpio_path,"/sys/class/gpio/gpio%s/value", rpigpio); sys_gpio(rpigpio_path, "0"); } else if (strcmp(long_options[option_index].name, "rpigpioalive") == 0) { strcpy(rpigpioalive, optarg); sys_gpio("/sys/class/gpio/unexport", rpigpioalive); sys_gpio("/sys/class/gpio/export", rpigpioalive); usleep(100*1000); /* short delay so OS can create the next device */ char tmp[MAX_CHAR]; sprintf(tmp,"/sys/class/gpio/gpio%s/direction", rpigpioalive); sys_gpio(tmp, "out"); sprintf(rpigpioalive_path,"/sys/class/gpio/gpio%s/value", rpigpioalive); sys_gpio(rpigpioalive_path, "0"); } break; case 'b': ident_en = 0; break; case 'c': strcpy(commport, optarg); if (openComPort(commport) != 0) { fprintf(stderr, "Can't open comm port: %s\n",commport); exit(1); } break; case 'r': // hamlib CAT rig myrig_model = atoi(optarg); // rig number not its name for now if ((myrig_model == 0 ) || (com_handle == COM_HANDLE_INVALID)) { fprintf(stderr,"No Comm port? use 'c' option before 'u' option\n"); fprintf(stderr,"Rig Hamlib model numbers found use rigctl -l to see list\n"); exit(1); } closeComPort(); // let hamlib use it now fprintf(stderr,"Freebeacon: Opening Hamlib with model %d\n",myrig_model); if (hamlib_init(myrig_model, commport)) { fprintf(stderr,"Hamlib failed to initilise: [use rigctl -l to see list]\n"); exit(1); } break; case 'h': printHelp(long_options, num_opts, argv); break; case 'u': udp_en = 1; break; case 'v': verbose = 1; break; case 't': sprintf(txtMsg,"tx Test"); state = STX; break; case 'l': listAudioDevices(); exit(0); break; case 'm': if (strcmp(optarg, "1600") == 0) freedv_mode = FREEDV_MODE_1600; else if (strcmp(optarg, "700C") == 0) { fprintf(stderr, "700C doesn't support text, so there is no trigger word. " " So we just record the received file every time we get sync"); recordAny = 1; freedv_mode = FREEDV_MODE_700C; } else if (strcmp(optarg, "700D") == 0) freedv_mode = FREEDV_MODE_700D; else if (strcmp(optarg, "700E") == 0) freedv_mode = FREEDV_MODE_700E; else { fprintf(stderr, "Unknown mode: %s\n", optarg); exit(1); } break; default: /* This will never be reached */ break; } } /* Open Sound Device and start processing --------------------------------------------------------------*/ if ((freedv_mode == FREEDV_MODE_700D) || (freedv_mode == FREEDV_MODE_700E) || (freedv_mode == FREEDV_MODE_2020)) { // 700D and 700E have some init time stuff so treat it special struct freedv_advanced adv; adv.interleave_frames = 1; f2 = freedv_open_advanced(freedv_mode, &adv); freedv_set_sync(f2, FREEDV_SYNC_AUTO); freedv_set_eq(f2, 0); freedv_set_clip(f2, 1); freedv_set_tx_bpf(f2, 1); } else { f2 = freedv_open(freedv_mode); } assert(f2 != NULL); if ((freedv_mode == FREEDV_MODE_700D) || (freedv_mode == FREEDV_MODE_2020)) { freedv_set_phase_est_bandwidth_mode(f2, 0); freedv_set_dpsk(f2, 0); } freedv_set_squelch_en(f2, 1); freedv_set_snr_squelch_thresh(f2, SQ_LEVEL); int fsm = freedv_get_modem_sample_rate(f2); /* modem sample rate */ int n8m = freedv_get_n_nom_modem_samples(f2); /* nominal modem sample buffer size at fsm sample rate */ int n48 = n8m*fssc/fsm; /* nominal modem sample buffer size at 48kHz */ float dT = (float)n48/fssc; /* period of each sound buffer */ if (verbose) fprintf(stderr, "fsm: %d n8m: %d n48: %d\n", fsm, n8m, n48); short stereo[2*n48]; /* stereo I/O buffer from port audio */ short rx48k[n48], tx48k[n48]; /* signals at 48 kHz */ short rxfsm[n48]; /* rx signal at modem sample rate */ freedv_set_callback_txt(f2, callbackNextRxChar, callbackNextTxChar, NULL); fifo = codec2_fifo_create(4*n8m); assert(fifo != NULL); /* fifo to smooth out variation in demod nin */ /* states for sample rate converters */ rxsrc = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(rxsrc != NULL); txsrc = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(txsrc != NULL); playsrc = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(playsrc != NULL); /* Open Port Audio device and set up config structures -----------------------------------------------------*/ deviceInfo = Pa_GetDeviceInfo(devNum); if (deviceInfo == NULL) { fprintf(stderr, "Couldn't get device info from Port Audio for device: %d\n", devNum); exit(1); } if (deviceInfo->maxInputChannels == 1) inputChannels = 1; else inputChannels = 2; /* input device */ inputParameters.device = devNum; inputParameters.channelCount = inputChannels; inputParameters.sampleFormat = paInt16; inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; /* output device */ if (deviceInfo->maxOutputChannels == 1) outputChannels = 1; else outputChannels = 2; outputParameters.device = devNum; outputParameters.channelCount = outputChannels; outputParameters.sampleFormat = paInt16; outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; /* open port audio for full duplex operation */ err = Pa_OpenStream( &stream, &inputParameters, &outputParameters, fssc, n48, /* changed from 0 to n48 to get Rpi audio to work without clicks */ paClipOff, NULL, /* no callback, use blocking API */ NULL ); if (err != paNoError) { fprintf(stderr, "Couldn't initialise sound device\n"); exit(1); } err = Pa_StartStream(stream); if (err != paNoError) { fprintf(stderr, "Couldn't start sound device\n"); exit(1); } /* Start UDP listener thread */ if (udp_en) start_udp_listener_thread(); /* Init for main loop ----------------------------------------------------------------------------*/ fprintf(stderr, "\nCtrl-C to exit\n"); fprintf(stderr, "freedv_mode: %d\n", freedv_mode); fprintf(stderr, "trigger string: %s\ntxFileName: %s\n", triggerString, txFileName); fprintf(stderr, "PortAudio devNum: %d\nsamplerate: %d\n", devNum, fssc); fprintf(stderr, "WaveFileWritePath: %s\n", waveFileWritePath); fprintf(stderr, "statusPageFile: %s\n", statusPageFileName); if (com_handle != COM_HANDLE_INVALID) { fprintf(stderr, "Comm Port for PTT: %s\n", commport); } if (*rpigpio) { fprintf(stderr, "Raspberry Pi BCM GPIO for PTT: %s\n", rpigpio); } if (*rpigpioalive) { fprintf(stderr, "Raspberry Pi BCM GPIO for Alive Indicator: %s\n", rpigpioalive); } signal(SIGINT, intHandler); /* ctrl-C to exit gracefully */ keepRunning = 1; ptxtMsg = txtMsg; triggered = 0; logTimer = 0; syncTimer = 0; haveRecording = 0; if (com_handle != COM_HANDLE_INVALID) { lowerRTS(); lowerDTR(); } /* -t flag: we are leaping straight into TX */ if (state == STX) { if (com_handle != COM_HANDLE_INVALID) { raiseRTS(); raiseDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "1"); } if (my_rig) hamlib_ptt_on(); sfPlayFile = openPlayFile(txFileName, &sfFs); } modem_stats_open(&g_stats); nin = freedv_get_n_max_speech_samples(f2); /* Main loop -------------------------------------------------------------------------------------*/ while(keepRunning) { if (state != STX) { short demod_in[freedv_get_n_max_modem_samples(f2)]; short speech_out[freedv_get_n_speech_samples(f2)]; COMP rx_fdm[freedv_get_n_max_modem_samples(f2)]; /* Read samples from sound card, resample to modem sample rate */ Pa_ReadStream(stream, stereo, n48); if (inputChannels == 2) { for(j=0; j peak) peak = rxfsm[j]; codec2_fifo_write(fifo, rxfsm, n8m_out); /* demodulate to decoded speech samples */ memset(speech_out, 0, sizeof(short)*freedv_get_n_speech_samples(f2)); while (codec2_fifo_read(fifo, demod_in, nin) == 0) { int nout = 0; for(int i=0; i SNR_MIN)) sync++; if (sfRecFileFromRadio) sf_write_short(sfRecFileFromRadio, demod_in, nin); nin = freedv_nin(f2); if (sfRecFileDecAudio) sf_write_short(sfRecFileDecAudio, speech_out, nout); tnout += nout; if (tnout > mnout) { sf_close(sfRecFileFromRadio); sf_close(sfRecFileDecAudio); } } } if (state == STX) { short mod_out[freedv_get_n_max_modem_samples(f2)]; short speech_in[freedv_get_n_speech_samples(f2)]; if (sfPlayFile != NULL) { /* resample input sound file as can't guarantee 8KHz sample rate */ unsigned int nsf = freedv_get_n_speech_samples(f2)*sfFs/FS8; short insf[nsf]; unsigned int n = sf_read_short(sfPlayFile, insf, nsf); resample(playsrc, speech_in, insf, FS8, sfFs, freedv_get_n_speech_samples(f2), nsf); //fwrite(speech_in, sizeof(short), freedv_get_n_nom_modem_samples(f2), ftmp); if (n != nsf) { /* end of file - this signals state machine we've finished */ sf_close(sfPlayFile); sfPlayFile = NULL; beaconTimer = 0; } } freedv_tx(f2, mod_out, speech_in); //fwrite(mod_out, sizeof(short), freedv_get_n_nom_modem_samples(f2), ftmp); int n48_out = resample(txsrc, tx48k, mod_out, fssc, fsm, n48, n8m); //printf("n48_out: %d n48: %d n_nom: %d\n", n48_out, n48, n8m); //fwrite(tx48k, sizeof(short), n48_out, ftmp); for(j=0; j 0) { next_state = SRX_MAYBE_SYNC; syncTimer = 0.0; *txtMsg = 0; ptxtMsg = txtMsg; freedv_set_total_bit_errors(f2, 0); freedv_set_total_bits(f2, 0); } break; case SRX_MAYBE_SYNC: if (sync > 0) { syncTimer += dT; if (syncTimer >= SYNC_TIMER) { /* OK we really are in sync */ next_state = SRX_SYNC; } } else { next_state = SRX_IDLE; triggered = 0; haveRecording = 0; } break; case SRX_SYNC: syncTimer += dT; if (!sync) { syncTimer = 0; next_state = SRX_MAYBE_UNSYNC; } /* if triggered kick off recording of two files */ if ((triggered || recordAny) && !haveRecording) { char timeStr[MAX_CHAR]; char recFileFromRadioName[MAX_CHAR], recFileDecAudioName[MAX_CHAR]; getTimeStr(timeStr); sprintf(recFileFromRadioName,"%s/%s_from_radio.wav", waveFileWritePath, timeStr); sprintf(recFileDecAudioName,"%s/%s_decoded_speech.wav", waveFileWritePath, timeStr); sfRecFileFromRadio = openRecFile(recFileFromRadioName, fsm); sfRecFileDecAudio = openRecFile(recFileDecAudioName, FS8); haveRecording = 1; if (freedv_mode != FREEDV_MODE_700C) { recordAny = 0; } tnout = 0; } break; case SRX_MAYBE_UNSYNC: if (!sync) { syncTimer += dT; if (syncTimer >= UNSYNC_TIMER) { /* we really are out of sync */ /* finish up any open recording files */ if (sfRecFileFromRadio) sf_close(sfRecFileFromRadio); if (sfRecFileDecAudio) sf_close(sfRecFileDecAudio); /* kick off a tx if triggered */ if (triggered) { float ber = (float)freedv_get_total_bit_errors(f2)/freedv_get_total_bits(f2); char tmpStr[MAX_CHAR]; sprintf(tmpStr, "SNR: %3.1f BER: %4.3f de %s\r", snr_sample, ber, callsign); strcpy(txtMsg, tmpStr); //fprintf(stderr, "TX txtMsg: %s\n", txtMsg); ptxtMsg = txtMsg; sfPlayFile = openPlayFile(txFileName, &sfFs); if (com_handle != COM_HANDLE_INVALID) { raiseRTS(); raiseDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "1"); } if (my_rig) hamlib_ptt_on(); next_state = STX; } else { next_state = SRX_IDLE; triggered = 0; haveRecording = 0; } } } else next_state = SRX_SYNC; /* sync is back so false alarm */ break; case STX: if (sfPlayFile == NULL) { if (com_handle != COM_HANDLE_INVALID) { lowerRTS(); lowerDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "0"); } if (my_rig) hamlib_ptt_off(); next_state = SRX_IDLE; triggered = 0; haveRecording = 0; } break; } /* end switch statement for case statement */ logTimer += dT; if (logTimer >= LOG_TIMER) { logTimer = 0; if (verbose) { fprintf(stderr, "state: %-20s peak: %6d sync: %d SNR: %3.1f triggered: %d recordany: %d\n", state_str[state], peak, sync, snr_est, triggered, recordAny); } if (*statusPageFileName) { char timeStr[MAX_CHAR]; time_t ltime; /* calendar time */ ltime=time(NULL); /* get current cal time */ sprintf(timeStr, "%s",asctime( localtime(<ime) ) ); strtok(timeStr, "\n"); fstatus = fopen(statusPageFileName, "wt"); if (fstatus != NULL) { fprintf(fstatus, "\n\n\n\n\n"); fprintf(fstatus, "%s: state: %s peak: %d sync: %d SNR: %3.1f triggered: %d recordany: %d txtMsg: %s\n", timeStr, state_str[state], peak, sync, snr_est, triggered, recordAny, txtMsg); fprintf(fstatus, "\n\n"); fclose(fstatus); } } /* toggle alive GPIO */ if (*rpigpioalive) { if (gpioAliveState) { gpioAliveState = 0; sys_gpio(rpigpioalive_path, "0"); } else { gpioAliveState = 1; sys_gpio(rpigpioalive_path, "1"); } } if ( !sync) { if ((beaconTimer++ > 250) && ident_en) { beaconTimer = 0; triggered = 1; sprintf(txtMsg, "700E Test by %s\r", callsign); ptxtMsg = txtMsg; if (sfPlayFile == NULL) sfPlayFile = openPlayFile(txFileName, &sfFs); if (com_handle != COM_HANDLE_INVALID) { raiseRTS(); raiseDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "1"); } if (my_rig) hamlib_ptt_on(); next_state = STX; } } } // end logTimer state = next_state; } /* end while loop */ /* Ctrl-C has been pressed lets shut down gracefully ------------------*/ /* lower PTT lines, shut down ports */ if (com_handle != COM_HANDLE_INVALID) { lowerRTS(); lowerDTR(); closeComPort(); } if (*rpigpio) { sys_gpio(rpigpio_path, "0"); sys_gpio("/sys/class/gpio/unexport", rpigpio); } if (*rpigpioalive) { sys_gpio(rpigpioalive_path, "0"); sys_gpio("/sys/class/gpio/unexport", rpigpioalive); } if (my_rig) { hamlib_ptt_off(); rig_close(my_rig); rig_cleanup(my_rig); } /* Shut down port audio */ err = Pa_StopStream(stream); if (err != paNoError) { fprintf(stderr, "Couldn't stop sound device\n"); exit(1); } Pa_CloseStream(stream); Pa_Terminate(); /* clean up states */ codec2_fifo_destroy(fifo); src_delete(rxsrc); src_delete(txsrc); src_delete(playsrc); freedv_close(f2); //fclose(ftmp); return 0; } /*--------------------------------------------------------------------------------------------------------*\ Comm port fuctions lifted from FreeDV \*--------------------------------------------------------------------------------------------------------*/ //---------------------------------------------------------------- // openComPort() opens the com port specified by the string // ie: "/dev/ttyUSB0" //---------------------------------------------------------------- int openComPort(const char *name) { if(com_handle != COM_HANDLE_INVALID) closeComPort(); #ifdef _WIN32 { COMMCONFIG CC; DWORD CCsize=sizeof(CC); COMMTIMEOUTS timeouts; DCB dcb; if(GetDefaultCommConfigA(name, &CC, &CCsize)) { CC.dcb.fOutxCtsFlow = FALSE; CC.dcb.fOutxDsrFlow = FALSE; CC.dcb.fDtrControl = DTR_CONTROL_DISABLE; CC.dcb.fDsrSensitivity = FALSE; CC.dcb.fRtsControl = RTS_CONTROL_DISABLE; SetDefaultCommConfigA(name, &CC, CCsize); } if((com_handle=CreateFileA(name ,GENERIC_READ|GENERIC_WRITE /* Access */ ,0 /* Share mode */ ,NULL /* Security attributes */ ,OPEN_EXISTING /* Create access */ ,FILE_ATTRIBUTE_NORMAL /* File attributes */ ,NULL /* Template */ ))==INVALID_HANDLE_VALUE) return false; if(GetCommTimeouts(com_handle, &timeouts)) { timeouts.ReadIntervalTimeout=MAXDWORD; timeouts.ReadTotalTimeoutMultiplier=0; timeouts.ReadTotalTimeoutConstant=0; // No-wait read timeout timeouts.WriteTotalTimeoutMultiplier=0; timeouts.WriteTotalTimeoutConstant=5000; // 5 seconds SetCommTimeouts(com_handle,&timeouts); } /* Force N-8-1 mode: */ if(GetCommState(com_handle, &dcb)==TRUE) { dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.DCBlength = sizeof(DCB); dcb.fBinary = TRUE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_DISABLE; dcb.fDsrSensitivity = FALSE; dcb.fTXContinueOnXoff= TRUE; dcb.fOutX = FALSE; dcb.fInX = FALSE; dcb.fRtsControl = RTS_CONTROL_DISABLE; dcb.fAbortOnError = FALSE; SetCommState(com_handle, &dcb); } } #else { struct termios t; if((com_handle=open(name, O_NONBLOCK|O_RDWR))==COM_HANDLE_INVALID) return -1; if(tcgetattr(com_handle, &t)==-1) { close(com_handle); com_handle = COM_HANDLE_INVALID; return -1; } t.c_iflag = ( IGNBRK /* ignore BREAK condition */ | IGNPAR /* ignore (discard) parity errors */ ); t.c_oflag = 0; /* No output processing */ t.c_cflag = ( CS8 /* 8 bits */ | CREAD /* enable receiver */ /* Fun snippet from the FreeBSD manpage: If CREAD is set, the receiver is enabled. Otherwise, no character is received. Not all hardware supports this bit. In fact, this flag is pretty silly and if it were not part of the termios specification it would be omitted. */ | CLOCAL /* ignore modem status lines */ ); t.c_lflag = 0; /* No local modes */ if(tcsetattr(com_handle, TCSANOW, &t)==-1) { close(com_handle); com_handle = COM_HANDLE_INVALID; return -1; } } #endif return 0; } void closeComPort(void) { #ifdef _WIN32 CloseHandle(com_handle); #else close(com_handle); #endif com_handle = COM_HANDLE_INVALID; } //---------------------------------------------------------------- // (raise|lower)(RTS|DTR)() // // Raises/lowers the specified signal //---------------------------------------------------------------- void raiseDTR(void) { if(com_handle == COM_HANDLE_INVALID) return; #ifdef _WIN32 EscapeCommFunction(com_handle, SETDTR); #else { // For C89 happiness int flags = TIOCM_DTR; ioctl(com_handle, TIOCMBIS, &flags); } #endif } void raiseRTS(void) { if(com_handle == COM_HANDLE_INVALID) return; #ifdef _WIN32 EscapeCommFunction(com_handle, SETRTS); #else { // For C89 happiness int flags = TIOCM_RTS; ioctl(com_handle, TIOCMBIS, &flags); } #endif } void lowerDTR(void) { if(com_handle == COM_HANDLE_INVALID) return; #ifdef _WIN32 EscapeCommFunction(com_handle, CLRDTR); #else { // For C89 happiness int flags = TIOCM_DTR; ioctl(com_handle, TIOCMBIC, &flags); } #endif } void lowerRTS(void) { if(com_handle == COM_HANDLE_INVALID) return; #ifdef _WIN32 EscapeCommFunction(com_handle, CLRRTS); #else { // For C89 happiness int flags = TIOCM_RTS; ioctl(com_handle, TIOCMBIC, &flags); } #endif } #define BUFSIZE 2048 #define SERVICE_PORT 21234 void *udp_listener_thread(void* p) { struct sockaddr_in myaddr; /* our address */ struct sockaddr_in remaddr; /* remote address */ socklen_t addrlen = sizeof(remaddr); /* length of addresses */ int recvlen; /* # bytes received */ int fd; /* our socket */ char txt[BUFSIZE]; /* receive buffer */ /* create a UDP socket */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "cannot create socket\n"); return 0; } /* bind the socket to any valid IP address and a specific port */ memset((char *)&myaddr, 0, sizeof(myaddr)); myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = htonl(INADDR_ANY); myaddr.sin_port = htons(SERVICE_PORT); if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { fprintf(stderr, "bind failed"); return 0; } /* now loop, receiving data and printing what we received */ while (runListener) { fprintf(stderr, "udp listener waiting on port %d - send me a command!\n", SERVICE_PORT); recvlen = recvfrom(fd, txt, BUFSIZE, 0, (struct sockaddr *)&remaddr, &addrlen); fprintf(stderr, "received %d bytes\n", recvlen); if (recvlen > 0) { txt[recvlen] = 0; fprintf(stderr, "txt: %s\n", txt); if (strcmp(txt, "recordany") == 0) { recordAny = 1; fprintf(stderr, "Next signal to sync will be recorded regardless of txt msg\n"); } if (strcmp(txt, "trigger") == 0) { triggered = 1; fprintf(stderr, "Now triggered\n"); } } } return NULL; } pthread_t start_udp_listener_thread(void) { pthread_t athread; runListener = 1; if (pthread_create(&athread, NULL, udp_listener_thread, NULL) != 0) fprintf(stderr, "Can't create UDP listener thread\n"); return athread; }