Skip to content

Commit

Permalink
[rffft] Add -P option to parse params from SatDump and GQRX filenames
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Herren (HB9FXX) <[email protected]>
  • Loading branch information
MartinHerren committed Sep 27, 2023
1 parent 96d4ac3 commit 2aaaa6e
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 13 deletions.
6 changes: 3 additions & 3 deletions Makefile.linux
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ rftrack: rftrack.o rfio.o rftime.o rftrace.o sgdp4.o satutl.o deep.o ferror.o
rfplot: rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o rftles.o
gfortran -o rfplot rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o rftles.o $(LFLAGS)

rffft: rffft.o rftime.o
$(CC) -o rffft rffft.o rftime.o -lfftw3f -lm
rffft: rffft.o rffft_internal.o rftime.o
$(CC) -o rffft rffft.o rffft_internal.o rftime.o -lfftw3f -lm

tests/tests: tests/tests.o tests/tests_rftles.o rftles.o satutl.o ferror.o
tests/tests: tests/tests.o tests/tests_rffft_internal.o tests/tests_rftles.o rffft_internal.o rftles.o satutl.o ferror.o
$(CC) -Wall -o $@ $^ -lcmocka -lm

tests: tests/tests
Expand Down
6 changes: 3 additions & 3 deletions Makefile.osx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ rftrack: rftrack.o rfio.o rftime.o rftrace.o sgdp4.o satutl.o deep.o ferror.o
rfplot: rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o versafit.o dsmin.o simplex.o rftles.o
$(CC) -o rfplot rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o versafit.o dsmin.o simplex.o rftles.o $(LFLAGS)

rffft: rffft.o rftime.o
$(CC) -o rffft rffft.o rftime.o -lfftw3f -lm $(LFLAGS)
rffft: rffft.o rffft_internal.o rftime.o
$(CC) -o rffft rffft.o rffft_internal.o rftime.o -lfftw3f -lm $(LFLAGS)

tests/tests: tests/tests.o tests/tests_rftles.o rftles.o satutl.o ferror.o
tests/tests: tests/tests.o tests/tests_rffft_internal.o tests/tests_rftles.o rffft_internal.o rftles.o satutl.o ferror.o
$(CC) -Wall -o $@ $^ -lcmocka -lm

tests: tests/tests
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,8 @@ With I/Q recordings obtained from Gqrx:

**Gqrx** records complex samples into `raw` files. The filename contains date, time, center frequency and samplerate separated by underscores. Replace `YYYYMMDD` and `HHMMSS` by your actual time and respectively. Pay attention to insert an uppercase `T` between date and time in the time stamp parameter of the `rffft` command.

The output spectrograms can be viewed and analysed using `rfplot`.
Alternatively, with I/Q recordings from GQRX and SatDump, the `-P` option can be used to automatically extract the timestamp, format, frequency and samplerate from the filename:

./rffft -P -i gqrx_YYYYMMDD_HHMMSS_97400000_2000000_fc.raw

The output spectrograms can be viewed and analysed using `rfplot`.
6 changes: 3 additions & 3 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ rftrack: rftrack.o rfio.o rftime.o rftrace.o sgdp4.o satutl.o deep.o ferror.o
rfplot: rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o versafit.o dsmin.o simplex.o rftles.o
gfortran -o rfplot rfplot.o rftime.o rfio.o rftrace.o sgdp4.o satutl.o deep.o ferror.o versafit.o dsmin.o simplex.o rftles.o $(LFLAGS)

rffft: rffft.o rftime.o
$(CC) -o rffft rffft.o rftime.o -lfftw3f -lm
rffft: rffft.o rffft_internal.o rftime.o
$(CC) -o rffft rffft.o rffft_internal.o rftime.o -lfftw3f -lm

tests/tests: tests/tests.o tests/tests_rftles.o rftles.o satutl.o ferror.o
tests/tests: tests/tests.o tests/tests_rffft_internal.o tests/tests_rftles.o rffft_internal.o rftles.o satutl.o ferror.o
$(CC) -Wall -o $@ $^ -lcmocka -lm

