Forked from git://github.com/merbanan/rtl_433.git
[rtl-433.git] / src / rtl_test.c
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  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25
26 #ifdef __APPLE__
27 #include <sys/time.h>
28 #else
29 #include <time.h>
30 #endif
31
32 #ifndef _WIN32
33 #include <unistd.h>
34 #else
35 #include <Windows.h>
36 #include "getopt/getopt.h"
37 #endif
38
39 #include "rtl-sdr.h"
40
41 #define DEFAULT_SAMPLE_RATE             2048000
42 #define DEFAULT_ASYNC_BUF_NUMBER        32
43 #define DEFAULT_BUF_LENGTH              (16 * 16384)
44 #define MINIMAL_BUF_LENGTH              512
45 #define MAXIMAL_BUF_LENGTH              (256 * 16384)
46
47 #define MHZ(x)  ((x)*1000*1000)
48
49 #define PPM_DURATION                    10
50
51 static int do_exit = 0;
52 static rtlsdr_dev_t *dev = NULL;
53
54 static int ppm_benchmark = 0;
55 static int64_t ppm_count = 0L;
56 static int64_t ppm_total = 0L;
57
58 #ifndef _WIN32
59 static struct timespec ppm_start;
60 static struct timespec ppm_recent;
61 static struct timespec ppm_now;
62 #endif
63
64 #ifdef __APPLE__
65 static struct timeval tv;
66 #endif
67
68 void usage(void)
69 {
70         fprintf(stderr,
71                 "rtl_test, a benchmark tool for RTL2832 based DVB-T receivers\n\n"
72                 "Usage:\n"
73                 "\t[-s samplerate (default: 2048000 Hz)]\n"
74                 "\t[-d device_index (default: 0)]\n"
75                 "\t[-t enable Elonics E4000 tuner benchmark]\n"
76                 #ifndef _WIN32
77                 "\t[-p enable PPM error measurement]\n"
78                 #endif
79                 "\t[-b output_block_size (default: 16 * 16384)]\n"
80                 "\t[-S force sync output (default: async)]\n");
81         exit(1);
82 }
83
84 #ifdef _WIN32
85 BOOL WINAPI
86 sighandler(int signum)
87 {
88         if (CTRL_C_EVENT == signum) {
89                 fprintf(stderr, "Signal caught, exiting!\n");
90                 do_exit = 1;
91                 rtlsdr_cancel_async(dev);
92                 return TRUE;
93         }
94         return FALSE;
95 }
96 #else
97 static void sighandler(int signum)
98 {
99         fprintf(stderr, "Signal caught, exiting!\n");
100         do_exit = 1;
101         rtlsdr_cancel_async(dev);
102 }
103 #endif
104
105 uint8_t bcnt, uninit = 1;
106
107 static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
108 {
109         uint32_t i, lost = 0;
110         int64_t ns;
111
112         if (uninit) {
113                 bcnt = buf[0];
114                 uninit = 0;
115         }
116
117         for (i = 0; i < len; i++) {
118                 if(bcnt != buf[i]) {
119                         lost += (buf[i] > bcnt) ? (buf[i] - bcnt) : (bcnt - buf[i]);
120                         bcnt = buf[i];
121                 }
122
123                 bcnt++;
124         }
125
126         if (lost)
127                 printf("lost at least %d bytes\n", lost);
128
129         if (!ppm_benchmark) {
130                 return;
131         }
132         ppm_count += (int64_t)len;
133 #ifndef _WIN32
134         #ifndef __APPLE__
135         clock_gettime(CLOCK_REALTIME, &ppm_now);
136         #else
137         gettimeofday(&tv, NULL);
138         ppm_now.tv_sec = tv.tv_sec;
139         ppm_now.tv_nsec = tv.tv_usec*1000;
140         #endif
141         if (ppm_now.tv_sec - ppm_recent.tv_sec > PPM_DURATION) {
142                 ns = 1000000000L * (int64_t)(ppm_now.tv_sec - ppm_recent.tv_sec);
143                 ns += (int64_t)(ppm_now.tv_nsec - ppm_recent.tv_nsec);
144                 printf("real sample rate: %i\n",
145                 (int)((1000000000L * ppm_count / 2L) / ns));
146                 #ifndef __APPLE__
147                 clock_gettime(CLOCK_REALTIME, &ppm_recent);
148                 #else
149                 gettimeofday(&tv, NULL);
150                 ppm_recent.tv_sec = tv.tv_sec;
151                 ppm_recent.tv_nsec = tv.tv_usec*1000;
152                 #endif
153                 ppm_total += ppm_count / 2L;
154                 ppm_count = 0L;
155         }
156 #endif
157 }
158
159 void e4k_benchmark(void)
160 {
161         uint32_t freq, gap_start = 0, gap_end = 0;
162         uint32_t range_start = 0, range_end = 0;
163
164         fprintf(stderr, "Benchmarking E4000 PLL...\n");
165
166         /* find tuner range start */
167         for (freq = MHZ(70); freq > MHZ(1); freq -= MHZ(1)) {
168                 if (rtlsdr_set_center_freq(dev, freq) < 0) {
169                         range_start = freq;
170                         break;
171                 }
172         }
173
174         /* find tuner range end */
175         for (freq = MHZ(2000); freq < MHZ(2300UL); freq += MHZ(1)) {
176                 if (rtlsdr_set_center_freq(dev, freq) < 0) {
177                         range_end = freq;
178                         break;
179                 }
180         }
181
182         /* find start of L-band gap */
183         for (freq = MHZ(1000); freq < MHZ(1300); freq += MHZ(1)) {
184                 if (rtlsdr_set_center_freq(dev, freq) < 0) {
185                         gap_start = freq;
186                         break;
187                 }
188         }
189
190         /* find end of L-band gap */
191         for (freq = MHZ(1300); freq > MHZ(1000); freq -= MHZ(1)) {
192                 if (rtlsdr_set_center_freq(dev, freq) < 0) {
193                         gap_end = freq;
194                         break;
195                 }
196         }
197
198         fprintf(stderr, "E4K range: %i to %i MHz\n",
199                 range_start/MHZ(1) + 1, range_end/MHZ(1) - 1);
200
201         fprintf(stderr, "E4K L-band gap: %i to %i MHz\n",
202                 gap_start/MHZ(1), gap_end/MHZ(1));
203 }
204
205 int main(int argc, char **argv)
206 {
207 #ifndef _WIN32
208         struct sigaction sigact;
209 #endif
210         int n_read;
211         int r, opt;
212         int i, tuner_benchmark = 0;
213         int sync_mode = 0;
214         uint8_t *buffer;
215         uint32_t dev_index = 0;
216         uint32_t samp_rate = DEFAULT_SAMPLE_RATE;
217         uint32_t out_block_size = DEFAULT_BUF_LENGTH;
218         int device_count;
219         int count;
220         int gains[100];
221         int real_rate;
222         int64_t ns;
223
224         while ((opt = getopt(argc, argv, "d:s:b:tpS::")) != -1) {
225                 switch (opt) {
226                 case 'd':
227                         dev_index = atoi(optarg);
228                         break;
229                 case 's':
230                         samp_rate = (uint32_t)atof(optarg);
231                         break;
232                 case 'b':
233                         out_block_size = (uint32_t)atof(optarg);
234                         break;
235                 case 't':
236                         tuner_benchmark = 1;
237                         break;
238                 case 'p':
239                         ppm_benchmark = PPM_DURATION;
240                         break;
241                 case 'S':
242                         sync_mode = 1;
243                         break;
244                 default:
245                         usage();
246                         break;
247                 }
248         }
249
250         if(out_block_size < MINIMAL_BUF_LENGTH ||
251            out_block_size > MAXIMAL_BUF_LENGTH ){
252                 fprintf(stderr,
253                         "Output block size wrong value, falling back to default\n");
254                 fprintf(stderr,
255                         "Minimal length: %u\n", MINIMAL_BUF_LENGTH);
256                 fprintf(stderr,
257                         "Maximal length: %u\n", MAXIMAL_BUF_LENGTH);
258                 out_block_size = DEFAULT_BUF_LENGTH;
259         }
260
261         buffer = malloc(out_block_size * sizeof(uint8_t));
262
263         device_count = rtlsdr_get_device_count();
264         if (!device_count) {
265                 fprintf(stderr, "No supported devices found.\n");
266                 exit(1);
267         }
268
269         fprintf(stderr, "Found %d device(s):\n", device_count);
270         for (i = 0; i < device_count; i++)
271                 fprintf(stderr, "  %d:  %s\n", i, rtlsdr_get_device_name(i));
272         fprintf(stderr, "\n");
273
274         fprintf(stderr, "Using device %d: %s\n",
275                 dev_index,
276                 rtlsdr_get_device_name(dev_index));
277
278         r = rtlsdr_open(&dev, dev_index);
279         if (r < 0) {
280                 fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
281                 exit(1);
282         }
283 #ifndef _WIN32
284         sigact.sa_handler = sighandler;
285         sigemptyset(&sigact.sa_mask);
286         sigact.sa_flags = 0;
287         sigaction(SIGINT, &sigact, NULL);
288         sigaction(SIGTERM, &sigact, NULL);
289         sigaction(SIGQUIT, &sigact, NULL);
290         sigaction(SIGPIPE, &sigact, NULL);
291 #else
292         SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
293 #endif
294         count = rtlsdr_get_tuner_gains(dev, NULL);
295         fprintf(stderr, "Supported gain values (%d): ", count);
296
297         count = rtlsdr_get_tuner_gains(dev, gains);
298         for (i = 0; i < count; i++)
299                 fprintf(stderr, "%.1f ", gains[i] / 10.0);
300         fprintf(stderr, "\n");
301
302         /* Set the sample rate */
303         r = rtlsdr_set_sample_rate(dev, samp_rate);
304         if (r < 0)
305                 fprintf(stderr, "WARNING: Failed to set sample rate.\n");
306
307         if (tuner_benchmark) {
308                 if (rtlsdr_get_tuner_type(dev) == RTLSDR_TUNER_E4000)
309                         e4k_benchmark();
310                 else
311                         fprintf(stderr, "No E4000 tuner found, aborting.\n");
312
313                 goto exit;
314         }
315
316         /* Enable test mode */
317         r = rtlsdr_set_testmode(dev, 1);
318
319         /* Reset endpoint before we start reading from it (mandatory) */
320         r = rtlsdr_reset_buffer(dev);
321         if (r < 0)
322                 fprintf(stderr, "WARNING: Failed to reset buffers.\n");
323
324         if (ppm_benchmark && !sync_mode) {
325                 fprintf(stderr, "Reporting PPM error measurement every %i seconds...\n", ppm_benchmark);
326                 fprintf(stderr, "Press ^C after a few minutes.\n");
327 #ifdef __APPLE__
328                 gettimeofday(&tv, NULL);
329                 ppm_recent.tv_sec = tv.tv_sec;
330                 ppm_recent.tv_nsec = tv.tv_usec*1000;
331                 ppm_start.tv_sec = tv.tv_sec;
332                 ppm_start.tv_nsec = tv.tv_usec*1000;
333 #elif __unix__
334                 clock_gettime(CLOCK_REALTIME, &ppm_recent);
335                 clock_gettime(CLOCK_REALTIME, &ppm_start);
336 #endif
337         }
338
339         if (sync_mode) {
340                 fprintf(stderr, "Reading samples in sync mode...\n");
341                 while (!do_exit) {
342                         r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read);
343                         if (r < 0) {
344                                 fprintf(stderr, "WARNING: sync read failed.\n");
345                                 break;
346                         }
347
348                         if ((uint32_t)n_read < out_block_size) {
349                                 fprintf(stderr, "Short read, samples lost, exiting!\n");
350                                 break;
351                         }
352                 }
353         } else {
354                 fprintf(stderr, "Reading samples in async mode...\n");
355                 r = rtlsdr_read_async(dev, rtlsdr_callback, NULL,
356                                       DEFAULT_ASYNC_BUF_NUMBER, out_block_size);
357         }
358
359         if (do_exit) {
360                 fprintf(stderr, "\nUser cancel, exiting...\n");
361                 if (ppm_benchmark) {
362 #ifndef _WIN32
363                         ns = 1000000000L * (int64_t)(ppm_recent.tv_sec - ppm_start.tv_sec);
364                         ns += (int64_t)(ppm_recent.tv_nsec - ppm_start.tv_nsec);
365                         real_rate = (int)(ppm_total * 1000000000L / ns);
366                         printf("Cumulative PPM error: %i\n",
367                         (int)round((double)(1000000 * (real_rate - (int)samp_rate)) / (double)samp_rate));
368 #endif
369                 }
370         }
371         else
372                 fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
373
374 exit:
375         rtlsdr_close(dev);
376         free (buffer);
377
378         return r >= 0 ? r : -r;
379 }