[RFC PATCH v2] rtl-sdr: add support for single gain stage tuning

Luigi Tarenga luigi.tarenga at gmail.com
Mon Jan 9 16:13:57 UTC 2017


Hello list,
I repropose a patch to set independent gain for each stage with r820t tuner.
The approach should be enough generalized to be extended to e4000 tuner too.

I'm looking for feedback.
best regards
Luigi

Signed-off-by: Luigi Tarenga <luigi.tarenga at gmail.com>
---
 include/rtl-sdr.h     |  59 +++++++++++++++++++++++++
 include/tuner_r82xx.h |   5 +++
 src/librtlsdr.c       | 118 +++++++++++++++++++++++++++++++++++++++++++++++---
 src/tuner_r82xx.c     |  83 +++++++++++++++++++++++++++++++++++
 4 files changed, 258 insertions(+), 7 deletions(-)

diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h
index fe64bea..3719df4 100644
--- a/include/rtl-sdr.h
+++ b/include/rtl-sdr.h
@@ -200,6 +200,65 @@ RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev);
 RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
 
 /*!
+ * Set the gain of a single gain stage for the device.
+ *
+ * Valid gain values may be queried with \ref rtlsdr_get_tuner_gain_stage_gains function.
+ * Usually those are from 0 to a positive integer plus -1 to set the stage in Auto gain mode.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param stage from 0 to number of gain stages minus 1.
+ * \param gain  from 0 to maximum supported gain for selected stage or -1 to set Auto gain mode.
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_tuner_gain_stage(rtlsdr_dev_t *dev, int stage, int gain);
+
+
+/*!
+ * Get the gain of a single gain stage for the device.
+ *
+ * The value returned is a number from 0 to maximum supported gain. or -1 to indicate Auto gain mode.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param stage from 0 to number of gain stages minus 1.
+ * \return the gain value in a range from 0 to maximum supported gain or -1 if Auto gain mode.
+ */
+RTLSDR_API int rtlsdr_get_tuner_gain_stage(rtlsdr_dev_t *dev, int stage);
+
+
+/*!
+ * Get the number of gain stages supported by the device.
+ *
+ * The value returned is a number from 0 to the number of independent gain stages in the device.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return the number of gain stages in the devices. 0 if none are supported.
+ */
+RTLSDR_API int rtlsdr_get_tuner_gain_stages(rtlsdr_dev_t *dev);
+
+/*!
+ * Get the supported values of gains in tenth of dB for a given stage.
+ *
+ * The value returned is the number of gain steps for the given stage and the maximum supported
+ * gain value plus one. The returned value -1 is the maximum value that
+ * can be used with \ref rtlsdr_set_tuner_gain_stage function as gain parameter.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param stage from 0 to number of gain stages minus 1.
+ * \param gains a pointer to an array of integer where supported dB steps will be copied. NULL for no copy.
+ * \return the number of gain steps for the given stage.
+ */
+RTLSDR_API int rtlsdr_get_tuner_gain_stage_gains(rtlsdr_dev_t *dev, int stage, int *gains);
+
+/*!
+ * Get the name of the given gain stage.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param stage from 0 to number of gain stages minus 1.
+ * \return the address of a NULL terminated char array containing a descriptive name of the give gain stage.
+ */
+RTLSDR_API const char* rtlsdr_get_tuner_gain_stage_name(rtlsdr_dev_t *dev, int stage);
+
+/*!
  * Set the gain for the device.
  * Manual gain mode must be enabled for this to work.
  *
diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h
index f6c206a..b21988d 100644
--- a/include/tuner_r82xx.h
+++ b/include/tuner_r82xx.h
@@ -115,6 +115,11 @@ 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_gain_stage(struct r82xx_priv *priv, int stage, int gain);
+int r82xx_get_gain_stage(struct r82xx_priv *priv, int stage);
+int r82xx_get_gain_stages(struct r82xx_priv *priv);
+int r82xx_get_gain_stage_gains(struct r82xx_priv *priv, int stage, int *gains);
+const char *r82xx_get_gain_stage_name(struct r82xx_priv *priv, int stage);
 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..d0db745 100644
--- a/src/librtlsdr.c
+++ b/src/librtlsdr.c
@@ -64,6 +64,11 @@ 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_gain_stage)(void *, int stage, int gain);
+	int (*get_gain_stage)(void *, int stage);
+	int (*get_gain_stages)(void *);
+	int (*get_gain_stage_gains)(void *, int stage, int *gains);
+	const char * (*get_gain_stage_name)(void *, int stage);
 } rtlsdr_tuner_iface_t;
 
 enum rtlsdr_async_status {
@@ -258,45 +263,76 @@ 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_gain_mode(void *dev, int manual) {
 	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
 	return r82xx_set_gain(&devt->r82xx_p, manual, 0);
 }
 
+int r820t_set_gain_stage(void *dev, int stage, int gain) {
+	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
+	return r82xx_set_gain_stage(&devt->r82xx_p, stage, gain);
+}
+
+int r820t_get_gain_stage(void *dev, int stage) {
+	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
+	return r82xx_get_gain_stage(&devt->r82xx_p, stage);
+}
+
+int r820t_get_gain_stages(void *dev) {
+	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
+	return r82xx_get_gain_stages(&devt->r82xx_p);
+}
+
+int r820t_get_gain_stage_gains(void *dev, int stage, int *gains) {
+	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
+	return r82xx_get_gain_stage_gains(&devt->r82xx_p, stage, gains);
+}
+
+const char *r820t_get_gain_stage_name(void *dev, int stage) {
+	rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
+	return r82xx_get_gain_stage_name(&devt->r82xx_p, stage);
+}
+
 /* 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, 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, 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, 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, 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, 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_gain_stage,
+		r820t_get_gain_stage,
+		r820t_get_gain_stages,
+		r820t_get_gain_stage_gains,
+		r820t_get_gain_stage_name
 	},
 	{
 		r820t_init, r820t_exit,
 		r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL,
-		r820t_set_gain_mode
+		r820t_set_gain_mode, NULL, NULL, NULL, NULL, NULL
 	},
 };
 
@@ -1089,6 +1125,74 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode)
 	return r;
 }
 
+int rtlsdr_set_tuner_gain_stage(rtlsdr_dev_t *dev, int stage, int gain)
+{
+	int r = 0;
+
+	if (!dev || !dev->tuner)
+		return -1;
+
+	if (dev->tuner->set_gain_stage) {
+		rtlsdr_set_i2c_repeater(dev, 1);
+		r = dev->tuner->set_gain_stage(dev, stage, gain);
+		rtlsdr_set_i2c_repeater(dev, 0);
+	}
+
+	return r;
+}
+
+int rtlsdr_get_tuner_gain_stage(rtlsdr_dev_t *dev, int stage)
+{
+	int r = 0;
+
+	if (!dev || !dev->tuner)
+		return -1;
+
+	if (dev->tuner->get_gain_stage)
+		r = dev->tuner->get_gain_stage(dev, stage);
+
+	return r;
+}
+
+int rtlsdr_get_tuner_gain_stages(rtlsdr_dev_t *dev)
+{
+	int r = 0;
+
+	if (!dev || !dev->tuner)
+		return -1;
+
+	if (dev->tuner->get_gain_stages)
+		r = dev->tuner->get_gain_stages(dev);
+
+	return r;
+}
+
+int rtlsdr_get_tuner_gain_stage_gains(rtlsdr_dev_t *dev, int stage, int *gains)
+{
+	int r = 0;
+
+	if (!dev || !dev->tuner)
+		return -1;
+
+	if (dev->tuner->get_gain_stage_gains)
+		r = dev->tuner->get_gain_stage_gains(dev, stage, gains);
+
+	return r;
+}
+
+const char *rtlsdr_get_tuner_gain_stage_name(rtlsdr_dev_t *dev, int stage)
+{
+	const char *r = "";
+
+	if (!dev || !dev->tuner)
+		return "";
+
+	if (dev->tuner->get_gain_stage_name)
+		r = dev->tuner->get_gain_stage_name(dev, stage);
+
+	return r;
+}
+
 int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
 {
 	int r = 0;
diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c
index f620238..8c9b5d1 100644
--- a/src/tuner_r82xx.c
+++ b/src/tuner_r82xx.c
@@ -1073,6 +1073,89 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain)
 	return 0;
 }
 
+#define STAGE_NUM 3
+typedef struct r82xx_gain_stage {
+	const char *name;
+	const int *steps;
+	const int num;
+	int gain;
+} r82xx_gain_stage_t;
+
+
+static r82xx_gain_stage_t gain_stage[STAGE_NUM] = {
+	{ "LNA", (int []) { 0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13 }, 16, 0},
+	{ "MIX", (int []) { 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 }, 16, 0},
+	{ "VGA", (int []) { 0, 26, 26, 30, 42, 35, 24, 13, 14, 32, 36, 34, 35, 37, 35, 36 }, 16, 0}
+};
+
+static const int r82xx_gain_stage_register[STAGE_NUM] = { 0x05, 0x07, 0x0c };
+static const int r82xx_gain_stage_mask[STAGE_NUM] = { 0x0f, 0x0f, 0x0f };
+static const int r82xx_gain_stage_manual[STAGE_NUM] = { 0x01, 0x00, 0x00 };
+static const int r82xx_gain_stage_manual_mask[STAGE_NUM] = { 0x10, 0x10, 0x10 };
+
+int r82xx_set_gain_stage(struct r82xx_priv *priv, int stage, int gain)
+{
+	int rc;
+
+	if (stage < 0 || stage >= (int) ARRAY_SIZE(gain_stage))
+		return -1;
+
+	if (gain < -1 || gain >= gain_stage[stage].num)
+		return -1;
+
+	if (gain >= 0) {
+		/* stage auto off */
+		rc = r82xx_write_reg_mask(priv, r82xx_gain_stage_register[stage], r82xx_gain_stage_manual[stage], r82xx_gain_stage_manual_mask[stage]);
+		if (rc < 0)
+			return rc;
+
+		rc = r82xx_write_reg_mask(priv, r82xx_gain_stage_register[stage], (uint8_t) gain, r82xx_gain_stage_mask[stage]);
+		if (rc < 0)
+			return rc;
+
+		gain_stage[stage].gain = gain;
+	} else {
+		/* stage auto on */
+		rc = r82xx_write_reg_mask(priv, r82xx_gain_stage_register[stage], ~(r82xx_gain_stage_manual[stage]), r82xx_gain_stage_manual_mask[stage]);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+int r82xx_get_gain_stage(struct r82xx_priv *priv, int stage)
+{
+	if (stage < 0 || stage >= (int) ARRAY_SIZE(gain_stage))
+		return 0;
+
+	return gain_stage[stage].gain;
+}
+
+int r82xx_get_gain_stages(struct r82xx_priv *priv)
+{
+	return ARRAY_SIZE(gain_stage);
+}
+
+int r82xx_get_gain_stage_gains(struct r82xx_priv *priv, int stage, int *gains)
+{
+	if (stage < 0 || stage >= (int) ARRAY_SIZE(gain_stage))
+		return -1;
+
+	if (gains)
+		memcpy(gains, gain_stage[stage].steps, gain_stage[stage].num * sizeof(int));
+
+	return gain_stage[stage].num;
+}
+
+const char *r82xx_get_gain_stage_name(struct r82xx_priv *priv, int stage)
+{
+	if (stage < 0 || stage >= (int) ARRAY_SIZE(gain_stage))
+		return "";
+
+	return gain_stage[stage].name;
+}
+
 /* 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
-- 
2.11.0


More information about the osmocom-sdr mailing list