Implement command-line option for LNA gain.
This commit is contained in:
parent
5744e0affd
commit
c2de34860a
55
NOTES.txt
55
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)
|
||||
|
||||
--
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
4
TODO.txt
4
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")
|
||||
|
|
71
main.cc
71
main.cc
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue