\subsection{Histogram memory} A histogram memory is the interface to a large multidetector. This is quite a complex piece of equipment. It does not only provide lots of data but also has a lot of configuration options. Things which need configuration are: histogram mode, overflow mode, measurement mode and the layout of the histograms. Let's discuss these different modes first. @d Modes @{ typedef enum { eHTransparent, eHNormal, eHTOF, eHStrobo, eHRPT, ePSD, eSANSTOF } HistMode; @} These modes are specific to the SINQ histogram memory. A histogram memory can be operated in transparent mode. It has not yet been defined what this means but it is sort of storing raw data from the detector without any summing or processing. Normal mode is better defined, this is sorting and summing data from the detector into apropriate bins for each detector. Time is not resolved. TOF mode means time of flight mode. In this mode incoming is not only sorted and summed into the correct detector bins but also resolved in time (which means energy for neutrons). This means for each detector there is a histogram counts versus time. Similar is stroboscopic mode (eHStrobo). In stroboscopic mode there is a histogram counts versus pulses for each physical detector. The bin switching is done via a secondary hardware signal or from software. This is useful for measuring diffraction data versus oscillating environment conditions. eHRPT is a special mode for the HRPT Cerca detector. It requires special handling, this is why there is a special flag. @d Modes @{ typedef enum { eOIgnore, eOCeil, eOCount, eReflect } OverFlowMode; @} Histogram memories may support different schemes for handling overflow conditions. In this condition there are more counts than fit into the configured binwidth. The simplest thing to do is to ignore the condition and simply wrap over to zero (eOIgnore). The other mode is to keep the bin at the highest count possible (eOCeil). More sophisticated histogram memories maintain a separate list where all those overflowed bins are registered. This is eOCount. eReflect has nothing to do with overflows. It says that the histogram should be reflected. This means counter 1 becomes counter max etc. This is special for HRPT. It happens to live in the OverFlowMode enum because this is really a sub mode descriptor. The measurement mode is the simple counter operation mode: wait for a timer or wait for a monitor to run full. The layout of the histogram memory (HM) is defined by the rank (i.e. the number of dimensions of the HM, the number of points in each dimension and by the binwidth in bits for each bin. For TOF and stroboscopic modes information about the binning in time or pulses is needed as well. Needless to say that these values have to match the geometry of the detector. Histograms usually have a number type associated with them. In order to minimise conversion overhead in the logical object, the data size will defined by a typedef, HistInt. There is a scheme for handling all this configuration information which may even be more for a Histogram memory with a specific driver. There is a dictionary of configuration options in the logical histogram memory object. These options can be set with a special command. Then on initialisation first the logical histogram memory evaluates the general options and then the driver in its Config function evaluates the driver specific options. The histogram memory supports several dimensions, a time binning option and optional buffering of histogram memory data read from the actual HM. All this data management stuff is handled in a separate class, HMdata. See the documentation for HMdata for more details. \subsubsection{The Histogram memory driver} Adhering to the Sics paradigm of dividing any device into a logical device and a hardware driver, the Sics histogram memory needs drivers as well. This section describes this driver interface. For an overview, see the structure definition: @d HistType @{ typedef struct __HistDriver { pHMdata data; /* counting operations data */ CounterMode eCount; float fCountPreset; /* status flags */ int iReconfig; int iUpdate; pStringDict pOption; /* interface functions */ int (*Configure)(pHistDriver self, SConnection *pCon, pStringDict pOpt, SicsInterp *pSics); int (*Start)(pHistDriver self, SConnection *pCon); int (*Halt)(pHistDriver self); int (*GetCountStatus)(pHistDriver self, SConnection *pCon); int (*GetError)(pHistDriver self, int *iCode, char *perror, int iErrlen); int (*TryAndFixIt)(pHistDriver self, int iCode); int (*GetData)(pHistDriver self, SConnection *pCon); int (*GetHistogram)(pHistDriver self, SConnection *pCon, int i, int iStart, int iEnd, HistInt *pData); int (*SetHistogram)(pHistDriver self, SConnection *pCon, int i, int iStart, int iEnd, HistInt *pData); long (*GetMonitor)(pHistDriver self, int i, SConnection *pCon); float (*GetTime)(pHistDriver self, SConnection *pCon); HistInt *(*SubSample)(pHistDriver self, SConnection *pCon,int bank, char *command); int (*Preset)(pHistDriver self, SConnection *pCon, HistInt iVal); int (*Pause)(pHistDriver self, SConnection *pCon); int (*Continue)(pHistDriver self, SConnection *pCon); int (*FreePrivate)(pHistDriver self); void *pPriv; } HistDriver; @} Quite a lot, but a histogram memory is quite a complex piece of equipment. The fields fPreset and CounterMode hold the counting parameter data. Than there are two status fields. The first is iReconfig. This will be set to true, whenever any changes to the configuration to the HM are made. This will be tested against when starting a measurement and will force a configuration call to be issued. A call to the interface function Configure clears this flag. The other flag is iUpdate. This is only useful when an in memory copy of the histogrammed data is maintained on the host computer. This flag is set when a count gets started and gets cleared when counting is finished and all data has been transferred. The next part defines the hardware interface functions to the histogram memory. Each of these functions must be defined by any complete implementation of a HM driver. If not stated otherwise these functions return 1 on success and 0 on failure. All functions take a pointer to their HistDriver structure as first parameter. Many functions have a pointer to an SConnection as parameter. This connection will be used for error reporting. \begin{itemize} \item {\bf Configure} configures the histogram memory to the specifications given in the fields of the HMdriver structure. Further driver specific information can be read from the options dictionary passed in. \item {\bf Start} starts a counting operation according to the current settings of the counter mode parameters. \item {\bf Halt} implements an emergency stop of a counting operation. \item {\bf GetCountStatus} serves to monitor the status of the counting operation. Possible return values to this call are: \begin{itemize} \item HWBUSY when still counting. \item HWNoBeam when the monitor is to low. \item HWIDLE or OKOK when nothing is going on. \item HWFault when there is an error on the device. \end{itemize} \item {\bf GetError} will be called whenever an error has been detected on the device. The task is to put an internal error code into the iCode parameter. The string parameter error will be filled with a text description of the error. But maximum iLen characters will be transferred to the error string in order to protect against memory corruption. Therefore iLen must be the maximum field length of error. \item {\bf TryAndFixIt} is the next function called in case of an error on the device. Its second parameter is the internal code obtained in the ICode parameter of the call to GetError. The task of this function is to examine the error code and do whatever is possible in software to fix the problem. TryAndFixIt returns one of the following values: \begin{itemize} \item MOTREDO when the error could be fixed, but the upper level code will need to rerun the command which failed. \item MOTFAIL when the software is unable to fix the problem and a real mechanic with a hammer is needed (or somebody able to reboot!). \item MOTOK when the error was fixed and nor further action is necessary. \end{itemize} \item {\bf GetData} transfers all the data collected in the HM into the host computers memory. \item {\bf GetHistogram} copies the histogram number i into the data space given by the pointer to a long array. A conversion from different binwidth to long is performed as well. \item {\bf SetHistogram} presets the histogram number i with the data given in lData. A conversion from different binwidth to long is performed as well. iStart and iStop define the start and end of the stretch of histogram to replace. \item {\bf GetMonitor} returns the counts in the monitor i. Returns a negative value on error. The error will have been printed to pCon. \item {\bf GetTime} returns the actual counting time. \item {\bf Preset} initializes the histogram memory to the value given by iVal. \item {\bf Pause} pauses data collection. \item {\bf Continue} continues a paused data collection. \item {\bf FreePrivate} will be called automatically by DeleteHistDriver and has the task to remove the private data installed by implementations of an actual histogram memory driver. \end{itemize} The last entry in the HistDriver structure is a pointer to void. Rather then use the overlay structure as with motor drivers, histogram memory stores driver specific information in a driver specific sub data structure. This is the pointer to pPrivate. KillPrivate is a function which is able to delete the driver specific data structure properly. As all the burden is up to the implementation of the histogram memory driver only these few functions operate on histogram memory drivers in general: @d HistDrivProt @{ pHistDriver CreateHistDriver(pStringDict pDict); void DeleteHistDriver(pHistDriver self); int HistDriverConfig(pHistDriver self, pStringDict pOpt, SConnection *pCon); HistInt *DefaultSubSample(pHistDriver self, SConnection *pCon, int bank, char *command); @} CreateHistDriver creates a new HistDriver data structure and returns it. Or NULL when the memory is exhausted. It also initialises the options database in pDict to the apropriate values. DeleteHistDriver removes a histogram memory driver from memory. It will automatically try and call Freeprivate in order to remove driver specific data. Than an the rest is removed. After this call self will point to rubbish. \subsubsection{The histogram memory object} Most of the HM data will be held with the driver as it is needed there for performing operations. Accordingly the datastructure associated with the histogram memory object is fairly simple: @d HistST @{ typedef struct __HistMem { pObjectDescriptor pDes; int iAccess; int iExponent; pHistDriver pDriv; int iInit; pICountable pCountInt; pICallBack pCall; } HistMem; @} According to the general Sics object interface the first field is the object descriptor. Note, that the histogram memory has to adhere to the countable objects interface. iAccess is the access code used to control user interaction with the HM. iExponent is the force of 10 used for calculating the actual preset value for counting in monitor mode. However, note, that all configuration commands will require Manager privilege at least. Then there is a pointer to the driver followed by pointers to the interfaces implemented by the histogram memory. pOption is a string dictionary of configuration options for the histogram memory. In order to save network bandwidth and for efficiency the histogram memory object buffers the histogram memory in memory. The histogram itself resides in iLocalData. iLocalLength is the length of iLocalData. iLocalUpdate is a flag which will be set when counting operations (histogram changes ) are going on and cleared when the histogram has been completely read. When counting the local data will be updated only at certain intervalls. This is a configuration option. iUpdateIntervall is that intervall in seconds, tLocal is the time for the next scheduled update. The interaction with the histogram memory can be classified into four groups: basic initialisation and destruction, configuration, counting and data retrieval. The first group to look at are the basics: @d Protos @{ pHistMem CreateHistMemory(char *drivername); void DeleteHistMemory(void *self); @} CreateHistMem creates a new histogram memory. On error this call returns NULL. On success a pointer to the new histogram memory object. CreateHistMem does NOT configure anything! A driver gets selected according to the driver name. DeleteHistMemory removes a histogram memory from computer memory. Any references to self afterwards will point to rubbish. The next group of functions refer to configuration. @d Protos @{ int HistGetOption(pHistMem self, char *name, char *result, int iResultLen); int HistSetOption(pHistMem self, char *name, char *value); int HistConfigure(pHistMem self, SConnection *pCon, SicsInterp *pSics); @} HistGetOption caters for the retrieval of the current option given in name. Maximum iResultLen characters of result will be copied into result, or an error message if things go wrong. HistSetOption sets the option name to value specified in value. No error checking except availability of the option and permission is done here. HistConfigure updates the internal datastructures from the dictionary and does the actual configuration of the HM. Tons of error messages will be printed to pCon. Valid names for the options in the dictionary are all the names in the histogram datastructure. Please note, that counting options are handled separately (see below). Additionally there may be driver specific options available. For iDim iRank numbers are expected which represent the dimensions of the HM. The functions for counting and manipulation of counting parameters have been separated from the rest of the configuration. The reason is that the main configuration will only be performed by highly privileged users, whereas the counting operations will be accessible by normal users. Counting is controlled by the following functions: @d Protos @{ float GetHistPreset(pHistMem self); int SetHistPreset(pHistMem self, float fVal); CounterMode GetHistCountMode(pHistMem self); int SetHistCountMode(pHistMem self, CounterMode eNew); long GetHistMonitor(pHistMem self, int i, SConnection *pCon); const float *GetHistTimeBin(pHistMem self, int *iLength); int GetHistLength(pHistMem self); int GetHistDim(pHistMem self, int iDim[MAXDIM], int *nDim); float GetHistCountTime(pHistMem self,SConnection *pCon); int HistDoCount(pHistMem self, SConnection *pCon); int HistBlockCount(pHistMem self, SConnection *pCon); void HistDirty(pHistMem self); int isSecondGen(pHistMem self); pHistMem FindHM(SicsInterp *pSics, char *name); @} The first four functions are simple parameter enquiry and manipulation functions. GetHistMonitor returns the count on monitor number i for the histogram memory. This function returns a negative value when the value specified for i is invalid. HistDoCount actually starts a counting operation. It will not block and wait for counting to finish. Please note, that many counting manipulation functions are hidden in the functions of the countable interface, which the HM has to implement. HistBlockCount also starts counting. But will block until counting has finished. Another set of functions is needed for manipulation of the data in the histogram memory: @d Protos @{ int SetHistogram(pHistMem self, SConnection *pCon, int i,int iStart, int iEnd, HistInt *lData); int GetHistogram(pHistMem self, SConnection *pCon, int i,int iStart, int iEnd, HistInt *lData, int iDataLen); HistInt *GetHistogramPointer(pHistMem self,SConnection *pCon); int GetHistogramDirect(pHistMem self, SConnection *pCon, int i, int iStart, int iEnd, HistInt *lData, int iDataLen); int PresetHistogram(pHistMem self, SConnection *pCon, HistInt lVal); @} For histogram I/O the following aproach has been taken: Histograms are kept as linear arrays of data regardless of the layout of the detector. It is the task of upper level code to map the right histogram to the right position. This aproach was choosen for two reasons: 1.) simplification of interface, 2.) easier to retrieve smaller chunks of data at a time. With both SetHistogram and GetHistogram the parameter i denotes the histogram to retrieve. A value of -1 retrieves the whole lot. iStart and iEnd allow for extraction of a subset of a histogram. These values get ignored when trying to retrieve a whole HM content. These routines perform conversion to and from long to the binwidth of the HM. SetHistogram initialises the HM from the lData provided. GetHistogram reads an histogram into lData but maximum iDataLen items. PresetHistogram presets the HM to the value lVal. Can be used to clear the HM. GetHistogram and GetHistogramPointer try to buffer the data when possible and configured. The configuration happens through the definition of an update intervall. GetHistogramDirect never buffers but goes for the histogram memory directly. The histogram memory object buffers the histograms for a adjustable period of time. GetHistogramPointer retrieves a pointer to the local histogram buffer. It also makes sure, that the histogram has been updated if appropriate. This is provided for efficiency and saves some memory copying and duplication of data during operations. Thus memory consumption can be reduced. However, the drawback is that you mess directly with the histogram memory content which is potentially dangerous. Use this method with great care! A last group of functions are those needed to interface to the Sics interpreter: @d Protos @{ int MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); int HistAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); @} MakeHistMem is supposed to be called with two parameters: name and driver which denote the name of the HM object and the type of driver to use. If all is well, this call will create a command named name. Remember, that the HM is not yet configured! HistAction is supposed to be where the action goes. HistAction understands the commands:\begin{itemize} \item {\bf name config option value} sets a configuration option. \item {\bf name init} configures the HM. \item {\bf name Preset val} returns the preset value if val is missing, else it sets it. \item {\bf name Mode val} returns the counting mode if val is missing, else it sets it. \item {\bf name count} starts a counting operation. \item {\bf name get number} gets histogram number. -1 as histogram number gets all. \item {\bf name set number val........} sets histogram number to the values following. \item {\bf name clear val} sets all the HM to val. \end{itemize} @o HistMem.h -d @{ /*-------------------------------------------------------------------------- H I S T M E M header for the histogram memory object for SICS. copyright: see implementation file. Mark Koennecke, April 1997 -----------------------------------------------------------------------------*/ #ifndef SICSHISTMEM #define SICSHISTMEM #define MAXDIM 3 typedef struct __HistDriver *pHistDriver; typedef struct __HistMem *pHistMem; /*-------------------------------------------------------------------------*/ typedef int HistInt; /* 32 bit integer on a DigitalUnix */ @< Modes @> /*--------------------------------------------------------------------------*/ @< Protos @> #endif @} @o HistDriv.i -d @{ /*--------------------------------------------------------------------------- H I S T D R I V internal header file which includes the definition of the Histogram memory driver structure. Mark Koennecke, April 1997 ----------------------------------------------------------------------------*/ #ifndef SICSHISTDRIV #define SICSHISTDRIV #include "hmdata.h" @< HistType@> @< HistDrivProt @> #endif @} @o HistMem.i -d @{ /*--------------------------------------------------------------------------- H I S T M E M -- Internal internal header file which includes the definition of the Histogram memory data structure. Mark Koennecke, April 1997 ----------------------------------------------------------------------------*/ #ifndef SICSHISTMEMINT #define SICSHISTMEMINT @< HistST@> #endif @}