123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872 |
- /*
- * 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
- * fft bins smaller than 61Hz
- * bandwidths smaller than 1MHz
- * check edge cropping for off-by-one and rounding errors
- */
- #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)
- #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5))
- #define _USE_MATH_DEFINES
- #endif
- #include <math.h>
- #include <pthread.h>
- #include <libusb.h>
- #include "rtl-sdr.h"
- #define DEFAULT_SAMPLE_RATE 24000
- #define DEFAULT_ASYNC_BUF_NUMBER 32
- #define DEFAULT_BUF_LENGTH (1 * 16384)
- #define MAXIMUM_OVERSAMPLE 16
- #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH)
- #define AUTO_GAIN -100
- #define BUFFER_DUMP (1<<12)
- 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;
- long mega_samples;
- //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;
- //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;
- 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 61-2M)\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"
- "\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 in rms bin mode)\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"
- "Convert CSV to a waterfall graphic with\n"
- "\thttp://kmkeen.com/tmp/heatmap.py.txt\n"
- "");
- exit(1);
- }
- #ifdef _WIN32
- BOOL WINAPI
- sighandler(int signum)
- {
- if (CTRL_C_EVENT == signum) {
- fprintf(stderr, "Signal caught, exiting!\n");
- do_exit = 1;
- return TRUE;
- }
- return FALSE;
- }
- #else
- static void sighandler(int signum)
- {
- fprintf(stderr, "Signal caught, exiting!\n");
- do_exit = 1;
- }
- #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)
- /* 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]);
- }
- }
- 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[], int16_t 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;
- int ln, lp;
- 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);
- ts->avg[0] += p;
- ts->samples += 1;
- /* complex pairs, half length */
- ts->mega_samples += (long)(buf_len/2);
- }
- double atofs(char *f)
- /* standard suffixes */
- {
- char last;
- int len;
- double suff = 1.0;
- len = strlen(f);
- last = f[len-1];
- f[len-1] = '\0';
- switch (last) {
- case 'g':
- case 'G':
- suff *= 1e3;
- case 'm':
- case 'M':
- suff *= 1e3;
- case 'k':
- case 'K':
- suff *= 1e3;
- suff *= atof(f);
- f[len-1] = last;
- return suff;
- }
- f[len-1] = last;
- return atof(f);
- }
- double atoft(char *f)
- /* time suffixes */
- {
- char last;
- int len;
- double suff = 1.0;
- len = strlen(f);
- last = f[len-1];
- f[len-1] = '\0';
- switch (last) {
- case 'h':
- case 'H':
- suff *= 60;
- case 'm':
- case 'M':
- suff *= 60;
- case 's':
- case 'S':
- suff *= atof(f);
- f[len-1] = last;
- return suff;
- }
- f[len-1] = last;
- return atof(f);
- }
- double atofp(char *f)
- /* percent suffixes */
- {
- char last;
- int len;
- double suff = 1.0;
- len = strlen(f);
- last = f[len-1];
- f[len-1] = '\0';
- switch (last) {
- case '%':
- suff *= 0.01;
- suff *= atof(f);
- f[len-1] = last;
- return suff;
- }
- f[len-1] = last;
- return atof(f);
- }
- int nearest_gain(int target_gain)
- {
- int i, err1, err2, count, close_gain;
- int* gains;
- count = rtlsdr_get_tuner_gains(dev, NULL);
- if (count <= 0) {
- return 0;
- }
- gains = malloc(sizeof(int) * count);
- count = rtlsdr_get_tuner_gains(dev, gains);
- close_gain = gains[0];
- for (i=0; i<count; i++) {
- err1 = abs(target_gain - close_gain);
- err2 = abs(target_gain - gains[i]);
- if (err2 < err1) {
- close_gain = gains[i];
- }
- }
- free(gains);
- return close_gain;
- }
- 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_size, bin_e, buf_len;
- struct tuning_state *ts;
- /* hacky string parsing */
- start = arg;
- stop = strchr(start, ':') + 1;
- stop[-1] = '\0';
- step = strchr(stop, ':') + 1;
- step[-1] = '\0';
- lower = (int)atofs(start);
- upper = (int)atofs(stop);
- max_size = (int)atofs(step);
- stop[-1] = ':';
- step[-1] = ':';
- /* evenly sized ranges, as close to 2MHz as possible */
- for (i=1; i<1500; i++) {
- bw_seen = (upper - lower) / i;
- bw_used = (int)((double)(bw_seen) / (1.0 - crop));
- if (bw_used > 2000000) {
- continue;}
- tune_count = i;
- break;
- }
- /* number of bins is power-of-two, bin size is under limit */
- for (i=1; i<=21; i++) {
- bin_e = i;
- bin_size = bw_used / (1<<i);
- if (bin_size <= max_size) {
- break;}
- }
- /* unless giant bins */
- if (max_size >= 1000000) {
- bw_seen = max_size;
- bw_used = max_size;
- tune_count = (upper - lower) / bw_seen;
- bin_e = 0;
- }
- if (tune_count > MAX_TUNES) {
- fprintf(stderr, "Error: bandwidth too wide.\n");
- exit(1);
- }
- buf_len = DEFAULT_BUF_LENGTH;
- if ((2<<bin_e) > buf_len) {
- buf_len = (2<<bin_e);
- }
- /* 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->mega_samples = 0L;
- 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, "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: %iHz\n", bin_size);
- fprintf(stderr, "Buffer size: %0.2fms\n", 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 scanner(void)
- {
- int i, j, f, n_read, offset, bin_e, bin_len, buf_len;
- 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) {
- break;}
- 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;
- }
- /* fft */
- for (j=0; j<buf_len; j++) {
- fft_buf[j] = (int16_t)ts->buf8[j] - 127;
- }
- for (offset=0; offset<buf_len; offset+=(2*bin_len)) {
- // todo, let rect skip this
- for (j=0; j<bin_len; j++) {
- fft_buf[offset+j*2] *= window_coefs[j];
- fft_buf[offset+j*2+1] *= window_coefs[j];
- }
- fix_fft(fft_buf+offset, bin_e);
- for (j=0; j<bin_len; j++) {
- ts->avg[j] += (long) abs(fft_buf[offset+j*2]);
- }
- ts->samples += 1;
- }
- }
- }
- void csv_dbm(struct tuning_state *ts, double crop)
- {
- int i, len, i1, i2, bw2;
- long tmp;
- double dbm;
- len = 1 << ts->bin_e;
- /* 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, ... */
- bw2 = (int)((double)ts->rate * (1.0-crop) * 0.5);
- fprintf(file, "%i, %i, %.2f, %i, ", ts->freq - bw2, ts->freq + bw2,
- (double)ts->rate / (double)len, ts->samples);
- // something seems off with the dbm math
- i1 = 0 + (int)((double)len * crop * 0.5);
- i2 = (len-1) - (int)((double)len * 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;
- ts->mega_samples = 0L;
- }
- int main(int argc, char **argv)
- {
- #ifndef _WIN32
- struct sigaction sigact;
- #endif
- char *filename = NULL;
- int i, length, n_read, r, opt, wb_mode = 0;
- int gain = AUTO_GAIN; // tenths of a dB
- uint8_t *buffer;
- uint32_t dev_index = 0;
- int device_count;
- int ppm_error = 0;
- int interval = 10;
- int fft_threads = 1;
- int smoothing = 0;
- int single = 0;
- double crop = 0.1;
- char vendor[256], product[256], serial[256];
- 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;
- while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:1h")) != -1) {
- switch (opt) {
- case 'f': // lower:upper:bin_size
- freq_optarg = strdup(optarg);
- break;
- case 'd':
- dev_index = atoi(optarg);
- 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 'h':
- default:
- usage();
- break;
- }
- }
- 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);
- device_count = rtlsdr_get_device_count();
- if (!device_count) {
- fprintf(stderr, "No supported devices found.\n");
- exit(1);
- }
- fprintf(stderr, "Found %d device(s):\n", device_count);
- for (i = 0; i < device_count; i++) {
- rtlsdr_get_device_usb_strings(i, vendor, product, serial);
- fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
- }
- fprintf(stderr, "\n");
- fprintf(stderr, "Using device %d: %s\n",
- dev_index, rtlsdr_get_device_name(dev_index));
- r = rtlsdr_open(&dev, 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
- /* Set the tuner gain */
- if (gain == AUTO_GAIN) {
- r = rtlsdr_set_tuner_gain_mode(dev, 0);
- } else {
- r = rtlsdr_set_tuner_gain_mode(dev, 1);
- gain = nearest_gain(gain);
- r = rtlsdr_set_tuner_gain(dev, gain);
- }
- if (r != 0) {
- fprintf(stderr, "WARNING: Failed to set tuner gain.\n");
- } else if (gain == AUTO_GAIN) {
- fprintf(stderr, "Tuner gain set to automatic.\n");
- } else {
- fprintf(stderr, "Tuner gain set to %0.2f dB.\n", gain/10.0);
- }
- r = rtlsdr_set_freq_correction(dev, ppm_error);
- if (strcmp(filename, "-") == 0) { /* Write log to stdout */
- file = stdout;
- #ifdef _WIN32
- _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) */
- r = rtlsdr_reset_buffer(dev);
- if (r < 0) {
- fprintf(stderr, "WARNING: Failed to reset buffers.\n");}
- /* 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], crop);
- }
- 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
|