diff --git a/configure.ac b/configure.ac index 7bc8e1d..1b94701 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,9 @@ AC_CHECK_LIB(pthread, pthread_create, [LIBS="$LIBS -lpthread"]) dnl libmath (for rtl_fm) AC_CHECK_LIB(m, atan2, [LIBS="$LIBS -lm"]) +dnl librealtime (for rtl_test) +AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS"]) + # The following test is taken from WebKit's webkit.m4 saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden " diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index baeb96f..5b078a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,11 @@ target_link_libraries(rtl_fm rtlsdr_shared ${CMAKE_THREAD_LIBS_INIT} m ) +if(APPLE) + target_link_libraries(rtl_test m) +else() + target_link_libraries(rtl_test m rt) +endif() endif() if(WIN32) diff --git a/src/Makefile.am b/src/Makefile.am index 45b91d0..4e2aa6d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ rtl_tcp_SOURCES = rtl_tcp.c rtl_tcp_LDADD = librtlsdr.la rtl_test_SOURCES = rtl_test.c -rtl_test_LDADD = librtlsdr.la +rtl_test_LDADD = librtlsdr.la $(LIBM) rtl_fm_SOURCES = rtl_fm.c rtl_fm_LDADD = librtlsdr.la $(LIBM) diff --git a/src/rtl_test.c b/src/rtl_test.c index acee6f4..3840f59 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -21,6 +21,13 @@ #include #include #include +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif #ifndef _WIN32 #include @@ -39,9 +46,25 @@ #define MHZ(x) ((x)*1000*1000) +#define PPM_DURATION 10 + static int do_exit = 0; static rtlsdr_dev_t *dev = NULL; +static int ppm_benchmark = 0; +static int64_t ppm_count = 0L; +static int64_t ppm_total = 0L; + +#ifndef _WIN32 +static struct timespec ppm_start; +static struct timespec ppm_recent; +static struct timespec ppm_now; +#endif + +#ifdef __APPLE__ +static struct timeval tv; +#endif + void usage(void) { fprintf(stderr, @@ -50,6 +73,9 @@ void usage(void) "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-d device_index (default: 0)]\n" "\t[-t enable Elonics E4000 tuner benchmark]\n" + #ifndef _WIN32 + "\t[-p enable PPM error measurement]\n" + #endif "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-S force sync output (default: async)]\n"); exit(1); @@ -81,6 +107,8 @@ uint8_t bcnt, uninit = 1; static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { uint32_t i, lost = 0; + int64_t ns; + double perf_sec; if (uninit) { bcnt = buf[0]; @@ -98,6 +126,35 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) if (lost) printf("lost at least %d bytes\n", lost); + + if (!ppm_benchmark) { + return; + } + ppm_count += (int64_t)len; +#ifndef _WIN32 + #ifndef __APPLE__ + clock_gettime(CLOCK_REALTIME, &ppm_now); + #else + gettimeofday(&tv, NULL); + ppm_now.tv_sec = tv.tv_sec; + ppm_now.tv_nsec = tv.tv_usec*1000; + #endif + if (ppm_now.tv_sec - ppm_recent.tv_sec > PPM_DURATION) { + ns = 1000000000L * (int64_t)(ppm_now.tv_sec - ppm_recent.tv_sec); + ns += (int64_t)(ppm_now.tv_nsec - ppm_recent.tv_nsec); + printf("real sample rate: %i\n", + (int)((1000000000L * ppm_count / 2L) / ns)); + #ifndef __APPLE__ + clock_gettime(CLOCK_REALTIME, &ppm_recent); + #else + gettimeofday(&tv, NULL); + ppm_recent.tv_sec = tv.tv_sec; + ppm_recent.tv_nsec = tv.tv_usec*1000; + #endif + ppm_total += ppm_count / 2L; + ppm_count = 0L; + } +#endif } void e4k_benchmark(void) @@ -162,8 +219,11 @@ int main(int argc, char **argv) int device_count; int count; int gains[100]; + int real_rate; + int64_t ns; + double perf_sec; - while ((opt = getopt(argc, argv, "d:s:b:tS::")) != -1) { + while ((opt = getopt(argc, argv, "d:s:b:tpS::")) != -1) { switch (opt) { case 'd': dev_index = atoi(optarg); @@ -177,6 +237,9 @@ int main(int argc, char **argv) case 't': tuner_benchmark = 1; break; + case 'p': + ppm_benchmark = PPM_DURATION; + break; case 'S': sync_mode = 1; break; @@ -260,6 +323,21 @@ int main(int argc, char **argv) if (r < 0) fprintf(stderr, "WARNING: Failed to reset buffers.\n"); + if (ppm_benchmark && !sync_mode) { + fprintf(stderr, "Reporting PPM error measurement every %i seconds...\n", ppm_benchmark); + fprintf(stderr, "Press ^C after a few minutes.\n"); +#ifdef __APPLE__ + gettimeofday(&tv, NULL); + ppm_recent.tv_sec = tv.tv_sec; + ppm_recent.tv_nsec = tv.tv_usec*1000; + ppm_start.tv_sec = tv.tv_sec; + ppm_start.tv_nsec = tv.tv_usec*1000; +#elif __unix__ + clock_gettime(CLOCK_REALTIME, &ppm_recent); + clock_gettime(CLOCK_REALTIME, &ppm_start); +#endif + } + if (sync_mode) { fprintf(stderr, "Reading samples in sync mode...\n"); while (!do_exit) { @@ -280,8 +358,18 @@ int main(int argc, char **argv) DEFAULT_ASYNC_BUF_NUMBER, out_block_size); } - if (do_exit) + if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n"); + if (ppm_benchmark) { +#ifndef _WIN32 + ns = 1000000000L * (int64_t)(ppm_recent.tv_sec - ppm_start.tv_sec); + ns += (int64_t)(ppm_recent.tv_nsec - ppm_start.tv_nsec); + real_rate = (int)(ppm_total * 1000000000L / ns); + printf("Cumulative PPM error: %i\n", + (int)round((double)(1000000 * (real_rate - (int)samp_rate)) / (double)samp_rate)); +#endif + } + } else fprintf(stderr, "\nLibrary error %d, exiting...\n", r);