tests: tests/tests
Expand Down
22 changes: 19 additions & 3 deletions rffft.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <sys/time.h>
#include "rftime.h"

#include "rffft_internal.h"

void usage(void)
{
printf("rffft: FFT RF observations\n\n");
Expand All @@ -27,6 +29,7 @@ void usage(void)
printf("-I Invert frequencies\n");
printf("-b Digitize output to bytes [off]\n");
printf("-q Quiet mode, no output [off]\n");
printf("-P Parse frequency, samplerate, format and start time from filename\n");
printf("-h This help\n");

return;
Expand All @@ -49,10 +52,11 @@ int main(int argc,char *argv[])
struct timeval start,end;
char tbuf[30],nfd[32],header[256]="";
int sign=1;
int parse_params_from_filename = 0;

// Read arguments
if (argc>1) {
while ((arg=getopt(argc,argv,"i:f:s:c:t:p:n:hm:F:T:bqR:o:IS:"))!=-1) {
while ((arg=getopt(argc,argv,"i:f:s:c:t:p:n:hm:F:T:bqR:o:IS:P"))!=-1) {
switch(arg) {

case 'i':
Expand Down Expand Up @@ -125,7 +129,12 @@ int main(int argc,char *argv[])
case 'I':
sign=-1;
break;


case 'P':
parse_params_from_filename = 1;
realtime=0;
break;

case 'h':
usage();
return 0;
Expand All @@ -140,9 +149,16 @@ int main(int argc,char *argv[])
return 0;
}

if (parse_params_from_filename != 0) {
if (rffft_params_from_filename(infname, &samp_rate, &freq, &informat, nfd) != 0) {
fprintf(stderr, "Error parsing parameters from filename\n");
exit(-1);
};
}

// Ensure integer number of spectra per subintegration
tint=ceil(fchan*tint)/fchan;

// Number of channels
nchan=(int) (samp_rate/fchan);

Expand Down
163 changes: 163 additions & 0 deletions rffft_internal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include "rffft_internal.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>


// Filename formats:
// - SatDump
// - 2023-08-05_08-02-00_16000000SPS_2274000000Hz.s8
// - 2023-08-05_18-02-45-534_16000000SPS_2284000000Hz.s16
// - 2023-08-05_18-02-45-1691258565.534000_16000000SPS_2284000000Hz.f32
// s8: char, s16 short int, f32 float.
// SatDump also supports .wav and compressed versions of s8/s16/f32 with .ziq
// extension. Those are not yet supported
// timestamp can have an added milliseconds field, configurable. This feature
// was broken during some time so files with this convention still exists.
// - GQRX:
// - gqrx_20230806_151838_428000000_200000_fc.raw
// format always float32
int rffft_params_from_filename(char * filename, double * samplerate, double * frequency, char * format, char * starttime) {
// Temp vars to hold parsed values
int p_year, p_month, p_day, p_hours, p_minutes, p_seconds, p_fractal_seconds;
int p_dummy_int;
double p_samplerate, p_frequency;
char p_format[16];
char p_dummy_string[128];
int parsed_tokens;

char * base_filename = basename(filename);

// Broken SatDump string with milliseconds activated
parsed_tokens = sscanf(
base_filename,
"%04d-%02d-%02d_%02d-%02d-%02d-%d.%06d_%lfSPS_%lfHz.%s",
&p_year,
&p_month,
&p_day,
&p_hours,
&p_minutes,
&p_seconds,
&p_dummy_int,
&p_fractal_seconds,
&p_samplerate,
&p_frequency,
p_format);

if (parsed_tokens == 11) {
*samplerate = p_samplerate;
*frequency = p_frequency;

if ((strlen(p_format) == 2) && (strncmp("s8", p_format, 2) == 0)) {
*format = 'c';
} else if ((strlen(p_format) == 3) && (strncmp("s16", p_format, 3) == 0)) {
*format = 'i';
} else if ((strlen(p_format) == 3) && (strncmp("f32", p_format, 3) == 0)) {
*format = 'f';
} else {
printf("Unsupported SatDump format %s\n", p_format);
return -1;
}

snprintf(starttime, 32, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", p_year, p_month, p_day, p_hours, p_minutes, p_seconds, p_fractal_seconds / 1000);

return 0;
}

// SatDump string with milliseconds activated
parsed_tokens = sscanf(
base_filename,
"%04d-%02d-%02d_%02d-%02d-%02d-%03d_%lfSPS_%lfHz.%s",
&p_year,
&p_month,
&p_day,
&p_hours,
&p_minutes,
&p_seconds,
&p_fractal_seconds,
&p_samplerate,
&p_frequency,
p_format);

if (parsed_tokens == 10) {
*samplerate = p_samplerate;
*frequency = p_frequency;

if ((strlen(p_format) == 2) && (strncmp("s8", p_format, 2) == 0)) {
*format = 'c';
} else if ((strlen(p_format) == 3) && (strncmp("s16", p_format, 3) == 0)) {
*format = 'i';
} else if ((strlen(p_format) == 3) && (strncmp("f32", p_format, 3) == 0)) {
*format = 'f';
} else {
printf("Unsupported SatDump format %s\n", p_format);
return -1;
}

snprintf(starttime, 32, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", p_year, p_month, p_day, p_hours, p_minutes, p_seconds, p_fractal_seconds);

return 0;
}

// SatDump string without milliseconds activated
parsed_tokens = sscanf(
base_filename,
"%04d-%02d-%02d_%02d-%02d-%02d_%lfSPS_%lfHz.%s",
&p_year,
&p_month,
&p_day,
&p_hours,
&p_minutes,
&p_seconds,
&p_samplerate,
&p_frequency,
p_format);

if (parsed_tokens == 9) {
*samplerate = p_samplerate;
*frequency = p_frequency;

if ((strlen(p_format) == 2) && (strncmp("s8", p_format, 2) == 0)) {
*format = 'c';
} else if ((strlen(p_format) == 3) && (strncmp("s16", p_format, 3) == 0)) {
*format = 'i';
} else if ((strlen(p_format) == 3) && (strncmp("f32", p_format, 3) == 0)) {
*format = 'f';
} else {
printf("Unsupported SatDump format %s\n", p_format);
return -1;
}

snprintf(starttime, 32, "%04d-%02d-%02dT%02d:%02d:%02d", p_year, p_month, p_day, p_hours, p_minutes, p_seconds);

return 0;
}

// GQRX
parsed_tokens = sscanf(
base_filename,
"gqrx_%04d%02d%02d_%02d%02d%02d_%lf_%lf_%s.raw",
&p_year,
&p_month,
&p_day,
&p_hours,
&p_minutes,
&p_seconds,
&p_frequency,
&p_samplerate,
p_dummy_string);

if (parsed_tokens == 9) {
*samplerate = p_samplerate;
*frequency = p_frequency;
*format = 'f';

snprintf(starttime, 32, "%04d-%02d-%02dT%02d:%02d:%02d", p_year, p_month, p_day, p_hours, p_minutes, p_seconds);

return 0;
}

return -1;
}
23 changes: 23 additions & 0 deletions rffft_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef _RFFFT_INTERNAL_H
#define _RFFFT_INTERNAL_H

#include "sgdp4h.h"

#ifdef __cplusplus
extern "C" {
#endif

// input:
// filename: filename string to parse
// output:
// samplerate: parsed samplerate
// frequency: parsed frequency
// format: parsed sample format: char: 'c', int: 'i', float: 'f'
// starttime: parsed start time string formatted YYYY-MM-DDTHH:MM:SS.sss
int rffft_params_from_filename(char * filename, double * samplerate, double * frequency, char * format, char * starttime);

#ifdef __cplusplus
}
#endif

#endif /* _RFFFT_INTERNAL_H */
2 changes: 2 additions & 0 deletions tests/tests.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "tests_rffft_internal.h"
#include "tests_rftles.h"

#include <stdarg.h>
Expand All @@ -9,6 +10,7 @@
int main(void) {
int failures = 0;

failures += run_rffft_internal_tests();
failures += run_tle_tests();

return failures;
Expand Down
76 changes: 76 additions & 0 deletions tests/tests_rffft_internal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "tests_rffft_internal.h"

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <stdlib.h>
#include <cmocka.h>

#include "../rffft_internal.h"

// Tests

// Test SatDump filenames
void rffft_internal_parse_satdump_filenames(void **state) {
double samplerate = 0;
double frequency = 0;
char format = '\0';
char starttime[] = "YYYY-mm-ddTHH:MM:SS.sss";
char ref_format = '\0';

// s8 file without milliseconds
ref_format = 'c';
assert_int_equal(0, rffft_params_from_filename("2023-08-05_08-02-00_16000000SPS_2274000000Hz.s8", &samplerate, &frequency, &format, starttime));
// assert_double_equal has been introduced in cmocka 1.1.6 not available on most distribs yet
assert_float_equal(16e6, samplerate, 1e-12);
assert_float_equal(2.274e9, frequency, 1e-12);
assert_memory_equal(&ref_format, &format, 1);
assert_string_equal("2023-08-05T08:02:00", starttime);

// f32 file with milliseconds
ref_format = 'f';
assert_int_equal(0, rffft_params_from_filename("2023-08-17_11-41-14-373_1000000SPS_100000000Hz.f32", &samplerate, &frequency, &format, starttime));
assert_float_equal(1e6, samplerate, 1e-12);
assert_float_equal(100e6, frequency, 1e-12);
assert_memory_equal(&ref_format, &format, 1);
assert_string_equal("2023-08-17T11:41:14.373", starttime);

// s16 file with broken milliseconds format
ref_format = 'i';
assert_int_equal(0, rffft_params_from_filename("2023-08-05_18-02-45-1691258565.534000_8000000SPS_2284000000Hz.s16", &samplerate, &frequency, &format, starttime));
assert_float_equal(8e6, samplerate, 1e-12);
assert_float_equal(2.284e9, frequency, 1e-12);
assert_memory_equal(&ref_format, &format, 1);
assert_string_equal("2023-08-05T18:02:45.534", starttime);

assert_int_equal(-1, rffft_params_from_filename("2023-08-05-19:59:30_16000000SPS_402000000Hz.f32", &samplerate, &frequency, &format, starttime));
}

// Test GQRX filenames
void rffft_internal_parse_gqrx_filenames(void **state) {
double samplerate = 0;
double frequency = 0;
char format = '\0';
char starttime[] = "YYYY-mm-ddTHH:MM:SS.sss";
char ref_format = '\0';

ref_format = 'f';
assert_int_equal(0, rffft_params_from_filename("gqrx_20230806_151838_428000000_200000_fc.raw", &samplerate, &frequency, &format, starttime));
// assert_double_equal has been introduced in cmocka 1.1.6 not available on most distribs yet
assert_float_equal(200e3, samplerate, 1e-12);
assert_float_equal(428e6, frequency, 1e-12);
assert_memory_equal(&ref_format, &format, 1);
assert_string_equal("2023-08-06T15:18:38", starttime);

assert_int_equal(-1, rffft_params_from_filename("gqrx_2023-08-06_15:18:38_428000000_200000_fc.raw", &samplerate, &frequency, &format, starttime));
}

// Entry point to run all tests
int run_rffft_internal_tests() {
const struct CMUnitTest tests[] = {
cmocka_unit_test(rffft_internal_parse_satdump_filenames),
cmocka_unit_test(rffft_internal_parse_gqrx_filenames),
};

return cmocka_run_group_tests_name("rffft internal", tests, NULL, NULL);
}
Loading

0 comments on commit 2aaaa6e

Please sign in to comment.