Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,22 @@ https://github.com/areaDetector/ADEiger/tags

Release Notes
=============
R3-6 (June XXX, 2025)
R3-6 (January XXX, 2026)
----
* Added support for the Stream2 interface. Stream2 supports multiple thresholds.
- Added new StreamVersion record to select the Stream or Stream2 interface.
- If Stream2 is selected and more than one threshold is enabled then the
dimensions of NDArrays created when DataSource is Stream will be [NumX, NumY, NumThresholds].
- ROI plugins can be used to select individual the thresholds to send to other plugins,
such as statistics, PVA for viewing, etc.
* Added support for Pilatus4 detectors.
driver creates one NDArray for each threshold.
- The driver sends the NDArrays for all thresholds on asyn address 0. It sends
only the NDArray for threshold N on address N (N=1 to number of enabled thresholds).
- Plugins can thus use asyn address 0 to receive NDArrays for all thresholds, address 1
to receive only the first enabled threshold, etc.
- Stream2 adds 2 new NDAttributes for each NDArray. These attributes identify which threshold that NDArray contains.
- ThresholdName is an NDAttrString attribute containing the name of the threshold as reported by the Stream2 interface.
These are "threshold_1", "threshold_2", etc.
- ThresholdEnergy is an NDAttrFloat64 attribute containing the energy of the threshold as reported by the Stream2 interface
in units of eV.
* Added support for Pilatus4 detectors. Thanks to Tejus Guruswamy for this.
* Added new FWHDF5Format record for the FileWriter interface.
- This record allows selecting the "Legacy" format, or the "v2024.2" format.
v2024.2 supports saving multiple thresholds.
Expand All @@ -38,6 +45,9 @@ R3-6 (June XXX, 2025)
For 16-bit data this would be a problem when there are over 32K counts per pixel.
Since the maximum count rate is about 2e6 counts/s there should never be more than 20K counts in 0.01 seconds,
and there should thus be no problem.
* Added new StreamAsTSSource record. If this is set to Yes, and if the data is coming from the Stream2
interface, then the NDArray timeStamp and epicsTS fields are taken from the timestamp information
sent by the detector over the Stream2 interface. Thanks to Bruno Martins for this.
* BEFORE RELEASE. Check the function of InternalEnable mode to see if it works now.
The documentation said it was flaky in firmware 1.5.0, so it probably works now and the
documentation should be fixed in several places.
Expand Down
18 changes: 13 additions & 5 deletions docs/ADEiger/eiger.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,19 @@ NDArrays. Otherwise a third-party client can listen on that socket for
data. The format of the packets is specified in the Eiger SIMPLON API
documentation.

The NDArrays received by the Stream1 API are 2-dimensional, [NX, NY].
If only a single threshold is enabled then the NDArrays received by the
Stream2 API are also 2-dimensional.
If more than one threshold is enabled then the NDArrays are 3-dimensional,
[NX, NY, NThreshholds].
If StreamVersion=Stream2 and if more than one threshold is enabled then the driver
generates one NDArray for each threshold in successive order for the enabled thresholds.
The driver sends the NDArrays for all thresholds on asyn address 0.
It sends only the NDArrays for threshold N on address N (N=1 to number of enabled thresholds).
Plugins can thus use asyn address 0 to receive NDArrays for all thresholds, address 1
to receive only the first enabled threshold, etc.

Stream2 adds 2 new NDAttributes for each NDArray. These attributes identify which threshold that NDArray contains.

- ThresholdName is an NDAttrString attribute containing the name of the threshold as reported by the Stream2 interface.
These are "threshold_1", "threshold_2", etc.
- ThresholdEnergy is an NDAttrFloat64 attribute containing the energy of the threshold as reported by the Stream2 interface
in units of eV.

The data sent from the Eiger server is unsigned 32-bit, 16-bit, or 8-bit integers,
depending on the exposure time.
Expand Down
127 changes: 67 additions & 60 deletions eigerApp/src/eigerDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
#define ENERGY_EPSILON 0.05
#define WAVELENGTH_EPSILON 0.0005

// Maximum number of thresholds Pilatus4 has 4
#define MAX_THRESHOLDS 4

// Error message formatters
#define ERR(msg) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: %s\n", \
driverName, functionName, msg)
Expand Down Expand Up @@ -173,7 +176,7 @@ eigerDetector::eigerDetector (const char *portName, const char *serverHostname,
int maxBuffers, size_t maxMemory, int priority,
int stackSize)

