Hi again Jeroen,
In adding codec2_decode(c2, ...), I now need to add to
/usr/local/include/codec2/
newamp2.h
newamp1.h
kiss_fftr.h
kiss_fft.h
defines.h
codec2_fft.h
codec2_internal.h
and in the end, void codec2_decode() does not return how many samples
of audio it had decoded. So, if it were a data frame, it might return zero.
Still in progress. So far, it compiles, but not tested.
Alan VK2ZIW
On Wed, 17 Jun 2020 18:18:25 +0200, Jeroen Vreeken wrote
> It is indeed not in the same header, but it is in the same library.
> You can just add '#include <codec2.h>" to your C file.
>
> The functions use the same 'bits packed in bytes' format, with oneexception:
> a freedv frame might contain multiple codec2 frames (thisdepends on the
> freedv mode)
>
> So suppose you have the data in 'unsigned char rxdata_out[bytes_f];'you can
> decode it with a loop:
>
> struct CODEC2 c2 = freedv_get_codec2(f);
> int bits_per_codec_frame = codec2_bits_per_frame(c2);
> int bytes_per_codec_frame = (bits_per_codec_frame + 7) /8;
> int bits_f = freedv_get_bits_per_codec_frame(f);
> int codec_frames = bits_f / bits_per_codec_frame;
> int samples_per_frame = codec2_samples_per_frame(c2);
>
> int i;
> for (i = 0; i < codec_frames; i++) {
>
> codec2_decode(c2,&speech_out[samples_per_frame*i],&rxdata_out[bytes_per_codec_frame*i]);
> }
>
> On 06/17/2020 01:43 PM, Al Beard wrote:
> Hi Jeroen,
>
>
> It's never easy....
>
>
> 1) codec2_decode() is not definedin freedv_api.h
> 2) codec2_decode() looks to require "bits"not "packed_bits"
>
>
> I'd like not to have to modify the codec2library so my freedv binary
> will run on an updated codec2 library.
>
>
> Al VK2ZIW
>
>
> On Wed, 17 Jun 2020 12:17:04 +0200, JeroenVreeken wrote
> > Hi Al,
> >
> > So you need both the codec2 data and the audio?
> > In that case I would use the freedv_rawdatarx function toget the codec2
> > data.
> > The audio data can be retrieved by callingcodec2_decode() with it, then you
> > have both.
> >
> > If you use the freedv_rawdatarx() function the textcallback functions will
> > still be called in the same manner asif you had used freedv_rx() so that
> > should not be a problem.
> >
> > 73,
> > Jeroen PE1RXQ
> >
> > On 06/17/2020 09:20 AM, Al Beard wrote:
> > Hi Jeroen,
> >
> >
> > I do actually want the decoded audioand the text string.
> > So, should I decode to voice, the700C file saved, after the QSO
> > has finished?
> >
> >
> > As I've mentioned, I'm not a "C"guru but I see a repeater as a very
> > worthwhile
> > project offering a remote areadigital voice communication method for us hams
> > where, unlike the commercial userseg. ambulances who have many bases so
> > HF skip will always (~mostly) getthe signal to/from base HQ.
> >
> >
> > Also, after a callto freedv_rawdatarx() is the text string recovered?
> > It's used to "trigger"retransmission.
> >
> >
> > Alan VK2ZIW
> >
> >
> > On Wed, 17 Jun 2020 02:22:20 +0200,Jeroen Vreeken wrote
> > > You should not mix freedv_rx() withfreedv_rawdatarx().
> > > Either one should be used.
> > > The only difference between the two is thatfreedv_rx() will go one step
> > > further after the modem partand decodes into audio.
> > >
> > > So after the call to freedv_rawdatarx() you aredone and can skip
> > > freedv_rx()
> > >
> > > On 06/16/2020 11:49 PM, Al Beard wrote:
> > > Hi Jeroen,
> > >
> > > In freebeacon.c
> > > Line: 720
> > >
> > > nin = freedv_nin(f);
> > > while (codec2_fifo_read(fifo,demod_in, nin) == 0) {
> > > int nout = 0;
> > > //Alan
> > > ncodecb =freedv_rawdatarx(f, rxdata_out, demod_in);
> > > // original code continues
> > > nout = freedv_rx(f,speech_out, demod_in);
> > > freedv_get_modem_stats(f,&sync, &snr_est);
> > > nin = freedv_nin(f);
> > >
> > > ---------
> > > Can I do this without causing any problems?
> > >
> > > I'm concerned that routines called byfreedv_rx() will have
> > > already processed the demod_in audio data.
> > >
> > > Alan VK2ZIW
> > >
> > > On Tue, 16 Jun 2020 17:58:14 +0200, JeroenVreeken wrote
> > > > Hi Al,
> > > >
> > > > Until recently you would have neededthe freedv_codecrx() and
> > > > freedv_codectx() functions.
> > > > If you have the 'latest greatest' gitversion you will need to use
> > > > freedv_rawdatarx() andfreedv_rawdatatx()
> > > >
> > > > 73,
> > > > Jeroen PE1RXQ
> > > >
> > > > On 06/16/2020 12:42 PM, Al Beard wrote:
> > > > > Hi all,
> > > > >
> > > > > Yes, it's been eighteen monthssince lightning blew my shack
> > > > > radios and computers and now Iwant to use the latest Codec2
> > > > > library code in a repeater.
> > > > >
> > > > > Starting with the "freebeacon"code, I want to retransmit the received
> > > > > 700C data and include the textstring or strings.
> > > > > So the first step is to receivethe "packed data bits".
> > > > >
> > > > > Can somebody point me to afunction in freedv_api.x that will allow
> > > > > getting the "packed data bits" ?
> > > > >
> > > > > (In the past, I had to add afunction.)
> > > > >
> > > > > 73 There is a valve type 73,has anybody used it in a transmitter
> > > > > or receiver?
> > > > >---------------------------------------------------
> > > > > Alan VK2ZIW
> > > > >
> > > > > OpenWebMail 2.53
> > > > >
> > > > >
> > > > >
> > > > >_______________________________________________
> > > > > Freetel-codec2 mailing list
> > > > > [email protected]
> > > > > https://lists.sourceforge.net/lists/listinfo/freetel-codec2
> > > > >
> > > >
> > > >_______________________________________________
> > > > Freetel-codec2 mailing list
> > > > [email protected]
> > > > https://lists.sourceforge.net/lists/listinfo/freetel-codec2
> > >
> > >---------------------------------------------------
> > > Alan Beard
> > >
> > > OpenWebMail 2.53
> > >
> > >
> > >
> > >
> > >_______________________________________________
Freetel-codec2 mailing
list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freetel-codec2
> >
> > ---------------------------------------------------
> > Alan Beard
> >
> > OpenWebMail 2.53
> >
> >
> >
> >
> >
> >_______________________________________________
Freetel-codec2 mailing
list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freetel-codec2
>
> ---------------------------------------------------
> Alan Beard
>
> OpenWebMail 2.53
>
>
>
>
>
>_______________________________________________
Freetel-codec2 mailing
list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freetel-codec2
---------------------------------------------------
Alan Beard
OpenWebMail 2.53
/*
freebeacon.c
David Rowe VK5DGR
Hamlib PTT and 700C support Bob VK4YA
FreeDV Beacon.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#include <sys/ioctl.h>
#endif
#include <samplerate.h>
#include <getopt.h>
#include "sndfile.h"
#include "portaudio.h"
#include "codec2_fifo.h"
#include "codec2_internal.h"
#include "codec2.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
/* 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; devn<numDevices; devn++) {
deviceInfo = Pa_GetDeviceInfo(devn);
if (deviceInfo == NULL) {
fprintf(stderr, "Couldn't open devNum: %d\n", devn);
return;
}
printf(" %2d %50s %8s %6d %6d %6d\n",
devn,
deviceInfo->name,
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-t (tx on start up, useful for testing)\n"
"\t-v (verbose)\n", argv[0]);
for(i=0; i<num_opts-1; i++) {
if(long_options[i].has_arg == no_argument) {
option_parameters="";
} else if (strcmp("dev", long_options[i].name) == 0) {
option_parameters = " DeviceNumber (-l --list to list devices)";
} else if (strcmp("trigger", long_options[i].name) == 0) {
option_parameters = " textString (used to trigger beacon)";
} else if (strcmp("callsign", long_options[i].name) == 0) {
option_parameters = " callsign (returned in text str to tx)";
} else if (strcmp("txfilename", long_options[i].name) == 0) {
option_parameters = " wavefile (to use for source audio on tramsmit)";
} else if (strcmp("samplerate", long_options[i].name) == 0) {
option_parameters = " sampleRateHz (audio device sample rate)";
} else if (strcmp("wavefilewritepath", long_options[i].name) == 0) {
option_parameters = " pathToWaveFiles (path to where wave files are written)";
} else if (strcmp("rpigpio", long_options[i].name) == 0) {
option_parameters = " GPIO (BCM GPIO number on Raspberry Pi for Tx PTT)";
} else if (strcmp("rpigpioalive", long_options[i].name) == 0) {
option_parameters = " GPIO (BCM GPIO number on Raspberry Pi for alive blinker)";
} else if (strcmp("statuspagefile", long_options[i].name) == 0) {
option_parameters = " statusPageFileName (where to write status web page)";
}
fprintf(stderr, "\t--%s%s\n", long_options[i].name, option_parameters);
}
exit(0);
}
/* text message callbacks */
void callbackNextRxChar(void *callback_state, char c) {
/* if we hit end of buffer wrap around to start */
if ((ptxtMsg - txtMsg) < (MAX_CHAR-1))
*ptxtMsg++ = c;
else
ptxtMsg = txtMsg;
/* if end of string let see if we have a match for the trigger
string. Note tx may send trigger string many times. We only
need to receive it once to trigger a beacon tx cycle. */
if (c == 13) {
*ptxtMsg++ = c;
*ptxtMsg = 0;
ptxtMsg = txtMsg;
if (verbose)
fprintf(stderr, " RX txtMsg: %s\n", txtMsg);
if (strstr(txtMsg, triggerString) != NULL) {
triggered = 1;
snr_sample = snr_est;
if (verbose)
fprintf(stderr, " Tx triggered!\n");
}
}
}
char callbackNextTxChar(void *callback_state) {
if ((*ptxtMsg == 0) || ((ptxtMsg - txtMsg) >= 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-%R:%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 *f;
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;
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;
unsigned char rxData[100];
/* 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, "FreeBeacon");
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_1600;
recordAny = 0;
myrig_model = 0;
my_rig = NULL;
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 '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 --------------------------------------------------------------*/
f = freedv_open(freedv_mode); assert(f != NULL);
int fsm = freedv_get_modem_sample_rate(f); /* modem sample rate */
int n8m = freedv_get_n_nom_modem_samples(f); /* 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(f, 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 */
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);
}
/* Main loop -------------------------------------------------------------------------------------*/
while(keepRunning) {
if (state != STX) {
short demod_in[freedv_get_n_max_modem_samples(f)];
short speech_out[freedv_get_n_speech_samples(f)];
/* Read samples from sound card, resample to modem sample rate */
Pa_ReadStream(stream, stereo, n48);
if (inputChannels == 2) {
for(j=0; j<n48; j++)
rx48k[j] = stereo[2*j]; /* left channel only */
}
else {
for(j=0; j<n48; j++)
rx48k[j] = stereo[j];
}
//fwrite(rx48k, sizeof(short), n8m, ftmp);
int n8m_out = resample(rxsrc, rxfsm, rx48k, fsm, fssc, n8m, n48);
/* crude input signal level meter */
peak = 0;
for (j=0; j<n8m_out; j++)
if (rxfsm[j] > peak)
peak = rxfsm[j];
codec2_fifo_write(fifo, rxfsm, n8m_out);
/* demodulate to decoded speech samples */
nin = freedv_nin(f);
while (codec2_fifo_read(fifo, demod_in, nin) == 0) {
int nout = 0;
// Alan1 nout = freedv_rx(f, speech_out, demod_in);
nout = freedv_rawdatarx(f, rxData, demod_in);
struct CODEC2 *c2 = freedv_get_codec2(f);
codec2_decode(c2, speech_out, rxData);
freedv_get_modem_stats(f, &sync, &snr_est);
nin = freedv_nin(f);
if (sfRecFileFromRadio)
sf_write_short(sfRecFileFromRadio, demod_in, nin);
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(f)];
short speech_in[freedv_get_n_speech_samples(f)];
if (sfPlayFile != NULL) {
/* resample input sound file as can't guarantee 8KHz sample rate */
unsigned int nsf = freedv_get_n_speech_samples(f)*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(f), nsf);
//fwrite(speech_in, sizeof(short), freedv_get_n_nom_modem_samples(f), ftmp);
if (n != nsf) {
/* end of file - this signals state machine we've finished */
sf_close(sfPlayFile);
sfPlayFile = NULL;
}
}
freedv_tx(f, mod_out, speech_in);
//fwrite(mod_out, sizeof(short), freedv_get_n_nom_modem_samples(f), 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<n48_out; j++) {
if (outputChannels == 2) {
stereo[2*j] = tx48k[j]; // left channel
stereo[2*j+1] = tx48k[j]; // right channel
}
else {
stereo[j] = tx48k[j]; // mono
}
}
Pa_WriteStream(stream, stereo, n48_out);
}
/* state machine processing */
next_state = state;
switch(state) {
case SRX_IDLE:
if (sync) {
next_state = SRX_MAYBE_SYNC;
syncTimer = 0.0;
*txtMsg = 0;
ptxtMsg = txtMsg;
freedv_set_total_bit_errors(f, 0);
freedv_set_total_bits(f, 0);
}
break;
case SRX_MAYBE_SYNC:
if (sync) {
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(f)/freedv_get_total_bits(f);
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, "<html>\n<head>\n<meta http-equiv=\"refresh\" content=\"2\">\n</head>\n<body>\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, "</body>\n</html>\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);
}
/* 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(f);
//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");
}
}
}
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;
}
_______________________________________________
Freetel-codec2 mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freetel-codec2