kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
				
				
				
			
		
			
				
	
	
		
			436 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			436 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|   FILE........: fsk_demod.c
 | |
|   AUTHOR......: Brady O'Brien
 | |
|   DATE CREATED: 8 January 2016
 | |
| 
 | |
|   C test driver for fsk_demod in fsk.c. Reads in a stream of 32 bit cpu endian
 | |
|   floats and writes out the detected bits
 | |
|    
 | |
| 
 | |
| \*---------------------------------------------------------------------------*/
 | |
| 
 | |
| /*
 | |
|   Copyright (C) 2016 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/>.
 | |
| */
 | |
| 
 | |
| #define TEST_FRAME_SIZE 100  /* must match fsk_get_test_bits.c */
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <getopt.h>
 | |
| #include <time.h>
 | |
| #include <signal.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| 
 | |
| #include "fsk.h"
 | |
| #include "codec2_fdmdv.h"
 | |
| #include "modem_stats.h"
 | |
| 
 | |
| /* cleanly exit when we get a SIGTERM */
 | |
| 
 | |
| void sig_handler(int signo)
 | |
| {
 | |
|     if (signo == SIGTERM) {
 | |
|         exit(0);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int main(int argc,char *argv[]){
 | |
|     struct FSK *fsk;
 | |
|     struct MODEM_STATS stats;
 | |
|     int Fs,Rs,M,P,stats_ctr,stats_loop;
 | |
|     float loop_time;
 | |
|     int enable_stats = 0;
 | |
|     int hbr = 1;
 | |
|     FILE *fin,*fout;
 | |
|     uint8_t *bitbuf = NULL;
 | |
|     int16_t *rawbuf;
 | |
|     COMP *modbuf;
 | |
|     float *sdbuf = NULL;
 | |
|     int i,j,Ndft;
 | |
|     int soft_dec_mode = 0;
 | |
|     stats_loop = 0;
 | |
|     int complex_input = 1, bytes_per_sample = 2;
 | |
|     int stats_rate = 8;
 | |
|     int testframe_mode = 0;
 | |
|     P = 0;
 | |
|     M = 0;
 | |
|     int fsk_lower = -1;
 | |
|     int fsk_upper = -1;
 | |
|     
 | |
|     /* -m 2/4 --mode 2/4  - FSK mode
 | |
|      * -l --lbr      - Low bit rate mode
 | |
|      * -p n --conv n    - Downconverted symbol size
 | |
|      * If p is unspecified, it will default to Ts
 | |
|      * -c --cs16      - Complex (signed 16-bit)
 | |
|      * -d --cu8      - Complex (unsigned 8-bit)
 | |
|      * If neither -c or -d are specified, input will be real valued signed 16-bit
 | |
|      * -t --stats - dump demod statistics to stderr
 | |
|      * -s --soft-dec - ouput soft decision (float 32-bit)
 | |
|      */
 | |
|      /* usage: [-l] [-p P]  [-s] [(-c|-d)] [-t] (2|4) SampleRate SymbolRatez InputModemRawFile OutputOneBitPerCharFile */
 | |
|      
 | |
|      int o = 0;
 | |
|      int opt_idx = 0;
 | |
|      while( o != -1 ){
 | |
|         static struct option long_opts[] = {
 | |
|             {"help",      no_argument,        0, 'h'},
 | |
|             {"lbr",       no_argument,        0, 'l'},
 | |
|             {"conv",      required_argument,  0, 'p'},
 | |
|             {"cs16",      no_argument,        0, 'c'},
 | |
|             {"cu8",       no_argument,        0, 'd'},
 | |
|             {"fsk_lower", optional_argument,  0, 'b'},
 | |
|             {"fsk_upper", optional_argument,  0, 'u'},
 | |
|             {"stats",     optional_argument,  0, 't'},
 | |
|             {"soft-dec",  no_argument,        0, 's'},
 | |
|             {"testframes",no_argument,        0, 'f'},
 | |
|             {0, 0, 0, 0}
 | |
|         };
 | |
|         
 | |
|         o = getopt_long(argc,argv,"fhlp:cdt::sb:u:",long_opts,&opt_idx);
 | |
|         
 | |
|         switch(o){
 | |
|             case 'l':
 | |
|                 hbr = 0;
 | |
|                 break;
 | |
|             case 'c':
 | |
|                 complex_input = 2;
 | |
|                 bytes_per_sample = 2;
 | |
|                 break;
 | |
|             case 'd':
 | |
|                 complex_input = 2;
 | |
|                 bytes_per_sample = 1;
 | |
|                 break;
 | |
|             case 'f':
 | |
|                 testframe_mode = 1;
 | |
|                 break;
 | |
|             case 't':
 | |
|                 enable_stats = 1;
 | |
|                 if(optarg != NULL){
 | |
|                     stats_rate = atoi(optarg);
 | |
|                     if(stats_rate == 0){
 | |
|                         stats_rate = 8;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case 's':
 | |
|                 soft_dec_mode = 1;
 | |
|                 break;
 | |
|             case 'p':
 | |
|                 P = atoi(optarg);
 | |
|                 break;
 | |
|             case 'b':
 | |
|                 if (optarg != NULL){
 | |
|                     fsk_lower = atoi(optarg);
 | |
|                 }
 | |
|                 break;
 | |
|             case 'u':
 | |
|                 if (optarg != NULL){
 | |
|                     fsk_upper = atoi(optarg);
 | |
|                 }
 | |
|                 break;
 | |
|             case 'h':
 | |
|             case '?':
 | |
|                 goto helpmsg;
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
|     int dx = optind;
 | |
|     
 | |
|     if( (argc - dx) < 5){
 | |
|         fprintf(stderr, "Too few arguments\n");
 | |
|         goto helpmsg;
 | |
|     }
 | |
|     
 | |
|     if( (argc - dx) > 5){
 | |
|         fprintf(stderr, "Too many arguments\n");
 | |
|         helpmsg:
 | |
|         fprintf(stderr,"usage: %s [-l] [-p P]  [-s] [(-c|-d)] [-t [r]] [-f] (2|4) SampleRate SymbolRate InputModemRawFile OutputFile\n",argv[0]);
 | |
|         fprintf(stderr," -lP --conv=P      -  P specifies the rate at which symbols are down-converted before further processing\n");
 | |
|         fprintf(stderr,"                        P must be divisible by the symbol size. Smaller P values will result in faster\n");
 | |
|         fprintf(stderr,"                        processing but lower demodulation preformance. If no P value is specified,\n");
 | |
|         fprintf(stderr,"                        P will default to it's highes possible value\n");
 | |
|         fprintf(stderr," -c --cs16         -  The raw input file will be in complex signed 16 bit format.\n");
 | |
|         fprintf(stderr," -d --cu8          -  The raw input file will be in complex unsigned 8 bit format.\n");
 | |
|         fprintf(stderr,"                        If neither -c nor -d are used, the input should be in signed 16 bit format.\n");
 | |
|         fprintf(stderr," -f --testframes   -  Testframe mode, prints stats to stderr when a testframe is detected, if -t (JSON) \n");
 | |
|         fprintf(stderr,"                        is enabled stats will be in JSON format\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");
 | |
|         fprintf(stderr," -s --soft-dec     -  The output file will be in a soft-decision format, with one 32-bit float per bit.\n");
 | |
|         fprintf(stderr,"                        If -s is not used, the output will be in a 1 byte-per-bit format.\n");
 | |
|         exit(1);
 | |
|     }
 | |
|     
 | |
|     /* Extract parameters */
 | |
|     M = atoi(argv[dx]);
 | |
|     Fs = atoi(argv[dx + 1]);
 | |
|     Rs = atoi(argv[dx + 2]);
 | |
|     
 | |
|     if( P == 0 ){
 | |
|         P = Fs/Rs;
 | |
|     }
 | |
|     
 | |
|     if( (M!=2) && (M!=4) ){
 | |
|         fprintf(stderr,"Mode %d is not valid. Mode must be 2 or 4.\n",M);
 | |
|         goto helpmsg;
 | |
|     }
 | |
|     
 | |
|     /* Open files */
 | |
|     if(strcmp(argv[dx + 3],"-")==0){
 | |
|         fin = stdin;
 | |
|     }else{
 | |
|         fin = fopen(argv[dx + 3],"r");
 | |
|     }
 | |
|     
 | |
|     if(strcmp(argv[dx + 4],"-")==0){
 | |
|         fout = stdout;
 | |
|     }else{
 | |
|         fout = fopen(argv[dx + 4],"w");
 | |
|     }
 | |
|     
 | |
|     
 | |
|     /* set up FSK */
 | |
|     if(!hbr) {
 | |
|         fsk = fsk_create(Fs,Rs,M,1200,400);
 | |
|     }
 | |
|     else {
 | |
|         fsk = fsk_create_hbr(Fs,Rs,P,M,1200,400);
 | |
|         if(fsk_lower> 0 && fsk_upper > fsk_lower){
 | |
|             fsk_set_est_limits(fsk,fsk_lower,fsk_upper);
 | |
|             fprintf(stderr,"Setting estimator limits to %d to %d Hz.\n",fsk_lower, fsk_upper);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(fin==NULL || fout==NULL || fsk==NULL){
 | |
|         fprintf(stderr,"Couldn't open files\n");
 | |
|         exit(1);
 | |
|     }
 | |
| 
 | |
|     /* set up testframe mode */
 | |
|          
 | |
|     int      testframecnt, bitcnt, biterr, testframe_detected;
 | |
|     uint8_t *bitbuf_tx = NULL, *bitbuf_rx = NULL;
 | |
|     if (testframe_mode) {
 | |
|         bitbuf_tx = (uint8_t*)malloc(sizeof(uint8_t)*TEST_FRAME_SIZE); assert(bitbuf_tx != NULL);
 | |
|         bitbuf_rx = (uint8_t*)malloc(sizeof(uint8_t)*TEST_FRAME_SIZE); assert(bitbuf_rx != NULL);
 | |
|     
 | |
|         /* Generate known tx frame from known seed */
 | |
|         
 | |
|         srand(158324);
 | |
|         for(i=0; i<TEST_FRAME_SIZE; i++){
 | |
|             bitbuf_tx[i] = rand()&0x1;
 | |
|             bitbuf_rx[i] = 0;
 | |
|         }
 | |
| 
 | |
|         testframecnt = 0;
 | |
|         bitcnt = 0;
 | |
|         biterr = 0;
 | |
|     }
 | |
|     
 | |
|     if(enable_stats){
 | |
|         loop_time = ((float)fsk_nin(fsk))/((float)Fs);
 | |
|         stats_loop = (int)(1/(stats_rate*loop_time));
 | |
|         stats_ctr = 0;
 | |
|     }
 | |
|     
 | |
|     /* allocate buffers for processing */
 | |
|     if(soft_dec_mode){
 | |
|         sdbuf = (float*)malloc(sizeof(float)*fsk->Nbits); assert(sdbuf != NULL);
 | |
|     }else{
 | |
|         bitbuf = (uint8_t*)malloc(sizeof(uint8_t)*fsk->Nbits); assert(bitbuf != NULL);
 | |
|     }
 | |
|     rawbuf = (int16_t*)malloc(bytes_per_sample*(fsk->N+fsk->Ts*2)*complex_input);
 | |
|     modbuf = (COMP*)malloc(sizeof(COMP)*(fsk->N+fsk->Ts*2));
 | |
| 
 | |
|     /* set up signal handler so we can terminate gracefully */
 | |
|     
 | |
|     if (signal(SIGTERM, sig_handler) == SIG_ERR) {
 | |
|         printf("\ncan't catch SIGTERM\n");
 | |
|     }
 | |
|     
 | |
|     /* Demodulate! */
 | |
|     
 | |
|     while( fread(rawbuf,bytes_per_sample*complex_input,fsk_nin(fsk),fin) == fsk_nin(fsk) ){
 | |
|         /* convert input to a buffer of floats.  Note scaling isn't really necessary for FSK */
 | |
| 
 | |
|        if (complex_input == 1) {
 | |
|             /* S16 real input */
 | |
|             for(i=0;i<fsk_nin(fsk);i++){
 | |
|                 modbuf[i].real = ((float)rawbuf[i])/FDMDV_SCALE;
 | |
|                 modbuf[i].imag = 0.0;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if (bytes_per_sample == 1) {
 | |
|                 /* U8 complex */
 | |
|                 uint8_t *rawbuf_u8 = (uint8_t*)rawbuf;
 | |
|                 for(i=0;i<fsk_nin(fsk);i++){
 | |
|                     modbuf[i].real = ((float)rawbuf_u8[2*i]-127.0)/128.0;
 | |
|                     modbuf[i].imag = ((float)rawbuf_u8[2*i+1]-127.0)/128.0;
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 /* S16 complex */
 | |
|                 for(i=0;i<fsk_nin(fsk);i++){
 | |
|                     modbuf[i].real = ((float)rawbuf[2*i])/FDMDV_SCALE;
 | |
|                     modbuf[i].imag = ((float)rawbuf[2*i+1]/FDMDV_SCALE);
 | |
|                 }
 | |
|             }            
 | |
|         }
 | |
| 
 | |
|         if(soft_dec_mode){
 | |
|             fsk_demod_sd(fsk,sdbuf,modbuf);
 | |
|         }else{
 | |
|             fsk_demod(fsk,bitbuf,modbuf);
 | |
|         }
 | |
|         
 | |
|         testframe_detected = 0;
 | |
|         if (testframe_mode) {
 | |
|             /* attempt to find a testframe and update stats */
 | |
|                     /* update silding window of input bits */
 | |
| 
 | |
|             int errs;
 | |
|             for(j=0; j<fsk->Nbits; j++) {
 | |
|                 for(i=0; i<TEST_FRAME_SIZE-1; i++) {
 | |
|                     bitbuf_rx[i] = bitbuf_rx[i+1];
 | |
|                 }
 | |
|                 if (soft_dec_mode == 1) {
 | |
|                     bitbuf_rx[TEST_FRAME_SIZE-1] = sdbuf[j] < 0.0;
 | |
|                 }
 | |
|                 else {
 | |
|                     bitbuf_rx[TEST_FRAME_SIZE-1] = bitbuf[j];
 | |
|                 }
 | |
| 
 | |
|                 /* compare to know tx frame.  If they are time aligned, there
 | |
|                    will be a fairly low bit error rate */
 | |
| 
 | |
|                 errs = 0;
 | |
|                 for(i=0; i<TEST_FRAME_SIZE; i++) {
 | |
|                     if (bitbuf_rx[i] != bitbuf_tx[i]) {
 | |
|                         errs++;
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (errs < 0.1*TEST_FRAME_SIZE) {
 | |
|                     /* OK, we have a valid test frame sync, so lets count errors */
 | |
|                     testframe_detected = 1;
 | |
|                     testframecnt++;
 | |
|                     bitcnt += TEST_FRAME_SIZE;
 | |
|                     biterr += errs;
 | |
|                     if (enable_stats == 0) {
 | |
|                         fprintf(stderr,"errs: %d FSK BER %f, bits tested %d, bit errors %d\n",
 | |
|                             errs, ((float)biterr/(float)bitcnt),bitcnt,biterr);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         } /* if (testframe_mode) ... */
 | |
|         
 | |
|         if (enable_stats) {
 | |
|             if ((stats_ctr < 0) || testframe_detected) {
 | |
|                 fsk_get_demod_stats(fsk,&stats);
 | |
| 
 | |
|                 /* Print standard 2FSK stats */
 | |
| 
 | |
|                 fprintf(stderr,"{");
 | |
|                 time_t seconds  = time(NULL);
 | |
| 
 | |
|                 fprintf(stderr,"\"secs\": %ld, \"EbNodB\": %5.1f, \"ppm\": %4d,",seconds, stats.snr_est, (int)fsk->ppm);
 | |
|                 fprintf(stderr," \"f1_est\":%.1f, \"f2_est\":%.1f",fsk->f_est[0],fsk->f_est[1]);
 | |
| 
 | |
|                 /* Print 4FSK stats if in 4FSK mode */
 | |
| 
 | |
|                 if(fsk->mode == 4){
 | |
|                     fprintf(stderr,", \"f3_est\":%.1f, \"f4_est\":%.1f",fsk->f_est[2],fsk->f_est[3]);
 | |
|                 }
 | |
| 	    
 | |
|                 if (testframe_mode == 0) {
 | |
|                     /* 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,"],");
 | |
| 	    
 | |
|                     /* Print a sample of the FFT from the freq estimator */
 | |
|                     fprintf(stderr,"\"samp_fft\":[");
 | |
|                     Ndft = fsk->Ndft/2;
 | |
|                     for(i=0; i<Ndft; i++){
 | |
|                         fprintf(stderr,"%f ",(fsk->fft_est)[i]);
 | |
|                         if(i<Ndft-1) fprintf(stderr,",");
 | |
|                     }
 | |
|                     fprintf(stderr,"]");
 | |
|                 }
 | |
|                 
 | |
|                 if (testframe_mode) {
 | |
|                     fprintf(stderr,", \"frames\":%d, \"bits\":%d, \"errs\":%d",testframecnt,bitcnt,biterr);
 | |
|                 }
 | |
|                 
 | |
|                 fprintf(stderr,"}\n");                
 | |
| 
 | |
|                 if (stats_ctr < 0) {
 | |
|                     stats_ctr = stats_loop;
 | |
|                 }
 | |
|             }
 | |
|             if (testframe_mode == 0) {
 | |
|                 stats_ctr--;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if(soft_dec_mode){
 | |
|             fwrite(sdbuf,sizeof(float),fsk->Nbits,fout);
 | |
|         }else{
 | |
|             fwrite(bitbuf,sizeof(uint8_t),fsk->Nbits,fout);
 | |
|         }
 | |
| 
 | |
|         if(fin == stdin || fout == stdin){
 | |
|             fflush(fin);
 | |
|             fflush(fout);
 | |
|         }
 | |
|     } /* while(fread ...... */
 | |
| 
 | |
|     if (testframe_mode) {
 | |
|         free(bitbuf_tx);
 | |
|         free(bitbuf_rx);
 | |
|     }
 | |
|     
 | |
|     if(soft_dec_mode){
 | |
|         free(sdbuf);
 | |
|     }else{
 | |
|         free(bitbuf);
 | |
|     }
 | |
|     
 | |
|     free(rawbuf);
 | |
|     free(modbuf);
 | |
|     
 | |
|     fclose(fin);
 | |
|     fclose(fout);
 | |
|     fsk_destroy(fsk);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 |