1
0
Fork 0

Implement command-line option for LNA gain.

This commit is contained in:
Joris van Rantwijk 2014-01-26 20:31:38 +01:00
parent 5744e0affd
commit c2de34860a
5 changed files with 120 additions and 49 deletions

View File

@ -160,18 +160,6 @@ radio4 2 MS/s 34 dB default ON 80 kHz 0.36 CLIP -38.0 dB/Hz
Conclusion: AGC mode has little effect on quality.
Local radio stations
--------------------
radio2 92600000 (good)
radio3 96800000 (good)
radio4 94300000 (bad)
qmusic 100700000 (medium)
radio538 102100000 (medium)
radio10 103800000 (bad)
radio west 89300000 (medium)
Stereo pilot frequency
----------------------
@ -183,4 +171,47 @@ radio538 19 kHz pilot = 18999.78 Hz +- 0.04 Hz (6 hours measurement)
Conclusion: stereo pilot is not a reliable time source.
Ferrites
--------
Claming a ferrite block on the USB cable, as close as possible to the DVB
received, reduces disturbance peaks in the spectrum by ~ 6 dB.
A second ferrite block at the PC side gives another small improvement.
The ferrite causes a clearly audible improvement of weak stations.
DIY antenna
-----------
Constructed antenna from a vertical telescopic rod antenna (85 cm)
and two steel wire ground radials ~ 85 cm.
Antenna placed indoors close to window.
Antenna connected to DVB receiver via 1.5 meter 75 Ohm coax cable.
The ground radials are floppy and look ridiculous but they are essential.
No ground radials, no reception.
The DIY antenna has ~ 20 dB more signal than the basic DVB antenna.
The DIY antenna causes notable improvement of weak stations.
Radio4 reception improves from very bad to almost good.
However, strong stations (radio3) sound slightly worse with the DIY antenna
than with the basic DVB antenna.
May be caused by clipping due to too strong signal from antenna.
Local radio stations
--------------------
radio2 92600000 (good)
radio3 96800000 (good)
radio4 94300000 (bad)
qmusic 100700000 (medium)
radio538 102100000 (medium)
radio10 103800000 (bad)
radio west 89300000 (medium)
--

View File

