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