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)",