
This is our new RELEASE-4_0 branch which was taken from ansto/93d9a7c Conflicts: .gitignore SICSmain.c asynnet.c confvirtualmot.c counter.c devexec.c drive.c event.h exebuf.c exeman.c histmem.c interface.h motor.c motorlist.c motorsec.c multicounter.c napi.c napi.h napi4.c network.c nwatch.c nxscript.c nxxml.c nxxml.h ofac.c reflist.c scan.c sicshipadaba.c sicsobj.c site_ansto/docs/Copyright.txt site_ansto/instrument/lyrebird/config/tasmad/sicscommon/nxsupport.tcl site_ansto/instrument/lyrebird/config/tasmad/taspub_sics/tasscript.tcl statusfile.c tasdrive.c tasub.c tasub.h tasublib.c tasublib.h
574 lines
14 KiB
C
574 lines
14 KiB
C
/**
|
|
* This is an implementation of the polymorphic single crystal calculation
|
|
* system defined in singlediff.h for a bisecting four circle diffractometer with
|
|
* an eulerian cradle.
|
|
*
|
|
* copyright: see file COPYRIGHT
|
|
*
|
|
* Mark Koennecke, August 2008
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <sics.h>
|
|
#include "singlediff.h"
|
|
#include "fourlib.h"
|
|
#include "ubfour.h"
|
|
#include "motor.h"
|
|
#include "singlex.h"
|
|
#include "motorlist.h"
|
|
#include "lld.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 chiVertical(double chi)
|
|
{
|
|
if (ABS(chi - .0) < CHITOLERANCE) {
|
|
return 1;
|
|
}
|
|
if (ABS(chi - 180.0) < CHITOLERANCE) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int checkTheta(pSingleDiff self, double *stt)
|
|
{
|
|
char pError[132];
|
|
int iTest;
|
|
float fHard;
|
|
pMotor pTheta;
|
|
|
|
pTheta = SXGetMotor(TwoTheta);
|
|
if (pTheta == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
iTest = MotorCheckBoundary(pTheta, (float) *stt, &fHard, pError, 131);
|
|
if (!iTest) {
|
|
return -1;
|
|
}
|
|
return iTest;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int checkBisecting(pSingleDiff self,
|
|
double *stt, double om, double chi, double phi)
|
|
{
|
|
int iTest;
|
|
float fHard, fLimit;
|
|
char pError[132];
|
|
pMotor pMot;
|
|
|
|
/* check two theta */
|
|
iTest = checkTheta(self, stt);
|
|
if (iTest != 1) {
|
|
return -1;
|
|
}
|
|
|
|
/* for omega check against the limits +- SCANBORDER in order to allow for
|
|
a omega scan
|
|
*/
|
|
pMot = SXGetMotor(Omega);
|
|
if (pMot == NULL) {
|
|
return 0;
|
|
}
|
|
MotorGetPar(pMot, "softlowerlim", &fLimit);
|
|
if ((float) om < fLimit) {
|
|
iTest = 0;
|
|
} else {
|
|
iTest = 1;
|
|
MotorGetPar(pMot, "softupperlim", &fLimit);
|
|
if ((float) om > fLimit) {
|
|
iTest = 0;
|
|
} else {
|
|
iTest = 1;
|
|
}
|
|
}
|
|
|
|
/* check chi and phi */
|
|
pMot = SXGetMotor(Chi);
|
|
if (pMot == NULL) {
|
|
return 0;
|
|
}
|
|
iTest += MotorCheckBoundary(pMot, (float) chi, &fHard, pError, 131);
|
|
pMot = SXGetMotor(Phi);
|
|
if (pMot == NULL) {
|
|
return 0;
|
|
}
|
|
iTest += MotorCheckBoundary(pMot, (float) phi, &fHard, pError, 131);
|
|
if (iTest == 3) { /* none of them burns */
|
|
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(pSingleDiff self, MATRIX z1, double *stt,
|
|
double *om, double *chi, double *phi)
|
|
{
|
|
int status;
|
|
float fLower, fUpper, omTarget, omOffset, phiSign;
|
|
double dumstt, offom, offchi, offphi, lambda;
|
|
pMotor pOmega, pPhi;
|
|
|
|
pOmega = SXGetMotor(Omega);
|
|
pPhi = SXGetMotor(Phi);
|
|
lambda = self->lambda;
|
|
if (pOmega == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
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(pOmega, "softlowerlim", &fLower);
|
|
MotorGetPar(pOmega, "softupperlim", &fUpper);
|
|
if (*om < fLower) {
|
|
omTarget = fLower;
|
|
}
|
|
if (*om > fUpper) {
|
|
omTarget = fUpper;
|
|
}
|
|
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(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(lambda, 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 int hklInRange(void *data, double fSet[4], int mask[4])
|
|
{
|
|
pSingleDiff self = (pSingleDiff) data;
|
|
float fHard, fLimit;
|
|
char pError[132];
|
|
int i, test;
|
|
double dTheta;
|
|
pMotor pOmega, pChi, pPhi;
|
|
|
|
pOmega = SXGetMotor(Omega);
|
|
pChi = SXGetMotor(Chi);
|
|
pPhi = SXGetMotor(Phi);
|
|
if (pOmega == NULL || pChi == NULL || pPhi == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* check two theta */
|
|
dTheta = fSet[0];
|
|
mask[0] = checkTheta(self, &dTheta);
|
|
fSet[0] = dTheta;
|
|
|
|
mask[1] = MotorCheckBoundary(pOmega, fSet[1], &fHard, pError, 131);
|
|
mask[2] = MotorCheckBoundary(pChi, fSet[2], &fHard, pError, 131);
|
|
mask[3] = MotorCheckBoundary(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 hklInRangeOrion(void *data, double fSet[4], int mask[4])
|
|
{
|
|
pSingleDiff self = (pSingleDiff) data;
|
|
float fHard, fLimit;
|
|
char pError[132];
|
|
int i, test;
|
|
double dTheta;
|
|
pMotor pOmega, pChi, pPhi;
|
|
|
|
pOmega = SXGetMotor(Omega);
|
|
pChi = SXGetMotor(Chi);
|
|
pPhi = SXGetMotor(Phi);
|
|
if (pOmega == NULL || pChi == NULL || pPhi == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* check two theta */
|
|
dTheta = fSet[0];
|
|
mask[0] = checkTheta(self, &dTheta);
|
|
fSet[0] = dTheta;
|
|
|
|
/* Orion is not upside down... */
|
|
fSet[2] = -1.*(fSet[2]+180.) + 360. ;
|
|
fSet[3] = circlify(fSet[3]+180.);
|
|
|
|
mask[1] = MotorCheckBoundary(pOmega, fSet[1], &fHard, pError, 131);
|
|
mask[2] = MotorCheckBoundary(pChi, fSet[2], &fHard, pError, 131);
|
|
mask[3] = MotorCheckBoundary(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 calculateBiSettings(pSingleDiff self,
|
|
double *hkl, double *settings)
|
|
{
|
|
MATRIX z1;
|
|
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi, myPsi;
|
|
int i, test, mask[4], iRetry;
|
|
double lambda;
|
|
|
|
myPsi = hkl[3];
|
|
if (myPsi > 0.1) {
|
|
iRetry = 1;
|
|
} else {
|
|
iRetry = 699;
|
|
}
|
|
|
|
z1 = calculateScatteringVector(self, hkl);
|
|
|
|
/*
|
|
just the plain angle calculation
|
|
*/
|
|
if (!z1mToBisecting(self->lambda, z1, &stt, &om, &chi, &phi)) {
|
|
return 0;
|
|
}
|
|
|
|
settings[0] = stt;
|
|
settings[1] = om;
|
|
settings[2] = chi;
|
|
settings[3] = phi;
|
|
if (iRetry == 1) {
|
|
rotatePsi(om, chi, phi, myPsi, &ompsi, &chipsi, &phipsi);
|
|
settings[1] = ompsi;
|
|
settings[2] = circlify(chipsi);
|
|
settings[3] = circlify(phipsi);
|
|
return 1;
|
|
} else {
|
|
if (hklInRange(self, settings, mask) == 1) {
|
|
return 1;
|
|
} else {
|
|
if (tryOmegaTweak(self, z1, &stt, &om, &chi, &phi) == 1) {
|
|
settings[0] = stt;
|
|
settings[1] = om;
|
|
settings[2] = chi;
|
|
settings[3] = phi;
|
|
return 1;
|
|
} else {
|
|
return findAllowedBisecting(self->lambda, z1, settings, hklInRange,
|
|
self);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
static int calculateBiSettingsOrion(pSingleDiff self,
|
|
double *hkl, double *settings)
|
|
{
|
|
MATRIX z1;
|
|
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi, myPsi;
|
|
int i, test, mask[4], iRetry;
|
|
double lambda;
|
|
|
|
myPsi = hkl[3];
|
|
if (myPsi > 0.1) {
|
|
iRetry = 1;
|
|
} else {
|
|
iRetry = 699;
|
|
}
|
|
|
|
z1 = calculateScatteringVector(self, hkl);
|
|
|
|
/*
|
|
just the plain angle calculation
|
|
*/
|
|
if (!z1mToBisecting(self->lambda, z1, &stt, &om, &chi, &phi)) {
|
|
return 0;
|
|
}
|
|
|
|
settings[0] = stt;
|
|
settings[1] = om;
|
|
settings[2] = -1.*(180.+chi) + 360.;
|
|
settings[3] = phi+180.;
|
|
if (iRetry == 1) {
|
|
rotatePsi(om, chi, phi, myPsi, &ompsi, &chipsi, &phipsi);
|
|
settings[1] = ompsi;
|
|
settings[2] = circlify(chipsi);
|
|
settings[3] = circlify(phipsi);
|
|
return 1;
|
|
} else {
|
|
if (hklInRange(self, settings, mask) == 1) {
|
|
return 1;
|
|
} else {
|
|
if (tryOmegaTweak(self, z1, &stt, &om, &chi, &phi) == 1) {
|
|
settings[0] = stt;
|
|
settings[1] = om;
|
|
settings[2] = chi;
|
|
settings[3] = phi;
|
|
return 1;
|
|
} else {
|
|
return findAllowedBisecting(self->lambda, z1, settings, hklInRangeOrion,
|
|
self);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
static int settingsToBiList(struct __SingleDiff *self, double *settings)
|
|
{
|
|
|
|
setNewMotorTarget(self->motList, (char *) SXGetMotorName(TwoTheta),
|
|
(float) settings[0]);
|
|
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Omega),
|
|
(float) settings[1]);
|
|
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Chi),
|
|
(float) settings[2]);
|
|
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Phi),
|
|
(float) settings[3]);
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
static int hklFromBiAngles(struct __SingleDiff *self, double *hkl)
|
|
{
|
|
pIDrivable pDriv;
|
|
MATRIX UBinv, z1m, rez;
|
|
double z1[3], stt, om, chi, phi;
|
|
int i;
|
|
|
|
pDriv = makeMotListInterface();
|
|
pDriv->GetValue(&self->motList, pServ->dummyCon);
|
|
|
|
UBinv = mat_inv(self->UB);
|
|
if (UBinv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
stt = getListMotorPosition(self->motList,
|
|
(char *) SXGetMotorName(TwoTheta));
|
|
om = getListMotorPosition(self->motList, (char *) SXGetMotorName(Omega));
|
|
chi = getListMotorPosition(self->motList, (char *) SXGetMotorName(Chi));
|
|
phi = getListMotorPosition(self->motList, (char *) SXGetMotorName(Phi));
|
|
z1FromAngles(self->lambda, stt, om, chi, phi, z1);
|
|
z1m = vectorToMatrix(z1);
|
|
rez = mat_mul(UBinv, z1m);
|
|
for (i = 0; i < 3; i++) {
|
|
hkl[i] = rez[i][0];
|
|
}
|
|
|
|
mat_free(UBinv);
|
|
mat_free(z1m);
|
|
mat_free(rez);
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
static int hklFromBiAnglesGiven(struct __SingleDiff *self,
|
|
double *settings, double *hkl)
|
|
{
|
|
MATRIX UBinv, z1m, rez;
|
|
double z1[3], stt, om, chi, phi;
|
|
int i;
|
|
|
|
UBinv = mat_inv(self->UB);
|
|
if (UBinv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
stt = settings[0];
|
|
om = settings[1];
|
|
chi = settings[2];
|
|
phi = settings[3];
|
|
|
|
z1FromAngles(self->lambda, stt, om, chi, phi, z1);
|
|
z1m = vectorToMatrix(z1);
|
|
rez = mat_mul(UBinv, z1m);
|
|
for (i = 0; i < 3; i++) {
|
|
hkl[i] = rez[i][0];
|
|
}
|
|
|
|
mat_free(UBinv);
|
|
mat_free(z1m);
|
|
mat_free(rez);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
static int getBiReflection(char *id, reflection * r)
|
|
{
|
|
pSICSOBJ refList;
|
|
double hkl[3], angles[4];
|
|
|
|
refList = SXGetReflectionList();
|
|
if (!GetRefIndexID(refList, id, hkl)) {
|
|
return 0;
|
|
} else {
|
|
r->h = hkl[0];
|
|
r->k = hkl[1];
|
|
r->l = hkl[2];
|
|
GetRefAnglesID(refList, id, angles);
|
|
r->s2t = angles[0];
|
|
r->om = angles[1];
|
|
r->chi = angles[2];
|
|
r->phi = angles[3];
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
MATRIX calcBiUBFromTwo(pSingleDiff self,
|
|
char *refid1, char *refid2, int *err)
|
|
{
|
|
MATRIX newUB;
|
|
reflection r1, r2;
|
|
lattice direct;
|
|
|
|
direct.a = self->cell[0];
|
|
direct.b = self->cell[1];
|
|
direct.c = self->cell[2];
|
|
direct.alpha = self->cell[3];
|
|
direct.beta = self->cell[4];
|
|
direct.gamma = self->cell[5];
|
|
|
|
if (!getBiReflection(refid1, &r1)) {
|
|
*err = REFERR;
|
|
return NULL;
|
|
}
|
|
if (!getBiReflection(refid2, &r2)) {
|
|
*err = REFERR;
|
|
return NULL;
|
|
}
|
|
|
|
newUB = calcUBFromCellAndReflections(direct, r1, r2, err);
|
|
|
|
return newUB;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
MATRIX calcBiUBFromThree(pSingleDiff self,
|
|
char *refid1, char *refid2, char *refid3,
|
|
int *err)
|
|
{
|
|
MATRIX newUB;
|
|
reflection r1, r2, r3;
|
|
|
|
if (!getBiReflection(refid1, &r1)) {
|
|
*err = REFERR;
|
|
return NULL;
|
|
}
|
|
if (!getBiReflection(refid2, &r2)) {
|
|
*err = REFERR;
|
|
return NULL;
|
|
}
|
|
if (!getBiReflection(refid3, &r3)) {
|
|
*err = REFERR;
|
|
return NULL;
|
|
}
|
|
|
|
newUB = calcUBFromThreeReflections(r1, r2, r3, self->lambda, err);
|
|
return newUB;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
static int calcBiZ1(pSingleDiff self, char *refid, double z1[3])
|
|
{
|
|
reflection r1;
|
|
|
|
if (!getBiReflection(refid, &r1)) {
|
|
return 0;
|
|
}
|
|
z1FromAngles(self->lambda, r1.s2t, r1.om, r1.chi, r1.phi, z1);
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
void initializeSingleBisecting(pSingleDiff diff)
|
|
{
|
|
|
|
if (diff->motList >= 0) {
|
|
LLDdelete(diff->motList);
|
|
}
|
|
diff->motList = LLDcreate(sizeof(MotControl));
|
|
addMotorToList(diff->motList, (char *) SXGetMotorName(TwoTheta), .0);
|
|
addMotorToList(diff->motList, (char *) SXGetMotorName(Omega), .0);
|
|
addMotorToList(diff->motList, (char *) SXGetMotorName(Chi), .0);
|
|
addMotorToList(diff->motList, (char *) SXGetMotorName(Phi), .0);
|
|
|
|
diff->calculateSettings = calculateBiSettings;
|
|
diff->settingsToList = settingsToBiList;
|
|
diff->hklFromAngles = hklFromBiAngles;
|
|
diff->hklFromAnglesGiven = hklFromBiAnglesGiven;
|
|
diff->calcUBFromTwo = calcBiUBFromTwo;
|
|
diff->calcUBFromThree = calcBiUBFromThree;
|
|
diff->calcZ1 = calcBiZ1;
|
|
}
|
|
/*--------------------------------------------------------------------*/
|
|
void initializeSingleBisectingOrion(pSingleDiff diff)
|
|
{
|
|
initializeSingleBisecting(diff);
|
|
diff->calculateSettings = calculateBiSettingsOrion;
|
|
}
|