@ -1,4 +1,5 @@
#include <climits>
#include <cstring>
#include <rtl-sdr.h>
@ -59,7 +60,7 @@ bool RtlSdrSource::configure(uint32_t sample_rate,
return false;
}
if (tuner_gain < 0) {
if (tuner_gain == INT_MIN) {
r = rtlsdr_set_tuner_gain_mode(m_dev, 0);
if (r < 0) {
m_error = "rtlsdr_set_tuner_gain_mode could not set automatic gain";
@ -116,31 +117,25 @@ uint32_t RtlSdrSource::get_frequency()
}
// Return current tuner gain in dB.
double RtlSdrSource::get_tuner_gain()
// Return current tuner gain in units of 0.1 dB.
int RtlSdrSource::get_tuner_gain()
{
return 0.1 * rtlsdr_get_tuner_gain(m_dev);
return rtlsdr_get_tuner_gain(m_dev);
}
// Return a list of supported tuner gain settings in dB.
vector<double> RtlSdrSource::get_tuner_gains()
// Return a list of supported tuner gain settings in units of 0.1 dB.
vector<int> RtlSdrSource::get_tuner_gains()
{
vector<double> result;
int num_gains = rtlsdr_get_tuner_gains(m_dev, NULL);
if (num_gains <= 0)
return result;
return vector<int>();
int gains[num_gains];
if (rtlsdr_get_tuner_gains(m_dev, gains) != num_gains)
return result;
vector<int> gains(num_gains);
if (rtlsdr_get_tuner_gains(m_dev, gains.data()) != num_gains)
return vector<int>();
result.reserve(num_gains);
for (int i = 0; i < num_gains; i++)
result.push_back(0.1 * gains[i]);
return result;
return gains;
}

View File

@ -25,7 +25,7 @@ public:
*
* sample_rate :: desired sample rate in Hz.
* frequency :: desired center frequency in Hz.
* tuner_gain :: desired tuner gain in 0.1 dB, or -1 for auto-gain.
* tuner_gain :: desired tuner gain in 0.1 dB, or INT_MIN for auto-gain.
* block_length :: preferred number of samples per block.
*
* Return true for success, false if an error occurred.
@ -42,11 +42,11 @@ public:
/** Return current center frequency in Hz. */
std::uint32_t get_frequency();
/** Return current tuner gain in dB. */
double get_tuner_gain();
/** Return current tuner gain in units of 0.1 dB. */
int get_tuner_gain();
/** Return a list of supported tuner gain settings in dB. */
std::vector<double> get_tuner_gains();
/** Return a list of supported tuner gain settings in units of 0.1 dB. */
std::vector<int> get_tuner_gains();
/** Return name of opened RTL-SDR device. */
std::string get_device_name() const

View File

@ -1,6 +1,8 @@
* (quality) solve issues with bad sound due to strong antenna signal
(experiment with adaptive LNA gain or adaptive IF gain)
* (speedup) maybe replace high-order FIR downsampling filter with 2nd order butterworth followed by lower order FIR filter
* figure out why we sometimes lose stereo lock
* (feature) implement RDS decoding
* (quality) consider FM demodulation with PLL instead of phase discriminator
* (quality) consider pulse-counting discriminator
* (speedup) consider downsampling of IF signal before FM detection
(work in progress in branch "filterif")

71
main.cc
View File

@ -23,6 +23,7 @@
#include <cmath>
#include <csignal>
#include <cstring>
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <memory>
@ -216,6 +217,7 @@ void usage()
"Usage: softfm -f freq [options]\n"
" -f freq Frequency of radio station in Hz\n"
" -d devidx RTL-SDR device index, 'list' to show device list (default 0)\n"
" -g gain Set LNA gain in dB, or 'auto' (default auto)\n"
" -s ifrate IF sample rate in Hz (default 1000000, min 900001)\n"
" -r pcmrate Audio sample rate in Hz (default 48000 Hz)\n"
" -a 0 Disable RTL AGC mode (default 1 = enabled)\n"
@ -290,6 +292,7 @@ int main(int argc, char **argv)
{
double freq = -1;
int devidx = 0;
int lnagain = INT_MIN;
double ifrate = 1.0e6;
int pcmrate = 48000;
bool stereo = true;
@ -308,6 +311,7 @@ int main(int argc, char **argv)
const struct option longopts[] = {
{ "freq", 1, NULL, 'f' },
{ "dev", 1, NULL, 'd' },
{ "gain", 1, NULL, 'g' },
{ "ifrate", 1, NULL, 's' },
{ "pcmrate", 1, NULL, 'r' },
{ "agc", 1, NULL, 'a' },
@ -321,7 +325,7 @@ int main(int argc, char **argv)
int c, longindex;
while ((c = getopt_long(argc, argv,
"f:d:s:r:MR:W:P::T:b:a:",
"f:d:g:s:r:MR:W:P::T:b:a:",
longopts, &longindex)) >= 0) {
switch (c) {
case 'f':
@ -333,6 +337,23 @@ int main(int argc, char **argv)
if (!parse_int(optarg, devidx))
devidx = -1;
break;
case 'g':
if (strcasecmp(optarg, "auto") == 0) {
lnagain = INT_MIN;
} else if (strcasecmp(optarg, "list") == 0) {
lnagain = INT_MIN + 1;
} else {
double tmpgain;
if (!parse_dbl(optarg, tmpgain)) {
badarg("-g");
}
long int tmpgain2 = lrint(tmpgain * 10);
if (tmpgain2 <= INT_MIN || tmpgain2 >= INT_MAX) {
badarg("-g");
}
lnagain = tmpgain2;
}
break;
case 's':
// NOTE: RTL does not suppor sample rate 900 kS/s or lower
if (!parse_dbl(optarg, ifrate) || ifrate <= 900000) {
@ -388,7 +409,8 @@ int main(int argc, char **argv)
vector<string> devnames = RtlSdrSource::get_device_names();
if (devidx < 0 || (unsigned int)devidx >= devnames.size()) {
fprintf(stderr, "ERROR: invalid device index %d\n", devidx);
if (devidx != -1)
fprintf(stderr, "ERROR: invalid device index %d\n", devidx);
fprintf(stderr, "Found %u devices:\n", (unsigned int)devnames.size());
for (unsigned int i = 0; i < devnames.size(); i++) {
fprintf(stderr, "%2u: %s\n", i, devnames[i].c_str());
@ -427,8 +449,22 @@ int main(int argc, char **argv)
exit(1);
}
// Check LNA gain.
if (lnagain != INT_MIN) {
vector<int> gains = rtlsdr.get_tuner_gains();
if (find(gains.begin(), gains.end(), lnagain) == gains.end()) {
if (lnagain != INT_MIN + 1)
fprintf(stderr, "ERROR: LNA gain %.1f dB not supported by tuner\n", lnagain * 0.1);
fprintf(stderr, "Supported LNA gains: ");
for (int g: gains)
fprintf(stderr, " %.1f dB ", 0.1 * g);
fprintf(stderr, "\n");
exit(1);
}
}
// Configure RTL-SDR device and start streaming.
rtlsdr.configure(ifrate, tuner_freq, -1,
rtlsdr.configure(ifrate, tuner_freq, lnagain,
RtlSdrSource::default_block_length, agcmode);
if (!rtlsdr) {
fprintf(stderr, "ERROR: RtlSdr: %s\n", rtlsdr.error().c_str());
@ -436,12 +472,19 @@ int main(int argc, char **argv)
}
tuner_freq = rtlsdr.get_frequency();
fprintf(stderr, "device tuned for %.6f MHz\n", tuner_freq * 1.0e-6);
fprintf(stderr, "device tuned for: %.6f MHz\n", tuner_freq * 1.0e-6);
if (lnagain == INT_MIN)
fprintf(stderr, "LNA gain: auto\n");
else
fprintf(stderr, "LNA gain: %.1f dB\n",
0.1 * rtlsdr.get_tuner_gain());
ifrate = rtlsdr.get_sample_rate();
fprintf(stderr, "IF sample rate %.0f Hz\n", ifrate);
fprintf(stderr, "IF sample rate: %.0f Hz\n", ifrate);
fprintf(stderr, "RTL AGC mode %s\n", agcmode ? "enabled" : "disabled");
fprintf(stderr, "RTL AGC mode: %s\n",
agcmode ? "enabled" : "disabled");
// Create source data queue.
DataBuffer<IQSample> source_buffer;
@ -458,8 +501,8 @@ int main(int argc, char **argv)
// Prevent aliasing at very low output sample rates.
double bandwidth_pcm = min(FmDecoder::default_bandwidth_pcm,
0.45 * pcmrate);
fprintf(stderr, "audio sample rate %u Hz\n", pcmrate);
fprintf(stderr, "audio bandwidth %.3f kHz\n", bandwidth_pcm * 1.0e-3);
fprintf(stderr, "audio sample rate: %u Hz\n", pcmrate);
fprintf(stderr, "audio bandwidth: %.3f kHz\n", bandwidth_pcm * 1.0e-3);
// Prepare decoder.
FmDecoder fm(ifrate, // sample_rate_if
@ -483,17 +526,17 @@ int main(int argc, char **argv)
outputbuf_samples = (unsigned int)(bufsecs * pcmrate);
}
if (outputbuf_samples > 0) {
fprintf(stderr, "output buffer %.1f seconds\n",
fprintf(stderr, "output buffer: %.1f seconds\n",
outputbuf_samples / double(pcmrate));
}
// Open PPS file.
if (!ppsfilename.empty()) {
if (ppsfilename == "-") {
fprintf(stderr, "Writing pulse-per-second markers to stdout\n");
fprintf(stderr, "writing pulse-per-second markers to stdout\n");
ppsfile = stdout;
} else {
fprintf(stderr, "Writing pulse-per-second markers to '%s'\n",
fprintf(stderr, "writing pulse-per-second markers to '%s'\n",
ppsfilename.c_str());
ppsfile = fopen(ppsfilename.c_str(), "w");
if (ppsfile == NULL) {
@ -510,17 +553,17 @@ int main(int argc, char **argv)
unique_ptr<AudioOutput> audio_output;
switch (outmode) {
case MODE_RAW:
fprintf(stderr, "Writing raw 16-bit audio samples to '%s'\n",
fprintf(stderr, "writing raw 16-bit audio samples to '%s'\n",
filename.c_str());
audio_output.reset(new RawAudioOutput(filename));
break;
case MODE_WAV:
fprintf(stderr, "Writing audio samples to '%s'\n",
fprintf(stderr, "writing audio samples to '%s'\n",
filename.c_str());
audio_output.reset(new WavAudioOutput(filename, pcmrate, stereo));
break;
case MODE_ALSA:
fprintf(stderr, "Playing audio to ALSA device '%s'\n",
fprintf(stderr, "playing audio to ALSA device '%s'\n",
alsadev.c_str());
audio_output.reset(new AlsaAudioOutput(alsadev, pcmrate, stereo));
break;