AnsweredAssumed Answered

VRf-Zero-Crossing_Detection

Question asked by VRFuser on Mar 12, 1996
send q VRf-Zero-Crossing_Detection vrf ww    

from: Greg Goebel / HP-MXD
      gvg@lvld.hp.com / 970-679-3030 / FAX 970-679-5971
to:   John Sletten / VRF
date: Wednesday, 13 March 1996 0930 MST


> I need to detect at what times a given signal crosses a specific
> threshold. I have tried doing this in HP-VEE but it takes far too
> long time.
>
> I would like to know if a set of VEE objects exist for this type of
> application. Will this be added to VEE 3.2 ?

I wouldn't expect it as a feature but one of our lab people did an appnote
on it a long time ago -- see below.

> If not I will probably have to write a DLL in C. That leads me to my
> next question, can I pass a waveform to C ?
>
> I would be thankful for any advice
>
> John Sletten
> Independent consultant
> Oslo, Norway



[VEE] Zero Crossing Detection Via An OS Escape Under VEE

   *gvg*

* We occasionally get requests here in TSE land that intrigue us.  This article
is the result of a customer wanting to get zero crossing information from a
waveform he was trying to analyze.  Although there are a number of ways to do
this in VEE all by itself, the customer needed the added performance tuning you
can get by using an OS escape to a C program.  The result is a set of C
programs you can use to implement a reference crossing detector.  (The customer
only wanted zero crossings, but the ability to compare waveforms and real
arrays to a reference value was a trivial thing to throw in.)

What we did was create a C program that will search an array of real numbers
(one-dimensional arrays only) or a VEE waveform for the times at which the
waveform crosses the reference value.  The program reads from "stdin" the
number of elements in the array, reads in the array and the reference value,
then writes back to "stdout" the number of reference crossings it finds,
followed by the actual crossing points.  We consider a value that exactly
equals the reference to be a valid crossing point.

The values written back represent an index position within the original array.
Let's say that we search an array of 256 points and find reference crossings
at array elements 5 and 10, and between elements 15 and 16.  The program
writes back the values 5, 10, and something in between 15 and 16.  We do this
because you might want to use an array that doesn't have a time span hooked to
it.  You can use the returned numbers to compute the actual time value if you
want to.  If you are using VEE waveforms, which do have time spans attached,
you need do nothing different either in the VEE or C programs, but VEE will
let you determine the time mapping for use in reconstructing the time at which
the reference crossings occur.  We show an example VEE program that does just
this later on in the article.

We are making this application available as a "shar" package on our "hpislsup"
support server (IP address 15.11.29.69).  If you anonymous ftp, change
directories to "/pub/vee/contributed-examples.  There you will find a file
called "zCross.sh"; it contains the C programs, makefile, and VEE program
referenced in this article.

One thing to point out is that the makefile assumes the source files are in the
same directory the makefile is in.  Also, the VEE program wants to be started
from the same directory the rest of the files are in; if the rest of the files
are in "/tmp", change directories to "/tmp" before you start VEE.

The layout of the VEE program follows:

                         +------------------+
                         | UnBuild Waveform |
   +-----------+         +------------------+
   | Function  +--+----->|        Time Span +--+
   | Generator |  |      +------------------+  |
   +-----------+  |                            |   +-----------+
                  |      +------------+        |   |  Formula  |
                  |      | Get Values |        |   +-----------+
                  |      +------------+        +-->| a  Result |
                  +----->| Total Size +--+-------->| b  Error  |
                  |      +------------+  |     +-->| c         |
                  |                      |     |   +-----------+
                  |  +-------------------+     |   |   c*a/b   |
                  |  |                         |   +-----------+
                  |  |   +--------------+      |
                  |  |   | HP-UX Escape |      |
   +------+       |  |   +--------------+      |
   | Real |       |  +-->| a       Exit |      |
   +------+       +----->| b          x |      |
   |  0   +------------->| c          y +------+
   +------+              +--------------+