: ADDriver(portName, 2, 0, maxBuffers, maxMemory,
: ADDriver(portName, MAX_THRESHOLDS+1, 0, maxBuffers, maxMemory,
0, 0, /* No interfaces beyond ADDriver.cpp */
ASYN_CANBLOCK | /* ASYN_CANBLOCK=1 */
ASYN_MULTIDEVICE, /* ASYN_MULTIDEVICE=1 */
Expand Down Expand Up @@ -1374,6 +1377,7 @@ void eigerDetector::streamTask (void)
}
int err;
stream_header_t header = {};
int numThresholds = 1;
for(;;)
{
unlock();
Expand Down Expand Up @@ -1424,7 +1428,7 @@ void eigerDetector::streamTask (void)
if (streamVersion == STREAM_VERSION_STREAM) {
err = mStreamAPI->waitFrame(&endFrames);
} else {
err = mStream2API->waitFrame(&endFrames);
err = mStream2API->waitFrame(&endFrames, &numThresholds);
}
lock();
if (err == STREAM_SUCCESS) {
Expand All @@ -1448,66 +1452,69 @@ void eigerDetector::streamTask (void)
break;
}

NDArray *pArray;
int decompress;
mStreamDecompress->get(decompress);
bool tsIsSet = false;
if (streamVersion == STREAM_VERSION_STREAM) {
err = mStreamAPI->getFrame(&pArray, pNDArrayPool, decompress);
} else {
err = mStream2API->getFrame(&pArray, pNDArrayPool, decompress, streamAsTsSource);
tsIsSet = streamAsTsSource;
}
int imageCounter, numImagesCounter, arrayCallbacks;
getIntegerParam(NDArrayCounter, &imageCounter);
getIntegerParam(ADNumImagesCounter, &numImagesCounter);
getIntegerParam(NDArrayCallbacks, &arrayCallbacks);

// The data returned from the StreamAPIs is unsigned.
// Bad pixels and gaps are very large positive numbers, which makes autoscaling difficult
// Optionally change the data type to signed.
// This improves autoscaling, but reduces the count range by 2X.
int signedData;
mSignedData->get(signedData);
if (signedData) {
int dataType = pArray->dataType;
switch (pArray->dataType) {
case NDUInt8:
pArray->dataType = NDInt8;
break;
case NDUInt16:
pArray->dataType = NDInt16;
break;
case NDUInt32:
pArray->dataType = NDInt32;
break;
default:
ERR_ARGS("Unknown data type=%d", dataType);
for (int thresh=0; thresh<numThresholds; thresh++) {
NDArray *pArray;
int decompress;
mStreamDecompress->get(decompress);
bool tsIsSet = false;
if (streamVersion == STREAM_VERSION_STREAM) {
err = mStreamAPI->getFrame(&pArray, pNDArrayPool, decompress);
} else {
err = mStream2API->getFrame(&pArray, pNDArrayPool, thresh, decompress, streamAsTsSource);
tsIsSet = streamAsTsSource;
}
int imageCounter, numImagesCounter, arrayCallbacks;
getIntegerParam(NDArrayCounter, &imageCounter);
getIntegerParam(ADNumImagesCounter, &numImagesCounter);
getIntegerParam(NDArrayCallbacks, &arrayCallbacks);

// The data returned from the StreamAPIs is unsigned.
// Bad pixels and gaps are very large positive numbers, which makes autoscaling difficult
// Optionally change the data type to signed.
// This improves autoscaling, but reduces the count range by 2X.
int signedData;
mSignedData->get(signedData);
if (signedData) {
int dataType = pArray->dataType;
switch (pArray->dataType) {
case NDUInt8:
pArray->dataType = NDInt8;
break;
case NDUInt16:
pArray->dataType = NDInt16;
break;
case NDUInt32:
pArray->dataType = NDInt32;
break;
default:
ERR_ARGS("Unknown data type=%d", dataType);
}
}

// Put the frame number and timestamp into the buffer
pArray->uniqueId = imageCounter;

// Only call updateTimeStamps if the stream2 has not set the ts itself
if (!tsIsSet)
updateTimeStamps(pArray);

// Update Omega angle for this frame
++mFrameNumber;

// Get any attributes that have been defined for this driver
this->getAttributes(pArray->pAttributeList);

// Call the NDArray callback
if (arrayCallbacks) {
doCallbacksGenericPointer(pArray, NDArrayData, 0);
doCallbacksGenericPointer(pArray, NDArrayData, thresh+1);
}
setIntegerParam(NDArrayCounter, ++imageCounter);
setIntegerParam(ADNumImagesCounter, ++numImagesCounter);

callParamCallbacks();
pArray->release();
}

// Put the frame number and timestamp into the buffer
pArray->uniqueId = imageCounter;

// Only call updateTimeStamps if the stream2 has not set the ts itself
if (!tsIsSet)
updateTimeStamps(pArray);

// Update Omega angle for this frame
++mFrameNumber;

// Get any attributes that have been defined for this driver
this->getAttributes(pArray->pAttributeList);

// Call the NDArray callback
if (arrayCallbacks)
doCallbacksGenericPointer(pArray, NDArrayData, 0);

setIntegerParam(NDArrayCounter, ++imageCounter);
setIntegerParam(ADNumImagesCounter, ++numImagesCounter);

callParamCallbacks();
pArray->release();
}

end:
Expand Down
Loading