rtl_power: apply first series of patches

* fix typos
* droop compensation
* improve sample rates, peak hold
* direct sampling
* offset tuning

Signed-off-by: Steve Markgraf <steve@steve-m.de>
This commit is contained in:
Kyle Keen 2013-12-26 19:19:42 +01:00 committed by Steve Markgraf
parent 1050b96b47
commit fca2b7d52f

View File

@ -65,10 +65,15 @@
#include "rtl-sdr.h" #include "rtl-sdr.h"
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define DEFAULT_BUF_LENGTH (1 * 16384) #define DEFAULT_BUF_LENGTH (1 * 16384)
#define AUTO_GAIN -100 #define AUTO_GAIN -100
#define BUFFER_DUMP (1<<12) #define BUFFER_DUMP (1<<12)
#define MAXIMUM_RATE 2800000
#define MINIMUM_RATE 1000000
static volatile int do_exit = 0; static volatile int do_exit = 0;
static rtlsdr_dev_t *dev = NULL; static rtlsdr_dev_t *dev = NULL;
FILE *file; FILE *file;
@ -96,6 +101,7 @@ struct tuning_state
/* having the iq buffer here is wasteful, but will avoid contention */ /* having the iq buffer here is wasteful, but will avoid contention */
uint8_t *buf8; uint8_t *buf8;
int buf_len; int buf_len;
//int *comp_fir;
//pthread_rwlock_t buf_lock; //pthread_rwlock_t buf_lock;
//pthread_mutex_t buf_mutex; //pthread_mutex_t buf_mutex;
}; };
@ -106,6 +112,8 @@ struct tuning_state tunes[MAX_TUNES];
int tune_count = 0; int tune_count = 0;
int boxcar = 1; int boxcar = 1;
int comp_fir_size = 0;
int peak_hold = 0;
void usage(void) void usage(void)
{ {
@ -114,7 +122,7 @@ void usage(void)
"Use:\trtl_power -f freq_range [-options] [filename]\n" "Use:\trtl_power -f freq_range [-options] [filename]\n"
"\t-f lower:upper:bin_size [Hz]\n" "\t-f lower:upper:bin_size [Hz]\n"
"\t (bin size is a maximum, smaller more convenient bins\n" "\t (bin size is a maximum, smaller more convenient bins\n"
"\t will be used. valid range 1Hz - 2MHz)\n" "\t will be used. valid range 1Hz - 2.8MHz)\n"
"\t[-i integration_interval (default: 10 seconds)]\n" "\t[-i integration_interval (default: 10 seconds)]\n"
"\t (buggy if a full sweep takes longer than the interval)\n" "\t (buggy if a full sweep takes longer than the interval)\n"
"\t[-1 enables single-shot mode (default: off)]\n" "\t[-1 enables single-shot mode (default: off)]\n"
@ -134,8 +142,13 @@ void usage(void)
"\t[-c crop_percent (default: 0%%, recommended: 20%%-50%%)]\n" "\t[-c crop_percent (default: 0%%, recommended: 20%%-50%%)]\n"
"\t (discards data at the edges, 100%% discards everything)\n" "\t (discards data at the edges, 100%% discards everything)\n"
"\t (has no effect for bins larger than 1MHz)\n" "\t (has no effect for bins larger than 1MHz)\n"
"\t[-F enables low-leakage downsample filter (default: off)]\n" "\t[-F fir_size (default: disabled)]\n"
"\t (has bad roll off, try with '-c 50%%')\n" "\t (enables low-leakage downsample filter,\n"
"\t fir_size can be 0 or 9. 0 has bad roll off,\n"
"\t try with '-c 50%%')\n"
"\t[-P enables peak hold (default: off)]\n"
"\t[-D enable direct sampling (default: off)]\n"
"\t[-O enable offset tuning (default: off)]\n"
"\n" "\n"
"CSV FFT output columns:\n" "CSV FFT output columns:\n"
"\tdate, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...\n\n" "\tdate, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...\n\n"
@ -189,6 +202,23 @@ static void sighandler(int signum)
#define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m)
#define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m)
/* {length, coef, coef, coef} and scaled by 2^15
for now, only length 9, optimal way to get +85% bandwidth */
#define CIC_TABLE_MAX 10
int cic_9_tables[][10] = {
{0,},
{9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156},
{9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128},
{9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129},
{9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122},
{9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120},
{9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120},
{9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119},
{9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119},
{9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119},
{9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199},
};
/* FFT based on fix_fft.c by Roberts, Slaney and Bouras /* FFT based on fix_fft.c by Roberts, Slaney and Bouras
http://www.jjj.de/fft/fftpage.html http://www.jjj.de/fft/fftpage.html
16 bit ints for everything 16 bit ints for everything
@ -379,7 +409,11 @@ void rms_power(struct tuning_state *ts)
err = t * 2 * dc - dc * dc * buf_len; err = t * 2 * dc - dc * dc * buf_len;
p -= (long)round(err); p -= (long)round(err);
if (!peak_hold) {
ts->avg[0] += p; ts->avg[0] += p;
} else {
ts->avg[0] = MAX(ts->avg[0], p);
}
ts->samples += 1; ts->samples += 1;
} }
@ -500,28 +534,29 @@ void frequency_range(char *arg, double crop)
step[-1] = ':'; step[-1] = ':';
downsample = 1; downsample = 1;
downsample_passes = 0; downsample_passes = 0;
/* evenly sized ranges, as close to 2MHz as possible */ /* evenly sized ranges, as close to MAXIMUM_RATE as possible */
// todo, replace loop with algebra
for (i=1; i<1500; i++) { for (i=1; i<1500; i++) {
bw_seen = (upper - lower) / i; bw_seen = (upper - lower) / i;
bw_used = (int)((double)(bw_seen) / (1.0 - crop)); bw_used = (int)((double)(bw_seen) / (1.0 - crop));
if (bw_used > 2000000) { if (bw_used > MAXIMUM_RATE) {
continue;} continue;}
tune_count = i; tune_count = i;
break; break;
} }
/* unless small bandwidth */ /* unless small bandwidth */
if (bw_used < 1000000) { if (bw_used < MINIMUM_RATE) {
tune_count = 1;} tune_count = 1;
if (boxcar && bw_used < 1000000) { downsample = MAXIMUM_RATE / bw_used;
downsample = 2000000 / bw_used;
bw_used = bw_used * downsample; bw_used = bw_used * downsample;
} }
while (bw_used < 1000000) { /* not boxcar */ if (!boxcar && downsample > 1) {
downsample_passes++; downsample_passes = (int)log2(downsample);
downsample = 1 << downsample_passes; downsample = 1 << downsample_passes;
bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop)); bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop));
} }
/* number of bins is power-of-two, bin size is under limit */ /* number of bins is power-of-two, bin size is under limit */
// todo, replace loop with log2
for (i=1; i<=21; i++) { for (i=1; i<=21; i++) {
bin_e = i; bin_e = i;
bin_size = (double)bw_used / (double)((1<<i) * downsample); bin_size = (double)bw_used / (double)((1<<i) * downsample);
@ -529,7 +564,7 @@ void frequency_range(char *arg, double crop)
break;} break;}
} }
/* unless giant bins */ /* unless giant bins */
if (max_size >= 1000000) { if (max_size >= MINIMUM_RATE) {
bw_seen = max_size; bw_seen = max_size;
bw_used = max_size; bw_used = max_size;
tune_count = (upper - lower) / bw_seen; tune_count = (upper - lower) / bw_seen;
@ -637,6 +672,36 @@ void remove_dc(int16_t *data, int length)
} }
} }
void generic_fir(int16_t *data, int length, int *fir)
/* Okay, not at all generic. Assumes length 9, fix that eventually. */
{
int d, f, temp, sum;
int hist[9] = {0,};
/* cheat on the beginning, let it go unfiltered */
for (d=0; d<18; d+=2) {
hist[d/2] = data[d];
}
for (d=18; d<length; d+=2) {
temp = data[d];
sum = 0;
sum += (hist[0] + hist[8]) * fir[1];
sum += (hist[1] + hist[7]) * fir[2];
sum += (hist[2] + hist[6]) * fir[3];
sum += (hist[3] + hist[5]) * fir[4];
sum += hist[4] * fir[5];
data[d] = (int16_t)(sum >> 15) ;
hist[0] = hist[1];
hist[1] = hist[2];
hist[2] = hist[3];
hist[3] = hist[4];
hist[4] = hist[5];
hist[5] = hist[6];
hist[6] = hist[7];
hist[7] = hist[8];
hist[8] = temp;
}
}
void downsample_iq(int16_t *data, int length) void downsample_iq(int16_t *data, int length)
{ {
fifth_order(data, length); fifth_order(data, length);
@ -647,7 +712,7 @@ void downsample_iq(int16_t *data, int length)
void scanner(void) void scanner(void)
{ {
int i, j, j2, f, n_read, offset, bin_e, bin_len, buf_len, ds; int i, j, j2, f, n_read, offset, bin_e, bin_len, buf_len, ds, ds_p;
int32_t w; int32_t w;
struct tuning_state *ts; struct tuning_state *ts;
bin_e = tunes[0].bin_e; bin_e = tunes[0].bin_e;
@ -673,22 +738,30 @@ void scanner(void)
fft_buf[j] = (int16_t)ts->buf8[j] - 127; fft_buf[j] = (int16_t)ts->buf8[j] - 127;
} }
ds = ts->downsample; ds = ts->downsample;
if (boxcar) { ds_p = ts->downsample_passes;
if (boxcar && ds > 1) {
j=2, j2=0; j=2, j2=0;
while (j < buf_len) { while (j < buf_len) {
fft_buf[j2] += fft_buf[j]; fft_buf[j2] += fft_buf[j];
fft_buf[j2+1] += fft_buf[j+1]; fft_buf[j2+1] += fft_buf[j+1];
fft_buf[j] = 0;
fft_buf[j+1] = 0;
j += 2; j += 2;
if (j % (ds*2) == 0) { if (j % (ds*2) == 0) {
j2 += 2;} j2 += 2;}
} }
} else { /* recursive */ } else if (ds_p) { /* recursive */
for (j=0; j < ts->downsample_passes; j++) { for (j=0; j < ds_p; j++) {
downsample_iq(fft_buf, buf_len >> j); downsample_iq(fft_buf, buf_len >> j);
} }
/* droop compensation */
if (comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) {
generic_fir(fft_buf, buf_len >> j, cic_9_tables[ds_p]);
generic_fir(fft_buf+1, (buf_len >> j)-1, cic_9_tables[ds_p]);
} }
remove_dc(fft_buf, buf_len >> j); }
remove_dc(fft_buf+1, (buf_len >> j) - 1); remove_dc(fft_buf, buf_len / ds);
remove_dc(fft_buf+1, (buf_len / ds) - 1);
/* window function and fft */ /* window function and fft */
for (offset=0; offset<(buf_len/ds); offset+=(2*bin_len)) { for (offset=0; offset<(buf_len/ds); offset+=(2*bin_len)) {
// todo, let rect skip this // todo, let rect skip this
@ -703,9 +776,15 @@ void scanner(void)
fft_buf[offset+j*2+1] = (int16_t)w; fft_buf[offset+j*2+1] = (int16_t)w;
} }
fix_fft(fft_buf+offset, bin_e); fix_fft(fft_buf+offset, bin_e);
if (!peak_hold) {
for (j=0; j<bin_len; j++) { for (j=0; j<bin_len; j++) {
ts->avg[j] += (long) abs(fft_buf[offset+j*2]); ts->avg[j] += (long) abs(fft_buf[offset+j*2]);
} }
} else {
for (j=0; j<bin_len; j++) {
ts->avg[j] = MAX((long) abs(fft_buf[offset+j*2]), ts->avg[j]);
}
}
ts->samples += ds; ts->samples += ds;
} }
} }
@ -773,6 +852,8 @@ int main(int argc, char **argv)
int fft_threads = 1; int fft_threads = 1;
int smoothing = 0; int smoothing = 0;
int single = 0; int single = 0;
int direct_sampling = 0;
int offset_tuning = 0;
double crop = 0.0; double crop = 0.0;
char vendor[256], product[256], serial[256]; char vendor[256], product[256], serial[256];
char *freq_optarg; char *freq_optarg;
@ -784,7 +865,7 @@ int main(int argc, char **argv)
double (*window_fn)(int, int) = rectangle; double (*window_fn)(int, int) = rectangle;
freq_optarg = ""; freq_optarg = "";
while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:1Fh")) != -1) { while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1PDOh")) != -1) {
switch (opt) { switch (opt) {
case 'f': // lower:upper:bin_size case 'f': // lower:upper:bin_size
freq_optarg = strdup(optarg); freq_optarg = strdup(optarg);
@ -838,8 +919,18 @@ int main(int argc, char **argv)
case '1': case '1':
single = 1; single = 1;
break; break;
case 'P':
peak_hold = 1;
break;
case 'D':
direct_sampling = 1;
break;
case 'O':
offset_tuning = 1;
break;
case 'F': case 'F':
boxcar = 0; boxcar = 0;
comp_fir_size = atoi(optarg);
break; break;
case 'h': case 'h':
default: default:
@ -907,6 +998,24 @@ int main(int argc, char **argv)
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
#endif #endif
if (direct_sampling) {
r = rtlsdr_set_direct_sampling(dev, 1);
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n");
} else {
fprintf(stderr, "Direct sampling mode enabled.\n");
}
}
if (offset_tuning) {
r = rtlsdr_set_offset_tuning(dev, 1);
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set offset tuning.\n");
} else {
fprintf(stderr, "Offset tuning mode enabled.\n");
}
}
/* Set the tuner gain */ /* Set the tuner gain */
if (gain == AUTO_GAIN) { if (gain == AUTO_GAIN) {
r = rtlsdr_set_tuner_gain_mode(dev, 0); r = rtlsdr_set_tuner_gain_mode(dev, 0);