Everything is in the closed view except the formula box.  The only box we need
to see in its detail view (other than the formula) is the HP-UX Escape.  It
looks like this:

   +----------------------------------------------------+
   |                     HP-UX Escape                   |
   +----------------------------------------------------+
   |       +------+                             +-----+ |
   | Shell | none |         Wait for child exit | Yes | |
   |       +------+                             +-----+ |
   |                        +-------------------------+ |
   | Pgm with params        | ./zCross                | |
   |                        +-------------------------+ |
   +---+------------------------------------+-----------+
   |   | WRITE TEXT a INT EOL               |           |
   | a | WRITE BINARY b REAL                | Exit Code |
   |   | WRITE TEXT c REAL STD EOL          |           |
   | b | READ TEXT x INT COUNT:1            |         x |
   |   | READ BINARY y REAL COUNT:x         |           |
   | c |                                    |         y |
   |   |                                    |           |
   +---+------------------------------------+-----------+

The terminal marked "a" represents the number of elements in the waveform
array, and this is written to the reference crossing detector.  "b" accepts the
contents of the array, and "c" is the reference value.  The latter two values
are likewise written to the C program.

The terminal marked "x" receives the number of reference crossings the C
program found, and this value is used to tell the next read transaction how
many real values to read back.  If we observed 4 values coming back from the
terminal "x" we could interpret the read transaction as:

   READ BINARY y REAL COUNT:4

This holds equally true if the reference crossing detector finds no crossing
points.  The following read transaction would read 0 values.

   READ BINARY y REAL COUNT:0

If you look at the formula box, the terminal marked "a" receives the waveform's
time span, "b" is the number of points in the waveform, and "c" accepts the
values returned from the reference crossing detector.  The expression "c*a/b"
effectively takes the time span divided by the number of elements in the array
to give a "time per division" increment for each point.  So, assuming that the
points are evenly spaced, every point will represent some portion of the total
time span.  Remembering that the value the reference crossing detector returns
is an index into the array, we can multiply the detector return values by the
"time per division increment".  Then we get the time at which the waveform
crossed through the reference.

Note that the error pin on the formula box is to account for those times when
there are no detected reference crossings.  We have read no values for "c", so
the formula can't work.

