PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/ATF2/control-software/epics-3.14.10/support/std/2-6/stdApp/src/devEpidFast.c

http://atf2flightsim.googlecode.com/
C | 395 lines | 308 code | 30 blank | 57 comment | 32 complexity | bfa661c49a721498fd8f837fafe9b45e MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  1. /* devEpidMpf.cc
  2. Author: Mark Rivers
  3. Date: 10/11/99
  4. 9-July-2004 Converted from MPF to asyn and from C++ to C
  5. */
  6. #include <stdlib.h>
  7. #include <stddef.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <math.h>
  11. #include <dbAccess.h>
  12. #include <dbDefs.h>
  13. #include <link.h>
  14. #include <epicsPrint.h>
  15. #include <epicsMutex.h>
  16. #include <epicsString.h>
  17. #include <dbCommon.h>
  18. #include <recSup.h>
  19. #include <devSup.h>
  20. #include <recGbl.h>
  21. #include <cantProceed.h>
  22. #include <alarm.h>
  23. #include <asynDriver.h>
  24. #include <asynDrvUser.h>
  25. #include <asynFloat64.h>
  26. #include "epidRecord.h"
  27. #include <epicsExport.h>
  28. typedef struct {
  29. double setPoint;
  30. double actual;
  31. double error;
  32. double prevError;
  33. int feedbackOn;
  34. int prevFeedbackOn;
  35. double highLimit;
  36. double lowLimit;
  37. double output;
  38. double KP;
  39. double KI;
  40. double KD;
  41. double P;
  42. double I;
  43. double D;
  44. double callbackInterval;
  45. double timePerPointRequested;
  46. double timePerPointActual;
  47. asynFloat64 *pfloat64Input;
  48. void *float64InputPvt;
  49. asynDrvUser *pdrvUser;
  50. void *drvUserPvt;
  51. asynFloat64 *pfloat64Output;
  52. void *float64OutputPvt;
  53. int inputChannel;
  54. char *inputName;
  55. int outputChannel;
  56. char *outputName;
  57. asynUser *pcallbackDataAsynUser;
  58. asynUser *pcallbackIntervalAsynUser;
  59. asynUser *pfloat64OutputAsynUser;
  60. double averageStore;
  61. int numAverage;
  62. int accumulated;
  63. epicsMutexId mutexId;
  64. } epidFastPvt;
  65. /* These functions are called from the record */
  66. static long init_record(epidRecord *pepid);
  67. static long update_params(epidRecord *epid);
  68. /* These are private functions */
  69. static void computeNumAverage(epidFastPvt *pPvt);
  70. static void do_PID(epidFastPvt *pPvt, double readback);
  71. /* These are callback functions, called from the driver */
  72. static void dataCallback(void *drvPvt, asynUser *pasynUser, double readback);
  73. static void intervalCallback(void *drvPvt, asynUser *pasynUser, double seconds);
  74. typedef struct {
  75. long number;
  76. DEVSUPFUN report;
  77. DEVSUPFUN init;
  78. DEVSUPFUN init_record;
  79. DEVSUPFUN get_ioint_info;
  80. DEVSUPFUN update_params;
  81. } epidFastDset;
  82. epidFastDset devEpidFast = {
  83. 6,
  84. NULL,
  85. NULL,
  86. init_record,
  87. NULL,
  88. update_params
  89. };
  90. epicsExportAddress(dset, devEpidFast);
  91. static long init_record(epidRecord *pepid)
  92. {
  93. struct instio *pinstio;
  94. asynStatus status;
  95. asynUser *pasynUser;
  96. asynInterface *pasynInterface;
  97. epidFastPvt *pPvt;
  98. char *tok_save;
  99. char *p;
  100. const char *drvUserName;
  101. size_t drvUserSize;
  102. char temp[100];
  103. void *registrarPvt;
  104. pPvt = callocMustSucceed(1, sizeof(*pPvt), "devEpidFast::init_record");
  105. pepid->dpvt = pPvt;
  106. pPvt->KP = 1;
  107. pPvt->lowLimit = 1.;
  108. pPvt->highLimit =-1.;
  109. pinstio = (struct instio*)&(pepid->inp.value);
  110. /* Parse to get inputName, inputChannel,
  111. * outputName, outputChannel
  112. * Copy to temp, since epicsStrtok_r overwrites it */
  113. strncpy(temp, pinstio->string, sizeof(temp));
  114. tok_save = NULL;
  115. p = epicsStrtok_r(temp, ", ", &tok_save);
  116. pPvt->inputName = epicsStrDup(p);
  117. p = epicsStrtok_r(NULL, ", ", &tok_save);
  118. pPvt->inputChannel = atoi(p);
  119. p = epicsStrtok_r(NULL, ", ", &tok_save);
  120. pPvt->outputName = epicsStrDup(p);
  121. p = epicsStrtok_r(NULL, ", ", &tok_save);
  122. pPvt->outputChannel = atoi(p);
  123. pPvt->mutexId = epicsMutexCreate();
  124. pasynUser = pasynManager->createAsynUser(0, 0);
  125. pPvt->pcallbackDataAsynUser = pasynUser;
  126. status = pasynManager->connectDevice(pasynUser, pPvt->inputName,
  127. pPvt->inputChannel);
  128. if (status != asynSuccess) {
  129. errlogPrintf("devEpidFast::init_record, %s error in connectDevice"
  130. " to input %s\n",
  131. pepid->name, pasynUser->errorMessage);
  132. goto bad;
  133. }
  134. pasynInterface = pasynManager->findInterface(pasynUser,
  135. asynFloat64Type, 1);
  136. if (!pasynInterface) {
  137. errlogPrintf("devEpidFast::init_record %s, cannot find "
  138. "asynFloat64 interface %s\n",
  139. pepid->name, pasynUser->errorMessage);
  140. goto bad;
  141. }
  142. pPvt->pfloat64Input = (asynFloat64 *) pasynInterface->pinterface;
  143. pPvt->float64InputPvt = pasynInterface->drvPvt;
  144. pasynInterface = pasynManager->findInterface(pasynUser,
  145. asynDrvUserType, 1);
  146. if (!pasynInterface) {
  147. errlogPrintf("devEpidFast::init_record %s, cannot find "
  148. "asynDrvUser interface %s\n",
  149. pepid->name, pasynUser->errorMessage);
  150. goto bad;
  151. }
  152. pPvt->pdrvUser = (asynDrvUser *)pasynInterface->pinterface;
  153. pPvt->drvUserPvt = pasynInterface->drvPvt;
  154. pasynUser = pasynManager->createAsynUser(0, 0);
  155. pPvt->pfloat64OutputAsynUser = pasynUser;
  156. status = pasynManager->connectDevice(pasynUser, pPvt->outputName,
  157. pPvt->outputChannel);
  158. if (status != asynSuccess) {
  159. errlogPrintf("devEpidFast::init_record %s, error in connectDevice"
  160. " to output %s\n",
  161. pepid->name, pasynUser->errorMessage);
  162. goto bad;
  163. }
  164. pasynInterface = pasynManager->findInterface(pasynUser,
  165. asynFloat64Type, 1);
  166. if (!pasynInterface) {
  167. errlogPrintf("devEpidFast::init_record %s, cannot find "
  168. "asynFloat64 interface %s\n",
  169. pepid->name, pasynUser->errorMessage);
  170. goto bad;
  171. }
  172. pPvt->pfloat64Output = (asynFloat64 *)pasynInterface->pinterface;
  173. pPvt->float64OutputPvt = pasynInterface->drvPvt;
  174. pPvt->pdrvUser->create(pPvt->drvUserPvt, pPvt->pcallbackDataAsynUser,
  175. "data", &drvUserName, &drvUserSize);
  176. if (!drvUserSize) {
  177. errlogPrintf("devEpidFast::init_record %s, asynDrvUser->create "
  178. "failed for data %s\n",
  179. pepid->name, pPvt->pcallbackDataAsynUser->errorMessage);
  180. goto bad;
  181. }
  182. pPvt->pfloat64Input->registerInterruptUser(pPvt->float64InputPvt,
  183. pPvt->pcallbackDataAsynUser,
  184. dataCallback, pPvt, &registrarPvt);
  185. pPvt->pcallbackIntervalAsynUser = pasynManager->duplicateAsynUser(
  186. pPvt->pcallbackDataAsynUser, 0, 0);
  187. pPvt->pdrvUser->create(pPvt->drvUserPvt, pPvt->pcallbackIntervalAsynUser,
  188. "SCAN_PERIOD", &drvUserName, &drvUserSize);
  189. if (!drvUserSize) {
  190. errlogPrintf("devEpidFast::init_record %s, asynDrvUser->create "
  191. "failed for SCAN_PERIOD %s\n",
  192. pepid->name, pPvt->pcallbackIntervalAsynUser->errorMessage);
  193. goto bad;
  194. }
  195. pPvt->pfloat64Input->registerInterruptUser(pPvt->float64InputPvt,
  196. pPvt->pcallbackIntervalAsynUser,
  197. intervalCallback, pPvt,
  198. &registrarPvt);
  199. status = pPvt->pfloat64Input->read(pPvt->float64InputPvt,
  200. pPvt->pcallbackIntervalAsynUser,
  201. &pPvt->callbackInterval);
  202. update_params(pepid);
  203. return(0);
  204. bad:
  205. pepid->pact=1;
  206. return(0);
  207. }
  208. static long update_params(epidRecord *pepid)
  209. {
  210. epidFastPvt *pPvt = (epidFastPvt *)pepid->dpvt;
  211. epicsMutexLock(pPvt->mutexId);
  212. /* If the user has changed the value of dt, requested time per point,
  213. * then update numAverage */
  214. if (pepid->dt != pPvt->timePerPointActual) {
  215. pPvt->timePerPointRequested = pepid->dt;
  216. computeNumAverage(pPvt);
  217. }
  218. /* Copy values from private structure to record */
  219. pepid->cval = pPvt->actual;
  220. pepid->err = pPvt->error;
  221. pepid->oval = pPvt->output;
  222. pepid->p = pPvt->P;
  223. pepid->i = pPvt->I;
  224. pepid->d = pPvt->D;
  225. pepid->dt = pPvt->timePerPointActual;
  226. /* Copy values from record to private structure */
  227. pPvt->feedbackOn = pepid->fbon;
  228. pPvt->highLimit = pepid->drvh;
  229. pPvt->lowLimit = pepid->drvl;
  230. pPvt->KP = pepid->kp;
  231. pPvt->KI = pepid->ki;
  232. pPvt->KD = pepid->kd;
  233. pPvt->setPoint = pepid->val;
  234. epicsMutexUnlock(pPvt->mutexId);
  235. asynPrint(pPvt->pcallbackDataAsynUser, ASYN_TRACEIO_DEVICE,
  236. "devEpidFast::update_params, record=%s\n "
  237. " feedback state %d, previous feedback state %d\n"
  238. " cval=%f, err=%f, oval=%f,\n"
  239. " P=%f, I=%f, D=%f, dt=%f\n",
  240. pepid->name,
  241. pPvt->feedbackOn, pPvt->prevFeedbackOn,
  242. pepid->cval, pepid->err, pepid->oval,
  243. pepid->p, pepid->i, pepid->d, pepid->dt);
  244. pepid->udf=0;
  245. return(0);
  246. }
  247. static void computeNumAverage(epidFastPvt *pPvt)
  248. {
  249. pPvt->numAverage = 0.5 + pPvt->timePerPointRequested /
  250. pPvt->callbackInterval;
  251. if (pPvt->numAverage < 1) pPvt->numAverage = 1;
  252. pPvt->timePerPointActual = pPvt->numAverage * pPvt->callbackInterval;
  253. }
  254. /* This is the function that is called back from the driver when the time
  255. * interval changes */
  256. static void intervalCallback(void *drvPvt, asynUser *pasynUser, double seconds)
  257. {
  258. epidFastPvt *pPvt = (epidFastPvt *)drvPvt;
  259. epicsMutexLock(pPvt->mutexId);
  260. pPvt->callbackInterval = seconds;
  261. computeNumAverage(pPvt);
  262. epicsMutexUnlock(pPvt->mutexId);
  263. }
  264. /* This is the function that is called back from the driver when a new readback
  265. * value is obtained */
  266. static void dataCallback(void *drvPvt, asynUser *pasynUser, double readBack)
  267. {
  268. epidFastPvt *pPvt = (epidFastPvt *)drvPvt;
  269. epicsMutexLock(pPvt->mutexId);
  270. /* No need to average if collecting every point */
  271. if (pPvt->numAverage == 1) {
  272. do_PID(pPvt, readBack);
  273. goto done;
  274. }
  275. pPvt->averageStore += readBack;
  276. if (++pPvt->accumulated < pPvt->numAverage) goto done;
  277. /* We have now collected the desired number of points to average */
  278. pPvt->averageStore /= pPvt->accumulated;
  279. do_PID(pPvt, pPvt->averageStore);
  280. pPvt->averageStore = 0.;
  281. pPvt->accumulated = 0;
  282. done:
  283. epicsMutexUnlock(pPvt->mutexId);
  284. }
  285. static void do_PID(epidFastPvt *pPvt, double readBack)
  286. /*
  287. 04/20/00 MLR Changed timing logic. No longer reprogram IP-330 clock,
  288. just change whether the callback is executed when called.
  289. 05/02/00 MLR Fixed bug in time calculation for integral term
  290. 05/03/00 MLR Added more sanity checks to integral term:
  291. If KI is 0. then set I to DRVL if KP is greater than 0,
  292. set I to DRVH is KP is less than 0.
  293. If feedback is off don't change integral term.
  294. 05/17/00 MLR Added another sanity checks to integral term:
  295. When feedback goes from OFF to ON then set integral term
  296. equal to the current value of the DAC output. This ensures
  297. a "bumpless" turn-on of feedback
  298. 01/16/01 MLR Added check for valid pFast in fastPID::init
  299. 04/08/03 MLR Converted to abstract base class, fastPID, from Ip300PID.
  300. fastPID is device-independent. This class must be derived
  301. by a device-dependent class that implements writeOutput(),
  302. readOutput(), and periodically calls doPID(), typically when
  303. a new input value is available.
  304. Converted Ip330PIDServer to base class fastPIDServer,
  305. moved to this file.
  306. 07/09/04 MLR Converted from MPF to asyn, and from C++ to C
  307. */
  308. {
  309. double dt;
  310. double derror;
  311. double dI;
  312. dt = pPvt->callbackInterval;
  313. pPvt->actual = readBack;
  314. pPvt->prevError = pPvt->error;
  315. pPvt->error = pPvt->setPoint - pPvt->actual;
  316. derror = pPvt->error - pPvt->prevError;
  317. pPvt->P = pPvt->KP*pPvt->error;
  318. /* Sanity checks on integral term:
  319. * 1) Don't increase I if output >= highLimit
  320. * 2) Don't decrease I if output <= lowLimit
  321. * 3) Don't change I if feedback is off
  322. * 4) Limit the integral term to be in the range betweem DRLV and DRVH
  323. * 5) If KI is zero then set the sum to DRVL (if KI is positive), or
  324. * DRVH (if KP is negative) to allow easily turning off the
  325. * integral term for PID tuning.
  326. */
  327. dI = pPvt->KP*pPvt->KI*pPvt->error*dt;
  328. if (pPvt->feedbackOn) {
  329. if (!pPvt->prevFeedbackOn) {
  330. pPvt->pfloat64Output->read(pPvt->float64OutputPvt,
  331. pPvt->pfloat64OutputAsynUser,
  332. &pPvt->I);
  333. } else {
  334. if (((pPvt->output > pPvt->lowLimit) &&
  335. (pPvt->output < pPvt->highLimit)) ||
  336. ((pPvt->output >= pPvt->highLimit) && ( dI < 0.)) ||
  337. ((pPvt->output <= pPvt->lowLimit) && ( dI > 0.))) {
  338. pPvt->I = pPvt->I + dI;
  339. if (pPvt->I < pPvt->lowLimit) pPvt->I = pPvt->lowLimit;
  340. if (pPvt->I > pPvt->highLimit) pPvt->I = pPvt->highLimit;
  341. }
  342. }
  343. }
  344. if (pPvt->KI == 0.) {
  345. if (pPvt->KP > 0.) pPvt->I = pPvt->lowLimit;
  346. else pPvt->I = pPvt->highLimit;
  347. }
  348. if (dt>0.0) pPvt->D = pPvt->KP*pPvt->KD*(derror/dt); else pPvt->D = 0.0;
  349. pPvt->output = (pPvt->P + pPvt->I + pPvt->D);
  350. /* Limit output to range from low to high limit */
  351. if (pPvt->output > pPvt->highLimit) pPvt->output = pPvt->highLimit;
  352. if (pPvt->output < pPvt->lowLimit) pPvt->output = pPvt->lowLimit;
  353. /* If feedback is on write output */
  354. if (pPvt->feedbackOn) {
  355. pPvt->pfloat64Output->write(pPvt->float64OutputPvt,
  356. pPvt->pfloat64OutputAsynUser,
  357. pPvt->output);
  358. }
  359. /* Save state of feedback */
  360. pPvt->prevFeedbackOn = pPvt->feedbackOn;
  361. }