1 /*---------------------------------------------------------------------------*\
2 
3   FILE........: fsk_demod.c
4   AUTHOR......: Brady O'Brien and David Rowe
5   DATE CREATED: 8 January 2016
6 
7   Command line FSK demodulator.  Reads in FSK samples, writes demodulated
8   output bits.
9 
10 \*---------------------------------------------------------------------------*/
11 
12 /*
13   Copyright (C) 2016 David Rowe
14 
15   All rights reserved.
16 
17   This program is free software; you can redistribute it and/or modify
18   it under the terms of the GNU Lesser General Public License version 2.1, as
19   published by the Free Software Foundation.  This program is
20   distributed in the hope that it will be useful, but WITHOUT ANY
21   WARRANTY; without even the implied warranty of MERCHANTABILITY or
22   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
23   License for more details.
24 
25   You should have received a copy of the GNU Lesser General Public License
26   along with this program; if not, see <http://www.gnu.org/licenses/>.
27 */
28 
29 #define TEST_FRAME_SIZE 100  /* must match fsk_get_test_bits.c */
30 
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <getopt.h>
35 #include <time.h>
36 #include <signal.h>
37 #include <unistd.h>
38 
39 #include "fsk.h"
40 #include "codec2_fdmdv.h"
41 #include "mpdecode_core.h"
42 #include "modem_stats.h"
43 
44 /* cleanly exit when we get a SIGTERM */
45 
sig_handler(int signo)46 void sig_handler(int signo)
47 {
48     if (signo == SIGTERM) {
49         exit(0);
50     }
51 }
52 
main(int argc,char * argv[])53 int main(int argc,char *argv[]){
54     struct FSK *fsk;
55     struct MODEM_STATS stats;
56     int Fs,Rs,M,P,stats_ctr,stats_loop;
57     float loop_time;
58     int enable_stats = 0;
59     FILE *fin,*fout;
60     uint8_t *bitbuf = NULL;
61     int16_t *rawbuf;
62     COMP *modbuf;
63     float *rx_filt = NULL;
64     float *llrs = NULL;
65     int i,j,Ndft;
66     int soft_dec_mode = 0;
67     stats_loop = 0;
68     int complex_input = 1, bytes_per_sample = 2;
69     int stats_rate = 8;
70     int testframe_mode = 0;
71     P = 8;  /* default */
72     M = 0;
73     int fsk_lower = 0;
74     int fsk_upper = 0;
75     int user_fsk_lower = 0;
76     int user_fsk_upper = 0;
77     int nsym = FSK_DEFAULT_NSYM;
78     int mask = 0;
79     int tone_separation = 100;
80 
81     int o = 0;
82     int opt_idx = 0;
83     while( o != -1 ){
84         static struct option long_opts[] = {
85             {"help",      no_argument,        0, 'h'},
86             {"conv",      required_argument,  0, 'p'},
87             {"cs16",      no_argument,        0, 'c'},
88             {"cu8",       no_argument,        0, 'd'},
89             {"fsk_lower", required_argument,  0, 'b'},
90             {"fsk_upper", required_argument,  0, 'u'},
91             {"stats",     optional_argument,  0, 't'},
92             {"soft-dec",  no_argument,        0, 's'},
93             {"testframes",no_argument,        0, 'f'},
94             {"nsym",      required_argument,  0, 'n'},
95             {"mask",      required_argument,  0, 'm'},
96             {0, 0, 0, 0}
97         };
98 
99         o = getopt_long(argc,argv,"fhlp:cdt::sb:u:m",long_opts,&opt_idx);
100 
101         switch(o){
102         case 'c':
103             complex_input = 2;
104             bytes_per_sample = 2;
105             break;
106         case 'd':
107             complex_input = 2;
108             bytes_per_sample = 1;
109             break;
110         case 'f':
111             testframe_mode = 1;
112             break;
113         case 't':
114             enable_stats = 1;
115             if(optarg != NULL){
116                 stats_rate = atoi(optarg);
117                 if(stats_rate == 0){
118                     stats_rate = 8;
119                 }
120             }
121             break;
122         case 's':
123             soft_dec_mode = 1;
124             break;
125         case 'p':
126             P = atoi(optarg);
127             break;
128         case 'b':
129             if (optarg != NULL) {
130                 fsk_lower = atoi(optarg);
131                 user_fsk_lower = 1;
132             }
133             break;
134         case 'u':
135             if (optarg != NULL){
136                 fsk_upper = atoi(optarg);
137                  user_fsk_upper = 1;
138             }
139             break;
140         case 'n':
141             if (optarg != NULL) {
142                 nsym = atoi(optarg);
143             }
144             break;
145         case 'm':
146             mask = 1;
147             tone_separation = atoi(optarg);
148             break;
149         case 'h':
150         case '?':
151             goto helpmsg;
152             break;
153         }
154     }
155     int dx = optind;
156 
157     if( (argc - dx) < 5) {
158         fprintf(stderr, "Too few arguments\n");
159         goto helpmsg;
160     }
161 
162     if( (argc - dx) > 5) {
163         fprintf(stderr, "Too many arguments\n");
164     helpmsg:
165         fprintf(stderr,"usage: %s [options] (2|4) SampleRate SymbolRate InputModemRawFile OutputFile\n",argv[0]);
166         fprintf(stderr," -c --cs16          The raw input file will be in complex signed 16 bit format.\n");
167         fprintf(stderr," -d --cu8           The raw input file will be in complex unsigned 8 bit format.\n");
168         fprintf(stderr,"                    If neither -c nor -d are used, the input should be in signed 16 bit format.\n");
169         fprintf(stderr," -f --testframes    Testframe mode, prints stats to stderr when a testframe is detected, if -t (JSON) \n");
170         fprintf(stderr,"                    is enabled stats will be in JSON format\n");
171         fprintf(stderr," -t[r] --stats=[r]  Print out modem statistics to stderr in JSON.\n");
172         fprintf(stderr,"                    r, if provided, sets the number of modem frames between statistic printouts.\n");
173         fprintf(stderr," -s --soft-dec      The output file will be in a soft-decision format, with one 32-bit float per bit.\n");
174         fprintf(stderr,"                    If -s is not used, the output will be in a 1 byte-per-bit format.\n");
175         fprintf(stderr," -p P               Number of timing offsets we have to choose from, default %d.\n", FSK_DEFAULT_P);
176         fprintf(stderr,"                    Fs/Rs/P must be an integer.  Smaller values result in faster operation, but\n");
177         fprintf(stderr,"                    coarse sampling. Try to keep >= 8\n");
178         fprintf(stderr,"                    processing but lower demodulation performance. Default %d\n", FSK_DEFAULT_P);
179         fprintf(stderr," --fsk_lower freq   lower limit of freq estimator (default 0 for real input, -Fs/2  for complex input)\n");
180         fprintf(stderr," --fsk_upper freq   upper limit of freq estimator (default Fs/2)\n");
181         fprintf(stderr," --nsym Nsym        number of symbols used for estimators. Default %d\n", FSK_DEFAULT_NSYM);
182         fprintf(stderr," --mask TxFreqSpace Use \"mask\" freq estimator (default is \"peak\" estimator)\n");
183         exit(1);
184     }
185 
186     /* Extract parameters */
187     M = atoi(argv[dx]);
188     Fs = atoi(argv[dx + 1]);
189     Rs = atoi(argv[dx + 2]);
190 
191     if( (M!=2) && (M!=4) ){
192         fprintf(stderr,"Mode %d is not valid. Mode must be 2 or 4.\n",M);
193         goto helpmsg;
194     }
195 
196     /* Open files */
197     if(strcmp(argv[dx + 3],"-")==0){
198         fin = stdin;
199     }else{
200         fin = fopen(argv[dx + 3],"r");
201     }
202 
203     if(strcmp(argv[dx + 4],"-")==0){
204         fout = stdout;
205     }else{
206         fout = fopen(argv[dx + 4],"w");
207     }
208 
209     /* set up FSK */
210     fsk = fsk_create_hbr(Fs,Rs,M,P,nsym,FSK_NONE,tone_separation);
211 
212     /* set freq estimator limits */
213     if (!user_fsk_lower) {
214         if (complex_input == 1)
215             fsk_lower = 0;
216         else
217             fsk_lower = -Fs/2;
218     }
219     if (!user_fsk_upper) {
220         fsk_upper = Fs/2;
221     }
222     fprintf(stderr,"Setting estimator limits to %d to %d Hz.\n", fsk_lower, fsk_upper);
223     fsk_set_freq_est_limits(fsk,fsk_lower,fsk_upper);
224 
225     fsk_set_freq_est_alg(fsk, mask);
226 
227     if(fin==NULL || fout==NULL || fsk==NULL){
228         fprintf(stderr,"Couldn't open files\n");
229         exit(1);
230     }
231 
232     /* set up testframe mode */
233 
234     int      testframecnt, bitcnt, biterr, testframe_detected;
235     uint8_t *bitbuf_tx = NULL, *bitbuf_rx = NULL;
236     if (testframe_mode) {
237         bitbuf_tx = (uint8_t*)malloc(sizeof(uint8_t)*TEST_FRAME_SIZE); assert(bitbuf_tx != NULL);
238         bitbuf_rx = (uint8_t*)malloc(sizeof(uint8_t)*TEST_FRAME_SIZE); assert(bitbuf_rx != NULL);
239 
240         /* Generate known tx frame from known seed */
241 
242         srand(158324);
243         for(i=0; i<TEST_FRAME_SIZE; i++){
244             bitbuf_tx[i] = rand()&0x1;
245             bitbuf_rx[i] = 0;
246         }
247 
248         testframecnt = 0;
249         bitcnt = 0;
250         biterr = 0;
251     }
252 
253     if(enable_stats){
254         loop_time = ((float)fsk_nin(fsk))/((float)Fs);
255         stats_loop = (int)(1/(stats_rate*loop_time));
256         stats_ctr = 0;
257     }
258 
259     /* allocate buffers for processing */
260     if (soft_dec_mode) {
261         rx_filt = (float*)malloc(sizeof(float)*fsk->mode*fsk->Nsym); assert(rx_filt != NULL);
262         llrs = (float*)malloc(sizeof(float)*fsk->Nbits); assert(llrs != NULL);
263     } else {
264         bitbuf = (uint8_t*)malloc(sizeof(uint8_t)*fsk->Nbits); assert(bitbuf != NULL);
265     }
266     rawbuf = (int16_t*)malloc(bytes_per_sample*(fsk->N+fsk->Ts*2)*complex_input);
267     modbuf = (COMP*)malloc(sizeof(COMP)*(fsk->N+fsk->Ts*2));
268 
269     /* set up signal handler so we can terminate gracefully */
270 
271     if (signal(SIGTERM, sig_handler) == SIG_ERR) {
272         printf("\ncan't catch SIGTERM\n");
273     }
274 
275     /* Demodulate! */
276 
277     while( fread(rawbuf,bytes_per_sample*complex_input,fsk_nin(fsk),fin) == fsk_nin(fsk) ){
278         /* convert input to a buffer of floats.  Note scaling isn't really necessary for FSK */
279 
280         if (complex_input == 1) {
281             /* S16 real input */
282             for(i=0;i<fsk_nin(fsk);i++){
283                 modbuf[i].real = ((float)rawbuf[i])/FDMDV_SCALE;
284                 modbuf[i].imag = 0.0;
285             }
286         }
287         else {
288             if (bytes_per_sample == 1) {
289                 /* U8 complex */
290                 uint8_t *rawbuf_u8 = (uint8_t*)rawbuf;
291                 for(i=0;i<fsk_nin(fsk);i++){
292                     modbuf[i].real = ((float)rawbuf_u8[2*i]-127.0)/128.0;
293                     modbuf[i].imag = ((float)rawbuf_u8[2*i+1]-127.0)/128.0;
294                 }
295             }
296             else {
297                 /* S16 complex */
298                 for(i=0;i<fsk_nin(fsk);i++){
299                     modbuf[i].real = ((float)rawbuf[2*i])/FDMDV_SCALE;
300                     modbuf[i].imag = ((float)rawbuf[2*i+1]/FDMDV_SCALE);
301                 }
302             }
303         }
304 
305         if (soft_dec_mode) {
306             int bps = log2(fsk->mode);
307             assert(fsk->Nbits == bps*fsk->Nsym);
308             /* output bit LLRs */
309             fsk_demod_sd(fsk, rx_filt, modbuf);
310             fsk_rx_filt_to_llrs(llrs, rx_filt, fsk->v_est, fsk->SNRest, fsk->mode, fsk->Nsym);
311         } else {
312             fsk_demod(fsk,bitbuf,modbuf);
313         }
314 
315         testframe_detected = 0;
316         if (testframe_mode) {
317             assert(soft_dec_mode == 0);
318 
319             /* attempt to find a testframe and update stats */
320             /* update silding window of input bits */
321 
322             int errs;
323             for(j=0; j<fsk->Nbits; j++) {
324                 for(i=0; i<TEST_FRAME_SIZE-1; i++) {
325                     bitbuf_rx[i] = bitbuf_rx[i+1];
326                 }
327                 bitbuf_rx[TEST_FRAME_SIZE-1] = bitbuf[j];
328 
329                 /* compare to know tx frame.  If they are time aligned, there
330                    will be a fairly low bit error rate */
331 
332                 errs = 0;
333                 for(i=0; i<TEST_FRAME_SIZE; i++) {
334                     if (bitbuf_rx[i] != bitbuf_tx[i]) {
335                         errs++;
336                     }
337                 }
338 
339                 if (errs < 0.1*TEST_FRAME_SIZE) {
340                     /* OK, we have a valid test frame sync, so lets count errors */
341                     testframe_detected = 1;
342                     testframecnt++;
343                     bitcnt += TEST_FRAME_SIZE;
344                     biterr += errs;
345                     if (enable_stats == 0) {
346                         fprintf(stderr,"errs: %d FSK BER %f, bits tested %d, bit errors %d\n",
347                                 errs, ((float)biterr/(float)bitcnt),bitcnt,biterr);
348                     }
349                 }
350             }
351         } /* if (testframe_mode) ... */
352 
353         if (enable_stats) {
354             if ((stats_ctr < 0) || testframe_detected) {
355                 fsk_get_demod_stats(fsk,&stats);
356 
357                 /* Print standard 2FSK stats */
358 
359                 fprintf(stderr,"{");
360                 time_t seconds  = time(NULL);
361 
362                 fprintf(stderr,"\"secs\": %ld, \"EbNodB\": %5.1f, \"ppm\": %4d,",seconds, stats.snr_est, (int)fsk->ppm);
363                 float *f_est;
364                 if (fsk->freq_est_type)
365                     f_est = fsk->f2_est;
366                 else
367                     f_est = fsk->f_est;
368                 fprintf(stderr," \"f1_est\":%.1f, \"f2_est\":%.1f",f_est[0],f_est[1]);
369 
370                 /* Print 4FSK stats if in 4FSK mode */
371 
372                 if(fsk->mode == 4){
373                     fprintf(stderr,", \"f3_est\":%.1f, \"f4_est\":%.1f",f_est[2],f_est[3]);
374                 }
375 
376                 if (testframe_mode == 0) {
377                     /* Print the eye diagram */
378 
379                     fprintf(stderr,",\t\"eye_diagram\":[");
380                     for(i=0;i<stats.neyetr;i++){
381                         fprintf(stderr,"[");
382                         for(j=0;j<stats.neyesamp;j++){
383                             fprintf(stderr,"%f ",stats.rx_eye[i][j]);
384                             if(j<stats.neyesamp-1) fprintf(stderr,",");
385                         }
386                         fprintf(stderr,"]");
387                         if(i<stats.neyetr-1) fprintf(stderr,",");
388                     }
389                     fprintf(stderr,"],");
390 
391                     /* Print a sample of the FFT from the freq estimator */
392                     fprintf(stderr,"\"samp_fft\":[");
393                     Ndft = fsk->Ndft/2;
394                     for(i=0; i<Ndft; i++){
395                         fprintf(stderr,"%f ",(fsk->Sf)[i]);
396                         if(i<Ndft-1) fprintf(stderr,",");
397                     }
398                     fprintf(stderr,"]");
399                 }
400 
401                 if (testframe_mode) {
402                     fprintf(stderr,", \"frames\":%d, \"bits\":%d, \"errs\":%d",testframecnt,bitcnt,biterr);
403                 }
404 
405                 fprintf(stderr,"}\n");
406 
407                 if (stats_ctr < 0) {
408                     stats_ctr = stats_loop;
409                 }
410             }
411             if (testframe_mode == 0) {
412                 stats_ctr--;
413             }
414         }
415 
416         if(soft_dec_mode){
417             fwrite(llrs,sizeof(float),fsk->Nbits,fout);
418         } else{
419             fwrite(bitbuf,sizeof(uint8_t),fsk->Nbits,fout);
420         }
421 
422         if(fout == stdin){
423             fflush(fout);
424         }
425     } /* while(fread ...... */
426 
427     if (testframe_mode) {
428         free(bitbuf_tx);
429         free(bitbuf_rx);
430     }
431 
432     if (soft_dec_mode) {
433         free(rx_filt);
434         free(llrs);
435     } else{
436         free(bitbuf);
437     }
438 
439     free(rawbuf);
440     free(modbuf);
441 
442     fclose(fin);
443     fclose(fout);
444     fsk_destroy(fsk);
445 
446     return 0;
447 }
448 
449