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