diff --git a/andorApp/Db/andorCCD.template b/andorApp/Db/andorCCD.template index 441492d..7492dd3 100644 --- a/andorApp/Db/andorCCD.template +++ b/andorApp/Db/andorCCD.template @@ -4,6 +4,7 @@ include "ADBase.template" include "NDFile.template" +include "CCDMultiTrack.template" # These are the records that we modify from NDFile.template record(mbbo, "$(P)$(R)FileFormat") @@ -373,6 +374,8 @@ record(mbbo, "$(P)$(R)AndorReadOutMode") field(ZRVL, "0") field(ONST, "Image") field(ONVL, "4") + field(TWST, "Random Track") + field(TWVL, "2") field(VAL, "4") } @@ -384,6 +387,8 @@ record(mbbi, "$(P)$(R)AndorReadOutMode_RBV") field(ZRVL, "0") field(ONST, "Image") field(ONVL, "4") + field(TWST, "Random Track") + field(TWVL, "2") field(SCAN, "I/O Intr") } @@ -421,6 +426,40 @@ record(mbbi, "$(P)$(R)AndorVSPeriod_RBV") field(SCAN, "I/O Intr") } +record(mbbo, "$(P)$(R)AndorVSAmplitude") +{ + field(PINI, "1") + field(DTYP, "asynInt32") + field(ZRST, "Normal") + field(ZRVL, "0") + field(ONST, "+1") + field(ONVL, "1") + field(TWST, "+2") + field(TWVL, "2") + field(THST, "+3") + field(THVL, "3") + field(FRST, "+4") + field(FRVL, "4") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ANDOR_VS_AMPLITUDE") + field(VAL, "0") +} + +record(mbbi, "$(P)$(R)AndorVSAmplitude_RBV") +{ + field(DTYP, "asynInt32") + field(ZRST, "Normal") + field(ZRVL, "0") + field(ONST, "+1") + field(ONVL, "1") + field(TWST, "+2") + field(TWVL, "2") + field(THST, "+3") + field(THVL, "3") + field(FRST, "+4") + field(FRVL, "4") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ANDOR_VS_AMPLITUDE") + field(SCAN, "I/O Intr") +} #Records in ADBase that do not apply to Andor record(mbbo, "$(P)$(R)ColorMode") diff --git a/andorApp/Db/andorCCD_settings.req b/andorApp/Db/andorCCD_settings.req index ae4b090..68efdea 100644 --- a/andorApp/Db/andorCCD_settings.req +++ b/andorApp/Db/andorCCD_settings.req @@ -11,6 +11,7 @@ $(P)$(R)AndorADCSpeed $(P)$(R)AndorReadOutMode $(P)$(R)AndorFTMode $(P)$(R)AndorVSPeriod +$(P)$(R)AndorVSAmplitude file "ADBase_settings.req", P=$(P), R=$(R) file "NDFile_settings.req", P=$(P), R=$(R) - +file "CCDMultiTrack_settings.req", P=$(P), R=$(R) diff --git a/andorApp/src/andorCCD.cpp b/andorApp/src/andorCCD.cpp index 32293a0..660a7b9 100755 --- a/andorApp/src/andorCCD.cpp +++ b/andorApp/src/andorCCD.cpp @@ -7,6 +7,7 @@ * Updated Dec 2011 for Asyn 4-17 and areaDetector 1-7 * * Major updates to get callbacks working, etc. by Mark Rivers Feb. 2011 + * Updated by Peter Heesterman to support multi-track operation Oct. 2019 * */ @@ -114,10 +115,10 @@ static void exitHandler(void *drvPvt); AndorCCD::AndorCCD(const char *portName, const char *installPath, int cameraSerial, int shamrockID, int maxBuffers, size_t maxMemory, int priority, int stackSize) - : ADDriver(portName, 1, NUM_ANDOR_DET_PARAMS, maxBuffers, maxMemory, + : ADDriver(portName, 1, 0, maxBuffers, maxMemory, asynEnumMask, asynEnumMask, ASYN_CANBLOCK, 1, priority, stackSize), - mExiting(false), mShamrockId(shamrockID), mSPEDoc(0), mInitOK(false) + mExiting(false), mExited(0), mShamrockId(shamrockID), mMultiTrack(this), mSPEDoc(0), mInitOK(false) { int status = asynSuccess; @@ -146,7 +147,7 @@ AndorCCD::AndorCCD(const char *portName, const char *installPath, int cameraSeri createParam(AndorShutterModeString, asynParamInt32, &AndorShutterMode); createParam(AndorShutterExTTLString, asynParamInt32, &AndorShutterExTTL); createParam(AndorPalFileNameString, asynParamOctet, &AndorPalFileName); - createParam(AndorAccumulatePeriodString, asynParamFloat64, &AndorAccumulatePeriod); + createParam(AndorAccumulatePeriodString, asynParamFloat64, &AndorAccumulatePeriod); createParam(AndorPreAmpGainString, asynParamInt32, &AndorPreAmpGain); createParam(AndorEmGainString, asynParamInt32, &AndorEmGain); createParam(AndorEmGainModeString, asynParamInt32, &AndorEmGainMode); @@ -156,6 +157,8 @@ AndorCCD::AndorCCD(const char *portName, const char *installPath, int cameraSeri createParam(AndorReadOutModeString, asynParamInt32, &AndorReadOutMode); createParam(AndorFrameTransferModeString, asynParamInt32, &AndorFrameTransferMode); createParam(AndorVerticalShiftPeriodString, asynParamInt32, &AndorVerticalShiftPeriod); + createParam(AndorVerticalShiftAmplitudeString, asynParamInt32, &AndorVerticalShiftAmplitude); + // Create the epicsEvent for signaling to the status task when parameters should have changed. // This will cause it to do a poll immediately, rather than wait for the poll time period. @@ -295,6 +298,7 @@ AndorCCD::AndorCCD(const char *portName, const char *installPath, int cameraSeri setupPreAmpGains(); setupVerticalShiftPeriods(); status |= setIntegerParam(AndorVerticalShiftPeriod, mVSIndex); + status |= setIntegerParam(AndorVerticalShiftAmplitude, 0); status |= setupShutter(-1); callParamCallbacks(); @@ -352,10 +356,15 @@ AndorCCD::~AndorCCD() { static const char *functionName = "~AndorCCD"; - this->lock(); mExiting = true; + this->lock(); printf("%s::%s Shutdown and freeing up memory...\n", driverName, functionName); try { + int acquireStatus; + checkStatus(GetStatus(&acquireStatus)); + if (acquireStatus == DRV_ACQUIRING) + checkStatus(AbortAcquisition()); + epicsEventSignal(dataEvent); checkStatus(FreeInternalMemory()); checkStatus(ShutDown()); } catch (const std::string &e) { @@ -364,6 +373,8 @@ AndorCCD::~AndorCCD() driverName, functionName, e.c_str()); } this->unlock(); + while (mExited < 2) + epicsThreadSleep(0.2); } @@ -666,7 +677,7 @@ asynStatus AndorCCD::writeInt32(asynUser *pasynUser, epicsInt32 value) (function == AndorEmGainMode) || (function == AndorEmGainAdvanced) || (function == AndorAdcSpeed) || (function == AndorPreAmpGain) || (function == AndorReadOutMode) || (function == AndorFrameTransferMode) || - (function == AndorVerticalShiftPeriod)) { + (function == AndorVerticalShiftPeriod) || (function == AndorVerticalShiftAmplitude)) { status = setupAcquisition(); if (function == AndorAdcSpeed) setupPreAmpGains(); if (status != asynSuccess) setIntegerParam(function, oldValue); @@ -815,11 +826,66 @@ asynStatus AndorCCD::writeFloat64(asynUser *pasynUser, epicsFloat64 value) driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, - "%s:%s: function=%d, value=%f\n", - driverName, functionName, function, value); + "%s:%s: function=%d, value=%f\n", + driverName, functionName, function, value); return status; } +/* Called if tracks acquisition mode is being used. + Sets up the track defintion. */ +void AndorCCD::setupTrackDefn(int minX, int sizeX, int binX) +{ + static const char *functionName = "setupTrackDefn"; + if (mMultiTrack.size() == 0) + { + asynPrint(this->pasynUserSelf, ASYN_TRACE_WARNING, + "%s:%s: A track defintion must be set to use tracks mode\n", + driverName, functionName); + return; + } + + static const int ValuesPerTrack = 6; + std::vector TrackDefn(mMultiTrack.size() * 6); + setIntegerParam(NDArraySizeY, mMultiTrack.DataHeight()); + for (size_t TrackNo = 0; TrackNo < mMultiTrack.size(); TrackNo++) + { + /* + Each track must be defined by a group of six integers. + - The top and bottom positions of the tracks. + - The left and right positions for the area of interest within each track + - The horizontal and vertical binning for each track. */ + TrackDefn[TrackNo * 6 + 0] = mMultiTrack.TrackStart(TrackNo); + TrackDefn[TrackNo * 6 + 1] = mMultiTrack.TrackEnd(TrackNo); + TrackDefn[TrackNo * 6 + 2] = minX + 1; + TrackDefn[TrackNo * 6 + 3] = minX + sizeX; + TrackDefn[TrackNo * 6 + 4] = binX; + TrackDefn[TrackNo * 6 + 5] = mMultiTrack.TrackBin(TrackNo); + } + checkStatus(SetCustomTrackHBin(binX)); + checkStatus(SetComplexImage(int(TrackDefn.size() / ValuesPerTrack), &TrackDefn[0])); +} + +/* Called to set tracks definition parameters. + Sets up the track defintion. */ +asynStatus AndorCCD::writeInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t nElements) +{ + static const char *functionName = "writeInt32Array"; + asynStatus status = asynSuccess; + try { + status = mMultiTrack.writeInt32Array(pasynUser, value, nElements); + if (status != asynError) + setupAcquisition(); + else + status = ADDriver::writeInt32Array(pasynUser, value, nElements); + } + catch (const std::string &e) { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s\n", + driverName, functionName, e.c_str()); + status = asynError; + } + return status; +} /** Controls shutter * @param[in] command 0=close, 1=open, -1=no change, only set other parameters */ @@ -969,6 +1035,8 @@ unsigned int AndorCCD::checkStatus(unsigned int returnStatus) throw std::string("ERROR: Error loading firmware."); } else if (returnStatus == DRV_NOT_SUPPORTED) { throw std::string("ERROR: Feature not supported."); + } else if (returnStatus == DRV_RANDOM_TRACK_ERROR) { + throw std::string("ERROR: Invalid combination of tracks"); } else { sprintf(message, "ERROR: Unknown error code=%d returned from Andor SDK.", returnStatus); throw std::string(message); @@ -1020,8 +1088,8 @@ void AndorCCD::statusTask(void) forcedFastPolls = 5; } + if (mExiting) break; this->lock(); - if (mExiting) break; try { //Only read these if we are not acquiring data @@ -1074,8 +1142,11 @@ void AndorCCD::statusTask(void) this->unlock(); } //End of loop - printf("%s:%s: Status thread exiting ...\n", driverName, functionName); + asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s: Status thread exiting ...\n", + driverName, functionName); + mExited++; } /** Set up acquisition parameters */ @@ -1098,6 +1169,7 @@ asynStatus AndorCCD::setupAcquisition() int readOutMode; int frameTransferMode; int verticalShiftPeriod; + int verticalShiftAmplitude; static const char *functionName = "setupAcquisition"; if (!mInitOK) { @@ -1185,17 +1257,23 @@ asynStatus AndorCCD::setupAcquisition() // Unfortunately there does not seem to be a way to query the Andor SDK // for the actual size of the image, so we must compute it. setIntegerParam(NDArraySizeX, sizeX/binX); - setIntegerParam(NDArraySizeY, sizeY/binY); + if (readOutMode != ARRandomTrack) + // The data height dimension is set by setupTrackDefn for multi-track mode. + setIntegerParam(NDArraySizeY, sizeY/binY); getIntegerParam(AndorFrameTransferMode, &frameTransferMode); getIntegerParam(AndorVerticalShiftPeriod, &verticalShiftPeriod); - + + getIntegerParam(AndorVerticalShiftAmplitude, &verticalShiftAmplitude); + try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s:, SetReadMode(%d)\n", driverName, functionName, readOutMode); checkStatus(SetReadMode(readOutMode)); + if (readOutMode == ARRandomTrack) + setupTrackDefn(minX, sizeX, binX); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s:, SetTriggerMode(%d)\n", @@ -1266,11 +1344,16 @@ asynStatus AndorCCD::setupAcquisition() driverName, functionName, frameTransferMode); checkStatus(SetFrameTransferMode(frameTransferMode)); - asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, - "%s:%s:, SetVSSpeed(%d)\n", - driverName, functionName, verticalShiftPeriod); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s:, SetVSSpeed(%d)\n", + driverName, functionName, verticalShiftPeriod); checkStatus(SetVSSpeed(verticalShiftPeriod)); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s:, SetVSAmplitude(%d)\n", + driverName, functionName, verticalShiftAmplitude); + checkStatus(SetVSAmplitude(verticalShiftAmplitude)); + switch (imageMode) { case ADImageSingle: if (numExposures == 1) { @@ -1395,19 +1478,22 @@ void AndorCCD::dataTask(void) epicsTimeStamp startTime; NDArray *pArray; int autoSave; + int readOutMode; static const char *functionName = "dataTask"; printf("%s:%s: Data thread started...\n", driverName, functionName); - + this->lock(); - while(1) { + while(!mExiting) { errorString = NULL; //Wait for event from main thread to signal that data acquisition has started. this->unlock(); status = epicsEventWait(dataEvent); + if (mExiting) + break; asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s:, got data event\n", driverName, functionName); @@ -1419,6 +1505,7 @@ void AndorCCD::dataTask(void) status = setupAcquisition(); if (status != asynSuccess) continue; getIntegerParam(ADShutterMode, &adShutterMode); + getIntegerParam(AndorReadOutMode, &readOutMode); if (adShutterMode == ADShutterModeEPICS) { ADDriver::setShutter(ADShutterOpen); } @@ -1450,7 +1537,7 @@ void AndorCCD::dataTask(void) acquiring = 0; } - while (acquiring) { + while ((acquiring) && (!mExiting)) { try { checkStatus(GetStatus(&acquireStatus)); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, @@ -1491,6 +1578,8 @@ void AndorCCD::dataTask(void) dims[0] = sizeX; dims[1] = sizeY; pArray = this->pNDArrayPool->alloc(nDims, dims, dataType, 0, NULL); + if (readOutMode == ARRandomTrack) + mMultiTrack.storeTrackAttributes(pArray->pAttributeList); // Read the oldest array // Is there still an image available? status = GetNumberNewImages(&firstImage, &lastImage); @@ -1533,11 +1622,14 @@ void AndorCCD::dataTask(void) callParamCallbacks(); } } catch (const std::string &e) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: %s\n", - driverName, functionName, e.c_str()); - errorString = const_cast(e.c_str()); - setStringParam(AndorMessage, errorString); + if (!mExiting) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s\n", + driverName, functionName, e.c_str()); + errorString = const_cast(e.c_str()); + setStringParam(AndorMessage, errorString); + } } } @@ -1553,7 +1645,8 @@ void AndorCCD::dataTask(void) /* Call the callbacks to update any changes */ callParamCallbacks(); } //End of loop - + mExited++; + this->unlock(); } diff --git a/andorApp/src/andorCCD.h b/andorApp/src/andorCCD.h index 191bad1..0dbf210 100755 --- a/andorApp/src/andorCCD.h +++ b/andorApp/src/andorCCD.h @@ -4,15 +4,17 @@ * @author Matthew Pearson * @date June 2009 * - * Updated Dec 2011 for Asyn 4-17 and areaDetector 1-7 + * Updated Dec 2011 for Asyn 4-17 and areaDetector 1-7 * * Major updates to get callbacks working, etc. by Mark Rivers Feb. 2011 + * Updated by Peter Heesterman to support multi-track operation Oct. 2019 */ #ifndef ANDORCCD_H #define ANDORCCD_H #include +#include #include "ADDriver.h" #include "SPEHeader.h" @@ -38,6 +40,7 @@ #define AndorReadOutModeString "ANDOR_READOUT_MODE" #define AndorFrameTransferModeString "ANDOR_FT_MODE" #define AndorVerticalShiftPeriodString "ANDOR_VS_PERIOD" +#define AndorVerticalShiftAmplitudeString "ANDOR_VS_AMPLITUDE" /** * Structure defining an ADC speed for the ADAndor driver. @@ -86,8 +89,9 @@ class AndorCCD : public ADDriver { /* These are the methods that we override from ADDriver */ virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value); + virtual asynStatus writeInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t nElements); virtual void report(FILE *fp, int details); - virtual asynStatus readEnum(asynUser *pasynUser, char *strings[], int values[], int severities[], + virtual asynStatus readEnum(asynUser *pasynUser, char *strings[], int values[], int severities[], size_t nElements, size_t *nIn); // Should be private, but are called from C so must be public @@ -96,7 +100,7 @@ class AndorCCD : public ADDriver { protected: int AndorCoolerParam; - #define FIRST_ANDOR_PARAM AndorCoolerParam +#define FIRST_ANDOR_PARAM AndorCoolerParam int AndorTempStatusMessage; int AndorMessage; int AndorShutterMode; @@ -112,7 +116,8 @@ class AndorCCD : public ADDriver { int AndorReadOutMode; int AndorFrameTransferMode; int AndorVerticalShiftPeriod; - #define LAST_ANDOR_PARAM AndorVerticalShiftPeriod + int AndorVerticalShiftAmplitude; +#define LAST_ANDOR_PARAM AndorVerticalShiftAmplitude private: @@ -121,13 +126,14 @@ class AndorCCD : public ADDriver { asynStatus setupShutter(int command); void saveDataFrame(int frameNumber); void setupADCSpeeds(); + void setupTrackDefn(int minX, int sizeX, int binX); void setupPreAmpGains(); void setupVerticalShiftPeriods(); unsigned int SaveAsSPE(char *fullFileName); /** * Additional image mode to those in ADImageMode_t */ - static const epicsInt32 AImageFastKinetics; + static const epicsInt32 AImageFastKinetics; /** * List of acquisiton modes. @@ -197,7 +203,8 @@ class AndorCCD : public ADDriver { unsigned int mAcquiringData; char *mInstallPath; bool mExiting; - + int mExited; + /** * ADC speed parameters */ @@ -222,17 +229,19 @@ class AndorCCD : public ADDriver { float mAccumulatePeriod; int mMinShutterOpenTime; int mMinShutterCloseTime; - + // Shamrock spectrometer ID int mShamrockId; // AndorCapabilities structure AndorCapabilities mCapabilities; + CCDMultiTrack mMultiTrack; + // EM Gain parameters int mEmGainRangeLow; int mEmGainRangeHigh; - + // SPE file header tagCSMAHEAD *mSPEHeader; xmlDocPtr mSPEDoc; @@ -241,7 +250,4 @@ class AndorCCD : public ADDriver { bool mInitOK; }; -#define NUM_ANDOR_DET_PARAMS ((int)(&LAST_ANDOR_PARAM - &FIRST_ANDOR_PARAM + 1)) - #endif //ANDORCCD_H - diff --git a/andorApp/src/shamrock.cpp b/andorApp/src/shamrock.cpp index b2b4b6f..74bd555 100644 --- a/andorApp/src/shamrock.cpp +++ b/andorApp/src/shamrock.cpp @@ -102,9 +102,6 @@ private: bool flipperMirrorIsPresent_[MAX_FLIPPER_MIRRORS]; }; -/** Number of asynPortDriver parameters this driver supports. */ -#define NUM_SR_PARAMS ((int)(&LAST_SR_PARAM - &FIRST_SR_PARAM + 1)) - /** Configuration function to configure one spectrograph. * * This function need to be called once for each spectrography to be used by the IOC. A call to this