rtl_power.c 24 KB

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