1 /*
2  * @(#)$Id: func.c,v 2.3 2009/01/17 02:31:16 baccala Exp $
3  *
4  * Copyright (C) 1996 - 2001 Tim Witham <twitham@quiknet.com>
5  *
6  * (see the files README and COPYING for more details)
7  *
8  * This file implements the signal math and memory.
9  * To add math functions, search for !!! and add to those sections.
10  *
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <math.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include "oscope.h"
22 #include "fft.h"
23 #include "display.h"
24 #include "func.h"
25 
26 Signal mem[26];		/* 26 memories, corresponding to 26 letters */
27 
28 /* recall given memory register to the currently selected signal */
29 void
recall_on_channel(Signal * signal,Channel * ch)30 recall_on_channel(Signal *signal, Channel *ch)
31 {
32   if (ch->signal) ch->signal->listeners --;
33   ch->signal = signal;
34   if (signal) {
35     signal->listeners ++;
36 
37     /* no guarantee that signal->bits will be correct yet */
38     ch->bits = signal->bits;
39   }
40 }
41 
42 void
recall(Signal * signal)43 recall(Signal *signal)
44 {
45   recall_on_channel(signal, &ch[scope.select]);
46 }
47 
48 /* store the currently selected signal to the given memory register */
49 void
save(char c)50 save(char c)
51 {
52   int i;
53 
54   i = c - 'A';
55 
56   if (ch[scope.select].signal == NULL) return;
57 
58   /* Don't want the name - leave that at 'Memory x'
59    * Also, increment frame instead of setting it to signal->frame in
60    * case signal->frame is the same as mem's old frame number!
61    */
62   if (mem[i].data != NULL) {
63     free(mem[i].data);
64   }
65   mem[i].data = malloc(ch[scope.select].signal->width * sizeof(short));
66   memcpy(mem[i].data, ch[scope.select].signal->data,
67 	 ch[scope.select].signal->width * sizeof(short));
68   mem[i].rate = ch[scope.select].signal->rate;
69   mem[i].num = ch[scope.select].signal->num;
70   mem[i].width = ch[scope.select].signal->width;
71   mem[i].frame ++;
72   mem[i].volts = ch[scope.select].signal->volts;
73 }
74 
75 /* !!! External process handling
76  *
77  * Could use some work... the original code (xoscope-1.8) always sent
78  * the first h_points data points from the first two display channels
79  * through to the external process, even if the scaling on those
80  * channels was set up so that weren't h_points of valid data, or
81  * set down so that what was displayed was way more than h_points.
82  * In any event, I'm not a big fan of these math functions, so
83  * I just left it alone like that.
84  */
85 
86 struct external {
87   struct external *next;
88   Signal signal;
89   int pid;			/* Zero if we already closed it down */
90   int to, from;			/* Pipes */
91   int last_frame_ch0, last_frame_ch1;
92   int last_num_ch0, last_num_ch1;
93 };
94 
95 static struct external *externals = NULL;
96 
97 /* startcommand() / start_command_on_channel()
98  *
99  * Start an external command running on the current display channel.
100  *
101  * gr_* UIs call this after prompting for command to run
102  */
103 
104 void
start_command_on_channel(char * command,Channel * ch)105 start_command_on_channel(char *command, Channel *ch)
106 {
107   struct external *ext;
108   int pid;
109   int from[2], to[2];
110   static char *path, *oscopepath;
111 
112   if (pipe(to) || pipe(from)) { /* get a set of pipes */
113     sprintf(error, "%s: can't create pipes", progname);
114     perror(error);
115     return;
116   }
117 
118   signal(SIGPIPE, SIG_IGN);
119 
120   if ((pid = fork()) > 0) {		/* parent */
121     close(to[0]);
122     close(from[1]);
123   } else if (pid == 0) {		/* child */
124     close(to[1]);
125     close(from[0]);
126     close(0);
127     close(1);				/* redirect stdin/out through pipes */
128     dup2(to[0], 0);
129     dup2(from[1], 1);
130     close(to[0]);
131     close(from[1]);
132 
133     /* XXX add additional environment vars here for sampling rate
134      * and number of samples per frame
135      */
136 
137     if ((oscopepath = getenv("OSCOPEPATH")) == NULL)
138       oscopepath = PACKAGE_LIBEXEC_DIR;
139     if ((path = malloc(strlen(oscopepath) + 6)) != NULL) {
140       sprintf(path,"PATH=%s", oscopepath);
141       putenv(path);
142       /* putenv() requires buffer to stick around, so no free(),
143        * but we're in the child, and about to exec, so no big deal
144        */
145     }
146 
147     execlp("/bin/sh", "sh", "-c", command, NULL);
148     sprintf(error, "%s: child can't exec /bin/sh -c \"%s\"",
149 	    progname, command);
150     perror(error);
151     exit(1);
152   } else {			/* fork error */
153     sprintf(error, "%s: can't fork", progname);
154     perror(error);
155     return;
156   }
157 
158   ext = malloc(sizeof(struct external));
159   if (ext == NULL) {
160     fprintf(stderr, "malloc() struct external failed\n");
161     return;
162   }
163   bzero(ext, sizeof(struct external));
164 
165   strncpy(ext->signal.savestr, command, sizeof(ext->signal.savestr));
166   ext->pid = pid;
167   ext->from = from[0];
168   ext->to = to[1];
169 
170   ext->next = externals;
171   externals = ext;
172 
173   message(command);
174 
175   recall_on_channel(&ext->signal, ch);
176 }
177 
178 void
startcommand(char * command)179 startcommand(char *command)
180 {
181   if (scope.select > 1) {
182     start_command_on_channel(command, &ch[scope.select]);
183     clear();
184   }
185 }
186 
187 /* Check everything on the externals list; run what needs to be run,
188  * and clean up anything left linguring behind.
189  */
190 
191 static void
run_externals(void)192 run_externals(void)
193 {
194   struct external *ext;
195 
196   short *a, *b, *c;
197   int i, errors;
198 
199   for (ext = externals; ext != NULL; ext = ext->next) {
200 
201     if (ext->signal.listeners > 0) {
202 
203       if ((ext->pid > 0) && (ch[0].signal != NULL) && (ch[1].signal != NULL)) {
204 
205 	/* There's a slight chance that if we change one of the channels,
206 	 * the new channel may have a frame number identical to the last
207 	 * one, but that shouldn't hurt us too bad.
208 	 */
209 
210 	if ((ch[0].signal->frame != ext->last_frame_ch0) ||
211 	    (ch[1].signal->frame != ext->last_frame_ch1)) {
212 
213 	  ext->last_frame_ch0 = ch[0].signal->frame;
214 	  ext->last_frame_ch1 = ch[1].signal->frame;
215 	  ext->signal.frame ++;
216 	  ext->signal.num = 0;
217 
218 	}
219 
220 	/* We may already have sent and received part of a frame, so
221 	 * start our pointers at whatever our last offset was, and
222 	 * keep going until we hit the limit of either channel 0
223 	 * or channel 1.   XXX explain this better
224 	 * XXX make sure this can't slow the program down!
225 	 */
226 
227 	a = ch[0].signal->data + ext->signal.num;
228 	b = ch[1].signal->data + ext->signal.num;
229 	c = ext->signal.data + ext->signal.num;
230 	errors = 0;
231 	for (i = ext->signal.num;
232 	     (i < ch[0].signal->num) && (i < ch[1].signal->num); i++) {
233 	  if (write(ext->to, a++, sizeof(short)) != sizeof(short))
234 	    errors ++;
235 	  if (write(ext->to, b++, sizeof(short)) != sizeof(short))
236 	    errors ++;
237 	  if (read(ext->from, c++, sizeof(short)) != sizeof(short))
238 	    errors ++;
239 	}
240 	ext->signal.num = i;
241 
242 	if (errors) {
243 	  sprintf(error, "%s: %d pipe r/w errors from \"%s\"",
244 		  progname, errors, ext->signal.savestr);
245 	  perror(error);
246 	  /* XXX do something here other than perror to notify user */
247 
248 	  close(ext->from);
249 	  close(ext->to);
250 	  waitpid(ext->pid, NULL, 0);
251 	  ext->pid = 0;
252 	}
253       }
254 
255     } else {
256 
257       /* Nobody listening anymore; close down the pipes and wait for
258        * process to exit.   Maybe we should timeout in case of a hang?
259        */
260 
261       if (ext->pid) {
262 	close(ext->from);
263 	close(ext->to);
264 	waitpid(ext->pid, NULL, 0);
265       }
266 
267       /* Delete ext from list and free() it */
268 
269     }
270   }
271 }
272 
273 /* !!! The functions; they take one arg: a Signal ptr to store results in */
274 
275 /* Invert */
276 void
inv(Signal * dest,Signal * src)277 inv(Signal *dest, Signal *src)
278 {
279   int i;
280   short *a, *b;
281 
282   if (src == NULL) return;
283 
284   dest->rate = src->rate;
285   dest->num = src->num;
286   dest->volts = src->volts;
287   dest->frame = src->frame;
288 
289   a = src->data;
290   b = dest->data;
291   for (i = 0 ; i < src->num; i++) {
292     *b++ = -1 * *a++;
293   }
294 }
295 
296 void
inv1(Signal * sig)297 inv1(Signal *sig)
298 {
299   inv(sig, ch[0].signal);
300 }
301 
302 void
inv2(Signal * sig)303 inv2(Signal *sig)
304 {
305   inv(sig, ch[1].signal);
306 }
307 
308 /* The sum of the two channels */
309 void
sum(Signal * dest)310 sum(Signal *dest)
311 {
312   int i;
313   short *a, *b, *c;
314 
315   if ((ch[0].signal == NULL) || (ch[1].signal == NULL)) return;
316 
317   a = ch[0].signal->data;
318   b = ch[1].signal->data;
319   c = dest->data;
320 
321   dest->frame = ch[0].signal->frame + ch[1].signal->frame;
322   dest->num = ch[0].signal->num;
323   if (dest->num > ch[1].signal->num) dest->num = ch[1].signal->num;
324 
325   for (i = 0 ; i < dest->num ; i++) {
326     *c++ = *a++ + *b++;
327   }
328 }
329 
330 /* The difference of the two channels */
331 void
diff(Signal * dest)332 diff(Signal *dest)
333 {
334   int i;
335   short *a, *b, *c;
336 
337   if ((ch[0].signal == NULL) || (ch[1].signal == NULL)) return;
338 
339   a = ch[0].signal->data;
340   b = ch[1].signal->data;
341   c = dest->data;
342 
343   dest->frame = ch[0].signal->frame + ch[1].signal->frame;
344   dest->num = ch[0].signal->num;
345   if (dest->num > ch[1].signal->num) dest->num = ch[1].signal->num;
346 
347   for (i = 0 ; i < dest->num ; i++) {
348     *c++ = *a++ - *b++;
349   }
350 }
351 
352 
353 /* The average of the two channels */
354 void
avg(Signal * dest)355 avg(Signal *dest)
356 {
357   int i;
358   short *a, *b, *c;
359 
360   if ((ch[0].signal == NULL) || (ch[1].signal == NULL)) return;
361 
362   a = ch[0].signal->data;
363   b = ch[1].signal->data;
364   c = dest->data;
365 
366   dest->frame = ch[0].signal->frame + ch[1].signal->frame;
367   dest->num = ch[0].signal->num;
368   if (dest->num > ch[1].signal->num) dest->num = ch[1].signal->num;
369 
370   for (i = 0 ; i < dest->num ; i++) {
371     *c++ = (*a++ + *b++) / 2;
372   }
373 }
374 
375 /* Fast Fourier Transform of channels 0 and 1
376  *
377  * The point of the dest->frame calculation is that the value changes
378  * whenever the data changes, but if the data is constant, it doesn't
379  * change.  The display code only looks at changes in frame number to
380  * decide when to redraw a signal; the actual value doesn't matter.
381  */
382 
383 void
fft1(Signal * dest)384 fft1(Signal *dest)
385 {
386   if (ch[0].signal == NULL) return;
387 
388   dest->num = 440;
389   dest->frame = 10000 * ch[0].signal->frame + ch[0].signal->num;
390 
391   fft(ch[0].signal->data, dest->data);
392 }
393 
394 void
fft2(Signal * dest)395 fft2(Signal *dest)
396 {
397   if (ch[1].signal == NULL) return;
398 
399   dest->num = 440;
400   dest->frame = 10000 * ch[1].signal->frame + ch[1].signal->num;
401 
402   fft(ch[1].signal->data, dest->data);
403 }
404 
405 /* isvalid() functions for the various math functions.
406  *
407  * These functions also have the side effect of setting the volts/rate
408  * fields in the Signal structure, something we count on happening
409  * whenever we call update_math_signals(), which calls these
410  * functions.
411  *
412  * These functions are also responsible for mallocing the data areas
413  * in the math function's associated Signal structures.
414  */
415 
ch1active(Signal * dest)416 int ch1active(Signal *dest)
417 {
418   dest->frame = 0;
419   dest->num = 0;
420 
421   if (ch[0].signal == NULL) {
422     dest->rate = 0;
423     dest->volts = 0;
424     return 0;
425   }
426 
427   dest->rate = ch[0].signal->rate;
428   dest->volts = ch[0].signal->volts;
429 
430   if (dest->width != ch[0].signal->width) {
431     dest->width = ch[0].signal->width;
432     if (dest->data != NULL) free(dest->data);
433     dest->data = malloc(dest->width * sizeof(short));
434   }
435 
436   return 1;
437 }
438 
ch2active(Signal * dest)439 int ch2active(Signal *dest)
440 {
441   dest->frame = 0;
442   dest->num = 0;
443 
444   if (ch[1].signal == NULL) {
445     dest->rate = 0;
446     dest->volts = 0;
447     return 0;
448   }
449 
450   dest->rate = ch[1].signal->rate;
451   dest->volts = ch[1].signal->volts;
452 
453   if (dest->width != ch[1].signal->width) {
454     dest->width = ch[1].signal->width;
455     if (dest->data != NULL) free(dest->data);
456     dest->data = malloc(dest->width * sizeof(short));
457   }
458 
459   return 1;
460 }
461 
chs12active(Signal * dest)462 int chs12active(Signal *dest)
463 {
464   dest->frame = 0;
465   dest->num = 0;
466 
467   if ((ch[0].signal == NULL) || (ch[1].signal == NULL)
468       || (ch[0].signal->rate != ch[1].signal->rate)
469       || (ch[0].signal->volts != ch[1].signal->volts)) {
470     dest->rate = 0;
471     dest->volts = 0;
472     return 0;
473   }
474 
475   dest->rate = ch[0].signal->rate;
476   dest->volts = ch[0].signal->volts;
477 
478   /* All of the associated functions (sum, diff, avg) only use the
479    * minimum of the samples on Channels 1 and 2, so we can safely base
480    * the size of our data array on Channel 1 only... the worst that
481    * can happen is that it is too big.
482    */
483 
484   if (dest->width != ch[0].signal->width) {
485     dest->width = ch[0].signal->width;
486     if (dest->data != NULL) free(dest->data);
487     dest->data = malloc(dest->width * sizeof(short));
488   }
489 
490   return 1;
491 }
492 
493 /* special isvalid() functions for FFT
494  *
495  * A considerable majority of the code in fft.c is devoted to scaling
496  * the FFT so that it fits in WINDOW_RIGHT - WINDOW_LEFT = 540 - 100 =
497  * 440 values.  The stored rate (negated to indicate that it's in
498  * Hz/sample, not samples/sec), is the frequency increment of each
499  * sample value in Hz, times 10.  Since the maximum frequency in
500  * an FFT is half the sampling rate, we divide that sampling rate
501  * by two, then by 440 to get Hz per sample, then multiply by 10,
502  * for a net of dividing by 88.  This is also how we get a value
503  * of 440 for dest->num (used above, in actual FFT functions).
504  */
505 
ch1FFTactive(Signal * dest)506 int ch1FFTactive(Signal *dest)
507 {
508   dest->frame = 0;
509   dest->num = 0;
510   dest->volts = 0;
511 
512   if (ch[0].signal == NULL) {
513     dest->rate = 0;
514     return 0;
515   } else {
516     dest->rate = -ch[0].signal->rate / 80;
517     return 1;
518   }
519 }
520 
ch2FFTactive(Signal * dest)521 int ch2FFTactive(Signal *dest)
522 {
523   dest->frame = 0;
524   dest->num = 0;
525   dest->volts = 0;
526 
527   if (ch[1].signal == NULL) {
528     dest->rate = 0;
529     return 0;
530   } else {
531     dest->rate = -ch[1].signal->rate / 80;
532     return 1;
533   }
534 }
535 
536 struct func {
537   void (*func)(Signal *);
538   char *name;
539   int (*isvalid)(Signal *);	/* returns TRUE if this function is valid */
540   Signal signal;
541 };
542 
543 struct func funcarray[] =
544 {
545   {inv1, "Inv. 1  ", ch1active},
546   {inv2, "Inv. 2  ", ch2active},
547   {sum, "Sum  1+2", chs12active},
548   {diff, "Diff 1-2", chs12active},
549   {avg, "Avg. 1,2", chs12active},
550   /* {fft1, "FFT. 1  ", ch1FFTactive}, */
551   /* {fft2, "FFT. 2  ", ch2FFTactive}, */
552 };
553 
554 /* the total number of "functions" */
555 int funccount = sizeof(funcarray) / sizeof(struct func);
556 
557 /* Cycle current scope chan to next function, taking heavy advantage
558  * of C incrementing pointers by the size of the thing they point to.
559  * Start by finding the current function in the function array that
560  * the channel is pointing to, and advancing to the next one,
561  * selecting the first item in the array if either we're currently
562  * pointing to the end of the array or pointing to something other
563  * than a function.  Then keep going, looking for the first function
564  * that returns TRUE to an isvalid() test, taking care that none of
565  * the functions may currently be valid.
566  */
567 
next_func(void)568 void next_func(void)
569 {
570   struct func *func, *func2;
571   Channel *chan = &ch[scope.select];
572 
573   for (func = &funcarray[0]; func < &funcarray[funccount]; func++) {
574     if (chan->signal == &func->signal) break;
575   }
576 
577   if (func == &funcarray[funccount]) func = &funcarray[0];
578   else if (func == &funcarray[funccount-1]) func = &funcarray[0];
579   else func ++;
580 
581   /* At this point, func points to the candidate function structure.
582    * See if it's valid, and keep going forward if it isn't
583    */
584 
585   func2 = func;
586   do {
587     if (func->isvalid(&func->signal)) {
588       recall(&func->signal);
589       return;
590     }
591     func ++;
592     if (func == &funcarray[funccount]) func = &funcarray[0];
593   } while (func != func2);
594 
595   /* If we're here, it's because we went through all the functions
596    * without finding one that returned valid.  No choice but to
597    * clear the channel.
598    */
599 
600   recall(NULL);
601 }
602 
603 /* Basically the same deal, but moving backwards in the array. */
604 
prev_func(void)605 void prev_func(void)
606 {
607   struct func *func, *func2;
608   Channel *chan = &ch[scope.select];
609 
610   for (func = &funcarray[0]; func < &funcarray[funccount]; func++) {
611     if (chan->signal == &func->signal) break;
612   }
613 
614   if (func == &funcarray[funccount]) func = &funcarray[funccount-1];
615   else if (func == &funcarray[0]) func = &funcarray[funccount-1];
616   else func --;
617 
618   /* At this point, func points to the candidate function structure.
619    * See if it's valid and go further backwards if it isn't
620    */
621 
622   func2 = func;
623   do {
624     if (func->isvalid(&func->signal)) {
625       recall(&func->signal);
626       return;
627     }
628     func --;
629     if (func < &funcarray[0]) func = &funcarray[funccount-1];
630   } while (func != func2);
631 
632   /* If we're here, it's because we went through all the functions
633    * without finding one that returned valid.  No choice but to
634    * clear the channel.
635    */
636 
637   recall(NULL);
638 }
639 
function_bynum_on_channel(int fnum,Channel * ch)640 int function_bynum_on_channel(int fnum, Channel *ch)
641 {
642   if ((fnum >= 0) && (fnum < funccount)
643       && funcarray[fnum].isvalid(&funcarray[fnum].signal)) {
644     recall_on_channel(&funcarray[fnum].signal, ch);
645     return TRUE;
646   }
647   return FALSE;
648 }
649 
650 /* Initialize math, called once by main at startup, and again whenever
651  * we read a file.
652  */
653 
654 void
init_math()655 init_math()
656 {
657   static int i;
658   static int once = 0;
659 
660   for (i = 0 ; i < 26 ; i++) {
661     if (once==1 && mem[i].data != NULL) {
662       free(mem[i].data);
663     }
664     mem[i].data = NULL;
665     mem[i].num = mem[i].frame = mem[i].volts = 0;
666     mem[i].listeners = 0;
667     sprintf(mem[i].name, "Memory %c", 'a' + i);
668     mem[i].savestr[0] = 'a' + i;
669     mem[i].savestr[1] = '\0';
670   }
671   for (i = 0; i < funccount; i++) {
672     strcpy(funcarray[i].signal.name, funcarray[i].name);
673     funcarray[i].signal.savestr[0] = '0' + i;
674     funcarray[i].signal.savestr[1] = '\0';
675   }
676   init_fft();
677   once=1;
678 }
679 
680 /* update_math_signals() is called whenever 'something' has changed in
681  * the scope settings, and we may need to recompute voltage and rate
682  * values for the generated math functions.  We do this by calling all
683  * the isvalid() functions for those math functions that have
684  * listeners, and return 0 if everything's OK, or -1 if some of them
685  * are no longer valid.
686  *
687  * XXX I'm still not completely clear on just what we should
688  * do with invalid channels that have listeners; don't want to
689  * arbitrarily clear them (I don't think), because then a single
690  * inadvertent keystroke on channel 0 or 1 might clear a bunch of
691  * math.
692  */
693 
694 int
update_math_signals(void)695 update_math_signals(void)
696 {
697   int i;
698   int retval = 0;
699 
700   for (i = 0; i < funccount; i++) {
701     if (funcarray[i].signal.listeners > 0) {
702       if (! funcarray[i].isvalid(&funcarray[i].signal)) retval = -1;
703     }
704   }
705 
706   return retval;
707 }
708 
709 /* Perform any math on the software channels, called many times by main loop */
710 void
do_math()711 do_math()
712 {
713   static int i;
714 
715   for (i = 0; i < funccount; i++) {
716     if (funcarray[i].signal.listeners > 0) {
717       funcarray[i].func(&funcarray[i].signal);
718     }
719   }
720 
721   run_externals();
722 
723 }
724 
725 /* Perform any math cleanup, called once by cleanup at program exit */
726 void
cleanup_math()727 cleanup_math()
728 {
729   EndFFT();
730 }
731 
732 /* measure the given channel */
733 void
measure_data(Channel * sig,struct signal_stats * stats)734 measure_data(Channel *sig, struct signal_stats *stats) {
735   static long int i, j, prev;
736   int min, max, midpoint;
737   float first = 0, last = 0, count = 0, imax = 0;
738 
739   stats->min = 0;
740   stats->max = 0;
741   stats->time = 0;
742   stats->freq = 0;
743   if ((sig->signal == NULL) || (sig->signal->num == 0)) return;
744 
745   prev = 1;
746   if (scope.curs) {		/* manual cursor measurements */
747     if (scope.cursa < scope.cursb) {
748       first = scope.cursa;
749       last = scope.cursb;
750     } else {
751       first = scope.cursb;
752       last = scope.cursa;
753     }
754     stats->min = stats->max = sig->signal->data[(int)first];
755     if ((j = sig->signal->data[(int)last]) < stats->min)
756       stats->min = j;
757     else if (j > stats->max)
758       stats->max = j;
759     count = 2;
760   } else {			/* automatic period measurements */
761     min = max = sig->signal->data[0];
762     for (i = 0 ; i < sig->signal->num ; i++) {
763       j = sig->signal->data[i];
764       if (j < min)
765 	min = j;
766       if (j > max) {
767 	max = j;
768 	imax = i;
769       }
770     }
771 
772     /* locate and count rising edges
773      * doesn't handle noise very well (noisy edges can get double counted)
774      */
775 
776     midpoint = (min + max)/2;
777     for (i = 0 ; i < sig->signal->num ; i++) {
778       j = sig->signal->data[i];
779       if (j > midpoint && prev <= midpoint) {
780 	if (!first)
781 	  first = i;
782 	last = i;
783 	count++;
784       }
785       prev = j;
786     }
787     stats->min = min;
788     stats->max = max;
789   }
790 
791   if (sig->signal->rate < 0) {
792 
793     /* Special case for FFT - signal rate will be < 0, the negative of
794      * the frequency step for each point in the transform, times 10.
795      * So multiply by the index of the maximum value to get frequency peak.
796      */
797 
798     stats->freq = (- sig->signal->rate) * imax / 10;
799     if (stats->freq > 0)
800       stats->time = 1000000 / stats->freq;
801 
802   } else if ((sig->signal->rate > 0) && (count > 1)) {
803 
804     /* estimate frequency from rising edge count
805      * assume a wave: period = length / # periods
806      */
807 
808     stats->time = 1000000 * (last - first) / (count - 1) / sig->signal->rate;
809     if (stats->time > 0)
810       stats->freq = 1000000 / stats->time;
811 
812   }
813 }
814