/* cnode.c from freebeacon.c David Rowe VK5DGR Hamlib PTT and 700C support Bob VK4YA Alan VK2ZIW FreeDV connector to app mvoice. */ #include #include #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 MAX_CHAR2 200 #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 /* interprocess signaling, "cnodePTT" named semaphore */ #define SEM_IDLE 1 #define SEM_RX 2 #define SEM_TX 3 /* 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 */ #define MV2STX 5 /* transmitting audio from app mvoice */ #define IDENT 6 /* transmitting ident */ char *state_str[] = { "Rx Idle", "Rx Maybe Sync", "Rx Sync", "Rx Maybe UnSync", "Tx", "Tx from net", "Tx ident" }; 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; //Alan sem_t* sem1; int semVal, old_semVal; /*--------------------------------------------------------------------------------------------------------*\ 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-c (comm port for Tx or CAT PTT)\n" "\t-u (Hamlib CAT model number [use rigctl -l to see list])\n" "\t-l --list (audio devices)\n" "\t-m --mode 1600|700C|700D\n" "\t --ndev loopback dev\n" "\t-t (tx on start up, useful for testing)\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; const PaDeviceInfo *ndeviceInfo = NULL; PaStream *stream = NULL; PaStream *nstream = NULL; int j, src_error, inputChannels, nin, devNum, ndevNum; int outputChannels; int state, next_state; SRC_STATE *rxsrc, *txsrc; SRC_STATE *playsrc; struct FIFO *fifo; char txFileName[MAX_CHAR2]; SNDFILE *sfPlayFile, *sfRecFileFromRadio, *sfRecFileDecAudio; int sfFs; int fssc; int triggerf, txfilenamef, callsignf, sampleratef, wavefilepathf, rpigpiof, rpigpioalivef; int statuspagef; int sync, haveRecording; 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_CHAR2], rpigpioalive[MAX_CHAR], rpigpioalive_path[MAX_CHAR2]; char statusPageFileName[MAX_CHAR]; FILE *fstatus; int gpioAliveState = 0; int freedv_mode; struct FIFO *nfifo; /* debug raw file */ //ftmp = fopen("t.raw", "wb"); //assert(ftmp != NULL); /* Defaults ----- DEFAULTS ----------------------------------------------------------------*/ devNum = 0; ndevNum = 0; fssc = FS48; sprintf(triggerString, "M17"); sprintf(txFileName, "txaudio.wav"); sprintf(callsign, "c2 node"); 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_700D; recordAny = 0; myrig_model = 0; my_rig = NULL; semVal = old_semVal = 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 }, { "ndev", required_argument, &ndevNum, 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, "ndev") == 0) { ndevNum = 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_CHAR2]; 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_CHAR2]; 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 'c': strcpy(commport, optarg); if (openComPort(commport) != 0) { fprintf(stderr, "Can't open comm port: %s\n",commport); exit(1); } break; case 'u': // 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 '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 { fprintf(stderr, "Unknown mode: %s\n", optarg); exit(1); } break; default: /* This will never be reached */ break; } } /* Open Sound Device and start processing --------------------------------------------------------------*/ f2 = freedv_open(freedv_mode); assert(f2 != NULL); 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 */ nfifo = codec2_fifo_create(8*n8m); assert(fifo != NULL); /* fifo to smooth out variation in network in */ /* 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 for radio 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 nsound device\n"); exit(1); } /* Second audio channel, loopback */ ndeviceInfo = Pa_GetDeviceInfo(ndevNum); if (ndeviceInfo == NULL) { fprintf(stderr, "\n\n ============= NO device info from Port Audio for device: %d\n\n\n", ndevNum); exit(1); } if (ndeviceInfo->maxInputChannels == 1) inputChannels = 1; else inputChannels = 2; /* input device */ inputParameters.device = ndevNum; inputParameters.channelCount = inputChannels; inputParameters.sampleFormat = paInt16; inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; /* output device */ if (ndeviceInfo->maxOutputChannels == 1) outputChannels = 1; else outputChannels = 2; outputParameters.device = ndevNum; 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( &nstream, &inputParameters, &outputParameters, FS8, /* 8000 as created by app mvoice */ n8m, /* changed from 0 to n8 to get Rpi audio to work without clicks */ paClipOff, NULL, /* no callback, use blocking API */ NULL ); if (err != paNoError) { fprintf(stderr, "\n============= Couldn't initialise loopback sound device\n"); exit(1); } err = Pa_StartStream(nstream); if (err != paNoError) { fprintf(stderr, "\n=============== Couldn't start sound device\n"); exit(1); } /* start semaphore for PTT and RX for mvoice process */ sem1 = sem_open("cnodePTT", O_CREAT, 0644, 0); if (sem1 == (void*) -1) fprintf(stderr, "\nopen semaphore cnodePTT failed\n"); sem_post(sem1); // increment to SEM_IDLE state /* Start UDP listener thread */ // Alan9 // 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, "PortAudio ndevNum: %d\nsamplerate: %d\n", ndevNum, 999); 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 == IDENT) { 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); } /* Main loop -------------------------------------------------------------------------------------*/ while(keepRunning) { //Alan1 check for Tx mode from semaphore sem_getvalue(sem1, &semVal); if(semVal == SEM_TX) { state = MV2STX; if(old_semVal != SEM_TX) { // ptt_set(1); if (com_handle != COM_HANDLE_INVALID) { raiseRTS(); raiseDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "1"); } if (my_rig) hamlib_ptt_on(); old_semVal = SEM_TX; } } else { if(old_semVal == SEM_TX) { // ptt_set(0); if (com_handle != COM_HANDLE_INVALID) { lowerRTS(); lowerDTR(); } if (*rpigpio) { sys_gpio(rpigpio_path, "0"); } if (my_rig) hamlib_ptt_off(); old_semVal = SEM_IDLE; state = SRX_IDLE; } } // listen to radio if ((state != STX) && (state != MV2STX)) { short demod_in[freedv_get_n_max_modem_samples(f2)]; short speech_out[freedv_get_n_speech_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 */ nin = freedv_nin(f2); while (codec2_fifo_read(fifo, demod_in, nin) == 0) { int nout = 0; nout = freedv_rx(f2, speech_out, demod_in); freedv_get_modem_stats(f2, &sync, &snr_est); nin = freedv_nin(f2); /* triggered */ if (sfRecFileFromRadio) sf_write_short(sfRecFileFromRadio, demod_in, nin); if (sfRecFileDecAudio) sf_write_short(sfRecFileDecAudio, speech_out, nout); if (sfRecFileDecAudio) Pa_WriteStream(nstream, speech_out, n8m); tnout += nout; if (tnout > mnout) { sf_close(sfRecFileFromRadio); sf_close(sfRecFileDecAudio); sfRecFileFromRadio = NULL; sfRecFileDecAudio = NULL; } } } if (state == MV2STX) { short mod_out[freedv_get_n_max_modem_samples(f2)]; short speech_in[freedv_get_n_speech_samples(f2)]; //Alan2 Pa_ReadStream(nstream, stereo, n8m); // from loopback if (inputChannels == 2) { for(j=0; j= 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_CHAR2], recFileDecAudioName[MAX_CHAR2]; 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); sfRecFileFromRadio = NULL; } if (sfRecFileDecAudio) { sf_close(sfRecFileDecAudio); sfRecFileDecAudio = NULL; } //Alan6 /* Send audio to mvoice if triggered */ if (triggered) { float ber = (float)freedv_get_total_bit_errors(f2)/freedv_get_total_bits(f2); char tmpStr[MAX_CHAR2]; 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(); */ //Alan4 sem_getvalue(sem1, &semVal); if(semVal == SEM_IDLE) sem_post(sem1); // set mvoice to tx to net // next_state = STX; } else { sem_getvalue(sem1, &semVal); if(semVal == SEM_RX) sem_wait(sem1); // set mvoice to rx from net next_state = SRX_IDLE; triggered = 0; haveRecording = 0; } } } else next_state = SRX_SYNC; /* sync is back so false alarm */ break; case IDENT: // Alan7 read in from loopback if (sfPlayFile == NULL) { next_state = SRX_IDLE; sem_getvalue(sem1, &semVal); if(semVal == SEM_TX) { sem_wait(sem1); sem_wait(sem1); } 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"); } } } 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); } sem_getvalue(sem1, &semVal); if(semVal == SEM_TX) { sem_wait(sem1); sem_wait(sem1); // for other process } /* 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_CloseStream(nstream); Pa_Terminate(); /* clean up states */ codec2_fifo_destroy(fifo); codec2_fifo_destroy(nfifo); src_delete(rxsrc); src_delete(txsrc); src_delete(playsrc); freedv_close(f2); sem_close(sem1); //fclose(ftmp); return 0; } /* ------- end of main() -----------*/ /*--------------------------------------------------------------------------------------------------------*\ 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"); } } } 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; }