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