From 284fd4df00d12814decf9a80c39c8eb93a0d1dce Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 25 Jan 2014 12:41:13 -0500 Subject: [PATCH 01/64] driver: tejeez's no-mod direct sampling --- include/tuner_r82xx.h | 1 + src/librtlsdr.c | 32 +++++++++++++++++++++++++++++--- src/tuner_r82xx.c | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index fd81e1d..60f110b 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -115,5 +115,6 @@ int r82xx_standby(struct r82xx_priv *priv); int r82xx_init(struct r82xx_priv *priv); int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); +int r82xx_set_nomod(struct r82xx_priv *priv); #endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 9a3ebcd..135c061 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -1138,13 +1138,36 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) if (!dev) return -1; - if (on) { + /* set up normal direct sampling */ + if (on == 1 || on == 2) { if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); rtlsdr_set_i2c_repeater(dev, 0); } + } + + /* set up no-mod direct sampling */ + if (on == 3 && dev->tuner) { + if (dev->tuner_type == RTLSDR_TUNER_E4000) { + fprintf(stderr, "Tuning E4000 to 3708 MHz\n"); + rtlsdr_set_i2c_repeater(dev, 1); + dev->tuner->init(dev); + dev->tuner->set_freq(dev, 3708000000u); + e4000_set_bw(dev, 15000000); + rtlsdr_set_i2c_repeater(dev, 0); + } + if (dev->tuner_type == RTLSDR_TUNER_R820T) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + rtlsdr_set_i2c_repeater(dev, 1); + dev->tuner->init(dev); + r82xx_set_nomod(&devt->r82xx_p); + rtlsdr_set_i2c_repeater(dev, 0); + } + } + /* common to all direct modes */ + if (on) { /* disable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); @@ -1155,11 +1178,14 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* swap I and Q ADC, this allows to select between two inputs */ - r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on > 1) ? 0x90 : 0x80, 1); + r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on == 2) ? 0x90 : 0x80, 1); fprintf(stderr, "Enabled direct sampling mode, input %i\n", on); dev->direct_sampling = on; - } else { + } + + /* disable direct sampling */ + if (!on) { if (dev->tuner && dev->tuner->init) { rtlsdr_set_i2c_repeater(dev, 1); r |= dev->tuner->init(dev); diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index e03e034..50e740d 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -783,7 +783,20 @@ static int r82xx_set_tv_standard(struct r82xx_priv *priv, flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ polyfil_cur = 0x60; /* r25[6:5]:min */ } else { - if (bw <= 6) { + if (bw < 6) { + /* narrowest bandwidth? */ + if_khz = 3570; + filt_cal_lo = 56000; /* 52000->56000 */ + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else if (bw == 6) { if_khz = 3570; filt_cal_lo = 56000; /* 52000->56000 */ filt_gain = 0x10; /* +3db, 6mhz on */ @@ -1105,6 +1118,32 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) return rc; } +int r82xx_set_nomod(struct r82xx_priv *priv) +{ + int rc = -1; + + fprintf(stderr, "Using R820T no-mod direct sampling mode\n"); + + rc = r82xx_set_tv_standard(priv, 8, TUNER_DIGITAL_TV, 0); + if (rc < 0) + goto err; + + /* experimentally determined magic numbers + * needs more experimenting with all the registers */ + rc = r82xx_set_mux(priv, 300000000); + if (rc < 0) + goto err; + + r82xx_set_pll(priv, 25000000); + +err: + if (rc < 0) + fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); + return rc; +} + + + /* * r82xx standby logic */ From 7d70b00792d581334bc5b02a108f130467497e72 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 25 Jan 2014 12:57:44 -0500 Subject: [PATCH 02/64] utils: tejeez's no-mod direct sampling --- src/convenience/convenience.c | 2 ++ src/rtl_fm.c | 5 ++++- src/rtl_power.c | 8 ++++---- src/rtl_sdr.c | 12 +++++++++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c index 517dc4e..39a049d 100644 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -174,6 +174,8 @@ int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) fprintf(stderr, "Enabled direct sampling mode, input 1/I.\n");} if (on == 2) { fprintf(stderr, "Enabled direct sampling mode, input 2/Q.\n");} + if (on == 3) { + fprintf(stderr, "Enabled no-mod direct sampling mode.\n");} return r; } diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 0f7ac38..b78da02 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -204,6 +204,7 @@ void usage(void) "\t dc: enable dc blocking filter\n" "\t deemp: enable de-emphasis filter\n" "\t direct: enable direct sampling\n" + "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" @@ -891,7 +892,7 @@ static void *controller_thread_fn(void *arg) /* set up primary channel */ optimal_settings(s->freqs[0], demod.rate_in); if (dongle.direct_sampling) { - verbose_direct_sampling(dongle.dev, 1);} + verbose_direct_sampling(dongle.dev, dongle.direct_sampling);} if (dongle.offset_tuning) { verbose_offset_tuning(dongle.dev);} @@ -1104,6 +1105,8 @@ int main(int argc, char **argv) demod.deemph = 1;} if (strcmp("direct", optarg) == 0) { dongle.direct_sampling = 1;} + if (strcmp("no-mod", optarg) == 0) { + dongle.direct_sampling = 3;} if (strcmp("offset", optarg) == 0) { dongle.offset_tuning = 1;} break; diff --git a/src/rtl_power.c b/src/rtl_power.c index 7eb1d06..84865d2 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -148,7 +148,7 @@ void usage(void) "\t fir_size can be 0 or 9. 0 has bad roll off,\n" "\t try with '-c 50%%')\n" "\t[-P enables peak hold (default: off)]\n" - "\t[-D enable direct sampling (default: off)]\n" + "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\t[-O enable offset tuning (default: off)]\n" "\n" "CSV FFT output columns:\n" @@ -781,7 +781,7 @@ int main(int argc, char **argv) double (*window_fn)(int, int) = rectangle; freq_optarg = ""; - while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1PDOh")) != -1) { + while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1PD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size freq_optarg = strdup(optarg); @@ -840,7 +840,7 @@ int main(int argc, char **argv) peak_hold = 1; break; case 'D': - direct_sampling = 1; + direct_sampling = atoi(optarg); break; case 'O': offset_tuning = 1; @@ -908,7 +908,7 @@ int main(int argc, char **argv) #endif if (direct_sampling) { - verbose_direct_sampling(dev, 1); + verbose_direct_sampling(dev, direct_sampling); } if (offset_tuning) { diff --git a/src/rtl_sdr.c b/src/rtl_sdr.c index e6537ca..0e2f681 100644 --- a/src/rtl_sdr.c +++ b/src/rtl_sdr.c @@ -55,6 +55,7 @@ void usage(void) "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-n number of samples to read (default: 0, infinite)]\n" "\t[-S force sync output (default: async)]\n" + "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\tfilename (a '-' dumps samples to stdout)\n\n"); exit(1); } @@ -113,6 +114,7 @@ int main(int argc, char **argv) int gain = 0; int ppm_error = 0; int sync_mode = 0; + int direct_sampling = 0; FILE *file; uint8_t *buffer; int dev_index = 0; @@ -121,7 +123,7 @@ int main(int argc, char **argv) uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; - while ((opt = getopt(argc, argv, "d:f:g:s:b:n:p:S")) != -1) { + while ((opt = getopt(argc, argv, "d:f:g:s:b:n:p:D:S")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); @@ -148,6 +150,9 @@ int main(int argc, char **argv) case 'S': sync_mode = 1; break; + case 'D': + direct_sampling = atoi(optarg); + break; default: usage(); break; @@ -197,6 +202,11 @@ int main(int argc, char **argv) #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif + + if (direct_sampling) { + verbose_direct_sampling(dev, direct_sampling); + } + /* Set the sample rate */ verbose_set_sample_rate(dev, samp_rate); From 389de6d8ed7e6c34e570bbac04c6007b4a19cc34 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 25 Jan 2014 14:06:12 -0500 Subject: [PATCH 03/64] utils: rebase ppm in eeprom prototype --- src/convenience/convenience.c | 30 +++++++++++++++++++++++++++++- src/convenience/convenience.h | 11 ++++++++++- src/rtl_fm.c | 3 +++ src/rtl_power.c | 5 +++++ src/rtl_tcp.c | 5 +++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c index 39a049d..6565e1a 100644 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 by Kyle Keen + * Copyright (C) 2013-2014 by Kyle Keen * * 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 @@ -234,6 +234,34 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error) return r; } +int verbose_ppm_eeprom(rtlsdr_dev_t *dev, int *ppm_error) +{ + #define start_char ' ' + #define stop_char 'p' + int i, r, len, status = -1; + char vendor[256], product[256], serial[256]; + r = rtlsdr_get_usb_strings(dev, vendor, product, serial); + if (r) { + return r; + } + len = strlen(serial); + if (len <= 3) { + return -1;} + if (serial[len-1] != stop_char) { + return -1;} + serial[len-1] = '\0'; + for (i=len-3; i>=0; i--) { + if (serial[i] != start_char) { + continue;} + fprintf(stderr, "PPM calibration found in eeprom.\n"); + status = 0; + *ppm_error = atoi(serial + i + 1); + break; + } + serial[len-1] = stop_char; + return status; +} + int verbose_reset_buffer(rtlsdr_dev_t *dev) { int r; diff --git a/src/convenience/convenience.h b/src/convenience/convenience.h index 1faa2af..2daac78 100644 --- a/src/convenience/convenience.h +++ b/src/convenience/convenience.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 by Kyle Keen + * Copyright (C) 2013-2014 by Kyle Keen * * 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 @@ -122,6 +122,15 @@ int verbose_gain_set(rtlsdr_dev_t *dev, int gain); int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error); +/*! + * Attempts to extract a correction value from eeprom and store it to an int. + * + * \param dev the device handle given by rtlsdr_open() + * \param ppm_error correction value in parts per million (ppm) + * \return 0 on success + */ +int verbose_ppm_eeprom(rtlsdr_dev_t *dev, int *ppm_error); + /*! * Reset buffer * diff --git a/src/rtl_fm.c b/src/rtl_fm.c index b78da02..19279e6 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -1208,6 +1208,9 @@ int main(int argc, char **argv) verbose_gain_set(dongle.dev, dongle.gain); } + if (!custom_ppm) { + verbose_ppm_eeprom(dongle.dev, &(dongle.ppm_error)); + } verbose_ppm_set(dongle.dev, dongle.ppm_error); if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */ diff --git a/src/rtl_power.c b/src/rtl_power.c index 84865d2..7e2286a 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -765,6 +765,7 @@ int main(int argc, char **argv) int dev_index = 0; int dev_given = 0; int ppm_error = 0; + int custom_ppm = 0; int interval = 10; int fft_threads = 1; int smoothing = 0; @@ -832,6 +833,7 @@ int main(int argc, char **argv) break; case 'p': ppm_error = atoi(optarg); + custom_ppm = 1; break; case '1': single = 1; @@ -923,6 +925,9 @@ int main(int argc, char **argv) verbose_gain_set(dev, gain); } + if (!custom_ppm) { + verbose_ppm_eeprom(dev, &ppm_error); + } verbose_ppm_set(dev, ppm_error); if (strcmp(filename, "-") == 0) { /* Write log to stdout */ diff --git a/src/rtl_tcp.c b/src/rtl_tcp.c index 317e0f3..81ed5e6 100644 --- a/src/rtl_tcp.c +++ b/src/rtl_tcp.c @@ -374,6 +374,7 @@ int main(int argc, char **argv) int dev_given = 0; int gain = 0; int ppm_error = 0; + int custom_ppm = 0; struct llist *curelem,*prev; pthread_attr_t attr; void *status; @@ -420,6 +421,7 @@ int main(int argc, char **argv) break; case 'P': ppm_error = atoi(optarg); + custom_ppm = 1; break; default: usage(); @@ -458,6 +460,9 @@ int main(int argc, char **argv) #endif /* Set the tuner error */ + if (!custom_ppm) { + verbose_ppm_eeprom(dev, &ppm_error); + } verbose_ppm_set(dev, ppm_error); /* Set the sample rate */ From e8d1d2655ca0306cb360c41d5d5c4c95dedf7127 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 29 Jan 2014 18:32:51 -0500 Subject: [PATCH 04/64] modprobe rules --- rtlsdr.conf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 rtlsdr.conf diff --git a/rtlsdr.conf b/rtlsdr.conf new file mode 100644 index 0000000..4dda64c --- /dev/null +++ b/rtlsdr.conf @@ -0,0 +1,2 @@ +# disable DVB drivers +blacklist dvb_usb_rtl28xxu From 3dfa4add72969500f874f0ada99efdeec6fa0a06 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 30 Jan 2014 10:30:14 -0500 Subject: [PATCH 05/64] rtl_fm: esbensen's discriminant --- src/rtl_fm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 19279e6..32a467a 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -215,7 +215,7 @@ void usage(void) "\t[-F fir_size (default: off)]\n" "\t enables low-leakage downsample filter\n" "\t size can be 0 or 9. 0 has bad roll off\n" - "\t[-A std/fast/lut choose atan math (default: std)]\n" + "\t[-A std/fast/lut/ale choose atan math (default: std)]\n" //"\t[-C clip_path (default: off)\n" //"\t (create time stamped raw clips, requires squelch)\n" //"\t (path must have '\%s' and will expand to date_time_freq)\n" @@ -515,6 +515,24 @@ int polar_disc_lut(int ar, int aj, int br, int bj) return 0; } +int esbensen(int ar, int aj, int br, int bj) +/* + input signal: s(t) = a*exp(-i*w*t+p) + a = amplitude, w = angular freq, p = phase difference + solve w + s' = -i(w)*a*exp(-i*w*t+p) + s'*conj(s) = -i*w*a*a + s'*conj(s) / |s|^2 = -i*w +*/ +{ + int cj, dr, dj; + int scaled_pi = 2608; /* 1<<14 / (2*pi) */ + dr = (br - ar) * 2; + dj = (bj - aj) * 2; + cj = bj*dr - br*dj; /* imag(ds*conj(s)) */ + return (scaled_pi * cj / (ar*ar+aj*aj+1)); +} + void fm_demod(struct demod_state *fm) { int i, pcm; @@ -536,6 +554,10 @@ void fm_demod(struct demod_state *fm) pcm = polar_disc_lut(lp[i], lp[i+1], lp[i-2], lp[i-1]); break; + case 3: + pcm = esbensen(lp[i], lp[i+1], + lp[i-2], lp[i-1]); + break; } fm->result[i/2] = (int16_t)pcm; } @@ -1122,6 +1144,8 @@ int main(int argc, char **argv) if (strcmp("lut", optarg) == 0) { atan_lut_init(); demod.custom_atan = 2;} + if (strcmp("ale", optarg) == 0) { + demod.custom_atan = 3;} break; case 'M': if (strcmp("fm", optarg) == 0) { From 3f2632dcaf5ea2c2f28af99a94c64b804a063bfd Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 2 Aug 2014 10:31:39 -0400 Subject: [PATCH 06/64] r82xx: set_dithering() --- include/rtl-sdr.h | 11 +++++++++++ include/tuner_r82xx.h | 2 ++ src/librtlsdr.c | 8 ++++++++ src/rtl_sdr.c | 17 ++++++++++++++++- src/tuner_r82xx.c | 11 +++++++++-- 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 489e117..22da878 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -322,6 +322,17 @@ RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); */ RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); +/*! + * Enable or disable frequency dithering for r820t tuners. + * Must be performed before freq_set(). + * Fails for other tuners. + * + * \param dev the device handle given by rtlsdr_open() + * \param on 0 means disabled, 1 enabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_dithering(rtlsdr_dev_t *dev, int dither); + /* streaming functions */ RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 60f110b..5ec500d 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -84,6 +84,7 @@ struct r82xx_priv { uint8_t input; int has_lock; int init_done; + int disable_dither; /* Store current mode */ uint32_t delsys; @@ -116,5 +117,6 @@ int r82xx_init(struct r82xx_priv *priv); int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); int r82xx_set_nomod(struct r82xx_priv *priv); +int r82xx_set_dither(struct r82xx_priv *priv, int dither); #endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 135c061..b5c1da7 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -1266,6 +1266,14 @@ int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) return (dev->offs_freq) ? 1 : 0; } +int rtlsdr_set_dithering(rtlsdr_dev_t *dev, int dither) +{ + if (dev->tuner_type == RTLSDR_TUNER_R820T) { + return r82xx_set_dither(&dev->r82xx_p, dither); + } + return 1; +} + static rtlsdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid) { unsigned int i; diff --git a/src/rtl_sdr.c b/src/rtl_sdr.c index 0e2f681..bd0a540 100644 --- a/src/rtl_sdr.c +++ b/src/rtl_sdr.c @@ -56,6 +56,7 @@ void usage(void) "\t[-n number of samples to read (default: 0, infinite)]\n" "\t[-S force sync output (default: async)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" + "\t[-N no dithering (default: use dithering)]\n" "\tfilename (a '-' dumps samples to stdout)\n\n"); exit(1); } @@ -115,6 +116,7 @@ int main(int argc, char **argv) int ppm_error = 0; int sync_mode = 0; int direct_sampling = 0; + int dithering = 1; FILE *file; uint8_t *buffer; int dev_index = 0; @@ -123,7 +125,7 @@ int main(int argc, char **argv) uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; - while ((opt = getopt(argc, argv, "d:f:g:s:b:n:p:D:S")) != -1) { + while ((opt = getopt(argc, argv, "d:f:g:s:b:n:p:D:SN")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); @@ -153,6 +155,9 @@ int main(int argc, char **argv) case 'D': direct_sampling = atoi(optarg); break; + case 'N': + dithering = 0; + break; default: usage(); break; @@ -203,6 +208,16 @@ int main(int argc, char **argv) SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif + if (!dithering) { + fprintf(stderr, "Disabling dithering... "); + r = rtlsdr_set_dithering(dev, dithering); + if (r) { + fprintf(stderr, "failure\n"); + } else { + fprintf(stderr, "success\n"); + } + } + if (direct_sampling) { verbose_direct_sampling(dev, direct_sampling); } diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 50e740d..a557c0d 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -506,7 +506,10 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) else val = 0x00; - rc = r82xx_write_reg_mask(priv, 0x12, val, 0x08); + if (priv->disable_dither) + val |= 0x10; + + rc = r82xx_write_reg_mask(priv, 0x12, val, 0x18); if (rc < 0) return rc; @@ -1142,7 +1145,11 @@ int r82xx_set_nomod(struct r82xx_priv *priv) return rc; } - +int r82xx_set_dither(struct r82xx_priv *priv, int dither) +{ + priv->disable_dither = !dither; + return 0; +} /* * r82xx standby logic From a7caaac7a41ca7f8765fa062170ef51a7d09d7c6 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 2 Aug 2014 13:15:17 -0400 Subject: [PATCH 07/64] r82xx: tejeez's if/bw filters --- include/rtl-sdr.h | 4 + include/tuner_r82xx.h | 5 +- src/librtlsdr.c | 49 ++++++-- src/tuner_r82xx.c | 256 +++++++++++++++--------------------------- 4 files changed, 139 insertions(+), 175 deletions(-) diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 22da878..656c55b 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -144,6 +144,10 @@ RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); +RTLSDR_API int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq); + +RTLSDR_API int rtlsdr_set_if_bandwidth(rtlsdr_dev_t *dev, int bw); + /*! * Get actual frequency the device is tuned to. * diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 5ec500d..8068205 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -32,7 +32,8 @@ #define R82XX_CHECK_ADDR 0x00 #define R82XX_CHECK_VAL 0x69 -#define R82XX_IF_FREQ 3570000 +#define R82XX_DEFAULT_IF_FREQ 6000000 +#define R82XX_DEFAULT_IF_BW 2000000 #define REG_SHADOW_START 5 #define NUM_REGS 30 @@ -118,5 +119,7 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); int r82xx_set_nomod(struct r82xx_priv *priv); int r82xx_set_dither(struct r82xx_priv *priv, int dither); +int r82xx_set_bw(struct r82xx_priv *priv, uint32_t bw); +int r82xx_set_if_freq(struct r82xx_priv *priv, uint32_t freq); #endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c index b5c1da7..2b0759e 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -64,6 +64,7 @@ typedef struct rtlsdr_tuner_iface { int (*set_gain)(void *, int gain /* tenth dB */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); + int (*set_if_freq)(void *, uint32_t freq /* Hz */); } rtlsdr_tuner_iface_t; enum rtlsdr_async_status { @@ -123,6 +124,7 @@ struct rtlsdr_dev { int dev_lost; int driver_active; unsigned int xfer_errors; + int tuner_initialized; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -238,7 +240,17 @@ int r820t_set_freq(void *dev, uint32_t freq) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_freq(&devt->r82xx_p, freq); } -int r820t_set_bw(void *dev, int bw) { return 0; } + +int r820t_set_bw(void *dev, int bw) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_bw(&devt->r82xx_p, bw); +} + +int r820t_set_if_freq(void *dev, uint32_t freq) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_if_freq(&devt->r82xx_p, freq); +} + int r820t_set_gain(void *dev, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_gain(&devt->r82xx_p, 1, gain); @@ -251,37 +263,37 @@ int r820t_set_gain_mode(void *dev, int manual) { /* definition order must match enum rtlsdr_tuner */ static rtlsdr_tuner_iface_t tuners[] = { { - NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ }, { e4000_init, e4000_exit, e4000_set_freq, e4000_set_bw, e4000_set_gain, e4000_set_if_gain, - e4000_set_gain_mode + e4000_set_gain_mode, NULL }, { _fc0012_init, fc0012_exit, fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, - fc0012_set_gain_mode + fc0012_set_gain_mode, NULL }, { _fc0013_init, fc0013_exit, fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, - fc0013_set_gain_mode + fc0013_set_gain_mode, NULL }, { fc2580_init, fc2580_exit, _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, - fc2580_set_gain_mode + fc2580_set_gain_mode, NULL }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, - r820t_set_gain_mode + r820t_set_gain_mode, r820t_set_if_freq }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, - r820t_set_gain_mode + r820t_set_gain_mode, r820t_set_if_freq }, }; @@ -662,6 +674,7 @@ int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); /* deinitialize tuner */ rtlsdr_set_i2c_repeater(dev, 0); + dev->tuner_initialized = 0; } /* poweroff demodulator and ADCs */ @@ -693,6 +706,14 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) tmp = if_freq & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); + /* Tell the R820T driver which IF frequency we are currently using + * so that it can choose the optimal IF filter settings. + * Works for normal tuning as well as no-mod direct sampling! */ + if(dev->tuner_initialized && dev->tuner && dev->tuner->set_if_freq) { + rtlsdr_set_i2c_repeater(dev, 1); + dev->tuner->set_if_freq(dev, freq); + rtlsdr_set_i2c_repeater(dev, 0); + } return r; } @@ -1144,6 +1165,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); rtlsdr_set_i2c_repeater(dev, 0); + dev->tuner_initialized = 0; } } @@ -1153,6 +1175,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) fprintf(stderr, "Tuning E4000 to 3708 MHz\n"); rtlsdr_set_i2c_repeater(dev, 1); dev->tuner->init(dev); + dev->tuner_initialized = 1; dev->tuner->set_freq(dev, 3708000000u); e4000_set_bw(dev, 15000000); rtlsdr_set_i2c_repeater(dev, 0); @@ -1161,6 +1184,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; rtlsdr_set_i2c_repeater(dev, 1); dev->tuner->init(dev); + dev->tuner_initialized = 1; r82xx_set_nomod(&devt->r82xx_p); rtlsdr_set_i2c_repeater(dev, 0); } @@ -1190,11 +1214,12 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) rtlsdr_set_i2c_repeater(dev, 1); r |= dev->tuner->init(dev); rtlsdr_set_i2c_repeater(dev, 0); + dev->tuner_initialized = 1; } if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) { - r |= rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); + r |= rtlsdr_set_if_freq(dev, R82XX_DEFAULT_IF_FREQ); /* enable spectrum inversion */ r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); @@ -1585,7 +1610,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) /* the R82XX use 3.57 MHz IF for the DVB-T 6 MHz mode, and * 4.57 MHz for the 8 MHz mode */ - rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); + rtlsdr_set_if_freq(dev, R82XX_DEFAULT_IF_FREQ); /* enable spectrum inversion */ rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); @@ -1598,8 +1623,10 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) break; } - if (dev->tuner->init) + if (dev->tuner->init) { r = dev->tuner->init(dev); + dev->tuner_initialized = 1; + } rtlsdr_set_i2c_repeater(dev, 0); diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index a557c0d..4611d99 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -760,104 +760,29 @@ static int r82xx_sysfreq_sel(struct r82xx_priv *priv, uint32_t freq, return 0; } -static int r82xx_set_tv_standard(struct r82xx_priv *priv, +static int r82xx_init_tv_standard(struct r82xx_priv *priv, unsigned bw, enum r82xx_tuner_type type, uint32_t delsys) { + /* everything that was previously done in r82xx_set_tv_standard + * and doesn't need to be changed when filter settings change */ int rc, i; uint32_t if_khz, filt_cal_lo; - uint8_t data[5]; - uint8_t filt_gain, img_r, filt_q, hp_cor, ext_enable, loop_through; + uint8_t data[5], val; + uint8_t filt_gain, img_r, ext_enable, loop_through; uint8_t lt_att, flt_ext_widest, polyfil_cur; - int need_calibration; - - if (delsys == SYS_ISDBT) { - if_khz = 4063; - filt_cal_lo = 59000; - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ - ext_enable = 0x40; /* r30[6], ext enable; r30[5]:0 ext at lna max */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ - } else { - if (bw < 6) { - /* narrowest bandwidth? */ - if_khz = 3570; - filt_cal_lo = 56000; /* 52000->56000 */ - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ - ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ - } else if (bw == 6) { - if_khz = 3570; - filt_cal_lo = 56000; /* 52000->56000 */ - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x6b; /* 1.7m disable, +2cap, 1.0mhz */ - ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ - } else if (bw == 7) { -#if 0 - /* - * There are two 7 MHz tables defined on the original - * driver, but just the second one seems to be visible - * by rtl2832. Keep this one here commented, as it - * might be needed in the future - */ - - if_khz = 4070; - filt_cal_lo = 60000; - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x2b; /* 1.7m disable, +1cap, 1.0mhz */ - ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ -#endif - /* 7 MHz, second table */ - if_khz = 4570; - filt_cal_lo = 63000; - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x2a; /* 1.7m disable, +1cap, 1.25mhz */ - ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ - } else { - if_khz = 4570; - filt_cal_lo = 68500; - filt_gain = 0x10; /* +3db, 6mhz on */ - img_r = 0x00; /* image negative */ - filt_q = 0x10; /* r10[4]:low q(1'b1) */ - hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */ - ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ - loop_through = 0x00; /* r5[7], lt on */ - lt_att = 0x00; /* r31[7], lt att enable */ - flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ - polyfil_cur = 0x60; /* r25[6:5]:min */ - } - } + + if_khz = R82XX_DEFAULT_IF_FREQ/1000; + filt_cal_lo = 56000; /* 52000->56000 */ + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ /* Initialize the shadow registers */ memcpy(priv->regs, r82xx_init_array, sizeof(r82xx_init_array)); @@ -881,72 +806,6 @@ static int r82xx_set_tv_standard(struct r82xx_priv *priv, } priv->int_freq = if_khz * 1000; - /* Check if standard changed. If so, filter calibration is needed */ - /* as we call this function only once in rtlsdr, force calibration */ - need_calibration = 1; - - if (need_calibration) { - for (i = 0; i < 2; i++) { - /* Set filt_cap */ - rc = r82xx_write_reg_mask(priv, 0x0b, hp_cor, 0x60); - if (rc < 0) - return rc; - - /* set cali clk =on */ - rc = r82xx_write_reg_mask(priv, 0x0f, 0x04, 0x04); - if (rc < 0) - return rc; - - /* X'tal cap 0pF for PLL */ - rc = r82xx_write_reg_mask(priv, 0x10, 0x00, 0x03); - if (rc < 0) - return rc; - - rc = r82xx_set_pll(priv, filt_cal_lo * 1000); - if (rc < 0 || !priv->has_lock) - return rc; - - /* Start Trigger */ - rc = r82xx_write_reg_mask(priv, 0x0b, 0x10, 0x10); - if (rc < 0) - return rc; - -// usleep_range(1000, 2000); - - /* Stop Trigger */ - rc = r82xx_write_reg_mask(priv, 0x0b, 0x00, 0x10); - if (rc < 0) - return rc; - - /* set cali clk =off */ - rc = r82xx_write_reg_mask(priv, 0x0f, 0x00, 0x04); - if (rc < 0) - return rc; - - /* Check if calibration worked */ - rc = r82xx_read(priv, 0x00, data, sizeof(data)); - if (rc < 0) - return rc; - - priv->fil_cal_code = data[4] & 0x0f; - if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) - break; - } - /* narrowest */ - if (priv->fil_cal_code == 0x0f) - priv->fil_cal_code = 0; - } - - rc = r82xx_write_reg_mask(priv, 0x0a, - filt_q | priv->fil_cal_code, 0x1f); - if (rc < 0) - return rc; - - /* Set BW, Filter_gain, & HP corner */ - rc = r82xx_write_reg_mask(priv, 0x0b, hp_cor, 0xef); - if (rc < 0) - return rc; - /* Set Img_R */ rc = r82xx_write_reg_mask(priv, 0x07, img_r, 0x80); if (rc < 0) @@ -985,11 +844,79 @@ static int r82xx_set_tv_standard(struct r82xx_priv *priv, /* Store current standard. If it changes, re-calibrate the tuner */ priv->delsys = delsys; priv->type = type; - priv->bw = bw; return 0; } +static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { + int rc, i; + uint8_t filt_q, hp_cor; + int cal; + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + + if(lpf <= 2500) { + hp_cor = 0xE0; /* 1.7m enable, +2cap */ + cal = 16*(2500-lpf) / (2500-2000); + } else if(lpf <= 3100) { + hp_cor = 0xA0; /* 1.7m enable, +1cap */ + cal = 16*(3100-lpf) / (3100-2200); + } else if(lpf <= 3900) { + hp_cor = 0x80; /* 1.7m enable, +0cap */ + cal = 16*(3900-lpf) / (3900-2600); + } else if(lpf <= 7100) { + hp_cor = 0x60; /* 1.7m disable, +2cap */ + cal = 16*(7100-lpf) / (7100-5300); + } else if(lpf <= 8700) { + hp_cor = 0x20; /* 1.7m disable, +1cap */ + cal = 16*(8700-lpf) / (8700-6300); + } else { + hp_cor = 0x00; /* 1.7m disable, +0cap */ + cal = 16*(11000-lpf) / (11000-7500); + } + + if(hpf >= 4700) hp_cor |= 0x00; /* 5 MHz */ + else if(hpf >= 3800) hp_cor |= 0x01; /* 4 MHz */ + else if(hpf >= 3000) hp_cor |= 0x02; /* -12dB @ 2.25 MHz */ + else if(hpf >= 2800) hp_cor |= 0x03; /* -8dB @ 2.25 MHz */ + else if(hpf >= 2600) hp_cor |= 0x04; /* -4dB @ 2.25 MHz */ + else if(hpf >= 2400) hp_cor |= 0x05; /* -12dB @ 1.75 MHz */ + else if(hpf >= 2200) hp_cor |= 0x06; /* -8dB @ 1.75 MHz */ + else if(hpf >= 2000) hp_cor |= 0x07; /* -4dB @ 1.75 MHz */ + else if(hpf >= 1800) hp_cor |= 0x08; /* -12dB @ 1.25 MHz */ + else if(hpf >= 1600) hp_cor |= 0x09; /* -8dB @ 1.25 MHz */ + else if(hpf >= 1400) hp_cor |= 0x0A; /* -4dB @ 1.25 MHz */ + else hp_cor |= 0x0B; + + + if(cal < 0) cal = 0; + else if(cal > 15) cal = 15; + priv->fil_cal_code = cal; + + fprintf(stderr, "Setting IF filter for %d...%d kHz: hp_cor=0x%02x, fil_cal_code=%d\n", hpf, lpf, hp_cor, cal); + + rc = r82xx_write_reg_mask(priv, 0x0a, + filt_q | priv->fil_cal_code, 0x1f); + if (rc < 0) + return rc; + + /* Set BW, Filter_gain, & HP corner */ + rc = r82xx_write_reg_mask(priv, 0x0b, hp_cor, 0xef); + if (rc < 0) + return rc; + + return 0; +} + +int r82xx_set_bw(struct r82xx_priv *priv, uint32_t bw) { + priv->bw = bw; + return r82xx_set_if_filter(priv, ((int)priv->int_freq - (int)bw/2)/1000, ((int)priv->int_freq + (int)bw/2)/1000); +} + +int r82xx_set_if_freq(struct r82xx_priv *priv, uint32_t freq) { + priv->int_freq = freq; + return r82xx_set_if_filter(priv, ((int)freq - (int)priv->bw/2)/1000, ((int)freq + (int)priv->bw/2)/1000); +} + static int r82xx_read_gain(struct r82xx_priv *priv) { uint8_t data[4]; @@ -1127,9 +1054,9 @@ int r82xx_set_nomod(struct r82xx_priv *priv) fprintf(stderr, "Using R820T no-mod direct sampling mode\n"); - rc = r82xx_set_tv_standard(priv, 8, TUNER_DIGITAL_TV, 0); + /*rc = r82xx_set_bw(priv, 1000000); if (rc < 0) - goto err; + goto err;*/ /* experimentally determined magic numbers * needs more experimenting with all the registers */ @@ -1275,11 +1202,14 @@ int r82xx_init(struct r82xx_priv *priv) rc = r82xx_write(priv, 0x05, r82xx_init_array, sizeof(r82xx_init_array)); - rc = r82xx_set_tv_standard(priv, 3, TUNER_DIGITAL_TV, 0); - if (rc < 0) - goto err; + rc |= r82xx_init_tv_standard(priv, 3, TUNER_DIGITAL_TV, 0); + + priv->bw = R82XX_DEFAULT_IF_BW; + priv->int_freq = R82XX_DEFAULT_IF_FREQ; + /* r82xx_set_bw will always be called by rtlsdr_set_sample_rate, + so there's no need to call r82xx_set_if_filter here */ - rc = r82xx_sysfreq_sel(priv, 0, TUNER_DIGITAL_TV, SYS_DVBT); + rc |= r82xx_sysfreq_sel(priv, 0, TUNER_DIGITAL_TV, SYS_DVBT); priv->init_done = 1; From 4198f7dedb2db3f0c07961007a0f87b74dd9c69d Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 2 Aug 2014 20:06:19 -0400 Subject: [PATCH 08/64] r82xx: register caching --- include/tuner_r82xx.h | 1 + src/tuner_r82xx.c | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 8068205..1aef4ea 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -86,6 +86,7 @@ struct r82xx_priv { int has_lock; int init_done; int disable_dither; + int reg_cache; /* Store current mode */ uint32_t delsys; diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 4611d99..38e6f3c 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -286,11 +286,6 @@ static int r82xx_write(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, return 0; } -static int r82xx_write_reg(struct r82xx_priv *priv, uint8_t reg, uint8_t val) -{ - return r82xx_write(priv, reg, &val, 1); -} - static int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg) { reg -= REG_SHADOW_START; @@ -301,6 +296,13 @@ static int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg) return -1; } +static int r82xx_write_reg(struct r82xx_priv *priv, uint8_t reg, uint8_t val) +{ + if (priv->reg_cache && r82xx_read_cache_reg(priv, reg) == val) + return 0; + return r82xx_write(priv, reg, &val, 1); +} + static int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t val, uint8_t bit_mask) { @@ -311,6 +313,8 @@ static int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t va val = (rc & ~bit_mask) | (val & bit_mask); + if (priv->reg_cache && r82xx_read_cache_reg(priv, reg) == val) + return 0; return r82xx_write(priv, reg, &val, 1); } @@ -1090,6 +1094,7 @@ int r82xx_standby(struct r82xx_priv *priv) if (!priv->init_done) return 0; + priv->reg_cache = 0; rc = r82xx_write_reg(priv, 0x06, 0xb1); if (rc < 0) return rc; @@ -1125,6 +1130,7 @@ int r82xx_standby(struct r82xx_priv *priv) /* Force initial calibration */ priv->type = -1; + priv->reg_cache = 1; return rc; } @@ -1199,6 +1205,7 @@ int r82xx_init(struct r82xx_priv *priv) priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; /* Initialize registers */ + priv->reg_cache = 0; rc = r82xx_write(priv, 0x05, r82xx_init_array, sizeof(r82xx_init_array)); @@ -1212,6 +1219,7 @@ int r82xx_init(struct r82xx_priv *priv) rc |= r82xx_sysfreq_sel(priv, 0, TUNER_DIGITAL_TV, SYS_DVBT); priv->init_done = 1; + priv->reg_cache = 1; err: if (rc < 0) From a0b613183d212695bcec573063140ef44357a7ef Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 2 Aug 2014 21:41:49 -0400 Subject: [PATCH 09/64] r82xx: register batching --- include/tuner_r82xx.h | 1 + src/tuner_r82xx.c | 54 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 1aef4ea..5be538d 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -87,6 +87,7 @@ struct r82xx_priv { int init_done; int disable_dither; int reg_cache; + int reg_batch, reg_low, reg_high; /* Store current mode */ uint32_t delsys; diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 38e6f3c..e0d6c5c 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -300,6 +300,14 @@ static int r82xx_write_reg(struct r82xx_priv *priv, uint8_t reg, uint8_t val) { if (priv->reg_cache && r82xx_read_cache_reg(priv, reg) == val) return 0; + if (priv->reg_batch) { + shadow_store(priv, reg, &val, 1); + if (reg < priv->reg_low) + priv->reg_low = reg; + if (reg > priv->reg_high) + priv->reg_high = reg; + return 0; + } return r82xx_write(priv, reg, &val, 1); } @@ -313,9 +321,34 @@ static int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t va val = (rc & ~bit_mask) | (val & bit_mask); - if (priv->reg_cache && r82xx_read_cache_reg(priv, reg) == val) - return 0; - return r82xx_write(priv, reg, &val, 1); + return r82xx_write_reg(priv, reg, val); +} + +static int r82xx_write_batch_init(struct r82xx_priv *priv) +{ + priv->reg_batch = 0; + if (priv->reg_cache) { + priv->reg_batch = 1; + priv->reg_low = NUM_REGS; + priv->reg_high = 0; + } + return 0; +} + +static int r82xx_write_batch_sync(struct r82xx_priv *priv) +{ + int rc, offset, len; + if (!priv->reg_cache) + return -1; + if (!priv->reg_batch) + return -1; + priv->reg_batch = 0; + if (priv->reg_low > priv->reg_high) + return -1; + offset = priv->reg_low - REG_SHADOW_START; + len = priv->reg_high - priv->reg_low + 1; + rc = r82xx_write(priv, priv->reg_low, priv->regs+offset, len); + return rc; } static uint8_t r82xx_bitrev(uint8_t byte) @@ -437,6 +470,8 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) uint8_t ni, si, nint, vco_fine_tune, val; uint8_t data[5]; + r82xx_write_batch_init(priv); + /* Frequency in kHz */ freq_khz = (freq + 500) / 1000; pll_ref = priv->cfg->xtal; @@ -535,6 +570,14 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) if (rc < 0) return rc; + if (priv->reg_batch) { + rc = r82xx_write_batch_sync(priv); + if (rc < 0) { + fprintf(stderr, "[R82XX] Batch error in PLL for %u Hz!\n", freq); + return rc; + } + } + for (i = 0; i < 2; i++) { // usleep_range(sleep_time, sleep_time + 1000); @@ -1026,6 +1069,8 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) uint32_t lo_freq = freq + priv->int_freq; uint8_t air_cable1_in; + r82xx_write_batch_init(priv); + rc = r82xx_set_mux(priv, lo_freq); if (rc < 0) goto err; @@ -1046,6 +1091,9 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); } + if (priv->reg_batch) { + rc = r82xx_write_batch_sync(priv); + } err: if (rc < 0) fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); From d59b178cc1eb27f0beca802a9cf82f1d6ded3a48 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 2 Aug 2014 22:54:30 -0400 Subject: [PATCH 10/64] lib: cache i2c repeater --- src/librtlsdr.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 2b0759e..1a2573e 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -125,6 +125,7 @@ struct rtlsdr_dev { int driver_active; unsigned int xfer_errors; int tuner_initialized; + int i2c_repeater_on; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -573,6 +574,10 @@ void rtlsdr_set_gpio_output(rtlsdr_dev_t *dev, uint8_t gpio) void rtlsdr_set_i2c_repeater(rtlsdr_dev_t *dev, int on) { + if (on == dev->i2c_repeater_on) + return; + on = !!on; /* values +2 to force on */ + dev->i2c_repeater_on = on; rtlsdr_demod_write_reg(dev, 1, 0x01, on ? 0x18 : 0x10, 1); } @@ -670,6 +675,8 @@ int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); + if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); /* deinitialize tuner */ @@ -692,6 +699,7 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); /* read corrected clock value */ if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) @@ -712,7 +720,6 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) if(dev->tuner_initialized && dev->tuner && dev->tuner->set_if_freq) { rtlsdr_set_i2c_repeater(dev, 1); dev->tuner->set_if_freq(dev, freq); - rtlsdr_set_i2c_repeater(dev, 0); } return r; } @@ -722,6 +729,7 @@ int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) int r = 0; uint8_t tmp; int16_t offs = ppm * (-1) * TWO_POW(24) / 1000000; + rtlsdr_set_i2c_repeater(dev, 0); tmp = offs & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x3f, tmp, 1); @@ -734,6 +742,7 @@ int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq) { int r = 0; + rtlsdr_set_i2c_repeater(dev, 0); if (!dev) return -1; @@ -831,6 +840,7 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 int r = 0; int i; uint8_t cmd[2]; + rtlsdr_set_i2c_repeater(dev, 0); if (!dev) return -1; @@ -868,6 +878,7 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ { int r = 0; int i; + rtlsdr_set_i2c_repeater(dev, 0); if (!dev) return -1; @@ -897,11 +908,11 @@ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) return -1; if (dev->direct_sampling) { + rtlsdr_set_i2c_repeater(dev, 0); r = rtlsdr_set_if_freq(dev, freq); } else if (dev->tuner && dev->tuner->set_freq) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq(dev, freq - dev->offs_freq); - rtlsdr_set_i2c_repeater(dev, 0); } if (!r) @@ -923,6 +934,7 @@ uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; + rtlsdr_set_i2c_repeater(dev, 0); if (!dev) return -1; @@ -1025,7 +1037,6 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) if (dev->tuner->set_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_gain((void *)dev, gain); - rtlsdr_set_i2c_repeater(dev, 0); } if (!r) @@ -1054,7 +1065,6 @@ int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) if (dev->tuner->set_if_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_if_gain(dev, stage, gain); - rtlsdr_set_i2c_repeater(dev, 0); } return r; @@ -1070,7 +1080,6 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) if (dev->tuner->set_gain_mode) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_gain_mode((void *)dev, mode); - rtlsdr_set_i2c_repeater(dev, 0); } return r; @@ -1085,6 +1094,7 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); /* check if the rate is supported by the resampler */ if ((samp_rate <= 225000) || (samp_rate > 3200000) || @@ -1140,6 +1150,7 @@ int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x03 : 0x05, 1); } @@ -1148,6 +1159,7 @@ int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x25 : 0x05, 1); } @@ -1189,6 +1201,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) rtlsdr_set_i2c_repeater(dev, 0); } } + rtlsdr_set_i2c_repeater(dev, 0); /* common to all direct modes */ if (on) { @@ -1256,6 +1269,7 @@ int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) { int r = 0; + rtlsdr_set_i2c_repeater(dev, 0); if (!dev) return -1; @@ -1624,6 +1638,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) } if (dev->tuner->init) { + rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->init(dev); dev->tuner_initialized = 1; } @@ -1648,6 +1663,7 @@ int rtlsdr_close(rtlsdr_dev_t *dev) { if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); if(!dev->dev_lost) { /* block until all async operations have been completed (if any) */ @@ -1686,6 +1702,7 @@ int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) { if (!dev) return -1; + rtlsdr_set_i2c_repeater(dev, 0); rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x0000, 2); From 29d2c849f9913ec5c440f7bb3d58533f2993ba54 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 3 Aug 2014 14:38:32 -0400 Subject: [PATCH 11/64] r82xx: pll tweaks beyond my ken --- src/tuner_r82xx.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index e0d6c5c..6824a4a 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -460,7 +460,6 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) uint32_t vco_min = 1770000; uint32_t vco_max = vco_min * 2; uint32_t freq_khz, pll_ref, pll_ref_khz; - uint16_t n_sdm = 2; uint16_t sdm = 0; uint8_t mix_div = 2; uint8_t div_buf = 0; @@ -492,6 +491,8 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; /* Calculate divider */ + if(freq_khz < vco_min/64) vco_min /= 2; + if(freq_khz >= vco_max/2) vco_max *= 2; while (mix_div <= 64) { if (((freq_khz * mix_div) >= vco_min) && ((freq_khz * mix_div) < vco_max)) { @@ -505,14 +506,16 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) mix_div = mix_div << 1; } - rc = r82xx_read(priv, 0x00, data, sizeof(data)); - if (rc < 0) - return rc; - if (priv->cfg->rafael_chip == CHIP_R828D) vco_power_ref = 1; + /* + rc = r82xx_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; vco_fine_tune = (data[4] & 0x30) >> 4; + */ + vco_fine_tune = 2; if (vco_fine_tune > vco_power_ref) div_num = div_num - 1; @@ -527,7 +530,10 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) nint = vco_freq / (2 * pll_ref); vco_fra = (vco_freq - 2 * pll_ref * nint) / 1000; - if (nint > ((128 / vco_power_ref) - 1)) { + if (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) { + fprintf(stderr, "[R828D] No valid PLL values for %u Hz!\n", freq); + return -1; + } else if (nint > 76) { fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); return -1; } @@ -553,15 +559,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; /* sdm calculator */ - while (vco_fra > 1) { - if (vco_fra > (2 * pll_ref_khz / n_sdm)) { - sdm = sdm + 32768 / (n_sdm / 2); - vco_fra = vco_fra - 2 * pll_ref_khz / n_sdm; - if (n_sdm >= 0x8000) - break; - } - n_sdm <<= 1; - } + sdm = (((vco_freq<<16)+pll_ref) / (2*pll_ref)) & 0xFFFF; rc = r82xx_write_reg(priv, 0x16, sdm >> 8); if (rc < 0) From 0bba67ab454a2692aece704a7be39a2a09bb0829 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 4 Aug 2014 12:39:10 -0400 Subject: [PATCH 12/64] r82xx: error on pll failure --- include/tuner_r82xx.h | 1 - src/tuner_r82xx.c | 19 ++++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 5be538d..77a87e3 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -83,7 +83,6 @@ struct r82xx_priv { uint32_t int_freq; uint8_t fil_cal_code; uint8_t input; - int has_lock; int init_done; int disable_dither; int reg_cache; diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 6824a4a..7410d81 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -585,23 +585,20 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; if (data[2] & 0x40) break; + if (i > 0) + break; - if (!i) { - /* Didn't lock. Increase VCO current */ - rc = r82xx_write_reg_mask(priv, 0x12, 0x60, 0xe0); - if (rc < 0) - return rc; - } + /* Didn't lock. Increase VCO current */ + rc = r82xx_write_reg_mask(priv, 0x12, 0x60, 0xe0); + if (rc < 0) + return rc; } if (!(data[2] & 0x40)) { fprintf(stderr, "[R82XX] PLL not locked!\n"); - priv->has_lock = 0; - return 0; + return -1; } - priv->has_lock = 1; - /* set pll autotune = 8kHz */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x08, 0x08); @@ -1074,7 +1071,7 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) goto err; rc = r82xx_set_pll(priv, lo_freq); - if (rc < 0 || !priv->has_lock) + if (rc < 0) goto err; /* switch between 'Cable1' and 'Air-In' inputs on sticks with From c5d7ebee5c7a40e52dbc50e3931f7d5096d7a9a6 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 4 Aug 2014 12:40:01 -0400 Subject: [PATCH 13/64] rtl_test: r820t tuning range --- src/rtl_test.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/rtl_test.c b/src/rtl_test.c index 9a6cfda..ba25390 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -76,7 +76,7 @@ void usage(void) "Usage:\n" "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-d device_index (default: 0)]\n" - "\t[-t enable Elonics E4000 tuner benchmark]\n" + "\t[-t enable E4000 or R820T tuner range benchmark]\n" #ifndef _WIN32 "\t[-p[seconds] enable PPM error measurement (default: 10 seconds)]\n" #endif @@ -267,6 +267,33 @@ void e4k_benchmark(void) gap_start/MHZ(1), gap_end/MHZ(1)); } +void r820t_benchmark(void) +{ + uint32_t freq; + uint32_t range_start = 0, range_end = 0; + + fprintf(stderr, "Benchmarking R820T PLL...\n"); + + /* find tuner range start */ + for (freq = MHZ(30); freq > MHZ(1); freq -= MHZ(1)) { + if (rtlsdr_set_center_freq(dev, freq)) { + break;} + range_start = freq; + } + + /* find tuner range end */ + for (freq = MHZ(1750); freq < MHZ(1950UL); freq += MHZ(1)) { + if (rtlsdr_set_center_freq(dev, freq)) { + break;} + range_end = freq; + } + + fprintf(stderr, "R820T range: %i to %i MHz\n", + range_start/MHZ(1), range_end/MHZ(1)); +} + + + int main(int argc, char **argv) { #ifndef _WIN32 @@ -360,11 +387,16 @@ int main(int argc, char **argv) verbose_set_sample_rate(dev, samp_rate); if (test_mode == TUNER_BENCHMARK) { - if (rtlsdr_get_tuner_type(dev) == RTLSDR_TUNER_E4000) - e4k_benchmark(); - else - fprintf(stderr, "No E4000 tuner found, aborting.\n"); - + switch (rtlsdr_get_tuner_type(dev)) { + case RTLSDR_TUNER_E4000: + e4k_benchmark(); + break; + case RTLSDR_TUNER_R820T: + r820t_benchmark(); + break; + default: + fprintf(stderr, "No testable tuner found, aborting.\n"); + } goto exit; } From fba770810b6debbc5b806b436349aefb59bb4bc2 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 5 Aug 2014 08:09:50 -0400 Subject: [PATCH 14/64] r82xx: build warning --- src/tuner_r82xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 7410d81..ada6c28 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -561,6 +561,8 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) /* sdm calculator */ sdm = (((vco_freq<<16)+pll_ref) / (2*pll_ref)) & 0xFFFF; + //fprintf(stderr, "LO: %u kHz, MixDiv: %u, PLLDiv: %u, VCO %u kHz, SDM: %u \n", (uint32_t)(freq/1000), mix_div, nint, (uint32_t)(vco_freq/1000), sdm); + rc = r82xx_write_reg(priv, 0x16, sdm >> 8); if (rc < 0) return rc; @@ -580,6 +582,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) // usleep_range(sleep_time, sleep_time + 1000); /* Check if PLL has locked */ + data[2] = 0; rc = r82xx_read(priv, 0x00, data, 3); if (rc < 0) return rc; From 588b673a5f9a9deb366406c29de5a431374cae97 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 5 Aug 2014 23:31:52 -0400 Subject: [PATCH 15/64] rtl_fm: half working AGC --- src/rtl_fm.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 32a467a..c81dd4e 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -31,17 +31,14 @@ * todo: * sanity checks * scale squelch to other input parameters - * test all the demodulations * pad output on hop * frequency ranges could be stored better - * scaled AM demod amplification * auto-hop after time limit * peak detector to tune onto stronger signals * fifo for active hop frequency * clips * noise squelch * merge stereo patch - * merge soft agc patch * merge udp patch * testmode to detect overruns * watchdog to reset bad dongle @@ -110,6 +107,17 @@ struct dongle_state struct demod_state *demod_target; }; +struct agc_state +{ + int64_t gain_num; + int64_t gain_den; + int64_t gain_max; + int gain_int; + int peak_target; + int attack_step; + int decay_step; +}; + struct demod_state { int exit_flag; @@ -139,6 +147,8 @@ struct demod_state int now_lpr; int prev_lpr_index; int dc_block, dc_avg; + int agc_enable; + struct agc_state *agc; void (*mode_demod)(struct demod_state*); pthread_rwlock_t rw; pthread_cond_t ready; @@ -203,6 +213,7 @@ void usage(void) "\t edge: enable lower edge tuning\n" "\t dc: enable dc blocking filter\n" "\t deemp: enable de-emphasis filter\n" + "\t swagc: enable software agc (only for AM)\n" "\t direct: enable direct sampling\n" "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" @@ -750,9 +761,36 @@ void arbitrary_resample(int16_t *buf1, int16_t *buf2, int len1, int len2) } } +void software_agc(struct demod_state *d) +/* ignores complex pairs, indirectly calculates power squelching */ +{ + int i = 0; + int output; + struct agc_state *agc = d->agc; + + for (i=0; i < d->result_len; i++) { + output = (int)((int64_t)d->result[i] * agc->gain_num / agc->gain_den); + + if (abs(output) < agc->peak_target) { + agc->gain_num += agc->decay_step; + } else { + agc->gain_num -= agc->attack_step; + } + + if (agc->gain_num < 1) { + agc->gain_num = 1;} + if (agc->gain_num > agc->gain_max) { + agc->gain_num = agc->gain_max;} + + agc->gain_int = (int)(agc->gain_num / agc->gain_den); + d->result[i] = output; + } +} + void full_demod(struct demod_state *d) { int i, ds_p; + int do_squelch = 0; int sr = 0; ds_p = d->downsample_passes; if (ds_p) { @@ -771,16 +809,26 @@ void full_demod(struct demod_state *d) } else { low_pass(d); } + if (d->agc_enable) { + software_agc(d); + if(d->squelch_level && d->agc->gain_int > d->squelch_level) { + do_squelch = 1;} /* power squelch */ - if (d->squelch_level) { + } else if (d->squelch_level) { sr = rms(d->lowpassed, d->lp_len, 1); if (sr < d->squelch_level) { - d->squelch_hits++; - for (i=0; ilp_len; i++) { - d->lowpassed[i] = 0; - } - } else { - d->squelch_hits = 0;} + do_squelch = 1;} + } + if (do_squelch) { + d->squelch_hits++; + for (i=0; ilp_len; i++) { + d->lowpassed[i] = 0; + } + } else { + d->squelch_hits = 0; + } + if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { + d->agc->gain_num = d->agc->gain_den; } d->mode_demod(d); /* lowpassed -> result */ if (d->mode_demod == &raw_demod) { @@ -986,6 +1034,7 @@ void demod_init(struct demod_state *s) s->post_downsample = 1; // once this works, default = 4 s->custom_atan = 0; s->deemph = 0; + s->agc_enable = 0; s->rate_out2 = -1; // flag for disabled s->mode_demod = &fm_demod; s->pre_j = s->pre_r = s->now_r = s->now_j = 0; @@ -1057,6 +1106,24 @@ void sanity_checks(void) } +int agc_init(struct demod_state *s) +{ + int i; + struct agc_state *agc; + + agc = malloc(sizeof(struct agc_state)); + s->agc = agc; + + agc->gain_den = 1<<13; + agc->gain_num = agc->gain_den; + agc->gain_int = (int)(agc->gain_den / agc->gain_num); + agc->peak_target = 1<<13; + agc->gain_max = 1<<10 * agc->gain_num; + agc->attack_step = 2; + agc->decay_step = 1; + return 0; +} + int main(int argc, char **argv) { #ifndef _WIN32 @@ -1067,6 +1134,7 @@ int main(int argc, char **argv) int custom_ppm = 0; dongle_init(&dongle); demod_init(&demod); + agc_init(&demod); output_init(&output); controller_init(&controller); @@ -1125,6 +1193,8 @@ int main(int argc, char **argv) demod.dc_block = 1;} if (strcmp("deemp", optarg) == 0) { demod.deemph = 1;} + if (strcmp("swagc", optarg) == 0) { + demod.agc_enable = 1;} if (strcmp("direct", optarg) == 0) { dongle.direct_sampling = 1;} if (strcmp("no-mod", optarg) == 0) { From 6d5cd1619acead8caf16b13c5f86b6b586ca3a2e Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 7 Aug 2014 12:00:08 -0400 Subject: [PATCH 16/64] rtl_fm: wav header --- src/rtl_fm.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index c81dd4e..c3cd28a 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -165,6 +165,7 @@ struct output_state int16_t result[MAXIMUM_BUF_LENGTH]; int result_len; int rate; + int wav_format; pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; @@ -217,6 +218,7 @@ void usage(void) "\t direct: enable direct sampling\n" "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" + "\t wav: generate WAV header\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" @@ -237,6 +239,7 @@ void usage(void) "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n" "\t | aplay -r 24k -f S16_LE -t raw -c 1\n" "\t -M wbfm | play -r 32k ... \n" + "\t -E wav | play -t wav - \n" "\t -s 22050 | multimon -t raw /dev/stdin\n\n"); exit(1); } @@ -1058,7 +1061,7 @@ void demod_cleanup(struct demod_state *s) void output_init(struct output_state *s) { - s->rate = DEFAULT_SAMPLE_RATE; + //s->rate = DEFAULT_SAMPLE_RATE; pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); @@ -1124,6 +1127,40 @@ int agc_init(struct demod_state *s) return 0; } +int generate_header(struct demod_state *d, struct output_state *o) +{ + int i, s_rate, b_rate; + char *channels = "\1\0"; + char *align = "\2\0"; + uint8_t samp_rate[4] = {0, 0, 0, 0}; + uint8_t byte_rate[4] = {0, 0, 0, 0}; + s_rate = o->rate; + b_rate = o->rate * 2; + if (d->mode_demod == &raw_demod) { + channels = "\2\0"; + align = "\4\0"; + b_rate *= 2; + } + for (i=0; i<4; i++) { + samp_rate[i] = (uint8_t)((s_rate >> (8*i)) & 0xFF); + byte_rate[i] = (uint8_t)((b_rate >> (8*i)) & 0xFF); + } + fwrite("RIFF", 1, 4, o->file); + fwrite("\xFF\xFF\xFF\xFF", 1, 4, o->file); /* size */ + fwrite("WAVE", 1, 4, o->file); + fwrite("fmt ", 1, 4, o->file); + fwrite("\x10\0\0\0", 1, 4, o->file); /* size */ + fwrite("\1\0", 1, 2, o->file); /* pcm */ + fwrite(channels, 1, 2, o->file); + fwrite(samp_rate, 1, 4, o->file); + fwrite(byte_rate, 1, 4, o->file); + fwrite(align, 1, 2, o->file); + fwrite("\x10\0", 1, 2, o->file); /* bits per channel */ + fwrite("data", 1, 4, o->file); + fwrite("\xFF\xFF\xFF\xFF", 1, 4, o->file); /* size */ + return 0; +} + int main(int argc, char **argv) { #ifndef _WIN32 @@ -1201,6 +1238,8 @@ int main(int argc, char **argv) dongle.direct_sampling = 3;} if (strcmp("offset", optarg) == 0) { dongle.offset_tuning = 1;} + if (strcmp("wav", optarg) == 0) { + output.wav_format = 1;} break; case 'F': demod.downsample_passes = 1; /* truthy placeholder */ @@ -1320,6 +1359,10 @@ int main(int argc, char **argv) } } + if (output.wav_format) { + generate_header(&demod, &output); + } + //r = rtlsdr_set_testmode(dongle.dev, 1); /* Reset endpoint before we start reading from it (mandatory) */ From 42d8f12c6ee95a78011de78dcb358c01f018802e Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 11 Aug 2014 04:14:17 -0400 Subject: [PATCH 17/64] rtl_fm: mostly working AGC --- src/rtl_fm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index c3cd28a..a08cf69 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -780,8 +780,8 @@ void software_agc(struct demod_state *d) agc->gain_num -= agc->attack_step; } - if (agc->gain_num < 1) { - agc->gain_num = 1;} + if (agc->gain_num < agc->gain_den) { + agc->gain_num = agc->gain_den;} if (agc->gain_num > agc->gain_max) { agc->gain_num = agc->gain_max;} @@ -1119,7 +1119,7 @@ int agc_init(struct demod_state *s) agc->gain_den = 1<<13; agc->gain_num = agc->gain_den; - agc->gain_int = (int)(agc->gain_den / agc->gain_num); + agc->gain_int = (int)(agc->gain_num / agc->gain_den); agc->peak_target = 1<<13; agc->gain_max = 1<<10 * agc->gain_num; agc->attack_step = 2; From 9ed9ffa37e24f3293fa960cfcd74909ac3e9996c Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 11 Aug 2014 15:54:48 -0400 Subject: [PATCH 18/64] lib: retry i2c on failure --- src/librtlsdr.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 1a2573e..a3b5359 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -1950,16 +1950,32 @@ uint32_t rtlsdr_get_tuner_clock(void *dev) int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len) { - if (dev) - return rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); - + int r; + int retries = 2; + if (!dev) + return -1; + do { + r = rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); + if (r >= 0) + return r; + rtlsdr_set_i2c_repeater(dev, 2); + retries--; + } while (retries > 0); return -1; } int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) { - if (dev) - return rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); - + int r; + int retries = 2; + if (!dev) + return -1; + do { + r = rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); + if (r >= 0) + return r; + rtlsdr_set_i2c_repeater(dev, 2); + retries--; + } while (retries > 0); return -1; } From 3cb8bd6aa87c0a9f412c19c0984a2ac7e7162c8d Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 11 Aug 2014 20:26:07 -0400 Subject: [PATCH 19/64] rtl_fm: proportional squelch --- src/rtl_fm.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index a08cf69..2567819 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -699,6 +699,25 @@ int rms(int16_t *samples, int len, int step) return (int)sqrt((p-err) / len); } +int squelch_to_rms(int db, struct dongle_state *dongle, struct demod_state *demod) +/* 0 dB = 1 rms at 50dB gain and 1024 downsample */ +{ + double linear, gain, downsample; + if (db == 0) { + return 0;} + linear = pow(10.0, (double)db/20.0); + gain = 50.0; + if (dongle->gain != AUTO_GAIN) { + gain = (double)(dongle->gain) / 10.0; + } + gain = 50.0 - gain; + gain = pow(10.0, gain/20.0); + downsample = 1024.0 / (double)demod->downsample; + linear = linear / gain; + linear = linear / downsample; + return (int)linear + 1; +} + void arbitrary_upsample(int16_t *buf1, int16_t *buf2, int len1, int len2) /* linear interpolation, len1 < len2 */ { @@ -964,6 +983,7 @@ static void *controller_thread_fn(void *arg) /* set up primary channel */ optimal_settings(s->freqs[0], demod.rate_in); + demod.squelch_level = squelch_to_rms(demod.squelch_level, &dongle, &demod); if (dongle.direct_sampling) { verbose_direct_sampling(dongle.dev, dongle.direct_sampling);} if (dongle.offset_tuning) { From 08889eecb5cc00290a641bd67423ab15821f05a5 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 14 Aug 2014 15:51:01 -0400 Subject: [PATCH 20/64] rtl_fm, power: stay open through broken pipes --- src/rtl_fm.c | 1 + src/rtl_power.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 2567819..218c868 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -1345,6 +1345,7 @@ int main(int argc, char **argv) sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); + signal(SIGPIPE, SIG_IGN); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif diff --git a/src/rtl_power.c b/src/rtl_power.c index 7e2286a..66b144b 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -905,6 +905,7 @@ int main(int argc, char **argv) sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); + signal(SIGPIPE, SIG_IGN); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif From 00a74f0f3e38dbe020e0bd27f15b121af6d4a42b Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 14 Aug 2014 23:30:21 -0400 Subject: [PATCH 21/64] rtl_sdr: units on -n --- src/rtl_sdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtl_sdr.c b/src/rtl_sdr.c index bd0a540..984e09a 100644 --- a/src/rtl_sdr.c +++ b/src/rtl_sdr.c @@ -147,7 +147,7 @@ int main(int argc, char **argv) out_block_size = (uint32_t)atof(optarg); break; case 'n': - bytes_to_read = (uint32_t)atof(optarg) * 2; + bytes_to_read = (uint32_t)atofs(optarg) * 2; break; case 'S': sync_mode = 1; From e98ab40414ae2e6549293f6ebd5a3b6326a63b82 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sat, 16 Aug 2014 00:56:37 -0400 Subject: [PATCH 22/64] rtl_fm: half finished stream padding --- src/rtl_fm.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 218c868..5b34b62 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -51,6 +51,12 @@ #include #include +#ifdef __APPLE__ +#include +#else +#include +#endif + #ifndef _WIN32 #include #else @@ -166,6 +172,7 @@ struct output_state int result_len; int rate; int wav_format; + int padded; pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; @@ -214,11 +221,12 @@ void usage(void) "\t edge: enable lower edge tuning\n" "\t dc: enable dc blocking filter\n" "\t deemp: enable de-emphasis filter\n" - "\t swagc: enable software agc (only for AM)\n" + "\t swagc: enable software agc (only for AM, broken)\n" "\t direct: enable direct sampling\n" "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" "\t wav: generate WAV header\n" + "\t pad: pad output with zeros (broken)\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" @@ -929,15 +937,74 @@ static void *demod_thread_fn(void *arg) return 0; } +#ifndef _WIN32 +static int get_nanotime(struct timespec *ts) +{ + int rv = ENOSYS; +#ifdef __unix__ + rv = clock_gettime(CLOCK_MONOTONIC, ts); +#elif __APPLE__ + struct timeval tv; + + rv = gettimeofday(&tv, NULL); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; +#endif + return rv; +} +#endif + static void *output_thread_fn(void *arg) { + int r = 0; struct output_state *s = arg; + //struct timespec abstime; + struct timespec start_time; + struct timespec now_time; + struct timespec fut_time; + int64_t interval, delay, samples, samples2, i; + samples = 0L; +#ifndef _WIN32 + get_nanotime(&start_time); +#endif while (!do_exit) { - // use timedwait and pad out under runs - safe_cond_wait(&s->ready, &s->ready_m); - pthread_rwlock_rdlock(&s->rw); - fwrite(s->result, 2, s->result_len, s->file); - pthread_rwlock_unlock(&s->rw); + if (!s->padded) { + safe_cond_wait(&s->ready, &s->ready_m); + pthread_rwlock_rdlock(&s->rw); + fwrite(s->result, 2, s->result_len, s->file); + pthread_rwlock_unlock(&s->rw); + continue; + } +#ifndef _WIN32 + /* padding requires output at constant rate */ + /* figure out how to do this with windows HPET */ + pthread_mutex_lock(&s->ready_m); + delay = 1000000000L * (int64_t)s->result_len / (int64_t)s->rate; + get_nanotime(&fut_time); + fut_time.tv_nsec += delay; + r = pthread_cond_timedwait(&s->ready, &s->ready_m, &fut_time); + pthread_mutex_unlock(&s->ready_m); + if (r != ETIMEDOUT) { + pthread_rwlock_rdlock(&s->rw); + fwrite(s->result, 2, s->result_len, s->file); + samples += s->result_len; + pthread_rwlock_unlock(&s->rw); + continue; + } + get_nanotime(&now_time); + interval = now_time.tv_sec - start_time.tv_sec; + interval *= 1000000000L; + interval += (now_time.tv_nsec - start_time.tv_nsec); + samples2 = interval * (int64_t)s->rate / 1000000000L; + samples2 -= samples; + //fprintf(stderr, "%lli %lli %lli\n", delay, interval, samples); + /* there must be a better way to write zeros */ + for (i=0L; ifile); + fputc(0, s->file); + } + samples += samples2; +#endif } return 0; } @@ -1260,6 +1327,8 @@ int main(int argc, char **argv) dongle.offset_tuning = 1;} if (strcmp("wav", optarg) == 0) { output.wav_format = 1;} + if (strcmp("pad", optarg) == 0) { + output.padded = 1;} break; case 'F': demod.downsample_passes = 1; /* truthy placeholder */ From 3223086277fdf0dc6671fcf0dc02fbbbe6171e86 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 20 Aug 2014 11:51:40 -0400 Subject: [PATCH 23/64] rtl_power: fixed size bins, refactoring --- src/rtl_power.c | 246 ++++++++++++++++++++++++++++++------------------ 1 file changed, 156 insertions(+), 90 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 66b144b..3fdf771 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -72,7 +72,7 @@ #define AUTO_GAIN -100 #define BUFFER_DUMP (1<<12) -#define MAXIMUM_RATE 2800000 +#define MAXIMUM_RATE 2400000 #define MINIMUM_RATE 1000000 static volatile int do_exit = 0; @@ -107,12 +107,20 @@ struct tuning_state //pthread_mutex_t buf_mutex; }; +struct channel_solve +/* details required to find optimal tuning */ +{ + int upper, lower, bin_spec; + int bw_wanted, bw_needed; + int bin_e, downsample, downsample_passes; + double crop, crop_tmp; +}; + /* 3000 is enough for 3GHz b/w worst case */ -#define MAX_TUNES 3000 +#define MAX_TUNES 4000 struct tuning_state tunes[MAX_TUNES]; int tune_count = 0; -int boxcar = 1; int comp_fir_size = 0; int peak_hold = 0; @@ -122,10 +130,9 @@ void usage(void) "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 1Hz - 2.8MHz)\n" + "\t valid range for bin_size is 1Hz - 2.8MHz\n" "\t[-i integration_interval (default: 10 seconds)]\n" - "\t (buggy if a full sweep takes longer than the interval)\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" @@ -134,19 +141,20 @@ void usage(void) "\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" + "\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" + "\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 for bins larger than 1MHz)\n" + "\t[-c crop_percent (default: 0%% suggested: 20%%)]\n" + "\t discards data at the edges, 100%% discards everything\n" + "\t has no effect for bins larger than 1MHz\n" + "\t this value is a minimum crop size, more may be discarded\n" "\t[-F fir_size (default: disabled)]\n" - "\t (enables low-leakage downsample filter,\n" - "\t fir_size can be 0 or 9. 0 has bad roll off,\n" - "\t try with '-c 50%%')\n" + "\t enables low-leakage downsample filter,\n" + "\t fir_size can be 0 or 9. 0 has bad roll off,\n" + "\t try with '-c 50%%'\n" "\t[-P enables peak hold (default: off)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\t[-O enable offset tuning (default: off)]\n" @@ -155,16 +163,17 @@ void usage(void) "\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" + "\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" + "\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" + "\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\n" + "\t collect data for one hour and compress it on the fly\n\n" "Convert CSV to a waterfall graphic with:\n" - "\t http://kmkeen.com/tmp/heatmap.py.txt \n"); + " https://github.com/keenerd/rtl-sdr-misc/blob/master/heatmap/heatmap.py \n" + "More examples at http://kmkeen.com/rtl-power/\n"); exit(1); } @@ -424,89 +433,145 @@ void rms_power(struct tuning_state *ts) ts->samples += 1; } -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)? +/* todo, add errors to parse_freq, solve_foo */ + +int parse_frequency(char *arg, struct channel_solve *c) { char *start, *stop, *step; - int i, j, upper, lower, max_size, bw_seen, bw_used, bin_e, buf_len; - int downsample, downsample_passes; - double bin_size; - 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); + c->lower = (int)atofs(start); + c->upper = (int)atofs(stop); + c->bin_spec = (int)atofs(step); stop[-1] = ':'; step[-1] = ':'; - downsample = 1; - downsample_passes = 0; + return 0; +} + +int solve_giant_bins(struct channel_solve *c) +{ + c->bw_wanted = c->bin_spec; + c->bw_needed = c->bin_spec; + tune_count = (c->upper - c->lower) / c->bin_spec; + c->bin_e = 0; + c->crop_tmp = 0; + return 0; +} + +int solve_downsample(struct channel_solve *c, int boxcar) +{ + int scan_size, bins_wanted, bins_needed, ds_next; + double bw; + + scan_size = c->upper - c->lower; + tune_count = 1; + c->bw_wanted = scan_size; + + bins_wanted = (int)ceil((double)scan_size / (double)c->bin_spec); + c->bin_e = (int)ceil(log2(bins_wanted)); + while (1) { + bins_needed = 1 << c->bin_e; + c->crop_tmp = (double)(bins_needed - bins_wanted) / (double)bins_needed; + if (c->crop_tmp >= c->crop) { + break;} + c->bin_e++; + } + + while (1) { + bw = (double)scan_size / (1.0 - c->crop_tmp); + c->bw_needed = (int)bw * c->downsample; + + if (boxcar) { + ds_next = c->downsample + 1; + } else { + ds_next = c->downsample * 2; + } + if (((int)bw * ds_next) > MAXIMUM_RATE) { + break;} + + c->downsample = ds_next; + if (!boxcar) { + c->downsample_passes++;} + } + + return 0; +} + +int solve_hopping(struct channel_solve *c) +{ + int i, scan_size, bins_all, bins_crop, bins_2; + scan_size = c->upper - c->lower; /* evenly sized ranges, as close to MAXIMUM_RATE as possible */ - // todo, replace loop with algebra - for (i=1; i<1500; i++) { - bw_seen = (upper - lower) / i; - bw_used = (int)((double)(bw_seen) / (1.0 - crop)); - if (bw_used > MAXIMUM_RATE) { + for (i=1; ibw_wanted = scan_size / i; + bins_all = scan_size / c->bin_spec; + bins_crop = (int)ceil((double)bins_all / (double)i); + c->bin_e = (int)ceil(log2(bins_crop)); + bins_2 = 1 << c->bin_e; + c->bw_needed = bins_2 * c->bin_spec; + c->crop_tmp = (double)(bins_2 - bins_crop) / (double)bins_2; + if (c->bw_needed > MAXIMUM_RATE) { + continue;} + if (c->crop_tmp < c->crop) { continue;} tune_count = i; break; } - /* unless small bandwidth */ - if (bw_used < MINIMUM_RATE) { - tune_count = 1; - downsample = MAXIMUM_RATE / bw_used; - bw_used = bw_used * downsample; - } - if (!boxcar && downsample > 1) { - downsample_passes = (int)log2(downsample); - downsample = 1 << downsample_passes; - bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop)); - } - /* number of bins is power-of-two, bin size is under limit */ - // todo, replace loop with log2 - for (i=1; i<=21; i++) { - bin_e = i; - bin_size = (double)bw_used / (double)((1<= MINIMUM_RATE) { - bw_seen = max_size; - bw_used = max_size; - tune_count = (upper - lower) / bw_seen; - bin_e = 0; - crop = 0; + return 0; +} + +void frequency_range(char *arg, double crop, int boxcar) +/* flesh out the tunes[] for scanning */ +{ + struct channel_solve c; + struct tuning_state *ts; + int i, j, buf_len; + + parse_frequency(arg, &c); + c.downsample = 1; + c.downsample_passes = 0; + c.crop = crop; + + if (c.bin_spec >= MINIMUM_RATE) { + fprintf(stderr, "Mode: rms power\n"); + solve_giant_bins(&c); + } else if ((c.upper - c.lower) < MINIMUM_RATE) { + fprintf(stderr, "Mode: downsampling\n"); + solve_downsample(&c, boxcar); + } else { + fprintf(stderr, "Mode: normal\n"); + solve_hopping(&c); } + c.crop = c.crop_tmp; + if (tune_count > MAX_TUNES) { fprintf(stderr, "Error: bandwidth too wide.\n"); exit(1); } - buf_len = 2 * (1<freq = lower + i*bw_seen + bw_seen/2; - ts->rate = bw_used; - ts->bin_e = bin_e; + ts->freq = c.lower + i*c.bw_wanted + c.bw_wanted/2; + ts->rate = c.bw_needed; + ts->bin_e = c.bin_e; ts->samples = 0; - ts->crop = crop; - ts->downsample = downsample; - ts->downsample_passes = downsample_passes; - ts->avg = (long*)malloc((1<crop = c.crop; + ts->downsample = c.downsample; + ts->downsample_passes = c.downsample_passes; + ts->avg = (long*)malloc((1<avg) { fprintf(stderr, "Error: malloc.\n"); exit(1); } - for (j=0; j<(1<avg[j] = 0L; } ts->buf8 = (uint8_t*)malloc(buf_len * sizeof(uint8_t)); @@ -518,14 +583,14 @@ void frequency_range(char *arg, double crop) } /* report */ fprintf(stderr, "Number of frequency hops: %i\n", tune_count); - fprintf(stderr, "Dongle bandwidth: %iHz\n", bw_used); - fprintf(stderr, "Downsampling by: %ix\n", downsample); - fprintf(stderr, "Cropping by: %0.2f%%\n", crop*100); - fprintf(stderr, "Total FFT bins: %i\n", tune_count * (1<downsample; ds_p = ts->downsample_passes; - if (boxcar && ds > 1) { + if (ds_p) { /* recursive */ + for (j=0; j < ds_p; j++) { + downsample_iq(fft_buf, buf_len >> j); + } + /* droop compensation */ + if (comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { + generic_fir(fft_buf, buf_len >> j, cic_9_tables[ds_p]); + generic_fir(fft_buf+1, (buf_len >> j)-1, cic_9_tables[ds_p]); + } + } else if (ds > 1) { /* boxcar */ j=2, j2=0; while (j < buf_len) { fft_buf[j2] += fft_buf[j]; @@ -668,15 +742,6 @@ void scanner(void) if (j % (ds*2) == 0) { j2 += 2;} } - } else if (ds_p) { /* recursive */ - for (j=0; j < ds_p; j++) { - downsample_iq(fft_buf, buf_len >> j); - } - /* droop compensation */ - if (comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { - generic_fir(fft_buf, buf_len >> j, cic_9_tables[ds_p]); - generic_fir(fft_buf+1, (buf_len >> j)-1, cic_9_tables[ds_p]); - } } remove_dc(fft_buf, buf_len / ds); remove_dc(fft_buf+1, (buf_len / ds) - 1); @@ -769,6 +834,7 @@ int main(int argc, char **argv) int interval = 10; int fft_threads = 1; int smoothing = 0; + int boxcar = 1; int single = 0; int direct_sampling = 0; int offset_tuning = 0; @@ -868,7 +934,7 @@ int main(int argc, char **argv) exit(1); } - frequency_range(freq_optarg, crop); + frequency_range(freq_optarg, crop, boxcar); if (tune_count == 0) { usage();} From 40bf3cb47784ec1b0cbff96e9a194eb009b05b01 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 21 Aug 2014 23:28:34 -0400 Subject: [PATCH 24/64] rtl_power: adjustable sample rate --- src/rtl_power.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 3fdf771..dbffa36 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -72,8 +72,9 @@ #define AUTO_GAIN -100 #define BUFFER_DUMP (1<<12) -#define MAXIMUM_RATE 2400000 +#define MAXIMUM_RATE 3200000 #define MINIMUM_RATE 1000000 +int target_rate = 2400000; static volatile int do_exit = 0; static rtlsdr_dev_t *dev = NULL; @@ -155,6 +156,7 @@ void usage(void) "\t enables low-leakage downsample filter,\n" "\t fir_size can be 0 or 9. 0 has bad roll off,\n" "\t try with '-c 50%%'\n" + "\t[-r max_sample_rate (default: 2.4M)]\n" "\t[-P enables peak hold (default: off)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\t[-O enable offset tuning (default: off)]\n" @@ -490,7 +492,7 @@ int solve_downsample(struct channel_solve *c, int boxcar) } else { ds_next = c->downsample * 2; } - if (((int)bw * ds_next) > MAXIMUM_RATE) { + if (((int)bw * ds_next) > target_rate) { break;} c->downsample = ds_next; @@ -505,7 +507,7 @@ int solve_hopping(struct channel_solve *c) { int i, scan_size, bins_all, bins_crop, bins_2; scan_size = c->upper - c->lower; - /* evenly sized ranges, as close to MAXIMUM_RATE as possible */ + /* evenly sized ranges, as close to target_rate as possible */ for (i=1; ibw_wanted = scan_size / i; bins_all = scan_size / c->bin_spec; @@ -514,7 +516,7 @@ int solve_hopping(struct channel_solve *c) bins_2 = 1 << c->bin_e; c->bw_needed = bins_2 * c->bin_spec; c->crop_tmp = (double)(bins_2 - bins_crop) / (double)bins_2; - if (c->bw_needed > MAXIMUM_RATE) { + if (c->bw_needed > target_rate) { continue;} if (c->crop_tmp < c->crop) { continue;} @@ -848,7 +850,7 @@ int main(int argc, char **argv) double (*window_fn)(int, int) = rectangle; freq_optarg = ""; - while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1PD:Oh")) != -1) { + while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1PD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size freq_optarg = strdup(optarg); @@ -901,6 +903,9 @@ int main(int argc, char **argv) ppm_error = atoi(optarg); custom_ppm = 1; break; + case 'r': + target_rate = (int)atofs(optarg); + break; case '1': single = 1; break; @@ -934,6 +939,13 @@ int main(int argc, char **argv) exit(1); } + if (target_rate < 2 * MINIMUM_RATE) { + target_rate = 2 * MINIMUM_RATE; + } + if (target_rate > MAXIMUM_RATE) { + target_rate = MAXIMUM_RATE; + } + frequency_range(freq_optarg, crop, boxcar); if (tune_count == 0) { From 6d9bb99eca81a10832936938d06c5eb490094eed Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Fri, 22 Aug 2014 20:10:59 -0400 Subject: [PATCH 25/64] rtl_power: multiple frequency ranges --- src/rtl_power.c | 288 +++++++++++++++++++++++++++++++----------------- 1 file changed, 189 insertions(+), 99 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index dbffa36..5642218 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -74,29 +74,37 @@ #define MAXIMUM_RATE 3200000 #define MINIMUM_RATE 1000000 -int target_rate = 2400000; +#define DEFAULT_TARGET 2400000 + +#define MAXIMUM_FFT 32 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 sine_table +{ + int16_t* Sinewave; + int N_WAVE; + int LOG2_N_WAVE; +}; + +struct sine_table s_tables[MAXIMUM_FFT]; struct tuning_state /* one per tuning range */ { int freq; int rate; + int gain; int bin_e; + int16_t *fft_buf; long *avg; /* length == 2^bin_e */ int samples; int downsample; int downsample_passes; /* for the recursive filter */ + int comp_fir_size; + int peak_hold; double crop; //pthread_rwlock_t avg_lock; //pthread_mutex_t avg_mutex; @@ -106,30 +114,41 @@ struct tuning_state //int *comp_fir; //pthread_rwlock_t buf_lock; //pthread_mutex_t buf_mutex; + int *window_coefs; + struct sine_table *sine; /* points to an element of s_tables */ }; struct channel_solve /* details required to find optimal tuning */ { int upper, lower, bin_spec; - int bw_wanted, bw_needed; + int hops, bw_wanted, bw_needed; int bin_e, downsample, downsample_passes; double crop, crop_tmp; }; +struct misc_settings +{ + int boxcar; + int comp_fir_size; + int peak_hold; + int target_rate; + double crop; + int gain; + double (*window_fn)(int, int); + int smoothing; +}; + /* 3000 is enough for 3GHz b/w worst case */ #define MAX_TUNES 4000 struct tuning_state tunes[MAX_TUNES]; int tune_count = 0; -int comp_fir_size = 0; -int peak_hold = 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" + "Use:\trtl_power -f freq_range [-options] [-f freq2 -opts2] [filename]\n" "\t-f lower:upper:bin_size [Hz]\n" "\t valid range for bin_size is 1Hz - 2.8MHz\n" "\t[-i integration_interval (default: 10 seconds)]\n" @@ -248,15 +267,34 @@ 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 (MAXIMUM_FFT-1)) { + fprintf(stderr, "Maximum FFT is 2^%i\n", MAXIMUM_FFT-1); + exit(1); + } + sine = &s_tables[size]; + if (sine->LOG2_N_WAVE == size) { + return;} + sine->LOG2_N_WAVE = size; + sine->N_WAVE = 1 << sine->LOG2_N_WAVE; + sine->Sinewave = malloc(sizeof(int16_t) * sine->N_WAVE*3/4); + for (i=0; iN_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]); + d = (double)i * 2.0 * M_PI / sine->N_WAVE; + sine->Sinewave[i] = (int)round(32767*sin(d)); + //printf("%i\n", sine->Sinewave[i]); + } +} + +void generate_sine_tables(void) +{ + struct tuning_state *ts; + int i; + for (i=0; i < tune_count; i++) { + ts = &tunes[i]; + sine_table(ts->bin_e); + ts->sine = &s_tables[ts->bin_e]; + ts->fft_buf = malloc(ts->buf_len * sizeof(int16_t)); } } @@ -268,13 +306,13 @@ inline int16_t FIX_MPY(int16_t a, int16_t b) return (c >> 1) + b; } -int fix_fft(int16_t iq[], int m) +int fix_fft(int16_t iq[], int m, struct sine_table *sine) /* 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) + if (n > sine->N_WAVE) {return -1;} mr = 0; nn = n - 1; @@ -296,14 +334,14 @@ int fix_fft(int16_t iq[], int m) iq[2*mr+1] = ti; } l = 1; - k = LOG2_N_WAVE-1; + k = sine->LOG2_N_WAVE-1; while (l < n) { shift = 1; istep = l << 1; for (m=0; mSinewave[j+sine->N_WAVE/4]; + wi = -sine->Sinewave[j]; if (shift) { wr >>= 1; wi >>= 1;} for (i=m; ipeak_hold) { ts->avg[0] += p; } else { ts->avg[0] = MAX(ts->avg[0], p); @@ -458,19 +496,19 @@ int solve_giant_bins(struct channel_solve *c) { c->bw_wanted = c->bin_spec; c->bw_needed = c->bin_spec; - tune_count = (c->upper - c->lower) / c->bin_spec; + c->hops = (c->upper - c->lower) / c->bin_spec; c->bin_e = 0; c->crop_tmp = 0; return 0; } -int solve_downsample(struct channel_solve *c, int boxcar) +int solve_downsample(struct channel_solve *c, int target_rate, int boxcar) { int scan_size, bins_wanted, bins_needed, ds_next; double bw; scan_size = c->upper - c->lower; - tune_count = 1; + c->hops = 1; c->bw_wanted = scan_size; bins_wanted = (int)ceil((double)scan_size / (double)c->bin_spec); @@ -503,7 +541,7 @@ int solve_downsample(struct channel_solve *c, int boxcar) return 0; } -int solve_hopping(struct channel_solve *c) +int solve_hopping(struct channel_solve *c, int target_rate) { int i, scan_size, bins_all, bins_crop, bins_2; scan_size = c->upper - c->lower; @@ -520,37 +558,50 @@ int solve_hopping(struct channel_solve *c) continue;} if (c->crop_tmp < c->crop) { continue;} - tune_count = i; + c->hops = i; break; } return 0; } -void frequency_range(char *arg, double crop, int boxcar) +void frequency_range(char *arg, struct misc_settings *ms) /* flesh out the tunes[] for scanning */ { struct channel_solve c; struct tuning_state *ts; - int i, j, buf_len; + int i, j, buf_len, length; + fprintf(stderr, "Range: %s\n", arg); parse_frequency(arg, &c); c.downsample = 1; c.downsample_passes = 0; - c.crop = crop; + c.crop = ms->crop; + + if (ms->target_rate < 2 * MINIMUM_RATE) { + ms->target_rate = 2 * MINIMUM_RATE; + } + if (ms->target_rate > MAXIMUM_RATE) { + ms->target_rate = MAXIMUM_RATE; + } + if ((ms->crop < 0.0) || (ms->crop > 1.0)) { + fprintf(stderr, "Crop value outside of 0 to 1.\n"); + exit(1); + } + if (c.bin_spec >= MINIMUM_RATE) { fprintf(stderr, "Mode: rms power\n"); solve_giant_bins(&c); } else if ((c.upper - c.lower) < MINIMUM_RATE) { fprintf(stderr, "Mode: downsampling\n"); - solve_downsample(&c, boxcar); + solve_downsample(&c, ms->target_rate, ms->boxcar); } else { fprintf(stderr, "Mode: normal\n"); - solve_hopping(&c); + solve_hopping(&c, ms->target_rate); } c.crop = c.crop_tmp; - if (tune_count > MAX_TUNES) { + if ((tune_count+c.hops) > MAX_TUNES) { fprintf(stderr, "Error: bandwidth too wide.\n"); exit(1); } @@ -559,15 +610,18 @@ void frequency_range(char *arg, double crop, int boxcar) buf_len = DEFAULT_BUF_LENGTH; } /* build the array */ - for (i=0; ifreq = c.lower + i*c.bw_wanted + c.bw_wanted/2; ts->rate = c.bw_needed; + ts->gain = ms->gain; ts->bin_e = c.bin_e; ts->samples = 0; ts->crop = c.crop; ts->downsample = c.downsample; ts->downsample_passes = c.downsample_passes; + ts->comp_fir_size = ms->comp_fir_size; + ts->peak_hold = ms->peak_hold; ts->avg = (long*)malloc((1<avg) { fprintf(stderr, "Error: malloc.\n"); @@ -582,23 +636,33 @@ void frequency_range(char *arg, double crop, int boxcar) exit(1); } ts->buf_len = buf_len; + length = 1 << c.bin_e; + ts->window_coefs = malloc(length * sizeof(int)); + for (j=0; jwindow_coefs[j] = (int)(256*ms->window_fn(j, length)); + } } + tune_count += c.hops; /* report */ - fprintf(stderr, "Number of frequency hops: %i\n", tune_count); + fprintf(stderr, "Number of frequency hops: %i\n", c.hops); fprintf(stderr, "Dongle bandwidth: %iHz\n", c.bw_needed); fprintf(stderr, "Downsampling by: %ix\n", c.downsample); fprintf(stderr, "Cropping by: %0.2f%%\n", c.crop*100); - fprintf(stderr, "Total FFT bins: %i\n", tune_count * (1<= 2) {return;} ts = &tunes[i]; - f = (int)rtlsdr_get_center_freq(dev); - if (f != ts->freq) { - retune(dev, ts->freq);} + fft_buf = ts->fft_buf; + regain(dev, ts->gain); + rerate(dev, ts->rate); + 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");} @@ -729,7 +822,7 @@ void scanner(void) downsample_iq(fft_buf, buf_len >> j); } /* droop compensation */ - if (comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { + if (ts->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { generic_fir(fft_buf, buf_len >> j, cic_9_tables[ds_p]); generic_fir(fft_buf+1, (buf_len >> j)-1, cic_9_tables[ds_p]); } @@ -752,16 +845,16 @@ void scanner(void) // todo, let rect skip this for (j=0; jwindow_coefs[j]); //w /= (int32_t)(ds); fft_buf[offset+j*2] = (int16_t)w; w = (int32_t)fft_buf[offset+j*2+1]; - w *= (int32_t)(window_coefs[j]); + w *= (int32_t)(ts->window_coefs[j]); //w /= (int32_t)(ds); fft_buf[offset+j*2+1] = (int16_t)w; } - fix_fft(fft_buf+offset, bin_e); - if (!peak_hold) { + fix_fft(fft_buf+offset, bin_e, ts->sine); + if (!ts->peak_hold) { for (j=0; javg[j] += real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]); } @@ -820,39 +913,49 @@ void csv_dbm(struct tuning_state *ts) ts->samples = 0; } +void init_misc(struct misc_settings *ms) +{ + ms->target_rate = DEFAULT_TARGET; + ms->boxcar = 1; + ms->comp_fir_size = 0; + ms->crop = 0.0; + ms->gain = AUTO_GAIN; + ms->window_fn = rectangle; + ms->smoothing = 0; +} + int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif char *filename = NULL; - int i, length, r, opt, wb_mode = 0; + int i, r, opt, wb_mode = 0; int f_set = 0; - int gain = AUTO_GAIN; // tenths of a dB int dev_index = 0; int dev_given = 0; int ppm_error = 0; int custom_ppm = 0; int interval = 10; int fft_threads = 1; - int smoothing = 0; - int boxcar = 1; int single = 0; int direct_sampling = 0; int offset_tuning = 0; - double crop = 0.0; 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; + struct misc_settings ms; freq_optarg = ""; + init_misc(&ms); while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1PD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size + if (f_set) { + frequency_range(freq_optarg, &ms);} freq_optarg = strdup(optarg); f_set = 1; break; @@ -861,10 +964,10 @@ int main(int argc, char **argv) dev_given = 1; break; case 'g': - gain = (int)(atof(optarg) * 10); + ms.gain = (int)(atof(optarg) * 10); break; case 'c': - crop = atofp(optarg); + ms.crop = atofp(optarg); break; case 'i': interval = (int)round(atoft(optarg)); @@ -874,27 +977,27 @@ int main(int argc, char **argv) break; case 's': if (strcmp("avg", optarg) == 0) { - smoothing = 0;} + ms.smoothing = 0;} if (strcmp("iir", optarg) == 0) { - smoothing = 1;} + ms.smoothing = 1;} break; case 'w': if (strcmp("rectangle", optarg) == 0) { - window_fn = rectangle;} + ms.window_fn = rectangle;} if (strcmp("hamming", optarg) == 0) { - window_fn = hamming;} + ms.window_fn = hamming;} if (strcmp("blackman", optarg) == 0) { - window_fn = blackman;} + ms.window_fn = blackman;} if (strcmp("blackman-harris", optarg) == 0) { - window_fn = blackman_harris;} + ms.window_fn = blackman_harris;} if (strcmp("hann-poisson", optarg) == 0) { - window_fn = hann_poisson;} + ms.window_fn = hann_poisson;} if (strcmp("youssef", optarg) == 0) { - window_fn = youssef;} + ms.window_fn = youssef;} if (strcmp("kaiser", optarg) == 0) { - window_fn = kaiser;} + ms.window_fn = kaiser;} if (strcmp("bartlett", optarg) == 0) { - window_fn = bartlett;} + ms.window_fn = bartlett;} break; case 't': fft_threads = atoi(optarg); @@ -904,13 +1007,13 @@ int main(int argc, char **argv) custom_ppm = 1; break; case 'r': - target_rate = (int)atofs(optarg); + ms.target_rate = (int)atofs(optarg); break; case '1': single = 1; break; case 'P': - peak_hold = 1; + ms.peak_hold = 1; break; case 'D': direct_sampling = atoi(optarg); @@ -919,8 +1022,8 @@ int main(int argc, char **argv) offset_tuning = 1; break; case 'F': - boxcar = 0; - comp_fir_size = atoi(optarg); + ms.boxcar = 0; + ms.comp_fir_size = atoi(optarg); break; case 'h': default: @@ -934,19 +1037,7 @@ int main(int argc, char **argv) exit(1); } - if ((crop < 0.0) || (crop > 1.0)) { - fprintf(stderr, "Crop value outside of 0 to 1.\n"); - exit(1); - } - - if (target_rate < 2 * MINIMUM_RATE) { - target_rate = 2 * MINIMUM_RATE; - } - if (target_rate > MAXIMUM_RATE) { - target_rate = MAXIMUM_RATE; - } - - frequency_range(freq_optarg, crop, boxcar); + frequency_range(freq_optarg, &ms); if (tune_count == 0) { usage();} @@ -997,11 +1088,16 @@ int main(int argc, char **argv) } /* Set the tuner gain */ - if (gain == AUTO_GAIN) { + for (i=0; i Date: Sat, 23 Aug 2014 08:33:43 -0400 Subject: [PATCH 26/64] rtl_power: linear output --- src/rtl_power.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 5642218..61d29ce 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -105,6 +105,7 @@ struct tuning_state int downsample_passes; /* for the recursive filter */ int comp_fir_size; int peak_hold; + int linear; double crop; //pthread_rwlock_t avg_lock; //pthread_mutex_t avg_mutex; @@ -132,6 +133,7 @@ struct misc_settings int boxcar; int comp_fir_size; int peak_hold; + int linear; int target_rate; double crop; int gain; @@ -176,7 +178,8 @@ void usage(void) "\t fir_size can be 0 or 9. 0 has bad roll off,\n" "\t try with '-c 50%%'\n" "\t[-r max_sample_rate (default: 2.4M)]\n" - "\t[-P enables peak hold (default: off)]\n" + "\t[-P enables peak hold (default: off/averaging)]\n" + "\t[-L enable linear output (default: off/dB)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\t[-O enable offset tuning (default: off)]\n" "\n" @@ -622,6 +625,7 @@ void frequency_range(char *arg, struct misc_settings *ms) ts->downsample_passes = c.downsample_passes; ts->comp_fir_size = ms->comp_fir_size; ts->peak_hold = ms->peak_hold; + ts->linear = ms->linear; ts->avg = (long*)malloc((1<avg) { fprintf(stderr, "Error: malloc.\n"); @@ -873,6 +877,7 @@ void csv_dbm(struct tuning_state *ts) int i, len, ds, i1, i2, bw2, bin_count; long tmp; double dbm; + char *sep = ", "; len = 1 << ts->bin_e; ds = ts->downsample; /* fix FFT stuff quirks */ @@ -895,18 +900,19 @@ void csv_dbm(struct tuning_state *ts) i1 = 0 + (int)((double)len * ts->crop * 0.5); i2 = (len-1) - (int)((double)len * ts->crop * 0.5); for (i=i1; i<=i2; i++) { + if (i == i2) { + sep = "\n"; + } dbm = (double)ts->avg[i]; dbm /= (double)ts->rate; dbm /= (double)ts->samples; - dbm = 10 * log10(dbm); - fprintf(file, "%.2f, ", dbm); + if (ts->linear) { + fprintf(file, "%.5g%s", dbm, sep); + } else { + dbm = 10 * log10(dbm); + fprintf(file, "%.2f%s", dbm, sep); + } } - 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; iavg[i] = 0L; } @@ -922,6 +928,8 @@ void init_misc(struct misc_settings *ms) ms->gain = AUTO_GAIN; ms->window_fn = rectangle; ms->smoothing = 0; + ms->peak_hold = 0; + ms->linear = 0; } int main(int argc, char **argv) @@ -951,7 +959,7 @@ int main(int argc, char **argv) freq_optarg = ""; init_misc(&ms); - while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1PD:Oh")) != -1) { + while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1PLD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size if (f_set) { @@ -1015,6 +1023,9 @@ int main(int argc, char **argv) case 'P': ms.peak_hold = 1; break; + case 'L': + ms.linear = 1; + break; case 'D': direct_sampling = atoi(optarg); break; From f2328161ea9b3d88d5f3b1d76540d9afe7a22c84 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 23 Aug 2014 22:08:23 +0100 Subject: [PATCH 27/64] r82xx: improved tuner precision Improve tuner precision by calculating the VCO divisor at full precision, not at kHz resolution. Also replace the manual divison loop with a simpler fixed-point calculation. --- src/tuner_r82xx.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index ada6c28..e91198e 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -456,11 +456,11 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) int rc, i; unsigned sleep_time = 10000; uint64_t vco_freq; - uint32_t vco_fra; /* VCO contribution by SDM (kHz) */ - uint32_t vco_min = 1770000; - uint32_t vco_max = vco_min * 2; - uint32_t freq_khz, pll_ref, pll_ref_khz; - uint16_t sdm = 0; + uint64_t vco_div; + uint32_t vco_min = 1770000; /* kHz */ + uint32_t vco_max = vco_min * 2; /* kHz */ + uint32_t freq_khz, pll_ref; + uint32_t sdm = 0; uint8_t mix_div = 2; uint8_t div_buf = 0; uint8_t div_num = 0; @@ -474,7 +474,6 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) /* Frequency in kHz */ freq_khz = (freq + 500) / 1000; pll_ref = priv->cfg->xtal; - pll_ref_khz = (priv->cfg->xtal + 500) / 1000; rc = r82xx_write_reg_mask(priv, 0x10, refdiv2, 0x10); if (rc < 0) @@ -527,8 +526,21 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; vco_freq = (uint64_t)freq * (uint64_t)mix_div; - nint = vco_freq / (2 * pll_ref); - vco_fra = (vco_freq - 2 * pll_ref * nint) / 1000; + + /* + * We want to approximate: + * vco_freq / (2 * pll_ref) + * in the form: + * nint + sdm/65536 + * where nint,sdm are integers and 0 < nint, 0 <= sdm < 65536 + * Scaling to fixed point and rounding: + * vco_div = 65536*(nint + sdm/65536) = int( 0.5 + 65536 * vco_freq / (2 * pll_ref) ) + * vco_div = 65536*nint + sdm = int( (pll_ref + 65536 * vco_freq) / (2 * pll_ref) ) + */ + + vco_div = (pll_ref + 65536 * vco_freq) / (2 * pll_ref); + nint = (uint32_t) (vco_div / 65536); + sdm = (uint32_t) (vco_div % 65536); if (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) { fprintf(stderr, "[R828D] No valid PLL values for %u Hz!\n", freq); @@ -546,7 +558,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; /* pw_sdm */ - if (!vco_fra) + if (sdm == 0) val = 0x08; else val = 0x00; @@ -558,9 +570,6 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) if (rc < 0) return rc; - /* sdm calculator */ - sdm = (((vco_freq<<16)+pll_ref) / (2*pll_ref)) & 0xFFFF; - //fprintf(stderr, "LO: %u kHz, MixDiv: %u, PLLDiv: %u, VCO %u kHz, SDM: %u \n", (uint32_t)(freq/1000), mix_div, nint, (uint32_t)(vco_freq/1000), sdm); rc = r82xx_write_reg(priv, 0x16, sdm >> 8); From f9ce8bd21f62924c1d765c7f369e3a765e0026de Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 25 Aug 2014 13:36:00 -0400 Subject: [PATCH 28/64] rtl_power: bugfix for odd bin numbers --- src/rtl_power.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 61d29ce..5d43986 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -107,6 +107,7 @@ struct tuning_state int peak_hold; int linear; double crop; + int crop_i1, crop_i2; //pthread_rwlock_t avg_lock; //pthread_mutex_t avg_mutex; /* having the iq buffer here is wasteful, but will avoid contention */ @@ -572,7 +573,7 @@ void frequency_range(char *arg, struct misc_settings *ms) { struct channel_solve c; struct tuning_state *ts; - int i, j, buf_len, length; + int i, j, buf_len, length, final_bins; fprintf(stderr, "Range: %s\n", arg); parse_frequency(arg, &c); @@ -645,6 +646,9 @@ void frequency_range(char *arg, struct misc_settings *ms) for (j=0; jwindow_coefs[j] = (int)(256*ms->window_fn(j, length)); } + final_bins = c.bw_wanted / c.bin_spec; + ts->crop_i1 = length/2 - final_bins/2; + ts->crop_i2 = ts->crop_i1 + final_bins - 1; } tune_count += c.hops; /* report */ @@ -874,7 +878,7 @@ void scanner(void) void csv_dbm(struct tuning_state *ts) { - int i, len, ds, i1, i2, bw2, bin_count; + int i, len, ds, bw2, bin_count; long tmp; double dbm; char *sep = ", "; @@ -897,10 +901,8 @@ void csv_dbm(struct tuning_state *ts) fprintf(file, "%i, %i, %.2f, %i, ", ts->freq - bw2, ts->freq + bw2, (double)ts->rate / (double)(len*ds), ts->samples); // something seems off with the dbm math - i1 = 0 + (int)((double)len * ts->crop * 0.5); - i2 = (len-1) - (int)((double)len * ts->crop * 0.5); - for (i=i1; i<=i2; i++) { - if (i == i2) { + for (i=ts->crop_i1; i<=ts->crop_i2; i++) { + if (i == ts->crop_i2) { sep = "\n"; } dbm = (double)ts->avg[i]; From ecae3d5109e07cc240a4aee45c83e801ec8d857d Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 25 Aug 2014 21:28:29 -0400 Subject: [PATCH 29/64] rtl_power: fix bugs with bin counting --- src/rtl_power.c | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 5d43986..feef236 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -106,8 +106,10 @@ struct tuning_state int comp_fir_size; int peak_hold; int linear; + int bin_spec; double crop; int crop_i1, crop_i2; + int freq_low, freq_high; //pthread_rwlock_t avg_lock; //pthread_mutex_t avg_mutex; /* having the iq buffer here is wasteful, but will avoid contention */ @@ -573,7 +575,8 @@ void frequency_range(char *arg, struct misc_settings *ms) { struct channel_solve c; struct tuning_state *ts; - int i, j, buf_len, length, final_bins; + int i, j, buf_len, length, hop_bins, logged_bins, planned_bins; + int lower_edge, actual_bw, upper_perfect, remainder; fprintf(stderr, "Range: %s\n", arg); parse_frequency(arg, &c); @@ -614,13 +617,17 @@ void frequency_range(char *arg, struct misc_settings *ms) buf_len = DEFAULT_BUF_LENGTH; } /* build the array */ + logged_bins = 0; + lower_edge = c.lower; + planned_bins = (c.upper - c.lower) / c.bin_spec; for (i=0; i < c.hops; i++) { ts = &tunes[tune_count + i]; - ts->freq = c.lower + i*c.bw_wanted + c.bw_wanted/2; + /* copy common values */ ts->rate = c.bw_needed; ts->gain = ms->gain; ts->bin_e = c.bin_e; ts->samples = 0; + ts->bin_spec = c.bin_spec; ts->crop = c.crop; ts->downsample = c.downsample; ts->downsample_passes = c.downsample_passes; @@ -646,9 +653,29 @@ void frequency_range(char *arg, struct misc_settings *ms) for (j=0; jwindow_coefs[j] = (int)(256*ms->window_fn(j, length)); } - final_bins = c.bw_wanted / c.bin_spec; - ts->crop_i1 = length/2 - final_bins/2; - ts->crop_i2 = ts->crop_i1 + final_bins - 1; + /* calculate unique values */ + ts->freq_low = lower_edge; + hop_bins = c.bw_wanted / c.bin_spec; + actual_bw = hop_bins * c.bin_spec; + ts->freq_high = lower_edge + actual_bw; + upper_perfect = c.lower + (i+1) * c.bw_wanted; + if (ts->freq_high + c.bin_spec <= upper_perfect) { + hop_bins += 1; + actual_bw = hop_bins * c.bin_spec; + ts->freq_high = lower_edge + actual_bw; + } + remainder = planned_bins - logged_bins - hop_bins; + if (i == c.hops-1 && remainder > 0) { + hop_bins += remainder; + actual_bw = hop_bins * c.bin_spec; + ts->freq_high = lower_edge + actual_bw; + } + logged_bins += hop_bins; + ts->crop_i1 = (length - hop_bins) / 2; + ts->crop_i2 = ts->crop_i1 + hop_bins - 1; + ts->freq = (lower_edge - ts->crop_i1 * c.bin_spec) + c.bw_needed/2; + /* prep for next hop */ + lower_edge = ts->freq_high; } tune_count += c.hops; /* report */ @@ -657,8 +684,7 @@ void frequency_range(char *arg, struct misc_settings *ms) fprintf(stderr, "Downsampling by: %ix\n", c.downsample); fprintf(stderr, "Cropping by: %0.2f%%\n", c.crop*100); fprintf(stderr, "Total FFT bins: %i\n", c.hops * (1<crop)); - bw2 = (int)(((double)ts->rate * (double)bin_count) / (len * 2 * ds)); - fprintf(file, "%i, %i, %.2f, %i, ", ts->freq - bw2, ts->freq + bw2, - (double)ts->rate / (double)(len*ds), ts->samples); + fprintf(file, "%i, %i, %i, %i, ", ts->freq_low, ts->freq_high, + ts->bin_spec, ts->samples); // something seems off with the dbm math for (i=ts->crop_i1; i<=ts->crop_i2; i++) { if (i == ts->crop_i2) { From ebb5f2a4cfa995efa2420853e36d96697e02ef63 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 25 Aug 2014 21:33:12 -0400 Subject: [PATCH 30/64] rtl_fm: bugfix from M. Curtis --- src/rtl_fm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 5b34b62..7e29600 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -1362,6 +1362,7 @@ int main(int argc, char **argv) demod.rate_in = 170000; demod.rate_out = 170000; demod.rate_out2 = 32000; + output.rate = 32000; demod.custom_atan = 1; //demod.post_downsample = 4; demod.deemph = 1; From e23b92c9102f3e68c59178a097014eafac4dbc83 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 26 Aug 2014 17:22:36 -0400 Subject: [PATCH 31/64] rtl_fm: in-place demodulators --- src/rtl_fm.c | 102 ++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 7e29600..dc966c2 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -104,7 +104,7 @@ struct dongle_state uint32_t freq; uint32_t rate; int gain; - uint16_t buf16[MAXIMUM_BUF_LENGTH]; + int16_t buf16[MAXIMUM_BUF_LENGTH]; uint32_t buf_len; int ppm_error; int offset_tuning; @@ -132,10 +132,8 @@ struct demod_state int lp_len; int16_t lp_i_hist[10][6]; int16_t lp_q_hist[10][6]; - int16_t result[MAXIMUM_BUF_LENGTH]; int16_t droop_i_hist[9]; int16_t droop_q_hist[9]; - int result_len; int rate_in; int rate_out; int rate_out2; @@ -364,22 +362,23 @@ void low_pass_real(struct demod_state *s) /* simple square window FIR */ // add support for upsampling? { + int16_t *lp = s->lowpassed; int i=0, i2=0; int fast = (int)s->rate_out; int slow = s->rate_out2; - while (i < s->result_len) { - s->now_lpr += s->result[i]; + while (i < s->lp_len) { + s->now_lpr += lp[i]; i++; s->prev_lpr_index += slow; if (s->prev_lpr_index < fast) { continue; } - s->result[i2] = (int16_t)(s->now_lpr / (fast/slow)); + lp[i2] = (int16_t)(s->now_lpr / (fast/slow)); s->prev_lpr_index -= fast; s->now_lpr = 0; i2 += 1; } - s->result_len = i2; + s->lp_len = i2; } void fifth_order(int16_t *data, int length, int16_t *hist) @@ -557,35 +556,32 @@ int esbensen(int ar, int aj, int br, int bj) void fm_demod(struct demod_state *fm) { - int i, pcm; + int i, pcm = 0; int16_t *lp = fm->lowpassed; - pcm = polar_discriminant(lp[0], lp[1], - fm->pre_r, fm->pre_j); - fm->result[0] = (int16_t)pcm; - for (i = 2; i < (fm->lp_len-1); i += 2) { + int16_t pr = fm->pre_r; + int16_t pj = fm->pre_j; + for (i = 0; i < (fm->lp_len-1); i += 2) { switch (fm->custom_atan) { case 0: - pcm = polar_discriminant(lp[i], lp[i+1], - lp[i-2], lp[i-1]); + pcm = polar_discriminant(lp[i], lp[i+1], pr, pj); break; case 1: - pcm = polar_disc_fast(lp[i], lp[i+1], - lp[i-2], lp[i-1]); + pcm = polar_disc_fast(lp[i], lp[i+1], pr, pj); break; case 2: - pcm = polar_disc_lut(lp[i], lp[i+1], - lp[i-2], lp[i-1]); + pcm = polar_disc_lut(lp[i], lp[i+1], pr, pj); break; case 3: - pcm = esbensen(lp[i], lp[i+1], - lp[i-2], lp[i-1]); + pcm = esbensen(lp[i], lp[i+1], pr, pj); break; } - fm->result[i/2] = (int16_t)pcm; + pr = lp[i]; + pj = lp[i+1]; + fm->lowpassed[i/2] = (int16_t)pcm; } - fm->pre_r = lp[fm->lp_len - 2]; - fm->pre_j = lp[fm->lp_len - 1]; - fm->result_len = fm->lp_len/2; + fm->pre_r = pr; + fm->pre_j = pj; + fm->lp_len = fm->lp_len / 2; } void am_demod(struct demod_state *fm) @@ -593,15 +589,14 @@ void am_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { // hypot uses floats but won't overflow //r[i/2] = (int16_t)hypot(lp[i], lp[i+1]); pcm = lp[i] * lp[i]; pcm += lp[i+1] * lp[i+1]; - r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; + lp[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; } - fm->result_len = fm->lp_len/2; + fm->lp_len = fm->lp_len / 2; // lowpass? (3khz) highpass? (dc) } @@ -609,49 +604,44 @@ void usb_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { pcm = lp[i] + lp[i+1]; - r[i/2] = (int16_t)pcm * fm->output_scale; + lp[i/2] = (int16_t)pcm * fm->output_scale; } - fm->result_len = fm->lp_len/2; + fm->lp_len = fm->lp_len / 2; } void lsb_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { pcm = lp[i] - lp[i+1]; - r[i/2] = (int16_t)pcm * fm->output_scale; + lp[i/2] = (int16_t)pcm * fm->output_scale; } - fm->result_len = fm->lp_len/2; + fm->lp_len = fm->lp_len / 2; } void raw_demod(struct demod_state *fm) { - int i; - for (i = 0; i < fm->lp_len; i++) { - fm->result[i] = (int16_t)fm->lowpassed[i]; - } - fm->result_len = fm->lp_len; + return; } void deemph_filter(struct demod_state *fm) { - static int avg; // cheating... + static int avg; // cheating, not threadsafe int i, d; + int16_t *lp = fm->lowpassed; // de-emph IIR // avg = avg * (1 - alpha) + sample * alpha; - for (i = 0; i < fm->result_len; i++) { - d = fm->result[i] - avg; + for (i = 0; i < fm->lp_len; i++) { + d = lp[i] - avg; if (d > 0) { avg += (d + fm->deemph_a/2) / fm->deemph_a; } else { avg += (d - fm->deemph_a/2) / fm->deemph_a; } - fm->result[i] = (int16_t)avg; + lp[i] = (int16_t)avg; } } @@ -659,13 +649,14 @@ void dc_block_filter(struct demod_state *fm) { int i, avg; int64_t sum = 0; - for (i=0; i < fm->result_len; i++) { - sum += fm->result[i]; + int16_t *lp = fm->lowpassed; + for (i=0; i < fm->lp_len; i++) { + sum += lp[i]; } - avg = sum / fm->result_len; + avg = sum / fm->lp_len; avg = (avg + fm->dc_avg * 9) / 10; - for (i=0; i < fm->result_len; i++) { - fm->result[i] -= avg; + for (i=0; i < fm->lp_len; i++) { + lp[i] -= avg; } fm->dc_avg = avg; } @@ -797,9 +788,10 @@ void software_agc(struct demod_state *d) int i = 0; int output; struct agc_state *agc = d->agc; + int16_t *lp = d->lowpassed; - for (i=0; i < d->result_len; i++) { - output = (int)((int64_t)d->result[i] * agc->gain_num / agc->gain_den); + for (i=0; i < d->lp_len; i++) { + output = (int)((int64_t)lp[i] * agc->gain_num / agc->gain_den); if (abs(output) < agc->peak_target) { agc->gain_num += agc->decay_step; @@ -813,7 +805,7 @@ void software_agc(struct demod_state *d) agc->gain_num = agc->gain_max;} agc->gain_int = (int)(agc->gain_num / agc->gain_den); - d->result[i] = output; + lp[i] = output; } } @@ -860,21 +852,21 @@ void full_demod(struct demod_state *d) if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { d->agc->gain_num = d->agc->gain_den; } - d->mode_demod(d); /* lowpassed -> result */ + d->mode_demod(d); /* lowpassed -> lowpassed */ if (d->mode_demod == &raw_demod) { return; } /* todo, fm noise squelch */ // use nicer filter here too? if (d->post_downsample > 1) { - d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);} + d->lp_len = low_pass_simple(d->lowpassed, d->lp_len, d->post_downsample);} if (d->deemph) { deemph_filter(d);} if (d->dc_block) { dc_block_filter(d);} if (d->rate_out2 > 0) { low_pass_real(d); - //arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out); + //arbitrary_resample(d->lowpassed, d->lowpassed, d->lp_len, d->lp_len * d->rate_out2 / d->rate_out); } } @@ -929,8 +921,8 @@ static void *demod_thread_fn(void *arg) continue; } pthread_rwlock_wrlock(&o->rw); - memcpy(o->result, d->result, 2*d->result_len); - o->result_len = d->result_len; + memcpy(o->result, d->lowpassed, 2*d->lp_len); + o->result_len = d->lp_len; pthread_rwlock_unlock(&o->rw); safe_cond_signal(&o->ready, &o->ready_m); } From 34ab4ac13a61ee0b8b8fb4338fecbb249fc6244e Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 26 Aug 2014 18:48:31 -0400 Subject: [PATCH 32/64] rtl_fm: no-copy architecture --- src/rtl_fm.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index dc966c2..c11e496 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -95,6 +95,11 @@ static int *atan_lut = NULL; static int atan_lut_size = 131072; /* 512 KB */ static int atan_lut_coef = 8; +// rewrite as dynamic and thread-safe for multi demod/dongle +#define SHARED_SIZE 4 +int16_t shared_samples[SHARED_SIZE][MAXIMUM_BUF_LENGTH]; +int ss_busy[SHARED_SIZE] = {0}; + struct dongle_state { int exit_flag; @@ -104,7 +109,7 @@ struct dongle_state uint32_t freq; uint32_t rate; int gain; - int16_t buf16[MAXIMUM_BUF_LENGTH]; + int16_t *buf16; uint32_t buf_len; int ppm_error; int offset_tuning; @@ -128,7 +133,7 @@ struct demod_state { int exit_flag; pthread_t thread; - int16_t lowpassed[MAXIMUM_BUF_LENGTH]; + int16_t *lowpassed; int lp_len; int16_t lp_i_hist[10][6]; int16_t lp_q_hist[10][6]; @@ -166,7 +171,7 @@ struct output_state pthread_t thread; FILE *file; char *filename; - int16_t result[MAXIMUM_BUF_LENGTH]; + int16_t *result; int result_len; int rate; int wav_format; @@ -870,6 +875,31 @@ void full_demod(struct demod_state *d) } } +int16_t* mark_shared_buffer(void) +{ + int i = 0; + while (1) { + i = (i+1) % SHARED_SIZE; + if (ss_busy[i] == 0) { + ss_busy[i] = 1; + return shared_samples[i]; + } + } + return NULL; +} + +int unmark_shared_buffer(int16_t *buf) +{ + int i; + for (i=0; ibuf16[i] = (int16_t)buf[i] - 127;} pthread_rwlock_wrlock(&d->rw); - memcpy(d->lowpassed, s->buf16, 2*len); + d->lowpassed = s->buf16; d->lp_len = len; pthread_rwlock_unlock(&d->rw); safe_cond_signal(&d->ready, &d->ready_m); + s->buf16 = mark_shared_buffer(); } static void *dongle_thread_fn(void *arg) @@ -921,7 +952,7 @@ static void *demod_thread_fn(void *arg) continue; } pthread_rwlock_wrlock(&o->rw); - memcpy(o->result, d->lowpassed, 2*d->lp_len); + o->result = d->lowpassed; o->result_len = d->lp_len; pthread_rwlock_unlock(&o->rw); safe_cond_signal(&o->ready, &o->ready_m); @@ -964,6 +995,7 @@ static void *output_thread_fn(void *arg) safe_cond_wait(&s->ready, &s->ready_m); pthread_rwlock_rdlock(&s->rw); fwrite(s->result, 2, s->result_len, s->file); + unmark_shared_buffer(s->result); pthread_rwlock_unlock(&s->rw); continue; } @@ -979,6 +1011,7 @@ static void *output_thread_fn(void *arg) if (r != ETIMEDOUT) { pthread_rwlock_rdlock(&s->rw); fwrite(s->result, 2, s->result_len, s->file); + unmark_shared_buffer(s->result); samples += s->result_len; pthread_rwlock_unlock(&s->rw); continue; @@ -1100,6 +1133,7 @@ void dongle_init(struct dongle_state *s) s->direct_sampling = 0; s->offset_tuning = 0; s->demod_target = &demod; + s->buf16 = mark_shared_buffer(); } void demod_init(struct demod_state *s) @@ -1129,6 +1163,7 @@ void demod_init(struct demod_state *s) pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); s->output_target = &output; + s->lowpassed = NULL; } void demod_cleanup(struct demod_state *s) @@ -1144,6 +1179,7 @@ void output_init(struct output_state *s) pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); + s->result = NULL; } void output_cleanup(struct output_state *s) From 6d5780388bd48dd20a63d365dcd7c3aa8a6b43bc Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 26 Aug 2014 22:32:30 +0100 Subject: [PATCH 33/64] r82xx: remove incorrect error from reg caching --- src/tuner_r82xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index e91198e..e4a1232 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -344,7 +344,7 @@ static int r82xx_write_batch_sync(struct r82xx_priv *priv) return -1; priv->reg_batch = 0; if (priv->reg_low > priv->reg_high) - return -1; + return 0; /* No registers were changed */ offset = priv->reg_low - REG_SHADOW_START; len = priv->reg_high - priv->reg_low + 1; rc = r82xx_write(priv, priv->reg_low, priv->regs+offset, len); From f3b45e1005198355179e732e89e7de382e173bd3 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 27 Aug 2014 18:44:01 -0400 Subject: [PATCH 34/64] rtl_power: doc corrections --- src/rtl_power.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index feef236..399b233 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -156,8 +156,9 @@ void usage(void) "Use:\trtl_power -f freq_range [-options] [-f freq2 -opts2] [filename]\n" "\t-f lower:upper:bin_size [Hz]\n" "\t valid range for bin_size is 1Hz - 2.8MHz\n" + "\t multiple frequency ranges are supported\n" "\t[-i integration_interval (default: 10 seconds)]\n" - "\t buggy if a full sweep takes longer than the interval\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" @@ -166,21 +167,22 @@ void usage(void) "\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" + "\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" + "\t hamming, blackman, blackman-harris, hann-poisson, bartlett, youssef\n" // kaiser "\t[-c crop_percent (default: 0%% suggested: 20%%)]\n" - "\t discards data at the edges, 100%% discards everything\n" - "\t has no effect for bins larger than 1MHz\n" - "\t this value is a minimum crop size, more may be discarded\n" + "\t discards data at the edges, 100%% discards everything\n" + "\t has no effect for bins larger than 1MHz\n" + "\t this value is a minimum crop size, more may be discarded\n" "\t[-F fir_size (default: disabled)]\n" - "\t enables low-leakage downsample filter,\n" - "\t fir_size can be 0 or 9. 0 has bad roll off,\n" - "\t try with '-c 50%%'\n" + "\t enables low-leakage downsample filter,\n" + "\t fir_size can be 0 or 9. 0 has bad roll off,\n" + "\t try -F 0 with '-c 50%%' to hide the roll off\n" "\t[-r max_sample_rate (default: 2.4M)]\n" + "\t possible values are 2M to 3.2M\n" "\t[-P enables peak hold (default: off/averaging)]\n" "\t[-L enable linear output (default: off/dB)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" @@ -190,14 +192,16 @@ void usage(void) "\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" + "\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" + "\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" + "\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\n" + "\t collect data for one hour and compress it on the fly\n\n" + "\tIf you have issues writing +2GB logs on a 32bit platform\n" + "\tuse redirection (rtl_power ... > filename.csv) instead\n\n " "Convert CSV to a waterfall graphic with:\n" " https://github.com/keenerd/rtl-sdr-misc/blob/master/heatmap/heatmap.py \n" "More examples at http://kmkeen.com/rtl-power/\n"); From 06af0f90f50cf955854fb814982a81a6aadbe3a6 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 27 Aug 2014 18:44:58 -0400 Subject: [PATCH 35/64] r82xx: direct sampling fixes from tejeez --- src/tuner_r82xx.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index e4a1232..691ac6f 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -946,7 +946,7 @@ static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { else if(cal > 15) cal = 15; priv->fil_cal_code = cal; - fprintf(stderr, "Setting IF filter for %d...%d kHz: hp_cor=0x%02x, fil_cal_code=%d\n", hpf, lpf, hp_cor, cal); + //fprintf(stderr, "Setting IF filter for %d...%d kHz: hp_cor=0x%02x, fil_cal_code=%d\n", hpf, lpf, hp_cor, cal); rc = r82xx_write_reg_mask(priv, 0x0a, filt_q | priv->fil_cal_code, 0x1f); @@ -1113,17 +1113,22 @@ int r82xx_set_nomod(struct r82xx_priv *priv) fprintf(stderr, "Using R820T no-mod direct sampling mode\n"); - /*rc = r82xx_set_bw(priv, 1000000); - if (rc < 0) - goto err;*/ + /* should probably play a bit more with the mux settings + to see if something works even better than this */ - /* experimentally determined magic numbers - * needs more experimenting with all the registers */ rc = r82xx_set_mux(priv, 300000000); - if (rc < 0) - goto err; + if (rc < 0) goto err; + + /* the VCO frequency setting still seems to have some effect on the noise floor */ + rc = r82xx_set_pll(priv, 50000000); + if (rc < 0) goto err; + + /* the most important part: set a divider number that does not really work */ + rc = r82xx_write_reg_mask(priv, 0x10, 0xd0, 0xe0); + if (rc < 0) goto err; - r82xx_set_pll(priv, 25000000); + /* VCO power off? */ + rc = r82xx_write_reg_mask(priv, 0x12, 0xe0, 0xe0); err: if (rc < 0) From 15bb16bd9b2fe4450a02a9c88b3d0f1e2b5f5bb3 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 27 Aug 2014 21:11:52 -0400 Subject: [PATCH 36/64] rtl_fm: fix lockup on squelch --- src/rtl_fm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index c11e496..b50027a 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -947,6 +947,7 @@ static void *demod_thread_fn(void *arg) do_exit = 1; } if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { + unmark_shared_buffer(d->lowpassed); d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ safe_cond_signal(&controller.hop, &controller.hop_m); continue; From 5e138084650b16a40a8aa2047ee96742c286ca16 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 28 Aug 2014 13:54:39 -0400 Subject: [PATCH 37/64] rtl_power: more bug fixes --- src/rtl_power.c | 22 +++++++++++++--------- src/tuner_r82xx.c | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 399b233..75eb21b 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -201,7 +201,7 @@ void usage(void) "\trtl_power -f ... -e 1h | gzip > log.csv.gz\n" "\t collect data for one hour and compress it on the fly\n\n" "\tIf you have issues writing +2GB logs on a 32bit platform\n" - "\tuse redirection (rtl_power ... > filename.csv) instead\n\n " + "\tuse redirection (rtl_power ... > filename.csv) instead\n\n" "Convert CSV to a waterfall graphic with:\n" " https://github.com/keenerd/rtl-sdr-misc/blob/master/heatmap/heatmap.py \n" "More examples at http://kmkeen.com/rtl-power/\n"); @@ -514,8 +514,7 @@ int solve_giant_bins(struct channel_solve *c) int solve_downsample(struct channel_solve *c, int target_rate, int boxcar) { - int scan_size, bins_wanted, bins_needed, ds_next; - double bw; + int scan_size, bins_wanted, bins_needed, ds_next, bw; scan_size = c->upper - c->lower; c->hops = 1; @@ -531,16 +530,18 @@ int solve_downsample(struct channel_solve *c, int target_rate, int boxcar) c->bin_e++; } + c->downsample = 1; + c->downsample_passes = 0; while (1) { - bw = (double)scan_size / (1.0 - c->crop_tmp); - c->bw_needed = (int)bw * c->downsample; + bw = (int)((double)scan_size / (1.0 - c->crop_tmp)); + c->bw_needed = bw * c->downsample; if (boxcar) { ds_next = c->downsample + 1; } else { ds_next = c->downsample * 2; } - if (((int)bw * ds_next) > target_rate) { + if ((bw * ds_next) > target_rate) { break;} c->downsample = ds_next; @@ -553,10 +554,13 @@ int solve_downsample(struct channel_solve *c, int target_rate, int boxcar) int solve_hopping(struct channel_solve *c, int target_rate) { - int i, scan_size, bins_all, bins_crop, bins_2; + int i, scan_size, bins_all, bins_crop, bins_2, min_hops; scan_size = c->upper - c->lower; + min_hops = scan_size / MAXIMUM_RATE - 1; + if (min_hops < 1) { + min_hops = 1;} /* evenly sized ranges, as close to target_rate as possible */ - for (i=1; ibw_wanted = scan_size / i; bins_all = scan_size / c->bin_spec; bins_crop = (int)ceil((double)bins_all / (double)i); @@ -677,7 +681,7 @@ void frequency_range(char *arg, struct misc_settings *ms) logged_bins += hop_bins; ts->crop_i1 = (length - hop_bins) / 2; ts->crop_i2 = ts->crop_i1 + hop_bins - 1; - ts->freq = (lower_edge - ts->crop_i1 * c.bin_spec) + c.bw_needed/2; + ts->freq = (lower_edge - ts->crop_i1 * c.bin_spec) + c.bw_needed/(2*c.downsample); /* prep for next hop */ lower_edge = ts->freq_high; } diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 691ac6f..1702bb6 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -1127,7 +1127,7 @@ int r82xx_set_nomod(struct r82xx_priv *priv) rc = r82xx_write_reg_mask(priv, 0x10, 0xd0, 0xe0); if (rc < 0) goto err; - /* VCO power off? */ + /* VCO power off */ rc = r82xx_write_reg_mask(priv, 0x12, 0xe0, 0xe0); err: From a710e4eed2a1662fa758b3d538e7643760330d52 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 28 Aug 2014 23:57:30 +0100 Subject: [PATCH 38/64] rtl_test: generic tuner range test This scans 0 - 3GHz looking for frequencies that can be tuned to without error, and refines the band edges further once a tuneable band is found. It should work with any tuner that correctly reports tuning errors. It takes about 1.5 minutes to complete on a R820T. --- src/rtl_test.c | 213 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 70 deletions(-) diff --git a/src/rtl_test.c b/src/rtl_test.c index ba25390..09515c8 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -76,7 +76,7 @@ void usage(void) "Usage:\n" "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-d device_index (default: 0)]\n" - "\t[-t enable E4000 or R820T tuner range benchmark]\n" + "\t[-t enable tuner range benchmark]\n" #ifndef _WIN32 "\t[-p[seconds] enable PPM error measurement (default: 10 seconds)]\n" #endif @@ -221,79 +221,161 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) #endif } -void e4k_benchmark(void) -{ - uint32_t freq, gap_start = 0, gap_end = 0; - uint32_t range_start = 0, range_end = 0; +/* smallest band or band gap that tuner_benchmark() will notice */ +static uint32_t max_step(uint32_t freq) { + if (freq < 1e6) + return 1e4; + if (freq > 1e8) + return 1e6; + return freq / 1e2; +} - fprintf(stderr, "Benchmarking E4000 PLL...\n"); +/* precision with which tuner_benchmark() will measure the edges of bands */ +static uint32_t min_step(uint32_t freq) { + return 100; +} - /* find tuner range start */ - for (freq = MHZ(70); freq > MHZ(1); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - range_start = freq; - break; - } - } +static void report_band_start(uint32_t start) { + fprintf(stderr, "Found a new band starting at %u Hz\n", start); +} - /* find tuner range end */ - for (freq = MHZ(2000); freq < MHZ(2300UL); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - range_end = freq; - break; - } - } +static void report_band(uint32_t low, uint32_t high) { + fprintf(stderr, "Tuning band: %u - %u Hz\n", low, high); +} - /* find start of L-band gap */ - for (freq = MHZ(1000); freq < MHZ(1300); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - gap_start = freq; - break; - } +void tuner_benchmark(void) +{ + uint32_t current = max_step(0); + uint32_t band_start = 0; + uint32_t low_bound = 0, high_bound = 0; + char buf[20]; + enum { FIND_START, REFINE_START, FIND_END, REFINE_END } state; + + fprintf(stderr, "Testing tuner range. This may take a couple of minutes..\n"); + + /* Scan for tuneable frequencies coarsely. When we find something, + * do a binary search to narrow down the exact edge of the band. + * + * This can potentially miss bands/gaps smaller than max_step(freq) + * but it is a lot faster than exhaustively scanning everything. + */ + + /* handle bands starting at 0Hz */ + if (rtlsdr_set_center_freq(dev, 0) < 0) + state = FIND_START; + else { + band_start = 0; + report_band_start(band_start); + state = FIND_END; } - /* find end of L-band gap */ - for (freq = MHZ(1300); freq > MHZ(1000); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - gap_end = freq; + while (current < 3e9 && !do_exit) { + switch (state) { + case FIND_START: + /* scanning for the start of a new band */ + if (rtlsdr_set_center_freq(dev, current) < 0) { + /* still looking for a band */ + low_bound = current; + current += max_step(current); + } else { + /* new band, starting somewhere at or before current */ + /* low_bound < start <= current */ + high_bound = current; + state = REFINE_START; + } break; - } - } - - fprintf(stderr, "E4K range: %i to %i MHz\n", - range_start/MHZ(1) + 1, range_end/MHZ(1) - 1); - fprintf(stderr, "E4K L-band gap: %i to %i MHz\n", - gap_start/MHZ(1), gap_end/MHZ(1)); -} - -void r820t_benchmark(void) -{ - uint32_t freq; - uint32_t range_start = 0, range_end = 0; - - fprintf(stderr, "Benchmarking R820T PLL...\n"); + case REFINE_START: + /* refining the start of a band */ + /* low_bound < bandstart <= high_bound */ + if (rtlsdr_set_center_freq(dev, current) == 0) { + /* current is inside the band */ + /* low_bound < bandstart <= current */ + if (current - low_bound <= min_step(current)) { + /* start found at low_bound */ + band_start = current; + report_band_start(band_start); + low_bound = current; + current = band_start + max_step(band_start); + state = FIND_END; + } else { + /* binary search */ + high_bound = current; + current = (current + low_bound) / 2; + } + } else { + /* current is outside the band */ + /* current < bandstart <= high_bound */ + if (high_bound - current <= min_step(current)) { + /* start found at high_bound */ + low_bound = band_start = high_bound; + report_band_start(band_start); + current = band_start + max_step(band_start); + state = FIND_END; + } else { + /* binary search */ + low_bound = current; + current = (current + high_bound) / 2; + } + } + break; - /* find tuner range start */ - for (freq = MHZ(30); freq > MHZ(1); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq)) { - break;} - range_start = freq; - } + case FIND_END: + /* scanning for the end of the current band */ + if (rtlsdr_set_center_freq(dev, current) == 0) { + /* still looking for the end of the band */ + low_bound = current; + current += max_step(current); + } else { + /* end found, coarsely */ + /* low_bound <= bandend < current, refine it */ + high_bound = current; + state = REFINE_END; + } + break; - /* find tuner range end */ - for (freq = MHZ(1750); freq < MHZ(1950UL); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq)) { - break;} - range_end = freq; + case REFINE_END: + /* refining the end of a band */ + /* low_bound <= bandend < high_bound */ + if (rtlsdr_set_center_freq(dev, current) < 0) { + /* current is outside the band */ + /* low_bound <= bandend < current */ + if (current - low_bound <= min_step(current)) { + /* band ends at low_bound */ + report_band(band_start, low_bound); + low_bound = current; + current = low_bound + max_step(low_bound); + state = FIND_START; + } else { + /* binary search */ + high_bound = current; + current = (current + low_bound) / 2; + } + } else { + /* current is inside the band */ + /* current <= bandend < high_bound */ + if (high_bound - current <= min_step(current)) { + /* band ends at high_bound */ + report_band(band_start, current); + low_bound = high_bound; + current = low_bound + max_step(low_bound); + state = FIND_START; + } else { + /* binary search */ + low_bound = current; + current = (current + high_bound) / 2; + } + } + break; + } } - fprintf(stderr, "R820T range: %i to %i MHz\n", - range_start/MHZ(1), range_end/MHZ(1)); + if (state == FIND_END) + report_band(band_start, current); + else if (state == REFINE_END) + report_band(band_start, low_bound); } - - int main(int argc, char **argv) { #ifndef _WIN32 @@ -387,16 +469,7 @@ int main(int argc, char **argv) verbose_set_sample_rate(dev, samp_rate); if (test_mode == TUNER_BENCHMARK) { - switch (rtlsdr_get_tuner_type(dev)) { - case RTLSDR_TUNER_E4000: - e4k_benchmark(); - break; - case RTLSDR_TUNER_R820T: - r820t_benchmark(); - break; - default: - fprintf(stderr, "No testable tuner found, aborting.\n"); - } + tuner_benchmark(); goto exit; } From 1cf92154837225a94749d9918c490d1275f91d23 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 29 Aug 2014 14:05:58 +0100 Subject: [PATCH 39/64] r82xx: enforce PLL register limits. This fixes out-of-range frequencies that would set the PLL registers to incorrect values rather than failing outright. --- src/tuner_r82xx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 1702bb6..86cfffb 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -505,6 +505,11 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) mix_div = mix_div << 1; } + if (mix_div > 64) { + fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); + return -1; + } + if (priv->cfg->rafael_chip == CHIP_R828D) vco_power_ref = 1; @@ -542,10 +547,9 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) nint = (uint32_t) (vco_div / 65536); sdm = (uint32_t) (vco_div % 65536); - if (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) { - fprintf(stderr, "[R828D] No valid PLL values for %u Hz!\n", freq); - return -1; - } else if (nint > 76) { + if (nint < 13 || + (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) || + (priv->cfg->rafael_chip != CHIP_R828D && nint > 76)) { fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); return -1; } From c918fda1ec29724d34b30e7796d8543062b0fdc7 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 31 Aug 2014 11:17:55 -0400 Subject: [PATCH 40/64] rtl_fm: link librt --- configure.ac | 2 +- src/CMakeLists.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b60ca81..0d3874b 100644 --- a/configure.ac +++ b/configure.ac @@ -46,7 +46,7 @@ AC_CHECK_LIB(m, sqrt, [LIBS="$LIBS -lm"]) dnl libmath (for rtl_power) AC_CHECK_LIB(m, atan2, [LIBS="$LIBS -lm"]) -dnl librealtime (for rtl_test) +dnl librealtime (for rtl_test, rtl_fm) AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS -lrt"]) # The following test is taken from WebKit's webkit.m4 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 77e1dc4..51d86f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -112,13 +112,14 @@ target_link_libraries(rtl_power rtlsdr_shared convenience_static ${CMAKE_THREAD_LIBS_INIT} ) if(UNIX) -target_link_libraries(rtl_fm m) target_link_libraries(rtl_adsb m) target_link_libraries(rtl_power m) if(APPLE) target_link_libraries(rtl_test m) + target_link_libraries(rtl_fm m) else() target_link_libraries(rtl_test m rt) + target_link_libraries(rtl_fm m rt) endif() endif() From 92a684cea94c9f5ead370145a97e58d8acb97bad Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 31 Aug 2014 11:46:25 -0400 Subject: [PATCH 41/64] rtl_test: refactor tuner_benchmark --- src/rtl_test.c | 195 +++++++++++++++++-------------------------------- 1 file changed, 65 insertions(+), 130 deletions(-) diff --git a/src/rtl_test.c b/src/rtl_test.c index 09515c8..1e28de7 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -53,6 +53,8 @@ #define PPM_DURATION 10 #define PPM_DUMP_TIME 5 +#define SCAN_LIMIT 2500000000 + static enum { NO_BENCHMARK, TUNER_BENCHMARK, @@ -235,145 +237,78 @@ static uint32_t min_step(uint32_t freq) { return 100; } -static void report_band_start(uint32_t start) { - fprintf(stderr, "Found a new band starting at %u Hz\n", start); -} - -static void report_band(uint32_t low, uint32_t high) { - fprintf(stderr, "Tuning band: %u - %u Hz\n", low, high); -} - -void tuner_benchmark(void) +int confirm_pll_lock(uint32_t f) { - uint32_t current = max_step(0); - uint32_t band_start = 0; - uint32_t low_bound = 0, high_bound = 0; - char buf[20]; - enum { FIND_START, REFINE_START, FIND_END, REFINE_END } state; - - fprintf(stderr, "Testing tuner range. This may take a couple of minutes..\n"); - - /* Scan for tuneable frequencies coarsely. When we find something, - * do a binary search to narrow down the exact edge of the band. - * - * This can potentially miss bands/gaps smaller than max_step(freq) - * but it is a lot faster than exhaustively scanning everything. - */ - - /* handle bands starting at 0Hz */ - if (rtlsdr_set_center_freq(dev, 0) < 0) - state = FIND_START; - else { - band_start = 0; - report_band_start(band_start); - state = FIND_END; + int i; + for (i=0; i<20; i++) { + if (rtlsdr_set_center_freq(dev, f) >= 0) + return 1; } + return 0; +} - while (current < 3e9 && !do_exit) { - switch (state) { - case FIND_START: - /* scanning for the start of a new band */ - if (rtlsdr_set_center_freq(dev, current) < 0) { - /* still looking for a band */ - low_bound = current; - current += max_step(current); - } else { - /* new band, starting somewhere at or before current */ - /* low_bound < start <= current */ - high_bound = current; - state = REFINE_START; - } - break; - - case REFINE_START: - /* refining the start of a band */ - /* low_bound < bandstart <= high_bound */ - if (rtlsdr_set_center_freq(dev, current) == 0) { - /* current is inside the band */ - /* low_bound < bandstart <= current */ - if (current - low_bound <= min_step(current)) { - /* start found at low_bound */ - band_start = current; - report_band_start(band_start); - low_bound = current; - current = band_start + max_step(band_start); - state = FIND_END; - } else { - /* binary search */ - high_bound = current; - current = (current + low_bound) / 2; - } - } else { - /* current is outside the band */ - /* current < bandstart <= high_bound */ - if (high_bound - current <= min_step(current)) { - /* start found at high_bound */ - low_bound = band_start = high_bound; - report_band_start(band_start); - current = band_start + max_step(band_start); - state = FIND_END; - } else { - /* binary search */ - low_bound = current; - current = (current + high_bound) / 2; - } - } +/* returns last frequency before achieving status of 'lock' */ +uint32_t coarse_search(uint32_t start, int lock) +{ + uint32_t f = start, f2; + int status; + while (f < SCAN_LIMIT) { + if (do_exit) break; + f2 = f + max_step(f); + status = rtlsdr_set_center_freq(dev, f2) >= 0; + if (!lock && !status) + status = confirm_pll_lock(f2); + if (status == lock) + return f; + f = f2; + } + return SCAN_LIMIT + 1; +} - case FIND_END: - /* scanning for the end of the current band */ - if (rtlsdr_set_center_freq(dev, current) == 0) { - /* still looking for the end of the band */ - low_bound = current; - current += max_step(current); - } else { - /* end found, coarsely */ - /* low_bound <= bandend < current, refine it */ - high_bound = current; - state = REFINE_END; - } +/* returns frequency of a transition + * must have one transition between start and start+step */ +uint32_t fine_search(uint32_t start, uint32_t step) +{ + int low_status, mid_status, high_status; + uint32_t f, stop; + stop = start + step; + f = start + step / 2; + low_status = rtlsdr_set_center_freq(dev, start) >= 0; + high_status = rtlsdr_set_center_freq(dev, stop) >= 0; + if (low_status == high_status) + return start; + while (step > min_step(start)) { + if (do_exit) break; + mid_status = rtlsdr_set_center_freq(dev, f) >= 0; + if (low_status == mid_status) + start = f; + else + stop = f; + step = stop - start; + f = start + step / 2; + } + return f; +} - case REFINE_END: - /* refining the end of a band */ - /* low_bound <= bandend < high_bound */ - if (rtlsdr_set_center_freq(dev, current) < 0) { - /* current is outside the band */ - /* low_bound <= bandend < current */ - if (current - low_bound <= min_step(current)) { - /* band ends at low_bound */ - report_band(band_start, low_bound); - low_bound = current; - current = low_bound + max_step(low_bound); - state = FIND_START; - } else { - /* binary search */ - high_bound = current; - current = (current + low_bound) / 2; - } - } else { - /* current is inside the band */ - /* current <= bandend < high_bound */ - if (high_bound - current <= min_step(current)) { - /* band ends at high_bound */ - report_band(band_start, current); - low_bound = high_bound; - current = low_bound + max_step(low_bound); - state = FIND_START; - } else { - /* binary search */ - low_bound = current; - current = (current + high_bound) / 2; - } - } +void tuner_benchmark(void) +{ + uint32_t f = 0, low_bound, high_bound; + fprintf(stderr, "Testing tuner range. This may take a couple of minutes...\n"); + while (!do_exit) { + /* find start of a band */ + f = coarse_search(f, 1); + low_bound = fine_search(f, max_step(f)); + f += max_step(f); + /* find stop of a band */ + f = coarse_search(f, 0); + high_bound = fine_search(f, max_step(f)); + f += max_step(f); + if (f > SCAN_LIMIT) break; - } + fprintf(stderr, "Band: %u - %u Hz\n", low_bound, high_bound); } - - if (state == FIND_END) - report_band(band_start, current); - else if (state == REFINE_END) - report_band(band_start, low_bound); } int main(int argc, char **argv) From 05bee3455a4e859cae89414ab1636efc2a9e5623 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 31 Aug 2014 14:28:09 -0400 Subject: [PATCH 42/64] rtl_fm: handle no availible buffers --- src/rtl_fm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index b50027a..d00d8e4 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -878,14 +878,15 @@ void full_demod(struct demod_state *d) int16_t* mark_shared_buffer(void) { int i = 0; - while (1) { - i = (i+1) % SHARED_SIZE; + for (i=0; i Date: Sun, 31 Aug 2014 20:22:06 -0400 Subject: [PATCH 43/64] rtl_fm: arbitrary translation prototype --- src/rtl_fm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index d00d8e4..35c57f8 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -87,6 +87,9 @@ #define FREQUENCIES_LIMIT 1000 +#define PI_INT (1<<14) +#define ONE_INT (1<<14) + static volatile int do_exit = 0; static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; static int ACTUAL_BUF_LENGTH; @@ -115,6 +118,7 @@ struct dongle_state int offset_tuning; int direct_sampling; int mute; + int pre_rotate; struct demod_state *demod_target; }; @@ -156,6 +160,12 @@ struct demod_state int now_lpr; int prev_lpr_index; int dc_block, dc_avg; + int rotate_enable; + double rotate_angle; + int32_t angle_a; /* constant, cos(rotate_angle) * ONE_INT */ + int32_t angle_b; /* constant, sin(rotate_angle) * ONE_INT */ + int32_t angle_sin; /* iterative */ + int32_t angle_cos; int agc_enable; struct agc_state *agc; void (*mode_demod)(struct demod_state*); @@ -325,6 +335,37 @@ void rotate_90(unsigned char *buf, uint32_t len) } } +void translate_init(struct demod_state *d, double angle) +/* angle in radians */ +{ + d->angle_a = (int32_t)(cos(demod.rotate_angle) * ONE_INT); + d->angle_b = (int32_t)(sin(demod.rotate_angle) * ONE_INT); + d->angle_sin = 0; + d->angle_cos = ONE_INT; +} + +void translate(struct demod_state *d) +{ + int i, len; + int32_t old_s, old_c, new_s, new_c, angle_a, angle_b; + int16_t *buf = d->lowpassed; + len = d->lp_len; + old_s = d->angle_sin; + old_c = d->angle_cos; + angle_a = d->angle_a; + angle_b = d->angle_b; + for (i=0; i> 14); + buf[i+1] = (int16_t)(((int32_t)buf[i+1] * old_s) >> 14); + new_s = (angle_b * old_c + angle_a * old_s) >> 14; + new_c = (angle_a * old_c + angle_b * old_s) >> 14; + old_s = new_s; + old_c = new_c; + } + d->angle_sin = old_s; + d->angle_cos = old_c; +} + void low_pass(struct demod_state *d) /* simple square window FIR */ { @@ -916,7 +957,7 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) buf[i] = 127;} s->mute = 0; } - if (!s->offset_tuning) { + if (s->pre_rotate) { rotate_90(buf, len);} for (i=0; i<(int)len; i++) { s->buf16[i] = (int16_t)buf[i] - 127;} @@ -1051,7 +1092,7 @@ static void optimal_settings(int freq, int rate) } capture_freq = freq; capture_rate = dm->downsample * dm->rate_in; - if (!d->offset_tuning) { + if (d->pre_rotate) { capture_freq = freq + capture_rate/4;} capture_freq += cs->edge * dm->rate_in / 2; dm->output_scale = (1<<15) / (128 * dm->downsample); @@ -1134,6 +1175,7 @@ void dongle_init(struct dongle_state *s) s->mute = 0; s->direct_sampling = 0; s->offset_tuning = 0; + s->pre_rotate = 1; s->demod_target = &demod; s->buf16 = mark_shared_buffer(); } @@ -1354,7 +1396,8 @@ int main(int argc, char **argv) if (strcmp("no-mod", optarg) == 0) { dongle.direct_sampling = 3;} if (strcmp("offset", optarg) == 0) { - dongle.offset_tuning = 1;} + dongle.offset_tuning = 1; + dongle.pre_rotate = 0;} if (strcmp("wav", optarg) == 0) { output.wav_format = 1;} if (strcmp("pad", optarg) == 0) { From 90706d40989a3d33c32bc9dcfcdd0f24e0b04658 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 31 Aug 2014 20:23:46 -0400 Subject: [PATCH 44/64] rtl_fm: remove unused resamplers --- src/rtl_fm.c | 65 ---------------------------------------------------- 1 file changed, 65 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 35c57f8..39f3522 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -763,71 +763,6 @@ int squelch_to_rms(int db, struct dongle_state *dongle, struct demod_state *demo return (int)linear + 1; } -void arbitrary_upsample(int16_t *buf1, int16_t *buf2, int len1, int len2) -/* linear interpolation, len1 < len2 */ -{ - int i = 1; - int j = 0; - int tick = 0; - double frac; // use integers... - while (j < len2) { - frac = (double)tick / (double)len2; - buf2[j] = (int16_t)(buf1[i-1]*(1-frac) + buf1[i]*frac); - j++; - tick += len1; - if (tick > len2) { - tick -= len2; - i++; - } - if (i >= len1) { - i = len1 - 1; - tick = len2; - } - } -} - -void arbitrary_downsample(int16_t *buf1, int16_t *buf2, int len1, int len2) -/* fractional boxcar lowpass, len1 > len2 */ -{ - int i = 1; - int j = 0; - int tick = 0; - double remainder = 0; - double frac; // use integers... - buf2[0] = 0; - while (j < len2) { - frac = 1.0; - if ((tick + len2) > len1) { - frac = (double)(len1 - tick) / (double)len2;} - buf2[j] += (int16_t)((double)buf1[i] * frac + remainder); - remainder = (double)buf1[i] * (1.0-frac); - tick += len2; - i++; - if (tick > len1) { - j++; - buf2[j] = 0; - tick -= len1; - } - if (i >= len1) { - i = len1 - 1; - tick = len1; - } - } - for (j=0; j Date: Mon, 1 Sep 2014 15:00:37 -0400 Subject: [PATCH 45/64] rtl_fm: software agc --- src/rtl_fm.c | 68 ++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 39f3522..ec7434a 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -30,7 +30,6 @@ * * todo: * sanity checks - * scale squelch to other input parameters * pad output on hop * frequency ranges could be stored better * auto-hop after time limit @@ -124,10 +123,9 @@ struct dongle_state struct agc_state { - int64_t gain_num; - int64_t gain_den; - int64_t gain_max; - int gain_int; + int32_t gain_num; + int32_t gain_den; + int32_t gain_max; int peak_target; int attack_step; int decay_step; @@ -232,9 +230,9 @@ void usage(void) "\t[-E enable_option (default: none)]\n" "\t use multiple -E to enable multiple options\n" "\t edge: enable lower edge tuning\n" - "\t dc: enable dc blocking filter\n" + "\t no-dc: disable dc blocking filter\n" "\t deemp: enable de-emphasis filter\n" - "\t swagc: enable software agc (only for AM, broken)\n" + "\t swagc: enable software agc (only for AM modes)\n" "\t direct: enable direct sampling\n" "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" @@ -633,7 +631,7 @@ void fm_demod(struct demod_state *fm) void am_demod(struct demod_state *fm) // todo, fix this extreme laziness { - int i, pcm; + int32_t i, pcm; int16_t *lp = fm->lowpassed; for (i = 0; i < fm->lp_len; i += 2) { // hypot uses floats but won't overflow @@ -643,7 +641,7 @@ void am_demod(struct demod_state *fm) lp[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; } fm->lp_len = fm->lp_len / 2; - // lowpass? (3khz) highpass? (dc) + // lowpass? (3khz) } void usb_demod(struct demod_state *fm) @@ -764,20 +762,22 @@ int squelch_to_rms(int db, struct dongle_state *dongle, struct demod_state *demo } void software_agc(struct demod_state *d) -/* ignores complex pairs, indirectly calculates power squelching */ { int i = 0; - int output; + int peaked = 0; + int32_t output; struct agc_state *agc = d->agc; int16_t *lp = d->lowpassed; for (i=0; i < d->lp_len; i++) { - output = (int)((int64_t)lp[i] * agc->gain_num / agc->gain_den); + output = ((int32_t)lp[i] * agc->gain_num / agc->gain_den); - if (abs(output) < agc->peak_target) { - agc->gain_num += agc->decay_step; + if (!peaked && abs(output) > agc->peak_target) { + peaked = 1;} + if (peaked) { + agc->gain_num += agc->attack_step; } else { - agc->gain_num -= agc->attack_step; + agc->gain_num += agc->decay_step; } if (agc->gain_num < agc->gain_den) { @@ -785,8 +785,12 @@ void software_agc(struct demod_state *d) if (agc->gain_num > agc->gain_max) { agc->gain_num = agc->gain_max;} - agc->gain_int = (int)(agc->gain_num / agc->gain_den); - lp[i] = output; + if (output >= (1<<15)) { + output = (1<<15) - 1;} + if (output < -(1<<15)) { + output = -(1<<15) + 1;} + + lp[i] = (int16_t)output; } } @@ -812,12 +816,8 @@ void full_demod(struct demod_state *d) } else { low_pass(d); } - if (d->agc_enable) { - software_agc(d); - if(d->squelch_level && d->agc->gain_int > d->squelch_level) { - do_squelch = 1;} /* power squelch */ - } else if (d->squelch_level) { + if (d->squelch_level) { sr = rms(d->lowpassed, d->lp_len, 1); if (sr < d->squelch_level) { do_squelch = 1;} @@ -835,16 +835,17 @@ void full_demod(struct demod_state *d) } d->mode_demod(d); /* lowpassed -> lowpassed */ if (d->mode_demod == &raw_demod) { - return; - } + return;} + if (d->dc_block) { + dc_block_filter(d);} + if (d->agc_enable) { + software_agc(d);} /* todo, fm noise squelch */ // use nicer filter here too? if (d->post_downsample > 1) { d->lp_len = low_pass_simple(d->lowpassed, d->lp_len, d->post_downsample);} if (d->deemph) { deemph_filter(d);} - if (d->dc_block) { - dc_block_filter(d);} if (d->rate_out2 > 0) { low_pass_real(d); //arbitrary_resample(d->lowpassed, d->lowpassed, d->lp_len, d->lp_len * d->rate_out2 / d->rate_out); @@ -1136,7 +1137,7 @@ void demod_init(struct demod_state *s) s->prev_lpr_index = 0; s->deemph_a = 0; s->now_lpr = 0; - s->dc_block = 0; + s->dc_block = 1; s->dc_avg = 0; pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); @@ -1211,13 +1212,12 @@ int agc_init(struct demod_state *s) agc = malloc(sizeof(struct agc_state)); s->agc = agc; - agc->gain_den = 1<<13; + agc->gain_den = 1<<16; agc->gain_num = agc->gain_den; - agc->gain_int = (int)(agc->gain_num / agc->gain_den); - agc->peak_target = 1<<13; - agc->gain_max = 1<<10 * agc->gain_num; - agc->attack_step = 2; + agc->peak_target = 1<<14; + agc->gain_max = 256 * agc->gain_den; agc->decay_step = 1; + agc->attack_step = -2; return 0; } @@ -1320,8 +1320,8 @@ int main(int argc, char **argv) case 'E': if (strcmp("edge", optarg) == 0) { controller.edge = 1;} - if (strcmp("dc", optarg) == 0) { - demod.dc_block = 1;} + if (strcmp("no-dc", optarg) == 0) { + demod.dc_block = 0;} if (strcmp("deemp", optarg) == 0) { demod.deemph = 1;} if (strcmp("swagc", optarg) == 0) { From 88244c041cb9eb03b84822d71666a94f65e1d7be Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 1 Sep 2014 17:54:28 -0400 Subject: [PATCH 46/64] rtl_fm: sw-agc quantization correction --- src/rtl_fm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index ec7434a..960fff5 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -129,6 +129,7 @@ struct agc_state int peak_target; int attack_step; int decay_step; + int error; }; struct demod_state @@ -770,7 +771,9 @@ void software_agc(struct demod_state *d) int16_t *lp = d->lowpassed; for (i=0; i < d->lp_len; i++) { - output = ((int32_t)lp[i] * agc->gain_num / agc->gain_den); + output = (int32_t)lp[i] * agc->gain_num + agc->error; + agc->error = output % agc->gain_den; + output /= agc->gain_den; if (!peaked && abs(output) > agc->peak_target) { peaked = 1;} @@ -1212,7 +1215,7 @@ int agc_init(struct demod_state *s) agc = malloc(sizeof(struct agc_state)); s->agc = agc; - agc->gain_den = 1<<16; + agc->gain_den = 1<<15; agc->gain_num = agc->gain_den; agc->peak_target = 1<<14; agc->gain_max = 256 * agc->gain_den; From 581b6b63b28cda24505f14364096175fef58a08d Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 2 Sep 2014 05:39:31 -0400 Subject: [PATCH 47/64] rtl_fm: pad out single channel underruns --- src/rtl_fm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 960fff5..1ef5c71 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -927,7 +927,8 @@ static void *demod_thread_fn(void *arg) if (d->exit_flag) { do_exit = 1; } - if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { + if (controller.freq_len > 1 && d->squelch_level && \ + d->squelch_hits > d->conseq_squelch) { unmark_shared_buffer(d->lowpassed); d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ safe_cond_signal(&controller.hop, &controller.hop_m); From c813134536ef26e46cd18549f2a61bb1bca99f00 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Tue, 2 Sep 2014 21:00:05 -0400 Subject: [PATCH 48/64] rtl_fm: stream padding --- src/rtl_fm.c | 63 +++++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 1ef5c71..1b01f73 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -30,7 +30,6 @@ * * todo: * sanity checks - * pad output on hop * frequency ranges could be stored better * auto-hop after time limit * peak detector to tune onto stronger signals @@ -188,6 +187,8 @@ struct output_state pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; + pthread_mutex_t trycond_m; + int trycond; }; struct controller_state @@ -238,7 +239,9 @@ void usage(void) "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" "\t wav: generate WAV header\n" - "\t pad: pad output with zeros (broken)\n" +#ifndef _WIN32 + "\t pad: pad output gaps with zeros\n" +#endif "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" @@ -927,6 +930,10 @@ static void *demod_thread_fn(void *arg) if (d->exit_flag) { do_exit = 1; } + pthread_rwlock_wrlock(&o->rw); + o->result = d->lowpassed; + o->result_len = d->lp_len; + pthread_rwlock_unlock(&o->rw); if (controller.freq_len > 1 && d->squelch_level && \ d->squelch_hits > d->conseq_squelch) { unmark_shared_buffer(d->lowpassed); @@ -934,11 +941,10 @@ static void *demod_thread_fn(void *arg) safe_cond_signal(&controller.hop, &controller.hop_m); continue; } - pthread_rwlock_wrlock(&o->rw); - o->result = d->lowpassed; - o->result_len = d->lp_len; - pthread_rwlock_unlock(&o->rw); safe_cond_signal(&o->ready, &o->ready_m); + pthread_mutex_lock(&o->trycond_m); + o->trycond = 0; + pthread_mutex_unlock(&o->trycond_m); } return 0; } @@ -954,7 +960,7 @@ static int get_nanotime(struct timespec *ts) rv = gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * 1000; + ts->tv_nsec = tv.tv_usec * 1000L; #endif return rv; } @@ -964,11 +970,9 @@ static void *output_thread_fn(void *arg) { int r = 0; struct output_state *s = arg; - //struct timespec abstime; struct timespec start_time; struct timespec now_time; - struct timespec fut_time; - int64_t interval, delay, samples, samples2, i; + int64_t i, duration, samples, samples_now; samples = 0L; #ifndef _WIN32 get_nanotime(&start_time); @@ -984,34 +988,33 @@ static void *output_thread_fn(void *arg) } #ifndef _WIN32 /* padding requires output at constant rate */ - /* figure out how to do this with windows HPET */ - pthread_mutex_lock(&s->ready_m); - delay = 1000000000L * (int64_t)s->result_len / (int64_t)s->rate; - get_nanotime(&fut_time); - fut_time.tv_nsec += delay; - r = pthread_cond_timedwait(&s->ready, &s->ready_m, &fut_time); - pthread_mutex_unlock(&s->ready_m); - if (r != ETIMEDOUT) { + /* pthread_cond_timedwait is terrible, roll our own trycond */ + // figure out how to do this with windows HPET + usleep(2000); + pthread_mutex_lock(&s->trycond_m); + r = s->trycond; + s->trycond = 1; + pthread_mutex_unlock(&s->trycond_m); + if (r == 0) { pthread_rwlock_rdlock(&s->rw); fwrite(s->result, 2, s->result_len, s->file); unmark_shared_buffer(s->result); - samples += s->result_len; + samples += (int64_t)s->result_len; pthread_rwlock_unlock(&s->rw); continue; } get_nanotime(&now_time); - interval = now_time.tv_sec - start_time.tv_sec; - interval *= 1000000000L; - interval += (now_time.tv_nsec - start_time.tv_nsec); - samples2 = interval * (int64_t)s->rate / 1000000000L; - samples2 -= samples; - //fprintf(stderr, "%lli %lli %lli\n", delay, interval, samples); - /* there must be a better way to write zeros */ - for (i=0L; irate) / 1000000000L; + if (samples_now < samples) { + continue;} + for (i=samples; ifile); fputc(0, s->file); } - samples += samples2; + samples = samples_now; #endif } return 0; @@ -1163,6 +1166,8 @@ void output_init(struct output_state *s) pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); + pthread_mutex_init(&s->trycond_m, NULL); + s->trycond = 1; s->result = NULL; } @@ -1171,6 +1176,7 @@ void output_cleanup(struct output_state *s) pthread_rwlock_destroy(&s->rw); pthread_cond_destroy(&s->ready); pthread_mutex_destroy(&s->ready_m); + pthread_mutex_destroy(&s->trycond_m); } void controller_init(struct controller_state *s) @@ -1430,6 +1436,7 @@ int main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); + output.padded = 0; #endif if (demod.deemph) { From 53d212d8a0cdd9a27b919210705da6bba98fd0b7 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Wed, 3 Sep 2014 23:56:21 -0400 Subject: [PATCH 49/64] rtl_fm: half finished dual-channel mode --- src/rtl_fm.c | 237 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 52 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 1b01f73..1e751e3 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -82,6 +82,7 @@ #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) #define AUTO_GAIN -100 #define BUFFER_DUMP 4096 +#define MAXIMUM_RATE 2400000 #define FREQUENCIES_LIMIT 1000 @@ -91,13 +92,14 @@ static volatile int do_exit = 0; static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; static int ACTUAL_BUF_LENGTH; +static uint32_t MINIMUM_RATE = 1000000; static int *atan_lut = NULL; static int atan_lut_size = 131072; /* 512 KB */ static int atan_lut_coef = 8; // rewrite as dynamic and thread-safe for multi demod/dongle -#define SHARED_SIZE 4 +#define SHARED_SIZE 6 int16_t shared_samples[SHARED_SIZE][MAXIMUM_BUF_LENGTH]; int ss_busy[SHARED_SIZE] = {0}; @@ -117,7 +119,7 @@ struct dongle_state int direct_sampling; int mute; int pre_rotate; - struct demod_state *demod_target; + struct demod_state *targets[2]; }; struct agc_state @@ -170,7 +172,18 @@ struct demod_state pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; - struct output_state *output_target; + struct buffer_bucket *output_target; +}; + +struct buffer_bucket +{ + int16_t *buf; + int len; + pthread_rwlock_t rw; + pthread_cond_t ready; + pthread_mutex_t ready_m; + pthread_mutex_t trycond_m; + int trycond; }; struct output_state @@ -179,16 +192,11 @@ struct output_state pthread_t thread; FILE *file; char *filename; - int16_t *result; - int result_len; + struct buffer_bucket results[2]; int rate; int wav_format; int padded; - pthread_rwlock_t rw; - pthread_cond_t ready; - pthread_mutex_t ready_m; - pthread_mutex_t trycond_m; - int trycond; + int lrmix; }; struct controller_state @@ -207,6 +215,7 @@ struct controller_state // multiple of these, eventually struct dongle_state dongle; struct demod_state demod; +struct demod_state demod2; struct output_state output; struct controller_state controller; @@ -242,6 +251,8 @@ void usage(void) #ifndef _WIN32 "\t pad: pad output gaps with zeros\n" #endif + "\t lrmix: one channel goes to left audio, one to right (broken)\n" + "\t remember to enable stereo (-c 2) in sox\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" @@ -340,8 +351,10 @@ void rotate_90(unsigned char *buf, uint32_t len) void translate_init(struct demod_state *d, double angle) /* angle in radians */ { - d->angle_a = (int32_t)(cos(demod.rotate_angle) * ONE_INT); - d->angle_b = (int32_t)(sin(demod.rotate_angle) * ONE_INT); + d->rotate_enable = 1; + d->rotate_angle = angle; + d->angle_a = (int32_t)(cos(angle) * ONE_INT); + d->angle_b = (int32_t)(sin(angle) * ONE_INT); d->angle_sin = 0; d->angle_cos = ONE_INT; } @@ -349,7 +362,7 @@ void translate_init(struct demod_state *d, double angle) void translate(struct demod_state *d) { int i, len; - int32_t old_s, old_c, new_s, new_c, angle_a, angle_b; + int32_t old_s, old_c, new_s, new_c, angle_a, angle_b, tmp; int16_t *buf = d->lowpassed; len = d->lp_len; old_s = d->angle_sin; @@ -357,12 +370,34 @@ void translate(struct demod_state *d) angle_a = d->angle_a; angle_b = d->angle_b; for (i=0; i> 14); - buf[i+1] = (int16_t)(((int32_t)buf[i+1] * old_s) >> 14); - new_s = (angle_b * old_c + angle_a * old_s) >> 14; - new_c = (angle_a * old_c + angle_b * old_s) >> 14; + //buf[i] = (int16_t)(((int32_t)buf[i] * old_c) >> 14); + //buf[i+1] = (int16_t)(((int32_t)buf[i+1] * old_s) >> 14); + //new_s = (angle_b * old_c + angle_a * old_s) >> 14; + //new_c = (angle_a * old_c + angle_b * old_s) >> 14; + tmp = (int32_t)buf[i] * old_c; + if (tmp % (1<<14) > (1<<13)) { + tmp += (1<<13);} + buf[i] = (int16_t)(tmp >> 14); + + tmp = (int32_t)buf[i+1] * old_s; + if (tmp % (1<<14) > (1<<13)) { + tmp += (1<<13);} + buf[i+1] = (int16_t)(tmp >> 14); + + tmp = angle_b * old_c + angle_a * old_s; + if (tmp % (1<<14) > (1<<13)) { + tmp += (1<<13);} + new_s = (int16_t)(tmp >> 14); + + tmp = angle_a * old_c + angle_b * old_s; + if (tmp % (1<<14) > (1<<13)) { + tmp += (1<<13);} + new_c = (int16_t)(tmp >> 14); + old_s = new_s; old_c = new_c; + + //fprintf(stderr, "%i %i\n", new_s, new_c); } d->angle_sin = old_s; d->angle_cos = old_c; @@ -805,6 +840,9 @@ void full_demod(struct demod_state *d) int i, ds_p; int do_squelch = 0; int sr = 0; + if(d->rotate_enable) { + translate(d); + } ds_p = d->downsample_passes; if (ds_p) { for (i=0; i < ds_p; i++) { @@ -888,7 +926,8 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { int i; struct dongle_state *s = ctx; - struct demod_state *d = s->demod_target; + struct demod_state *d = s->targets[0]; + struct demod_state *d2 = s->targets[1]; if (do_exit) { return;} @@ -903,6 +942,13 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) rotate_90(buf, len);} for (i=0; i<(int)len; i++) { s->buf16[i] = (int16_t)buf[i] - 127;} + if (d2 != NULL) { + pthread_rwlock_wrlock(&d2->rw); + d2->lowpassed = mark_shared_buffer(); + memcpy(d2->lowpassed, s->buf16, 2*len); + pthread_rwlock_unlock(&d2->rw); + safe_cond_signal(&d2->ready, &d2->ready_m); + } pthread_rwlock_wrlock(&d->rw); d->lowpassed = s->buf16; d->lp_len = len; @@ -921,7 +967,7 @@ static void *dongle_thread_fn(void *arg) static void *demod_thread_fn(void *arg) { struct demod_state *d = arg; - struct output_state *o = d->output_target; + struct buffer_bucket *o = d->output_target; while (!do_exit) { safe_cond_wait(&d->ready, &d->ready_m); pthread_rwlock_wrlock(&d->rw); @@ -931,8 +977,8 @@ static void *demod_thread_fn(void *arg) do_exit = 1; } pthread_rwlock_wrlock(&o->rw); - o->result = d->lowpassed; - o->result_len = d->lp_len; + o->buf = d->lowpassed; + o->len = d->lp_len; pthread_rwlock_unlock(&o->rw); if (controller.freq_len > 1 && d->squelch_level && \ d->squelch_hits > d->conseq_squelch) { @@ -968,8 +1014,10 @@ static int get_nanotime(struct timespec *ts) static void *output_thread_fn(void *arg) { - int r = 0; + int j, r = 0; struct output_state *s = arg; + struct buffer_bucket *b0 = &s->results[0]; + struct buffer_bucket *b1 = &s->results[1]; struct timespec start_time; struct timespec now_time; int64_t i, duration, samples, samples_now; @@ -978,12 +1026,27 @@ static void *output_thread_fn(void *arg) get_nanotime(&start_time); #endif while (!do_exit) { + if (s->lrmix) { + safe_cond_wait(&b0->ready, &b0->ready_m); + pthread_rwlock_rdlock(&b0->rw); + safe_cond_wait(&b1->ready, &b1->ready_m); + pthread_rwlock_rdlock(&b1->rw); + for(j=0; j < b0->len; j++) { + fwrite(b0->buf+j, 2, 1, s->file); + fwrite(b1->buf+j, 2, 1, s->file); + } + unmark_shared_buffer(b1->buf); + pthread_rwlock_unlock(&b1->rw); + unmark_shared_buffer(b0->buf); + pthread_rwlock_unlock(&b0->rw); + continue; + } if (!s->padded) { - safe_cond_wait(&s->ready, &s->ready_m); - pthread_rwlock_rdlock(&s->rw); - fwrite(s->result, 2, s->result_len, s->file); - unmark_shared_buffer(s->result); - pthread_rwlock_unlock(&s->rw); + safe_cond_wait(&b0->ready, &b0->ready_m); + pthread_rwlock_rdlock(&b0->rw); + fwrite(b0->buf, 2, b0->len, s->file); + unmark_shared_buffer(b0->buf); + pthread_rwlock_unlock(&b0->rw); continue; } #ifndef _WIN32 @@ -991,16 +1054,16 @@ static void *output_thread_fn(void *arg) /* pthread_cond_timedwait is terrible, roll our own trycond */ // figure out how to do this with windows HPET usleep(2000); - pthread_mutex_lock(&s->trycond_m); - r = s->trycond; - s->trycond = 1; - pthread_mutex_unlock(&s->trycond_m); + pthread_mutex_lock(&b0->trycond_m); + r = b0->trycond; + b0->trycond = 1; + pthread_mutex_unlock(&b0->trycond_m); if (r == 0) { - pthread_rwlock_rdlock(&s->rw); - fwrite(s->result, 2, s->result_len, s->file); - unmark_shared_buffer(s->result); - samples += (int64_t)s->result_len; - pthread_rwlock_unlock(&s->rw); + pthread_rwlock_rdlock(&b0->rw); + fwrite(b0->buf, 2, b0->len, s->file); + unmark_shared_buffer(b0->buf); + samples += (int64_t)b0->len; + pthread_rwlock_unlock(&b0->rw); continue; } get_nanotime(&now_time); @@ -1028,7 +1091,7 @@ static void optimal_settings(int freq, int rate) struct dongle_state *d = &dongle; struct demod_state *dm = &demod; struct controller_state *cs = &controller; - dm->downsample = (1000000 / dm->rate_in) + 1; + dm->downsample = (MINIMUM_RATE / dm->rate_in) + 1; if (dm->downsample_passes) { dm->downsample_passes = (int)log2(dm->downsample) + 1; dm->downsample = 1 << dm->downsample_passes; @@ -1045,6 +1108,47 @@ static void optimal_settings(int freq, int rate) dm->output_scale = 1;} d->freq = (uint32_t)capture_freq; d->rate = (uint32_t)capture_rate; + //translate_init(&demod, 0.25 * 2 * 3.14159); + //d->pre_rotate = 0; +} + +void optimal_lrmix(void) +{ + double angle1, angle2; + uint32_t freq, freq1, freq2, bw, dongle_bw, mr; + if (controller.freq_len != 2) { + fprintf(stderr, "error: lrmix requires two frequencies\n"); + do_exit = 1; + exit(1); + } + if (output.padded) { + fprintf(stderr, "warning: lrmix does not support padding\n"); + } + freq1 = controller.freqs[0]; + freq2 = controller.freqs[1]; + bw = demod.rate_out; + freq = freq1 / 2 + freq2 / 2 + bw; + mr = (uint32_t)abs((int64_t)freq1 - (int64_t)freq2) + bw; + if (mr > MINIMUM_RATE) { + MINIMUM_RATE = mr;} + dongle.pre_rotate = 0; + optimal_settings(freq, bw); + output.padded = 0; + demod2 = demod; + demod2.output_target = &output.results[1]; + dongle.targets[1] = &demod2; + dongle_bw = dongle.rate; + if (dongle_bw > MAXIMUM_RATE) { + fprintf(stderr, "error: unable to find optimal settings\n"); + do_exit = 1; + exit(1); + } + angle1 = ((double)freq1 - (double)freq) / (double)dongle_bw; + angle1 *= 2 * 3.14159; + angle2 = ((double)freq2 - (double)freq) / (double)dongle_bw; + angle2 *= 2 * 3.14159; + translate_init(&demod, angle1); + translate_init(&demod2, angle2); } static void *controller_thread_fn(void *arg) @@ -1067,6 +1171,11 @@ static void *controller_thread_fn(void *arg) if (dongle.offset_tuning) { verbose_offset_tuning(dongle.dev);} + /* set up lrmix */ + if (output.lrmix) { + optimal_lrmix(); + } + /* Set the frequency */ verbose_set_frequency(dongle.dev, dongle.freq); fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); @@ -1082,6 +1191,8 @@ static void *controller_thread_fn(void *arg) safe_cond_wait(&s->hop, &s->hop_m); if (s->freq_len <= 1) { continue;} + if (output.lrmix) { + continue;} /* hacky hopping */ s->freq_now = (s->freq_now + 1) % s->freq_len; optimal_settings(s->freqs[s->freq_now], demod.rate_in); @@ -1119,7 +1230,8 @@ void dongle_init(struct dongle_state *s) s->direct_sampling = 0; s->offset_tuning = 0; s->pre_rotate = 1; - s->demod_target = &demod; + s->targets[0] = &demod; + s->targets[1] = NULL; s->buf16 = mark_shared_buffer(); } @@ -1149,7 +1261,7 @@ void demod_init(struct demod_state *s) pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); - s->output_target = &output; + s->output_target = &output.results[0]; s->lowpassed = NULL; } @@ -1162,21 +1274,27 @@ void demod_cleanup(struct demod_state *s) void output_init(struct output_state *s) { + int i; //s->rate = DEFAULT_SAMPLE_RATE; - pthread_rwlock_init(&s->rw, NULL); - pthread_cond_init(&s->ready, NULL); - pthread_mutex_init(&s->ready_m, NULL); - pthread_mutex_init(&s->trycond_m, NULL); - s->trycond = 1; - s->result = NULL; + for (i=0; i<2; i++) { + pthread_rwlock_init(&s->results[i].rw, NULL); + pthread_cond_init(&s->results[i].ready, NULL); + pthread_mutex_init(&s->results[i].ready_m, NULL); + pthread_mutex_init(&s->results[i].trycond_m, NULL); + s->results[i].trycond = 1; + s->results[i].buf = NULL; + } } void output_cleanup(struct output_state *s) { - pthread_rwlock_destroy(&s->rw); - pthread_cond_destroy(&s->ready); - pthread_mutex_destroy(&s->ready_m); - pthread_mutex_destroy(&s->trycond_m); + int i; + for (i=0; i<2; i++) { + pthread_rwlock_destroy(&s->results[i].rw); + pthread_cond_destroy(&s->results[i].ready); + pthread_mutex_destroy(&s->results[i].ready_m); + pthread_mutex_destroy(&s->results[i].trycond_m); + } } void controller_init(struct controller_state *s) @@ -1207,11 +1325,16 @@ void sanity_checks(void) exit(1); } - if (controller.freq_len > 1 && demod.squelch_level == 0) { + if (!output.lrmix && controller.freq_len > 1 && demod.squelch_level == 0) { fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); exit(1); } + if (demod.mode_demod == &raw_demod && output.lrmix) { + fprintf(stderr, "'raw' is not a supported demodulator for lrmix\n"); + exit(1); + } + } int agc_init(struct demod_state *s) @@ -1240,7 +1363,7 @@ int generate_header(struct demod_state *d, struct output_state *o) uint8_t byte_rate[4] = {0, 0, 0, 0}; s_rate = o->rate; b_rate = o->rate * 2; - if (d->mode_demod == &raw_demod) { + if (d->mode_demod == &raw_demod || o->lrmix) { channels = "\2\0"; align = "\4\0"; b_rate *= 2; @@ -1347,6 +1470,8 @@ int main(int argc, char **argv) output.wav_format = 1;} if (strcmp("pad", optarg) == 0) { output.padded = 1;} + if (strcmp("lrmix", optarg) == 0) { + output.lrmix = 1;} break; case 'F': demod.downsample_passes = 1; /* truthy placeholder */ @@ -1482,6 +1607,9 @@ int main(int argc, char **argv) usleep(100000); pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output)); pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod)); + if (output.lrmix) { + pthread_create(&demod2.thread, NULL, demod_thread_fn, (void *)(&demod2)); + } pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle)); while (!do_exit) { @@ -1497,7 +1625,12 @@ int main(int argc, char **argv) pthread_join(dongle.thread, NULL); safe_cond_signal(&demod.ready, &demod.ready_m); pthread_join(demod.thread, NULL); - safe_cond_signal(&output.ready, &output.ready_m); + if (output.lrmix) { + safe_cond_signal(&demod2.ready, &demod2.ready_m); + pthread_join(demod2.thread, NULL); + } + safe_cond_signal(&output.results[0].ready, &output.results[0].ready_m); + safe_cond_signal(&output.results[1].ready, &output.results[1].ready_m); pthread_join(output.thread, NULL); safe_cond_signal(&controller.hop, &controller.hop_m); pthread_join(controller.thread, NULL); From 0037e5ffdcd39f15abbd0659c82c7090ad4cdf92 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 7 Sep 2014 23:17:41 -0400 Subject: [PATCH 50/64] rtl_fm: arbitrary translation --- src/rtl_fm.c | 133 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 52 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 1e751e3..4c28817 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -133,6 +133,14 @@ struct agc_state int error; }; +struct translate_state +{ + double angle; /* radians */ + int16_t *sincos; /* pairs */ + int len; + int i; +}; + struct demod_state { int exit_flag; @@ -161,11 +169,7 @@ struct demod_state int prev_lpr_index; int dc_block, dc_avg; int rotate_enable; - double rotate_angle; - int32_t angle_a; /* constant, cos(rotate_angle) * ONE_INT */ - int32_t angle_b; /* constant, sin(rotate_angle) * ONE_INT */ - int32_t angle_sin; /* iterative */ - int32_t angle_cos; + struct translate_state rotate; int agc_enable; struct agc_state *agc; void (*mode_demod)(struct demod_state*); @@ -348,59 +352,76 @@ void rotate_90(unsigned char *buf, uint32_t len) } } -void translate_init(struct demod_state *d, double angle) -/* angle in radians */ +int translate_init(struct translate_state *ts) +/* two pass: first to find optimal length, second to alloc/fill */ { - d->rotate_enable = 1; - d->rotate_angle = angle; - d->angle_a = (int32_t)(cos(angle) * ONE_INT); - d->angle_b = (int32_t)(sin(angle) * ONE_INT); - d->angle_sin = 0; - d->angle_cos = ONE_INT; + int max_length = 100000; + int i, s, c, best_i; + double a, a2, err, best_360; + if (fabs(ts->angle) < 2*M_PI/max_length) { + fprintf(stderr, "angle too small or array too short\n"); + return 1; + } + ts->i = 0; + ts->sincos = NULL; + if (ts->len) { + max_length = ts->len; + ts->sincos = malloc(max_length * sizeof(int16_t)); + } + a = 0.0; + err = 0.0; + best_i = 0; + best_360 = 4.0; + for (i=0; i < max_length; i+=2) { + s = (int)round(sin(a + err) * (1<<14)); + c = (int)round(cos(a + err) * (1<<14)); + a2 = atan2(s, c); + err = fmod(a, 2*M_PI) - a2; + a += ts->angle; + while (a > M_PI) { + a -= 2*M_PI;} + while (a < -M_PI) { + a += 2*M_PI;} + if (i != 0 && fabs(a) < best_360) { + best_i = i; + best_360 = fabs(a); + } + if (i != 0 && fabs(a) < 0.0000001) { + break;} + if (ts->sincos) { + ts->sincos[i] = s; + ts->sincos[i+1] = c; + //fprintf(stderr, "%i %i %i\n", i, s, c); + } + } + if (ts->sincos) { + return 0; + } + ts->len = best_i + 2; + return translate_init(ts); } void translate(struct demod_state *d) { - int i, len; - int32_t old_s, old_c, new_s, new_c, angle_a, angle_b, tmp; + int i, len, sc_i, sc_len; + int32_t tmp, ar, aj, br, bj; int16_t *buf = d->lowpassed; + int16_t *sincos = d->rotate.sincos; len = d->lp_len; - old_s = d->angle_sin; - old_c = d->angle_cos; - angle_a = d->angle_a; - angle_b = d->angle_b; - for (i=0; i> 14); - //buf[i+1] = (int16_t)(((int32_t)buf[i+1] * old_s) >> 14); - //new_s = (angle_b * old_c + angle_a * old_s) >> 14; - //new_c = (angle_a * old_c + angle_b * old_s) >> 14; - tmp = (int32_t)buf[i] * old_c; - if (tmp % (1<<14) > (1<<13)) { - tmp += (1<<13);} + sc_i = d->rotate.i; + sc_len = d->rotate.len; + for (i=0; i> 14); - - tmp = (int32_t)buf[i+1] * old_s; - if (tmp % (1<<14) > (1<<13)) { - tmp += (1<<13);} + tmp = aj*br + ar*bj; buf[i+1] = (int16_t)(tmp >> 14); - - tmp = angle_b * old_c + angle_a * old_s; - if (tmp % (1<<14) > (1<<13)) { - tmp += (1<<13);} - new_s = (int16_t)(tmp >> 14); - - tmp = angle_a * old_c + angle_b * old_s; - if (tmp % (1<<14) > (1<<13)) { - tmp += (1<<13);} - new_c = (int16_t)(tmp >> 14); - - old_s = new_s; - old_c = new_c; - - //fprintf(stderr, "%i %i\n", new_s, new_c); } - d->angle_sin = old_s; - d->angle_cos = old_c; + d->rotate.i = sc_i; } void low_pass(struct demod_state *d) @@ -535,7 +556,7 @@ int polar_discriminant(int ar, int aj, int br, int bj) double angle; multiply(ar, aj, br, -bj, &cr, &cj); angle = atan2((double)cj, (double)cr); - return (int)(angle / 3.14159 * (1<<14)); + return (int)(angle / M_PI * (1<<14)); } int fast_atan2(int y, int x) @@ -1108,8 +1129,10 @@ static void optimal_settings(int freq, int rate) dm->output_scale = 1;} d->freq = (uint32_t)capture_freq; d->rate = (uint32_t)capture_rate; - //translate_init(&demod, 0.25 * 2 * 3.14159); //d->pre_rotate = 0; + //demod.rotate_enable = 1; + //demod.rotate.angle = -0.25 * 2 * M_PI; + //translate_init(&demod.rotate); } void optimal_lrmix(void) @@ -1145,10 +1168,12 @@ void optimal_lrmix(void) } angle1 = ((double)freq1 - (double)freq) / (double)dongle_bw; angle1 *= 2 * 3.14159; + demod.rotate.angle = angle1; angle2 = ((double)freq2 - (double)freq) / (double)dongle_bw; angle2 *= 2 * 3.14159; - translate_init(&demod, angle1); - translate_init(&demod2, angle2); + demod2.rotate.angle = angle2; + translate_init(&demod.rotate); + translate_init(&demod2.rotate); } static void *controller_thread_fn(void *arg) @@ -1250,6 +1275,9 @@ void demod_init(struct demod_state *s) s->custom_atan = 0; s->deemph = 0; s->agc_enable = 0; + s->rotate_enable = 0; + s->rotate.len = 0; + s->rotate.sincos = NULL; s->rate_out2 = -1; // flag for disabled s->mode_demod = &fm_demod; s->pre_j = s->pre_r = s->now_r = s->now_j = 0; @@ -1396,6 +1424,7 @@ int main(int argc, char **argv) int r, opt; int dev_given = 0; int custom_ppm = 0; + dongle_init(&dongle); demod_init(&demod); agc_init(&demod); From 8ad17c2c9580662c4fa6804fa6ff5c57c5543d02 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Mon, 8 Sep 2014 00:10:45 -0400 Subject: [PATCH 51/64] rtl_fm: three-quarters of dual channel --- src/rtl_fm.c | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 4c28817..09b69af 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -596,7 +596,7 @@ int atan_lut_init(void) atan_lut = malloc(atan_lut_size * sizeof(int)); for (i = 0; i < atan_lut_size; i++) { - atan_lut[i] = (int) (atan((double) i / (1<rw); d2->lowpassed = mark_shared_buffer(); memcpy(d2->lowpassed, s->buf16, 2*len); + d2->lp_len = len; pthread_rwlock_unlock(&d2->rw); safe_cond_signal(&d2->ready, &d2->ready_m); } @@ -1135,6 +1136,30 @@ static void optimal_settings(int freq, int rate) //translate_init(&demod.rotate); } +void clone_demod(struct demod_state *d2, struct demod_state *d1) +/* copy from d1 to d2 */ +{ + d2->rate_in = d1->rate_in; + d2->rate_out = d1->rate_out; + d2->rate_out2 = d1->rate_out2; + d2->downsample = d1->downsample; + d2->downsample_passes = d1->downsample_passes; + d2->post_downsample = d1->post_downsample; + d2->output_scale = d1->output_scale; + d2->squelch_level = d1->squelch_level; + d2->conseq_squelch = d1->conseq_squelch; + d2->squelch_hits = d1->squelch_hits; + d2->terminate_on_squelch = d1->terminate_on_squelch; + d2->comp_fir_size = d1->comp_fir_size; + d2->custom_atan = d1->custom_atan; + d2->deemph = d1->deemph; + d2->deemph_a = d1->deemph_a; + d2->dc_block = d1->dc_block; + d2->rotate_enable = d1->rotate_enable; + d2->agc_enable = d1->agc_enable; + d2->mode_demod = d1->mode_demod; +} + void optimal_lrmix(void) { double angle1, angle2; @@ -1157,7 +1182,8 @@ void optimal_lrmix(void) dongle.pre_rotate = 0; optimal_settings(freq, bw); output.padded = 0; - demod2 = demod; + clone_demod(&demod2, &demod); + //demod2 = demod; demod2.output_target = &output.results[1]; dongle.targets[1] = &demod2; dongle_bw = dongle.rate; @@ -1167,13 +1193,12 @@ void optimal_lrmix(void) exit(1); } angle1 = ((double)freq1 - (double)freq) / (double)dongle_bw; - angle1 *= 2 * 3.14159; - demod.rotate.angle = angle1; + demod.rotate.angle = angle1 * 2 * M_PI; angle2 = ((double)freq2 - (double)freq) / (double)dongle_bw; - angle2 *= 2 * 3.14159; - demod2.rotate.angle = angle2; + demod2.rotate.angle = angle2 * 2 * M_PI; translate_init(&demod.rotate); translate_init(&demod2.rotate); + //fprintf(stderr, "a1 %f, a2 %f\n", angle1, angle2); } static void *controller_thread_fn(void *arg) @@ -1427,6 +1452,7 @@ int main(int argc, char **argv) dongle_init(&dongle); demod_init(&demod); + demod_init(&demod2); agc_init(&demod); output_init(&output); controller_init(&controller); From 1b1c03d4e9330c30c9f4cc6de11fbe1572664fe7 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Fri, 17 Oct 2014 12:22:03 -0400 Subject: [PATCH 52/64] rtl_power: fix NaN on 32 bit platforms (T. Kaminski) --- src/rtl_power.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 75eb21b..54ca8a2 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -99,7 +99,7 @@ struct tuning_state int gain; int bin_e; int16_t *fft_buf; - long *avg; /* length == 2^bin_e */ + int64_t *avg; /* length == 2^bin_e */ int samples; int downsample; int downsample_passes; /* for the recursive filter */ @@ -461,19 +461,19 @@ void rms_power(struct tuning_state *ts) int i, s; uint8_t *buf = ts->buf8; int buf_len = ts->buf_len; - long p, t; + int64_t p, t; double dc, err; p = t = 0L; for (i=0; ipeak_hold) { ts->avg[0] += p; @@ -642,7 +642,7 @@ void frequency_range(char *arg, struct misc_settings *ms) ts->comp_fir_size = ms->comp_fir_size; ts->peak_hold = ms->peak_hold; ts->linear = ms->linear; - ts->avg = (long*)malloc((1<avg = (int64_t*)malloc((1<avg) { fprintf(stderr, "Error: malloc.\n"); exit(1); @@ -772,11 +772,11 @@ void remove_dc(int16_t *data, int length) { int i; int16_t ave; - long sum = 0L; + int64_t sum = 0L; for (i=0; i < length; i+=2) { sum += data[i]; } - ave = (int16_t)(sum / (long)(length)); + ave = (int16_t)(sum / (int64_t)(length)); if (ave == 0) { return;} for (i=0; i < length; i+=2) { @@ -822,10 +822,10 @@ void downsample_iq(int16_t *data, int length) //remove_dc(data+1, length-1); } -long real_conj(int16_t real, int16_t imag) +int64_t real_conj(int16_t real, int16_t imag) /* real(n * conj(n)) */ { - return ((long)real*(long)real + (long)imag*(long)imag); + return ((int64_t)real*(int64_t)real + (int64_t)imag*(int64_t)imag); } void scanner(void) @@ -913,7 +913,7 @@ void scanner(void) void csv_dbm(struct tuning_state *ts) { int i, len, ds; - long tmp; + int64_t tmp; double dbm; char *sep = ", "; len = 1 << ts->bin_e; From 012ca95107a681076d7daeca80305dee623cd4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 1 Nov 2014 12:20:45 +0100 Subject: [PATCH 53/64] rtl_test: Support PPM error measurement on Windows. --- src/rtl_test.c | 60 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/rtl_test.c b/src/rtl_test.c index 1e28de7..1f9dda1 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -55,6 +55,21 @@ #define SCAN_LIMIT 2500000000 +struct time_generic +/* holds all the platform specific values */ +{ +#ifndef _WIN32 + time_t tv_sec; + long tv_nsec; +#else + long tv_sec; + long tv_nsec; + int init; + LARGE_INTEGER frequency; + LARGE_INTEGER ticks; +#endif +}; + static enum { NO_BENCHMARK, TUNER_BENCHMARK, @@ -79,9 +94,7 @@ void usage(void) "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-d device_index (default: 0)]\n" "\t[-t enable tuner range benchmark]\n" -#ifndef _WIN32 "\t[-p[seconds] enable PPM error measurement (default: 10 seconds)]\n" -#endif "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-S force sync output (default: async)]\n"); exit(1); @@ -136,12 +149,15 @@ static void underrun_test(unsigned char *buf, uint32_t len, int mute) } #ifndef _WIN32 -static int ppm_gettime(struct timespec *ts) +static int ppm_gettime(struct time_generic *tg) { int rv = ENOSYS; + struct timespec ts; #ifdef __unix__ - rv = clock_gettime(CLOCK_MONOTONIC, ts); + rv = clock_gettime(CLOCK_MONOTONIC, &ts); + tg->tv_sec = ts.tv_sec; + tg->tv_nsec = ts.tv_nsec; #elif __APPLE__ struct timeval tv; @@ -151,6 +167,24 @@ static int ppm_gettime(struct timespec *ts) #endif return rv; } +#endif + +#ifdef _WIN32 +static int ppm_gettime(struct time_generic *tg) +{ + int rv; + int64_t frac; + if (!tg->init) { + QueryPerformanceFrequency(&tg->frequency); + tg->init = 1; + } + rv = QueryPerformanceCounter(&tg->ticks); + tg->tv_sec = tg->ticks.QuadPart / tg->frequency.QuadPart; + frac = (int64_t)(tg->ticks.QuadPart - (tg->tv_sec * tg->frequency.QuadPart)); + tg->tv_nsec = (long)(frac * 1000000000L / (int64_t)tg->frequency.QuadPart); + return !rv; +} +#endif static int ppm_report(uint64_t nsamples, uint64_t interval) { @@ -167,8 +201,9 @@ static void ppm_test(uint32_t len) static uint64_t interval = 0; static uint64_t nsamples_total = 0; static uint64_t interval_total = 0; - struct timespec ppm_now; - static struct timespec ppm_recent; + static struct time_generic ppm_now; + static struct time_generic ppm_recent; + static enum { PPM_INIT_NO, PPM_INIT_DUMP, @@ -176,6 +211,7 @@ static void ppm_test(uint32_t len) } ppm_init = PPM_INIT_NO; ppm_gettime(&ppm_now); + if (ppm_init != PPM_INIT_RUN) { /* * Kyle Keen wrote: @@ -191,8 +227,7 @@ static void ppm_test(uint32_t len) } if (ppm_init == PPM_INIT_DUMP && ppm_recent.tv_sec < ppm_now.tv_sec) return; - ppm_recent.tv_sec = ppm_now.tv_sec; - ppm_recent.tv_nsec = ppm_now.tv_nsec; + ppm_recent = ppm_now; ppm_init = PPM_INIT_RUN; return; } @@ -202,25 +237,22 @@ static void ppm_test(uint32_t len) return; interval *= 1000000000UL; interval += (int64_t)(ppm_now.tv_nsec - ppm_recent.tv_nsec); + nsamples_total += nsamples; interval_total += interval; printf("real sample rate: %i current PPM: %i cumulative PPM: %i\n", (int)((1000000000UL * nsamples) / interval), ppm_report(nsamples, interval), ppm_report(nsamples_total, interval_total)); - ppm_recent.tv_sec = ppm_now.tv_sec; - ppm_recent.tv_nsec = ppm_now.tv_nsec; + ppm_recent = ppm_now; nsamples = 0; } -#endif static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { underrun_test(buf, len, 0); -#ifndef _WIN32 if (test_mode == PPM_BENCHMARK) ppm_test(len); -#endif } /* smallest band or band gap that tuner_benchmark() will notice */ @@ -415,7 +447,7 @@ int main(int argc, char **argv) verbose_reset_buffer(dev); if ((test_mode == PPM_BENCHMARK) && !sync_mode) { - fprintf(stderr, "Reporting PPM error measurement every %i seconds...\n", ppm_duration); + fprintf(stderr, "Reporting PPM error measurement every %u seconds...\n", ppm_duration); fprintf(stderr, "Press ^C after a few minutes.\n"); } From 61e2b669de174cb5526df0fbdc872aba9ff7e181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 1 Nov 2014 13:02:48 +0100 Subject: [PATCH 54/64] rtl_fm: Support output padding on Windows. --- src/rtl_fm.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 09b69af..0ae5060 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -252,9 +252,7 @@ void usage(void) "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" "\t wav: generate WAV header\n" -#ifndef _WIN32 "\t pad: pad output gaps with zeros\n" -#endif "\t lrmix: one channel goes to left audio, one to right (broken)\n" "\t remember to enable stereo (-c 2) in sox\n" "\tfilename ('-' means stdout)\n" @@ -1040,11 +1038,18 @@ static void *output_thread_fn(void *arg) struct output_state *s = arg; struct buffer_bucket *b0 = &s->results[0]; struct buffer_bucket *b1 = &s->results[1]; + int64_t i, duration, samples = 0LL, samples_now; +#ifdef _WIN32 + LARGE_INTEGER perfFrequency; + LARGE_INTEGER start_time; + LARGE_INTEGER now_time; + + QueryPerformanceFrequency(&perfFrequency); + QueryPerformanceCounter(&start_time); +#else struct timespec start_time; struct timespec now_time; - int64_t i, duration, samples, samples_now; - samples = 0L; -#ifndef _WIN32 + get_nanotime(&start_time); #endif while (!do_exit) { @@ -1071,10 +1076,9 @@ static void *output_thread_fn(void *arg) pthread_rwlock_unlock(&b0->rw); continue; } -#ifndef _WIN32 + /* padding requires output at constant rate */ /* pthread_cond_timedwait is terrible, roll our own trycond */ - // figure out how to do this with windows HPET usleep(2000); pthread_mutex_lock(&b0->trycond_m); r = b0->trycond; @@ -1088,11 +1092,17 @@ static void *output_thread_fn(void *arg) pthread_rwlock_unlock(&b0->rw); continue; } +#ifdef _WIN32 + QueryPerformanceCounter(&now_time); + duration = now_time.QuadPart - start_time.QuadPart; + samples_now = (duration * s->rate) / perfFrequency.QuadPart; +#else get_nanotime(&now_time); duration = now_time.tv_sec - start_time.tv_sec; duration *= 1000000000L; duration += (now_time.tv_nsec - start_time.tv_nsec); - samples_now = (duration * (int64_t)s->rate) / 1000000000L; + samples_now = (duration * s->rate) / 1000000000UL; +#endif if (samples_now < samples) { continue;} for (i=samples; ifile); } samples = samples_now; -#endif } return 0; } From d426cfd23126be10eb296abad4636e3e7ec149b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 1 Nov 2014 12:36:47 +0100 Subject: [PATCH 55/64] convenience: No junk when device info isn't properly extracted. --- src/convenience/convenience.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c index 6565e1a..cc8a8bf 100644 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -275,7 +275,7 @@ int verbose_device_search(char *s) { int i, device_count, device, offset; char *s2; - char vendor[256], product[256], serial[256]; + char vendor[256] = {0}, product[256] = {0}, serial[256] = {0}; device_count = rtlsdr_get_device_count(); if (!device_count) { fprintf(stderr, "No supported devices found.\n"); @@ -283,8 +283,11 @@ int verbose_device_search(char *s) } 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); + if (rtlsdr_get_device_usb_strings(i, vendor, product, serial) == 0) { + fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); + } else { + fprintf(stderr, " %d: %s\n", i, "Failed to query data"); + } } fprintf(stderr, "\n"); /* does string look like raw id number */ From a2d23c590ff97821127d04c18009fab16f964d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 1 Nov 2014 15:29:05 +0100 Subject: [PATCH 56/64] rtl_fm: Avoid possible null pointer dereference. --- src/rtl_fm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 0ae5060..6841d20 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -945,13 +945,15 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { int i; struct dongle_state *s = ctx; - struct demod_state *d = s->targets[0]; - struct demod_state *d2 = s->targets[1]; + struct demod_state *d; + struct demod_state *d2; if (do_exit) { return;} - if (!ctx) { + if (!s) { return;} + d = s->targets[0]; + d2 = s->targets[1]; if (s->mute) { for (i=0; imute; i++) { buf[i] = 127;} From 6aa5f175890b03f619c125fd5712481b444a4822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 1 Nov 2014 14:27:48 +0100 Subject: [PATCH 57/64] Variable cleanup and corrections. Remove unused variables. Use proper type specifier. Explicitly cast to correct type to suppress MSVC warnings. --- src/librtlsdr.c | 8 ++++---- src/rtl_fm.c | 3 +-- src/rtl_power.c | 7 +++---- src/rtl_tcp.c | 24 ++++++++++++------------ src/tuner_e4k.c | 12 ++++++------ src/tuner_r82xx.c | 7 +++---- 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/librtlsdr.c b/src/librtlsdr.c index a3b5359..622f985 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -705,7 +705,7 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) return -2; - if_freq = ((freq * TWO_POW(22)) / rtl_xtal) * (-1); + if_freq = (int32_t)(((freq * TWO_POW(22)) / rtl_xtal) * -1); tmp = (if_freq >> 16) & 0x3f; r = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); @@ -728,7 +728,7 @@ int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; uint8_t tmp; - int16_t offs = ppm * (-1) * TWO_POW(24) / 1000000; + int16_t offs = (int16_t)(ppm * -1 * TWO_POW(24) / 1000000); rtlsdr_set_i2c_repeater(dev, 0); tmp = offs & 0xff; @@ -1103,7 +1103,7 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) return -EINVAL; } - rsamp_ratio = (dev->rtl_xtal * TWO_POW(22)) / samp_rate; + rsamp_ratio = (uint32_t)((dev->rtl_xtal * TWO_POW(22)) / samp_rate); rsamp_ratio &= 0x0ffffffc; real_rsamp_ratio = rsamp_ratio | ((rsamp_ratio & 0x08000000) << 1); @@ -1853,7 +1853,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, r = libusb_submit_transfer(dev->xfer[i]); if (r < 0) { - fprintf(stderr, "Failed to submit transfer %i!\n", i); + fprintf(stderr, "Failed to submit transfer %u!\n", i); dev->async_status = RTLSDR_CANCELING; break; } diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 6841d20..822ef96 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -755,7 +755,7 @@ void dc_block_filter(struct demod_state *fm) for (i=0; i < fm->lp_len; i++) { sum += lp[i]; } - avg = sum / fm->lp_len; + avg = (int)(sum / fm->lp_len); avg = (avg + fm->dc_avg * 9) / 10; for (i=0; i < fm->lp_len; i++) { lp[i] -= avg; @@ -1403,7 +1403,6 @@ void sanity_checks(void) int agc_init(struct demod_state *s) { - int i; struct agc_state *agc; agc = malloc(sizeof(struct agc_state)); diff --git a/src/rtl_power.c b/src/rtl_power.c index 54ca8a2..f881fec 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -830,7 +830,7 @@ int64_t real_conj(int16_t real, int16_t imag) void scanner(void) { - int i, j, j2, f, g, n_read, offset, bin_e, bin_len, buf_len, ds, ds_p; + int i, j, j2, n_read, offset, bin_e, bin_len, buf_len, ds, ds_p; int32_t w; struct tuning_state *ts; int16_t *fft_buf; @@ -912,12 +912,11 @@ void scanner(void) void csv_dbm(struct tuning_state *ts) { - int i, len, ds; + int i, len; int64_t tmp; double dbm; char *sep = ", "; len = 1 << ts->bin_e; - ds = ts->downsample; /* fix FFT stuff quirks */ if (ts->bin_e > 0) { /* nuke DC component (not effective for all windows) */ @@ -972,7 +971,7 @@ int main(int argc, char **argv) struct sigaction sigact; #endif char *filename = NULL; - int i, r, opt, wb_mode = 0; + int i, r, opt; int f_set = 0; int dev_index = 0; int dev_given = 0; diff --git a/src/rtl_tcp.c b/src/rtl_tcp.c index 81ed5e6..c4c599e 100644 --- a/src/rtl_tcp.c +++ b/src/rtl_tcp.c @@ -303,23 +303,23 @@ static void *command_worker(void *arg) } switch(cmd.cmd) { case 0x01: - printf("set freq %d\n", ntohl(cmd.param)); + printf("set freq %u\n", ntohl(cmd.param)); rtlsdr_set_center_freq(dev,ntohl(cmd.param)); break; case 0x02: - printf("set sample rate %d\n", ntohl(cmd.param)); + printf("set sample rate %u\n", ntohl(cmd.param)); rtlsdr_set_sample_rate(dev, ntohl(cmd.param)); break; case 0x03: - printf("set gain mode %d\n", ntohl(cmd.param)); + printf("set gain mode %u\n", ntohl(cmd.param)); rtlsdr_set_tuner_gain_mode(dev, ntohl(cmd.param)); break; case 0x04: - printf("set gain %d\n", ntohl(cmd.param)); + printf("set gain %u\n", ntohl(cmd.param)); rtlsdr_set_tuner_gain(dev, ntohl(cmd.param)); break; case 0x05: - printf("set freq correction %d\n", ntohl(cmd.param)); + printf("set freq correction %u\n", ntohl(cmd.param)); rtlsdr_set_freq_correction(dev, ntohl(cmd.param)); break; case 0x06: @@ -328,31 +328,31 @@ static void *command_worker(void *arg) rtlsdr_set_tuner_if_gain(dev, tmp >> 16, (short)(tmp & 0xffff)); break; case 0x07: - printf("set test mode %d\n", ntohl(cmd.param)); + printf("set test mode %u\n", ntohl(cmd.param)); rtlsdr_set_testmode(dev, ntohl(cmd.param)); break; case 0x08: - printf("set agc mode %d\n", ntohl(cmd.param)); + printf("set agc mode %u\n", ntohl(cmd.param)); rtlsdr_set_agc_mode(dev, ntohl(cmd.param)); break; case 0x09: - printf("set direct sampling %d\n", ntohl(cmd.param)); + printf("set direct sampling %u\n", ntohl(cmd.param)); rtlsdr_set_direct_sampling(dev, ntohl(cmd.param)); break; case 0x0a: - printf("set offset tuning %d\n", ntohl(cmd.param)); + printf("set offset tuning %u\n", ntohl(cmd.param)); rtlsdr_set_offset_tuning(dev, ntohl(cmd.param)); break; case 0x0b: - printf("set rtl xtal %d\n", ntohl(cmd.param)); + printf("set rtl xtal %u\n", ntohl(cmd.param)); rtlsdr_set_xtal_freq(dev, ntohl(cmd.param), 0); break; case 0x0c: - printf("set tuner xtal %d\n", ntohl(cmd.param)); + printf("set tuner xtal %u\n", ntohl(cmd.param)); rtlsdr_set_xtal_freq(dev, 0, ntohl(cmd.param)); break; case 0x0d: - printf("set tuner gain by index %d\n", ntohl(cmd.param)); + printf("set tuner gain by index %u\n", ntohl(cmd.param)); set_gain_by_index(dev, ntohl(cmd.param)); break; default: diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c index c2ec044..0f2d265 100644 --- a/src/tuner_e4k.c +++ b/src/tuner_e4k.c @@ -443,7 +443,7 @@ static uint32_t compute_flo(uint32_t f_osc, uint8_t z, uint16_t x, uint8_t r) if (fvco == 0) return -EINVAL; - return fvco / r; + return (uint32_t)(fvco / r); } static int e4k_band_set(struct e4k_state *e4k, enum e4k_band band) @@ -483,10 +483,10 @@ uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint uint32_t i; uint8_t r = 2; uint64_t intended_fvco, remainder; - uint64_t z = 0; + uint8_t z = 0; uint32_t x; - int flo; - int three_phase_mixing = 0; + uint32_t flo; + uint8_t three_phase_mixing = 0; oscp->r_idx = 0; if (!is_fosc_valid(fosc)) @@ -507,13 +507,13 @@ uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint intended_fvco = (uint64_t)intended_flo * r; /* compute integral component of multiplier */ - z = intended_fvco / fosc; + z = (uint8_t)(intended_fvco / fosc); /* compute fractional part. this will not overflow, * as fosc(max) = 30MHz and z(max) = 255 */ remainder = intended_fvco - (fosc * z); /* remainder(max) = 30MHz, E4K_PLL_Y = 65536 -> 64bit! */ - x = (remainder * E4K_PLL_Y) / fosc; + x = (uint32_t)((remainder * E4K_PLL_Y) / fosc); /* x(max) as result of this computation is 65536 */ flo = compute_flo(fosc, z, x, r); diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 86cfffb..5c5c603 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -542,7 +542,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) * vco_div = 65536*(nint + sdm/65536) = int( 0.5 + 65536 * vco_freq / (2 * pll_ref) ) * vco_div = 65536*nint + sdm = int( (pll_ref + 65536 * vco_freq) / (2 * pll_ref) ) */ - + vco_div = (pll_ref + 65536 * vco_freq) / (2 * pll_ref); nint = (uint32_t) (vco_div / 65536); sdm = (uint32_t) (vco_div % 65536); @@ -826,9 +826,8 @@ static int r82xx_init_tv_standard(struct r82xx_priv *priv, { /* everything that was previously done in r82xx_set_tv_standard * and doesn't need to be changed when filter settings change */ - int rc, i; + int rc; uint32_t if_khz, filt_cal_lo; - uint8_t data[5], val; uint8_t filt_gain, img_r, ext_enable, loop_through; uint8_t lt_att, flt_ext_widest, polyfil_cur; @@ -907,7 +906,7 @@ static int r82xx_init_tv_standard(struct r82xx_priv *priv, } static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { - int rc, i; + int rc; uint8_t filt_q, hp_cor; int cal; filt_q = 0x10; /* r10[4]:low q(1'b1) */ From 5ba889896f27a08d7f0a6a952f6c1dea7456058b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Thu, 30 Oct 2014 23:22:29 +0100 Subject: [PATCH 58/64] build: Fix compilation on VS2013, Cygwin and MinGW. - Link librtlsdr dynamically also on Windows. --- CMakeLists.txt | 1 + src/CMakeLists.txt | 42 ++++++++++++++---------------------------- src/rtl_adsb.c | 3 +-- src/rtl_fm.c | 4 ++-- src/rtl_power.c | 4 ++-- src/rtl_tcp.c | 4 ---- 6 files changed, 20 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0597600..8053e55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ # Project setup ######################################################################## cmake_minimum_required(VERSION 2.6) +set(CMAKE_LEGACY_CYGWIN_WIN32 0) project(rtlsdr C) #select the release build type by default to get optimization flags diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51d86f2..6647d5c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,11 +28,9 @@ add_library(rtlsdr_shared SHARED tuner_fc2580.c tuner_r82xx.c ) - target_link_libraries(rtlsdr_shared ${LIBUSB_LIBRARIES} ) - set_target_properties(rtlsdr_shared PROPERTIES DEFINE_SYMBOL "rtlsdr_EXPORTS") set_target_properties(rtlsdr_shared PROPERTIES OUTPUT_NAME rtlsdr) set_target_properties(rtlsdr_shared PROPERTIES SOVERSION ${MAJOR_VERSION}) @@ -46,25 +44,17 @@ add_library(rtlsdr_static STATIC tuner_fc2580.c tuner_r82xx.c ) +target_link_libraries(rtlsdr_static + ${LIBUSB_LIBRARIES} +) +set_property(TARGET rtlsdr_static APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) add_library(convenience_static STATIC convenience/convenience.c ) - -if(WIN32) -add_library(libgetopt_static STATIC - getopt/getopt.c -) target_link_libraries(convenience_static rtlsdr_shared ) -endif() - -target_link_libraries(rtlsdr_static - ${LIBUSB_LIBRARIES} -) - -set_property(TARGET rtlsdr_static APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) if(NOT WIN32) # Force same library filename for static and shared variants of the library @@ -83,31 +73,31 @@ add_executable(rtl_adsb rtl_adsb.c) add_executable(rtl_power rtl_power.c) set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power) -target_link_libraries(rtl_sdr rtlsdr_shared convenience_static +target_link_libraries(rtl_sdr convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_tcp rtlsdr_shared convenience_static +target_link_libraries(rtl_tcp convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_test rtlsdr_shared convenience_static +target_link_libraries(rtl_test convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_fm rtlsdr_shared convenience_static +target_link_libraries(rtl_fm convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_eeprom rtlsdr_shared convenience_static +target_link_libraries(rtl_eeprom convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_adsb rtlsdr_shared convenience_static +target_link_libraries(rtl_adsb convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -target_link_libraries(rtl_power rtlsdr_shared convenience_static +target_link_libraries(rtl_power convenience_static rtlsdr_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) @@ -124,6 +114,9 @@ endif() endif() if(WIN32) +add_library(libgetopt_static STATIC + getopt/getopt.c +) target_link_libraries(rtl_sdr libgetopt_static) target_link_libraries(rtl_tcp ws2_32 libgetopt_static) target_link_libraries(rtl_test libgetopt_static) @@ -131,13 +124,6 @@ target_link_libraries(rtl_fm libgetopt_static) target_link_libraries(rtl_eeprom libgetopt_static) target_link_libraries(rtl_adsb libgetopt_static) target_link_libraries(rtl_power libgetopt_static) -set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_test APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_fm APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_eeprom APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_adsb APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) -set_property(TARGET rtl_power APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) endif() ######################################################################## # Install built library files & utilities diff --git a/src/rtl_adsb.c b/src/rtl_adsb.c index 7b10306..7092802 100644 --- a/src/rtl_adsb.c +++ b/src/rtl_adsb.c @@ -43,8 +43,7 @@ #include "rtl-sdr.h" #include "convenience/convenience.h" -#ifdef _WIN32 -#define sleep Sleep +#if defined(_MSC_VER) && _MSC_VER < 1800 #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 822ef96..e8b48cf 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -63,7 +63,7 @@ #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1800 #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES @@ -322,7 +322,7 @@ int cic_9_tables[][10] = { {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1800 double log2(double n) { return log(n) / log(2.0); diff --git a/src/rtl_power.c b/src/rtl_power.c index f881fec..2e1bf2d 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -53,7 +53,7 @@ #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1800 #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES @@ -260,7 +260,7 @@ int cic_9_tables[][10] = { {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1800 double log2(double n) { return log(n) / log(2.0); diff --git a/src/rtl_tcp.c b/src/rtl_tcp.c index c4c599e..35eadac 100644 --- a/src/rtl_tcp.c +++ b/src/rtl_tcp.c @@ -110,11 +110,7 @@ int gettimeofday(struct timeval *tv, void* ignored) tmp <<= 32; tmp |= ft.dwLowDateTime; tmp /= 10; -#ifdef _MSC_VER - tmp -= 11644473600000000Ui64; -#else tmp -= 11644473600000000ULL; -#endif tv->tv_sec = (long)(tmp / 1000000UL); tv->tv_usec = (long)(tmp % 1000000UL); } From bc872cbcfcdbed70218589a7af3949ffbc11ecd0 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 13 Nov 2014 10:43:19 -0500 Subject: [PATCH 59/64] build: libusb pkgconfig for NixOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From Bjørn Forsman --- librtlsdr.pc.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/librtlsdr.pc.in b/librtlsdr.pc.in index 5e55049..76b4190 100644 --- a/librtlsdr.pc.in +++ b/librtlsdr.pc.in @@ -7,5 +7,6 @@ Name: RTL-SDR Library Description: C Utility Library Version: @VERSION@ Cflags: -I${includedir}/ @RTLSDR_PC_CFLAGS@ -Libs: -L${libdir} -lrtlsdr -lusb-1.0 +Libs: -L${libdir} -lrtlsdr Libs.private: @RTLSDR_PC_LIBS@ +Requires: libusb-1.0 From f1e8e4d38d69b9f66fb69a6a4d028f30f197294c Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 30 Nov 2014 22:43:30 -0500 Subject: [PATCH 60/64] rtl_power: improved hopping math --- src/rtl_power.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index 2e1bf2d..dc777e1 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -552,9 +552,32 @@ int solve_downsample(struct channel_solve *c, int target_rate, int boxcar) return 0; } +int solve_single(struct channel_solve *c, int target_rate) +{ + int i, scan_size, bins_all, bins_crop, bin_e, bins_2, bw_needed; + scan_size = c->upper - c->lower; + bins_all = scan_size / c->bin_spec; + bins_crop = (int)ceil((double)bins_all * (1.0 + c->crop)); + bin_e = (int)ceil(log2(bins_crop)); + bins_2 = 1 << bin_e; + bw_needed = bins_2 * c->bin_spec; + + if (bw_needed > target_rate) { + /* actually multi-hop */ + return 1;} + + c->bw_wanted = scan_size; + c->bw_needed = bw_needed; + c->hops = 1; + c->bin_e = bin_e; + /* crop will always be bigger than specified crop */ + c->crop_tmp = (double)(bins_2 - bins_all) / (double)bins_2; + return 0; +} + int solve_hopping(struct channel_solve *c, int target_rate) { - int i, scan_size, bins_all, bins_crop, bins_2, min_hops; + int i, scan_size, bins_all, bins_sub, bins_crop, bins_2, min_hops; scan_size = c->upper - c->lower; min_hops = scan_size / MAXIMUM_RATE - 1; if (min_hops < 1) { @@ -563,11 +586,12 @@ int solve_hopping(struct channel_solve *c, int target_rate) for (i=min_hops; ibw_wanted = scan_size / i; bins_all = scan_size / c->bin_spec; - bins_crop = (int)ceil((double)bins_all / (double)i); + bins_sub = (int)ceil((double)bins_all / (double)i); + bins_crop = (int)ceil((double)bins_sub * (1.0 + c->crop)); c->bin_e = (int)ceil(log2(bins_crop)); bins_2 = 1 << c->bin_e; c->bw_needed = bins_2 * c->bin_spec; - c->crop_tmp = (double)(bins_2 - bins_crop) / (double)bins_2; + c->crop_tmp = (double)(bins_2 - bins_sub) / (double)bins_2; if (c->bw_needed > target_rate) { continue;} if (c->crop_tmp < c->crop) { @@ -583,7 +607,7 @@ void frequency_range(char *arg, struct misc_settings *ms) { struct channel_solve c; struct tuning_state *ts; - int i, j, buf_len, length, hop_bins, logged_bins, planned_bins; + int r, i, j, buf_len, length, hop_bins, logged_bins, planned_bins; int lower_edge, actual_bw, upper_perfect, remainder; fprintf(stderr, "Range: %s\n", arg); @@ -603,15 +627,24 @@ void frequency_range(char *arg, struct misc_settings *ms) exit(1); } - + r = -1; if (c.bin_spec >= MINIMUM_RATE) { fprintf(stderr, "Mode: rms power\n"); solve_giant_bins(&c); } else if ((c.upper - c.lower) < MINIMUM_RATE) { fprintf(stderr, "Mode: downsampling\n"); solve_downsample(&c, ms->target_rate, ms->boxcar); + } else if ((c.upper - c.lower) < MAXIMUM_RATE) { + r = solve_single(&c, ms->target_rate); } else { - fprintf(stderr, "Mode: normal\n"); + fprintf(stderr, "Mode: hopping\n"); + solve_hopping(&c, ms->target_rate); + } + + if (r == 0) { + fprintf(stderr, "Mode: single\n"); + } else if (r == 1) { + fprintf(stderr, "Mode: hopping\n"); solve_hopping(&c, ms->target_rate); } c.crop = c.crop_tmp; From 3c01571043affb5b5386d3bc77f4d2708b27860a Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 30 Nov 2014 23:06:31 -0500 Subject: [PATCH 61/64] rtl_power: fix peak hold --- src/rtl_power.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index dc777e1..e79ea6b 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -971,7 +971,9 @@ void csv_dbm(struct tuning_state *ts) } dbm = (double)ts->avg[i]; dbm /= (double)ts->rate; - dbm /= (double)ts->samples; + if (!ts->peak_hold) { + dbm /= (double)ts->samples; + } if (ts->linear) { fprintf(file, "%.5g%s", dbm, sep); } else { From c6e6be52f783bb11d4554b6122a6439454e82713 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Sun, 30 Nov 2014 23:35:55 -0500 Subject: [PATCH 62/64] rtl_power: epoch time --- src/rtl_power.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index e79ea6b..c3adf32 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -131,6 +131,8 @@ struct channel_solve double crop, crop_tmp; }; +enum time_modes { VERBOSE_TIME, EPOCH_TIME }; + struct misc_settings { int boxcar; @@ -142,6 +144,7 @@ struct misc_settings int gain; double (*window_fn)(int, int); int smoothing; + enum time_modes time_mode; }; /* 3000 is enough for 3GHz b/w worst case */ @@ -183,6 +186,7 @@ void usage(void) "\t try -F 0 with '-c 50%%' to hide the roll off\n" "\t[-r max_sample_rate (default: 2.4M)]\n" "\t possible values are 2M to 3.2M\n" + "\t[-E enables epoch timestamps (default: off/verbose)]\n" "\t[-P enables peak hold (default: off/averaging)]\n" "\t[-L enable linear output (default: off/dB)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" @@ -998,6 +1002,7 @@ void init_misc(struct misc_settings *ms) ms->smoothing = 0; ms->peak_hold = 0; ms->linear = 0; + ms->time_mode = VERBOSE_TIME; } int main(int argc, char **argv) @@ -1009,6 +1014,7 @@ int main(int argc, char **argv) int i, r, opt; int f_set = 0; int dev_index = 0; + char dev_label[255]; int dev_given = 0; int ppm_error = 0; int custom_ppm = 0; @@ -1026,8 +1032,9 @@ int main(int argc, char **argv) struct misc_settings ms; freq_optarg = ""; init_misc(&ms); + strcpy(dev_label, "DEFAULT"); - while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1PLD:Oh")) != -1) { + while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1EPLD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size if (f_set) { @@ -1037,6 +1044,7 @@ int main(int argc, char **argv) break; case 'd': dev_index = verbose_device_search(optarg); + strncpy(dev_label, optarg, 255); dev_given = 1; break; case 'g': @@ -1088,6 +1096,9 @@ int main(int argc, char **argv) case '1': single = 1; break; + case 'E': + ms.time_mode = EPOCH_TIME; + break; case 'P': ms.peak_hold = 1; break; @@ -1215,7 +1226,12 @@ int main(int argc, char **argv) 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); + if (ms.time_mode == VERBOSE_TIME) { + strftime(t_str, 50, "%Y-%m-%d, %H:%M:%S", cal_time); + } + if (ms.time_mode == EPOCH_TIME) { + snprintf(t_str, 50, "%u, %s", (unsigned)time_now, dev_label); + } for (i=0; i Date: Sun, 29 Mar 2015 12:07:27 +0100 Subject: [PATCH 63/64] rtl_fm: add swagc-aggressive --- src/rtl_fm.c | 54 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/rtl_fm.c b/src/rtl_fm.c index e8b48cf..6c11a76 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -103,6 +103,13 @@ static int atan_lut_coef = 8; int16_t shared_samples[SHARED_SIZE][MAXIMUM_BUF_LENGTH]; int ss_busy[SHARED_SIZE] = {0}; +enum agc_mode_t +{ + agc_off = 0, + agc_normal, + agc_aggressive +}; + struct dongle_state { int exit_flag; @@ -170,7 +177,7 @@ struct demod_state int dc_block, dc_avg; int rotate_enable; struct translate_state rotate; - int agc_enable; + enum agc_mode_t agc_mode; struct agc_state *agc; void (*mode_demod)(struct demod_state*); pthread_rwlock_t rw; @@ -248,6 +255,7 @@ void usage(void) "\t no-dc: disable dc blocking filter\n" "\t deemp: enable de-emphasis filter\n" "\t swagc: enable software agc (only for AM modes)\n" + "\t swagc-aggressive: enable aggressive software agc (only for AM modes)\n" "\t direct: enable direct sampling\n" "\t no-mod: enable no-mod direct sampling\n" "\t offset: enable offset tuning\n" @@ -823,19 +831,31 @@ void software_agc(struct demod_state *d) { int i = 0; int peaked = 0; - int32_t output; + int32_t output = 0; + int abs_output = 0; struct agc_state *agc = d->agc; int16_t *lp = d->lowpassed; + int attack_step = agc->attack_step; + int aggressive = agc_aggressive == d->agc_mode; + float peak_factor = 1.0; for (i=0; i < d->lp_len; i++) { output = (int32_t)lp[i] * agc->gain_num + agc->error; agc->error = output % agc->gain_den; output /= agc->gain_den; + abs_output = abs(output); + peaked = abs_output > agc->peak_target; + + if (peaked && aggressive && attack_step <= 1) { + peak_factor = fmin(5.0, (float) abs_output / agc->peak_target); + attack_step = (int) (pow(agc->attack_step - peak_factor, peak_factor) * (176 + 3 * peak_factor)); + } - if (!peaked && abs(output) > agc->peak_target) { - peaked = 1;} if (peaked) { - agc->gain_num += agc->attack_step; + agc->gain_num -= attack_step; + if (aggressive) { + attack_step = (int) (attack_step / 1.2); + } } else { agc->gain_num += agc->decay_step; } @@ -901,7 +921,7 @@ void full_demod(struct demod_state *d) return;} if (d->dc_block) { dc_block_filter(d);} - if (d->agc_enable) { + if (d->agc_mode != agc_off) { software_agc(d);} /* todo, fm noise squelch */ // use nicer filter here too? @@ -1167,7 +1187,7 @@ void clone_demod(struct demod_state *d2, struct demod_state *d1) d2->deemph_a = d1->deemph_a; d2->dc_block = d1->dc_block; d2->rotate_enable = d1->rotate_enable; - d2->agc_enable = d1->agc_enable; + d2->agc_mode = d1->agc_mode; d2->mode_demod = d1->mode_demod; } @@ -1310,7 +1330,7 @@ void demod_init(struct demod_state *s) s->post_downsample = 1; // once this works, default = 4 s->custom_atan = 0; s->deemph = 0; - s->agc_enable = 0; + s->agc_mode = agc_off; s->rotate_enable = 0; s->rotate.len = 0; s->rotate.sincos = NULL; @@ -1409,11 +1429,16 @@ int agc_init(struct demod_state *s) s->agc = agc; agc->gain_den = 1<<15; - agc->gain_num = agc->gain_den; agc->peak_target = 1<<14; agc->gain_max = 256 * agc->gain_den; - agc->decay_step = 1; - agc->attack_step = -2; + agc->gain_num = agc->gain_den; + agc->decay_step = 1; + agc->attack_step = 2; + if (s->agc_mode == agc_aggressive) { + agc->decay_step = agc->decay_step * 4; + agc->attack_step = agc->attack_step * 5; + } + return 0; } @@ -1463,7 +1488,6 @@ int main(int argc, char **argv) dongle_init(&dongle); demod_init(&demod); demod_init(&demod2); - agc_init(&demod); output_init(&output); controller_init(&controller); @@ -1523,7 +1547,9 @@ int main(int argc, char **argv) if (strcmp("deemp", optarg) == 0) { demod.deemph = 1;} if (strcmp("swagc", optarg) == 0) { - demod.agc_enable = 1;} + demod.agc_mode = agc_normal;} + if (strcmp("swagc-aggressive", optarg) == 0) { + demod.agc_mode = agc_aggressive;} if (strcmp("direct", optarg) == 0) { dongle.direct_sampling = 1;} if (strcmp("no-mod", optarg) == 0) { @@ -1583,6 +1609,8 @@ int main(int argc, char **argv) } } + agc_init(&demod); + /* quadruple sample_rate to limit to Δθ to ±π/2 */ demod.rate_in *= demod.post_downsample; From 0d825fe08ef1e0225340fa7d8dffa621ad80a818 Mon Sep 17 00:00:00 2001 From: Kyle Keen Date: Thu, 31 Dec 2015 19:22:39 -0500 Subject: [PATCH 64/64] rtl_power: trough hold --- src/rtl_power.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/rtl_power.c b/src/rtl_power.c index c3adf32..217b745 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -67,6 +67,7 @@ #include "convenience/convenience.h" #define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define DEFAULT_BUF_LENGTH (1 * 16384) #define AUTO_GAIN -100 @@ -104,7 +105,7 @@ struct tuning_state int downsample; int downsample_passes; /* for the recursive filter */ int comp_fir_size; - int peak_hold; + int peak_hold; /* 1 = peak, 0 = off, -1 = trough */ int linear; int bin_spec; double crop; @@ -188,6 +189,7 @@ void usage(void) "\t possible values are 2M to 3.2M\n" "\t[-E enables epoch timestamps (default: off/verbose)]\n" "\t[-P enables peak hold (default: off/averaging)]\n" + "\t[-T enables trough hold (default: off/averaging)]\n" "\t[-L enable linear output (default: off/dB)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" "\t[-O enable offset tuning (default: off)]\n" @@ -479,10 +481,12 @@ void rms_power(struct tuning_state *ts) err = t * 2 * dc - dc * dc * buf_len; p -= (int64_t)round(err); - if (!ts->peak_hold) { + if (ts->peak_hold == 0) { ts->avg[0] += p; - } else { + } else if (ts->peak_hold == 1) { ts->avg[0] = MAX(ts->avg[0], p); + } else if (ts->peak_hold == -1) { + ts->avg[0] = MIN(ts->avg[0], p); } ts->samples += 1; } @@ -685,7 +689,10 @@ void frequency_range(char *arg, struct misc_settings *ms) exit(1); } for (j=0; j<(1<avg[j] = 0L; + if (ts->peak_hold == -1) { + ts->avg[j] = 1e6; + } else { + ts->avg[j] = 0L;} } ts->buf8 = (uint8_t*)malloc(buf_len * sizeof(uint8_t)); if (!ts->buf8) { @@ -933,14 +940,18 @@ void scanner(void) fft_buf[offset+j*2+1] = (int16_t)w; } fix_fft(fft_buf+offset, bin_e, ts->sine); - if (!ts->peak_hold) { + if (ts->peak_hold == 0) { for (j=0; javg[j] += real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]); } - } else { + } else if (ts->peak_hold == 1){ for (j=0; javg[j] = MAX(real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]), ts->avg[j]); } + } else if (ts->peak_hold == -1){ + for (j=0; javg[j] = MIN(real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]), ts->avg[j]); + } } ts->samples += ds; } @@ -975,7 +986,7 @@ void csv_dbm(struct tuning_state *ts) } dbm = (double)ts->avg[i]; dbm /= (double)ts->rate; - if (!ts->peak_hold) { + if (ts->peak_hold == 0) { dbm /= (double)ts->samples; } if (ts->linear) { @@ -986,7 +997,10 @@ void csv_dbm(struct tuning_state *ts) } } for (i=0; iavg[i] = 0L; + if (ts->peak_hold == -1) { + ts->avg[i] = 1e6; + } else { + ts->avg[i] = 0L;} } ts->samples = 0; } @@ -1034,7 +1048,7 @@ int main(int argc, char **argv) init_misc(&ms); strcpy(dev_label, "DEFAULT"); - while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1EPLD:Oh")) != -1) { + while ((opt = getopt(argc, argv, "f:i:s:r:t:d:g:p:e:w:c:F:1EPTLD:Oh")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size if (f_set) { @@ -1102,6 +1116,9 @@ int main(int argc, char **argv) case 'P': ms.peak_hold = 1; break; + case 'T': + ms.peak_hold = -1; + break; case 'L': ms.linear = 1; break;