Package: radio
Version: 3.103-3+b1
Severity: normal
Tags: patch
Dear Maintainer,
To replicate: Run 'radio -f 107.7'
Expected result: Frequency should be 107.7.
Actual result: Frequency is 107.69.
The 'radio' program uses floating point when reading in the frequency
from the user. (E.g. "107.7"). However, the frequency actually set are
usually wrong due to floating point rounding.
The fix is to read the input as a string and manipulate it as
characters. I've included a patch which does exactly that.
-- System Information:
Debian Release: 8.4
APT prefers oldstable-updates
APT policy: (500, 'oldstable-updates'), (500, 'oldstable')
Architecture: i386 (i686)
Kernel: Linux 3.16.0-4-686-pae (SMP w/2 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
Versions of packages radio depends on:
ii libasound2 1.0.28-1
ii libc6 2.19-18+deb8u10
ii libncurses5 5.9+20140913-1+b1
ii libtinfo5 5.9+20140913-1+b1
radio recommends no packages.
radio suggests no packages.
-- no debconf information
--- console/radio.c.orig 2013-04-18 04:18:43.000000000 -0700
+++ console/radio.c 2017-08-27 06:04:18.956868941 -0700
@@ -102,7 +102,7 @@
memset (&frequency, 0, sizeof(frequency));
frequency.type = V4L2_TUNER_RADIO;
- frequency.frequency = *ifreq * (freqfact / 1e6);
+ frequency.frequency = (*ifreq/1e3) * (freqfact/1e3); /* Avoid wraparound */
if (ioctl(fd, VIDIOC_S_FREQUENCY, &frequency) == -1) {
perror("VIDIOC_S_FREQUENCY");
return errno;
@@ -687,11 +687,34 @@
/* ---------------------------------------------------------------------- */
+static int freqstrexp( char *fstr, int exp ) {
+ /* Given a string of the form "107.7", return the integer result
+ of that string times 10^exp. This is needed because multiplying
+ by a float gives the wrong numbers due to rounding. */
+
+ /* q points to the radix (decimal point or comma) or end of string */
+ char *q = fstr;
+ while (*q>='0' && *q<='9') q++;
+
+ /* Handle fractional part, counting down exp for each digit */
+ while (exp--) {
+ q++;
+ if (*q>='0' && *q<='9') /* Shift digits over */
+ *(q-1)=*q;
+ else /* Not a digit, use a zero */
+ *(q-1)='0';
+ *q='\0';
+ }
+ return atoi(fstr);
+}
+
+/* ---------------------------------------------------------------------- */
+
int main(int argc, char *argv[])
{
int lastband = -1, ifreq = -1, lastfreq = -1, mute = 0;
int i, c, fd, quit = 0, scan = 0, write_config = 0;
- float newfreq = 0;
+ char *newfreqstr = calloc(sizeof(char), 80);
setlocale(LC_ALL,"");
@@ -739,7 +762,7 @@
}
break;
case 'f':
- sscanf(optarg, "%f", &newfreq);
+ sscanf(optarg, "%79s", newfreqstr);
break;
case 'c':
device = optarg;
@@ -859,13 +882,13 @@
do_scan(fd, scan, write_config);
}
- if (newfreq) {
+ if (newfreqstr[0]) {
if (band >= 0 && bands[band].rangehigh < (2 * freqfact)) {
- fprintf(stderr, "Tuning to %.2f kHz\n", newfreq);
- ifreq = newfreq * 1e3;
+ fprintf(stderr, "Tuning to %s kHz\n", newfreqstr);
+ ifreq = freqstrexp(newfreqstr, 3);
} else {
- fprintf(stderr, "Tuning to %.2f MHz\n", newfreq);
- ifreq = newfreq * 1e6;
+ fprintf(stderr, "Tuning to %s MHz\n", newfreqstr);
+ ifreq = freqstrexp(newfreqstr, 6);
}
if (radio_setfreq(fd, &ifreq) != 0)
return 1;
@@ -894,7 +917,7 @@
band = p_bands[i];
}
- if (band == -1 && !newfreq && fkeys[0]) {
+ if (band == -1 && !newfreqstr[0] && fkeys[0]) {
band = fkeybands[0];
ifreq = fkeys[0];
lastfreq = -1;
@@ -1044,22 +1067,24 @@
curs_set(1);
echo();
wrefresh(wcommand);
- wscanw(wcommand, "%f", &newfreq);
+ wscanw(wcommand, "%79s", newfreqstr);
cbreak();
noecho();
keypad(stdscr, 1);
curs_set(0);
wrefresh(wcommand);
if (bands[band].rangehigh < (2 * freqfact)) {
- if (newfreq >= FREQ_MIN_KHZ && newfreq <= FREQ_MAX_KHZ)
- ifreq = newfreq * 1e3;
+ int newfreq = freqstrexp(newfreqstr, 3);
+ if (newfreq >= FREQ_MIN_KHZ*1e3 && newfreq <= FREQ_MAX_KHZ*1e3)
+ ifreq = newfreq;
else
mvwprintw(wcommand, 1, 2,
"Frequency out of range (%.1f-%.1f kHz)",
FREQ_MIN_KHZ, FREQ_MAX_KHZ);
} else {
- if (newfreq >= FREQ_MIN_MHZ && newfreq <= FREQ_MAX_MHZ)
- ifreq = newfreq * 1e6;
+ int newfreq = freqstrexp(newfreqstr, 6);
+ if (newfreq >= FREQ_MIN_MHZ*1e6 && newfreq <= FREQ_MAX_MHZ*1e6)
+ ifreq = newfreq;
else
mvwprintw(wcommand, 1, 2,
"Frequency out of range (%.2f-%.2f MHz)",