first pass at horus_demod, compiles OK but untested, need Cmake integration next
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 9 Apr 2018 02:49:00 +0000 (02:49 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 9 Apr 2018 02:49:00 +0000 (02:49 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@3455 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/src/horus_api.c
codec2-dev/src/horus_api.h
codec2-dev/src/horus_demod.c [new file with mode: 0644]
codec2-dev/src/modem_stats.h

index c829a8d91202101dd344652fb3563dd77283b36e..d6f2851f0a60575c70aa48338fc75b5de18c624b 100644 (file)
@@ -128,7 +128,9 @@ void horus_close (struct horus *hstates) {
 
 uint32_t horus_nin(struct horus *hstates) {
     assert(hstates != NULL);
-    return fsk_nin(hstates->fsk);
+    int nin = fsk_nin(hstates->fsk);
+    assert(nin <= horus_get_max_demod_in(hstates));
+    return nin;
 }
 
 int horus_find_uw(struct horus *hstates) {
@@ -327,6 +329,21 @@ int horus_get_mode(struct horus *hstates) {
     return hstates->mode;
 }
 
+int horus_get_Fs(struct horus *hstates) {
+    assert(hstates != NULL);
+    return hstates->Fs;
+}
+
+int horus_get_mFSK(struct horus *hstates) {
+    assert(hstates != NULL);
+    return hstates->mFSK;
+}
+
+int horus_get_max_demod_in(struct horus *hstates) {
+    /* copied from fsk_demod.c, a nicer fsk_max_nin function would be useful */
+    return sizeof(short)*(hstates->fsk->N + hstates->fsk->Ts*2);
+}
+
 int horus_get_max_ascii_out_len(struct horus *hstates) {
     if (hstates->mode == HORUS_MODE_RTTY) {
         return hstates->max_packet_len/10;     /* 7 bit ASCII, plus 3 sync bits */
@@ -345,15 +362,24 @@ void horus_get_modem_stats(struct horus *hstates, int *sync, float *snr_est) {
 
     *sync = 0;
     
-    /* TODO SNR is actually an Eb/No est for FSK - should we scale
-       Eb/No for SNR in 3kHz? */       
+    /* SNR scaled from Eb/No est returned by FSK to SNR in 3000 Hz */
 
     fsk_get_demod_stats(hstates->fsk, &stats);
-    *snr_est = stats.snr_est;
+    *snr_est = stats.snr_est + 10*log10(hstates->Rs/3000);
 }
 
 void horus_get_modem_extended_stats (struct horus *hstates, struct MODEM_STATS *stats) {
+    int i;
+    
     assert(hstates != NULL);
+
     fsk_get_demod_stats(hstates->fsk, stats);
+
+    stats->snr_est = stats->snr_est + 10*log10(hstates->Rs/3000);
+
+    assert(hstates->mFSK <= MODEM_STATS_MAX_F_EST);
+    for (i=0; i<hstates->mFSK; i++) {
+        stats->f_est[i] = hstates->fsk->f_est[i];
+    }
 }
 
index 515bb5459169c8ca13eb4ab1f6a92481fb30739b..c3fd1a2485ea34ecdadaa54696ba3b00b04d9607 100644 (file)
@@ -56,11 +56,14 @@ int           horus_rx    (struct horus *hstates, char ascii_out[], short demod_
       
 int           horus_get_version              (void);
 int           horus_get_mode                 (struct horus *hstates);
+int           horus_get_Fs                   (struct horus *hstates);      
+int           horus_get_mFSK                 (struct horus *hstates);      
 void          horus_get_modem_stats          (struct horus *hstates, int *sync, float *snr_est);
 void          horus_get_modem_extended_stats (struct horus *hstates, struct MODEM_STATS *stats);
 
-/* how much storage you need for ascii_out[] */
+/* how much storage you need for demod_in[] and  ascii_out[] */
       
+int           horus_get_max_demod_in         (struct horus *hstates);
 int           horus_get_max_ascii_out_len    (struct horus *hstates);
 
 #endif
diff --git a/codec2-dev/src/horus_demod.c b/codec2-dev/src/horus_demod.c
new file mode 100644 (file)
index 0000000..65216d4
--- /dev/null
@@ -0,0 +1,230 @@
+/*---------------------------------------------------------------------------*\
+
+  FILE........: horus_demod.c
+  AUTHOR......: David Rowe
+  DATE CREATED: April 2018
+
+  Command line demo program for the Horus API, that exercises
+  horus_api.c using file input/output (can be stdin/stdout for real
+  time operation).  Prints JSON stats, just like Brady's fsk_demod.c
+  Can operate in Horus RTTY or Binary mode.
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2018 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include "horus_api.h"
+#include "fsk.h"
+#include "horus_l2.h"
+
+int main(int argc, char *argv[]) {
+    struct   horus *hstates;
+    struct   MODEM_STATS stats;
+    FILE    *fin,*fout;
+    int      i,j,Ndft,mode;
+    int      stats_ctr,stats_loop, stats_rate;
+    float    loop_time;
+    int      enable_stats = 0;
+
+    stats_loop = 0;
+    stats_rate = 8;
+    
+    int o = 0;
+    int opt_idx = 0;
+    while ( o != -1 ) {
+        static struct option long_opts[] = {
+            {"help",      no_argument,        0, 'h'},
+            {"mode",      required_argument,  0, 'm'},
+            {"stats",     optional_argument,  0, 't'},
+            {0, 0, 0, 0}
+        };
+        
+        o = getopt_long(argc,argv,"hm:t::",long_opts,&opt_idx);
+        
+        switch(o) {
+            case 'm':
+                mode = -1;
+                if ((strcmp(optarg, "RTTY") == 0) || (strcmp(optarg, "rtty") == 0)) {
+                    mode = HORUS_MODE_RTTY;
+                }
+                if ((strcmp(optarg, "BINARY") == 0) || (strcmp(optarg, "binary") == 0)) {
+                    mode = HORUS_MODE_BINARY;
+                }
+                if (mode = -1) {
+                    fprintf(stderr, "use --mode RTTY or --mode binary\n");
+                    exit(1);
+                }
+                break;
+            case 't':
+                enable_stats = 1;
+                if (optarg != NULL){
+                    stats_rate = atoi(optarg);
+                    if (stats_rate == 0) {
+                        stats_rate = 8;
+                    }
+                }
+                break;
+            case 'h':
+            case '?':
+                goto helpmsg;
+                break;
+        }
+
+        int dx = optind;
+    
+        if( (argc - dx) < 1) {
+            fprintf(stderr, "Too few arguments\n");
+            goto helpmsg;
+        }
+    
+        if( (argc - dx) > 5){
+            fprintf(stderr, "Too many arguments\n");
+        helpmsg:
+            fprintf(stderr,"usage: %s -m RTTY|binary [-t [r]] InputModemRawFile OutputAsciiFile\n",argv[0]);
+            fprintf(stderr,"\n");
+            fprintf(stderr,"InputModemRawFile      -  48 kHz 16 bit shorts real modem signal from radio\n");
+            fprintf(stderr," -m RTTY|binary\n"); 
+            fprintf(stderr,"--mode=RTTY|binary[r]  -  RTTY or binary Horus protcols\n");
+            fprintf(stderr," -t[r] --stats=[r]     -  Print out modem statistics to stderr in JSON.\n");
+            fprintf(stderr,"                          r, if provided, sets the number of modem frames"
+                                                 "between statistic printouts\n");
+            exit(1);
+        }
+        
+        /* Open files */
+
+        if (strcmp(argv[dx + 1],"-")==0) {
+            fin = stdin;
+        } else {
+            fin = fopen(argv[dx + 1],"rb");
+        }
+    
+        if (strcmp(argv[dx + 2],"-")==0) {
+            fout = stdout;
+        } else {
+            fout = fopen(argv[dx + 4],"w");
+        }
+        
+        if ((fin==NULL) || (fout==NULL)) {
+            fprintf(stderr,"Couldn't open test vector files\n");
+            exit(1);
+        }
+    }
+
+    /* end command line processing */
+
+    hstates = horus_open(mode);
+    if (hstates == NULL) {
+        fprintf(stderr, "Couldn't open Horus API\n");
+        exit(1);
+    }
+    
+    if (enable_stats) {
+        loop_time = (float)horus_nin(hstates)/horus_get_Fs(hstates);
+        stats_loop = (int)(1.0/(stats_rate*loop_time));
+        stats_ctr = 0;
+    }
+    
+    int   max_demod_in = horus_get_max_demod_in(hstates);
+    short demod_in[max_demod_in];
+    int   max_ascii_out = horus_get_max_ascii_out_len(hstates);
+    char  ascii_out[max_ascii_out];
+    
+    /* Main loop ----------------------------------------------------------------------- */
+
+    while( fread(demod_in, sizeof(short), horus_nin(hstates), fin) == horus_nin(hstates) ) {
+        if (horus_rx(hstates, ascii_out, demod_in)) {
+            fprintf(stdout, "%s\n", ascii_out);
+        }
+        
+        if (enable_stats && stats_ctr <= 0) {
+
+            horus_get_modem_extended_stats(hstates, &stats);
+
+           /* Print standard 2FSK stats */
+
+            fprintf(stderr,"{\"EbNodB\": %2.2f,\t\"ppm\": %d,",stats.snr_est, (int)stats.clock_offset);
+            fprintf(stderr,"\t\"f1_est\":%.1f,\t\"f2_est\":%.1f",stats.f_est[0], stats.f_est[1]);
+
+           /* Print 4FSK stats if in 4FSK mode */
+
+            if (horus_get_mFSK(hstates) == 4) {
+                fprintf(stderr,",\t\"f3_est\":%.1f,\t\"f4_est\":%.1f", stats.f_est[2], stats.f_est[3]);
+            }
+           
+           /* Print the eye diagram */
+
+            fprintf(stderr,",\t\"eye_diagram\":[");
+            for(i=0;i<stats.neyetr;i++){
+                fprintf(stderr,"[");
+                for(j=0;j<stats.neyesamp;j++){
+                    fprintf(stderr,"%f ",stats.rx_eye[i][j]);
+                    if(j<stats.neyesamp-1) fprintf(stderr,",");
+                }
+                fprintf(stderr,"]");
+                if(i<stats.neyetr-1) fprintf(stderr,",");
+            }
+            fprintf(stderr,"],");
+           
+           fprintf(stderr,"\"samp_fft\":[");
+
+            #ifdef FIXME_LATER
+            /* TODO: need a horus_ function to dig into modem spectrum */
+            
+           /* Print a sample of the FFT from the freq estimator */
+
+           Ndft = hstates->fsk->Ndft/2;
+           for(i=0; i<Ndft; i++){
+               fprintf(stderr,"%f ",(hstates->fsk->fft_est)[i]);
+               if(i<Ndft-1) fprintf(stderr,",");
+           }
+            #else
+
+            /* All zero dummy data for now */
+
+           Ndft = 128;
+           for(i=0; i<Ndft; i++) {
+               fprintf(stderr,"%f ", 0.0);
+               if(i<Ndft-1) fprintf(stderr,",");
+           }
+            
+            #endif
+
+           fprintf(stderr,"]}\n");
+            stats_ctr = stats_loop;
+        }
+        stats_ctr--;
+
+        if (fin == stdin || fout == stdin){
+            fflush(fin);
+            fflush(fout);
+        }
+    }
+    
+    horus_close(hstates);
+
+    return 0;
+}
+
index 86309d8e83f4720a4ced89a3699c31ce3d40eb7b..65d83fdbe81b1a85ce1d49cc4fe267e8c9a4a5b2 100644 (file)
 #include "comp.h"
 #include "kiss_fft.h"
 
-#define MODEM_STATS_NC_MAX 20
-#define MODEM_STATS_NR_MAX 6
-#define MODEM_STATS_ET_MAX 8
-#define MODEM_STATS_NSPEC            512
-#define MODEM_STATS_MAX_F_HZ         4000
-
+#define MODEM_STATS_NC_MAX    20
+#define MODEM_STATS_NR_MAX    6
+#define MODEM_STATS_ET_MAX    8
+#define MODEM_STATS_NSPEC     512
+#define MODEM_STATS_MAX_F_HZ  4000
+#define MODEM_STATS_MAX_F_EST 4
+      
 struct MODEM_STATS {
     int    Nc;
     float  snr_est;                          /* estimated SNR of rx signal in dB (3 kHz noise BW)  */
@@ -58,6 +59,10 @@ struct MODEM_STATS {
     int    neyetr;                           /* How many eye traces are plotted */
     int    neyesamp;                         /* How many samples in the eye diagram */
 
+    /* optional for FSK modems - est tone freqs */
+
+    float f_est[MODEM_STATS_MAX_F_EST];
+    
     /* Buf for FFT/waterfall */
 
     float        fft_buf[2*MODEM_STATS_NSPEC];