hello list, with this patch I try to start adding support for independent gain setting to rtl-sdr. If this will go on (after review of course) I will be able to send a patch for gr-osmosdr to expose those controls to end user applications like gqrx. I see some users wish to experiment with manual tuning like it's possible to do on SDR# with airspy dongle that use R820T tuner. I see some users started their own tree to add this. There will be some complexity added but I think this can be masked with a proper patching of gr-osmosdr to no break compatibility with existing software and let users get the old control layout.
In a future patch I wish to add: - single automatic gain control. I ask if it's ok to expose a function like rtlsdr_set_tuner_gain_mode() and internally recycle the function added by this patch but with a new *mode* parameter. - optimized gain for *max sensitivity* and *max linearity* like those found in airspy code. here I ask if is fine to expose gains as just integers instead of tenth of dB.
best regards Luigi
Signed-off-by: Luigi Tarenga luigi.tarenga@gmail.com --- include/rtl-sdr.h | 62 ++++++++++++++++++++++++++ include/tuner_r82xx.h | 3 ++ src/librtlsdr.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++--- src/tuner_r82xx.c | 53 +++++++++++++++++++++- 4 files changed, 230 insertions(+), 8 deletions(-)
diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index fe64bea..2eec99c 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -216,6 +216,41 @@ RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
/*! + * Set the LNA gain for the device. + * + * Valid gain values for the R820T tuner are from 0 to 15. + * + * \param dev the device handle given by rtlsdr_open() + * \param gain between 0 and 15. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_lna_gain(rtlsdr_dev_t *dev, int gain); + +/*! + * Set the Mixer gain for the device. + * Manual gain mode must be enabled for this to work. + * + * Valid gain values for the R820T tuner are from 0 to 15. + * + * \param dev the device handle given by rtlsdr_open() + * \param gain between 0 and 15. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_mix_gain(rtlsdr_dev_t *dev, int gain); + +/*! + * Set the VGA gain for the device. + * Manual gain mode must be enabled for this to work. + * + * Valid gain values for the R820T tuner are from 0 to 15. + * + * \param dev the device handle given by rtlsdr_open() + * \param gain between 0 and 15. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_vga_gain(rtlsdr_dev_t *dev, int gain); + +/*! * Set the bandwidth for the device. * * \param dev the device handle given by rtlsdr_open() @@ -233,6 +268,33 @@ RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw); RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev);
/*! + * Get actual LNA gain the device is configured to. + * The LNA gain must have been set with \ref rtlsdr_set_tuner_lna_gain function. + * + * \param dev the device handle given by rtlsdr_open() + * \return -1 on error, LNA gain register value (0 to 15 for R820T). + */ +RTLSDR_API int rtlsdr_get_tuner_lna_gain(rtlsdr_dev_t *dev); + +/*! + * Get actual Mixer gain the device is configured to. + * The Mixer gain must have been set with \ref rtlsdr_set_tuner_mix_gain function. + * + * \param dev the device handle given by rtlsdr_open() + * \return -1 on error, Mixer gain register value (0 to 15 for R820T). + */ +RTLSDR_API int rtlsdr_get_tuner_mix_gain(rtlsdr_dev_t *dev); + +/*! + * Get actual VGA gain the device is configured to. + * The VGA gain must have been set with \ref rtlsdr_set_tuner_vga_gain function. + * + * \param dev the device handle given by rtlsdr_open() + * \return -1 on error, VGA gain register value (0 to 15 for R820T). + */ +RTLSDR_API int rtlsdr_get_tuner_vga_gain(rtlsdr_dev_t *dev); + +/*! * Set the intermediate frequency gain for the device. * * \param dev the device handle given by rtlsdr_open() diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index f6c206a..3ddb9c6 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_mix_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 9b7ba52..10556a8 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -62,6 +62,9 @@ typedef struct rtlsdr_tuner_iface { int (*set_freq)(void *, uint32_t freq /* Hz */); int (*set_bw)(void *, int bw /* Hz */); int (*set_gain)(void *, int gain /* tenth dB */); + int (*set_lna_gain)(void *, int gain /* register value */); + int (*set_mix_gain)(void *, int gain /* register value */); + int (*set_vga_gain)(void *, int gain /* register value */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); } rtlsdr_tuner_iface_t; @@ -117,6 +120,9 @@ struct rtlsdr_dev { uint32_t offs_freq; /* Hz */ int corr; /* ppm */ int gain; /* tenth dB */ + int lna_gain; /* register value */ + int mix_gain; /* register value */ + int vga_gain; /* register value */ struct e4k_state e4k_s; struct r82xx_config r82xx_c; struct r82xx_priv r82xx_p; @@ -258,6 +264,18 @@ 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); } +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_mix_gain(void *dev, int gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_mix_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,36 +284,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, 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_freq, e4000_set_bw, e4000_set_gain, NULL, NULL, NULL, e4000_set_if_gain, e4000_set_gain_mode }, { _fc0012_init, fc0012_exit, - fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, + fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, NULL, NULL, NULL, fc0012_set_gain_mode }, { _fc0013_init, fc0013_exit, - fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, + fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, NULL, NULL, NULL, fc0013_set_gain_mode }, { fc2580_init, fc2580_exit, - _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, + _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, NULL, NULL, NULL, fc2580_set_gain_mode }, { r820t_init, r820t_exit, - r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, + r820t_set_freq, r820t_set_bw, r820t_set_gain, + r820t_set_lna_gain, r820t_set_mix_gain, r820t_set_vga_gain, NULL, r820t_set_gain_mode }, { r820t_init, r820t_exit, - r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, + r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, NULL, NULL, NULL, r820t_set_gain_mode }, }; @@ -1049,6 +1068,69 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) return r; }
+int rtlsdr_set_tuner_lna_gain(rtlsdr_dev_t *dev, int gain) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_lna_gain) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_lna_gain((void *)dev, gain); + rtlsdr_set_i2c_repeater(dev, 0); + } + + if (!r) + dev->lna_gain = gain; + else + dev->lna_gain = 0; + + return r; +} + +int rtlsdr_set_tuner_mix_gain(rtlsdr_dev_t *dev, int gain) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_mix_gain) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_mix_gain((void *)dev, gain); + rtlsdr_set_i2c_repeater(dev, 0); + } + + if (!r) + dev->mix_gain = gain; + else + dev->mix_gain = 0; + + return r; +} + +int rtlsdr_set_tuner_vga_gain(rtlsdr_dev_t *dev, int gain) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_vga_gain) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_vga_gain((void *)dev, gain); + rtlsdr_set_i2c_repeater(dev, 0); + } + + if (!r) + dev->vga_gain = gain; + else + dev->vga_gain = 0; + + return r; +} + int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) { if (!dev) @@ -1057,6 +1139,30 @@ int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) return dev->gain; }
+int rtlsdr_get_tuner_lna_gain(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + return dev->lna_gain; +} + +int rtlsdr_get_tuner_mix_gain(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + return dev->mix_gain; +} + +int rtlsdr_get_tuner_vga_gain(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + return dev->vga_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 f620238..64d412b 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -1018,7 +1018,7 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) if (rc < 0) return rc;
- /* Mixer auto off */ + /* Mixer auto off */ rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); if (rc < 0) return rc; @@ -1073,6 +1073,57 @@ 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; + + /* LNA auto off */ + rc = r82xx_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + /* set LNA gain */ + rc = r82xx_write_reg_mask(priv, 0x05, (uint8_t) gain, 0x0f); + if (rc < 0) + return rc; + + return 0; +} + +int r82xx_set_mix_gain(struct r82xx_priv *priv, int gain) +{ + int rc; + + /* Mixer auto off */ + rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + /* set Mixer gain */ + rc = r82xx_write_reg_mask(priv, 0x07, (uint8_t) gain, 0x0f); + if (rc < 0) + return rc; + + return 0; +} + +int r82xx_set_vga_gain(struct r82xx_priv *priv, int gain) +{ + int rc; + + /* VGA auto off */ + rc = r82xx_write_reg_mask(priv, 0x0c, 0, 0x10); + if (rc < 0) + return rc; + + /* set VGA gain */ + rc = r82xx_write_reg_mask(priv, 0x0c, (uint8_t) gain, 0x0f); + 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
Hi,
First off, thanks for trying to clean this up into a mergeable state !
I can't actually try the patch, it doesn't apply. How did you generate and send the patch to the list ? Make sure to use git-send-email as mail-software will usually mangle the patch and prevent it from being used ...
Comments on the patch itself :
* Doc and API for the top-level functions (exposed one) should be tuner independent, so you can't reference R820T directly in there, nor can you expect the user to know register values ... that's why API use tenth of dB there and do the mapping in a tuner specific way in the actual tuner driver.
* I'm not a big fan of adding 3 methods to the API ... what happens when the R820T3 comes out with one more gain stage ? ... And yes, I know there is a "IF" one in there that pretty much matches E4k only, but IMHO that's a mistage. We can't change the past but we can certainly learn from it and avoid repeating it.
So from the API standpoint, I would use the concept of "named" gain stages. You get : - one function to query the available gain stages - one function to query for a given gain stages what gain values are possible / valid - one function to set the curent gain value - one function to get the current gain value
This way : 1) We could actually cleanup the E4k impl as well and also properly expose its various gain stages 2) In gr-osmosdr you can also query / register named gain stages at runtime depending on what's really available 3) That API should be fairly future proof.
I didn't really understand the linearity / gain question but IIUC that should be doable without new functions : The meaning of the set_gain_mode existing API could be extended to be a bitfield / flags. ( With the existing defined 0|1 value staying what they are but allowing new ones to be defined). And so you'd have the 0|2 flag to select the preferred gain setting algorithm. then the tuner can store that in its internal state and act appropriately on the next gain setting call.
CC to steve and dimitri, please chime in if you disagree with my view on this :)
Cheers,
Sylvain
Hi Sylvain, many thanks for the feedback. I apologie for the inconvenient about patch format. I used git format-email but seems I did something wrong in the cut&paste. If nobody ask for, I will avoid repost the patch at the current state.
I agree with you on all point but one. I would like to have a way to get gains values as an array of int and expose integers if wanted (maybe by just using an index?) . I can copy values from those measured by Steve of course but there can be a case where a device diverge from those (not linear, bad batch, overtemp etc..)? Since usually we work with dbFS that have nothing to do with dBm or dbuV I think using simple integer is still ok. Still I will try to put things in a way where you can query the gains value choosing between integers and tenth of dB.
I will try to put the new patch down asap but I will be on vacation next week so I'm not sure to be able to finish very soon.
best regards and happy new year Luigi
On 29/12/16 16:45, Sylvain Munaut wrote:
Hi,
First off, thanks for trying to clean this up into a mergeable state !
I can't actually try the patch, it doesn't apply. How did you generate and send the patch to the list ? Make sure to use git-send-email as mail-software will usually mangle the patch and prevent it from being used ...
Comments on the patch itself :
- Doc and API for the top-level functions (exposed one) should be
tuner independent, so you can't reference R820T directly in there, nor can you expect the user to know register values ... that's why API use tenth of dB there and do the mapping in a tuner specific way in the actual tuner driver.
- I'm not a big fan of adding 3 methods to the API ... what happens
when the R820T3 comes out with one more gain stage ? ... And yes, I know there is a "IF" one in there that pretty much matches E4k only, but IMHO that's a mistage. We can't change the past but we can certainly learn from it and avoid repeating it.
So from the API standpoint, I would use the concept of "named" gain stages. You get : - one function to query the available gain stages - one function to query for a given gain stages what gain valuesare possible / valid - one function to set the curent gain value - one function to get the current gain value
This way :
- We could actually cleanup the E4k impl as well and also properly
expose its various gain stages 2) In gr-osmosdr you can also query / register named gain stages at runtime depending on what's really available 3) That API should be fairly future proof.
I didn't really understand the linearity / gain question but IIUC that should be doable without new functions : The meaning of the set_gain_mode existing API could be extended to be a bitfield / flags. ( With the existing defined 0|1 value staying what they are but allowing new ones to be defined). And so you'd have the 0|2 flag to select the preferred gain setting algorithm. then the tuner can store that in its internal state and act appropriately on the next gain setting call.
CC to steve and dimitri, please chime in if you disagree with my view on this :)
Cheers,
Sylvain