500 lines
13 KiB
C
500 lines
13 KiB
C
/*-----------------------------------------------------------------------
|
|
M A X I M I Z E
|
|
|
|
This optimises a peak against a motor or virtual motor. The algorithm
|
|
used is the same as in the program difrac by P. White and E. Gabe. The
|
|
algorithm used is more efficient then the one based on scans as in
|
|
fitcenter and optimise as it does not need as many counting operations.
|
|
|
|
The algorithm works as follows:
|
|
|
|
Data is kept in an array of length MAXPTS. We start at the middle of
|
|
the array.
|
|
|
|
- From the current position we first step to the left side until we reach
|
|
a point where the counts are less then half maximum or to many points.
|
|
Now there are four possible situations:
|
|
|
|
- The peak is still to our left. Reposition and do again.
|
|
- No peak found or peak is normal, i.e max close to 50. In either case
|
|
proceed with right side.
|
|
- peak all on left side. Calculate some indexes and calculate meridian.
|
|
|
|
- From the current position go to the right side until we reach the
|
|
point where counts are less then half maximum or the maximum number
|
|
of pints has been reached. Again four cases:
|
|
|
|
- The peak is still to our right. Reposition and do again.
|
|
- No peak found: error return.
|
|
- Normal peak: calculate meridian.
|
|
- peak all on left side. Calculate some indexes and calculate meridian.
|
|
|
|
- Calculate the median of the peak.
|
|
|
|
|
|
copyright: see copyright.h
|
|
|
|
|
|
Initial coding: Mark Koennecke, November 1999
|
|
-------------------------------------------------------------------------*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <tcl.h>
|
|
#include "fortify.h"
|
|
#include "sics.h"
|
|
#include "countdriv.h"
|
|
#include "counter.h"
|
|
#include "drive.h"
|
|
#include "maximize.h"
|
|
#include "motor.h"
|
|
|
|
#define MAXPTS 100
|
|
|
|
typedef struct __MAXIMIZE {
|
|
pObjectDescriptor pDes;
|
|
pCounter pCount;
|
|
int i360;
|
|
int maxpts;
|
|
} Maxxii;
|
|
|
|
/*-----------------------------------------------------------------------
|
|
put into 360 degree range if 360 degree flag is active.
|
|
*/
|
|
static float in360(pMax self, float fVal)
|
|
{
|
|
if (self->i360) {
|
|
if (fVal < 0.)
|
|
fVal = 360. + fVal;
|
|
if (fVal > 360.)
|
|
fVal = fVal - 360.;
|
|
}
|
|
return fVal;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
static int maxDrive(void *pObject, char *pVarName,
|
|
float fPos, SConnection * pCon)
|
|
{
|
|
pDummy pDum;
|
|
int status;
|
|
char pBueffel[132];
|
|
|
|
/* start */
|
|
pDum = (pDummy) pObject;
|
|
status = StartDevice(pServ->pExecutor,
|
|
pVarName, pDum->pDescriptor, pObject, pCon,
|
|
RUNDRIVE, fPos);
|
|
if (!status) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: failed to start %s", pVarName);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* wait */
|
|
while(DevExecLevelRunning(pServ->pExecutor,RUNDRIVE)){
|
|
TaskYield(pServ->pTasker);
|
|
}
|
|
|
|
/* check interrupts */
|
|
if (SCGetInterrupt(pCon) >= eAbortScan) {
|
|
SCWrite(pCon, "ERROR: Maximizing interrupted", eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int maxCount(void *pCount, CounterMode eMode,
|
|
float fPreset, long *lCts, SConnection * pCon)
|
|
{
|
|
int iRet;
|
|
|
|
SetCounterMode((pCounter) pCount, eMode);
|
|
iRet = DoCount((pCounter) pCount, fPreset, pCon, 1);
|
|
if (!iRet) {
|
|
return 0;
|
|
}
|
|
*lCts = GetCounts((pCounter) pCount, pCon);
|
|
if (SCGetInterrupt(pCon) >= eAbortScan) {
|
|
SCWrite(pCon, "ERROR: maximizing aborted", eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static float readMPDrivable(void *pVar, SConnection * pCon)
|
|
{
|
|
float value = -999.99;
|
|
pIDrivable pDriv = NULL;
|
|
pDummy pDum = (pDummy) pVar;
|
|
|
|
pDriv = GetDrivableInterface(pVar);
|
|
assert(pDriv != NULL);
|
|
value = pDriv->GetValue(pVar, pCon);
|
|
return value;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int MaximizePeak(pMax self, void *pVar, char *pVarName,
|
|
float fStep, CounterMode eMode,
|
|
float fPreset, SConnection * pCon)
|
|
{
|
|
float x[MAXPTS], y[MAXPTS];
|
|
float fMax, fCent, fS, fArea, fStart, fPos, fDA, fDisc;
|
|
int iBot, iTop, iMax, i, iRet, iSkip, iTMin, iTMax;
|
|
pIDrivable pDriv = NULL;
|
|
long lCts, lMax, lMin;
|
|
char pBueffel[132];
|
|
|
|
/* get drivable interface */
|
|
pDriv = GetDrivableInterface(pVar);
|
|
if (!pDriv) {
|
|
SCWrite(pCon, "ERROR: variable is NOT drivable, cannot maximize",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
start:
|
|
lMax = 0;
|
|
lMin = 0x7fffffff;
|
|
fStart = readMPDrivable(pVar, pCon);
|
|
if (fStart < -999999.) {
|
|
return 0;
|
|
}
|
|
|
|
/* search to the left until out of space or lCts < lMax/2. */
|
|
SCWrite(pCon, "Searching for low angle boundary..", eLog);
|
|
for (i = self->maxpts / 2; i >= 0; i--) {
|
|
/* drive motor */
|
|
fPos = fStart - (self->maxpts / 2 - i) * fStep;
|
|
fPos = in360(self, fPos);
|
|
if (maxDrive(pVar, pVarName, fPos, pCon) != 1) {
|
|
return 0;
|
|
}
|
|
x[i] = readMPDrivable(pVar, pCon);
|
|
/* count */
|
|
if (maxCount(self->pCount, eMode, fPreset, &lCts, pCon) != 1) {
|
|
return 0;
|
|
}
|
|
/* print a message */
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%5d %8.2f %ld", i, x[i], lCts);
|
|
SCWrite(pCon, pBueffel, eLog);
|
|
|
|
/* store counts and some logic */
|
|
if (lCts < 1) {
|
|
y[i] = 0.;
|
|
} else {
|
|
y[i] = (float) lCts;
|
|
}
|
|
if (lCts > lMax) {
|
|
lMax = lCts;
|
|
iMax = i;
|
|
}
|
|
if (lCts < lMin) {
|
|
lMin = lCts;
|
|
}
|
|
if (lCts < lMax / 2) {
|
|
break;
|
|
}
|
|
}
|
|
/* some logic: the four cases */
|
|
iSkip = 0;
|
|
iBot = i;
|
|
/* first case: peak out of range: do again */
|
|
if ((iMax == 0) && (lMax / 2 > lMin)) {
|
|
goto start;
|
|
}
|
|
/* no peak found or normal peak: continue at other side */
|
|
if ((i < 1) || (y[self->maxpts / 2] > lMax / 2)) {
|
|
iSkip = 0;
|
|
} else {
|
|
/* next case: all of the peak in measured half:
|
|
find max value and skip the right half
|
|
*/
|
|
for (i = self->maxpts / 2; i > 0; i--) {
|
|
if (y[i] > lMax / 2) {
|
|
iTop = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
iSkip = 1;
|
|
}
|
|
|
|
/*
|
|
do the same to the right hand side, but only if required
|
|
*/
|
|
if (iSkip == 0) {
|
|
lMin = 100000;
|
|
lMax = -100000;
|
|
SCWrite(pCon, "Searching for high angle boundary..", eLog);
|
|
for (i = self->maxpts / 2; i < self->maxpts; i++) {
|
|
/* drive motor */
|
|
fPos = fStart + (i - self->maxpts / 2) * fStep;
|
|
fPos = in360(self, fPos);
|
|
if (maxDrive(pVar, pVarName, fPos, pCon) != 1) {
|
|
return 0;
|
|
}
|
|
x[i] = readMPDrivable(pVar, pCon);
|
|
/* count */
|
|
if (maxCount(self->pCount, eMode, fPreset, &lCts, pCon) != 1) {
|
|
return 0;
|
|
}
|
|
/* print a message */
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%5d %8.2f %ld", i, x[i], lCts);
|
|
SCWrite(pCon, pBueffel, eLog);
|
|
|
|
/* store counts and some logic */
|
|
if (lCts < 1) {
|
|
y[i] = 0.;
|
|
} else {
|
|
y[i] = (float) lCts;
|
|
}
|
|
if (lCts > lMax) {
|
|
lMax = lCts;
|
|
iMax = i;
|
|
}
|
|
if (lCts < lMin) {
|
|
lMin = lCts;
|
|
}
|
|
if (lCts < lMax / 2) {
|
|
break;
|
|
}
|
|
}
|
|
/* some logic again: four cases */
|
|
iTop = i;
|
|
iTop++;
|
|
/* first case: peak is at high angle side */
|
|
if ((i > self->maxpts - 2) && (lMax * 0.5 > lMin)) {
|
|
goto start;
|
|
}
|
|
/* second case: no peak */
|
|
if ((iTop > self->maxpts - 2)) {
|
|
SCWrite(pCon, "ERROR: no peak found!", eError);
|
|
return 0;
|
|
}
|
|
/* third case: normal peak */
|
|
if (y[self->maxpts / 2] >= 0.5 * lMax) {
|
|
iTop--;
|
|
}
|
|
/*
|
|
fourth case: the peak is completely at the high angle
|
|
side
|
|
*/
|
|
else {
|
|
for (i = self->maxpts / 2; i < self->maxpts; i++) {
|
|
if (y[i] > lMax / 2) {
|
|
iBot = i - 1;
|
|
break;
|
|
}
|
|
}
|
|
iTop--;
|
|
}
|
|
}
|
|
/* end of iSkip */
|
|
if ((iBot < 2) || (iTop > self->maxpts - 2) || (lMax < lMin * 2)) {
|
|
SCWrite(pCon, "ERROR: no peak found!", eError);
|
|
return 0;
|
|
}
|
|
#ifdef DEBUG
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "iBot = %d, iTop = %d, lMax = %ld", iBot, iTop, lMax);
|
|
SCWrite(pCon, pBueffel, eWarning);
|
|
#endif
|
|
|
|
/* calculate median */
|
|
fArea = 0.;
|
|
for (i = iBot; i < iTop; i++) {
|
|
fArea += (x[i + 1] - x[i]) * (y[i + 1] + y[i]) * .25;
|
|
}
|
|
fS = 0.;
|
|
for (i = iBot; i < iTop; i++) {
|
|
fS += (x[i + 1] - x[i]) * (y[i + 1] + y[i]) * .5;
|
|
if (fS > fArea) {
|
|
break;
|
|
}
|
|
}
|
|
fS -= .5 * (x[i + 1] - x[i]) * (y[i + 1] + y[i]);
|
|
fDA = fArea - fS;
|
|
if (y[i + 1] == y[i]) {
|
|
fCent = x[i] + fDA / y[i];
|
|
} else {
|
|
fS = (y[i + 1] - y[i]) / (x[i + 1] - x[i]);
|
|
if ((y[i] * y[i] + 2. * fS * fDA) < 0.) {
|
|
SCWrite(pCon, "ERROR calculating median", eError);
|
|
return 0;
|
|
}
|
|
fDisc = sqrt(y[i] * y[i] + 2. * fS * fDA);
|
|
fCent = x[i] + (fDisc - y[i]) / fS;
|
|
}
|
|
/* finished ! */
|
|
for (i = 0; i < iTop - 1; i++) {
|
|
if (fCent >= x[i] && fCent < x[i + 1]) {
|
|
lCts = y[i];
|
|
break;
|
|
}
|
|
}
|
|
maxDrive(pVar, pVarName, fCent, pCon);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "Found peak center at %8.2f, Count = %ld", fCent,
|
|
lCts);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
static void MaxKill(void *pData)
|
|
{
|
|
pMax self = (pMax) pData;
|
|
if (self == NULL)
|
|
return;
|
|
|
|
if (self->pDes)
|
|
DeleteDescriptor(self->pDes);
|
|
free(self);
|
|
}
|
|
|
|
/*-------------------- wrap-i-wrap ----------------------------------*/
|
|
int MaximizeFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pMax pNew = NULL;
|
|
CommandList *pCom = NULL;
|
|
pICountable pCts = NULL;
|
|
|
|
if (argc < 2) {
|
|
SCWrite(pCon,
|
|
"ERROR: insufficient number of arguments to MaximizeFactory",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
/* find a countable */
|
|
pCom = FindCommand(pSics, argv[1]);
|
|
if (pCom == NULL) {
|
|
SCWrite(pCon, "ERROR: counter not found in MaximizeFactory", eError);
|
|
return 0;
|
|
}
|
|
pCts = GetCountableInterface(pCom->pData);
|
|
if (!pCts) {
|
|
SCWrite(pCon, "ERROR: argument to MaximizeFactory is no counter",
|
|
eError);
|
|
return 0;
|
|
}
|
|
pNew = (pMax) malloc(sizeof(Maxxii));
|
|
pNew->pDes = CreateDescriptor("Maximizer");
|
|
pNew->pCount = pCom->pData;
|
|
pNew->i360 = 0;
|
|
pNew->maxpts = 100;
|
|
|
|
AddCommand(pSics, "max", MaximizeAction, MaxKill, pNew);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
* max motor step preset mode
|
|
* ---------------------------------------------------------------------*/
|
|
int MaximizeAction(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pMax self = NULL;
|
|
CommandList *pCom = NULL;
|
|
char pBueffel[256];
|
|
double dVal;
|
|
float fStep, fPreset;
|
|
CounterMode eCount;
|
|
int iRet, iVal;
|
|
|
|
self = (pMax) pData;
|
|
assert(self);
|
|
assert(pCon);
|
|
|
|
/* check privilege */
|
|
if (!SCMatchRights(pCon, usUser)) {
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* enough arguments ? */
|
|
if (argc < 5) {
|
|
if (argc > 1) {
|
|
strtolower(argv[1]);
|
|
if (strcmp(argv[1], "in360") == 0) {
|
|
if (argc > 2) {
|
|
iVal = atoi(argv[2]);
|
|
if (iVal != 0 && iVal != 1) {
|
|
SCWrite(pCon, "ERROR: only 0, 1 allowed for in360", eError);
|
|
return 0;
|
|
}
|
|
self->i360 = iVal;
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else {
|
|
snprintf(pBueffel, 255, "max.in360 = %d", self->i360);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
return 1;
|
|
}
|
|
}
|
|
if (strcmp(argv[1], "maxpts") == 0) {
|
|
if (argc > 2) {
|
|
iVal = atoi(argv[2]);
|
|
if (iVal < 10 || iVal > 100) {
|
|
SCWrite(pCon, "ERROR: maxpst must be between 10 and 100",
|
|
eError);
|
|
return 0;
|
|
}
|
|
self->maxpts = iVal;
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else {
|
|
snprintf(pBueffel, 255, "max.maxpts = %d", self->maxpts);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
SCWrite(pCon, "ERROR: Insufficient number of arguments to max",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
/* decode arguments */
|
|
pCom = FindCommand(pSics, argv[1]);
|
|
if (!pCom) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: object %s NOT found!", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
if (!pCom->pData) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: object %s Invalid!!", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
iRet = Tcl_GetDouble(pSics->pTcl, argv[2], &dVal);
|
|
if (iRet != TCL_OK) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: failed to convert %s to number", argv[2]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
fStep = (float) dVal;
|
|
|
|
iRet = Tcl_GetDouble(pSics->pTcl, argv[4], &dVal);
|
|
if (iRet != TCL_OK) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: failed to convert %s to number", argv[4]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
fPreset = (float) dVal;
|
|
|
|
/* convert countmode */
|
|
strtolower(argv[3]);
|
|
if (strcmp(argv[3], "timer") == 0) {
|
|
eCount = eTimer;
|
|
} else {
|
|
eCount = ePreset;
|
|
}
|
|
return MaximizePeak(self, pCom->pData, argv[1], fStep, eCount,
|
|
fPreset, pCon);
|
|
}
|