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) {
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 */
*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];
+ }
}
--- /dev/null
+/*---------------------------------------------------------------------------*\
+
+ 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;
+}
+