diff options
author | dinashi <dos21h@gmail.com> | 2022-03-13 11:41:55 +0000 |
---|---|---|
committer | dinashi <dos21h@gmail.com> | 2022-03-13 11:41:55 +0000 |
commit | 37b99c704d77c60fa808fd393d17045d1d6cf32e (patch) | |
tree | 55fdd71d02c182ec3fcf6fdb9af03979ff84fb3f /utils/rtl_power.c | |
parent | ded8adc2fec31c627cce97e1d328e739631bf759 (diff) | |
download | r820t-37b99c704d77c60fa808fd393d17045d1d6cf32e.tar.gz r820t-37b99c704d77c60fa808fd393d17045d1d6cf32e.zip |
Add all changes and READMe
Diffstat (limited to 'utils/rtl_power.c')
-rw-r--r-- | utils/rtl_power.c | 1015 |
1 files changed, 0 insertions, 1015 deletions
diff --git a/utils/rtl_power.c b/utils/rtl_power.c deleted file mode 100644 index 6204de2..0000000 --- a/utils/rtl_power.c +++ /dev/null @@ -1,1015 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de> - * Copyright (C) 2012 by Hoernchen <la@tfc-server.de> - * Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * 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 General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -/* - * rtl_power: general purpose FFT integrator - * -f low_freq:high_freq:max_bin_size - * -i seconds - * outputs CSV - * time, low, high, step, db, db, db ... - * db optional? raw output might be better for noise correction - * todo: - * threading - * randomized hopping - * noise correction - * continuous IIR - * general astronomy usefulness - * multiple dongles - * multiple FFT workers - * check edge cropping for off-by-one and rounding errors - * 1.8MS/s for hiding xtal harmonics - */ - -#include <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - -#ifndef _WIN32 -#include <unistd.h> -#else -#include <windows.h> -#include <fcntl.h> -#include <io.h> -#include "getopt/getopt.h" -#define usleep(x) Sleep(x/1000) -#if defined(_MSC_VER) && (_MSC_VER < 1800) -#define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) -#endif -#define _USE_MATH_DEFINES -#endif - -#include <math.h> -#include <pthread.h> -#include <libusb.h> - -#include "rtl-sdr.h" -#include "convenience/convenience.h" - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) - -#define DEFAULT_BUF_LENGTH (1 * 16384) -#define AUTO_GAIN -100 -#define BUFFER_DUMP (1<<12) - -#define MAXIMUM_RATE 2800000 -#define MINIMUM_RATE 1000000 - -static volatile int do_exit = 0; -static rtlsdr_dev_t *dev = NULL; -FILE *file; - -int16_t* Sinewave; -double* power_table; -int N_WAVE, LOG2_N_WAVE; -int next_power; -int16_t *fft_buf; -int *window_coefs; - -struct tuning_state -/* one per tuning range */ -{ - int freq; - int rate; - int bin_e; - long *avg; /* length == 2^bin_e */ - int samples; - int downsample; - int downsample_passes; /* for the recursive filter */ - double crop; - //pthread_rwlock_t avg_lock; - //pthread_mutex_t avg_mutex; - /* having the iq buffer here is wasteful, but will avoid contention */ - uint8_t *buf8; - int buf_len; - //int *comp_fir; - //pthread_rwlock_t buf_lock; - //pthread_mutex_t buf_mutex; -}; - -/* 3000 is enough for 3GHz b/w worst case */ -#define MAX_TUNES 3000 -struct tuning_state tunes[MAX_TUNES]; -int tune_count = 0; - -int boxcar = 1; -int comp_fir_size = 0; -int peak_hold = 0; - -void usage(void) -{ - fprintf(stderr, - "rtl_power, a simple FFT logger for RTL2832 based DVB-T receivers\n\n" - "Use:\trtl_power -f freq_range [-options] [filename]\n" - "\t-f lower:upper:bin_size [Hz]\n" - "\t (bin size is a maximum, smaller more convenient bins\n" - "\t will be used. valid range 1Hz - 2.8MHz)\n" - "\t[-i integration_interval (default: 10 seconds)]\n" - "\t (buggy if a full sweep takes longer than the interval)\n" - "\t[-1 enables single-shot mode (default: off)]\n" - "\t[-e exit_timer (default: off/0)]\n" - //"\t[-s avg/iir smoothing (default: avg)]\n" - //"\t[-t threads (default: 1)]\n" - "\t[-d device_index (default: 0)]\n" - "\t[-g tuner_gain (default: automatic)]\n" - "\t[-p ppm_error (default: 0)]\n" - "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" - "\tfilename (a '-' dumps samples to stdout)\n" - "\t (omitting the filename also uses stdout)\n" - "\n" - "Experimental options:\n" - "\t[-w window (default: rectangle)]\n" - "\t (hamming, blackman, blackman-harris, hann-poisson, bartlett, youssef)\n" - // kaiser - "\t[-c crop_percent (default: 0%%, recommended: 20%%-50%%)]\n" - "\t (discards data at the edges, 100%% discards everything)\n" - "\t (has no effect for bins larger than 1MHz)\n" - "\t[-F fir_size (default: disabled)]\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" - "CSV FFT output columns:\n" - "\tdate, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...\n\n" - "Examples:\n" - "\trtl_power -f 88M:108M:125k fm_stations.csv\n" - "\t (creates 160 bins across the FM band,\n" - "\t individual stations should be visible)\n" - "\trtl_power -f 100M:1G:1M -i 5m -1 survey.csv\n" - "\t (a five minute low res scan of nearly everything)\n" - "\trtl_power -f ... -i 15m -1 log.csv\n" - "\t (integrate for 15 minutes and exit afterwards)\n" - "\trtl_power -f ... -e 1h | gzip > log.csv.gz\n" - "\t (collect data for one hour and compress it on the fly)\n\n" - "Convert CSV to a waterfall graphic with:\n" - "\t http://kmkeen.com/tmp/heatmap.py.txt \n"); - exit(1); -} - -void multi_bail(void) -{ - if (do_exit == 1) - { - fprintf(stderr, "Signal caught, finishing scan pass.\n"); - } - if (do_exit >= 2) - { - fprintf(stderr, "Signal caught, aborting immediately.\n"); - } -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - do_exit++; - multi_bail(); - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - do_exit++; - multi_bail(); -} -#endif - -/* more cond dumbness */ -#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) - -/* {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}, -}; - -#if defined(_MSC_VER) && (_MSC_VER < 1800) -double log2(double n) -{ - return log(n) / log(2.0); -} -#endif - -/* FFT based on fix_fft.c by Roberts, Slaney and Bouras - http://www.jjj.de/fft/fftpage.html - 16 bit ints for everything - -32768..+32768 maps to -1.0..+1.0 -*/ - -void sine_table(int size) -{ - int i; - double d; - LOG2_N_WAVE = size; - N_WAVE = 1 << LOG2_N_WAVE; - Sinewave = malloc(sizeof(int16_t) * N_WAVE*3/4); - power_table = malloc(sizeof(double) * N_WAVE); - for (i=0; i<N_WAVE*3/4; i++) - { - d = (double)i * 2.0 * M_PI / N_WAVE; - Sinewave[i] = (int)round(32767*sin(d)); - //printf("%i\n", Sinewave[i]); - } -} - -static inline int16_t FIX_MPY(int16_t a, int16_t b) -/* fixed point multiply and scale */ -{ - int c = ((int)a * (int)b) >> 14; - b = c & 0x01; - return (c >> 1) + b; -} - -int fix_fft(int16_t iq[], int m) -/* interleaved iq[], 0 <= n < 2**m, changes in place */ -{ - int mr, nn, i, j, l, k, istep, n, shift; - int16_t qr, qi, tr, ti, wr, wi; - n = 1 << m; - if (n > N_WAVE) - {return -1;} - mr = 0; - nn = n - 1; - /* decimation in time - re-order data */ - for (m=1; m<=nn; ++m) { - l = n; - do - {l >>= 1;} - while (mr+l > nn); - mr = (mr & (l-1)) + l; - if (mr <= m) - {continue;} - // real = 2*m, imag = 2*m+1 - tr = iq[2*m]; - iq[2*m] = iq[2*mr]; - iq[2*mr] = tr; - ti = iq[2*m+1]; - iq[2*m+1] = iq[2*mr+1]; - iq[2*mr+1] = ti; - } - l = 1; - k = LOG2_N_WAVE-1; - while (l < n) { - shift = 1; - istep = l << 1; - for (m=0; m<l; ++m) { - j = m << k; - wr = Sinewave[j+N_WAVE/4]; - wi = -Sinewave[j]; - if (shift) { - wr >>= 1; wi >>= 1;} - for (i=m; i<n; i+=istep) { - j = i + l; - tr = FIX_MPY(wr,iq[2*j]) - FIX_MPY(wi,iq[2*j+1]); - ti = FIX_MPY(wr,iq[2*j+1]) + FIX_MPY(wi,iq[2*j]); - qr = iq[2*i]; - qi = iq[2*i+1]; - if (shift) { - qr >>= 1; qi >>= 1;} - iq[2*j] = qr - tr; - iq[2*j+1] = qi - ti; - iq[2*i] = qr + tr; - iq[2*i+1] = qi + ti; - } - } - --k; - l = istep; - } - return 0; -} - -double rectangle(int i, int length) -{ - return 1.0; -} - -double hamming(int i, int length) -{ - double a, b, w, N1; - a = 25.0/46.0; - b = 21.0/46.0; - N1 = (double)(length-1); - w = a - b*cos(2*i*M_PI/N1); - return w; -} - -double blackman(int i, int length) -{ - double a0, a1, a2, w, N1; - a0 = 7938.0/18608.0; - a1 = 9240.0/18608.0; - a2 = 1430.0/18608.0; - N1 = (double)(length-1); - w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1); - return w; -} - -double blackman_harris(int i, int length) -{ - double a0, a1, a2, a3, w, N1; - a0 = 0.35875; - a1 = 0.48829; - a2 = 0.14128; - a3 = 0.01168; - N1 = (double)(length-1); - w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1); - return w; -} - -double hann_poisson(int i, int length) -{ - double a, N1, w; - a = 2.0; - N1 = (double)(length-1); - w = 0.5 * (1 - cos(2*M_PI*i/N1)) * \ - pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1); - return w; -} - -double youssef(int i, int length) -/* really a blackman-harris-poisson window, but that is a mouthful */ -{ - double a, a0, a1, a2, a3, w, N1; - a0 = 0.35875; - a1 = 0.48829; - a2 = 0.14128; - a3 = 0.01168; - N1 = (double)(length-1); - w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1); - a = 0.0025; - w *= pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1); - return w; -} - -double kaiser(int i, int length) -// todo, become more smart -{ - return 1.0; -} - -double bartlett(int i, int length) -{ - double N1, L, w; - L = (double)length; - N1 = L - 1; - w = (i - N1/2) / (L/2); - if (w < 0) { - w = -w;} - w = 1 - w; - return w; -} - -void rms_power(struct tuning_state *ts) -/* for bins between 1MHz and 2MHz */ -{ - int i, s; - uint8_t *buf = ts->buf8; - int buf_len = ts->buf_len; - long p, t; - double dc, err; - - p = t = 0L; - for (i=0; i<buf_len; i++) { - s = (int)buf[i] - 127; - t += (long)s; - p += (long)(s * s); - } - /* correct for dc offset in squares */ - dc = (double)t / (double)buf_len; - err = t * 2 * dc - dc * dc * buf_len; - p -= (long)round(err); - - if (!peak_hold) { - ts->avg[0] += p; - } else { - ts->avg[0] = MAX(ts->avg[0], p); - } - ts->samples += 1; -} - -void frequency_range(char *arg, double crop) -/* flesh out the tunes[] for scanning */ -// do we want the fewest ranges (easy) or the fewest bins (harder)? -{ - char *start, *stop, *step; - int i, j, upper, lower, max_size, bw_seen, bw_used, bin_e, buf_len; - int downsample, downsample_passes; - double bin_size; - struct tuning_state *ts; - /* hacky string parsing */ - start = arg; - stop = strchr(start, ':') + 1; - if (stop == (char *)1) { - fprintf(stderr, "Bad frequency range specification: %s\n", arg); - exit(1); - } - stop[-1] = '\0'; - step = strchr(stop, ':') + 1; - if (step == (char *)1) { - fprintf(stderr, "Bad frequency range specification: %s\n", arg); - exit(1); - } - step[-1] = '\0'; - lower = (int)atofs(start); - upper = (int)atofs(stop); - max_size = (int)atofs(step); - stop[-1] = ':'; - step[-1] = ':'; - downsample = 1; - downsample_passes = 0; - /* evenly sized ranges, as close to MAXIMUM_RATE as possible */ - // todo, replace loop with algebra - for (i=1; i<1500; i++) { - bw_seen = (upper - lower) / i; - bw_used = (int)((double)(bw_seen) / (1.0 - crop)); - if (bw_used > MAXIMUM_RATE) { - continue;} - tune_count = i; - break; - } - /* unless small bandwidth */ - if (bw_used < MINIMUM_RATE) { - tune_count = 1; - downsample = MAXIMUM_RATE / bw_used; - bw_used = bw_used * downsample; - } - if (!boxcar && downsample > 1) { - downsample_passes = (int)log2(downsample); - downsample = 1 << downsample_passes; - bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop)); - } - /* number of bins is power-of-two, bin size is under limit */ - // todo, replace loop with log2 - for (i=1; i<=21; i++) { - bin_e = i; - bin_size = (double)bw_used / (double)((1<<i) * downsample); - if (bin_size <= (double)max_size) { - break;} - } - /* unless giant bins */ - if (max_size >= MINIMUM_RATE) { - bw_seen = max_size; - bw_used = max_size; - tune_count = (upper - lower) / bw_seen; - bin_e = 0; - crop = 0; - } - if (tune_count > MAX_TUNES) { - fprintf(stderr, "Error: bandwidth too wide.\n"); - exit(1); - } - buf_len = 2 * (1<<bin_e) * downsample; - if (buf_len < DEFAULT_BUF_LENGTH) { - buf_len = DEFAULT_BUF_LENGTH; - } - /* build the array */ - for (i=0; i<tune_count; i++) { - ts = &tunes[i]; - ts->freq = lower + i*bw_seen + bw_seen/2; - ts->rate = bw_used; - ts->bin_e = bin_e; - ts->samples = 0; - ts->crop = crop; - ts->downsample = downsample; - ts->downsample_passes = downsample_passes; - ts->avg = (long*)malloc((1<<bin_e) * sizeof(long)); - if (!ts->avg) { - fprintf(stderr, "Error: malloc.\n"); - exit(1); - } - for (j=0; j<(1<<bin_e); j++) { - ts->avg[j] = 0L; - } - ts->buf8 = (uint8_t*)malloc(buf_len * sizeof(uint8_t)); - if (!ts->buf8) { - fprintf(stderr, "Error: malloc.\n"); - exit(1); - } - ts->buf_len = buf_len; - } - /* report */ - fprintf(stderr, "Number of frequency hops: %i\n", tune_count); - fprintf(stderr, "Dongle bandwidth: %iHz\n", bw_used); - fprintf(stderr, "Downsampling by: %ix\n", downsample); - fprintf(stderr, "Cropping by: %0.2f%%\n", crop*100); - fprintf(stderr, "Total FFT bins: %i\n", tune_count * (1<<bin_e)); - fprintf(stderr, "Logged FFT bins: %i\n", \ - (int)((double)(tune_count * (1<<bin_e)) * (1.0-crop))); - fprintf(stderr, "FFT bin size: %0.2fHz\n", bin_size); - fprintf(stderr, "Buffer size: %i bytes (%0.2fms)\n", buf_len, 1000 * 0.5 * (float)buf_len / (float)bw_used); -} - -void retune(rtlsdr_dev_t *d, int freq) -{ - uint8_t dump[BUFFER_DUMP]; - int n_read; - rtlsdr_set_center_freq(d, (uint32_t)freq); - /* wait for settling and flush buffer */ - usleep(5000); - rtlsdr_read_sync(d, &dump, BUFFER_DUMP, &n_read); - if (n_read != BUFFER_DUMP) { - fprintf(stderr, "Error: bad retune.\n");} -} - -void fifth_order(int16_t *data, int length) -/* for half of interleaved data */ -{ - int i; - int a, b, c, d, e, f; - a = data[0]; - b = data[2]; - c = data[4]; - d = data[6]; - e = data[8]; - f = data[10]; - /* a downsample should improve resolution, so don't fully shift */ - /* ease in instead of being stateful */ - data[0] = ((a+b)*10 + (c+d)*5 + d + f) >> 4; - data[2] = ((b+c)*10 + (a+d)*5 + e + f) >> 4; - data[4] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; - for (i=12; i<length; i+=4) { - a = c; - b = d; - c = e; - d = f; - e = data[i-2]; - f = data[i]; - data[i/2] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; - } -} - -void remove_dc(int16_t *data, int length) -/* works on interleaved data */ -{ - int i; - int16_t ave; - long sum = 0L; - for (i=0; i < length; i+=2) { - sum += data[i]; - } - ave = (int16_t)(sum / (long)(length)); - if (ave == 0) { - return;} - for (i=0; i < length; i+=2) { - data[i] -= ave; - } -} - -void generic_fir(int16_t *data, int length, int *fir) -/* Okay, not at all generic. Assumes length 9, fix that eventually. */ -{ - int d, 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) -{ - fifth_order(data, length); - //remove_dc(data, length); - fifth_order(data+1, length-1); - //remove_dc(data+1, length-1); -} - -long real_conj(int16_t real, int16_t imag) -/* real(n * conj(n)) */ -{ - return ((long)real*(long)real + (long)imag*(long)imag); -} - -void scanner(void) -{ - int i, j, j2, f, n_read, offset, bin_e, bin_len, buf_len, ds, ds_p; - int32_t w; - struct tuning_state *ts; - bin_e = tunes[0].bin_e; - bin_len = 1 << bin_e; - buf_len = tunes[0].buf_len; - for (i=0; i<tune_count; i++) { - if (do_exit >= 2) - {return;} - ts = &tunes[i]; - f = (int)rtlsdr_get_center_freq(dev); - if (f != ts->freq) { - retune(dev, ts->freq);} - rtlsdr_read_sync(dev, ts->buf8, buf_len, &n_read); - if (n_read != buf_len) { - fprintf(stderr, "Error: dropped samples.\n");} - /* rms */ - if (bin_len == 1) { - rms_power(ts); - continue; - } - /* prep for fft */ - for (j=0; j<buf_len; j++) { - fft_buf[j] = (int16_t)ts->buf8[j] - 127; - } - ds = ts->downsample; - ds_p = ts->downsample_passes; - if (boxcar && ds > 1) { - j=2, j2=0; - while (j < buf_len) { - fft_buf[j2] += fft_buf[j]; - fft_buf[j2+1] += fft_buf[j+1]; - fft_buf[j] = 0; - fft_buf[j+1] = 0; - j += 2; - if (j % (ds*2) == 0) { - j2 += 2;} - } - } else if (ds_p) { /* recursive */ - for (j=0; j < ds_p; 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 / ds); - remove_dc(fft_buf+1, (buf_len / ds) - 1); - /* window function and fft */ - for (offset=0; offset<(buf_len/ds); offset+=(2*bin_len)) { - // todo, let rect skip this - for (j=0; j<bin_len; j++) { - w = (int32_t)fft_buf[offset+j*2]; - w *= (int32_t)(window_coefs[j]); - //w /= (int32_t)(ds); - fft_buf[offset+j*2] = (int16_t)w; - w = (int32_t)fft_buf[offset+j*2+1]; - w *= (int32_t)(window_coefs[j]); - //w /= (int32_t)(ds); - fft_buf[offset+j*2+1] = (int16_t)w; - } - fix_fft(fft_buf+offset, bin_e); - if (!peak_hold) { - for (j=0; j<bin_len; j++) { - ts->avg[j] += real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]); - } - } else { - for (j=0; j<bin_len; j++) { - ts->avg[j] = MAX(real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]), ts->avg[j]); - } - } - ts->samples += ds; - } - } -} - -void csv_dbm(struct tuning_state *ts) -{ - int i, len, ds, i1, i2, bw2, bin_count; - long tmp; - double dbm; - len = 1 << ts->bin_e; - ds = ts->downsample; - /* fix FFT stuff quirks */ - if (ts->bin_e > 0) { - /* nuke DC component (not effective for all windows) */ - ts->avg[0] = ts->avg[1]; - /* FFT is translated by 180 degrees */ - for (i=0; i<len/2; i++) { - tmp = ts->avg[i]; - ts->avg[i] = ts->avg[i+len/2]; - ts->avg[i+len/2] = tmp; - } - } - /* Hz low, Hz high, Hz step, samples, dbm, dbm, ... */ - bin_count = (int)((double)len * (1.0 - ts->crop)); - bw2 = (int)(((double)ts->rate * (double)bin_count) / (len * 2 * ds)); - fprintf(file, "%i, %i, %.2f, %i, ", ts->freq - bw2, ts->freq + bw2, - (double)ts->rate / (double)(len*ds), ts->samples); - // something seems off with the dbm math - i1 = 0 + (int)((double)len * ts->crop * 0.5); - i2 = (len-1) - (int)((double)len * ts->crop * 0.5); - for (i=i1; i<=i2; i++) { - dbm = (double)ts->avg[i]; - dbm /= (double)ts->rate; - dbm /= (double)ts->samples; - dbm = 10 * log10(dbm); - fprintf(file, "%.2f, ", dbm); - } - dbm = (double)ts->avg[i2] / ((double)ts->rate * (double)ts->samples); - if (ts->bin_e == 0) { - dbm = ((double)ts->avg[0] / \ - ((double)ts->rate * (double)ts->samples));} - dbm = 10 * log10(dbm); - fprintf(file, "%.2f\n", dbm); - for (i=0; i<len; i++) { - ts->avg[i] = 0L; - } - ts->samples = 0; -} - -int main(int argc, char **argv) -{ -#ifndef _WIN32 - struct sigaction sigact; -#endif - char *filename = NULL; - int i, length, r, opt, wb_mode = 0; - int f_set = 0; - int gain = AUTO_GAIN; // tenths of a dB - int dev_index = 0; - int dev_given = 0; - int ppm_error = 0; - int interval = 10; - int fft_threads = 1; - int smoothing = 0; - int single = 0; - int direct_sampling = 0; - int offset_tuning = 0; - int enable_biastee = 0; - double crop = 0.0; - char *freq_optarg; - time_t next_tick; - time_t time_now; - time_t exit_time = 0; - char t_str[50]; - struct tm *cal_time; - double (*window_fn)(int, int) = rectangle; - freq_optarg = ""; - - while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1PDOhT")) != -1) { - switch (opt) { - case 'f': // lower:upper:bin_size - freq_optarg = strdup(optarg); - f_set = 1; - break; - case 'd': - dev_index = verbose_device_search(optarg); - dev_given = 1; - break; - case 'g': - gain = (int)(atof(optarg) * 10); - break; - case 'c': - crop = atofp(optarg); - break; - case 'i': - interval = (int)round(atoft(optarg)); - break; - case 'e': - exit_time = (time_t)((int)round(atoft(optarg))); - break; - case 's': - if (strcmp("avg", optarg) == 0) { - smoothing = 0;} - if (strcmp("iir", optarg) == 0) { - smoothing = 1;} - break; - case 'w': - if (strcmp("rectangle", optarg) == 0) { - window_fn = rectangle;} - if (strcmp("hamming", optarg) == 0) { - window_fn = hamming;} - if (strcmp("blackman", optarg) == 0) { - window_fn = blackman;} - if (strcmp("blackman-harris", optarg) == 0) { - window_fn = blackman_harris;} - if (strcmp("hann-poisson", optarg) == 0) { - window_fn = hann_poisson;} - if (strcmp("youssef", optarg) == 0) { - window_fn = youssef;} - if (strcmp("kaiser", optarg) == 0) { - window_fn = kaiser;} - if (strcmp("bartlett", optarg) == 0) { - window_fn = bartlett;} - break; - case 't': - fft_threads = atoi(optarg); - break; - case 'p': - ppm_error = atoi(optarg); - break; - case '1': - single = 1; - break; - case 'P': - peak_hold = 1; - break; - case 'D': - direct_sampling = 1; - break; - case 'O': - offset_tuning = 1; - break; - case 'F': - boxcar = 0; - comp_fir_size = atoi(optarg); - break; - case 'T': - enable_biastee = 1; - break; - case 'h': - default: - usage(); - break; - } - } - - if (!f_set) { - fprintf(stderr, "No frequency range provided.\n"); - exit(1); - } - - if ((crop < 0.0) || (crop > 1.0)) { - fprintf(stderr, "Crop value outside of 0 to 1.\n"); - exit(1); - } - - frequency_range(freq_optarg, crop); - - if (tune_count == 0) { - usage();} - - if (argc <= optind) { - filename = "-"; - } else { - filename = argv[optind]; - } - - if (interval < 1) { - interval = 1;} - - fprintf(stderr, "Reporting every %i seconds\n", interval); - - if (!dev_given) { - dev_index = verbose_device_search("0"); - } - - if (dev_index < 0) { - exit(1); - } - - r = rtlsdr_open(&dev, (uint32_t)dev_index); - if (r < 0) { - fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); - exit(1); - } -#ifndef _WIN32 - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigact, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - if (direct_sampling) { - verbose_direct_sampling(dev, 1); - } - - if (offset_tuning) { - verbose_offset_tuning(dev); - } - - /* Set the tuner gain */ - if (gain == AUTO_GAIN) { - verbose_auto_gain(dev); - } else { - gain = nearest_gain(dev, gain); - verbose_gain_set(dev, gain); - } - - verbose_ppm_set(dev, ppm_error); - - rtlsdr_set_bias_tee(dev, enable_biastee); - if (enable_biastee) - fprintf(stderr, "activated bias-T on GPIO PIN 0\n"); - - if (strcmp(filename, "-") == 0) { /* Write log to stdout */ - file = stdout; -#ifdef _WIN32 - // Is this necessary? Output is ascii. - _setmode(_fileno(file), _O_BINARY); -#endif - } else { - file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Failed to open %s\n", filename); - exit(1); - } - } - - /* Reset endpoint before we start reading from it (mandatory) */ - verbose_reset_buffer(dev); - - /* actually do stuff */ - rtlsdr_set_sample_rate(dev, (uint32_t)tunes[0].rate); - sine_table(tunes[0].bin_e); - next_tick = time(NULL) + interval; - if (exit_time) { - exit_time = time(NULL) + exit_time;} - fft_buf = malloc(tunes[0].buf_len * sizeof(int16_t)); - length = 1 << tunes[0].bin_e; - window_coefs = malloc(length * sizeof(int)); - for (i=0; i<length; i++) { - window_coefs[i] = (int)(256*window_fn(i, length)); - } - while (!do_exit) { - scanner(); - time_now = time(NULL); - if (time_now < next_tick) { - continue;} - // time, Hz low, Hz high, Hz step, samples, dbm, dbm, ... - cal_time = localtime(&time_now); - strftime(t_str, 50, "%Y-%m-%d, %H:%M:%S", cal_time); - for (i=0; i<tune_count; i++) { - fprintf(file, "%s, ", t_str); - csv_dbm(&tunes[i]); - } - fflush(file); - while (time(NULL) >= next_tick) { - next_tick += interval;} - if (single) { - do_exit = 1;} - if (exit_time && time(NULL) >= exit_time) { - do_exit = 1;} - } - - /* clean up */ - - if (do_exit) { - fprintf(stderr, "\nUser cancel, exiting...\n");} - else { - fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} - - if (file != stdout) { - fclose(file);} - - rtlsdr_close(dev); - free(fft_buf); - free(window_coefs); - //for (i=0; i<tune_count; i++) { - // free(tunes[i].avg); - // free(tunes[i].buf8); - //} - return r >= 0 ? r : -r; -} - -// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab |