wavefiles being saved OK, RS232 DTR/RTS works, improved txt layout, ready for OTA...
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 14 Dec 2015 01:21:27 +0000 (01:21 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 14 Dec 2015 01:21:27 +0000 (01:21 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@2525 01035d8c-6547-0410-b346-abe4f91aad63

freebeacon/Makefile [new file with mode: 0644]
freebeacon/README.txt [new file with mode: 0644]
freebeacon/freebeacon.c
freebeacon/freebeacon.txt [deleted file]

diff --git a/freebeacon/Makefile b/freebeacon/Makefile
new file mode 100644 (file)
index 0000000..c106514
--- /dev/null
@@ -0,0 +1,4 @@
+all: freebeacon
+
+freebeacon: freebeacon.c
+       gcc -I/usr/local/include/codec2 freebeacon.c -g -o freebeacon -lsamplerate -lportaudio -lsndfile -lcodec2 -Wall
diff --git a/freebeacon/README.txt b/freebeacon/README.txt
new file mode 100644 (file)
index 0000000..4d41e8d
--- /dev/null
@@ -0,0 +1 @@
+See comments at top of freedvbeacon.txt
index 768a6fea904c4c7cfee934f05a0fd04240ecdba3..0e85dbd783cf92f125fd7b4a0d4d24afc6bfd667 100644 (file)
@@ -3,12 +3,30 @@
   David Rowe 
   Created Dec 2015
 
-  FreeDV beacon daemon.  Listens for freedv signals, then transmits a
-  reply.  Saves received signal from radio and decoded audio as wavefiles
-  so they can be saved to a web site.
-
-  If stereo audio device we use left channel only.  RTS and DTR is
-  rasised on Tx to trigger Radio PTT.
+  FreeDV 1600 beacon.  Listens for FreeDV signals, then transmits a
+  reply. Places the received signal files on a web server. Requires a
+  Linux machine with a sound card and RS232-PTT interface to your radio.
+  Just one sound card is required.
+
+  When a "trigger" string is detected in the rx FreeDV text message
+  (e.g. "hello beacon", or the beacon callsign), the beacon will
+  transmit a signal report back to you.
+
+  It requires a "txfilename" wave file to transmit, e.g. some one
+  saying "Hi, I am a FreeDV beacon blah blah".  The signal report is
+  encoded into the transmit text message.  Make the wave file long
+  enough so that the the signal report is repeated a few times, say 30
+  seconds. Transmit will stop when the wave file is played once.
+
+  Freebeacon saves the received audio from the radio AND the decoded
+  audio as wavefiles.  The file length is limited to 60 seconds. If
+  you run freebeacon in a webserver directory these will appear on the
+  Web.  Add a cron job to your machine to clean these files up once a
+  day.
+
+  If your input audio device is stereo note we only listen to
+  the left channel.  RTS and DTR is raised on transmit, and lowered
+  otherwise.
 
   A whole lot of code was lifted from freedv-dev for this program.
 
       [X] prog sound dongle debug
   [X] RS232 PTT code
   [X] writing to wave files
-  [ ] test mode to tx straight away then end, to check levels, debug RS232
-  [ ] test OTA
-  [ ] OTA on RPi
+  [X] test mode to tx straight away then end, to check levels, debug RS232
+  [ ] FreeDV 700 support
+  [ ] daemonise
+      + change all fprintfs to use log file in daemon mode
+  [ ] test OTA on laptop
+  [ ] test OTA on RPi
   [ ] writing text string to a web page (cat, create if doesn't exist)
   [ ] samples from stdin option to work from sdr
+  [ ] monitor rx and tx audio on another sound device
   [ ] option to not tx, just log info, for rx only stations
+  [ ] Hamlib support for keying different radios
   [ ] basic SM1000 version
-      + has audio interfaces 
+      + add mode, state machine
+      + has audio interfaces, PTT, so neat solution
 
   Building:
     Note you need the libraries on the gcc line installed (TODO find apt-get package names)
     gcc -I/usr/local/include/codec2 freebeacon.c -o freebeacon -lsamplerate -lportaudio -lsndfile -lcodec2
 
-  Running:
-    ./freebeacon
+  Usage:
+    ./freebeacon -h
+
+  List sound devices
+    ./freebeacon -l
+
+  Example usage:
+    ./freebeacon -c /dev/ttyUSB1 --txfilename ~/codec2-dev/wav/vk5qi.wav --dev 4 -v --trigger hello
 
 */
 
 #define UNSYNC_FRAMES       25                  // frames of lost sync we need to see to change state
 #define PEAK_COUNTER        10                  // how often to report peak input level 
 #define COM_HANDLE_INVALID  -1
+#define LOG_COUNTER         50
 
-/* globals used to communicate with async events */
+/* globals used to communicate with async events and callback functions */
 
 volatile int keepRunning;
 char txtMsg[MAX_CHAR], *ptxtMsg, triggerString[MAX_CHAR];
 int triggered;
 float snr_est, snr_sample;
-int com_handle;
+int com_handle, verbose;
 
 /* state machine defines */
 
@@ -104,10 +135,18 @@ void lowerDTR(void);
 void raiseRTS(void);
 void lowerRTS(void);
 
+
+/*--------------------------------------------------------------------------------------------------------*\
+
+                                                  FUNCTIONS
+
+\*--------------------------------------------------------------------------------------------------------*/
+
 /* Called on Ctrl-C */
 
 void intHandler(int dummy) {
     keepRunning = 0;
+    fprintf(stderr,"\nShutting Down ......\n");
 }
 
 /* returns number of output samples generated by resampling */
@@ -182,6 +221,7 @@ void printHelp(const struct option* long_options, int num_opts, char* argv[])
                 "Options:\n"
                 "\t-l --list (audio devices)\n"
                 "\t-c        (comm port for Tx PTT)\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) {
@@ -223,11 +263,13 @@ void callbackNextRxChar(void *callback_state, char c) {
         *ptxtMsg++ = c;
         *ptxtMsg = 0;
          ptxtMsg = txtMsg;
-         fprintf(stderr, "RX txtMsg: %s\n", txtMsg);
+         if (verbose)
+             fprintf(stderr, "  RX txtMsg: %s\n", txtMsg);
          if (strstr(txtMsg, triggerString) != NULL) {
              triggered = 1;
              snr_sample = snr_est;
-             fprintf(stderr, "Tx triggered!\n");
+             if (verbose)
+                 fprintf(stderr, "  Tx triggered!\n");
          }
     } 
 }
@@ -278,7 +320,11 @@ SNDFILE *openRecFile(char fileName[], int sfFs)
 }
 
   
+/*--------------------------------------------------------------------------------------------------------*\
+
+                                                  MAIN
 
+\*--------------------------------------------------------------------------------------------------------*/
 
 int main(int argc, char *argv[]) {
     struct freedv      *f;
@@ -286,7 +332,7 @@ int main(int argc, char *argv[]) {
     PaStreamParameters  inputParameters, outputParameters;
     const PaDeviceInfo *deviceInfo = NULL;
     PaStream           *stream = NULL;
-    int                 n8k, j, src_error, inputChannels, nin, devNum;
+    int                 j, src_error, inputChannels, nin, devNum;
     int                 outputChannels;
     int                 state, next_state;
     SRC_STATE          *rxsrc, *txsrc;
@@ -297,12 +343,14 @@ int main(int argc, char *argv[]) {
     int                 sfFs;
     int                 fssc;                 
     int                 triggerf, txfilenamef, callsignf, sampleratef;
-    int                 sync, verbose;
+    int                 sync;
     char                commport[MAX_CHAR];
     char                callsign[MAX_CHAR];
     FILE               *ftmp;
     unsigned int        sync_counter, peakCounter;
     unsigned int        tnout,mnout;
+    short               peak;
+    unsigned int        logCounter;
 
     /* debug raw file */
 
@@ -319,7 +367,11 @@ int main(int argc, char *argv[]) {
     verbose = 0;
     com_handle = COM_HANDLE_INVALID;
     mnout = 60*FS8;
-
+    state = SRX_IDLE;
+    *txtMsg = 0;
+    sfRecFileFromRadio = NULL;
+    sfRecFileDecAudio = NULL;
+    
     if (Pa_Initialize()) {
         fprintf(stderr, "Port Audio failed to initialize");
         exit(1);
@@ -327,7 +379,7 @@ int main(int argc, char *argv[]) {
  
     /* Process command line options -----------------------------------------------------------*/
 
-    char* opt_string = "hlvc";
+    char* opt_string = "hlvc:t";
     struct option long_options[] = {
         { "dev", required_argument, &devNum, 1 },
         { "trigger", required_argument, &triggerf, 1 },
@@ -365,7 +417,7 @@ int main(int argc, char *argv[]) {
         case 'c':
             strcpy(commport, optarg);
             if (openComPort(commport) != 0) {
-                fprintf(stderr, "Can't topne comm port: %s\n",commport);
+                fprintf(stderr, "Can't open comm port: %s\n",commport);
                 exit(1);
             }
             break;
@@ -378,6 +430,11 @@ int main(int argc, char *argv[]) {
             verbose = 1;
             break;
 
+        case 't':
+            sprintf(txtMsg,"tx Test");
+            state = STX;
+            break;
+
         case 'l':
             listAudioDevices();
             exit(0);
@@ -413,7 +470,7 @@ int main(int argc, char *argv[]) {
     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 */
+    /* Open Port Audio device and set up config structures -----------------------------------------------------*/
 
     deviceInfo = Pa_GetDeviceInfo(devNum);
     if (deviceInfo == NULL) {
@@ -469,23 +526,28 @@ int main(int argc, char *argv[]) {
         exit(1);
     }
 
-    fprintf(stderr, "Ctrl-C to exit\n");
-    fprintf(stderr, "trigger string: %s  txFileName: %s\n", triggerString, txFileName);
-    fprintf(stderr, "PortAudio devNum: %d  samplerate: %d\n", devNum, fssc);
 
-    signal(SIGINT, intHandler);  /* ctrl-C to exit gracefully */
+    /* Init for main loop ----------------------------------------------------------------------------*/
 
-    /* init for main loop */
+    fprintf(stderr, "\nCtrl-C to exit\n");
+    fprintf(stderr, "trigger string: %s\ntxFileName: %s\n", triggerString, txFileName);
+    fprintf(stderr, "PortAudio devNum: %d\nsamplerate: %d\n", devNum, fssc);
 
-    state = SRX_IDLE;
+    signal(SIGINT, intHandler);  /* ctrl-C to exit gracefully */
     keepRunning = 1;
-    *txtMsg = 0;
     ptxtMsg = txtMsg;
     triggered = 0;
     peakCounter = 0;
+    logCounter = 0;
     if (com_handle != COM_HANDLE_INVALID) {
         lowerRTS(); lowerDTR();
     }
+    if (state == STX) {
+        raiseRTS(); raiseDTR();
+        sfPlayFile = openPlayFile(txFileName, &sfFs);
+    }
+
+    /* Main loop -------------------------------------------------------------------------------------*/
 
     while(keepRunning) {
 
@@ -493,7 +555,7 @@ int main(int argc, char *argv[]) {
             short demod_in[freedv_get_n_max_modem_samples(f)];
             short speech_out[freedv_get_n_speech_samples(f)];
 
-            /* Read samples, resample to modem sample rate */
+            /* Read samples froun sound card, resample to modem sample rate */
 
             Pa_ReadStream(stream, stereo, n48);
 
@@ -505,19 +567,17 @@ int main(int argc, char *argv[]) {
                 for(j=0; j<n48; j++)
                     rx48k[j] = stereo[j]; 
             }
-            //printf("%d\n", rx48k[0]);
-            //printf("fsm: %d fssc: %d n8m: %d n48: %d\n", fsm, fssc, n8m, n48);
             int n8m_out = resample(rxsrc, rxfsm, rx48k, fsm, fssc, n8m, n48);
             //fwrite(rxfsm, sizeof(short), n8m, ftmp);
           
             if (verbose) {
-                short peak = 0;
+                /* crude input signal level meter */
+                peak = 0;
                 for (j=0; j<n8m_out; j++)
                     if (rxfsm[j] > peak)
                         peak = rxfsm[j];
                 if (peakCounter++ == PEAK_COUNTER) {
                     peakCounter = 0;
-                    fprintf(stderr, "peak: %d\n", peak);
                 }
             }
 
@@ -526,7 +586,6 @@ int main(int argc, char *argv[]) {
             /* demodulate to decoded speech samples */
 
             nin = freedv_nin(f);
-            //printf("n8m_out: %d fifo_used: %d nin: %d\n", n8m_out, fifo_used(fifo), nin);
             while (fifo_read(fifo, demod_in, nin) == 0) {
                 int nout = freedv_rx(f, speech_out, demod_in);
                 freedv_get_modem_stats(f, &sync, &snr_est);
@@ -543,11 +602,10 @@ int main(int argc, char *argv[]) {
             }
         }
 
+
         if (state == STX) {
             short mod_out[freedv_get_n_max_modem_samples(f)];
             short speech_in[freedv_get_n_speech_samples(f)];
-
-            /* TODO: assert PTT, e.g. via RS232 */
             
             if (sfPlayFile != NULL) {
                 /* resample input sound file as can't guarantee 8KHz sample rate */
@@ -555,7 +613,7 @@ int main(int argc, char *argv[]) {
                 unsigned int nsf = freedv_get_n_speech_samples(f)*sfFs/FS8;
                 short        insf[nsf];
                 unsigned int n = sf_read_short(sfPlayFile, insf, nsf);
-                n8k = resample(playsrc, speech_in, insf, FS8, sfFs, freedv_get_n_speech_samples(f), 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);
 
@@ -614,10 +672,11 @@ int main(int argc, char *argv[]) {
                     char timeStr[MAX_CHAR];
                     sprintf(timeStr, "%s",asctime( localtime(&ltime) ) );
                     int i=0;
-                    unsigned char str[]="a ";
-                    while (str[i]) {
-                        if (isspace(str[i])) 
-                            str[i]='_';
+                    while (timeStr[i]) {
+                        if (isspace(timeStr[i]) || (timeStr[i] == ':')) 
+                            timeStr[i]='_';
+                        else
+                            timeStr[i] = tolower(timeStr[i]);
                         i++;
                     }
                     char recFileFromRadioName[MAX_CHAR], recFileDecAudioName[MAX_CHAR];
@@ -635,8 +694,6 @@ int main(int argc, char *argv[]) {
             break;
         case SRX_SYNC:
             sync_counter++;
-            if (((sync_counter % SYNC_FRAMES) == 0) && verbose)
-                fprintf(stderr, "sync: %d snr: %3.1f\n", sync, snr_est);                
             if (!sync) {
                 sync_counter = 0;
                 next_state = SRX_MAYBE_UNSYNC;
@@ -664,14 +721,14 @@ int main(int argc, char *argv[]) {
                         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);
+                        //fprintf(stderr, "TX txtMsg: %s\n", txtMsg);
                         ptxtMsg = txtMsg;
                         sfPlayFile = openPlayFile(txFileName, &sfFs);
 
                         if (com_handle != COM_HANDLE_INVALID) {
                             raiseRTS(); raiseDTR();
                         }
-                       next_state = STX;
+                        next_state = STX;
                     }
                     else {
                         next_state = SRX_IDLE;
@@ -685,20 +742,30 @@ int main(int argc, char *argv[]) {
             if (sfPlayFile == NULL) {
 
                 if (com_handle != COM_HANDLE_INVALID) {
-                    raiseRTS(); raiseDTR();
+                    lowerRTS(); lowerDTR();
                 }
                 next_state = SRX_IDLE;
             }
             break;
         }
 
-        /* filter out IDLE->MAYBE_SYNC as this fires on channel noise all the time */
+        if (verbose) {
+            if (logCounter++ == LOG_COUNTER) {
+                logCounter = 0;
+                fprintf(stderr, "state: %-20s  peak: %6d  sync: %d  SNR: %3.1f  triggered: %d\n", 
+                        state_str[state], peak, sync, snr_est, triggered);
+            }
+        }
 
-        if ((next_state != state) && verbose) 
-            fprintf(stderr, "state: %-20s next_state: %-20s\n", state_str[state], state_str[next_state]);
         state = next_state;
     }
 
+    /* lower PTT lines */
+
+    if (com_handle != COM_HANDLE_INVALID) {
+        lowerRTS(); lowerDTR();
+    }
+
     /* Shut down port audio */
 
     err = Pa_StopStream(stream);
@@ -720,7 +787,11 @@ int main(int argc, char *argv[]) {
 }
 
 
-/* Comm port fuctions lifted from FreeDV -------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------------------------------------*\
+
+                                      Comm port fuctions lifted from FreeDV
+
+\*--------------------------------------------------------------------------------------------------------*/
 
 //----------------------------------------------------------------
 // openComPort() opens the com port specified by the string
diff --git a/freebeacon/freebeacon.txt b/freebeacon/freebeacon.txt
deleted file mode 100644 (file)
index a042d46..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-- freebeacon runs as a linux daemon
-- configured via command line at start up
-
-Beacon:
-
-- text string "XXX" entered on command line at start up
-- when we get valid sync, start parsing rx text
-- on rx of "XXX", trigger tx event.
-- tx starts after sync is lost for 10 seconds
-- send canned, encoded speech, echoing received txt string
-- then back to listen
-
-Logging files:
-
-- log dir entered on command line.  This could be a web server directory,
-  then logged files will automatically appear on the web
-- maximum nuber of audio log files entered on command line
-- when we get a valid sync and txt string "XXX"
-  - log rx audio to wave file in log directory
-  - log decoded audio to wave file in log directory
-- stop writing wave files when sync lost for 10s or after 60 seconds
-- use rotating file names 0 ... 9 so we re-use storage
-- optional: fire off event to another process, perhaps via a UDP port,
-  this could be used to add text to a web page, (e.g. txt string)
-
-Task List
----------
-
-[ ] daemonise
-[ ] command line parsing for 
-    [ ] log dir and number of log files
-    [ ] sound devices
-[ ] state machine
-[ ] work out how to play to sound devices, maybe portaudio?
-[ ] code to key tx via rs232
-