rtl_power.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. /*
  2. * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
  3. * Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
  4. * Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
  5. * Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /*
  21. * rtl_power: general purpose FFT integrator
  22. * -f low_freq:high_freq:max_bin_size
  23. * -i seconds
  24. * outputs CSV
  25. * time, low, high, step, db, db, db ...
  26. * db optional? raw output might be better for noise correction
  27. * todo:
  28. * threading
  29. * randomized hopping
  30. * noise correction
  31. * continuous IIR
  32. * general astronomy usefulness
  33. * multiple dongles
  34. * multiple FFT workers
  35. * check edge cropping for off-by-one and rounding errors
  36. * 1.8MS/s for hiding xtal harmonics
  37. */
  38. #include <errno.h>
  39. #include <signal.h>
  40. #include <string.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <time.h>
  44. #ifndef _WIN32
  45. #include <unistd.h>
  46. #else
  47. #include <windows.h>
  48. #include <fcntl.h>
  49. #include <io.h>
  50. #include "getopt/getopt.h"
  51. #define usleep(x) Sleep(x/1000)
  52. #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5))
  53. #define _USE_MATH_DEFINES
  54. #endif
  55. #include <math.h>
  56. #include <pthread.h>
  57. #include <libusb.h>
  58. #include "rtl-sdr.h"
  59. #define DEFAULT_BUF_LENGTH (1 * 16384)
  60. #define AUTO_GAIN -100
  61. #define BUFFER_DUMP (1<<12)
  62. static volatile int do_exit = 0;
  63. static rtlsdr_dev_t *dev = NULL;
  64. FILE *file;
  65. int16_t* Sinewave;
  66. double* power_table;
  67. int N_WAVE, LOG2_N_WAVE;
  68. int next_power;
  69. int16_t *fft_buf;
  70. int *window_coefs;
  71. struct tuning_state
  72. /* one per tuning range */
  73. {
  74. int freq;
  75. int rate;
  76. int bin_e;
  77. long *avg; /* length == 2^bin_e */
  78. int samples;
  79. int downsample;
  80. int downsample_passes; /* for the recursive filter */
  81. double crop;
  82. //pthread_rwlock_t avg_lock;
  83. //pthread_mutex_t avg_mutex;
  84. /* having the iq buffer here is wasteful, but will avoid contention */
  85. uint8_t *buf8;
  86. int buf_len;
  87. //pthread_rwlock_t buf_lock;
  88. //pthread_mutex_t buf_mutex;
  89. };
  90. /* 3000 is enough for 3GHz b/w worst case */
  91. #define MAX_TUNES 3000
  92. struct tuning_state tunes[MAX_TUNES];
  93. int tune_count = 0;
  94. int boxcar = 1;
  95. void usage(void)
  96. {
  97. fprintf(stderr,
  98. "rtl_power, a simple FFT logger for RTL2832 based DVB-T receivers\n\n"
  99. "Use:\trtl_power -f freq_range [-options] [filename]\n"
  100. "\t-f lower:upper:bin_size [Hz]\n"
  101. "\t (bin size is a maximum, smaller more convenient bins\n"
  102. "\t will be used. valid range 1Hz - 2MHz)\n"
  103. "\t[-i integration_interval (default: 10 seconds)]\n"
  104. "\t (buggy if a full sweep takes longer than the interval)\n"
  105. "\t[-1 enables single-shot mode (default: off)]\n"
  106. "\t[-e exit_timer (default: off/0)]\n"
  107. //"\t[-s avg/iir smoothing (default: avg)]\n"
  108. //"\t[-t threads (default: 1)]\n"
  109. "\t[-d device_index (default: 0)]\n"
  110. "\t[-g tuner_gain (default: automatic)]\n"
  111. "\t[-p ppm_error (default: 0)]\n"
  112. "\tfilename (a '-' dumps samples to stdout)\n"
  113. "\t (omitting the filename also uses stdout)\n"
  114. "\n"
  115. "Experimental options:\n"
  116. "\t[-w window (default: rectangle)]\n"
  117. "\t (hamming, blackman, blackman-harris, hann-poisson, bartlett, youssef)\n"
  118. // kaiser
  119. "\t[-c crop_percent (default: 0%%, recommended: 20%%-50%%)]\n"
  120. "\t (discards data at the edges, 100%% discards everything)\n"
  121. "\t (has no effect for bins larger than 1MHz)\n"
  122. "\t[-F enables low-leakage downsample filter (default: off)]\n"
  123. "\t (has bad roll off, try with '-c 50%%')\n"
  124. "\n"
  125. "CSV FFT output columns:\n"
  126. "\tdate, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...\n\n"
  127. "Examples:\n"
  128. "\trtl_power -f 88M:108M:125k fm_stations.csv\n"
  129. "\t (creates 160 bins across the FM band,\n"
  130. "\t individual stations should be visible)\n"
  131. "\trtl_power -f 100M:1G:1M -i 5m -1 survey.csv\n"
  132. "\t (a five minute low res scan of nearly everything)\n"
  133. "\trtl_power -f ... -i 15m -1 log.csv\n"
  134. "\t (integrate for 15 minutes and exit afterwards)\n"
  135. "\trtl_power -f ... -e 1h | gzip > log.csv.gz\n"
  136. "\t (collect data for one hour and compress it on the fly)\n\n"
  137. "Convert CSV to a waterfall graphic with:\n"
  138. "\t http://kmkeen.com/tmp/heatmap.py.txt \n");
  139. exit(1);
  140. }
  141. void multi_bail(void)
  142. {
  143. if (do_exit == 1)
  144. {
  145. fprintf(stderr, "Signal caught, finishing scan pass.\n");
  146. }
  147. if (do_exit >= 2)
  148. {
  149. fprintf(stderr, "Signal caught, aborting immediately.\n");
  150. }
  151. }
  152. #ifdef _WIN32
  153. BOOL WINAPI
  154. sighandler(int signum)
  155. {
  156. if (CTRL_C_EVENT == signum) {
  157. do_exit++;
  158. multi_bail();
  159. return TRUE;
  160. }
  161. return FALSE;
  162. }
  163. #else
  164. static void sighandler(int signum)
  165. {
  166. do_exit++;
  167. multi_bail();
  168. }
  169. #endif
  170. /* more cond dumbness */
  171. #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m)
  172. #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m)
  173. /* FFT based on fix_fft.c by Roberts, Slaney and Bouras
  174. http://www.jjj.de/fft/fftpage.html
  175. 16 bit ints for everything
  176. -32768..+32768 maps to -1.0..+1.0
  177. */
  178. void sine_table(int size)
  179. {
  180. int i;
  181. double d;
  182. LOG2_N_WAVE = size;
  183. N_WAVE = 1 << LOG2_N_WAVE;
  184. Sinewave = malloc(sizeof(int16_t) * N_WAVE*3/4);
  185. power_table = malloc(sizeof(double) * N_WAVE);
  186. for (i=0; i<N_WAVE*3/4; i++)
  187. {
  188. d = (double)i * 2.0 * M_PI / N_WAVE;
  189. Sinewave[i] = (int)round(32767*sin(d));
  190. //printf("%i\n", Sinewave[i]);
  191. }
  192. }
  193. inline int16_t FIX_MPY(int16_t a, int16_t b)
  194. /* fixed point multiply and scale */
  195. {
  196. int c = ((int)a * (int)b) >> 14;
  197. b = c & 0x01;
  198. return (c >> 1) + b;
  199. }
  200. int fix_fft(int16_t iq[], int m)
  201. /* interleaved iq[], 0 <= n < 2**m, changes in place */
  202. {
  203. int mr, nn, i, j, l, k, istep, n, shift;
  204. int16_t qr, qi, tr, ti, wr, wi;
  205. n = 1 << m;
  206. if (n > N_WAVE)
  207. {return -1;}
  208. mr = 0;
  209. nn = n - 1;
  210. /* decimation in time - re-order data */
  211. for (m=1; m<=nn; ++m) {
  212. l = n;
  213. do
  214. {l >>= 1;}
  215. while (mr+l > nn);
  216. mr = (mr & (l-1)) + l;
  217. if (mr <= m)
  218. {continue;}
  219. // real = 2*m, imag = 2*m+1
  220. tr = iq[2*m];
  221. iq[2*m] = iq[2*mr];
  222. iq[2*mr] = tr;
  223. ti = iq[2*m+1];
  224. iq[2*m+1] = iq[2*mr+1];
  225. iq[2*mr+1] = ti;
  226. }
  227. l = 1;
  228. k = LOG2_N_WAVE-1;
  229. while (l < n) {
  230. shift = 1;
  231. istep = l << 1;
  232. for (m=0; m<l; ++m) {
  233. j = m << k;
  234. wr = Sinewave[j+N_WAVE/4];
  235. wi = -Sinewave[j];
  236. if (shift) {
  237. wr >>= 1; wi >>= 1;}
  238. for (i=m; i<n; i+=istep) {
  239. j = i + l;
  240. tr = FIX_MPY(wr,iq[2*j]) - FIX_MPY(wi,iq[2*j+1]);
  241. ti = FIX_MPY(wr,iq[2*j+1]) + FIX_MPY(wi,iq[2*j]);
  242. qr = iq[2*i];
  243. qi = iq[2*i+1];
  244. if (shift) {
  245. qr >>= 1; qi >>= 1;}
  246. iq[2*j] = qr - tr;
  247. iq[2*j+1] = qi - ti;
  248. iq[2*i] = qr + tr;
  249. iq[2*i+1] = qi + ti;
  250. }
  251. }
  252. --k;
  253. l = istep;
  254. }
  255. return 0;
  256. }
  257. double rectangle(int i, int length)
  258. {
  259. return 1.0;
  260. }
  261. double hamming(int i, int length)
  262. {
  263. double a, b, w, N1;
  264. a = 25.0/46.0;
  265. b = 21.0/46.0;
  266. N1 = (double)(length-1);
  267. w = a - b*cos(2*i*M_PI/N1);
  268. return w;
  269. }
  270. double blackman(int i, int length)
  271. {
  272. double a0, a1, a2, w, N1;
  273. a0 = 7938.0/18608.0;
  274. a1 = 9240.0/18608.0;
  275. a2 = 1430.0/18608.0;
  276. N1 = (double)(length-1);
  277. w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1);
  278. return w;
  279. }
  280. double blackman_harris(int i, int length)
  281. {
  282. double a0, a1, a2, a3, w, N1;
  283. a0 = 0.35875;
  284. a1 = 0.48829;
  285. a2 = 0.14128;
  286. a3 = 0.01168;
  287. N1 = (double)(length-1);
  288. w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1);
  289. return w;
  290. }
  291. double hann_poisson(int i, int length)
  292. {
  293. double a, N1, w;
  294. a = 2.0;
  295. N1 = (double)(length-1);
  296. w = 0.5 * (1 - cos(2*M_PI*i/N1)) * \
  297. pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1);
  298. return w;
  299. }
  300. double youssef(int i, int length)
  301. /* really a blackman-harris-poisson window, but that is a mouthful */
  302. {
  303. double a, a0, a1, a2, a3, w, N1;
  304. a0 = 0.35875;
  305. a1 = 0.48829;
  306. a2 = 0.14128;
  307. a3 = 0.01168;
  308. N1 = (double)(length-1);
  309. w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1);
  310. a = 0.0025;
  311. w *= pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1);
  312. return w;
  313. }
  314. double kaiser(int i, int length)
  315. // todo, become more smart
  316. {
  317. return 1.0;
  318. }
  319. double bartlett(int i, int length)
  320. {
  321. double N1, L, w;
  322. L = (double)length;
  323. N1 = L - 1;
  324. w = (i - N1/2) / (L/2);
  325. if (w < 0) {
  326. w = -w;}
  327. w = 1 - w;
  328. return w;
  329. }
  330. void rms_power(struct tuning_state *ts)
  331. /* for bins between 1MHz and 2MHz */
  332. {
  333. int i, s;
  334. uint8_t *buf = ts->buf8;
  335. int buf_len = ts->buf_len;
  336. long p, t;
  337. int ln, lp;
  338. double dc, err;
  339. p = t = 0L;
  340. for (i=0; i<buf_len; i++) {
  341. s = (int)buf[i] - 127;
  342. t += (long)s;
  343. p += (long)(s * s);
  344. }
  345. /* correct for dc offset in squares */
  346. dc = (double)t / (double)buf_len;
  347. err = t * 2 * dc - dc * dc * buf_len;
  348. p -= (long)round(err);
  349. ts->avg[0] += p;
  350. ts->samples += 1;
  351. }
  352. double atofs(char *f)
  353. /* standard suffixes */
  354. {
  355. char last;
  356. int len;
  357. double suff = 1.0;
  358. len = strlen(f);
  359. last = f[len-1];
  360. f[len-1] = '\0';
  361. switch (last) {
  362. case 'g':
  363. case 'G':
  364. suff *= 1e3;
  365. case 'm':
  366. case 'M':
  367. suff *= 1e3;
  368. case 'k':
  369. case 'K':
  370. suff *= 1e3;
  371. suff *= atof(f);
  372. f[len-1] = last;
  373. return suff;
  374. }
  375. f[len-1] = last;
  376. return atof(f);
  377. }
  378. double atoft(char *f)
  379. /* time suffixes */
  380. {
  381. char last;
  382. int len;
  383. double suff = 1.0;
  384. len = strlen(f);
  385. last = f[len-1];
  386. f[len-1] = '\0';
  387. switch (last) {
  388. case 'h':
  389. case 'H':
  390. suff *= 60;
  391. case 'm':
  392. case 'M':
  393. suff *= 60;
  394. case 's':
  395. case 'S':
  396. suff *= atof(f);
  397. f[len-1] = last;
  398. return suff;
  399. }
  400. f[len-1] = last;
  401. return atof(f);
  402. }
  403. double atofp(char *f)
  404. /* percent suffixes */
  405. {
  406. char last;
  407. int len;
  408. double suff = 1.0;
  409. len = strlen(f);
  410. last = f[len-1];
  411. f[len-1] = '\0';
  412. switch (last) {
  413. case '%':
  414. suff *= 0.01;
  415. suff *= atof(f);
  416. f[len-1] = last;
  417. return suff;
  418. }
  419. f[len-1] = last;
  420. return atof(f);
  421. }
  422. int nearest_gain(int target_gain)
  423. {
  424. int i, err1, err2, count, close_gain;
  425. int* gains;
  426. count = rtlsdr_get_tuner_gains(dev, NULL);
  427. if (count <= 0) {
  428. return 0;
  429. }
  430. gains = malloc(sizeof(int) * count);
  431. count = rtlsdr_get_tuner_gains(dev, gains);
  432. close_gain = gains[0];
  433. for (i=0; i<count; i++) {
  434. err1 = abs(target_gain - close_gain);
  435. err2 = abs(target_gain - gains[i]);
  436. if (err2 < err1) {
  437. close_gain = gains[i];
  438. }
  439. }
  440. free(gains);
  441. return close_gain;
  442. }
  443. void frequency_range(char *arg, double crop)
  444. /* flesh out the tunes[] for scanning */
  445. // do we want the fewest ranges (easy) or the fewest bins (harder)?
  446. {
  447. char *start, *stop, *step;
  448. int i, j, upper, lower, max_size, bw_seen, bw_used, bin_e, buf_len;
  449. int downsample, downsample_passes;
  450. double bin_size;
  451. struct tuning_state *ts;
  452. /* hacky string parsing */
  453. start = arg;
  454. stop = strchr(start, ':') + 1;
  455. stop[-1] = '\0';
  456. step = strchr(stop, ':') + 1;
  457. step[-1] = '\0';
  458. lower = (int)atofs(start);
  459. upper = (int)atofs(stop);
  460. max_size = (int)atofs(step);
  461. stop[-1] = ':';
  462. step[-1] = ':';
  463. downsample = 1;
  464. downsample_passes = 0;
  465. /* evenly sized ranges, as close to 2MHz as possible */
  466. for (i=1; i<1500; i++) {
  467. bw_seen = (upper - lower) / i;
  468. bw_used = (int)((double)(bw_seen) / (1.0 - crop));
  469. if (bw_used > 2000000) {
  470. continue;}
  471. tune_count = i;
  472. break;
  473. }
  474. /* unless small bandwidth */
  475. if (bw_used < 1000000) {
  476. tune_count = 1;}
  477. if (boxcar && bw_used < 1000000) {
  478. downsample = 2000000 / bw_used;
  479. bw_used = bw_used * downsample;
  480. }
  481. while (bw_used < 1000000) { /* not boxcar */
  482. downsample_passes++;
  483. downsample = 1 << downsample_passes;
  484. bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop));
  485. }
  486. /* number of bins is power-of-two, bin size is under limit */
  487. for (i=1; i<=21; i++) {
  488. bin_e = i;
  489. bin_size = (double)bw_used / (double)((1<<i) * downsample);
  490. if (bin_size <= (double)max_size) {
  491. break;}
  492. }
  493. /* unless giant bins */
  494. if (max_size >= 1000000) {
  495. bw_seen = max_size;
  496. bw_used = max_size;
  497. tune_count = (upper - lower) / bw_seen;
  498. bin_e = 0;
  499. crop = 0;
  500. }
  501. if (tune_count > MAX_TUNES) {
  502. fprintf(stderr, "Error: bandwidth too wide.\n");
  503. exit(1);
  504. }
  505. buf_len = 2 * (1<<bin_e) * downsample;
  506. if (buf_len < DEFAULT_BUF_LENGTH) {
  507. buf_len = DEFAULT_BUF_LENGTH;
  508. }
  509. /* build the array */
  510. for (i=0; i<tune_count; i++) {
  511. ts = &tunes[i];
  512. ts->freq = lower + i*bw_seen + bw_seen/2;
  513. ts->rate = bw_used;
  514. ts->bin_e = bin_e;
  515. ts->samples = 0;
  516. ts->crop = crop;
  517. ts->downsample = downsample;
  518. ts->downsample_passes = downsample_passes;
  519. ts->avg = (long*)malloc((1<<bin_e) * sizeof(long));
  520. if (!ts->avg) {
  521. fprintf(stderr, "Error: malloc.\n");
  522. exit(1);
  523. }
  524. for (j=0; j<(1<<bin_e); j++) {
  525. ts->avg[j] = 0L;
  526. }
  527. ts->buf8 = (uint8_t*)malloc(buf_len * sizeof(uint8_t));
  528. if (!ts->buf8) {
  529. fprintf(stderr, "Error: malloc.\n");
  530. exit(1);
  531. }
  532. ts->buf_len = buf_len;
  533. }
  534. /* report */
  535. fprintf(stderr, "Number of frequency hops: %i\n", tune_count);
  536. fprintf(stderr, "Dongle bandwidth: %iHz\n", bw_used);
  537. fprintf(stderr, "Downsampling by: %ix\n", downsample);
  538. fprintf(stderr, "Cropping by: %0.2f%%\n", crop*100);
  539. fprintf(stderr, "Total FFT bins: %i\n", tune_count * (1<<bin_e));
  540. fprintf(stderr, "Logged FFT bins: %i\n", \
  541. (int)((double)(tune_count * (1<<bin_e)) * (1.0-crop)));
  542. fprintf(stderr, "FFT bin size: %0.2fHz\n", bin_size);
  543. fprintf(stderr, "Buffer size: %i bytes (%0.2fms)\n", buf_len, 1000 * 0.5 * (float)buf_len / (float)bw_used);
  544. }
  545. void retune(rtlsdr_dev_t *d, int freq)
  546. {
  547. uint8_t dump[BUFFER_DUMP];
  548. int n_read;
  549. rtlsdr_set_center_freq(d, (uint32_t)freq);
  550. /* wait for settling and flush buffer */
  551. usleep(5000);
  552. rtlsdr_read_sync(d, &dump, BUFFER_DUMP, &n_read);
  553. if (n_read != BUFFER_DUMP) {
  554. fprintf(stderr, "Error: bad retune.\n");}
  555. }
  556. void fifth_order(int16_t *data, int length)
  557. /* for half of interleaved data */
  558. {
  559. int i;
  560. int a, b, c, d, e, f;
  561. a = data[0];
  562. b = data[2];
  563. c = data[4];
  564. d = data[6];
  565. e = data[8];
  566. f = data[10];
  567. /* a downsample should improve resolution, so don't fully shift */
  568. /* ease in instead of being stateful */
  569. data[0] = ((a+b)*10 + (c+d)*5 + d + f) >> 4;
  570. data[2] = ((b+c)*10 + (a+d)*5 + e + f) >> 4;
  571. data[4] = (a + (b+e)*5 + (c+d)*10 + f) >> 4;
  572. for (i=12; i<length; i+=4) {
  573. a = c;
  574. b = d;
  575. c = e;
  576. d = f;
  577. e = data[i-2];
  578. f = data[i];
  579. data[i/2] = (a + (b+e)*5 + (c+d)*10 + f) >> 4;
  580. }
  581. }
  582. void remove_dc(int16_t *data, int length)
  583. /* works on interleaved data */
  584. {
  585. int i;
  586. int16_t ave;
  587. long sum = 0L;
  588. for (i=0; i < length; i+=2) {
  589. sum += data[i];
  590. }
  591. ave = (int16_t)(sum / (long)(length));
  592. if (ave == 0) {
  593. return;}
  594. for (i=0; i < length; i+=2) {
  595. data[i] -= ave;
  596. }
  597. }
  598. void downsample_iq(int16_t *data, int length)
  599. {
  600. fifth_order(data, length);
  601. //remove_dc(data, length);
  602. fifth_order(data+1, length-1);
  603. //remove_dc(data+1, length-1);
  604. }
  605. void scanner(void)
  606. {
  607. int i, j, j2, f, n_read, offset, bin_e, bin_len, buf_len, ds;
  608. int32_t w;
  609. struct tuning_state *ts;
  610. bin_e = tunes[0].bin_e;
  611. bin_len = 1 << bin_e;
  612. buf_len = tunes[0].buf_len;
  613. for (i=0; i<tune_count; i++) {
  614. if (do_exit >= 2)
  615. {return;}
  616. ts = &tunes[i];
  617. f = (int)rtlsdr_get_center_freq(dev);
  618. if (f != ts->freq) {
  619. retune(dev, ts->freq);}
  620. rtlsdr_read_sync(dev, ts->buf8, buf_len, &n_read);
  621. if (n_read != buf_len) {
  622. fprintf(stderr, "Error: dropped samples.\n");}
  623. /* rms */
  624. if (bin_len == 1) {
  625. rms_power(ts);
  626. continue;
  627. }
  628. /* prep for fft */
  629. for (j=0; j<buf_len; j++) {
  630. fft_buf[j] = (int16_t)ts->buf8[j] - 127;
  631. }
  632. ds = ts->downsample;
  633. if (boxcar) {
  634. j=2, j2=0;
  635. while (j < buf_len) {
  636. fft_buf[j2] += fft_buf[j];
  637. fft_buf[j2+1] += fft_buf[j+1];
  638. j += 2;
  639. if (j % (ds*2) == 0) {
  640. j2 += 2;}
  641. }
  642. } else { /* recursive */
  643. for (j=0; j < ts->downsample_passes; j++) {
  644. downsample_iq(fft_buf, buf_len >> j);
  645. }
  646. }
  647. remove_dc(fft_buf, buf_len >> j);
  648. remove_dc(fft_buf+1, (buf_len >> j) - 1);
  649. /* window function and fft */
  650. for (offset=0; offset<(buf_len/ds); offset+=(2*bin_len)) {
  651. // todo, let rect skip this
  652. for (j=0; j<bin_len; j++) {
  653. w = (int32_t)fft_buf[offset+j*2];
  654. w *= (int32_t)(window_coefs[j]);
  655. //w /= (int32_t)(ds);
  656. fft_buf[offset+j*2] = (int16_t)w;
  657. w = (int32_t)fft_buf[offset+j*2+1];
  658. w *= (int32_t)(window_coefs[j]);
  659. //w /= (int32_t)(ds);
  660. fft_buf[offset+j*2+1] = (int16_t)w;
  661. }
  662. fix_fft(fft_buf+offset, bin_e);
  663. for (j=0; j<bin_len; j++) {
  664. ts->avg[j] += (long) abs(fft_buf[offset+j*2]);
  665. }
  666. ts->samples += ds;
  667. }
  668. }
  669. }
  670. void csv_dbm(struct tuning_state *ts)
  671. {
  672. int i, len, ds, i1, i2, bw2, bin_count;
  673. long tmp;
  674. double dbm;
  675. len = 1 << ts->bin_e;
  676. ds = ts->downsample;
  677. /* fix FFT stuff quirks */
  678. if (ts->bin_e > 0) {
  679. /* nuke DC component (not effective for all windows) */
  680. ts->avg[0] = ts->avg[1];
  681. /* FFT is translated by 180 degrees */
  682. for (i=0; i<len/2; i++) {
  683. tmp = ts->avg[i];
  684. ts->avg[i] = ts->avg[i+len/2];
  685. ts->avg[i+len/2] = tmp;
  686. }
  687. }
  688. /* Hz low, Hz high, Hz step, samples, dbm, dbm, ... */
  689. bin_count = (int)((double)len * (1.0 - ts->crop));
  690. bw2 = (int)(((double)ts->rate * (double)bin_count) / (len * 2 * ds));
  691. fprintf(file, "%i, %i, %.2f, %i, ", ts->freq - bw2, ts->freq + bw2,
  692. (double)ts->rate / (double)(len*ds), ts->samples);
  693. // something seems off with the dbm math
  694. i1 = 0 + (int)((double)len * ts->crop * 0.5);
  695. i2 = (len-1) - (int)((double)len * ts->crop * 0.5);
  696. for (i=i1; i<=i2; i++) {
  697. dbm = (double)ts->avg[i];
  698. dbm /= (double)ts->rate;
  699. dbm /= (double)ts->samples;
  700. dbm = 10 * log10(dbm);
  701. fprintf(file, "%.2f, ", dbm);
  702. }
  703. dbm = (double)ts->avg[i2] / ((double)ts->rate * (double)ts->samples);
  704. if (ts->bin_e == 0) {
  705. dbm = ((double)ts->avg[0] / \
  706. ((double)ts->rate * (double)ts->samples));}
  707. dbm = 10 * log10(dbm);
  708. fprintf(file, "%.2f\n", dbm);
  709. for (i=0; i<len; i++) {
  710. ts->avg[i] = 0L;
  711. }
  712. ts->samples = 0;
  713. }
  714. int main(int argc, char **argv)
  715. {
  716. #ifndef _WIN32
  717. struct sigaction sigact;
  718. #endif
  719. char *filename = NULL;
  720. int i, length, n_read, r, opt, wb_mode = 0;
  721. int f_set = 0;
  722. int gain = AUTO_GAIN; // tenths of a dB
  723. uint8_t *buffer;
  724. uint32_t dev_index = 0;
  725. int device_count;
  726. int ppm_error = 0;
  727. int interval = 10;
  728. int fft_threads = 1;
  729. int smoothing = 0;
  730. int single = 0;
  731. double crop = 0.0;
  732. char vendor[256], product[256], serial[256];
  733. char *freq_optarg;
  734. time_t next_tick;
  735. time_t time_now;
  736. time_t exit_time = 0;
  737. char t_str[50];
  738. struct tm *cal_time;
  739. double (*window_fn)(int, int) = rectangle;
  740. freq_optarg = "";
  741. while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:1Fh")) != -1) {
  742. switch (opt) {
  743. case 'f': // lower:upper:bin_size
  744. freq_optarg = strdup(optarg);
  745. f_set = 1;
  746. break;
  747. case 'd':
  748. dev_index = atoi(optarg);
  749. break;
  750. case 'g':
  751. gain = (int)(atof(optarg) * 10);
  752. break;
  753. case 'c':
  754. crop = atofp(optarg);
  755. break;
  756. case 'i':
  757. interval = (int)round(atoft(optarg));
  758. break;
  759. case 'e':
  760. exit_time = (time_t)((int)round(atoft(optarg)));
  761. break;
  762. case 's':
  763. if (strcmp("avg", optarg) == 0) {
  764. smoothing = 0;}
  765. if (strcmp("iir", optarg) == 0) {
  766. smoothing = 1;}
  767. break;
  768. case 'w':
  769. if (strcmp("rectangle", optarg) == 0) {
  770. window_fn = rectangle;}
  771. if (strcmp("hamming", optarg) == 0) {
  772. window_fn = hamming;}
  773. if (strcmp("blackman", optarg) == 0) {
  774. window_fn = blackman;}
  775. if (strcmp("blackman-harris", optarg) == 0) {
  776. window_fn = blackman_harris;}
  777. if (strcmp("hann-poisson", optarg) == 0) {
  778. window_fn = hann_poisson;}
  779. if (strcmp("youssef", optarg) == 0) {
  780. window_fn = youssef;}
  781. if (strcmp("kaiser", optarg) == 0) {
  782. window_fn = kaiser;}
  783. if (strcmp("bartlett", optarg) == 0) {
  784. window_fn = bartlett;}
  785. break;
  786. case 't':
  787. fft_threads = atoi(optarg);
  788. break;
  789. case 'p':
  790. ppm_error = atoi(optarg);
  791. break;
  792. case '1':
  793. single = 1;
  794. break;
  795. case 'F':
  796. boxcar = 0;
  797. break;
  798. case 'h':
  799. default:
  800. usage();
  801. break;
  802. }
  803. }
  804. if (!f_set) {
  805. fprintf(stderr, "No frequency range provided.\n");
  806. exit(1);
  807. }
  808. if ((crop < 0.0) || (crop > 1.0)) {
  809. fprintf(stderr, "Crop value outside of 0 to 1.\n");
  810. exit(1);
  811. }
  812. frequency_range(freq_optarg, crop);
  813. if (tune_count == 0) {
  814. usage();}
  815. if (argc <= optind) {
  816. filename = "-";
  817. } else {
  818. filename = argv[optind];
  819. }
  820. if (interval < 1) {
  821. interval = 1;}
  822. fprintf(stderr, "Reporting every %i seconds\n", interval);
  823. device_count = rtlsdr_get_device_count();
  824. if (!device_count) {
  825. fprintf(stderr, "No supported devices found.\n");
  826. exit(1);
  827. }
  828. fprintf(stderr, "Found %d device(s):\n", device_count);
  829. for (i = 0; i < device_count; i++) {
  830. rtlsdr_get_device_usb_strings(i, vendor, product, serial);
  831. fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
  832. }
  833. fprintf(stderr, "\n");
  834. fprintf(stderr, "Using device %d: %s\n",
  835. dev_index, rtlsdr_get_device_name(dev_index));
  836. r = rtlsdr_open(&dev, dev_index);
  837. if (r < 0) {
  838. fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
  839. exit(1);
  840. }
  841. #ifndef _WIN32
  842. sigact.sa_handler = sighandler;
  843. sigemptyset(&sigact.sa_mask);
  844. sigact.sa_flags = 0;
  845. sigaction(SIGINT, &sigact, NULL);
  846. sigaction(SIGTERM, &sigact, NULL);
  847. sigaction(SIGQUIT, &sigact, NULL);
  848. sigaction(SIGPIPE, &sigact, NULL);
  849. #else
  850. SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
  851. #endif
  852. /* Set the tuner gain */
  853. if (gain == AUTO_GAIN) {
  854. r = rtlsdr_set_tuner_gain_mode(dev, 0);
  855. } else {
  856. r = rtlsdr_set_tuner_gain_mode(dev, 1);
  857. gain = nearest_gain(gain);
  858. r = rtlsdr_set_tuner_gain(dev, gain);
  859. }
  860. if (r != 0) {
  861. fprintf(stderr, "WARNING: Failed to set tuner gain.\n");
  862. } else if (gain == AUTO_GAIN) {
  863. fprintf(stderr, "Tuner gain set to automatic.\n");
  864. } else {
  865. fprintf(stderr, "Tuner gain set to %0.2f dB.\n", gain/10.0);
  866. }
  867. r = rtlsdr_set_freq_correction(dev, ppm_error);
  868. if (strcmp(filename, "-") == 0) { /* Write log to stdout */
  869. file = stdout;
  870. #ifdef _WIN32
  871. // Is this necessary? Output is ascii.
  872. _setmode(_fileno(file), _O_BINARY);
  873. #endif
  874. } else {
  875. file = fopen(filename, "wb");
  876. if (!file) {
  877. fprintf(stderr, "Failed to open %s\n", filename);
  878. exit(1);
  879. }
  880. }
  881. /* Reset endpoint before we start reading from it (mandatory) */
  882. r = rtlsdr_reset_buffer(dev);
  883. if (r < 0) {
  884. fprintf(stderr, "WARNING: Failed to reset buffers.\n");}
  885. /* actually do stuff */
  886. rtlsdr_set_sample_rate(dev, (uint32_t)tunes[0].rate);
  887. sine_table(tunes[0].bin_e);
  888. next_tick = time(NULL) + interval;
  889. if (exit_time) {
  890. exit_time = time(NULL) + exit_time;}
  891. fft_buf = malloc(tunes[0].buf_len * sizeof(int16_t));
  892. length = 1 << tunes[0].bin_e;
  893. window_coefs = malloc(length * sizeof(int));
  894. for (i=0; i<length; i++) {
  895. window_coefs[i] = (int)(256*window_fn(i, length));
  896. }
  897. while (!do_exit) {
  898. scanner();
  899. time_now = time(NULL);
  900. if (time_now < next_tick) {
  901. continue;}
  902. // time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...
  903. cal_time = localtime(&time_now);
  904. strftime(t_str, 50, "%Y-%m-%d, %H:%M:%S", cal_time);
  905. for (i=0; i<tune_count; i++) {
  906. fprintf(file, "%s, ", t_str);
  907. csv_dbm(&tunes[i]);
  908. }
  909. fflush(file);
  910. while (time(NULL) >= next_tick) {
  911. next_tick += interval;}
  912. if (single) {
  913. do_exit = 1;}
  914. if (exit_time && time(NULL) >= exit_time) {
  915. do_exit = 1;}
  916. }
  917. /* clean up */
  918. if (do_exit) {
  919. fprintf(stderr, "\nUser cancel, exiting...\n");}
  920. else {
  921. fprintf(stderr, "\nLibrary error %d, exiting...\n", r);}
  922. if (file != stdout) {
  923. fclose(file);}
  924. rtlsdr_close(dev);
  925. free(fft_buf);
  926. free(window_coefs);
  927. //for (i=0; i<tune_count; i++) {
  928. // free(tunes[i].avg);
  929. // free(tunes[i].buf8);
  930. //}
  931. return r >= 0 ? r : -r;
  932. }
  933. // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab