Files
sics/hkl.c
2007-11-27 13:36:15 +00:00

1801 lines
46 KiB
C

/*---------------------------------------------------------------------------
H K L
Implementation of the SICS object for doing four circle diffractometer
angle calculations. The actual transformation routines were recoded
in C from F77 routines provided by J. Allibon, ILL with the MAD
system.
copyright: see copyright.h
Mark Koennecke, February 1998
Updated to use fourlib.
Mark Koennecke, December 2001
Introduced HM mode in order to cope with with the fact that TRICS has
three detectors.
Mark Koennecke, May 2002
Added handling of the chi ==0 or chi == 180 degree case to tryTweakOmega
Mark Koennecke, December 2003
-----------------------------------------------------------------------------*/
#include <math.h>
#include <ctype.h>
#include "sics.h"
#include "fortify.h"
#include "motor.h"
#include "selector.h"
#include "selvar.h"
#include "fourlib.h"
#include "matrix/matrix.h"
#include "hkl.h"
#include "hkl.i"
#include "splitter.h"
/*
the tolerance in chi we give before we allow to fix omega with phi
*/
#define CHITOLERANCE 3.
#define ABS(x) (x < 0 ? -(x) : (x))
/*-------------------------------------------------------------------------*/
static int HKLSave(void *pData, char *name, FILE *fd)
{
pHKL self = NULL;
self = (pHKL)pData;
if( (self == NULL) || (fd == NULL) )
{
return 1;
}
fprintf(fd,"#Crystallographic Settings\n");
if(self->iManual == 1)
{
fprintf(fd,"%s lambda %f\n",name, self->fLambda);
}
fprintf(fd,
"%s setub %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f\n",
name,
self->fUB[0], self->fUB[1], self->fUB[2], self->fUB[3], self->fUB[4],
self->fUB[5], self->fUB[6], self->fUB[7], self->fUB[8]);
fprintf(fd,"%s hm %d\n",name, self->iHM);
fprintf(fd,"%s scantolerance %f\n", name,self->scanTolerance);
fprintf(fd,"%s nb %d\n", name, self->iNOR);
return 1;
}
/*---------------------------------------------------------------------------*/
pHKL CreateHKL(pMotor pTheta, pMotor pOmega, pMotor pChi,
pMotor pPhi, pMotor pNu)
{
pHKL pNew = NULL;
assert(pTheta);
assert(pOmega);
assert(pChi);
assert(pPhi);
/* allocate memory */
pNew = (pHKL)malloc(sizeof(HKL));
if(!pNew)
{
return NULL;
}
memset(pNew,0,sizeof(HKL));
/* create object descriptor */
pNew->pDes = CreateDescriptor("4-Circle-Calculus");
if(!pNew->pDes)
{
free(pNew);
return NULL;
}
pNew->pDes->SaveStatus = HKLSave;
pNew->pTheta = pTheta;
pNew->pOmega = pOmega;
pNew->pChi = pChi;
pNew->pPhi = pPhi;
pNew->pNu = pNu;
pNew->fLambda = 1.38;
pNew->iManual = 1;
pNew->iQuad = 1;
pNew->iHM = 0;
pNew->fUB[0] = 1.;
pNew->fUB[4] = 1.;
pNew->fUB[8] = 1.;
pNew->UBinv = NULL;
pNew->scanTolerance = 2.5;
return pNew;
}
/*--------------------------------------------------------------------------*/
void DeleteHKL(void *pData)
{
pHKL self = NULL;
self = (pHKL)pData;
if(!self)
{
return;
}
if(self->pDes)
{
DeleteDescriptor(self->pDes);
}
free(self);
}
/*---------------------------------------------------------------------------*/
int HKLFactory(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pMotor pTheta = NULL, pOmega = NULL, pChi = NULL,
pPhi = NULL, pNu = NULL;
pHKL self = NULL;
int iRet;
/* check no of arguments */
if(argc < 5)
{
SCWrite(pCon,"ERROR: Insufficient number of arguments to HKLFactory",
eError);
return 0;
}
/* check motors */
pTheta = FindMotor(pSics,argv[1]);
if(!pTheta)
{
SCWrite(pCon,"ERROR: cannot find two theta motor",eError);
return 0;
}
pOmega = FindMotor(pSics,argv[2]);
if(!pOmega)
{
SCWrite(pCon,"ERROR: cannot find omega motor",eError);
return 0;
}
pPhi = FindMotor(pSics,argv[3]);
if(!pPhi)
{
SCWrite(pCon,"ERROR: cannot find phi motor",eError);
return 0;
}
pChi = FindMotor(pSics,argv[4]);
if(!pChi)
{
SCWrite(pCon,"ERROR: cannot find chi motor",eError);
return 0;
}
if(argc >= 6)
{
pNu = FindMotor(pSics,argv[5]);
if(!pNu)
{
SCWrite(pCon,"WARNING: cannot find nu motor",eWarning);
}
}
/* make a new structure */
self = CreateHKL(pTheta, pOmega, pPhi, pChi, pNu);
if(!self)
{
SCWrite(pCon,"ERROR: cannot allocate HKL data structure",eError);
return 0;
}
/* install a command */
iRet = AddCommand(pSics,
"HKL",
HKLAction,
DeleteHKL,
self);
if(!iRet)
{
SCWrite(pCon,"ERROR: duplicate command HKL not created",eError);
DeleteHKL(self);
return 0;
}
return 1;
}
#include "selvar.i"
/*---------------------------------------------------------------------------*/
static int HKLCallback(int iEvent, void *pEvent, void *pUser,
commandContext cc)
{
pHKL self = NULL;
pSelVar pVar = NULL;
float fVal;
if(iEvent == WLCHANGE)
{
self = (pHKL)pUser;
pVar = (pSelVar)pEvent;
assert(self);
assert(pVar);
if(pVar->pCon != NULL)
{
fVal = GetSelValue(pVar, pVar->pCon);
if(fVal > -900.)
{
self->fLambda = fVal;
return 1;
}
}
}
return 1;
}
/*--------------------------------------------------------------------------*/
int SetWavelengthVariable(SConnection *pCon, pHKL self, pSelVar pVar)
{
pICallBack pCall = NULL, pCall2 = NULL;
pDummy pDum = NULL;
float fVal;
commandContext comCon;
assert(pCon);
assert(self);
assert(pVar);
/* try to get callback interface of the new mono variable */
pDum = (pDummy)pVar;
pCall2 = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
if(!pCall2)
{
return 0;
}
/* clear old stuff, if apropriate */
if(self->pMono)
{
pDum = (pDummy)self->pMono;
pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
if(pCall)
{
RemoveCallback(pCall,self->lID);
self->lID = 0;
self->pMono = NULL;
self->iManual = 1;
}
}
/* install new callback */
comCon.transID = 0;
strncpy(comCon.deviceID,"internal",SCDEVIDLEN);
self->lID = RegisterCallback(pCall2,
comCon,
WLCHANGE,
HKLCallback,
self,
NULL);
self->pMono = pVar;
self->iManual = 0;
/* update the current value */
fVal = GetSelValue(pVar,pCon);
if(fVal > -900.)
{
self->fLambda = fVal;
}
else
{
return 0;
}
return 1;
}
/*-------------------------------------------------------------------------*/
void SetHKLScanTolerance(pHKL self, float fVal)
{
assert(self);
self->scanTolerance = fVal;
}
/*-------------------------------------------------------------------------*/
int SetWavelengthManual(pHKL self, float fVal)
{
pICallBack pCall = NULL;
pDummy pDum = NULL;
assert(self);
/* clear old stuff, if apropriate */
if(self->pMono)
{
pDum = (pDummy)self->pMono;
pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
if(pCall)
{
RemoveCallback(pCall,self->lID);
self->lID = 0;
self->pMono = NULL;
self->iManual = 1;
}
}
self->fLambda = fVal;
return 1;
}
/*------------------------------------------------------------------------*/
int GetLambda(pHKL self, float *fVal)
{
assert(self);
*fVal = self->fLambda;
return 1;
}
/*-------------------------------------------------------------------------*/
int GetCurrentHKL(pHKL self, float fHKL[3])
{
int i;
assert(self);
for(i = 0; i < 3; i++)
{
fHKL[i] = self->fLastHKL[i];
}
return 1;
}
/*-------------------------------------------------------------------------*/
int SetUB(pHKL self, float fUB[9])
{
int i;
MATRIX m;
assert(self);
for(i = 0; i < 9; i++)
{
self->fUB[i] = fUB[i];
}
/* invert UB matrix for use in backwards calculation */
if(self->UBinv != NULL)
{
mat_free(self->UBinv);
}
m = mat_creat(3,3,ZERO_MATRIX);
for(i = 0; i < 3; i++)
{
m[0][i] = self->fUB[i];
m[1][i] = self->fUB[3+i];
m[2][i] = self->fUB[6+i];
}
self->UBinv = mat_inv(m);
mat_free(m);
return 1;
}
/*-------------------------------------------------------------------------*/
int GetUB(pHKL self, float fUB[9])
{
int i;
assert(self);
for(i = 0; i < 9; i++)
{
fUB[i] = self->fUB[i];
}
return 1;
}
/*--------------------------------------------------------------------------*/
int SetNOR(pHKL self, int iNOB)
{
assert(self);
/* cannot set normal beam geometry if no nu motor */
if( (iNOB == 1) && (self->pNu == NULL))
return 0;
self->iNOR = iNOB;
return 1;
}
/*-----------------------------------------------------------------------*/
static int checkTheta(pHKL self, double *stt){
char pError[132];
int iTest;
float fHard;
iTest = MotorCheckBoundary(self->pTheta,(float)*stt, &fHard,pError,131);
if(!iTest)
{
/*
check if it is on the other detectors
*/
if(self->iHM){
iTest = MotorCheckBoundary(self->pTheta,(float)*stt-45.,
&fHard,pError,131);
if(iTest){
*stt -= 45.;
} else {
iTest = MotorCheckBoundary(self->pTheta,(float)*stt-90.,
&fHard,pError,131);
if(iTest) {
*stt -= 90.;
}
}
}
if(!iTest){
return -1;
}
}
return 1;
}
/*-----------------------------------------------------------------------*/
static int checkBisecting(pHKL self,
double *stt, double om, double chi, double phi)
{
int iTest;
float fHard, fLimit;
char pError[132];
/* check two theta */
iTest = checkTheta(self, stt);
if(!iTest){
return -1;
}
/* for omega check against the limits +- SCANBORDER in order to allow for
a omega scan
*/
MotorGetPar(self->pOmega,"softlowerlim",&fLimit);
if((float)om < fLimit + self->scanTolerance){
iTest = 0;
} else {
iTest = 1;
MotorGetPar(self->pOmega,"softupperlim",&fLimit);
if((float)om > fLimit - self->scanTolerance){
iTest = 0;
} else {
iTest = 1;
}
}
/* check chi and phi*/
iTest += MotorCheckBoundary(self->pChi,(float)chi, &fHard,pError,131);
iTest += MotorCheckBoundary(self->pPhi,(float)phi, &fHard,pError,131);
if(iTest == 3) /* none of them burns */
{
return 1;
}
return 0;
}
/*-----------------------------------------------------------------------*/
static int checkNormalBeam(double om, double *gamma, double nu,
float fSet[4], SConnection *pCon, pHKL self)
{
int iTest;
char pError[132];
float fHard;
fSet[0] = (float)*gamma;
fSet[1] = (float)om;
fSet[2] = (float)nu;
/* check omega, gamma and nu */
iTest = MotorCheckBoundary(self->pOmega,(float)om, &fHard,pError,131);
iTest += checkTheta(self,gamma);
iTest += MotorCheckBoundary(self->pNu,(float)nu, &fHard,pError,131);
if(iTest == 3) /* none of them burns */
{
return 1;
}
return 0;
}
/*--------------------------------------------------------------------*/
static int chiVertical(double chi){
if(ABS(chi - .0) < CHITOLERANCE){
return 1;
}
if(ABS(chi - 180.0) < CHITOLERANCE){
return 1;
}
return 0;
}
/*-----------------------------------------------------------------------
tryOmegaTweak tries to calculate a psi angle in order to put an
offending omega back into range.
This routine also handles the special case when chi ~ 0 or chi ~ 180.
Then it is possible to fix a omega problem by turing in phi.
-----------------------------------------------------------------------*/
static int tryOmegaTweak(pHKL self, MATRIX z1, double *stt, double *om,
double *chi, double *phi){
int status;
float fLower, fUpper, omTarget, omOffset, phiSign;
double dumstt, offom, offchi, offphi;
status = checkBisecting(self,stt,*om,*chi,*phi);
if(status < 0){
return 0; /* stt is burning */
} else if(status == 1){
return 1;
}
/*
Is omega really the problem?
*/
omTarget = -9999;
MotorGetPar(self->pOmega,"softlowerlim",&fLower);
MotorGetPar(self->pOmega,"softupperlim",&fUpper);
if(*om < fLower + self->scanTolerance) {
omTarget = fLower + self->scanTolerance;
}
if(*om > fUpper - self->scanTolerance){
omTarget = fUpper - self->scanTolerance;
}
if(omTarget < -7000){
return 0;
}
/*
calculate omega offset
*/
omOffset = *om - omTarget;
omOffset = -omOffset;
/*
check for the special case of chi == 0 or chi == 180
*/
if(chiVertical(*chi)){
dumstt = *stt;
offom = omTarget;
offchi = *chi;
MotorGetPar(self->pPhi,"sign",&phiSign);
offphi = *phi - omOffset*phiSign;
if(checkBisecting(self,&dumstt,offom,offchi,offphi)){
*om = offom;
*chi = offchi;
*phi = offphi;
return 1;
}
}
/*
calculate angles with omega offset
*/
status = z1ToAnglesWithOffset(self->fLambda,z1, omOffset, &dumstt,
&offom, &offchi, &offphi);
if(!status){
return 0;
}
if(checkBisecting(self,&dumstt,offom,offchi,offphi)){
*om = offom;
*chi = offchi;
*phi = offphi;
return 1;
}
return 0;
}
/*-----------------------------------------------------------------------*/
static MATRIX calculateScatteringVector(pHKL self, float fHKL[3])
{
MATRIX z1, hkl, ubm;
int i;
hkl = mat_creat(3,1,ZERO_MATRIX);
ubm = mat_creat(3,3,ZERO_MATRIX);
for(i = 0; i < 3; i++)
{
hkl[i][0] = fHKL[i];
ubm[0][i] = self->fUB[i];
ubm[1][i] = self->fUB[i+3];
ubm[2][i] = self->fUB[i+6];
}
z1 = mat_mul(ubm,hkl);
mat_free(ubm);
mat_free(hkl);
return z1;
}
/*---------------------------------------------------------------------*/
static int calculateBisectingOld(MATRIX z1, pHKL self, SConnection *pCon,
float fSet[4], double myPsi, int iRetry)
{
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi;
int i, test;
/*
just the plain angle calculation
*/
if(!z1mToBisecting(self->fLambda,z1,&stt,&om,&chi,&phi))
{
return 0;
}
/*
check if angles in limits. If omega problem: try to tweak
*/
chi = circlify(chi);
phi = circlify(phi);
if(iRetry > 1)
{
if(tryOmegaTweak(self,z1,&stt,&om,&chi,&phi)){
fSet[0] = (float)stt;
fSet[1] = (float)om;
fSet[2] = (float)circlify(chi);
fSet[3]= (float)circlify(phi);
return 1;
}
}
/*
if this does not work, try rotating through psi in order to
find a useful setting
*/
for(i = 0; i < iRetry; i++)
{
if(iRetry > 1)
{
psi = i*.5;
}
else
{
psi = myPsi;
}
rotatePsi(om,chi,phi,psi,&ompsi,&chipsi,&phipsi);
chipsi = circlify(chipsi);
phipsi = circlify(phipsi);
test = checkBisecting(self,&stt,ompsi,chipsi,phipsi);
if(test == 1)
{
fSet[0] = (float)stt;
fSet[1] = (float)ompsi;
fSet[2] = (float)chipsi;
fSet[3]= (float)phipsi;
return 1;
}
}
/*
giving up! But calculate one more time in order to show Jurg where
he should be.
*/
z1mToBisecting(self->fLambda,z1,&stt,&om,&chi,&phi);
if(iRetry == 1){
rotatePsi(om,chi,phi,psi,&ompsi,&chipsi,&phipsi);
fSet[0] = (float)stt;
fSet[1] = (float)ompsi;
fSet[2] = (float)chipsi;
fSet[3]= (float)phipsi;
} else {
fSet[0] = (float)stt;
fSet[1] = (float)om;
fSet[2] = (float)chi;
fSet[3]= (float)phi;
}
return 0;
}
/*----------------------------------------------------------------------*/
int hklInRange(void *data, float fSet[4], int mask[4])
{
pHKL self = (pHKL)data;
float fHard, fLimit;
char pError[132];
int i, test;
double dTheta;
/* check two theta */
dTheta = fSet[0];
mask[0] = checkTheta(self, &dTheta);
fSet[0] = dTheta;
/* for omega check against the limits +- SCANBORDER in order to allow for
a omega scan.
*/
MotorGetPar(self->pOmega,"softlowerlim",&fLimit);
if((float)fSet[1] < fLimit + self->scanTolerance){
mask[1] = 0;
} else {
mask[1] = 1;
MotorGetPar(self->pOmega,"softupperlim",&fLimit);
if((float)fSet[1] > fLimit - self->scanTolerance){
mask[1] = 0;
} else {
mask[1] = 1;
}
}
/* check chi and phi*/
mask[2] = MotorCheckBoundary(self->pChi,fSet[2], &fHard,pError,131);
mask[3] = MotorCheckBoundary(self->pPhi,fSet[3], &fHard,pError,131);
for(i = 0, test = 0; i < 4; i++){
test += mask[i];
}
if(test != 4) {
return 0;
} else {
return 1;
}
}
/*---------------------------------------------------------------------*/
static int calculateBisecting(MATRIX z1, pHKL self, SConnection *pCon,
float fSet[4], double myPsi, int iRetry)
{
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi;
int i, test, mask[4];
/*
just the plain angle calculation
*/
if(!z1mToBisecting(self->fLambda,z1,&stt,&om,&chi,&phi))
{
return 0;
}
fSet[0] = stt;
fSet[1] = om;
fSet[2] = chi;
fSet[3] = phi;
if(iRetry == 1) {
rotatePsi(om,chi,phi,myPsi,&ompsi,&chipsi,&phipsi);
fSet[1] = ompsi;
fSet[2] = circlify(chipsi);
fSet[3] = circlify(phipsi);
return 1;
} else {
if(hklInRange(self,fSet, mask) == 1){
return 1;
} else {
if(tryOmegaTweak(self,z1, &stt, &om, &chi, &phi) == 1){
fSet[0] = stt;
fSet[1] = om;
fSet[2] = chi;
fSet[3] = phi;
return 1;
} else {
return findAllowedBisecting(self->fLambda, z1, fSet, hklInRange,self);
}
}
}
}
/*-----------------------------------------------------------------------*/
static int calculateNormalBeam(MATRIX z1, pHKL self, SConnection *pCon,
float fSet[4], double myPsi, int iRetry)
{
int i, iTest, status;
double stt, om, chi, phi, gamma, nu, psi, omnb;
float currentPhi, currentChi;
double ompsi, chipsi, phipsi;
MATRIX chim, phim, z4, z3;
/*
The usual condition for normal beam calculations is that both chi
and phi are 0. This is not the case at TRICS. Therefore we have to
multiply the scattering vector first with the chi and phi rotations
before we start.
*/
iTest = MotorGetSoftPosition(self->pChi,pCon,&currentChi);
if(iTest != 1)
{
return 0;
}
iTest = MotorGetSoftPosition(self->pPhi,pCon,&currentPhi);
if(iTest != 1)
{
return 0;
}
phim = mat_creat(3,3,ZERO_MATRIX);
phimat(phim,(double)currentPhi);
z4 = mat_mul(phim,z1);
chim = mat_creat(3,3,ZERO_MATRIX);
chimat(chim,(double)currentChi);
z3 = mat_mul(chim,z4);
mat_free(phim);
mat_free(chim);
mat_free(z4);
status = z1mToNormalBeam(self->fLambda, z3, &gamma, &omnb, &nu);
/* omnb += 180.; */
mat_free(z3);
if(status != 1)
{
return 0;
}
if(checkNormalBeam(omnb, &gamma, nu,fSet,pCon,self)){
return 1;
} else {
if(checkNormalBeam(omnb + 360., &gamma, nu, fSet,pCon,self)){
return 1;
} else {
return 0;
}
}
}
/*---------------------------------------------------------------------*/
static int calculateNormalBeamOmega(MATRIX z1, pHKL self,
SConnection *pCon,
float fSet[4], double omOffset)
{
int iTest;
double stt, om, chi, phi, gamma, nu, psi;
float currentPhi, currentChi;
double ompsi, chipsi, phipsi;
MATRIX chim, phim, z4, z3;
/*
The usual condition for normal beam calculations is that both chi
and phi are 0. This is not the case at TRICS. Therefore we have to
multiply the scattering vector first with the chi and phi rotations
before we start.
*/
iTest = MotorGetSoftPosition(self->pChi,pCon,&currentChi);
if(iTest != 1)
{
return 0;
}
iTest = MotorGetSoftPosition(self->pPhi,pCon,&currentPhi);
if(iTest != 1)
{
return 0;
}
phim = mat_creat(3,3,ZERO_MATRIX);
phimat(phim,(double)currentPhi);
z4 = mat_mul(phim,z1);
chim = mat_creat(3,3,ZERO_MATRIX);
chimat(chim,(double)currentChi);
z3 = mat_mul(chim,z4);
mat_free(phim);
mat_free(chim);
mat_free(z4);
/*
do the bisecting angles first
*/
if(!z1ToAnglesWithOffset(self->fLambda,z3, omOffset, &stt,
&ompsi, &chi, &phi))
{
return 0;
}
if(ABS(chi -90.) < .001 && ABS(phi-180.) < .001)
{
chi = .0;
phi = .0;
}
if(bisToNormalBeam(stt,ompsi,chi,phi,
&om, &gamma, &nu))
{
if(checkNormalBeam(om, &gamma, nu,fSet,pCon,self))
{
return 1;
}
}
return 0;
}
/*-------------------------------------------------------------------------
calculates the four circle settings. If the position can not be reached
because of a limit violation, then psi is rotated in 10 degree steps
until either the loop ends or we finally succeed. If there is a omega
violation we first try to calculate a delta omega which puts omega
into the right range. This is a fix because the omega movement is quite
often restricted due to the cryogenic garbage around the sample.
*/
int CalculateSettings(pHKL self, float fHKL[3], float fPsi, int iHamil,
float fSet[4], SConnection *pCon)
{
char pError[132];
char pBueffel[512];
float fHard, fVal, fUpper, fLower;
double myPsi = fPsi;
MATRIX z1;
double stt, om, chi, phi, ompsi, chipsi, phipsi;
int i,iRetry, status;
/* catch shitty input */
if( (fHKL[0] == 0.) && (fHKL[1] == 0.) && (fHKL[2] == 0.))
{
SCWrite(pCon,"ERROR: I will not calculate angles for HKL = (0,0,0) ",
eError);
return 0;
}
/* some people are stupid.......... */
myPsi = circlify(fPsi);
/*
no retries if specific psi requested.
*/
if((myPsi > 0.1) )
{
iRetry = 1;
}
else
{
iRetry = 699;
}
z1 = calculateScatteringVector(self,fHKL);
if(self->iNOR == 0)
{
status = calculateBisecting(z1,self,pCon,fSet, myPsi, iRetry);
}
else if(self->iNOR == 1)
{
status = calculateNormalBeam(z1,self,pCon,fSet, myPsi, iRetry);
}
else
{
myPsi = fPsi;
status = calculateNormalBeamOmega(z1,self,pCon,fSet, myPsi);
}
if(!status)
{
if(iRetry == 1)
{
sprintf(pBueffel,
"ERROR: cannot calculate %4.1f %4.1f %4.1f, psi = %4.1f",
fHKL[0], fHKL[1], fHKL[2], fPsi);
SCWrite(pCon,pBueffel,eError);
}
else
{
sprintf(pBueffel,"ERROR: cannot calculate %4.1f %4.1f %4.1f",
fHKL[0], fHKL[1], fHKL[2]);
SCWrite(pCon,pBueffel,eError);
}
}
mat_free(z1);
return status;
return 0;
}
/*------------------------------------------------------------------------*/
static void stopHKLMotors(pHKL self)
{
if(self->pTheta != NULL)
{
self->pTheta->pDrivInt->Halt(self->pTheta);
}
if(self->pOmega != NULL)
{
self->pOmega->pDrivInt->Halt(self->pOmega);
}
if(self->pChi != NULL)
{
self->pChi->pDrivInt->Halt(self->pChi);
}
if(self->pPhi != NULL)
{
self->pPhi->pDrivInt->Halt(self->pPhi);
}
if(self->pNu != NULL)
{
self->pNu->pDrivInt->Halt(self->pNu);
}
}
/*------------------------------------------------------------------------*/
int startHKLMotors(pHKL self, SConnection *pCon, float fSet[4])
{
char pBueffel[512];
pDummy pDum;
int iRet;
/* start all the motors */
pDum = (pDummy)self->pTheta;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[0]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
stopHKLMotors(self);
return 0;
}
pDum = (pDummy)self->pOmega;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[1]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start omega motor",eError);
stopHKLMotors(self);
return 0;
}
/* special case: normal beam */
if(self->iNOR)
{
pDum = (pDummy)self->pNu;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[2]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start nu motor",eError);
return 0;
}
return 1;
}
pDum = (pDummy)self->pChi;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[2]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start chi motor",eError);
stopHKLMotors(self);
return 0;
}
pDum = (pDummy)self->pPhi;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[3]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start phi",eError);
stopHKLMotors(self);
return 0;
}
return 1;
}
/*-------------------------------------------------------------------------*/
int RunHKL(pHKL self, float fHKL[3],
float fPsi, int iHamil, SConnection *pCon)
{
float fSet[4];
int iRet,i;
char pBueffel[512];
pDummy pDum;
assert(self);
iRet = CalculateSettings(self,fHKL,fPsi,iHamil,fSet,pCon);
if(!iRet)
{
SCWrite(pCon,"ERROR: NOT started",eError);
return 0;
}
iRet = startHKLMotors(self,pCon,fSet);
if(iRet != 1){
return iRet;
}
for(i = 0; i < 3; i++)
{
self->fLastHKL[i] = fHKL[i];
}
return 1;
}
/*-------------------------------------------------------------------------*/
int DriveSettings(pHKL self, float fSet[4], SConnection *pCon)
{
int iRet,i, iReturn;
char pBueffel[512];
pDummy pDum;
iReturn = 1;
/* start all the motors */
pDum = (pDummy)self->pTheta;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[0]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
StopExe(pServ->pExecutor,"all");
iReturn = 0;
goto ente;
}
pDum = (pDummy)self->pOmega;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[1]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start omega motor",eError);
StopExe(pServ->pExecutor,"all");
iReturn = 0;
goto ente;
}
/* special case: normal beam */
if(self->iNOR)
{
pDum = (pDummy)self->pNu;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[2]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start nu motor",eError);
StopExe(pServ->pExecutor,"all");
iReturn = 0;
}
goto ente;
}
pDum = (pDummy)self->pChi;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[2]);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot start chi motor",eError);
StopExe(pServ->pExecutor,"all");
iReturn = 0;
goto ente;
}
pDum = (pDummy)self->pPhi;
iRet = StartDevice(pServ->pExecutor, "HKL",
pDum->pDescriptor, pDum, pCon,fSet[3]);
if(!iRet)
{
StopExe(pServ->pExecutor,"all");
iReturn = 0;
SCWrite(pCon,"ERROR: cannot start phi",eError);
}
ente:
/* wait for end */
iRet = Wait4Success(pServ->pExecutor);
switch(iRet)
{
case DEVINT:
if(SCGetInterrupt(pCon) == eAbortOperation)
{
SCSetInterrupt(pCon,eContinue);
SCWrite(pCon,"Driving to HKL Aborted",eStatus);
}
return 0;
break;
case DEVDONE:
SCWrite(pCon,"Driving to Reflection done",eStatus);
break;
default:
SCWrite(pCon,"WARNING: driving to HKL finished with problems",
eWarning);
if(SCGetInterrupt(pCon) != eContinue)
{
return 0;
}
break;
}
return iReturn;
}
/*--------------------------------------------------------------------------*/
int DriveHKL(pHKL self, float fHKL[3],
float fPsi, int iHamil, SConnection *pCon)
{
int iRet, iReturn;
long lID;
char pBueffel[132];
assert(self);
/* start running */
iReturn = 1;
iRet = RunHKL(self,fHKL,fPsi,iHamil,pCon);
if(!iRet)
{
StopExe(pServ->pExecutor,"all");
iReturn = 0;
}
/* wait for end */
iRet = Wait4Success(pServ->pExecutor);
switch(iRet)
{
case DEVINT:
if(SCGetInterrupt(pCon) == eAbortOperation)
{
SCSetInterrupt(pCon,eContinue);
SCWrite(pCon,"Driving to HKL Aborted",eStatus);
}
return 0;
break;
case DEVDONE:
if(fPsi > .01)
{
snprintf(pBueffel,131,
"Driving to %8.4f %8.4f %8.4f, psi = %8.4f done",
fHKL[0], fHKL[1], fHKL[2],fPsi);
}
else
{
snprintf(pBueffel,131,"Driving to %8.4f %8.4f %8.4f done",
fHKL[0], fHKL[1], fHKL[2]);
}
SCWrite(pCon,pBueffel,eStatus);
break;
default:
SCWrite(pCon,"WARNING: driving to HKL finished with problems",
eWarning);
if(SCGetInterrupt(pCon) != eContinue)
{
return 0;
}
break;
}
return iReturn;
}
/*-----------------------------------------------------------------------*/
int GetCurrentPosition(pHKL self, SConnection *pCon, float fPos[4])
{
float fVal;
int iRet, iResult;
assert(self);
assert(pCon);
iResult = 1;
iRet = MotorGetSoftPosition(self->pTheta,pCon, &fVal);
if(iRet == 1)
{
fPos[0] = fVal;
}
else
{
iResult = 0;
}
iRet = MotorGetSoftPosition(self->pOmega,pCon, &fVal);
if(iRet == 1)
{
fPos[1] = fVal;
}
else
{
iResult = 0;
}
/* normal beam geometry */
if(self->iNOR == 1)
{
iRet = MotorGetSoftPosition(self->pNu,pCon, &fVal);
if(iRet == 1)
{
fPos[2] = fVal;
}
else
{
iResult = 0;
}
return iResult;
}
/* bissecting geometry */
iRet = MotorGetSoftPosition(self->pChi,pCon, &fVal);
if(iRet == 1)
{
fPos[2] = fVal;
}
else
{
iResult = 0;
}
iRet = MotorGetSoftPosition(self->pPhi,pCon, &fVal);
if(iRet == 1)
{
fPos[3] = fVal;
}
else
{
iResult = 0;
}
return iResult;
}
/*-------------------------------------------------------------------------
For the conversion from angles to HKL.
-------------------------------------------------------------------------*/
static int angle2HKL(pHKL self ,double tth, double om,
double chi, double phi, float fHKL[3])
{
MATRIX rez, z1m;
double z1[3];
int i;
if(self->UBinv == NULL){
return 0;
}
z1FromAngles(self->fLambda,tth,om,chi,phi,z1);
z1m = vectorToMatrix(z1);
/* multiply with UBinv in order to yield HKL */
rez = mat_mul(self->UBinv,z1m);
for(i = 0; i < 3; i++)
{
fHKL[i] = (float)rez[i][0];
}
mat_free(z1m);
mat_free(rez);
return 1;
}
/*---------------------------------------------------------------------------*/
int GetHKLFromAngles(pHKL self, SConnection *pCon, float fHKL[3])
{
int status;
float fAng[4];
status = GetCurrentPosition(self,pCon,fAng);
if(status == 1)
{
angle2HKL(self,fAng[0], fAng[1], fAng[2], fAng[3],fHKL);
return 1;
}
return 0;
}
/*--------------------------------------------------------------------------*/
static int GetCommandData(int argc, char *argv[], float fHKL[3],
float *fPsi, int *iHamil, SConnection *pCon)
{
int iRet, i;
double d;
char pBueffel[512];
if(argc < 3)
{
SCWrite(pCon,
"ERROR: Insufficient reflection data specified for calculation",
eError);
return 0;
}
/* get the reflection */
for(i = 0; i < 3; i++)
{
if(!isNumeric(argv[i]))
{
sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[i]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
fHKL[i] = atof(argv[i]);
}
*fPsi = 0.;
*iHamil = 0;
/* has psi been specicifed ? */
if(argc > 3)
{
if(!isNumeric(argv[3]))
{
sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[3]);
SCWrite(pCon,pBueffel,eError);
}
*fPsi = atof(argv[3]);
}
/* has iHamil been specified ? */
if(argc > 4)
{
if(!isNumeric(argv[4]))
{
sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[4]);
SCWrite(pCon,pBueffel,eError);
}
*iHamil = atof(argv[4]);
}
return 1;
}
/*--------------------------------------------------------------------------*/
int HKLAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
int iRet,i, iHamil;
char pBueffel[512];
float fUB[9], fPsi, fVal;
float fHKL[3], fSet[4];
double dVal;
pHKL self = NULL;
CommandList *pCom = NULL;
pDummy pDum = NULL;
assert(pCon);
assert(pSics);
self = (pHKL)pData;
assert(self);
/* enough arguments ? */
if(argc < 2)
{
SCWrite(pCon,"Insufficient number of arguments to HKL",eError);
return 0;
}
/*-------- handle list */
strtolower(argv[1]);
if(strcmp(argv[1],"list") == 0 )
{
sprintf(pBueffel,
"lambda = %f Normal Beam = %d Quadrant = %d HM = %d",
self->fLambda, self->iNOR,
self->iQuad,self->iHM);
SCWrite(pCon,pBueffel,eValue);
sprintf(pBueffel,"UB = { %f %f %f",
self->fUB[0], self->fUB[1],self->fUB[2]);
SCWrite(pCon,pBueffel,eValue);
sprintf(pBueffel," %f %f %f",
self->fUB[3], self->fUB[4],self->fUB[5]);
SCWrite(pCon,pBueffel,eValue);
sprintf(pBueffel," %f %f %f }",
self->fUB[6], self->fUB[7],self->fUB[8]);
SCWrite(pCon,pBueffel,eValue);
return 1;
sprintf(pBueffel,"Last HKL: %f %f %f ",
self->fLastHKL[0], self->fLastHKL[1],self->fLastHKL[2]);
SCWrite(pCon,pBueffel,eValue);
snprintf(pBueffel,510,"%s.scantolerance = %f", argv[0],
self->scanTolerance);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
/*----------- current */
else if(strcmp(argv[1],"current") == 0)
{
if(self->iNOR)
{
sprintf(pBueffel,"Last HKL: %8.4f %8.4f %8.4f ",
self->fLastHKL[0], self->fLastHKL[1],self->fLastHKL[2]);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
else
{
/* do a serious calculation based on angles */
iRet = GetCurrentPosition(self,pCon,fSet);
if(iRet == 0)
{
return 0;
}
angle2HKL(self,(double)fSet[0],(double)fSet[1],
(double)fSet[2],(double)fSet[3],fHKL);
sprintf(pBueffel,"Current HKL: %8.4f %8.4f %8.4f ",
fHKL[0], fHKL[1],fHKL[2]);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
}
else if(strcmp(argv[1],"fromangles") == 0)
{
if(argc < 6)
{
SCWrite(pCon,
"ERROR: need stt, om, chi,phi to calculate HKL from angles",
eError);
return 0;
}
for(i = 0; i < 4; i++)
{
iRet = Tcl_GetDouble(InterpGetTcl(pSics),argv[i+2],&dVal);
if(iRet != TCL_OK)
{
snprintf(pBueffel,511,"ERROR: failed to convert %s to number",
argv[i+2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
fSet[i] = (float)dVal;
}
angle2HKL(self,(double)fSet[0],(double)fSet[1],
(double)fSet[2],(double)fSet[3],fHKL);
sprintf(pBueffel,"HKL from angles: %8.4f %8.4f %8.4f ",
fHKL[0], fHKL[1],fHKL[2]);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
/*------------- lambda */
else if(strcmp(argv[1],"lambda") == 0)
{
if(argc < 3)
{
snprintf(pBueffel,132,"%s.lambda = %f", argv[0],self->fLambda);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
if(!isNumeric(argv[2]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
fVal = atof(argv[2]);
iRet = SetWavelengthManual(self,fVal);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot set wavelength",eError);
return 0;
}
SCSendOK(pCon);
return 1;
}
/*------------- getub*/
else if(strcmp(argv[1],"getub") == 0)
{
snprintf(pBueffel,510,"%s.ub = %f %f %f %f %f %f %f %f %f",
argv[0], self->fUB[0], self->fUB[1], self->fUB[2],
self->fUB[3], self->fUB[4], self->fUB[5],
self->fUB[6], self->fUB[7], self->fUB[8]);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
/*------- lambdavar*/
else if(strcmp(argv[1],"lambdavar") == 0)
{
if(argc < 3)
{
SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL lambdavar",eError);
return 0;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
pCom = FindCommand(pSics,argv[2]);
if(!pCom)
{
sprintf(pBueffel,"ERROR: cannot find variable --> %s <--",argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
pDum = (pDummy)pCom->pData;
if(!pDum)
{
sprintf(pBueffel,"ERROR: cannot find variable --> %s <--",argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
if(strcmp(pDum->pDescriptor->name,"SicsSelVar") != 0)
{
sprintf(pBueffel,"ERROR: variable --> %s <-- has nothing to do with wavelength",argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
iRet = SetWavelengthVariable(pCon,self,(pSelVar)pDum);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot set wavelength variable",eError);
return 0;
}
SCSendOK(pCon);
return 1;
}
/*------------ UB */
else if(strcmp(argv[1],"setub") == 0)
{
if(argc < 11)
{
SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL setUB",eError);
return 0;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
for(i = 2; i < 11; i++)
{
if(!isNumeric(argv[i]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[i]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
fUB[i-2] = atof(argv[i]);
}
iRet = SetUB(self,fUB);
if(!iRet)
{
SCWrite(pCon,"ERROR: cannot set UB Matrix",eError);
return 0;
}
SCSendOK(pCon);
return 1;
}
/*------------- HM mode */
else if(strcmp(argv[1],"hm") == 0)
{
if(argc < 3)
{
sprintf(pBueffel,"%s.hm = %d", argv[0],self->iHM);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
if(!isNumeric(argv[2]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
self->iHM = atoi(argv[2]);
SCSendOK(pCon);
return 1;
}
/*------------- normal beam */
else if(strcmp(argv[1],"nb") == 0)
{
if(argc < 3)
{
snprintf(pBueffel,511,"%s.nb = %d",argv[0],self->iNOR);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
if(!isNumeric(argv[2]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
iRet = SetNOR(self,atoi(argv[2]));
if(!iRet)
{
SCWrite(pCon,
"ERROR: cannot set Normal Beam geometry, probably nu motor undefined",
eError);
return 0;
}
SCSendOK(pCon);
return 1;
}
/*------------- quadrant */
else if(strcmp(argv[1],"quadrant") == 0)
{
if(argc < 3)
{
SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL quadrant",eError);
return 0;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
if(!isNumeric(argv[2]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
iRet = atoi(argv[2]);
if(!( (iRet == 1) || (iRet == 0)) )
{
sprintf(pBueffel,"ERROR: Invalid parameter %d for quadrant, posiible: 0,1",
iRet);
SCWrite(pCon,pBueffel,eError);
return 0;
}
self->iQuad = iRet;
SCSendOK(pCon);
return 1;
}
/*------------- scantolerance */
else if(strcmp(argv[1],"scantolerance") == 0)
{
if(argc < 3)
{
snprintf(pBueffel,510,"%s.scantolerance = %f",argv[0],
self->scanTolerance);
SCWrite(pCon,pBueffel,eValue);
return 1;
}
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
if(!isNumeric(argv[2]))
{
sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
self->scanTolerance = atof(argv[2]);
SCSendOK(pCon);
return 1;
}
/*------------- calculate */
else if(strcmp(argv[1],"calc") == 0)
{
iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
if(!iRet)
{
return 0;
}
iRet = CalculateSettings(self,fHKL, fPsi, iHamil, fSet,pCon);
if(self->iNOR)
{
sprintf(pBueffel," gamma = %f, omega = %f, nu = %f",
fSet[0], fSet[1], fSet[2]);
SCWrite(pCon,pBueffel,eValue);
}
else
{
sprintf(pBueffel," 2-theta = %f, omega = %f, chi = %f, phi = %f",
fSet[0], fSet[1], fSet[2],fSet[3]);
SCWrite(pCon,pBueffel,eValue);
}
if(!iRet)
{
SCWrite(pCon,
"WARNING: Cannot drive to the hkl of your desire",
eWarning);
return 0;
}
return 1;
}
/*------------------ run */
else if(strcmp(argv[1],"run") == 0)
{
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
if(!iRet)
{
return 0;
}
iRet = RunHKL(self,fHKL,fPsi, iHamil, pCon);
if(!iRet)
{
return 0;
}
else
{
SCSendOK(pCon);
return 1;
}
}
/*------------------ drive */
else if(strcmp(argv[1],"drive") == 0)
{
if(!SCMatchRights(pCon,usUser))
{
return 0;
}
iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
if(!iRet)
{
return 0;
}
iRet = DriveHKL(self,fHKL,fPsi, iHamil, pCon);
if(!iRet)
{
return 0;
}
else
{
SCSendOK(pCon);
return 1;
}
}
else
{
sprintf(pBueffel,"ERROR: subcommand --> %s <-- not recognized",
argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
return 0; /* not reached */
}