The rest of the article contains the C programs we used to implement the
reference crossing detector and the associated makefile.

   /*******************************************************************/
   /*                                                                 */
   /* Program: zCross.c                                               */
   /* Purpose: Implements a reference-crossing detector for use with  */
   /*          VEE's mapped or unmapped real arrays.  We read from    */
   /*          stdin a real array and a reference value.  We then     */
   /*          search through the array looking for single points     */
   /*          that exactly match the reference value, or for pairs   */
   /*          of points whose values lie above and below the         */
   /*          reference value.  If we find an exact match, we add    */
   /*          that value to the list of detected reference crossings.*/
   /*          If we find two consecutive points whose values lie     */
   /*          above and below the reference, we perform a linear     */
   /*          interpolation to approximate the X axis position where */
   /*          the two values would have crossed the reference.  The  */
   /*          X axis values we write to stdout are implemented as a  */
   /*          linked list.  This is because there is no way to       */
   /*          predict in advance the number of reference crossings   */
   /*          we will end up with.  There are other ways to do this, */
   /*          but this appears to be the cleanest when looking from  */
   /*          a VEE program's perspective.                           */
   /*                                                                 */
   /*          One thing to note is that the reference crossing       */
   /*          values we write back are referenced with respect to a  */
   /*          positional index within the source array.  For         */
   /*          instance, if we have an array of 256 points and we     */
   /*          detect reference crossings at points 5 and 10, the     */
   /*          values we write back are 5 and 10.  We do this so that */
   /*          the reference crossing detector can work on mapped as  */
   /*          well as unmapped arrays.  If you wish to recover time- */
   /*          related information, you can divide the array's time   */
   /*          span by the number of points in the array to get a     */
   /*          "per division" incremental time.  You can then take the*/
   /*          array of values the reference detector returns and     */
   /*          multiply it by the time increment.  The resulting      */
   /*          set of numbers represents the times at which the array */
   /*          crossed the reference value.                           */
   /*                                                                 */
   /* Functions: readSourceArray(unsigned int*, double*)              */
   /*          This function reads via stdin the size of the array,   */
   /*          the array itself, and a reference value.               */
   /*                                                                 */
   /*          interpolate(unsigned int, double*, double)             */
   /*          This function accepts a position index into the source */
   /*          array, a pointer to the array, and a reference value.  */
   /*          It is called when the main function determines that    */
   /*          two successive points in the array bracket the         */
   /*          reference value.  The return value is a double         */
   /*          whose value is a straight line interpolation of the    */
   /*          X axis position of line which crosses through the      */
   /*          reference value.                                       */
   /*                                                                 */
   /* Dependencies: This program depends on a file called "lList.c"   */
   /*          and a make file called "zCross.mk".                    */
   /*                                                                 */
   /* To compile: make -f zCross.mk                                   */
   /*                                                                 */
   /*******************************************************************/
  
   #include <stdio.h>       /* used for fread() and fwrite()          */
   #include <stdlib.h>      /* used for malloc()                      */
   #include <sys/types.h>   /* used for the type definition of size_t */
   #include <math.h>        /* used for fabs()                        */
  
   /*******************************************************************/
   /*                      global variables                           */
   /*******************************************************************/
  
   extern unsigned int crossingCount;     /* indicates the number of  */
                                          /* reference crossings      */
  
   extern struct node *head, *current;    /* pointers to a structure  */
                                          /* for use in implementing  */
                                          /* a linked list            */
  
   /*******************************************************************/
   /*                      external functions                         */
   /*******************************************************************/
  
   extern void initList();                /* a function that sets     */
                                          /* initial conditions for   */
                                          /* linked list              */
  
   extern int add2list();                 /* a function that adds     */
                                          /* a reference crossing     */
                                          /* point point to a linked  */
                                          /* list.  returns a 0 to    */
                                          /* indicate success, a -1   */
                                          /* to indicate failure      */
   extern void cleanup();                 /* a function that frees    */
                                          /* the dynamically-allocated*/
                                          /* memory                   */
  
   extern void printList();               /* a function that writes   */
                                          /* the number of reference  */
                                          /* crossings and the cross- */
                                          /* ing values to stdout     */
  
   /*******************************************************************/
   /*                         functions                               */
   /*******************************************************************/
  
   /*******************************************************************/
   /*                                                                 */
   /* double* readSourceArray(arraySize, reference)                   */
   /*                                                                 */
   /* Purpose: reads from stdin the size of the array we will search  */
   /*          for reference values, the array, and the reference     */
   /*          value we will search for.                              */
   /*                                                                 */
   /*          We try to dynamically allocate enough memory to hold   */
   /*          the entire array the calling program wants to send.    */
   /*          If we can't malloc enough to hold the array, we        */
   /*          return a NULL pointer indicating failure, otherwise    */
   /*          we return a pointer to the array of interest.          */
   /*                                                                 */
   /*          We also retuen a NULL pointer if we can't read the     */
   /*          array.                                                 */
   /*                                                                 */
   /*******************************************************************/
  
   double* readSourceArray(arraySize, reference)
   unsigned int *arraySize;
   double *reference;
  
   {
      double *pSource;
      unsigned int blockSize;
      char lf;
  
      /* consume the line feed the calling program sends*/
      fscanf(stdin, "%u%c", arraySize, &lf);
  
      blockSize = *arraySize * sizeof(double);
  
      if((pSource=(double *) malloc(blockSize)) == (double *) 0){
         return((double *)0);
      } /* if */
  
      /* read the array from the calling program */
      if((fread((void *) pSource, sizeof(double), *arraySize, stdin))
      == (size_t) 0){
         return((double *)0);
      }  /* if */
  
      fscanf(stdin, "%lf%c", reference, &lf);
  
      return(pSource);
   } /* end readSourceArray() */
  
   /*******************************************************************/
   /*                                                                 */
   /* double interpolate(xTime, pSource, reference)                   */
   /*                                                                 */
   /* Purpose: performs a linear interpolation on the two points      */
   /*          the main program thinks bracket the reference value.   */
   /*          We take the ratio of the Y axis difference between     */
   /*          two array points, and the Y axis difference between    */
   /*          the first point and the reference, to yield a          */
   /*          proportion.  We then multiply that proportion by       */
   /*          the X axis delta to yield an incremental distance.     */
   /*          We then add that incremental X distance to the X axis  */
   /*          position of the first of the two points to yield       */
   /*          the projectd X axis value at which the the two array   */
   /*          points would intersect the Y reference value.          */
   /*                                                                 */
   /*******************************************************************/
  
   double interpolate(xTime, pSource, reference)
   unsigned int xTime;
   double *pSource, reference;
  
   {
      double x1, x2, y1, y2, deltaX, deltaY, deltaXprime, deltaYprime;
      double proportion, timeValue;
  
      x1 = (double) xTime;
      x2 = x1+1.0;
      y1 = *pSource;
      y2 = *(pSource + 1);
      deltaX = x2-x1;
      deltaY = fabs(y2-y1);
      deltaYprime = fabs(y1-reference);
      proportion = deltaYprime/deltaY;
      deltaXprime = proportion*deltaX;
      timeValue = deltaXprime+x1;
  
      return(timeValue);
   } /* end interpolate () */
  
   /*******************************************************************/
   /*                                                                 */
   /* void main()                                                     */
   /*                                                                 */
   /*******************************************************************/
  
   void main()
  
   {
      unsigned int arraySize, counter; /* counter serves as the array */
                                       /* index                       */
  
      double *sourceArray, *freeArray, reference=0.0, crossPoint=-1.0;
  
                                       /* we will set freeArray equal */
                                       /* to the address of the array */
                                       /* of interest so that, after  */
                                       /* we perform pointer math on  */
                                       /* on the source array, we can */
                                       /* free the dynamic memeory we */
                                       /* allocated                   */
  
      if((sourceArray=readSourceArray(&arraySize, &reference))
      == (double *)0){
         exit(-1);
      } /* if */
      else{
         freeArray = sourceArray;
  
         /* set up the initial conditions for the linked list */
  
         initList();
  
         for(counter = 0; counter < arraySize; counter ++, sourceArray ++){
            if(*sourceArray == reference){
  
               /* this point exactly matches the       */
               /* the reference.  It counts as a       */
               /* crossing                             */
  
               crossPoint = (double) counter;
  
               if((add2list(crossPoint)) < 0){
  
                  /* something went wrong in      */
                  /* adding this point to the list*/
                  /* but continue processing the  */
                  /* rest of the array anyway     */
  
                  continue;
               } /* if */
            } /* if */
            else{
               if(counter >= arraySize - 1){
  
                  continue;
  
                  /* last point in waveform may    */
                  /* exactly match the reference.  */
                  /* want to check for that con-   */
                  /* dition without walking off the*/
                  /* end of the array              */
  
               }/* if */
  
               /* this disgusting statement figures out */
               /* if two consecutive points (that don't */
               /* exactly equal the reference) are both */
               /* above or both below the reference.  If*/
               /* so, we ignore them and continue with  */
               /* the rest of the array.  If not, we    */
               /* perform an interpolation and add the  */
               /* result to our list of crossing points */
  
               if(((*sourceArray <= reference) && (*(sourceArray + 1)
            <= reference)) || ((*sourceArray >= reference) &&
            (*(sourceArray + 1) >= reference))){
                  continue;
               } /* if */
               else{
  
                  if((add2list(interpolate(counter, sourceArray,
               reference))) < 0){
                     continue;
                  } /* if */
  
               } /* else */
            } /* else */
         } /* for */
      } /* else */
  
      /* write the number of reference crossing points to stdout     */
      /* then write the crossing points themselves                   */
  
      printList();
  
      /* free the dynamically-allocated memory the program used      */
  
      cleanup(freeArray);
  
      exit(0);
   } /* end main() */
  
  
   /*******************************************************************/
   /*                                                                 */
   /* Program: lList.c                                                */
   /* Purpose: implements a linked list for use with a reference      */
   /*          crossing detector program.                             */
   /* Functions: void initList()                                      */
   /*          this function sets up the initial conditions for       */
   /*          using the linked list.                                 */
   /*                                                                 */
   /*          int add2list(double)                                   */
   /*          this function accepts the value representing the time  */
   /*          at which the waveform crossed the reference value.     */
   /*          this function attempts to create an additional node    */
   /*          to link into the list and then place the time value    */
   /*          into the node.  We return 0 to indicate success and    */
   /*          -1 to indicate failure.                                */
   /*                                                                 */
   /*          void cleanup(pSource)                                  */
   /*          this function frees all dynamically-allocated memory   */
   /*          and flushed stdout.  it accepts a pointer to the array */
   /*          we searched.                                           */
   /*                                                                 */
   /*          void printList()                                       */
   /*          this function runs through the linked list, writing    */
   /*          to stdout the values it finds in the list.  It first   */
   /*          prints to stdout the number of reference crossings it  */
   /*          finds.                                                 */
   /*                                                                 */
   /*******************************************************************/
  
   #include <stdio.h>    /* needed for fprintf() and fwrite() */
   #include <stdlib.h>   /* needed for malloc()               */
  
   /*******************************************************************/
   /*                       global variables                          */
   /*******************************************************************/
  
   /* This structure defines the contents of the nodes in the         */
   /* linked list.  It contains space for a double which represents an*/
   /* X axis time value.  It also contains space for a pointer to a   */
   /* a structure of this same type.  The pointer will contain the    */
   /* address of the next node in the list.  This gives you a way to  */
   /* find the next link in the list.                                 */
  
   struct node{
      double xTime;
      struct node *next;
   };
  
   /* These are pointers to node structures.  head will point at the  */
   /* first link in the list, and current will point at the node you  */
   /* are currently working with.                                     */
  
   struct node *head, *current;
  
   /* keeps track of the number of nodes in the list                  */
  
   unsigned int crossingCount;
  
   /*******************************************************************/
   /*                          functions                              */
   /*******************************************************************/
  
   /*******************************************************************/
   /*                                                                 */
   /* void initList()                                                 */
   /* Purpose: sets the value of head and current to NULL.  head and  */
   /*          current are pointers to nodes in the list.  The value  */
   /*          of NULL is used to indicate that the list is empty     */
   /*          or you have reached the last node in the list.         */
   /*                                                                 */
   /*******************************************************************/
  
   void initList()
  
   {
      head = current = (struct node *)0;
      crossingCount = 0;
   } /* end initList() */

   /*******************************************************************/
   /*                                                                 */
   /* int add2list(timeValue)                                         */
   /* Purpose: creates a new link in the list, attaches it to the end */
   /*          of the list, and places the X axis time value in the   */
   /*          node.  We return the value 0 to indicate success and   */
   /*          -1 to indicate failure.                                */
   /*                                                                 */
   /*******************************************************************/
  
   int add2list(timeValue)
   double timeValue;
  
   {
      /* create a place for a new link in the list */
      struct node *nodePtr;
  
      if((nodePtr=(struct node *) malloc(sizeof(struct node))) == (struct node *)0){
         /* the variable declaration above assigns space to hold a        */
         /* pointer to that data type, in this case a structure.  malloc()*/
         /* places the address of the data the structure will hold into   */
         /* the variable named nodePtr.  If, for some reason, malloc()    */
         /* can't assign space to hold the data, it returns a NULL.       */
         /* If we actually do get a NULL returned, we return from this    */
         /* function the value -1 indicating a failure.                   */
  
         return(-1);
  
      } /* if */
  
      if(crossingCount == 0){
         /* the list is empty so head points at this, the first node in  */
         /* the list                                                     */
  
         head = nodePtr;
  
      } /* if */
      else{
         /* The list has nodes in it, so we enter the address of the new */
         /* node into the "next" field in the node we are currently work-*/
         /* ing with                                                     */
  
         current -> next = nodePtr;
  
      } /* else */
  
      /* We make the node we are currently working with the node we just */
      /* created                                                         */
  
      current = nodePtr;

      /* put the time value into the node we just created                */
  
      current -> xTime = timeValue;
  
      /* make the "next" field in the current node point to null, indi-  */
      /* cating that this is the last node in the list                   */
  
      current -> next = (struct node *)0;
  
      /* add 1 to the number of nodes we have */
  
      crossingCount ++;
  
      return(0);
   } /* end add2list() */
  
   /*******************************************************************/
   /*                                                                 */
   /* void cleanup(pSource)                                           */
   /* Purpose: this function frees the memory that the program        */
   /*          dynamically allocates.  pSource is a pointer to the    */
   /*          array we search for reference crossings.  We also      */
   /*          walk through the linked list freeing memory.  We then  */
   /*          flush stdout to make sure everything gets written.     */
   /*                                                                 */
   /*******************************************************************/
  
   void cleanup(pSource)
   double* pSource;
  
   {
      /* make the node you are currently working with point to the    */
      /* start of the linked list                                     */
  
      current = head;
  
      while(crossingCount > 0){
  
         /* make the node you are currently working with point to the */
         /* next link in the list                                     */
  
         current = current -> next;
  
         /* free the node pointed to by head                          */
  
         free((void *) head);
        
         /* make the pointer to the start of the list point at the    */
         /* node we are currently working with.  Effectively this     */
         /* resets the pointer to the start of the list               */
  
         head = current;
  
         /* there's one less node in the list now                     */
  
         crossingCount --;
      } /* while */
  
      free((void *)pSource);
      fflush(stdout);
  
   } /* end cleanup() */
  
   /*******************************************************************/
   /*                                                                 */
   /* void printList()                                                */
   /* Purpose: this function prints to stdout the number of reference */
   /*          crossings found.  it then writes the actual time       */
   /*          values in binary form.  the time values are eight-byte */
   /*          floating-point numbers.  we write the values by walking*/
   i*          through the linked list, writing the value stored in   */
   /*          each node as we go.                                    */
   /*                                                                 */
   /*******************************************************************/
  
   void printList()
  
   {
      /* make the node you are currently working with point to the    */
      /* start of the linked list                                     */
  
      current = head;
  
      /* print the number of values you are going to send             */
  
      fprintf(stdout, "%u
", crossingCount);
  
      /* if the pointer to the node you are currently working with    */
      /* is NULL, you have reached the last node in the list          */
  
      while(current != (struct node *)0){
  
         /* write time value contained in the current node to stdout  */
  
         fwrite(&(current -> xTime), sizeof(double), (size_t) 1, stdout);
        
         /* make the node you are currently working with point to the */
         /* next link in the list                                     */
  
         current = current -> next;
  
      } /* while */
  
   } /* end printList() */

The makefile is named "zCross.mk".  To compile the above programs, use the
command:

   make -f zCross.mk

The "-g" option tells the C compiler to include debugging information.
You may remove it to create a smaller executable if you wish.

   zCross: zCross.o lList.o
      cc -g -o zCross zCross.o lList.o -lm
   zCross.o: zCross.c
      cc -g -c zCross.c
   lList.o: lList.c
      cc -g -c lList.c

[<>]

Outcomes