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

Luigi Tarenga luigi.tarenga at gmail.com
Tue Jan 10 10:10:59 UTC 2017


Hi list,
there is a bug in v2. I didn't put the gain array in the priv structure but used
a global variable so multiple instance would use the same variable.

v3 fix this by putting a gain[] in the priv structure so every dongle get its 
array allocated with a malloc.

best regards
Luigi

Signed-off-by: Luigi Tarenga <luigi.tarenga at gmail.com>
---
 include/rtl-sdr.h     |  59 +++++++++++++++++++++++++
 include/tuner_r82xx.h |   7 +++
 src/librtlsdr.c       | 118 +++++++++++++++++++++++++++++++++++++++++++++++---
 src/tuner_r82xx.c     |  81 ++++++++++++++++++++++++++++++++++
 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..04bb113 100644
--- a/include/tuner_r82xx.h
+++ b/include/tuner_r82xx.h
@@ -38,6 +38,7 @@
 #define NUM_REGS		30
 #define NUM_IMR			5
 #define IMR_TRIAL		9
+#define NUM_GAIN_STAGES	3
 
 #define VER_NUM			49
 
@@ -90,6 +91,7 @@ struct r82xx_priv {
 	enum r82xx_tuner_type		type;
 
 	uint32_t			bw;	/* in MHz */
+	int					gain[NUM_GAIN_STAGES];
 
 	void *rtl_dev;
 };
@@ -115,6 +117,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..eeb646f 100644
--- a/src/tuner_r82xx.c
+++ b/src/tuner_r82xx.c
@@ -1073,6 +1073,87 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain)
 	return 0;
 }
 
+typedef struct r82xx_gain_stage {
+	const char *name;
+	const int *steps;
+	const int num;
+} r82xx_gain_stage_t;
+
+
+static r82xx_gain_stage_t gain_stage[NUM_GAIN_STAGES] = {
+	{ "LNA", (int []) { 0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13 }, 16 },
+	{ "MIX", (int []) { 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 }, 16 },
+	{ "VGA", (int []) { 0, 26, 26, 30, 42, 35, 24, 13, 14, 32, 36, 34, 35, 37, 35, 36 }, 16 }
+};
+
+static const int r82xx_gain_stage_register[NUM_GAIN_STAGES] = { 0x05, 0x07, 0x0c };
+static const int r82xx_gain_stage_mask[NUM_GAIN_STAGES] = { 0x0f, 0x0f, 0x0f };
+static const int r82xx_gain_stage_manual[NUM_GAIN_STAGES] = { 0x01, 0x00, 0x00 };
+static const int r82xx_gain_stage_manual_mask[NUM_GAIN_STAGES] = { 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;
+
+		priv->gain[stage] = 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 priv->gain[stage];
+}
+
+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