diff --git a/demod/iq_svcl/README.md b/demod/iq_svcl/README.md
index 7867894..c7cc3ec 100644
--- a/demod/iq_svcl/README.md
+++ b/demod/iq_svcl/README.md
@@ -23,16 +23,23 @@ receive IF stream from baseband IQ via TCP, default `PORT=1280 (iq_svcl.h)`
``: IF sample rate
`=8,16,32`: output/IF bits per (real) sample (u8, s16 or f32)
- down-converts up to `MAX_FQ=(4+1) (iq_base.h)` channels/signals. More signals than number of CPUs/cores is not recommended.
+ down-converts up to `MAX_FQ=(4+1) (iq_base.h)` channels/signals. (On older CPUs, more signals than number of CPU cores is not recommended.)
(Note: If the baseband sample rate has no appropriate factors (e.g. if prime), the IF sample rate might be high and IF-processing slow.)
- One channel can be used for scanning, `--fft ` makes FFT (2 seconds average).
- The FFT is saved in `` as `;`, approx. 200 Hz per bin.
+ One channel can be used for scanning, e.g. `./iq_server --fft_avg ` makes *m* rows of avg-FFT (FFT_AVG=2 seconds average), and
+ this channel will be reused for client FFT requests. Only one channel/thread can be used for FFT/scanning. A client can request a new FFT,
+ if the last FFT has finished.
+ There are two kinds of FFTs, `fft_all` and `fft_avg`. `fft_avg` integrates over FFT_AVG=2 seconds and can be used for signal peak scanning.
+ For waterfall display, `--fft_all ` produces *m*\*FFT_AVG\*FFT_FPS/2 rows of FFT (FFT_AVG seconds, FFT_FPS/2 per sec).
+ The FFT is saved in `` as
+ `sec.ms,freq_min,freq_max,Hz/bin,N_bins, db_1,...,db_N`
+ approx. 200 Hz per bin.
+ Choose `filename="-"` for `stdout`.
If no output bps is chosen (`--bo [8,16,32]`), the IF bps is equal to the baseband bps. It is recommended to use
`--bo 32` (i.e. float32) output, then no quantization noise is introduced when converting from internal float32 samples.
- Ex.2
[terminal 1]
- `T1$ rtl_sdr -f 403.0M -s 1920000 - | ./iq_server --fft fft_server.txt --bo 32 - 1920000 8`
+ `T1$ rtl_sdr -f 403.0M -s 1920000 - | ./iq_server --fft_avg 1 fft_server.csv --bo 32 - 1920000 8`
[terminal 2]
`T2$ ./iq_client --freq -0.3125 | ./m10mod -c -vv --IQ 0.0 - 48000 32`
[terminal 3]
@@ -41,7 +48,17 @@ receive IF stream from baseband IQ via TCP, default `PORT=1280 (iq_svcl.h)`
`T4$ ./iq_client --stop` (*close all clients and stop server*)
- The `iq_server` `--fft` option immediately starts reading the IQ stream (so buffering is reduced).
- `./iq_client --fft ` can also request FFT.
- The IF sample rate `if_sr` is at least 48000 such that the baseband sample rate `sr` is a multiple of `if_sr`.
+ - The `iq_server` FFT options immediately start reading the IQ stream (so buffering is reduced).
+ `./iq_client `, where `=--fft_avg_cl` or `--fft_all_cl`, requests FFT from the server.
+ (`=--fft_avg_sv/--fft_all_sv` would save the FFT at the server, but only if `./iq_server --enable_clsv_out`.)
+ The IF sample rate `if_sr` is at least 48000 and such that the baseband sample rate `sr` is a multiple of `if_sr`.
+
+ - Ex.3
+ [terminal 1]
+ `T1$ rtl_sdr -f 404550k -s 2048000 - | ./iq_server --fft_avg 1 fft_avg.csv --bo 32 - 2048000 8`
+ (scan FFT: `./scan_fft fft_avg.csv`)
+ [terminal 3]
+ `T3$ ./iq_client --fft_all_cl -1 - | python plot_fft_ani.py 3 -`
+ ![FFT image](fft3-1.png "FFT")
+
diff --git a/demod/iq_svcl/fft3-1.png b/demod/iq_svcl/fft3-1.png
new file mode 100644
index 0000000..029c60b
Binary files /dev/null and b/demod/iq_svcl/fft3-1.png differ
diff --git a/demod/iq_svcl/iq_base.h b/demod/iq_svcl/iq_base.h
index db33108..eca90c5 100644
--- a/demod/iq_svcl/iq_base.h
+++ b/demod/iq_svcl/iq_base.h
@@ -34,7 +34,10 @@ typedef struct {
double xlt_fq;
float complex *blk;
int used;
+ //
int fft;
+ int stop_fft;
+ int fft_num;
} thd_t;
@@ -97,6 +100,7 @@ typedef struct {
thd_t thd;
int fd;
char *fname;
+ FILE *fpo;
} thargs_t;
diff --git a/demod/iq_svcl/iq_client.c b/demod/iq_svcl/iq_client.c
index 93b5487..2cb8180 100644
--- a/demod/iq_svcl/iq_client.c
+++ b/demod/iq_svcl/iq_client.c
@@ -4,6 +4,27 @@
*
* gcc -O2 iq_client.c -o iq_client
*
+ * usage:
+ * (request IF IQ samples)
+ * ./iq_client [--ip ] [--port ] --freq # -0.5 < fq < 0.5
+ *
+ * (request FFT)
+ * ./iq_client # FFT csv output
+ * :
+ * --fft_avg_cl # client out
+ * --fft_all_cl # client out
+ * --fft_avg_sv # server out (if iq_server --enable_clsv_out)
+ * --fft_avg_sv # server out (if iq_server --enable_clsv_out)
+ * :
+ * _avg_: m avg-FFTs
+ * _all_: m*FFT_FPS/2 FFTs
+ * m = -1 : continuous FFT output
+ * :
+ * csv filename ("-": stdout)
+ *
+ * ./iq_client - # close client
+ * ./iq_client --stop # close all clients, stop server
+ *
* author: zilog80
*/
@@ -49,17 +70,24 @@ int main(int argc, char *argv[]) {
}
else serv_port = port;
}
- else if (strcmp(*argv, "--fft0") == 0) {
- sprintf(sendln, "%s", "--fft0");
- }
- else if (strcmp(*argv, "--fft") == 0) {
- sprintf(sendln, "%s", "--fft");
- ++argv;
- if (*argv) {
- fname_fft = *argv;
+ else if (strncmp(*argv, "--fft", 5) == 0) {
+ char *arg_fft = *argv;
+ int fft_num = 0;
+ int opt_fft_cl = 0;
+ if (strncmp(arg_fft+5, "_avg", 4) == 0) opt_fft_cl = OPT_FFT_AVG;
+ else if (strncmp(arg_fft+5, "_all", 4) != 0) return -1;
+ if (strncmp(arg_fft+5+4, "_cl", 3) == 0) {
+ opt_fft_cl |= OPT_FFT_CLNT;
+ re = 2;
}
+ else if (strncmp(arg_fft+5+4, "_sv", 3) == 0) opt_fft_cl |= OPT_FFT_SERV;
else return -1;
- re = 2;
+ ++argv;
+ if (*argv) fft_num = atoi(*argv); else return -1;
+ ++argv;
+ if (*argv) fname_fft = *argv; else return -1;
+ //sprintf(sendln, "%s_%s_%s", "--fft", (opt_fft_cl & OPT_FFT_AVG) ? "avg" : "all", (opt_fft_cl & OPT_FFT_CLNT) ? "cl" : "sv");
+ sprintf(sendln, "%s %d %s", arg_fft, fft_num, fname_fft);
}
else if (strcmp(*argv, "--freq") == 0) {
++argv;
@@ -155,7 +183,9 @@ int main(int argc, char *argv[]) {
else if ( re == 2 )
{
// fft data
- FILE *fpo = fopen(fname_fft, "wb");
+ FILE *fpo = NULL;
+ if (fname_fft[0] == '-') fpo = stdout;
+ else fpo = fopen(fname_fft, "wb");
if (fpo != NULL) {
memset(recvln, 0, LINELEN+1);
diff --git a/demod/iq_svcl/iq_server.c b/demod/iq_svcl/iq_server.c
index b78cdba..0a7d5bf 100644
--- a/demod/iq_svcl/iq_server.c
+++ b/demod/iq_svcl/iq_server.c
@@ -7,6 +7,24 @@
*
* (gcc -O2 iq_client.c -o iq_client)
*
+ * usage:
+ * ./iq_server [--enable_clsv_out] [--port ] [iq_baseband.wav]
+ * no wav-file: stdin
+ *
+ * ./iq_server [--bo ] - [iq_baseband.raw]
+ * =8,16,32 bit client/IF output
+ *
+ * ./iq_server [iq_baseband.wav] # FFT csv output
+ * :
+ * --fft_avg
+ * --fft_all
+ * :
+ * _avg: m avg-FFTs
+ * _all: m*FFT_FPS/2 FFTs
+ * m = -1 : continuous FFT output
+ * :
+ * csv filename ("-": stdout)
+ *
* author: zilog80
*/
@@ -27,13 +45,13 @@
#include "iq_svcl.h"
#include "iq_base.h"
+#define FFT_AVG 2 // fft_avg: integrate FFT_AVG seconds, 2*FFT_FPS FFTs
+#define FFT_FPS 16 // fft_all: output (ca.) FFT_FPS/2 per sec
+
#define FPOUT stderr
-#define OPT_FFT_SERV 1 // server
-#define OPT_FFT_CLSV 2 // server (client request)
-#define OPT_FFT_CLNT 3 // server -> client
-
static int option_dbg = 0;
+static int option_clsv_out = 0;
static int tcp_eof = 0;
@@ -275,21 +293,82 @@ static void *thd_IF(void *targs) { // pcm_t *pcm, double xlt_fq
return NULL;
}
-#define FFT_SEC 2
-#define FFT_FPS 20
+
+static int fft_txt_prn(FILE *fpo, dft_t *dft, float *db) {
+ int j;
+
+ fprintf(fpo, "# ; ## sr:%d , N:%d\n", dft->sr, dft->N);
+ for (j = dft->N/2; j < dft->N/2 + dft->N; j++) {
+ fprintf(fpo, "%+11.8f;%7.2f\n", bin2fq(dft, j % dft->N), db[j % dft->N]);
+ }
+
+ return 0;
+}
+
+static int fft_txt_tcp(int fd, dft_t *dft, float *db) {
+ char sendln[LINELEN+1];
+ int sendln_len;
+ int j, l;
+
+ snprintf(sendln, LINELEN, "# ; ## sr:%d , N:%d\n", dft->sr, dft->N);
+ sendln_len = strlen(sendln);
+ l = write(fd, sendln, sendln_len);
+ for (j = dft->N/2; j < dft->N/2 + dft->N; j++) {
+ memset(sendln, 0, LINELEN+1);
+ snprintf(sendln, LINELEN, "%+11.8f;%7.2f\n", bin2fq(dft, j % dft->N), db[j % dft->N]);
+ sendln_len = strlen(sendln);
+ l = write(fd, sendln, sendln_len);
+ }
+
+ return 0;
+}
+
+
+static int fft_csv_prn(FILE *fpo, dft_t *dft, float *db, double t_sec) {
+ int j;
+
+ fprintf(fpo, "%7.3f, ", t_sec);
+ fprintf(fpo, "%d, %d, ", (int)bin2freq(dft, dft->N/2), (int)bin2freq(dft, dft->N/2 - 1));
+ fprintf(fpo, "%.2f, ", dft->sr/(double)dft->N);
+ fprintf(fpo, "%d, ", dft->N);
+ for (j = dft->N/2; j < dft->N/2 + dft->N; j++) {
+ fprintf(fpo, "%7.2f%c", db[j % dft->N], j < dft->N/2 + dft->N-1 ? ',' : '\n');
+ }
+
+ return 0;
+}
+
+static int fft_csv_tcp(int fd, dft_t *dft, float *db, double t_sec) {
+ char sendln[LINELEN+1];
+ int sendln_len;
+ int j, l;
+
+ snprintf(sendln, LINELEN, "%7.3f, %d, %d, %.2f, %d, ",
+ t_sec, (int)bin2freq(dft, dft->N/2), (int)bin2freq(dft, dft->N/2 - 1), dft->sr/(double)dft->N, dft->N);
+ sendln_len = strlen(sendln);
+ l = write(fd, sendln, sendln_len);
+ for (j = dft->N/2; j < dft->N/2 + dft->N; j++) {
+ memset(sendln, 0, LINELEN+1);
+ snprintf(sendln, LINELEN, "%7.2f%c", db[j % dft->N], j < dft->N/2 + dft->N-1 ? ',' : '\n');
+ sendln_len = strlen(sendln);
+ l = write(fd, sendln, sendln_len);
+ }
+
+ return 0;
+}
+
static void *thd_FFT(void *targs) {
thargs_t *tharg = targs;
pcm_t *pcm = &(tharg->pcm);
- FILE *fpo = NULL;
- char *fname_fft = "db_fft.txt";
-
int k;
int bitQ = 0;
float complex *z = NULL;
+ float *db = NULL;
+ float *all_rZ = NULL;
float *avg_rZ = NULL;
float *avg_db = NULL;
@@ -312,7 +391,6 @@ static void *thd_FFT(void *targs) {
dsp.bps_out = pcm->bps_out;
- //(dsp.thd)->fft = 1;
if (option_dbg) {
fprintf(stderr, "init FFT buffers\n");
}
@@ -326,6 +404,8 @@ static void *thd_FFT(void *targs) {
z = calloc(dsp.decM+1, sizeof(float complex)); if (z == NULL) goto exit_thread;
+ db = calloc(dsp.DFT.N+1, sizeof(float)); if (db == NULL) goto exit_thread;
+ all_rZ = calloc(dsp.DFT.N+1, sizeof(float)); if (all_rZ == NULL) goto exit_thread;
avg_rZ = calloc(dsp.DFT.N+1, sizeof(float)); if (avg_rZ == NULL) goto exit_thread;
avg_db = calloc(dsp.DFT.N+1, sizeof(float)); if (avg_db == NULL) goto exit_thread;
@@ -334,12 +414,16 @@ static void *thd_FFT(void *targs) {
int len = dsp.DFT.N / dsp.decM;
int mlen = len*dsp.decM;
int sum_n = 0;
- int sec = FFT_SEC;
- int fft_step = dsp.sr_base/(dsp.DFT.N*FFT_FPS);
+ int sum_fft = 0;
+ int avg_sec = FFT_AVG;
+ int fft_step = (int)(dsp.sr_base/(double)(dsp.DFT.N*FFT_FPS) + 0.5);
int n_fft = 0;
int th_used = 0;
int readSamples = 1;
+ int n_out = 0;
+
+
bitQ = 0;
while ( bitQ != EOF )
{
@@ -367,65 +451,95 @@ static void *thd_FFT(void *targs) {
n++;
if (n == len) { // mlen = len * decM <= DFT.N
- n_fft += 1;
-
- if ((dsp.thd)->fft && sum_n*n_fft*mlen < sec*dsp.sr_base && n_fft >= fft_step)
+ if ( (dsp.thd)->fft )
{
- for (j = 0; j < mlen; j++) {
- dsp.DFT.Z[j] *= dsp.DFT.win[j];
+ if ((dsp.thd)->fft_num == 0)
+ {
+ if ( tharg->fpo ) { fclose(tharg->fpo); tharg->fpo = NULL; }
+ else if ( tharg->fd > STDIN_FILENO ) { close(tharg->fd); tharg->fd = -1; }
+
+ (dsp.thd)->fft = 0;
}
- while (j < dsp.DFT.N) dsp.DFT.Z[j++] = 0.0; // dft(Z[...]) != 0
- raw_dft(&(dsp.DFT), dsp.DFT.Z);
+ n_fft += 1;
- for (j = 0; j < dsp.DFT.N; j++) avg_rZ[j] += cabs(dsp.DFT.Z[j]);
+ if (sum_n*n_fft*mlen < avg_sec*dsp.sr_base && n_fft >= fft_step) {
+ n_fft = fft_step;
- sum_n++;
- n_fft = 0;
- }
- if (sum_n*fft_step*mlen >= sec*dsp.sr_base) {
-
- for (j = 0; j < dsp.DFT.N; j++) avg_rZ[j] /= dsp.DFT.N*(float)sum_n;
- for (j = 0; j < dsp.DFT.N; j++) avg_db[j] = 20.0*log10(avg_rZ[j]+1e-20);
-
-
- pthread_mutex_lock( (dsp.thd)->mutex );
- fprintf(FPOUT, "<%d: FFT>\n", (dsp.thd)->tn);
- pthread_mutex_unlock( (dsp.thd)->mutex );
-
- if ( (dsp.thd)->fft == OPT_FFT_CLNT ) { // send FFT data to client
- char sendln[LINELEN+1];
- int sendln_len;
- int l;
- snprintf(sendln, LINELEN, "# ; ## sr:%d , N:%d\n", dsp.DFT.sr, dsp.DFT.N);
- sendln_len = strlen(sendln);
- l = write(tharg->fd, sendln, sendln_len);
- for (j = dsp.DFT.N/2; j < dsp.DFT.N/2 + dsp.DFT.N; j++) {
- memset(sendln, 0, LINELEN+1);
- snprintf(sendln, LINELEN, "%+11.8f;%7.2f\n", bin2fq(&(dsp.DFT), j % dsp.DFT.N), avg_db[j % dsp.DFT.N]);
- sendln_len = strlen(sendln);
- l = write(tharg->fd, sendln, sendln_len);
+ if (sum_fft == 0) {
+ pthread_mutex_lock( (dsp.thd)->mutex );
+ fprintf(FPOUT, "<%d: FFT_START>\n", (dsp.thd)->tn);
+ pthread_mutex_unlock( (dsp.thd)->mutex );
}
- }
- else { // save FFT at server
- if ( (dsp.thd)->fft == OPT_FFT_SERV ) fname_fft = tharg->fname;
- else /* OPT_FFT_CLSV */ fname_fft = "db_fft_cl.txt";
- fpo = fopen(fname_fft, "wb");
- if (fpo != NULL) {
- fprintf(fpo, "# ; ## sr:%d , N:%d\n", dsp.DFT.sr, dsp.DFT.N);
- for (j = dsp.DFT.N/2; j < dsp.DFT.N/2 + dsp.DFT.N; j++) {
- fprintf(fpo, "%+11.8f;%7.2f\n", bin2fq(&(dsp.DFT), j % dsp.DFT.N), avg_db[j % dsp.DFT.N]);
+
+ for (j = 0; j < mlen; j++) {
+ dsp.DFT.Z[j] *= dsp.DFT.win[j];
+ }
+ while (j < dsp.DFT.N) dsp.DFT.Z[j++] = 0.0; // dft(Z[...]) != 0
+
+ raw_dft(&(dsp.DFT), dsp.DFT.Z);
+
+ for (j = 0; j < dsp.DFT.N; j++) {
+ float rZ = cabs(dsp.DFT.Z[j]);
+ avg_rZ[j] += rZ;
+ all_rZ[j] += rZ;
+ }
+
+ if ( (sum_fft&1)==1 && ((dsp.thd)->fft & OPT_FFT_AVG) == 0 ) {
+ double t_sec = sum_fft*n_fft*mlen / (double)dsp.sr_base;
+ for (j = 0; j < dsp.DFT.N; j++) { // if sum_fft odd,
+ db[j] = 20.0*log10(0.5*all_rZ[j]/dsp.DFT.N+1e-20); // 0.5: rZ_0+rZ_1
+ all_rZ[j] = 0.0f;
+ }
+ // sec.ms, freq_min, freq_max, Hz/bin, N_bins, db_1, ..., db_N
+ if ( tharg->fpo ) { // save FFT at server
+ fft_csv_prn(tharg->fpo, &(dsp.DFT), db, t_sec);
+ }
+ else if ( tharg->fd > STDIN_FILENO ) {
+ fft_csv_tcp(tharg->fd, &(dsp.DFT), db, t_sec);
}
- fclose(fpo);
}
- else {
- fprintf(stderr, "error: open %s\n", fname_fft);
- }
- }
- if ( (dsp.thd)->fft != OPT_FFT_SERV ) close(tharg->fd);
- (dsp.thd)->fft = 0;
- sum_n = 0;
+ sum_n++;
+ sum_fft++;
+ n_fft = 0;
+ }
+ if (sum_n*fft_step*mlen >= avg_sec*dsp.sr_base) {
+ float nN = 1.0/(dsp.DFT.N*(float)sum_n);
+ for (j = 0; j < dsp.DFT.N; j++) {
+ avg_db[j] = 20.0*log10(nN*avg_rZ[j]+1e-20);
+ avg_rZ[j] = 0.0f;
+ }
+
+ if ( (dsp.thd)->fft & OPT_FFT_AVG ) {
+ double t_sec = sum_fft*fft_step*mlen / (double)dsp.sr_base;
+ if ( tharg->fpo ) { // send FFT data to client
+ fft_csv_prn(tharg->fpo, &(dsp.DFT), avg_db, t_sec);
+ }
+ else if ( tharg->fd > STDIN_FILENO ) {
+ fft_csv_tcp(tharg->fd, &(dsp.DFT), avg_db, t_sec);
+ }
+ }
+
+ n_out++;
+
+ if ((dsp.thd)->fft_num > 0 && n_out >= (dsp.thd)->fft_num)
+ {
+ if ( tharg->fpo ) { fclose(tharg->fpo); tharg->fpo = NULL; }
+ else if ( tharg->fd > STDIN_FILENO ) { close(tharg->fd); tharg->fd = -1; }
+
+ (dsp.thd)->fft = 0;
+ sum_fft = 0;
+ n_out = 0;
+
+ pthread_mutex_lock( (dsp.thd)->mutex );
+ fprintf(FPOUT, "<%d: FFT_STOP>\n", (dsp.thd)->tn);
+ pthread_mutex_unlock( (dsp.thd)->mutex );
+
+ }
+
+ sum_n = 0;
+ }
}
#ifdef FFT_READ_SINK_MIN
@@ -445,12 +559,39 @@ static void *thd_FFT(void *targs) {
if ( (dsp.thd)->used == 0 )
{
+ (dsp.thd)->fft = 0;
pthread_mutex_lock( (dsp.thd)->mutex );
fprintf(FPOUT, "<%d: CLOSE>\n", (dsp.thd)->tn);
pthread_mutex_unlock( (dsp.thd)->mutex );
break;
}
+ if ( (dsp.thd)->stop_fft > 0 )
+ {
+ if ( tharg->fpo ) { fclose(tharg->fpo); tharg->fpo = NULL; }
+ else if ( tharg->fd > STDIN_FILENO ) { close(tharg->fd); tharg->fd = -1; }
+
+ (dsp.thd)->fft = 0;
+ (dsp.thd)->stop_fft = 0;
+ sum_fft = 0;
+ sum_n = 0;
+ n_out = 0;
+
+ pthread_mutex_lock( (dsp.thd)->mutex );
+ fprintf(FPOUT, "<%d: STOP_FFT>\n", (dsp.thd)->tn);
+ pthread_mutex_unlock( (dsp.thd)->mutex );
+ }
+ }
+
+ if ((dsp.thd)->fft_num < 0)
+ {
+ if ( tharg->fpo ) { fclose(tharg->fpo); tharg->fpo = NULL; }
+ else if ( tharg->fd > STDIN_FILENO ) { close(tharg->fd); tharg->fd = -1; }
+ (dsp.thd)->fft = 0;
+
+ pthread_mutex_lock( (dsp.thd)->mutex );
+ fprintf(FPOUT, "<%d: FFT_STOP>\n", (dsp.thd)->tn);
+ pthread_mutex_unlock( (dsp.thd)->mutex );
}
if (bitQ == EOF) {
@@ -465,6 +606,8 @@ static void *thd_FFT(void *targs) {
exit_thread:
if (z) { free(z); z = NULL; }
+ if (db) { free(db); db = NULL; }
+ if (all_rZ) { free(all_rZ); all_rZ = NULL; }
if (avg_rZ) { free(avg_rZ); avg_rZ = NULL; }
if (avg_db) { free(avg_db); avg_db = NULL; }
@@ -496,6 +639,8 @@ int main(int argc, char **argv) {
char tcp_buf[TCPBUF_LEN];
int th_used = 0;
int tn_fft = -1;
+ int opt_fft = 0;
+ int fft_num = 0;
pcm_t pcm = {0};
@@ -510,11 +655,15 @@ int main(int argc, char **argv) {
for (k = 0; k < MAX_FQ; k++) base_fqs[k] = 0.0;
+ // server options
++argv;
while ((*argv) && (!wavloaded)) {
if (strcmp(*argv, "--dbg") == 0) {
option_dbg = 1;
}
+ else if (strcmp(*argv, "--enable_clsv_out") == 0) {
+ option_clsv_out = 1;
+ }
else if (strcmp(*argv, "--port") == 0) {
int port = 0;
++argv;
@@ -524,7 +673,11 @@ int main(int argc, char **argv) {
}
else serv_port = port;
}
- else if (strcmp(*argv, "--fft") == 0) {
+ else if (strncmp(*argv, "--fft", 5) == 0) {
+ char *arg_fft = *argv;
+ if (strncmp(arg_fft+5, "_avg", 4) == 0) opt_fft = OPT_FFT_SERV | OPT_FFT_AVG;
+ else if (strncmp(arg_fft+5, "_all", 4) == 0) opt_fft = OPT_FFT_SERV;
+ else return -1;
if (xlt_cnt < MAX_FQ) {
base_fqs[xlt_cnt] = 0.0;
rstype[xlt_cnt] = thd_FFT;
@@ -532,6 +685,8 @@ int main(int argc, char **argv) {
xlt_cnt++;
}
++argv;
+ if (*argv) fft_num = atoi(*argv); else return -1;
+ ++argv;
if (*argv) fname_fft = *argv; else return -1;
}
else if (strcmp(*argv, "-") == 0) {
@@ -597,8 +752,21 @@ int main(int argc, char **argv) {
for (k = 0; k < xlt_cnt; k++) {
if (k == tn_fft) {
- tharg[k].thd.fft = OPT_FFT_SERV;
+ tharg[k].thd.fft = opt_fft;
tharg[k].fname = fname_fft;
+ tharg[k].thd.fft_num = fft_num;
+ tharg[k].thd.stop_fft = 0;
+
+ if ( (tharg[k].thd.fft & 0xF) == OPT_FFT_SERV ) { // save FFT at server
+ if (fname_fft) {
+ if (fname_fft[0] == '-') tharg[k].fpo = stdout;
+ else tharg[k].fpo = fopen(fname_fft, "wb");
+ }
+ else return -1;
+ if (tharg[k].fpo == NULL) {
+ fprintf(stderr, "error: open %s\n", fname_fft);
+ }
+ }
}
tharg[k].thd.tn = k;
tharg[k].thd.tn_bit = (1< 1 ) {
char *freq = tcp_buf;
while (l > 1 && tcp_buf[l-1] < 0x20) l--;
@@ -701,23 +870,76 @@ int main(int argc, char **argv) {
break;
}
else if ( strncmp(tcp_buf, "--fft", 5) == 0 ) {
+ opt_fft = 0;
+ if (strncmp(tcp_buf+5, "_avg", 4) == 0) opt_fft = OPT_FFT_AVG;
+ else if (strncmp(tcp_buf+5, "_all", 4) != 0) return -1;
+ if (strncmp(tcp_buf+5+4, "_cl", 3) == 0) opt_fft |= OPT_FFT_CLNT;
+ else if (strncmp(tcp_buf+5+4, "_sv", 3) == 0) {
+ if (option_clsv_out) opt_fft |= OPT_FFT_SERV;
+ else {
+ pthread_mutex_lock( &mutex );
+ fprintf(FPOUT, "\n");
+ pthread_mutex_unlock( &mutex );
+ close(conn_fd);
+ continue;
+ }
+ }
+ else return -1;
+ if (tcp_buf+5+4+4) fft_num = atoi(tcp_buf+5+4+4); else fft_num = 0;
char *fname_fft_cl = "db_fft_cl.txt";
- int opt_fft = strcmp(tcp_buf, "--fft0") == 0 ? OPT_FFT_CLSV : OPT_FFT_CLNT;
- //close(conn_fd);
+ char *pbuf = tcp_buf;
+ for (pbuf = tcp_buf+5+4+4; *pbuf; pbuf++) {
+ if (*pbuf == ' ') break;
+ }
+ if (*pbuf == ' ') {
+ fname_fft_cl = pbuf+1;
+ }
if ( !tcp_eof )
{
if (tn_fft >= 0) {
- tharg[tn_fft].thd.fft = opt_fft;
- tharg[tn_fft].fname = fname_fft_cl;
- tharg[tn_fft].fd = conn_fd;
+ if (tharg[tn_fft].thd.fft == 0) {
+ tharg[tn_fft].thd.fft_num = fft_num;
+ tharg[tn_fft].thd.stop_fft = 0;
+ tharg[tn_fft].fname = fname_fft_cl;
+ tharg[tn_fft].fd = conn_fd;
+
+ if ( (opt_fft & 0xF) == OPT_FFT_SERV ) { // save FFT at server
+ if (fname_fft_cl) {
+ if (fname_fft_cl[0] == '-') tharg[tn_fft].fpo = stdout;
+ else tharg[tn_fft].fpo = fopen(fname_fft_cl, "wb");
+ }
+ else return -1;
+ if (tharg[tn_fft].fpo == NULL) {
+ fprintf(stderr, "error: open %s\n", fname_fft_cl);
+ }
+ close(tharg[tn_fft].fd); tharg[tn_fft].fd = -1; // tharg[tn_fft].fd == conn_fd
+ }
+ else { // (opt_fft & 0xF) == OPT_FFT_CLNT : send FFT to client
+ tharg[tn_fft].fpo = NULL;
+ }
+
+ tharg[tn_fft].thd.fft = opt_fft;
+ }
+ else {
+ pthread_mutex_lock( &mutex );
+ fprintf(FPOUT, "<%d: FFT running>\n", tn_fft);
+ pthread_mutex_unlock( &mutex );
+
+ close(conn_fd);
+ }
}
else {
for (k = 0; k < MAX_FQ; k++) {
- if (tharg[k].thd.used == 0) break;
+ if (tharg[k].thd.used == 0) {
+ if (k != tn_fft) break;
+ }
}
if (k < MAX_FQ) {
- tharg[k].thd.fft = opt_fft;
+ tharg[k].thd.fft_num = fft_num;
+ tharg[k].thd.stop_fft = 0;
tharg[k].fname = fname_fft_cl;
+ tharg[k].fd = conn_fd;
+
tn_fft = k;
tharg[k].thd.tn = k;
tharg[k].thd.tn_bit = (1< : close
int num = atoi(tcp_buf+1);
if (num >= 0 && num < MAX_FQ) {
- if (num != tn_fft) {
+ if (num != tn_fft)
+ {
tharg[num].thd.used = 0;
}
+ else
+ {
+ tharg[num].thd.stop_fft = 1;
+ }
}
close(conn_fd);
}
@@ -789,6 +1030,7 @@ int main(int argc, char **argv) {
rbf1 |= tharg[k].thd.tn_bit;
tharg[k].thd.used = 1;
tharg[k].thd.fft = 0;
+ tharg[k].thd.stop_fft = 0;
pthread_create(&tharg[k].thd.tid, NULL, thd_IF, &tharg[k]);
diff --git a/demod/iq_svcl/iq_svcl.h b/demod/iq_svcl/iq_svcl.h
index 04a7c56..071b19c 100644
--- a/demod/iq_svcl/iq_svcl.h
+++ b/demod/iq_svcl/iq_svcl.h
@@ -6,6 +6,10 @@
#include
+#define OPT_FFT_SERV 1 // server
+#define OPT_FFT_CLNT 2 // server -> client
+#define OPT_FFT_AVG 0x100
+
#define TCPBUF_LEN 1024
#define SERV_BACKLOG 6
diff --git a/demod/iq_svcl/plot_fft.py b/demod/iq_svcl/plot_fft.py
index 32a81d0..ddebabc 100644
--- a/demod/iq_svcl/plot_fft.py
+++ b/demod/iq_svcl/plot_fft.py
@@ -8,7 +8,7 @@ import matplotlib.ticker as ticker
if len(sys.argv) < 2:
print("usage:")
- print("\tpython %s " % sys.argv[0])
+ print("\tpython %s " % sys.argv[0])
sys.exit()
fft_file = sys.argv[1]
@@ -18,10 +18,17 @@ if not os.path.isfile(fft_file):
sys.exit()
-data = np.genfromtxt( fft_file, delimiter=';', names=['fq','db'] , skip_header=1 )
+raw_data = np.genfromtxt( fft_file, delimiter=',', max_rows=1)
+data1 = raw_data[:] # max_rows=2: raw_data[0,:]
+print(data1)
-fq = data['fq']
-db = data['db']
+db = data1[5:]
+
+sr = -2.0*data1[1]
+
+freq_min = data1[1] / sr
+freq_max = data1[2] / sr
+fq = np.arange(freq_min, freq_max, 1.0/(data1[4]+1))
N = len(db)
m = np.mean(db)
diff --git a/demod/iq_svcl/plot_fft_ani.py b/demod/iq_svcl/plot_fft_ani.py
new file mode 100644
index 0000000..c3d20b4
--- /dev/null
+++ b/demod/iq_svcl/plot_fft_ani.py
@@ -0,0 +1,186 @@
+
+import os
+import sys
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.ticker as ticker
+import matplotlib.animation as animation
+
+
+if len(sys.argv) < 3:
+ print("usage:")
+ print("\tpython <1/2/3> %s " % sys.argv[0])
+ sys.exit()
+
+OPT_FFT_L = 1
+OPT_FFT_W = 2
+OPT_FFT_B = 3
+
+OPT_FFT = OPT_FFT_B
+if (sys.argv[1] == '1'):
+ OPT_FFT = OPT_FFT_L
+elif (sys.argv[1] == '2'):
+ OPT_FFT = OPT_FFT_W
+
+fft_file = sys.argv[2]
+
+
+if (fft_file == "-"):
+ f = sys.stdin
+else:
+ try:
+ f = open(fft_file)
+ except IOError:
+ print("error: open %s" % fft_file)
+ sys.exit()
+
+
+FFT_FPS = 16/2
+WIN_SEC = 10
+win = WIN_SEC*FFT_FPS
+
+line = f.readline()
+#line = f.readline()
+
+row = np.fromstring(line, dtype=float, sep=',')
+data = row[5:]
+
+l = len(data)
+m = np.mean(data)
+
+data5 = np.full([win,l], m)
+for x in range(2, l-2):
+ data5[0,x] = (data[x-2]+data[x-1]+data[x]+data[x+1]+data[x+2])/5.0
+
+min_db = np.min(data5)
+max_db = np.max(data5)
+
+sr = -2.0*row[1]
+
+freq_min = row[1]
+freq_max = row[2]
+N = row[4]
+
+limits = [freq_min/1e3, freq_max/1e3, WIN_SEC, 0.0]
+
+fq = np.arange(freq_min/sr, freq_max/sr, 1.0/(row[4]+1))
+
+
+################################################################################################
+
+if (OPT_FFT == OPT_FFT_L):
+ fig = plt.figure(figsize=(12, 5))
+ ax1 = fig.add_subplot(111)
+ ax1.set_xlim([fq[0], fq[-1]])
+ ax1.set_ylim([-110.0, -30.0])
+
+ lp, = ax1.plot( fq, data5[0,:], color='g', linewidth=0.4)
+
+ count = 0
+
+ def animate_lp(i):
+ line = f.readline()
+ if line:
+ row = np.fromstring(line, dtype=float, sep=',')
+ data = row[5:]
+
+ global count
+ count += 1
+ global data5
+ #data5 = np.roll(data5, 1, axis=0)
+ for x in range(2, l-2):
+ data5[0,x] = (data[x-2]+data[x-1]+data[x]+data[x+1]+data[x+2])/5.0
+
+ lp.set_data(fq, data5[0,:])
+
+ return [lp]
+
+ ani = animation.FuncAnimation(fig, animate_lp, interval=10, blit=True)
+
+################################################################################################
+
+elif (OPT_FFT == OPT_FFT_W):
+ fig = plt.figure(figsize=(12, 5))
+ ax2 = fig.add_subplot(111)
+ ax2.set_xlabel('Frequency (kHz)')
+ ax2.set_ylabel('Time (s)')
+
+ im = ax2.imshow(data5, vmin=-110.0, vmax=-50.0, extent=limits, animated=True)
+ ax2.set_aspect('auto')
+ fig.colorbar(im, orientation='vertical')
+
+ count = 0
+
+ def animate_im(i):
+ line = f.readline()
+ if line:
+ row = np.fromstring(line, dtype=float, sep=',')
+ data = row[5:]
+
+ global count
+ count += 1
+ global data5
+ data5 = np.roll(data5, 1, axis=0)
+ for x in range(2, l-2):
+ data5[0,x] = (data[x-2]+data[x-1]+data[x]+data[x+1]+data[x+2])/5.0
+
+ im.set_data(data5)
+
+ # update vmin/vmax
+ if (count % win == 0):
+ min_db = np.min(data5)
+ max_db = np.max(data5)
+ im.set_clim(vmin=min_db, vmax=max_db)
+ return [im]
+
+ ani = animation.FuncAnimation(fig, animate_im, interval=10, blit=True)
+
+################################################################################################
+
+else:
+ fig = plt.figure(figsize=(12, 8))
+ ax1 = fig.add_subplot(211)
+ ax1.set_xlim([fq[0], fq[-1]])
+ ax1.set_ylim([-110.0, -30.0])
+ ax2 = fig.add_subplot(212)
+ ax2.set_xlabel('Frequency (kHz)')
+ ax2.set_ylabel('Time (s)')
+
+ lp, = ax1.plot( fq, data5[0,:], color='g', linewidth=0.4)
+ im = ax2.imshow(data5, vmin=-110.0, vmax=-50.0, extent=limits, animated=True)
+
+ ax2.set_aspect('auto')
+
+ count = 0
+
+ def animate(i):
+ line = f.readline()
+ if line:
+ row = np.fromstring(line, dtype=float, sep=',')
+ data = row[5:]
+
+ global count
+ count += 1
+ global data5
+ data5 = np.roll(data5, 1, axis=0)
+ for x in range(2, l-2):
+ data5[0,x] = (data[x-2]+data[x-1]+data[x]+data[x+1]+data[x+2])/5.0
+
+ im.set_data(data5)
+ lp.set_data(fq, data5[0,:])
+
+ # update vmin/vmax
+ if (count % win == 0):
+ min_db = np.min(data5)
+ max_db = np.max(data5)
+ im.set_clim(vmin=min_db, vmax=max_db)
+ return [lp,im]
+
+ ani = animation.FuncAnimation(fig, animate, interval=10, blit=True)
+
+################################################################################################
+
+plt.show()
+
+
+
diff --git a/demod/iq_svcl/plot_wfft.py b/demod/iq_svcl/plot_wfft.py
new file mode 100644
index 0000000..cf29e87
--- /dev/null
+++ b/demod/iq_svcl/plot_wfft.py
@@ -0,0 +1,52 @@
+
+import os
+import sys
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.ticker as ticker
+
+
+if len(sys.argv) < 2:
+ print("usage:")
+ print("\tpython %s " % sys.argv[0])
+ sys.exit()
+
+fft_file = sys.argv[1]
+
+if not os.path.isfile(fft_file):
+ print("error: %s not found" % fft_file)
+ sys.exit()
+
+
+raw_data = np.genfromtxt( fft_file, delimiter=',')
+data = raw_data[:,5:]
+
+dim = np.shape(data)
+m = np.mean(data)
+data5 = np.full(dim, m)
+for y in range(dim[0]):
+ for x in range(2, dim[1]-2):
+ data5[y,x] = (data[y,x-2]+data[y,x-1]+data[y,x]+data[y,x+1]+data[y,x+2])/5.0
+
+freq_min = raw_data[0,1] / 1e3
+freq_max = raw_data[0,2] / 1e3
+N = raw_data[0,4]
+tmin = raw_data[dim[0]-1,0]
+tmax = raw_data[0,0]
+limits = [freq_min, freq_max, tmin, tmax]
+
+
+fig = plt.figure(figsize=(15, 5))
+ax = fig.add_subplot(111)
+ax.set_title('Waterfall')
+ax.set_xlabel('Frequency (kHz)')
+ax.set_ylabel('Time (s)')
+
+plt.imshow(data5, extent=limits)
+
+ax.set_aspect('auto')
+plt.colorbar(orientation='vertical')
+plt.show()
+
+
+
diff --git a/demod/iq_svcl/scan_fft.c b/demod/iq_svcl/scan_fft.c
index 42d6a37..9e60eb1 100644
--- a/demod/iq_svcl/scan_fft.c
+++ b/demod/iq_svcl/scan_fft.c
@@ -52,7 +52,7 @@ int main(int argc, char **argv) {
if (argv[1] == NULL) {
fprintf(stderr, "usage:\n");
- fprintf(stderr, "\t%s \n", argv[0]);
+ fprintf(stderr, "\t%s \n", argv[0]);
return 1;
}
fp = fopen(argv[1], "rb");
@@ -61,9 +61,34 @@ int main(int argc, char **argv) {
return 1;
}
+
+ memset(line, 0, LINELEN+1);
+
N = 0;
+ j = 0;
+ n = 0;
+ // sec.ms,freq_min,freq_max,Hz/bin,N_bins, ...
while ( (c = fgetc(fp)) != EOF) {
- if (c == '\n') N++;
+ if (c == '\n') break;
+ if (c == ' ') continue;
+ if (c == ',') {
+ if (n == 1) {
+ int freq_min = atoi(line);
+ sr = -2*freq_min;
+ }
+ if (n == 4) {
+ N = atoi(line);
+ break;
+ }
+
+ n++;
+ memset(line, 0, LINELEN+1);
+ j = 0;
+ }
+ else {
+ line[j] = c;
+ j++;
+ }
}
db = calloc(N+1, sizeof(float)); if (db == NULL) return 2;
@@ -73,20 +98,30 @@ int main(int argc, char **argv) {
intdb = calloc(N+1, sizeof(float)); if (intdb == NULL) return 2;
peak = calloc(N+1, sizeof(float)); if (peak == NULL) return 2;
- fseek(fp, 0, SEEK_SET);
- pbuf = fgets(line, LINELEN, fp);
- p1 = strstr(line, "sr:");
- if (p1) sr = atoi(p1+3);
- for (n = 0; n < N; n++) {
- memset(line, 0, LINELEN+1);
- pbuf = fgets(line, LINELEN, fp);
- p1 = strstr(line, ";"); //p2 = strstr(p1+1, ";");
- if (p1) {
- fq[n] = atof(line); //freq[n] = atof(p1+1);
- db[n] = atof(p1+1); //atof(p2+1);
+ // ..., db_1,...,db_N:
+ memset(line, 0, LINELEN+1);
+ j = 0;
+ n = 0;
+ while ( (c = fgetc(fp)) != EOF) {
+ if (c == '\n') break;
+ if (c == ' ') continue;
+ if (c == ',') {
+ if (n < N) {
+ db[n] = atof(line);
+ fq[n] = -0.5 + n/(float)N;
+ }
+
+ n++;
+ memset(line, 0, LINELEN+1);
+ j = 0;
+ }
+ else {
+ line[j] = c;
+ j++;
}
}
+
f0 = N/2;
globmin = 0.0;
@@ -94,14 +129,12 @@ int main(int argc, char **argv) {
float db_spike3 = 10.0;
int spike_wl3 = 3; //freq2bin(&DFT, 200); // 3 // 200 Hz
int spike_wl5 = 5; //freq2bin(&DFT, 200); // 3 // 200 Hz
- //float db_spike1 = 15.0;
- //int spike_wl1 = 1; //freq2bin(&DFT, 200); // 3 // 200 Hz
+
dx = 200.0;
if (sr) dx = sr*(fq[f0+1]-fq[f0]); //freq[f0+1]-freq[f0];
dn = 2*(int)(2400.0/dx)+1; // (odd/symmetric) integration width: 4800+dx Hz
if (option_verbose > 1) fprintf(stderr, "dn = %d\n", dn);
- //for (j = 0; j < N; j++) db[j] /= (float)n;
// dc-spike (N-1,)N,0,1(,2): subtract mean/avg
// spikes in general: