Note that at least in the US the frequency accuracy of broadcast FM stations is
no better than the transmission mode needs. NFM weather broadcasts are about as
good as it gets for most things on VHF. Cellphone signals may well provide the
best calibration sources. The towers have ppb oscillators in profusion and one
presumes they use them.
{^_^} Joanne/W6MKU
On 2014-07-31 01:23, Tobias wrote:
Hi!
I have just started using rtl-sdr in my application, SvxLink:
http://svxlink.org/. Thanks for your work on the RTL code. It opens up the world
of wideband receivers to everyone. The SvxLink RTL code is in the rtl-branch on
GitHub right now if anyone is interested:
https://github.com/sm0svx/svxlink/tree/rtl
I was missing some simple utility to calibrate the frequency error for a dongle.
The simplest way I came up with was to modify the rtl_fm utility since it is
very easy to find out the frequency error of an FM signal. To get a measure of
the frequency error, all that have to be done in the FM demodulator is:
samp_rate * angle / (2*PI)
The angle is already compensated with PI in the current demodulator so I only
had to do the rest in my added calibration code.
I first filed this patch as a pull request on GitHub
(https://github.com/steve-m/librtlsdr/pull/9) but then saw that contributions
should be mailed to this mailing list.
The rtl_fm utility may now be used to calibrate the frequency error of a DVB-T
dongle using the "-c" command line switch:
| [-c] Do frequency error calibration
Frequency error calibration will only work for FM.
Use a higher sample rate, like -s170k, to handle
large frequency errors. A strong and noise free
signal is needed for good results. For example, use a
broadcast FM station for calibration. Let the
calibration run for some minutes until the values
have stabilized. Rerun with -p option using the
suggested ppm_error. Try this on a couple of stations.
Repeat until the error is stable at about zero.
Example: rtl_fm -f99.3M -s170k -r48k -c - | aplay -r48k -fS16_LE -
|
Regards,
Tobias
rtl_fm-cal.patch
diff --git a/src/rtl_fm.c b/src/rtl_fm.c
index 0f7ac38..a58fb24 100644
--- a/src/rtl_fm.c
+++ b/src/rtl_fm.c
@@ -144,6 +144,9 @@ struct demod_state
pthread_cond_t ready;
pthread_mutex_t ready_m;
struct output_state *output_target;
+ double cal_sum;
+ int cal_cnt;
+ int calibrate;
};
struct output_state
@@ -198,6 +201,17 @@ void usage(void)
//"\t for fm squelch is inverted\n"
//"\t[-o oversampling (default: 1, 4 recommended)]\n"
"\t[-p ppm_error (default: 0)]\n"
+ "\t[-c] Do frequency error calibration\n"
+ "\t Frequency error calibration will only work for FM.\n"
+ "\t Use a higher sample rate, like -s170k, to handle\n"
+ "\t large frequency errors. A strong and noise free\n"
+ "\t signal is needed for good results. For example, use a\n"
+ "\t broadcast FM station for calibration. Let the\n"
+ "\t calibration run for some minutes until the values\n"
+ "\t have stabilized. Rerun with -p option using the\n"
+ "\t suggested ppm_error. Try this on a couple of stations.\n"
+ "\t Repeat until the error is stable at about zero.\n"
+ "\t Example: rtl_fm -f99.3M -s170k -r48k -c - | aplay -r48k
-fS16_LE -\n"
"\t[-E enable_option (default: none)]\n"
"\t use multiple -E to enable multiple options\n"
"\t edge: enable lower edge tuning\n"
@@ -727,6 +741,28 @@ void arbitrary_resample(int16_t *buf1, int16_t *buf2, int
len1, int len2)
}
}
+void calc_fm_fq_offset(struct demod_state *fm)
+{
+ int i;
+ for (i=0; i<fm->result_len; ++i)
+ {
+ /* Correct demodulated FM using rate_in * pcm / 2 and remove
+ * integer multiplier */
+ fm->cal_sum += fm->rate_in * (double)fm->result[i] / (1 << 15);
+ if (++fm->cal_cnt >= fm->rate_in)
+ {
+ double fq_offset = fm->cal_sum / fm->cal_cnt;
+ double fq_corr = -1000000.0 * fq_offset / dongle.freq;
+ int ppm_error = (int)round(fq_corr);
+ fprintf(stderr, "fq_offset=%+.0fHz ppm_error=%+d\n",
+ fq_offset, ppm_error);
+ fm->cal_sum = 0.0;
+ fm->cal_cnt = 0;
+ }
+ }
+
+}
+
void full_demod(struct demod_state *d)
{
int i, ds_p;
@@ -763,6 +799,12 @@ void full_demod(struct demod_state *d)
if (d->mode_demod == &raw_demod) {
return;
}
+
+ if (d->calibrate)
+ {
+ calc_fm_fq_offset(d);
+ }
+
/* todo, fm noise squelch */
// use nicer filter here too?
if (d->post_downsample > 1) {
@@ -975,6 +1017,9 @@ void demod_init(struct demod_state *s)
pthread_cond_init(&s->ready, NULL);
pthread_mutex_init(&s->ready_m, NULL);
s->output_target = &output;
+ s->cal_sum = 0.0;
+ s->cal_cnt = 0;
+ s->calibrate = 0;
}
void demod_cleanup(struct demod_state *s)
@@ -1047,7 +1092,7 @@ int main(int argc, char **argv)
output_init(&output);
controller_init(&controller);
- while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:h")) !=
-1) {
+ while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:ch")) !=
-1) {
switch (opt) {
case 'd':
dongle.dev_index = verbose_device_search(optarg);
@@ -1142,6 +1187,9 @@ int main(int argc, char **argv)
demod.deemph = 1;
demod.squelch_level = 0;}
break;
+ case 'c':
+ demod.calibrate = 1;
+ break;
case 'h':
default:
usage();
@@ -1149,6 +1197,13 @@ int main(int argc, char **argv)
}
}
+ if (demod.calibrate && demod.mode_demod != &fm_demod)
+ {
+ fprintf(stderr, "Error: Frequency calibration can only "
+ "be done using the FM demodulator\n");
+ exit(1);
+ }
+
/* quadruple sample_rate to limit to Δθ to ±π/2 */
demod.rate_in *= demod.post_downsample;