From e2d6d48cde99315b6ac99ed82f15095874391a3a Mon Sep 17 00:00:00 2001 From: marcofilho Date: Fri, 10 Oct 2025 16:48:07 +0200 Subject: [PATCH 1/2] Decrease image-timestamp latency. The timestamp buffer is now filled unconditionally, always after dcamcap_transferinfo is called. It is only used, however, if trigger mode is external. This reduces time between detecting an image and getting the timestamp from around 1.6 ms to around 200 ns. --- ADOrcaApp/src/orca.cpp | 8 ++++---- ADOrcaApp/src/orca.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ADOrcaApp/src/orca.cpp b/ADOrcaApp/src/orca.cpp index da91796..a05d974 100644 --- a/ADOrcaApp/src/orca.cpp +++ b/ADOrcaApp/src/orca.cpp @@ -841,6 +841,7 @@ void Orca::imageTask() { double maxAcqusitionTime, acqusitionRate; int triggerMode = DCAMPROP_TRIGGERSOURCE__INTERNAL; int triggerActive; + char buf[256]; lock(); while (1) { @@ -875,7 +876,7 @@ void Orca::imageTask() { // get image transfer status. unlock(); - imageTransferStatus(m_hdcam, captransferinfo); + imageTransferStatus(m_hdcam, captransferinfo, buf); lock(); if (prevAcquisitionCount < (uint64_t)captransferinfo.nFrameCount) { @@ -926,8 +927,6 @@ void Orca::imageTask() { // Update asyn parameters setIntegerParam(evrCountsSinceAcqStart, evr_counts_since_last_start); setIntegerParam(evrTriggerDropped, evr_trigger_dropped); - char buf[256]; - getStringParam(evrTimeStamp, 256, buf); // remember parsed timestamp values sscanf(buf, "%u.%u", &mTimeStampSec, &mTimeStampNsec); @@ -1742,7 +1741,7 @@ void Orca::getImageInformation(HDCAM hdcam, int32& pixeltype, int32& width, } asynStatus Orca::imageTransferStatus(HDCAM hdcam, - DCAMCAP_TRANSFERINFO& captransferinfo) { + DCAMCAP_TRANSFERINFO& captransferinfo, char *buf) { DCAMERR err; const char* functionName = "imageTransferStatus"; memset(&captransferinfo, 0, sizeof(captransferinfo)); @@ -1750,6 +1749,7 @@ asynStatus Orca::imageTransferStatus(HDCAM hdcam, // get number of captured image err = dcamcap_transferinfo(m_hdcam, &captransferinfo); + getStringParam(evrTimeStamp, 256, buf); if (failed(err)) { ERR_ARGS("DCAMERR: 0x%08X [%s]", m_err, "dcamcap_transferinfo"); return asynError; diff --git a/ADOrcaApp/src/orca.h b/ADOrcaApp/src/orca.h index 6a1696b..0811ce5 100644 --- a/ADOrcaApp/src/orca.h +++ b/ADOrcaApp/src/orca.h @@ -333,7 +333,7 @@ class epicsShareClass Orca : public ADDriver { int32 rowbytes, int32 cx, int32 cy, epicsUInt32& ts_sec, epicsUInt32& ts_microsec); asynStatus imageTransferStatus(HDCAM hdcam, - DCAMCAP_TRANSFERINFO& captransferinfo); + DCAMCAP_TRANSFERINFO& captransferinfo, char *buf); // wrapper functions for dcamapi int allocateBuffers(unsigned int); From cceb18989b2f205f306a03d44497cd3bf9ae5b4c Mon Sep 17 00:00:00 2001 From: marcofilho Date: Sun, 11 Oct 2026 17:46:44 +0200 Subject: [PATCH 2/2] Make epicsTS carry the proper EVR timestamp epicsTS is the one passed to kafka: https://gitlab.esss.lu.se/epics-modules/ADPluginKafka/-/blob/2.0.2/adpluginkafkaApp/src/KafkaPlugin.cpp?ref_type=tags#L40 The documentation is a bit confusing on what is the difference between epicsTS and timeStamp, but apparently both can be modified with no problems. After [this discussion](github.com/areaDetector/ADCore/issues?q=is%3Aissue%20state%3Aclosed) I decided it was fine to keep the epicsTS in nanoseconds with the EVR values. --- ADOrcaApp/src/orca.cpp | 14 +++++++++----- ADOrcaApp/src/orca.h | 3 --- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ADOrcaApp/src/orca.cpp b/ADOrcaApp/src/orca.cpp index a05d974..0a629f0 100644 --- a/ADOrcaApp/src/orca.cpp +++ b/ADOrcaApp/src/orca.cpp @@ -834,6 +834,7 @@ void Orca::imageTask() { uint64_t prevAcquisitionCount = 0; DCAMCAP_TRANSFERINFO captransferinfo; epicsTimeStamp prevAcqTime, currentAcqTime; + epicsTimeStamp acqTimestamp; epicsTimeStamp prev_trigger_time, current_trigger_time; double elapsedTime; int evr_counts = 0; @@ -929,14 +930,17 @@ void Orca::imageTask() { setIntegerParam(evrTriggerDropped, evr_trigger_dropped); // remember parsed timestamp values - sscanf(buf, "%u.%u", &mTimeStampSec, &mTimeStampNsec); - timestamp = mTimeStampSec + mTimeStampNsec / 1.e9; - FLOW_ARGS("mTimeStampSec: %u - mTimeStampNsec: %u\n", mTimeStampSec, - mTimeStampNsec); + sscanf(buf, "%u.%u", &acqTimestamp.secPastEpoch, &acqTimestamp.nsec); + acqTimestamp.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH; + + timestamp = acqTimestamp.secPastEpoch + acqTimestamp.nsec / 1.e9; + FLOW_ARGS("acqTimestamp.secPastEpoch: %u - acqTimestamp.nsec: %u\n", acqTimestamp.secPastEpoch, + acqTimestamp.nsec); FLOW_ARGS("Timestamp from evr: %f\n", timestamp); } else { + updateTimeStamp(&acqTimestamp); timestamp = (ts_sec + ts_microsec / 1.0e6) - (exposure_time + readout_time); FLOW_ARGS("Timestamp from camera: %f\n", timestamp); @@ -966,7 +970,7 @@ void Orca::imageTask() { if (pImage) { pImage->uniqueId = count; pImage->timeStamp = timestamp; - updateTimeStamp(&pImage->epicsTS); + pImage->epicsTS = acqTimestamp; memcpy(pImage->pData, (epicsUInt16*)image, pImage->dataSize); diff --git a/ADOrcaApp/src/orca.h b/ADOrcaApp/src/orca.h index 0811ce5..e2ecbff 100644 --- a/ADOrcaApp/src/orca.h +++ b/ADOrcaApp/src/orca.h @@ -189,9 +189,6 @@ class epicsShareClass Orca : public ADDriver { int readParameter(int propertyID, bool processPV = true); int readParameterStr(int paramIndex); - epicsUInt32 mTimeStampSec; - epicsUInt32 mTimeStampNsec; - void imageTask(); void temperatureTask();