added librtsdr options vcocmin/vcocmax/vcoalgo for testing

* see https://github.com/librtlsdr/librtlsdr/issues/91
* vcocmin/vcocmax: sets VCO current for R820T/2
* added tuner internal caching for VCO current,
 that register isn't written when unnecessary
* added vcoalgo option:
- vcoalgo=2 allows to select/use r82xx_set_pll() from
 https://github.com/rtlsdrblog/rtl-sdr.git
- vcoalgo=1 is previous algorithm, just with higher vco_max=3.9GHz
- vcoalgo=0 is previous algorithm - the default:
 kept this until https://github.com/steve-m/librtlsdr/pull/10
 is measured
* rtl_test: added options -f and -e to define where to start and end
 the tuner range test .. for quicker testing if the new options
 change/extend the tuner's frequency range

Signed-off-by: hayati ayguen <h_ayguen@web.de>
development
hayati ayguen 2020-09-09 08:02:49 +02:00
parent 907da08bfc
commit c7d071e17e
4 changed files with 265 additions and 31 deletions

View File

@ -68,6 +68,9 @@ enum r82xx_xtal_cap_value {
struct r82xx_config {
uint8_t i2c_addr;
uint8_t vco_curr_min; /* VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */
uint8_t vco_curr_max; /* value is inverted: programmed is 7-value, that 0 is lowest current */
uint8_t vco_algo;
uint32_t xtal;
enum r82xx_chip rafael_chip;
unsigned int max_i2c_msg_len;
@ -89,6 +92,7 @@ struct r82xx_priv {
* on which the band center shall be positioned */
uint8_t fil_cal_code;
uint8_t input;
uint8_t last_vco_curr;
int has_lock;
int init_done;
int sideband;

View File

@ -99,6 +99,8 @@
#define LOG_API_SET_FREQ 0
#define INIT_R820T_TUNER_GAIN 0
#define ENABLE_VCO_OPTIONS 1
/* activate/use RTL's IF AGC control .. from https://github.com/old-dab/rtlsdr
* purpose: make AGC more smooth .. and NOT freeze
@ -379,6 +381,10 @@ int r820t_init(void *dev) {
devt->r82xx_c.rafael_chip = CHIP_R820T;
}
devt->r82xx_c.vco_curr_min = 0xff; /* VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */
devt->r82xx_c.vco_curr_max = 0xff; /* value is inverted: programmed is 7-value, that 0 is lowest current */
devt->r82xx_c.vco_algo = 0x00;
rtlsdr_get_xtal_freq(devt, NULL, &devt->r82xx_c.xtal);
devt->r82xx_c.max_i2c_msg_len = 8;
@ -4184,6 +4190,11 @@ const char * rtlsdr_get_opt_help(int longInfo)
"\t\t 0: use I & Q; 1: use I; 2: use Q; 3: use I below threshold frequency;\n"
"\t\t 4: use Q below threshold frequency (=RTL-SDR v3)\n"
"\t\t other values set the threshold frequency\n"
#if ENABLE_VCO_OPTIONS
"\t\tvcocmin=<current> set R820T/2 VCO current min: 0..7: higher value is more current\n"
"\t\tvcocmax=<current> set R820T/2 VCO current max: 0..7\n"
"\t\tvcoalgo=<algo> set R820T/2 VCO algorithm. 0: default. 1: with vcomax=3.9G. 2: Youssef/Carl\n"
#endif
"\t\tTp=<gpio_pin> set GPIO pin for Bias T, default =0 for rtl-sdr.com compatible V3\n"
"\t\tT=<bias_tee> 1 activates power at antenna one some dongles, e.g. rtl-sdr.com's V3\n"
#ifdef WITH_UDP_SERVER
@ -4196,7 +4207,12 @@ const char * rtlsdr_get_opt_help(int longInfo)
"\t[-O\tset RTL options string seperated with ':', e.g. -O 'bc=30000:agc=0' ]\n"
"\t\tverbose:f=<freqHz>:bw=<bw_in_kHz>:bc=<if_in_Hz>:sb=<sideband>\n"
"\t\tagc=<tuner_gain_mode>:gain=<tenth_dB>:ifm=<tuner_if_mode>:dagc=<rtl_agc>\n"
#if ENABLE_VCO_OPTIONS
"\t\tds=<direct_sampling>:dm=<ds_mode_thresh>:vcocmin=<c>:vcocmax=<c>:vcoalgo=<a>\n"
"\t\tT=<bias_tee>\n"
#else
"\t\tds=<direct_sampling>:dm=<ds_mode_thresh>:T=<bias_tee>\n"
#endif
#ifdef WITH_UDP_SERVER
"\t\tport=<udp_port default with 1>\n"
#endif
@ -4314,6 +4330,47 @@ int rtlsdr_set_opt_string(rtlsdr_dev_t *dev, const char *opts, int verbose)
dev->direct_sampling_threshold = dm;
ret = rtlsdr_set_ds_mode(dev, dev->direct_sampling_mode, dev->direct_sampling_threshold);
}
#if ENABLE_VCO_OPTIONS
else if (!strncmp(optPart, "vcocmin=", 8)) {
int current = atoi(optPart +8);
if ( 0 <= current && current <= 7 )
{
dev->r82xx_c.vco_curr_min = 7 - current;
ret = 0;
if (verbose)
fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcocmin config %d\n", current);
} else if (verbose) {
fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcocmin config: valid range 0 .. 7\n");
ret = 1;
}
}
else if (!strncmp(optPart, "vcocmax=", 8)) {
int current = atoi(optPart +8);
if ( 0 <= current && current <= 7 )
{
dev->r82xx_c.vco_curr_max = 7 - current;
ret = 0;
if (verbose)
fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcocmax config %d\n", current);
} else if (verbose) {
fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcocmax config: valid range 0 .. 7\n");
ret = 1;
}
}
else if (!strncmp(optPart, "vcoalgo=", 8)) {
int algo = atoi(optPart +8);
if ( 0 <= algo && algo <= 2 )
{
dev->r82xx_c.vco_curr_max = algo;
ret = 0;
if (verbose)
fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcoalgo config %d\n", algo);
} else if (verbose) {
fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcoalgo config: valid range 0 .. 2\n");
ret = 1;
}
}
#endif
else if (!strncmp(optPart, "tp=", 3) || !strncmp(optPart, "Tp=", 3) || !strncmp(optPart, "TP=", 3) ) {
int gpio_pin_no = atoi(optPart +3);
if (verbose)

View File

@ -107,6 +107,8 @@ void usage(void)
"\t[-d device_index or serial (default: 0)]\n"
"%s"
"\t[-t enable tuner range benchmark]\n"
"\t[-f first/begin frequency for tuner range benchmark, default: 0]\n"
"\t[-e end frequency for tuner range benchmark, default: 3e9 = 3G ]\n"
#ifndef _WIN32
"\t[-p[seconds] enable PPM error measurement (default: 10 seconds)]\n"
#endif
@ -338,9 +340,9 @@ static int set_center_freq_wait(rtlsdr_dev_t *dev, uint32_t freq, const char * s
}
void tuner_benchmark(void)
void tuner_benchmark(uint32_t beg_freq, uint32_t end_freq)
{
uint32_t current = max_step(0);
uint32_t current = beg_freq; /* max_step(0); */
uint32_t band_start = 0;
uint32_t low_bound = 0, high_bound = 0;
int rc;
@ -357,16 +359,16 @@ void tuner_benchmark(void)
*/
/* handle bands starting at 0Hz */
rc = set_center_freq_wait(dev, 0, "FIND_START");
rc = set_center_freq_wait(dev, current, "FIND_START");
if (rc < 0)
state = FIND_START;
else {
band_start = 0;
band_start = current;
report_band_start(band_start);
state = FIND_END;
}
while (current < 3e9 && !do_exit) {
while (current < end_freq && !do_exit) {
switch (state) {
case FIND_START:
/* scanning for the start of a new band */
@ -508,10 +510,12 @@ int main(int argc, char **argv)
int dev_index = 0;
int dev_given = 0;
uint32_t out_block_size = DEFAULT_BUF_LENGTH;
uint32_t tuner_bench_beg_freq = 0;
uint32_t tuner_bench_end_freq = 0;
int count;
int gains[100];
while ((opt = getopt(argc, argv, "d:s:b:O:tp::Sh")) != -1) {
while ((opt = getopt(argc, argv, "d:s:b:O:tf:e:p::Sh")) != -1) {
switch (opt) {
case 'd':
dev_index = verbose_device_search(optarg);
@ -529,6 +533,12 @@ int main(int argc, char **argv)
case 't':
test_mode = TUNER_BENCHMARK;
break;
case 'f':
tuner_bench_beg_freq = (uint32_t)atofs(optarg);
break;
case 'e':
tuner_bench_end_freq = (uint32_t)atofs(optarg);
break;
case 'p':
test_mode = PPM_BENCHMARK;
if (optarg)
@ -598,7 +608,7 @@ int main(int argc, char **argv)
}
if (test_mode == TUNER_BENCHMARK) {
tuner_benchmark();
tuner_benchmark(tuner_bench_beg_freq, tuner_bench_end_freq);
goto exit;
}

View File

@ -33,6 +33,9 @@
#define WITH_ASYM_FILTER 0
#define PRINT_PLL_ERRORS 0
#define PRINT_VGA_REG 0
#define PRINT_INITIAL_REGISTERS 0
#define PRINT_ACTUAL_VCO_AND_ERR 0
/* #define VGA_FOR_AGC_MODE 16 */
#define DEFAULT_IF_VGA_VAL 11
@ -711,6 +714,128 @@ static int r82xx_set_mux(struct r82xx_priv *priv, uint32_t freq)
return rc;
}
/* function of Youssef (AirSpy) and Carl (RTL-SDR) */
static int r82xx_set_pll_yc(struct r82xx_priv *priv, uint32_t freq)
{
const uint32_t vco_min = 1770000000;
const uint32_t vco_max = 3900000000;
uint32_t pll_ref = (priv->cfg->xtal);
uint32_t pll_ref_2x = (pll_ref * 2);
int rc;
uint32_t vco_exact;
uint32_t vco_frac;
uint32_t con_frac;
uint32_t div_num;
uint32_t n_sdm;
uint16_t sdm;
uint8_t ni;
uint8_t si;
uint8_t nint;
uint8_t val_dith;
uint8_t data[5];
/* Calculate divider */
for (div_num = 0; div_num < 5; div_num++)
{
vco_exact = freq << (div_num + 1);
if (vco_exact >= vco_min && vco_exact <= vco_max)
{
break;
}
}
vco_exact = freq << (div_num + 1);
nint = (uint8_t) ((vco_exact + (pll_ref >> 16)) / pll_ref_2x);
vco_frac = vco_exact - pll_ref_2x * nint;
nint -= 13;
ni = (nint >> 2);
si = nint - (ni << 2);
/* Set the phase splitter */
rc = r82xx_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0);
if(rc < 0)
return rc;
/* Disable Dither */
val_dith = (priv->disable_dither) ? 0x10 : 0x00;
rc = r82xx_write_reg_mask(priv, 0x12, val_dith, 0x18);
if (rc < 0)
return rc;
/* Set the rough VCO frequency */
rc = r82xx_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6)));
if(rc < 0)
return rc;
if (vco_frac == 0)
{
/* Disable frac pll */
rc = r82xx_write_reg_mask(priv, 0x12, 0x08, 0x08);
if(rc < 0)
return rc;
}
else
{
vco_frac += pll_ref >> 16;
sdm = 0;
for(n_sdm = 0; n_sdm < 16; n_sdm++)
{
con_frac = pll_ref >> n_sdm;
if (vco_frac >= con_frac)
{
sdm |= (uint16_t) (0x8000 >> n_sdm);
vco_frac -= con_frac;
if (vco_frac == 0)
break;
}
}
/*
actual_freq = (((nint << 16) + sdm) * (uint64_t) pll_ref_2x) >> (div_num + 1 + 16);
delta = freq - actual_freq
if (actual_freq != freq)
{
fprintf(stderr,"Tunning delta: %d Hz", delta);
}
*/
rc = r82xx_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff));
if (rc < 0)
return rc;
rc = r82xx_write_reg(priv, 0x16, (uint8_t)(sdm >> 8));
if (rc < 0)
return rc;
/* Enable frac pll */
rc = r82xx_write_reg_mask(priv, 0x12, 0x00, 0x08);
if (rc < 0)
return rc;
}
/***/
/* Check if PLL has locked */
rc = r82xx_read(priv, 0x00, data, 3);
if (rc < 0)
return rc;
if (!(data[2] & 0x40)) {
#if PRINT_PLL_ERRORS
fprintf(stderr, "[R82XX] PLL not locked at Tuner LO %u Hz for RF %u Hz!\n",
freq, priv->rf_freq);
#endif
priv->has_lock = 0;
return -1;
}
priv->has_lock = 1;
return rc;
}
static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq)
{
/* freq == tuner's LO frequency */
@ -718,7 +843,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq)
uint64_t vco_freq;
uint64_t vco_div;
uint32_t vco_min = 1770000; /* kHz */
uint32_t vco_max = vco_min * 2; /* kHz */
uint32_t vco_max = (priv->cfg->vco_algo == 0) ? (vco_min * 2) : 3900000; /* kHz */
uint32_t freq_khz, pll_ref;
uint32_t sdm = 0;
uint8_t mix_div = 2;
@ -727,8 +852,24 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq)
uint8_t vco_power_ref = 2;
uint8_t refdiv2 = 0;
uint8_t ni, si, nint, vco_fine_tune, val;
uint8_t vco_curr_min = (priv->cfg->vco_curr_min == 0xff) ? 0x80 : ( priv->cfg->vco_curr_min << 5 );
uint8_t vco_curr_max = (priv->cfg->vco_curr_max == 0xff) ? 0x60 : ( priv->cfg->vco_curr_max << 5 );
/* devt->r82xx_c.vco_min = 0xff; * VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */
/* devt->r82xx_c.vco_max = 0xff; * value is inverted: programmed is 7-value, that 0 is lowest current */
uint8_t data[5];
if (priv->cfg->vco_algo == 2)
{
/* r82xx_set_pll_yc() assumes fixed maximum current */
if (priv->last_vco_curr != vco_curr_max) {
rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_max, 0xe0);
if (rc < 0)
return rc;
priv->last_vco_curr = vco_curr_max;
}
return r82xx_set_pll_yc(priv, freq);
}
/* Frequency in kHz */
freq_khz = (freq + 500) / 1000;
pll_ref = priv->cfg->xtal;
@ -743,9 +884,12 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq)
return rc;
/* set VCO current = 100 */
rc = r82xx_write_reg_mask(priv, 0x12, 0x80, 0xe0);
if (priv->last_vco_curr != vco_curr_min) {
rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_min, 0xe0);
if (rc < 0)
return rc;
priv->last_vco_curr = vco_curr_min;
}
/* Calculate divider */
while (mix_div <= 64) {
@ -802,7 +946,7 @@ 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 0
#if PRINT_ACTUAL_VCO_AND_ERR
{
uint64_t actual_vco = (uint64_t)2 * pll_ref * nint + (uint64_t)2 * pll_ref * sdm / 65536;
fprintf(stderr, "[R82XX] requested %u Hz; selected mix_div=%u vco_freq=%lu nint=%u sdm=%u; actual_vco=%lu; tuning error=%+dHz\n",
@ -850,14 +994,17 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq)
rc = r82xx_read(priv, 0x00, data, 3);
if (rc < 0)
return rc;
if (data[2] & 0x40)
if ( (data[2] & 0x40) || vco_curr_max == vco_curr_min )
break;
if (!i) {
/* Didn't lock. Increase VCO current */
rc = r82xx_write_reg_mask(priv, 0x12, 0x60, 0xe0);
if (priv->last_vco_curr != vco_curr_max) {
rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_max, 0xe0);
if (rc < 0)
return rc;
priv->last_vco_curr = vco_curr_max;
}
}
}
@ -1752,6 +1899,19 @@ int r82xx_init(struct r82xx_priv *priv)
{
int rc;
#if PRINT_INITIAL_REGISTERS
#define INIT_NUM_READ_REGS 16
uint8_t initial_register_values[INIT_NUM_READ_REGS]; /* see what is 'default' */
int k;
/* get initial register values - just to see .. */
memset( &(initial_register_values[0]), 0, sizeof(initial_register_values) );
printf("R820T/2 initial register settings:\n");
r82xx_read(priv, 0x00, initial_register_values, sizeof(initial_register_values));
for (k=0; k < INIT_NUM_READ_REGS; ++k)
printf("register 0x%02x: 0x%02x\n", k, initial_register_values[k]);
printf("\n");
#endif
/* TODO: R828D might need r82xx_xtal_check() */
priv->xtal_cap_sel = XTAL_HIGH_CAP_0P;
@ -1764,6 +1924,7 @@ int r82xx_init(struct r82xx_priv *priv)
priv->last_LNA_value = 0;
priv->last_Mixer_value = 0;
priv->last_VGA_value = DEFAULT_IF_VGA_VAL;
priv->last_vco_curr = 0xff;
/* Initialize override registers */
memset( &(priv->override_data[0]), 0, NUM_REGS * sizeof(uint8_t) );
@ -1773,6 +1934,8 @@ int r82xx_init(struct r82xx_priv *priv)
rc = r82xx_write_arr(priv, 0x05,
r82xx_init_array, sizeof(r82xx_init_array));
priv->last_vco_curr = r82xx_init_array[0x12 - 0x05] & 0xe0;
rc = r82xx_set_tv_standard(priv, TUNER_DIGITAL_TV, 0);
if (rc < 0)
goto err;