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

Reply via email to