diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index fe64bea5..f8c36046 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -169,6 +169,13 @@ RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm); */ RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev); +enum gain_types { + lna, + mixer, + vga, + total +}; + enum rtlsdr_tuner { RTLSDR_TUNER_UNKNOWN = 0, RTLSDR_TUNER_E4000, @@ -214,6 +221,16 @@ RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); +RTLSDR_API int rtlsdr_set_specific_gain(rtlsdr_dev_t *dev, int gain, enum gain_types gain_type); +RTLSDR_API int rtlsdr_set_lna_gain(rtlsdr_dev_t *dev, int gain); +RTLSDR_API int rtlsdr_set_mixer_gain(rtlsdr_dev_t *dev, int gain); +RTLSDR_API int rtlsdr_set_vga_gain(rtlsdr_dev_t *dev, int gain); + +RTLSDR_API int rtlsdr_get_lna_gains(rtlsdr_dev_t *dev, int *gains); +RTLSDR_API int rtlsdr_get_mixer_gains(rtlsdr_dev_t *dev, int *gains); +RTLSDR_API int rtlsdr_get_vga_gains(rtlsdr_dev_t *dev, int *gains); +RTLSDR_API int rtlsdr_get_specific_gains(rtlsdr_dev_t *dev, int *gains, enum gain_types gain_type); + /*! * Set the bandwidth for the device. diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index f6c206aa..b5371d07 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -115,6 +115,9 @@ 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_lna_gain(struct r82xx_priv *priv, int gain); +int r82xx_set_mixer_gain(struct r82xx_priv *priv, int gain); +int r82xx_set_vga_gain(struct r82xx_priv *priv, int gain); int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate); #endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 06506062..8cf39a92 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -64,6 +64,9 @@ 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_lna_gain)(void *, int gain /* tenth dB */); + int (*set_mixer_gain)(void *, int gain /* tenth dB */); + int (*set_vga_gain)(void *, int gain /* tenth dB */); } rtlsdr_tuner_iface_t; enum rtlsdr_async_status { @@ -254,10 +257,34 @@ int r820t_set_bw(void *dev, int bw) { return rtlsdr_set_center_freq(devt, devt->freq); } +/* + * r820t_set_gain sets vga gain to 16 and then + * increments lna and mixer gains until the sum + * is greater than or equal to the desired gain. + */ 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); } + +/* + * The following three functions allow for setting + * the lna, mixer and vga gains independently. + */ +int r820t_set_lna_gain(void *dev, int gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_lna_gain(&devt->r82xx_p, gain); +} +int r820t_set_mixer_gain(void *dev, int gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_mixer_gain(&devt->r82xx_p, gain); +} +int r820t_set_vga_gain(void *dev, int gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_vga_gain(&devt->r82xx_p, gain); +} + + int r820t_set_gain_mode(void *dev, int manual) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_gain(&devt->r82xx_p, manual, 0); @@ -266,37 +293,43 @@ 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, 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,NULL,NULL }, { _fc0012_init, fc0012_exit, fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, - fc0012_set_gain_mode + fc0012_set_gain_mode, + NULL,NULL,NULL }, { _fc0013_init, fc0013_exit, fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, - fc0013_set_gain_mode + fc0013_set_gain_mode, + NULL,NULL,NULL }, { fc2580_init, fc2580_exit, _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, - fc2580_set_gain_mode + fc2580_set_gain_mode, + NULL,NULL,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_lna_gain, r820t_set_mixer_gain, r820t_set_vga_gain }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, - r820t_set_gain_mode + r820t_set_gain_mode, + r820t_set_lna_gain, r820t_set_mixer_gain, r820t_set_vga_gain }, }; @@ -955,6 +988,72 @@ enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) return dev->tuner_type; } +int rtlsdr_get_lna_gains(rtlsdr_dev_t *dev, int *gains) +{ + return rtlsdr_get_specific_gains(dev,gains,lna); +} +int rtlsdr_get_mixer_gains(rtlsdr_dev_t *dev, int *gains) +{ + return rtlsdr_get_specific_gains(dev,gains,mixer); +} +int rtlsdr_get_vga_gains(rtlsdr_dev_t *dev, int *gains) +{ + return rtlsdr_get_specific_gains(dev,gains,vga); +} + +/* + * rtlsdr_get_specific_gains is essentially a copy of + * rtlsdr_get_tuner_gains but for getting the individual + * lna, mixer and vga gains (currently hard coded below). + * I don't like the double switch, but it was quick and + * allows for expansion to support other tuners so it + * will do for now. + */ +int rtlsdr_get_specific_gains(rtlsdr_dev_t *dev, int *gains,enum gain_types gain_type) +{ + const int r82xx_lna_gains[] = { 0, 9, 22, 62, 100, 113, 144, 166, 192, 223, 249, 263, 282, 287, 322, 335 }; + const int r82xx_mixer_gains[] = { 0, 5, 15, 25, 44, 53, 63, 88, 105, 115, 123, 139, 152, 158, 161, 153 }; + const int r82xx_vga_gains[] = { 0, 26, 52, 82, 124, 159, 183, 196, 210, 242, 278, 312, 347, 384, 419, 455 }; + + const int unknown_lna_gains[] = { 0 /* no gain values */ }; + + const int *ptr = NULL; + int len = 0; + + if (!dev) + return -1; + + switch (dev->tuner_type) { + case RTLSDR_TUNER_R820T: + case RTLSDR_TUNER_R828D: + switch(gain_type) + { + case lna: + ptr = r82xx_lna_gains; len = sizeof(r82xx_lna_gains); + break; + case mixer: + ptr = r82xx_mixer_gains; len = sizeof(r82xx_mixer_gains); + break; + case vga: + default: + ptr = r82xx_vga_gains; len = sizeof(r82xx_vga_gains); + } + break; + default: + ptr = unknown_lna_gains; len = sizeof(unknown_lna_gains); + break; + } + + if (!gains) { /* no buffer provided, just return the count */ + return len / sizeof(int); + } else { + if (len) + memcpy(gains, ptr, len); + + return len / sizeof(int); + } +} + int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) { /* all gain values are expressed in tenths of a dB */ @@ -1028,15 +1127,67 @@ int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw) } int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) +{ + return rtlsdr_set_specific_gain(dev,gain,total); +} + +int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) +{ + if (!dev) + return 0; + + return dev->gain; +} + +/* + * rtlsdr_set_lna_gain, rtlsdr_set_mixer_gain and rtlsdr_set_vga_gain + * all call rtlsdr_set_specific_gain. I've also rolled + * rtlsdr_set_tuner_gain (above) into this as it's using the same code. + */ +int rtlsdr_set_lna_gain(rtlsdr_dev_t *dev, int gain) +{ + return rtlsdr_set_specific_gain(dev,gain,lna); +} +int rtlsdr_set_mixer_gain(rtlsdr_dev_t *dev, int gain) +{ + return rtlsdr_set_specific_gain(dev,gain,mixer); +} +int rtlsdr_set_vga_gain(rtlsdr_dev_t *dev, int gain) +{ + return rtlsdr_set_specific_gain(dev,gain,vga); +} + +int rtlsdr_set_specific_gain(rtlsdr_dev_t *dev, int gain, enum gain_types gain_type) { int r = 0; + + int (*ptr_set_gain)(void *, int gain /* tenth dB */) = NULL; if (!dev || !dev->tuner) return -1; - if (dev->tuner->set_gain) { + switch(gain_type) + { + case lna: + if (dev->tuner->set_lna_gain) + ptr_set_gain = (dev->tuner->set_lna_gain); + break; + case mixer: + if (dev->tuner->set_mixer_gain) + ptr_set_gain = (dev->tuner->set_mixer_gain); + break; + case vga: + if (dev->tuner->set_vga_gain) + ptr_set_gain = (dev->tuner->set_vga_gain); + break; + default: + if(dev->tuner->set_gain) + ptr_set_gain = (dev->tuner->set_gain); + } + + if (ptr_set_gain) { rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_gain((void *)dev, gain); + r = ptr_set_gain((void *)dev, gain); rtlsdr_set_i2c_repeater(dev, 0); } @@ -1048,14 +1199,6 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) return r; } -int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) -{ - if (!dev) - return 0; - - return dev->gain; -} - int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) { int r = 0; diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index f6202384..7cce20df 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -1004,6 +1004,7 @@ static const int r82xx_mixer_gain_steps[] = { 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 }; + int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) { int rc; @@ -1073,6 +1074,92 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) return 0; } +int r82xx_set_lna_gain(struct r82xx_priv *priv, int gain) +{ + int rc; + + int i, total_gain = 0; + uint8_t lna_index = 0; + uint8_t data[4]; + + /* LNA auto off */ + rc = r82xx_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r82xx_lna_gain_steps[++lna_index]; + + } + + /* set LNA gain */ + rc = r82xx_write_reg_mask(priv, 0x05, lna_index, 0x0f); + if (rc < 0) + return rc; + + return 0; +} + +int r82xx_set_mixer_gain(struct r82xx_priv *priv, int gain) +{ + int rc; + + int i, total_gain = 0; + uint8_t mix_index = 0; + uint8_t data[4]; + + /* Mixer auto off */ + rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + rc = r82xx_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r82xx_mixer_gain_steps[++mix_index]; + } + + /* set Mixer gain */ + rc = r82xx_write_reg_mask(priv, 0x07, mix_index, 0x0f); + if (rc < 0) + return rc; + + return 0; +} + + +int r82xx_set_vga_gain(struct r82xx_priv *priv, int gain) +{ + int rc; + + int i, total_gain = 0; + uint8_t vga_index = 0; + uint8_t data[4]; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r82xx_vga_gain_steps[++vga_index]; + } + + /* set VGA gain */ + rc = r82xx_write_reg_mask(priv, 0x0c, vga_index, 0x9f); + if (rc < 0) + return rc; + + return 0; +} + + /* Bandwidth contribution by low-pass filter. */ static const int r82xx_if_low_pass_bw_table[] = { 1700000, 1600000, 1550000, 1450000, 1200000, 900000, 700000, 550000, 450000, 350000