From c2de34860aaa1517c788739a7bdb79a31e210aa2 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Sun, 26 Jan 2014 20:31:38 +0100 Subject: [PATCH] Implement command-line option for LNA gain. --- NOTES.txt | 55 +++++++++++++++++++++++++++++--------- RtlSdrSource.cc | 29 +++++++++----------- RtlSdrSource.h | 10 +++---- TODO.txt | 4 ++- main.cc | 71 +++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 120 insertions(+), 49 deletions(-) diff --git a/NOTES.txt b/NOTES.txt index 10b8a6a..7bcacbc 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -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) + -- diff --git a/RtlSdrSource.cc b/RtlSdrSource.cc index 295e726..f81fa81 100644 --- a/RtlSdrSource.cc +++ b/RtlSdrSource.cc @@ -1,4 +1,5 @@ +#include #include #include @@ -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 RtlSdrSource::get_tuner_gains() +// Return a list of supported tuner gain settings in units of 0.1 dB. +vector RtlSdrSource::get_tuner_gains() { - vector result; - int num_gains = rtlsdr_get_tuner_gains(m_dev, NULL); if (num_gains <= 0) - return result; + return vector(); - int gains[num_gains]; - if (rtlsdr_get_tuner_gains(m_dev, gains) != num_gains) - return result; + vector gains(num_gains); + if (rtlsdr_get_tuner_gains(m_dev, gains.data()) != num_gains) + return vector(); - result.reserve(num_gains); - for (int i = 0; i < num_gains; i++) - result.push_back(0.1 * gains[i]); - - return result; + return gains; } diff --git a/RtlSdrSource.h b/RtlSdrSource.h index d6bed0f..94a6e4d 100644 --- a/RtlSdrSource.h +++ b/RtlSdrSource.h @@ -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 get_tuner_gains(); + /** Return a list of supported tuner gain settings in units of 0.1 dB. */ + std::vector get_tuner_gains(); /** Return name of opened RTL-SDR device. */ std::string get_device_name() const diff --git a/TODO.txt b/TODO.txt index e13f8b7..3b2b5b9 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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") diff --git a/main.cc b/main.cc index 4ead3ac..33893f3 100644 --- a/main.cc +++ b/main.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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 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 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 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 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;