Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages | Examples

dmu.c

Go to the documentation of this file.
00001 /************************************************************************************************/
00002 /*! \file dmu.c
00003 *  \brief Interact with DMU.
00004 *  \author $Author: jayhawk_hokie $
00005 *  \version $Revision: 1.4 $
00006 *  \date    $Date: 2007/08/02 23:53:42 $
00007 ************************************************************************************************/
00008 /*!
00009 *
00010 ************************************************************************************************/
00011 
00012 #include "dmu.h"
00013 
00014 /* Global variables for MT scope */
00015 
00016 /* DMU Data Conversion Factors
00017  * The first group needs to be set in dmuInit or during the setup/config
00018  * stage of a finished application, but accessed in the DMU polling
00019  * function, which is actually a signal handler (using the current
00020  * realtime timer setup).
00021  */
00022 int dmufd;              /* fd to the open DMU serial port */
00023 char *dmudata;          /* DMU raw data buffer */
00024 char *cmd;              /* DMU cmd string buffer */
00025 float bias[7];          /* DMU channel biases */
00026 const float stepSize = ADCV_REF/4095.0; /* ADC voltage stepsize */
00027 
00028 /* Shared memory objects
00029  * The next group is for sharing data between the two main threads
00030  * of the finished app.  One thread will read data from the DMU at
00031  * a rate as close as possible (using timers) to the commanded rate,
00032  * while the rest of the app will run in the other thread.
00033  */
00034 adat_t *sh_attdat;      /* Shared attitude data */
00035 int sh_nextw, sh_nextr; /* Next shared data write and read indexes */
00036 long sh_serial;         /* Shared data serial number */
00037 int rrate;              /* Commanded read rate */
00038 
00039 int dmuInit(long maxBufferSize, int rate, int flags) {
00040   pthread_t daq_thr;
00041 
00042   /* Flags are not currently used.
00043    * This was originally coded around 5/02.  The prototype was
00044    *   substantially updated with new args (it used to have just rate)
00045    *   around 7/8/02,but the actual code changes were never made.
00046    *   maxBufferSize implemented 3/4/03.
00047    */
00048 
00049   if (maxBufferSize == 0) maxBufferSize = 1024;
00050   sh_attdat = (adat_t *)malloc(sizeof(adat_t) * maxBufferSize);
00051   sh_serial = 0;
00052   sh_nextw = sh_nextr = 0;
00053   rrate = rate;
00054 
00055   if (pthread_create(&daq_thr, NULL, _daq, NULL)) {
00056     perror("pthread_create()");
00057     /* commented out because couldnt find "errlog"
00058      * errlog("dmuInit(): pthread_create() failed to create DMU read thread.\n");
00059      */
00060     return(-1);
00061   }
00062 
00063   /* This is the parent process. */
00064   return(0);
00065 }
00066 
00067 
00068 /* This is the child process, where all the fun stuff happens... */
00069 
00070 void* _daq(void* nada) {
00071   struct timeval rp_tv;
00072   struct itimerval rptimer;
00073   int i;
00074   
00075   /* First, let's initialize the DMU... */
00076 
00077   init_serial(DMU_PORT, B19200, &dmufd);        /* Initialize the port */
00078   cmd = (char *)malloc(sizeof(char)*6);         /* Setup the command string */
00079   sprintf(cmd, "!0RA%c", 6);
00080   dmudata = (char *)malloc(sizeof(char)*20);    /* Setup the data buffer */
00081 
00082   /* Next, we setup the period timer */
00083   rp_tv.tv_sec = 0;             /* 0 seconds */
00084   rp_tv.tv_usec = 1000000/rrate;        /* 10^6 / rate microseconds->
00085                                  * 10^6 usec/sec * 1/rate sec/read ->
00086                                  * sec's cancel -> 10^6/rate usec/read */
00087   if (rp_tv.tv_usec < 12500) {
00088     rp_tv.tv_usec = 1;
00089   } else {
00090     rp_tv.tv_usec -= 12500;
00091   }
00092   rptimer.it_interval =
00093     rptimer.it_value = rp_tv;   /* Set rptimer to rp_tv and reset to rp_tv */
00094 
00095   for (i = 0; i <= 6; i++) bias[i] = BIAS_DFLT;  /* Init bias settings */
00096   /* Then enter the tuned values (this should be done in dynamic or
00097    * perhaps even automatic configuration at some point. */
00098   bias[XQRS] = 2.467;
00099   bias[YQRS] = 2.520;
00100   bias[ZQRS] = 2.515;
00101   bias[XACC] = 2.423;
00102   bias[YACC] = 2.540;
00103   bias[ZACC] = 3.690;
00104   bias[TSEN] = TS_OFFSET;
00105 
00106   /* Start the timer */
00107   setitimer(ITIMER_REAL, &rptimer, NULL);
00108   signal(SIGALRM, polldmu);     /* Set the sighandler */
00109   while(1)                      /* And loop forever */
00110     sleep(5);
00111   return NULL;
00112 }
00113 
00114 /* This is our SIGALRM handler.  It WILL be called every 10^6/rate usec,
00115  * +- 10 usec max.  If the write()'s and/or read()'s fail repeatedly
00116  * (they're non-blocking and will just loop) enough that the timer
00117  * expires again before this function finishes, we have a loop counter
00118  * that will add the timer reset value to our deltat calculation so that
00119  * when we do get data, it will have the right age.
00120  *
00121  * The danger here is that if we the timer expires during a read() or
00122  * write() loop, we'll have sent half a command, or our read data will
00123  * be permanently misaligned.  If this gets to be a problem, we may need
00124  * to go to blocking read()'s.  We simply cannot afford to catch a signal
00125  * midway through a read() loop, because since the data is all numeric
00126  * and includes no sentinel values, any misalignment will be noncorrectable.
00127  *
00128  * A possible workaround without doing blocking read()'s (which would
00129  * mean we couldn't count the signal) would be to simply change the signal
00130  * disposition for the duration of the function to either SIGIGN or
00131  * perhaps even a different handler that would only increment the loop count.
00132  */
00133 void polldmu(int sig) {
00134   static int tbw,               /* Total bytes written */
00135         tbr,                    /* Total bytes read */
00136         lc=0,                   /* Loop counter */
00137         curchan,                /* Current channel */
00138         i, hob, lob;
00139   static long deltat,           /* Total deltat from last read */
00140         curts,                  /* Current timestamp */
00141         lastts;                 /* Last timestamp */
00142   static float adVolts[7],
00143         temp,
00144         xRate, yRate, zRate,
00145         xAcc, yAcc, zAcc;
00146   static struct timeval tod;
00147 
00148   lc++;         /* Increment the loop counter.  This way, if the timer
00149                  * expires before we finish handling the data, we'll know,
00150                  * and at least we can calculate a correct deltat when we
00151                  * do get that far.
00152                  */
00153   for (tbw = 0; tbw < 5; )              /* Loop the write until the entire */
00154     tbw += write(dmufd, cmd, 5);        /* command is written */
00155 
00156   for (tbr = 0; tbr < 14; )               /* Loop the read until we get a */
00157     tbr += read(dmufd, dmudata+tbr, 14-tbr); /* full set of 14 data bytes */
00158 
00159   gettimeofday(&tod, NULL);             /* Get the timer value */
00160   curts = tod.tv_usec;                  /* Fetch the usec element.  This is
00161                                          * the raw timestamp for this data. */
00162   deltat = curts - lastts;              /* Calc deltat.  lc should be >= 1 */
00163   if (deltat < 0) deltat += 1000000;
00164 
00165   /* Calc individual channel values */
00166   /* This is where we were screwing up the upper-half of the data range because
00167    * we didn't account for the fact that the implicit cast from char to int
00168    * interprets the bits using 8-bit 2's complement notation, meaning the range
00169    * is -127 to 127, and not 0 to 255.  Thus, 0-127 are mapped as-is, while
00170    * 128-255 get mapped to -127-(-1).  A cleaner solution than the one implemented
00171    * may be to do a byte-wise copy from &dmudata[i] to &some_int to sidestep the
00172    * issues of typecasting, like perhaps:
00173    * memcpy(&some_int, &(dmudata[i]), 2) OR
00174    * memcpy((void *)(1+&some_int), &(dmudata[i]), 1);
00175    * memcpy(&some_int, &(dmudata[i+1]), 1);
00176    * But what's here works, so it's staying for now.
00177    */
00178   for (i = 0, curchan = 6; i < 14; i+=2, curchan--) {
00179     if (dmudata[i] < 0)
00180         hob = dmudata[i] + 255;
00181     else
00182         hob = dmudata[i];
00183     if (dmudata[i+1] < 0)
00184         lob = dmudata[i+1] + 255;
00185     else
00186         lob = dmudata[i+1];
00187     adVolts[curchan] = (256 * hob + lob) * stepSize;
00188     dmudata[i] = dmudata[i+1] = 0;                  /* Then clear buffer */
00189   }
00190 
00191   /* Calculate data values... */
00192   xRate = (-1)*(adVolts[XQRS] - bias[XQRS]) / RATE_SF;
00193   yRate = (-1)*(adVolts[YQRS] - bias[YQRS]) / RATE_SF;
00194   zRate = (-1)*(adVolts[ZQRS] - bias[ZQRS]) / RATE_SF;
00195   xAcc = ((adVolts[XACC] - bias[XACC]) / ACC_SF * G);
00196   yAcc = ((adVolts[YACC] - bias[YACC]) / ACC_SF * G);
00197   zAcc = ((adVolts[ZACC] - bias[ZACC]) / ACC_SF * G);
00198   temp = adVolts[TSEN] / TEMP_SF - bias[TSEN];
00199 
00200   /* Put the data in the shared mem space... */
00201   sh_attdat[sh_nextw].xrate = xRate;
00202   sh_attdat[sh_nextw].yrate = yRate;
00203   sh_attdat[sh_nextw].zrate = zRate;
00204   sh_attdat[sh_nextw].xaccel = xAcc;
00205   sh_attdat[sh_nextw].yaccel = yAcc;
00206   sh_attdat[sh_nextw].zaccel = zAcc;
00207   sh_attdat[sh_nextw].temp = temp;
00208   sh_attdat[sh_nextw].deltat = deltat;
00209   sh_attdat[sh_nextw].serial = sh_serial;
00210   if (sh_nextw==1022) sh_nextw=0;
00211     else sh_nextw++;
00212   sh_serial++;
00213   
00214   /* Whew!  We made it.  Let's reset the loop counter and update the
00215    * timestamps and return.
00216    */
00217   lc = 0;
00218   lastts = curts;
00219   return;
00220 }
00221 
00222 
00223 int dmuReadData(adat_t **accData, int maxsets) {
00224   static int i,
00225         ind;
00226 
00227   if (sh_nextr < 1023)                  /* We haven't reached the end, so ... */
00228     ind = sh_nextr;                     /* the index is the nextread ptr */
00229   else
00230     ind = sh_nextr - 1023;              /* Else, it rolls back around */
00231 
00232   /*  Now, we run the loop until we get to the nextwrite ptr (the ptr
00233    *   that points to the sharedmem address where the next data will go.
00234    */
00235   for (i = 0; i < maxsets && ind != sh_nextw; i++) {
00236     if ((sh_nextr + i) < 1023)
00237       ind = sh_nextr + i;
00238     else
00239       ind = sh_nextr + i - 1023;
00240 /*
00241     printf("%d -> %d\t", ind, i);
00242     fflush(stdout);
00243 */
00244     (*accData)[i].xrate = sh_attdat[ind].xrate;
00245     (*accData)[i].yrate = sh_attdat[ind].yrate;
00246     (*accData)[i].zrate = sh_attdat[ind].zrate;
00247     (*accData)[i].xaccel = sh_attdat[ind].xaccel;
00248     (*accData)[i].yaccel = sh_attdat[ind].yaccel;
00249     (*accData)[i].zaccel = sh_attdat[ind].zaccel;
00250     (*accData)[i].temp = sh_attdat[ind].temp;
00251     (*accData)[i].deltat = sh_attdat[ind].deltat;
00252   }
00253   sh_nextr = ind++;
00254   if (i) return(i-1);
00255     else return(0);
00256 }
00257 
00258 /* Do not change the comments below - they will be added automatically by CVS*/
00259 /*****************************************************************************
00260 *       $Log: dmu.c,v $
00261 *       Revision 1.4  2007/08/02 23:53:42  jayhawk_hokie
00262 *       Fixed Warnings.
00263 *
00264 *       Revision 1.3  2007/08/02 23:40:44  jayhawk_hokie
00265 *       Fixed Warnings.
00266 *
00267 *
00268 *
00269 ******************************************************************************/
00270 

Generated on Wed Sep 5 12:54:19 2007 for DSACSS Operational Code by  doxygen 1.3.9.1