Subversion Repositories f9daq

Rev

Rev 197 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/********************************************************************

  Name:         DRS.cpp
  Created by:   Stefan Ritt, Matthias Schneebeli

  Contents:     Library functions for DRS mezzanine and USB boards

  $Id: DRS.cpp 22289 2016-04-27 09:40:58Z ritt $

\********************************************************************/

#define NEW_TIMING_CALIBRATION

#ifdef USE_DRS_MUTEX
#include "wx/wx.h"    // must be before <windows.h>
#endif

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <algorithm>
#include <sys/stat.h>
#include <fcntl.h>
#include "strlcpy.h"
#include "DRS.h"

#ifdef _MSC_VER
#pragma warning(disable:4996)
#   include <windows.h>
#   include <direct.h>
#else
#   include <unistd.h>
#   include <sys/time.h>
inline void Sleep(useconds_t x)
{
   usleep(x * 1000);
}
#endif

#ifdef _MSC_VER
#include <conio.h>
#define drs_kbhit() kbhit()
#else
#include <sys/ioctl.h>

int drs_kbhit()
{
   int n;

   ioctl(0, FIONREAD, &n);
   return (n > 0);
}
static inline int getch()
{
   return getchar();
}
#endif

#include "DRS.h"

#ifdef _MSC_VER
extern "C" {
#endif

#include "mxml.h"

#ifdef _MSC_VER
}
#endif

/*---- minimal FPGA firmvare version required for this library -----*/
const int REQUIRED_FIRMWARE_VERSION_DRS2 = 5268;
const int REQUIRED_FIRMWARE_VERSION_DRS3 = 6981;
const int REQUIRED_FIRMWARE_VERSION_DRS4 = 15147;

/*---- calibration methods to be stored in EEPROMs -----------------*/

#define VCALIB_METHOD_V4 1
#define TCALIB_METHOD_V4 1

#define VCALIB_METHOD  2
#define TCALIB_METHOD  2 // correct for sampling frequency, calibrate every channel

/*---- VME addresses -----------------------------------------------*/
#ifdef HAVE_VME
/* assuming following DIP Switch settings:

   SW1-1: 1 (off)       use geographical addressing (1=left, 21=right)
   SW1-2: 1 (off)       \
   SW1-3: 1 (off)        >  VME_WINSIZE = 8MB, subwindow = 1MB
   SW1-4: 0 (on)        /
   SW1-5: 0 (on)        reserverd
   SW1-6: 0 (on)        reserverd
   SW1-7: 0 (on)        reserverd
   SW1-8: 0 (on)       \
                        |
   SW2-1: 0 (on)        |
   SW2-2: 0 (on)        |
   SW2-3: 0 (on)        |
   SW2-4: 0 (on)        > VME_ADDR_OFFSET = 0
   SW2-5: 0 (on)        |
   SW2-6: 0 (on)        |
   SW2-7: 0 (on)        |
   SW2-8: 0 (on)       /

   which gives
     VME base address = SlotNo * VME_WINSIZE + VME_ADDR_OFFSET
                      = SlotNo * 0x80'0000
*/

#define GEVPC_BASE_ADDR           0x00000000
#define GEVPC_WINSIZE               0x800000
#define GEVPC_USER_FPGA   (GEVPC_WINSIZE*2/8)
#define PMC1_OFFSET                  0x00000
#define PMC2_OFFSET                  0x80000
#define PMC_CTRL_OFFSET              0x00000    /* all registers 32 bit */
#define PMC_STATUS_OFFSET            0x10000
#define PMC_FIFO_OFFSET              0x20000
#define PMC_RAM_OFFSET               0x40000
#endif                          // HAVE_VME
/*---- USB addresses -----------------------------------------------*/
#define USB_TIMEOUT                     1000    // one second
#ifdef HAVE_USB
#define USB_CTRL_OFFSET                 0x00    /* all registers 32 bit */
#define USB_STATUS_OFFSET               0x40
#define USB_RAM_OFFSET                  0x80
#define USB_CMD_IDENT                      0    // Query identification
#define USB_CMD_ADDR                       1    // Address cycle
#define USB_CMD_READ                       2    // "VME" read <addr><size>
#define USB_CMD_WRITE                      3    // "VME" write <addr><size>
#define USB_CMD_READ12                     4    // 12-bit read <LSB><MSB>
#define USB_CMD_WRITE12                    5    // 12-bit write <LSB><MSB>

#define USB2_CMD_READ                      1
#define USB2_CMD_WRITE                     2
#define USB2_CTRL_OFFSET             0x00000    /* all registers 32 bit */
#define USB2_STATUS_OFFSET           0x10000
#define USB2_FIFO_OFFSET             0x20000
#define USB2_RAM_OFFSET              0x40000
#endif                          // HAVE_USB

/*------------------------------------------------------------------*/

using namespace std;

#ifdef HAVE_USB
#define USB2_BUFFER_SIZE (1024*1024+10)
unsigned char static *usb2_buffer = NULL;
#endif

/*------------------------------------------------------------------*/

#ifdef USE_DRS_MUTEX
static wxMutex *s_drsMutex = NULL; // used for wxWidgets multi-threaded programs
#endif

/*------------------------------------------------------------------*/

DRS::DRS()
:  fNumberOfBoards(0)
#ifdef HAVE_VME
    , fVmeInterface(0)
#endif
{
#ifdef HAVE_USB
   MUSB_INTERFACE *usb_interface;
#endif

#if defined(HAVE_VME) || defined(HAVE_USB)
   int index = 0, i=0;
#endif

   memset(fError, 0, sizeof(fError));

#ifdef HAVE_VME
   unsigned short type, fw, magic, serial, temperature;
   mvme_addr_t addr;

   if (mvme_open(&fVmeInterface, 0) == MVME_SUCCESS) {

      mvme_set_am(fVmeInterface, MVME_AM_A32);
      mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);

      /* check all VME slave slots */
      for (index = 2; index <= 21; index++) {

         /* check PMC1 */
         addr = GEVPC_BASE_ADDR + index * GEVPC_WINSIZE;        // VME board base address
         addr += GEVPC_USER_FPGA;       // UsrFPGA base address
         addr += PMC1_OFFSET;   // PMC1 offset

         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
         i = mvme_read(fVmeInterface, &magic, addr + PMC_STATUS_OFFSET + REG_MAGIC, 2);
         if (i == 2) {
            if (magic != 0xC0DE) {
               printf("Found old firmware, please upgrade immediately!\n");
               fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1);
               fNumberOfBoards++;
            } else {

               /* read board type */
               mvme_read(fVmeInterface, &type, addr + PMC_STATUS_OFFSET + REG_BOARD_TYPE, 2);
               type &= 0xFF;
               if (type == 2 || type == 3 || type == 4) {    // DRS2 or DRS3 or DRS4

                  /* read firmware number */
                  mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_VERSION_FW, 2);

                  /* read serial number */
                  mvme_read(fVmeInterface, &serial, addr + PMC_STATUS_OFFSET + REG_SERIAL_BOARD, 2);

                  /* read temperature register to see if CMC card is present */
                  mvme_read(fVmeInterface, &temperature, addr + PMC_STATUS_OFFSET + REG_TEMPERATURE, 2);

                  /* LED blinking */
#if 0
                  do {
                     data = 0x00040000;
                     mvme_write(fVmeInterface, addr + PMC_CTRL_OFFSET + REG_CTRL, &data, sizeof(data));
                     mvme_write(fVmeInterface, addr + PMC2_OFFSET + PMC_CTRL_OFFSET + REG_CTRL, &data,
                                sizeof(data));

                     Sleep(500);

                     data = 0x00000000;
                     mvme_write(fVmeInterface, addr + PMC_CTRL_OFFSET + REG_CTRL, &data, sizeof(data));
                     mvme_write(fVmeInterface, addr + PMC2_OFFSET + PMC_CTRL_OFFSET + REG_CTRL, data,
                                sizeof(data));

                     Sleep(500);

                  } while (1);
#endif

                  if (temperature == 0xFFFF) {
                     printf("Found VME board in slot %d, fw %d, but no CMC board in upper slot\n", index, fw);
                  } else {
                     printf("Found DRS%d board %2d in upper VME slot %2d, serial #%d, firmware revision %d\n", type, fNumberOfBoards, index, serial, fw);

                     fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1);
                     if (!fBoard[fNumberOfBoards]->HasCorrectFirmware())
                        sprintf(fError, "Wrong firmware version: board has %d, required is %d. Board may not work correctly.\n",
                                fBoard[fNumberOfBoards]->GetFirmwareVersion(),
                                fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
                     fNumberOfBoards++;
                  }
               }
            }
         }

         /* check PMC2 */
         addr = GEVPC_BASE_ADDR + index * GEVPC_WINSIZE;        // VME board base address
         addr += GEVPC_USER_FPGA;       // UsrFPGA base address
         addr += PMC2_OFFSET;   // PMC2 offset

         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
         i = mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_MAGIC, 2);
         if (i == 2) {
            if (magic != 0xC0DE) {
               printf("Found old firmware, please upgrade immediately!\n");
               fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1 | 1);
               fNumberOfBoards++;
            } else {

               /* read board type */
               mvme_read(fVmeInterface, &type, addr + PMC_STATUS_OFFSET + REG_BOARD_TYPE, 2);
               type &= 0xFF;
               if (type == 2 || type == 3 || type == 4) {    // DRS2 or DRS3 or DRS4

                  /* read firmware number */
                  mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_VERSION_FW, 2);

                  /* read serial number */
                  mvme_read(fVmeInterface, &serial, addr + PMC_STATUS_OFFSET + REG_SERIAL_BOARD, 2);

                  /* read temperature register to see if CMC card is present */
                  mvme_read(fVmeInterface, &temperature, addr + PMC_STATUS_OFFSET + REG_TEMPERATURE, 2);

                  if (temperature == 0xFFFF) {
                     printf("Found VME board in slot %d, fw %d, but no CMC board in lower slot\n", index, fw);
                  } else {
                     printf("Found DRS%d board %2d in lower VME slot %2d, serial #%d, firmware revision %d\n", type, fNumberOfBoards, index, serial, fw);

                     fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, ((index - 2) << 1) | 1);
                     if (!fBoard[fNumberOfBoards]->HasCorrectFirmware())
                        sprintf(fError, "Wrong firmware version: board has %d, required is %d. Board may not work correctly.\n",
                                fBoard[fNumberOfBoards]->GetFirmwareVersion(),
                                fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
                     fNumberOfBoards++;
                  }
               }
            }
         }
      }
   } else
      printf("Cannot access VME crate, check driver, power and connection\n");
#endif                          // HAVE_VME

#ifdef HAVE_USB
   unsigned char buffer[512];
   int found, one_found, usb_slot;

   one_found = 0;
   usb_slot = 0;
   for (index = 0; index < 127; index++) {
      found = 0;

      /* check for USB-Mezzanine test board */
      if (musb_open(&usb_interface, 0x10C4, 0x1175, index, 1, 0) == MUSB_SUCCESS) {

         /* check ID */
         buffer[0] = USB_CMD_IDENT;
         musb_write(usb_interface, 2, buffer, 1, USB_TIMEOUT);

         i = musb_read(usb_interface, 1, (char *) buffer, sizeof(buffer), USB_TIMEOUT);
         if (strcmp((char *) buffer, "USB_MEZZ2 V1.0") != 0) {
            /* no USB-Mezzanine board found */
            musb_close(usb_interface);
         } else {
            usb_interface->usb_type = 1;        // USB 1.1
            fBoard[fNumberOfBoards] = new DRSBoard(usb_interface, usb_slot++);
            if (!fBoard[fNumberOfBoards]->HasCorrectFirmware())
               sprintf(fError, "Wrong firmware version: board has %d, required is %d. Board may not work correctly.\n",
                       fBoard[fNumberOfBoards]->GetFirmwareVersion(),
                       fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
            fNumberOfBoards++;
            found = 1;
            one_found = 1;
         }
      }

      /* check for DRS4 evaluation board */
      if (musb_open(&usb_interface, 0x04B4, 0x1175, index, 1, 0) == MUSB_SUCCESS) {

         /* check ID */
         if (musb_get_device(usb_interface) != 1) {
            /* no DRS evaluation board found */
            musb_close(usb_interface);
         } else {

            /* drain any data from Cy7C68013 FIFO if FPGA startup caused erratic write */
            do {
               i = musb_read(usb_interface, 8, buffer, sizeof(buffer), 100);
               if (i > 0)
                  printf("%d bytes stuck in buffer\n", i);
            } while (i > 0);

            usb_interface->usb_type = 2;        // USB 2.0
            fBoard[fNumberOfBoards] = new DRSBoard(usb_interface, usb_slot++);
            if (!fBoard[fNumberOfBoards]->HasCorrectFirmware())
               sprintf(fError, "Wrong firmware version: board has %d, required is %d. Board may not work correctly.\n",
                      fBoard[fNumberOfBoards]->GetFirmwareVersion(),
                      fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
            fNumberOfBoards++;
            found = 1;
            one_found = 1;
         }
      }

      if (!found) {
         if (!one_found)
            printf("USB successfully scanned, but no boards found\n");
         break;
      }
   }
#endif                          // HAVE_USB

   return;
}

/*------------------------------------------------------------------*/

DRS::~DRS()
{
   int i;
   for (i = 0; i < fNumberOfBoards; i++) {
      delete fBoard[i];
   }

#ifdef HAVE_USB
   if (usb2_buffer) {
      free(usb2_buffer);
      usb2_buffer = NULL;
   }
#endif

#ifdef HAVE_VME
   mvme_close(fVmeInterface);
#endif
}

/*------------------------------------------------------------------*/

void DRS::SortBoards()
{
   /* sort boards according to serial number (simple bubble sort) */
   for (int i=0 ; i<fNumberOfBoards-1 ; i++) {
      for (int j=i+1 ; j<fNumberOfBoards ; j++) {
         if (fBoard[i]->GetBoardSerialNumber() < fBoard[j]->GetBoardSerialNumber()) {
            DRSBoard* b = fBoard[i];
            fBoard[i] = fBoard[j];
            fBoard[j] = b;
         }
      }
   }
}

/*------------------------------------------------------------------*/

void DRS::SetBoard(int i, DRSBoard *b)
{
   fBoard[i] = b;
}

/*------------------------------------------------------------------*/

bool DRS::GetError(char *str, int size)
{
   if (fError[0])
      strlcpy(str, fError, size);

   return fError[0] > 0;
}

/*------------------------------------------------------------------*/

#ifdef HAVE_USB
DRSBoard::DRSBoard(MUSB_INTERFACE * musb_interface, int usb_slot)
:  fDAC_COFSA(0)
    , fDAC_COFSB(0)
    , fDAC_DRA(0)
    , fDAC_DSA(0)
    , fDAC_TLEVEL(0)
    , fDAC_ACALIB(0)
    , fDAC_DSB(0)
    , fDAC_DRB(0)
    , fDAC_COFS(0)
    , fDAC_ADCOFS(0)
    , fDAC_CLKOFS(0)
    , fDAC_ROFS_1(0)
    , fDAC_ROFS_2(0)
    , fDAC_INOFS(0)
    , fDAC_BIAS(0)
    , fDRSType(0)
    , fBoardType(0)
    , fRequiredFirmwareVersion(0)
    , fFirmwareVersion(0)
    , fBoardSerialNumber(0)
    , fHasMultiBuffer(0)
    , fCtrlBits(0)
    , fNumberOfReadoutChannels(0)
    , fReadoutChannelConfig(0)
    , fADCClkPhase(0)
    , fADCClkInvert(0)
    , fExternalClockFrequency(0)
    , fUsbInterface(musb_interface)
#ifdef HAVE_VME
    , fVmeInterface(0)
    , fBaseAddress(0)
#endif
    , fSlotNumber(usb_slot)
    , fNominalFrequency(0)
    , fMultiBuffer(0)
    , fDominoMode(0)
    , fDominoActive(0)
    , fChannelConfig(0)
    , fChannelCascading(1)
    , fChannelDepth(1024)
    , fWSRLoop(0)
    , fReadoutMode(0)
    , fReadPointer(0)
    , fNMultiBuffer(0)
    , fTriggerEnable1(0)
    , fTriggerEnable2(0)
    , fTriggerSource(0)
    , fTriggerDelay(0)
    , fTriggerDelayNs(0)
    , fSyncDelay(0)
    , fDelayedStart(0)
    , fTranspMode(0)
    , fDecimation(0)
    , fRange(0)
    , fCommonMode(0.8)
    , fAcalMode(0)
    , fAcalVolt(0)
    , fTcalFreq(0)
    , fTcalLevel(0)
    , fTcalPhase(0)
    , fTcalSource(0)
    , fRefclk(0)
    , fMaxChips(0)
    , fResponseCalibration(0)
    , fVoltageCalibrationValid(false)
    , fCellCalibratedRange(0)
    , fCellCalibratedTemperature(0)
    , fTimeData(0)
    , fNumberOfTimeData(0)
    , fDebug(0)
    , fTriggerStartBin(0)
{
   if (musb_interface->usb_type == 1)
      fTransport = TR_USB;
   else
      fTransport = TR_USB2;
   memset(fStopCell, 0, sizeof(fStopCell));
   memset(fStopWSR, 0, sizeof(fStopWSR));
   fTriggerBus = 0;
   ConstructBoard();
}

#endif

#ifdef HAVE_VME
/*------------------------------------------------------------------*/

DRSBoard::DRSBoard(MVME_INTERFACE * mvme_interface, mvme_addr_t base_address, int slot_number)
:fDAC_COFSA(0)
, fDAC_COFSB(0)
, fDAC_DRA(0)
, fDAC_DSA(0)
, fDAC_TLEVEL(0)
, fDAC_ACALIB(0)
, fDAC_DSB(0)
, fDAC_DRB(0)
, fDAC_COFS(0)
, fDAC_ADCOFS(0)
, fDAC_CLKOFS(0)
, fDAC_ROFS_1(0)
, fDAC_ROFS_2(0)
, fDAC_INOFS(0)
, fDAC_BIAS(0)
, fDRSType(0)
, fBoardType(0)
, fRequiredFirmwareVersion(0)
, fFirmwareVersion(0)
, fBoardSerialNumber(0)
, fHasMultiBuffer(0)
, fTransport(TR_VME)
, fCtrlBits(0)
, fNumberOfReadoutChannels(0)
, fReadoutChannelConfig(0)
, fADCClkPhase(0)
, fADCClkInvert(0)
, fExternalClockFrequency(0)
#ifdef HAVE_USB
, fUsbInterface(0)
#endif
#ifdef HAVE_VME
, fVmeInterface(mvme_interface)
, fBaseAddress(base_address)
, fSlotNumber(slot_number)
#endif
, fNominalFrequency(0)
, fRefClock(0)
, fMultiBuffer(0)
, fDominoMode(1)
, fDominoActive(1)
, fChannelConfig(0)
, fChannelCascading(1)
, fChannelDepth(1024)
, fWSRLoop(1)
, fReadoutMode(0)
, fReadPointer(0)
, fNMultiBuffer(0)
, fTriggerEnable1(0)
, fTriggerEnable2(0)
, fTriggerSource(0)
, fTriggerDelay(0)
, fTriggerDelayNs(0)
, fSyncDelay(0)
, fDelayedStart(0)
, fTranspMode(0)
, fDecimation(0)
, fRange(0)
, fCommonMode(0.8)
, fAcalMode(0)
, fAcalVolt(0)
, fTcalFreq(0)
, fTcalLevel(0)
, fTcalPhase(0)
, fTcalSource(0)
, fRefclk(0)
, fMaxChips(0)
, fResponseCalibration(0)
, fTimeData(0)
, fNumberOfTimeData(0)
, fDebug(0)
, fTriggerStartBin(0)
{
   ConstructBoard();
}

#endif

/*------------------------------------------------------------------*/

DRSBoard::~DRSBoard()
{
   int i;
#ifdef HAVE_USB
   if (fTransport == TR_USB || fTransport == TR_USB2)
      musb_close(fUsbInterface);
#endif

#ifdef USE_DRS_MUTEX
   if (s_drsMutex)
      delete s_drsMutex;
   s_drsMutex = NULL;
#endif

   // Response Calibration
   delete fResponseCalibration;

   // Time Calibration
   for (i = 0; i < fNumberOfTimeData; i++) {
      delete fTimeData[i];
   }
   delete[]fTimeData;
}

/*------------------------------------------------------------------*/

void DRSBoard::ConstructBoard()
{
   unsigned char buffer[2];
   unsigned int bits;

   fDebug = 0;
   fWSRLoop = 1;
   fCtrlBits = 0;

   fExternalClockFrequency = 1000. / 30.;
   strcpy(fCalibDirectory, ".");

   /* check board communication */
   if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0) {
      InitFPGA();
      if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0)
         return;
   }

   ReadSerialNumber();

   /* set correct reference clock */
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
      fRefClock = 60;
   else
      fRefClock = 33;

   /* get mode from hardware */
   bits = GetCtrlReg();
   fMultiBuffer = (bits & BIT_MULTI_BUFFER) > 0;
   fNMultiBuffer = 0;
   if (fHasMultiBuffer && fMultiBuffer)
      fNMultiBuffer = 3;
   if (fDRSType == 4) {
      fDominoMode = (bits & BIT_CONFIG_DMODE) > 0;
   } else {
      fDominoMode = (bits & BIT_DMODE) > 0;
   }
   fTriggerEnable1 = (bits & BIT_ENABLE_TRIGGER1) > 0;
   fTriggerEnable2 = (bits & BIT_ENABLE_TRIGGER2) > 0;
   fTriggerSource = ((bits & BIT_TR_SOURCE1) > 0) | (((bits & BIT_TR_SOURCE2) > 0) << 1);
   fReadoutMode = (bits & BIT_READOUT_MODE) > 0;
   Read(T_CTRL, &fReadPointer, REG_READ_POINTER, 2);
   fADCClkInvert = (bits & BIT_ADCCLK_INVERT) > 0;
   fDominoActive = (bits & BIT_DACTIVE) > 0;
   ReadFrequency(0, &fNominalFrequency);
   if (fNominalFrequency < 0.1 || fNominalFrequency > 6)
      fNominalFrequency = 1;

   /* initialize number of channels */
   if (fDRSType == 4) {
      if (fBoardType == 6) {
         unsigned short d;
         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
         fReadoutChannelConfig = d & 0xFF;
         if (d == 7)
            fNumberOfReadoutChannels = 9;
         else
            fNumberOfReadoutChannels = 5;
      } else
         fNumberOfReadoutChannels = 9;
   } else
      fNumberOfReadoutChannels = 10;

   if (fBoardType == 1) {
      fDAC_COFSA = 0;
      fDAC_COFSB = 1;
      fDAC_DRA = 2;
      fDAC_DSA = 3;
      fDAC_TLEVEL = 4;
      fDAC_ACALIB = 5;
      fDAC_DSB = 6;
      fDAC_DRB = 7;
   } else if (fBoardType == 2 || fBoardType == 3) {
      fDAC_COFS = 0;
      fDAC_DSA = 1;
      fDAC_DSB = 2;
      fDAC_TLEVEL = 3;
      fDAC_CLKOFS = 5;
      fDAC_ACALIB = 6;
      fDAC_ADCOFS = 7;
   } else if (fBoardType == 4) {
      fDAC_ROFS_1 = 0;
      fDAC_DSA = 1;
      fDAC_DSB = 2;
      fDAC_ROFS_2 = 3;
      fDAC_BIAS = 4;
      fDAC_INOFS = 5;
      fDAC_ACALIB = 6;
      fDAC_ADCOFS = 7;
   } else if (fBoardType == 5) {
      fDAC_ROFS_1 = 0;
      fDAC_CMOFS = 1;
      fDAC_CALN = 2;
      fDAC_CALP = 3;
      fDAC_BIAS = 4;
      fDAC_TLEVEL = 5;
      fDAC_ONOFS = 6;
   } else if (fBoardType == 6) {
      fDAC_ONOFS = 0;
      fDAC_CMOFSP = 1;
      fDAC_CALN = 2;
      fDAC_CALP = 3;
      fDAC_CMOFSN = 5;
      fDAC_ROFS_1 = 6;
      fDAC_BIAS = 7;
   } else if (fBoardType == 7) {
      fDAC_ROFS_1 = 0;
      fDAC_CMOFS = 1;
      fDAC_CALN = 2;
      fDAC_CALP = 3;
      fDAC_BIAS = 4;
      fDAC_TLEVEL = 5;
      fDAC_ONOFS = 6;
   } else if (fBoardType == 8 || fBoardType == 9) {
      fDAC_ROFS_1 = 0;
      fDAC_TLEVEL4 = 1;
      fDAC_CALN = 2;
      fDAC_CALP = 3;
      fDAC_BIAS = 4;
      fDAC_TLEVEL1 = 5;
      fDAC_TLEVEL2 = 6;
      fDAC_TLEVEL3 = 7;
   }
   
   if (fDRSType < 4) {
      // Response Calibration
      fResponseCalibration = new ResponseCalibration(this);

      // Time Calibration
      fTimeData = new DRSBoard::TimeData *[kNumberOfChipsMax];
      fNumberOfTimeData = 0;
   }
}

/*------------------------------------------------------------------*/

void DRSBoard::ReadSerialNumber()
{
   unsigned char buffer[2];
   int number;

   // check magic number
   if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0) {
      printf("Cannot read from board\n");
      return;
   }

   number = (static_cast < int >(buffer[1]) << 8) +buffer[0];
   if (number != 0xC0DE) {
      printf("Invalid magic number: %04X\n", number);
      return;
   }
   
   // read board type
   Read(T_STATUS, buffer, REG_BOARD_TYPE, 2);
   fDRSType = buffer[0];
   fBoardType = buffer[1];

   // read firmware version
   Read(T_STATUS, buffer, REG_VERSION_FW, 2);
   fFirmwareVersion = (static_cast < int >(buffer[1]) << 8) +buffer[0];

   // retrieve board serial number
   Read(T_STATUS, buffer, REG_SERIAL_BOARD, 2);
   number = (static_cast < int >(buffer[1]) << 8) +buffer[0];
   fBoardSerialNumber = number;

   // determine DRS type and board type for old boards from setial number
   if (fBoardType == 0) {
      // determine board version from serial number
      if (number >= 2000 && number < 5000) {
         fBoardType = 6;
         fDRSType = 4;
      } else if (number >= 1000) {
         fBoardType = 4;
         fDRSType = 3;
      } else if (number >= 100)
         fBoardType = 3;
      else if (number > 0)
         fBoardType = 2;
      else {
         fBoardType = 3;
         fDRSType = 2;
         fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS2;
      }
   }

   // set constants according to board type
   if (fBoardType == 6)
      fNumberOfChips = 4;
   else
      fNumberOfChips = 1;

   if (fDRSType == 4)
      fNumberOfChannels = 9;
   else
      fNumberOfChannels = 10;

   // retrieve firmware version
   if (fDRSType == 2)
      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS2;
   if (fDRSType == 3)
      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS3;
   if (fDRSType == 4)
      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS4;

   fHasMultiBuffer = ((fBoardType == 6) && fTransport == TR_VME);
}

/*------------------------------------------------------------------*/

void DRSBoard::ReadCalibration(void)
{
   unsigned short buf[1024*16]; // 32 kB
   int i, j, chip;

   fVoltageCalibrationValid = false;
   fTimingCalibratedFrequency = 0;

   memset(fCellOffset,  0, sizeof(fCellOffset));
   memset(fCellGain,    0, sizeof(fCellGain));
   memset(fCellOffset2, 0, sizeof(fCellOffset2));
   memset(fCellDT,      0, sizeof(fCellDT));

   /* read offsets and gain from eeprom */
   if (fBoardType == 9) {
      memset(buf, 0, sizeof(buf));
      ReadEEPROM(0, buf, 4096);
     
      /* check voltage calibration method */
      if ((buf[2] & 0xFF) == VCALIB_METHOD)
         fVoltageCalibrationValid = true;
      else {
         fCellCalibratedRange = 0;
         fCellCalibratedTemperature = -100;
         return;
      }
     
      /* check timing calibration method */
      if ((buf[2] >> 8) == TCALIB_METHOD) {
         float fl; // float from two 16-bit integers
         memcpy(&fl, &buf[8], sizeof(float));
         fTimingCalibratedFrequency = fl;
      } else
         fTimingCalibratedFrequency = -1;
     
      fCellCalibratedRange = ((int) (buf[10] & 0xFF)) / 100.0; // -50 ... +50 => -0.5 V ... +0.5 V
      fCellCalibratedTemperature = (buf[10] >> 8) / 2.0;
     
      ReadEEPROM(1, buf, 1024*32);
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++) {
            fCellOffset[i][j] = buf[(i*1024+j)*2];
            fCellGain[i][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
         }
     
      ReadEEPROM(2, buf, 1024*32);
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++)
            fCellOffset2[i][j]   = buf[(i*1024+j)*2];
     
   } else if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8) {
      memset(buf, 0, sizeof(buf));
      ReadEEPROM(0, buf, 32);

      /* check voltage calibration method */
      if ((buf[2] & 0xFF) == VCALIB_METHOD_V4) // board < 9 has "1", board 9 has "2"
         fVoltageCalibrationValid = true;
      else {
         fCellCalibratedRange = 0;
         return;
      }
      fCellCalibratedTemperature = -100;

      /* check timing calibration method */
      if ((buf[4] & 0xFF) == TCALIB_METHOD_V4) { // board < 9 has "1", board 9 has "2"
         fTimingCalibratedFrequency = buf[6] / 1000.0;
      } else
         fTimingCalibratedFrequency = -1;

      fCellCalibratedRange = ((int) (buf[2] >> 8)) / 100.0; // -50 ... +50 => -0.5 V ... +0.5 V
      ReadEEPROM(1, buf, 1024*32);
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++) {
            fCellOffset[i][j] = buf[(i*1024+j)*2];
            fCellGain[i][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
         }

      ReadEEPROM(2, buf, 1024*5*4);
      for (i=0 ; i<1 ; i++)
         for (j=0 ; j<1024; j++) {
            fCellOffset[i+8][j] = buf[(i*1024+j)*2];
            fCellGain[i+8][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
         }

      for (i=0 ; i<4 ; i++)
         for (j=0 ; j<1024; j++) {
            fCellOffset2[i*2][j]   = buf[2*1024+(i*1024+j)*2];
            fCellOffset2[i*2+1][j] = buf[2*1024+(i*1024+j)*2+1];
         }

   } else if (fBoardType == 6) {
      ReadEEPROM(0, buf, 16);

      /* check voltage calibration method */
      if ((buf[2] & 0xFF) == VCALIB_METHOD)
         fVoltageCalibrationValid = true;
      else {
         fCellCalibratedRange = 0;
         return;
      }

      /* check timing calibration method */
      if ((buf[4] & 0xFF) == TCALIB_METHOD)
         fTimingCalibratedFrequency = buf[6] / 1000.0; // 0 ... 6000 => 0 ... 6 GHz
      else
         fTimingCalibratedFrequency = 0;

      fCellCalibratedRange = ((int) (buf[2] >> 8)) / 100.0; // -50 ... +50 => -0.5 V ... +0.5 V

      for (chip=0 ; chip<4 ; chip++) {
         ReadEEPROM(1+chip, buf, 1024*32);
         for (i=0 ; i<8 ; i++)
            for (j=0 ; j<1024; j++) {
               fCellOffset[i+chip*9][j] = buf[(i*1024+j)*2];
               fCellGain[i+chip*9][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
            }
      }

      ReadEEPROM(5, buf, 1024*4*4);
      for (chip=0 ; chip<4 ; chip++)
         for (j=0 ; j<1024; j++) {
            fCellOffset[8+chip*9][j] = buf[j*2+chip*0x0800];
            fCellGain[8+chip*9][j]   = buf[j*2+1+chip*0x0800]/65535.0*0.4+0.7;
         }

      ReadEEPROM(7, buf, 1024*32);
      for (i=0 ; i<8 ; i++) {
         for (j=0 ; j<1024; j++) {
            fCellOffset2[i][j]   = buf[i*0x800 + j*2];
            fCellOffset2[i+9][j] = buf[i*0x800 + j*2+1];
         }
      }

      ReadEEPROM(8, buf, 1024*32);
      for (i=0 ; i<8 ; i++) {
         for (j=0 ; j<1024; j++) {
            fCellOffset2[i+18][j] = buf[i*0x800 + j*2];
            fCellOffset2[i+27][j] = buf[i*0x800 + j*2+1];
         }
      }

   } else
      return;

   /* read timing calibration from eeprom */
   if (fBoardType == 9) {
      if (fTimingCalibratedFrequency == 0) {
         for (i=0 ; i<8 ; i++)
            for (j=0 ; j<1024 ; j++) {
               fCellDT[0][i][j] = 1/fNominalFrequency;
            }
      } else {
         ReadEEPROM(2, buf, 1024*32);
         for (i=0 ; i<8 ; i++)
            for (j=0 ; j<1024; j++) {
               fCellDT[0][i][j]   = (buf[(i*1024+j)*2+1] - 1000) / 10000.0;
            }
      }
   } else if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8) {
      if (fTimingCalibratedFrequency == 0) {
         for (i=0 ; i<1024 ; i++)
            fCellDT[0][0][i] = 1/fNominalFrequency;
      } else {
         ReadEEPROM(0, buf, 1024*sizeof(short)*2);
         for (i=0 ; i<8 ; i++) {
            for (j=0 ; j<1024; j++) {
               // use calibration for all channels
               fCellDT[0][i][j] = buf[j*2+1]/10000.0;
            }
         }
      }
   } else if (fBoardType == 6) {
      if (fTimingCalibratedFrequency == 0) {
         for (i=0 ; i<1024 ; i++)
            for (j=0 ; j<4 ; j++)
               fCellDT[0][j][i] = 1/fNominalFrequency;
      } else {
         ReadEEPROM(6, buf, 1024*sizeof(short)*4);
         for (i=0 ; i<1024; i++) {
            fCellDT[0][0][i] = buf[i*2]/10000.0;
            fCellDT[1][0][i] = buf[i*2+1]/10000.0;
            fCellDT[2][0][i] = buf[i*2+0x800]/10000.0;
            fCellDT[3][0][i] = buf[i*2+0x800+1]/10000.0;
         }
      }
   }
   
#if 0
   /* Read Daniel's file */
   int fh = open("cal_ch2.dat", O_RDONLY);
   float v;
   read(fh, &v, sizeof(float));
   for (i=0 ; i<1024 ; i++) {
      read(fh, &v, sizeof(float));
      fCellDT[0][2][(i+0) % 1024] = v;
   }
   close(fh);
   fh = open("cal_ch4.dat", O_RDONLY);
   read(fh, &v, sizeof(float));
   for (i=0 ; i<1024 ; i++) {
      read(fh, &v, sizeof(float));
      fCellDT[0][6][(i+0)%1024] = v;
   }
   close(fh);
#endif

#if 0
   /* write timing calibration to EEPROM page 0 */
   double t1, t2;
   ReadEEPROM(0, buf, sizeof(buf));
   for (i=0,t1=0 ; i<1024; i++) {
      t2 = fCellT[0][i] - t1;
      t2 = (unsigned short) (t2 * 10000 + 0.5);
      t1 += t2 / 10000.0;
      buf[i*2+1] = (unsigned short) t2;
   }
   
   /* write calibration method and frequency */
   buf[4] = TCALIB_METHOD;
   buf[6] = (unsigned short) (fNominalFrequency * 1000 + 0.5);
   fTimingCalibratedFrequency = buf[6] / 1000.0;
   WriteEEPROM(0, buf, sizeof(buf));
#endif
   
}

/*------------------------------------------------------------------*/

bool DRSBoard::HasCorrectFirmware()
{
   /* check for required firmware version */
   return (fFirmwareVersion >= fRequiredFirmwareVersion);
}

/*------------------------------------------------------------------*/

int DRSBoard::InitFPGA(void)
{

#ifdef HAVE_USB
   if (fTransport == TR_USB2) {
      unsigned char buffer[1];
      int i, status;

      /* blink Cy7C68013A LED and issue an FPGA reset */
      buffer[0] = 0;            // LED off
      musb_write(fUsbInterface, 1, buffer, 1, 100);
      Sleep(50);

      buffer[0] = 1;            // LED on
      musb_write(fUsbInterface, 1, buffer, 1, 100);

      /* wait until EEPROM page #0 has been read */
      for (i=0 ; i<100 ; i++) {
         Read(T_STATUS, &status, REG_STATUS, 4);
         if ((status & BIT_SERIAL_BUSY) == 0)
            break;
         Sleep(10);
      }
   }
#endif

   return 1;
}

/*------------------------------------------------------------------*/

/* Generic read function accessing VME or USB */

int DRSBoard::Write(int type, unsigned int addr, void *data, int size)
{
#ifdef USE_DRS_MUTEX
   if (!s_drsMutex) {
      s_drsMutex = new wxMutex();
      assert(s_drsMutex);
   }
   s_drsMutex->Lock();
#endif

   if (fTransport == TR_VME) {

#ifdef HAVE_VME
      unsigned int base_addr;

      base_addr = fBaseAddress;

      if (type == T_CTRL)
         base_addr += PMC_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr += PMC_STATUS_OFFSET;
      else if (type == T_RAM)
         base_addr += PMC_RAM_OFFSET;

      if (size == 1) {
         /* 8-bit write access */
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D8);
         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
      } else if (size == 2) {
         /* 16-bit write access */
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
      } else {
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);

         /* as long as no block transfer is supported, do pseudo block transfer */
         mvme_set_blt(fVmeInterface, MVME_BLT_NONE);

         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
      }

#ifdef USE_DRS_MUTEX
      s_drsMutex->Unlock();
#endif
      return size;
#endif                          // HAVE_VME

   } else if (fTransport == TR_USB) {
#ifdef HAVE_USB
      unsigned char buffer[64], ack;
      unsigned int base_addr;
      int i, j, n;

      if (type == T_CTRL)
         base_addr = USB_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr = USB_STATUS_OFFSET;
      else if (type == T_RAM)
         base_addr = USB_RAM_OFFSET;
      else
         base_addr = 0;

      if (type != T_RAM) {

         /*---- register access ----*/

         if (size == 2) {
            /* word swapping: first 16 bit sit at upper address */
            if ((addr % 4) == 0)
               addr = addr + 2;
            else
               addr = addr - 2;
         }

         buffer[0] = USB_CMD_WRITE;
         buffer[1] = base_addr + addr;
         buffer[2] = size;

         for (i = 0; i < size; i++)
            buffer[3 + i] = *((unsigned char *) data + i);

         /* try 10 times */
         ack = 0;
         for (i = 0; i < 10; i++) {
            n = musb_write(fUsbInterface, 2, buffer, 3 + size, USB_TIMEOUT);
            if (n == 3 + size) {
               for (j = 0; j < 10; j++) {
                  /* wait for acknowledge */
                  n = musb_read(fUsbInterface, 1, &ack, 1, USB_TIMEOUT);
                  if (n == 1 && ack == 1)
                     break;

                  printf("Redo receive\n");
               }
            }

            if (ack == 1) {
#ifdef USE_DRS_MUTEX
               s_drsMutex->Unlock();
#endif
               return size;
            }

            printf("Redo send\n");
         }
      } else {

         /*---- RAM access ----*/

         buffer[0] = USB_CMD_ADDR;
         buffer[1] = base_addr + addr;
         musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);

         /* chop buffer into 60-byte packets */
         for (i = 0; i <= (size - 1) / 60; i++) {
            n = size - i * 60;
            if (n > 60)
               n = 60;
            buffer[0] = USB_CMD_WRITE12;
            buffer[1] = n;

            for (j = 0; j < n; j++)
               buffer[2 + j] = *((unsigned char *) data + j + i * 60);

            musb_write(fUsbInterface, 2, buffer, 2 + n, USB_TIMEOUT);

            for (j = 0; j < 10; j++) {
               /* wait for acknowledge */
               n = musb_read(fUsbInterface, 1, &ack, 1, USB_TIMEOUT);
               if (n == 1 && ack == 1)
                  break;

               printf("Redo receive acknowledge\n");
            }
         }

#ifdef USE_DRS_MUTEX
         s_drsMutex->Unlock();
#endif
         return size;
      }
#endif                          // HAVE_USB
   } else if (fTransport == TR_USB2) {
#ifdef HAVE_USB
      unsigned int base_addr;
      int i;

      if (usb2_buffer == NULL)
         usb2_buffer = (unsigned char *) malloc(USB2_BUFFER_SIZE);
      assert(usb2_buffer);

      /* only accept even address and number of bytes */
      assert(addr % 2 == 0);
      assert(size % 2 == 0);

      /* check for maximum size */
      assert(size <= USB2_BUFFER_SIZE - 10);

      if (type == T_CTRL)
         base_addr = USB2_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr = USB2_STATUS_OFFSET;
      else if (type == T_FIFO)
         base_addr = USB2_FIFO_OFFSET;
      else if (type == T_RAM)
         base_addr = USB2_RAM_OFFSET;
      else
         base_addr = 0;

      if (type != T_RAM && size == 2) {
         /* word swapping: first 16 bit sit at upper address */
         if ((addr % 4) == 0)
            addr = addr + 2;
         else
            addr = addr - 2;
      }

      addr += base_addr;

      usb2_buffer[0] = USB2_CMD_WRITE;
      usb2_buffer[1] = 0;

      usb2_buffer[2] = (addr >> 0) & 0xFF;
      usb2_buffer[3] = (addr >> 8) & 0xFF;
      usb2_buffer[4] = (addr >> 16) & 0xFF;
      usb2_buffer[5] = (addr >> 24) & 0xFF;

      usb2_buffer[6] = (size >> 0) & 0xFF;
      usb2_buffer[7] = (size >> 8) & 0xFF;
      usb2_buffer[8] = (size >> 16) & 0xFF;
      usb2_buffer[9] = (size >> 24) & 0xFF;

      for (i = 0; i < size; i++)
         usb2_buffer[10 + i] = *((unsigned char *) data + i);

      i = musb_write(fUsbInterface, 4, usb2_buffer, 10 + size, USB_TIMEOUT);
      if (i != 10 + size)
         printf("musb_write error: %d\n", i);

#ifdef USE_DRS_MUTEX
      s_drsMutex->Unlock();
#endif
      return i;
#endif                          // HAVE_USB
   }

#ifdef USE_DRS_MUTEX
   s_drsMutex->Unlock();
#endif
   return 0;
}

/*------------------------------------------------------------------*/

/* Generic read function accessing VME or USB */

int DRSBoard::Read(int type, void *data, unsigned int addr, int size)
{
#ifdef USE_DRS_MUTEX
   if (!s_drsMutex) {
      s_drsMutex = new wxMutex();
      assert(s_drsMutex);
   }
   s_drsMutex->Lock();
#endif

   memset(data, 0, size);
 
   if (fTransport == TR_VME) {

#ifdef HAVE_VME
      unsigned int base_addr;
      int n, i;

      base_addr = fBaseAddress;

      if (type == T_CTRL)
         base_addr += PMC_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr += PMC_STATUS_OFFSET;
      else if (type == T_RAM)
         base_addr += PMC_RAM_OFFSET;
      else if (type == T_FIFO)
         base_addr += PMC_FIFO_OFFSET;

      mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);

      n = 0;
      if (size == 1) {
         /* 8-bit read access */
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D8);
         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
      } else if (size == 2) {
         /* 16-bit read access */
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
      } else {
         mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);

         //mvme_set_blt(fVmeInterface, MVME_BLT_NONE); // pseudo block transfer
         mvme_set_blt(fVmeInterface, MVME_BLT_2EVME);   // 2eVME if implemented
         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
         while (n != size) {
            printf("Only read %d out of %d, retry with %d: ", n, size, size - n);
            i = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data) + n / 4, base_addr + addr + n,
                          size - n);
            printf("read %d\n", i);
            if (i == 0) {
               printf("Error reading VME\n");
               return n;
            }
            n += i;
         }

         //for (i = 0; i < size; i += 4)
         //   mvme_read(fVmeInterface, (mvme_locaddr_t *)((char *)data+i), base_addr + addr+i, 4);
      }

#ifdef USE_DRS_MUTEX
      s_drsMutex->Unlock();
#endif

      return n;

#endif                          // HAVE_VME
   } else if (fTransport == TR_USB) {
#ifdef HAVE_USB
      unsigned char buffer[64];
      unsigned int base_addr;
      int i, j, ret, n;

      if (type == T_CTRL)
         base_addr = USB_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr = USB_STATUS_OFFSET;
      else if (type == T_RAM)
         base_addr = USB_RAM_OFFSET;
      else
         assert(0);             // FIFO not implemented

      if (type != T_RAM) {

         /*---- register access ----*/

         if (size == 2) {
            /* word swapping: first 16 bit sit at uppder address */
            if ((addr % 4) == 0)
               addr = addr + 2;
            else
               addr = addr - 2;
         }

         buffer[0] = USB_CMD_READ;
         buffer[1] = base_addr + addr;
         buffer[2] = size;

         musb_write(fUsbInterface, 2, buffer, 2 + size, USB_TIMEOUT);
         i = musb_read(fUsbInterface, 1, data, size, USB_TIMEOUT);

#ifdef USE_DRS_MUTEX
         s_drsMutex->Unlock();
#endif
         if (i != size)
            return 0;

         return size;
      } else {

         /*---- RAM access ----*/

         /* in RAM mode, only the 2048-byte page can be selected */
         buffer[0] = USB_CMD_ADDR;
         buffer[1] = base_addr + (addr >> 11);
         musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);

         /* receive data in 60-byte packets */
         for (i = 0; i <= (size - 1) / 60; i++) {
            n = size - i * 60;
            if (n > 60)
               n = 60;
            buffer[0] = USB_CMD_READ12;
            buffer[1] = n;
            musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);

            ret = musb_read(fUsbInterface, 1, buffer, n, USB_TIMEOUT);

            if (ret != n) {
               /* try again */
               ret = musb_read(fUsbInterface, 1, buffer, n, USB_TIMEOUT);
               if (ret != n) {
#ifdef USE_DRS_MUTEX
                  s_drsMutex->Unlock();
#endif
                  return 0;
               }
            }

            for (j = 0; j < ret; j++)
               *((unsigned char *) data + j + i * 60) = buffer[j];
         }

#ifdef USE_DRS_MUTEX
         s_drsMutex->Unlock();
#endif
         return size;
      }
#endif                          // HAVE_USB
   } else if (fTransport == TR_USB2) {
#ifdef HAVE_USB
      unsigned char buffer[10];
      unsigned int base_addr;
      int i;

      /* only accept even address and number of bytes */
      assert(addr % 2 == 0);
      assert(size % 2 == 0);

      /* check for maximum size */
      assert(size <= USB2_BUFFER_SIZE - 10);

      if (type == T_CTRL)
         base_addr = USB2_CTRL_OFFSET;
      else if (type == T_STATUS)
         base_addr = USB2_STATUS_OFFSET;
      else if (type == T_FIFO)
         base_addr = USB2_FIFO_OFFSET;
      else if (type == T_RAM)
         base_addr = USB2_RAM_OFFSET;
      else
         base_addr = 0;

      if (type != T_RAM && size == 2) {
         /* word swapping: first 16 bit sit at upper address */
         if ((addr % 4) == 0)
            addr = addr + 2;
         else
            addr = addr - 2;
      }

      addr += base_addr;

      buffer[0] = USB2_CMD_READ;
      buffer[1] = 0;

      buffer[2] = (addr >> 0) & 0xFF;
      buffer[3] = (addr >> 8) & 0xFF;
      buffer[4] = (addr >> 16) & 0xFF;
      buffer[5] = (addr >> 24) & 0xFF;

      buffer[6] = (size >> 0) & 0xFF;
      buffer[7] = (size >> 8) & 0xFF;
      buffer[8] = (size >> 16) & 0xFF;
      buffer[9] = (size >> 24) & 0xFF;

      i = musb_write(fUsbInterface, 4, buffer, 10, USB_TIMEOUT);
      if (i != 10)
         printf("musb_read error %d\n", i);

      i = musb_read(fUsbInterface, 8, data, size, USB_TIMEOUT);
#ifdef USE_DRS_MUTEX
      s_drsMutex->Unlock();
#endif
      return i;
#endif                          // HAVE_USB
   }

#ifdef USE_DRS_MUTEX
   s_drsMutex->Unlock();
#endif
   return 0;
}

/*------------------------------------------------------------------*/

void DRSBoard::SetLED(int state)
{
   // Set LED state
   if (state)
      fCtrlBits |= BIT_LED;
   else
      fCtrlBits &= ~BIT_LED;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
}

/*------------------------------------------------------------------*/

int DRSBoard::SetChannelConfig(int firstChannel, int lastChannel, int nConfigChannels)
{
   unsigned short d;

   if (lastChannel < 0 || lastChannel > 10) {
      printf("Invalid number of channels: %d (must be between 0 and 10)\n", lastChannel);
      return 0;
   }

   // Set number of channels
   if (fDRSType == 2) {
      // register must contain last channel to read out starting from 9
      d = 9 - lastChannel;
      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);
   } else if (fDRSType == 3) {
      // upper four bits of register must contain last channel to read out starting from 9
      d = (firstChannel << 4) | lastChannel;
      Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);

      // set bit pattern for write shift register
      switch (nConfigChannels) {
      case 1:
         d = 0x001;
         break;
      case 2:
         d = 0x041;
         break;
      case 3:
         d = 0x111;
         break;
      case 4:
         d = 0x249;
         break;
      case 6:
         d = 0x555;
         break;
      case 12:
         d = 0xFFF;
         break;
      default:
         printf("Invalid channel configuration\n");
         return 0;
      }
      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);
   } else if (fDRSType == 4) {
     
      int oldMultiBuffer = fMultiBuffer;

      // make sure FPGA state machine is idle
      if (fHasMultiBuffer) {
         SetMultiBuffer(0);
         if (IsBusy()) {
            SoftTrigger();
            while (IsBusy());
         }
      }

      if (fBoardType == 6) {
         // determined channel readout mode A/C[even/odd], B/D[even/odd] or A/B/C/D
         fReadoutChannelConfig = firstChannel;
         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
         d = (d & 0xFF00) | firstChannel; // keep higher 8 bits which are ADClkPhase
         Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);
      } else {
         // upper four bits of register must contain last channel to read out starting from 9
         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
         d = (d & 0xFF00) | (firstChannel << 4) | lastChannel; // keep higher 8 bits which are ADClkPhase
         Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);
      }

      // set bit pattern for write shift register
      fChannelConfig = 0;
      switch (nConfigChannels) {
      case 1:
         fChannelConfig = 0x01;
         fChannelCascading = 8;
         break;
      case 2:
         fChannelConfig = 0x11;
         fChannelCascading = 4;
         break;
      case 4:
         fChannelConfig = 0x55;
         fChannelCascading = 2;
         break;
      case 8:
         fChannelConfig = 0xFF;
         fChannelCascading = 1;
         break;
      default:
         printf("Invalid channel configuration\n");
         return 0;
      }
      d = fChannelConfig | (fDominoMode << 8) | (1 << 9) | (fWSRLoop << 10) | (0xF8 << 8);

      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);

      fChannelDepth = fChannelCascading * (fDecimation ? kNumberOfBins/2 : kNumberOfBins);

      if (fHasMultiBuffer && oldMultiBuffer) {
         Reinit(); // set WP=0
         SetMultiBuffer(oldMultiBuffer);
         SetMultiBufferRP(0);
      }
   }

   if (fBoardType == 6) {
      if (fReadoutChannelConfig == 7)
         fNumberOfReadoutChannels = 9;
      else
         fNumberOfReadoutChannels = 5;
   } else {
      fNumberOfReadoutChannels = lastChannel - firstChannel + 1;
   }

   return 1;
}

/*------------------------------------------------------------------*/

void DRSBoard::SetNumberOfChannels(int nChannels)
{
   SetChannelConfig(0, nChannels - 1, 12);
}

/*------------------------------------------------------------------*/

void DRSBoard::SetADCClkPhase(int phase, bool invert)
{
   unsigned short d = 0;

   /* Set the clock phase of the ADC via the variable phase shift
      in the Xilinx DCM. One unit is equal to the clock period / 256,
      so at 30 MHz this is about 130ps. The possible range at 30 MHz
      is -87 ... +87 */


   // keep lower 8 bits which are the channel mode
   Read(T_CTRL, &d, REG_ADCCLK_PHASE, 2);
   d = (d & 0x00FF) | (phase << 8);
   Write(T_CTRL, REG_ADCCLK_PHASE, &d, 2);

   if (invert)
      fCtrlBits |= BIT_ADCCLK_INVERT;
   else
      fCtrlBits &= ~BIT_ADCCLK_INVERT;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   fADCClkPhase = phase;
   fADCClkInvert = invert;
}

/*------------------------------------------------------------------*/

void DRSBoard::SetWarmup(unsigned int microseconds)
{
   /* Set the "warmup" time. When starting the domino wave, the DRS4
      chip together with its power supply need some time to stabilize
      before high resolution data can be taken (jumping baseline
      problem). This sets the time in ticks of 900ns before triggers
      are accepted */


   unsigned short ticks;

   if (microseconds == 0)
      ticks = 0;
   else
      ticks = (unsigned short) (microseconds / 0.9 + 0.5) - 1;
   Write(T_CTRL, REG_WARMUP, &ticks, 2);
}

/*------------------------------------------------------------------*/

void DRSBoard::SetCooldown(unsigned int microseconds)
{
   /* Set the "cooldown" time. When stopping the domino wave, the
      power supply needs some time to stabilize before high resolution
      data can read out (slanted baseline problem). This sets the
      time in ticks of 900 ns before the readout is started */


   unsigned short ticks;

   ticks = (unsigned short) (microseconds / 0.9 + 0.5) - 1;
   Write(T_CTRL, REG_COOLDOWN, &ticks, 2);
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDAC(unsigned char channel, double value)
{
   // Set DAC value
   unsigned short d;

   /* normalize to 2.5V for 16 bit */
   if (value < 0)
      value = 0;
   if (value > 2.5)
      value = 2.5;
   d = static_cast < unsigned short >(value / 2.5 * 0xFFFF + 0.5);

   Write(T_CTRL, REG_DAC_OFS + (channel * 2), &d, 2);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::ReadDAC(unsigned char channel, double *value)
{
   // Readback DAC value from control register
   unsigned char buffer[2];

   /* map 0->1, 1->0, 2->3, 3->2, etc. */
   //ofs = channel + 1 - 2*(channel % 2);

   Read(T_CTRL, buffer, REG_DAC_OFS + (channel * 2), 2);

   /* normalize to 2.5V for 16 bit */
   *value = 2.5 * (buffer[0] + (buffer[1] << 8)) / 0xFFFF;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetRegulationDAC(double *value)
{
   // Get DAC value from status register (-> freq. regulation)
   unsigned char buffer[2];

   if (fBoardType == 1)
      Read(T_STATUS, buffer, REG_RDAC3, 2);
   else if (fBoardType == 2 || fBoardType == 3 || fBoardType == 4)
      Read(T_STATUS, buffer, REG_RDAC1, 2);
   else
     memset(buffer, 0, sizeof(buffer));

   /* normalize to 2.5V for 16 bit */
   *value = 2.5 * (buffer[0] + (buffer[1] << 8)) / 0xFFFF;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::StartDomino()
{
   // Start domino sampling
   fCtrlBits |= BIT_START_TRIG;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_START_TRIG;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::Reinit()
{
   // Stop domino sampling
   // reset readout state machine
   // reset FIFO counters
   fCtrlBits |= BIT_REINIT_TRIG;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_REINIT_TRIG;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::Init()
{
   // Init FPGA on USB2 board
   InitFPGA();

   // Turn off multi-buffer mode to avoid immediate startup
   SetMultiBuffer(0);

   // Reinitialize
   fCtrlBits |= BIT_REINIT_TRIG;        // reset readout state machine
   if (fDRSType == 2)
      fCtrlBits &= ~BIT_FREQ_AUTO_ADJ;  // turn auto. freq regul. off
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_REINIT_TRIG;

   if (fBoardType == 1) {
      // set max. domino speed
      SetDAC(fDAC_DRA, 2.5);
      SetDAC(fDAC_DSA, 2.5);
      SetDAC(fDAC_DRB, 2.5);
      SetDAC(fDAC_DSB, 2.5);
      // set readout offset
      SetDAC(fDAC_COFSA, 0.9);
      SetDAC(fDAC_COFSB, 0.9);
      SetDAC(fDAC_TLEVEL, 1.7);
   } else if (fBoardType == 2 || fBoardType == 3) {
      // set max. domino speed
      SetDAC(fDAC_DSA, 2.5);
      SetDAC(fDAC_DSB, 2.5);

      // set readout offset
      SetDAC(fDAC_COFS, 0.9);
      SetDAC(fDAC_TLEVEL, 1.7);
      SetDAC(fDAC_ADCOFS, 1.7); // 1.7 for DC coupling, 1.25 for AC
      SetDAC(fDAC_CLKOFS, 1);
   } else if (fBoardType == 4) {
      // set max. domino speed
      SetDAC(fDAC_DSA, 2.5);
      SetDAC(fDAC_DSB, 2.5);

      // set readout offset
      SetDAC(fDAC_ROFS_1, 1.25);        // LVDS level
      //SetDAC(fDAC_ROFS_2, 0.85);   // linear range  0.1V ... 1.1V
      SetDAC(fDAC_ROFS_2, 1.05);        // differential input from Lecce splitter

      SetDAC(fDAC_ADCOFS, 1.25);
      SetDAC(fDAC_ACALIB, 0.5);
      SetDAC(fDAC_INOFS, 0.6);
      SetDAC(fDAC_BIAS, 0.70);  // a bit above the internal bias of 0.68V
   
   } else if (fBoardType == 5) {
      // DRS4 USB Evaluation Board 1.1 + 2.0

      // set max. domino speed
      SetDAC(fDAC_DSA, 2.5);

      // set readout offset
      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
      fRange = 0;
      SetDAC(fDAC_ROFS_1, fROFS);

      // set common mode offset
      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
      SetDAC(fDAC_CMOFS, fCommonMode);

      // calibration voltage
      SetDAC(fDAC_CALP, fCommonMode);
      SetDAC(fDAC_CALN, fCommonMode);

      // OUT- offset
      SetDAC(fDAC_ONOFS, 1.25);

      SetDAC(fDAC_BIAS, 0.70);

   } else if (fBoardType == 6) {
      // DRS4 Mezzanine Board 1.0
     
      // set readout offset
      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
      fRange = 0;
      SetDAC(fDAC_ROFS_1, fROFS);

      // set common mode offset
      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
      SetDAC(fDAC_CMOFSP, fCommonMode);
      SetDAC(fDAC_CMOFSN, fCommonMode);

      // calibration voltage
      SetDAC(fDAC_CALN, fCommonMode);
      SetDAC(fDAC_CALP, fCommonMode);

      // OUT- offset
      SetDAC(fDAC_ONOFS, 1.25);

      SetDAC(fDAC_BIAS, 0.70);
   } else if (fBoardType == 7) {
      // DRS4 USB Evaluation 3.0

      // set max. domino speed
      SetDAC(fDAC_DSA, 2.5);

      // set readout offset
      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
      fRange = 0;
      SetDAC(fDAC_ROFS_1, fROFS);

      // set common mode for THS4508
      SetDAC(fDAC_CMOFS, 2.4);

      // calibration voltage
      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
      SetDAC(fDAC_CALP, fCommonMode);
      SetDAC(fDAC_CALN, fCommonMode);

      // OUT- offset
      SetDAC(fDAC_ONOFS, 1.25);

      SetDAC(fDAC_BIAS, 0.70);
   } else if (fBoardType == 8 || fBoardType == 9) {
      // DRS4 USB Evaluation 4.0

      // set readout offset
      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
      fRange = 0;
      SetDAC(fDAC_ROFS_1, fROFS);

      // calibration voltage
      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
      SetDAC(fDAC_CALP, fCommonMode);
      SetDAC(fDAC_CALN, fCommonMode);

      SetDAC(fDAC_BIAS, 0.70);
   }

   /* set default number of channels per chip */
   if (fDRSType == 4) {
      if (fTransport == TR_USB2)
         SetChannelConfig(0, fNumberOfReadoutChannels - 1, 8);
      else
         SetChannelConfig(7, fNumberOfReadoutChannels - 1, 8);
   } else
      SetChannelConfig(0, fNumberOfReadoutChannels - 1, 12);

   // set ADC clock phase
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      fADCClkPhase = 0;
      fADCClkInvert = 0;
   } else if (fBoardType == 6) {
      fADCClkPhase = 65;
      fADCClkInvert = 0;
   }

   // default settings
   fMultiBuffer = 0;
   fNMultiBuffer = 0;
   fDominoMode = 1;
   fReadoutMode = 1;
   fReadPointer = 0;
   fTriggerEnable1 = 0;
   fTriggerEnable2 = 0;
   fTriggerSource = 0;
   fTriggerDelay = 0;
   fTriggerDelayNs = 0;
   fSyncDelay = 0;
   fNominalFrequency = 1;
   fDominoActive = 1;

   // load calibration from EEPROM
   ReadCalibration();

   // get some settings from hardware
   fRange = GetCalibratedInputRange();
   if (fRange < 0 || fRange > 0.5)
      fRange = 0;
   fNominalFrequency = GetCalibratedFrequency();
   if (fNominalFrequency < 0.1 || fNominalFrequency > 6)
      fNominalFrequency = 1;

   
   if (fHasMultiBuffer) {
      SetMultiBuffer(fMultiBuffer);
      SetMultiBufferRP(fReadPointer);
   }
   SetDominoMode(fDominoMode);
   SetReadoutMode(fReadoutMode);
   EnableTrigger(fTriggerEnable1, fTriggerEnable2);
   SetTriggerSource(fTriggerSource);
   SetTriggerDelayPercent(0);
   SetSyncDelay(fSyncDelay);
   SetDominoActive(fDominoActive);
   SetFrequency(fNominalFrequency, true);
   SetInputRange(fRange);
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
      SelectClockSource(0); // FPGA clock
   if (fBoardType == 6) {
      SetADCClkPhase(fADCClkPhase, fADCClkInvert);
      SetWarmup(0);
      SetCooldown(100);
      SetDecimation(0);
   }

   // disable calibration signals
   EnableAcal(0, 0);
   SetCalibTiming(0, 0);
   EnableTcal(0);

   // got to idle state
   Reinit();

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDominoMode(unsigned char mode)
{
   // Set domino mode
   // mode == 0: single sweep
   // mode == 1: run continously
   //
   fDominoMode = mode;

   if (fDRSType == 4) {
      unsigned short d;
      Read(T_CTRL, &d, REG_CONFIG, 2);
      fChannelConfig = d & 0xFF;

      d = fChannelConfig | (fDominoMode << 8) | (1 << 9) | (fWSRLoop << 10) | (0xF8 << 8);
      Write(T_CTRL, REG_CONFIG, &d, 2);
   } else {
      if (mode)
         fCtrlBits |= BIT_DMODE;
      else
         fCtrlBits &= ~BIT_DMODE;

      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDominoActive(unsigned char mode)
{
   // Set domino activity
   // mode == 0: stop during readout
   // mode == 1: keep domino wave running
   //
   fDominoActive = mode;
   if (mode)
      fCtrlBits |= BIT_DACTIVE;
   else
      fCtrlBits &= ~BIT_DACTIVE;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetReadoutMode(unsigned char mode)
{
   // Set readout mode
   // mode == 0: start from first bin
   // mode == 1: start from domino stop
   //
   fReadoutMode = mode;
   if (mode)
      fCtrlBits |= BIT_READOUT_MODE;
   else
      fCtrlBits &= ~BIT_READOUT_MODE;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SoftTrigger(void)
{
   // Send a software trigger
   fCtrlBits |= BIT_SOFT_TRIG;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_SOFT_TRIG;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::EnableTrigger(int flag1, int flag2)
{
   // Enable external trigger
   fTriggerEnable1 = flag1;
   fTriggerEnable2 = flag2;
   if (flag1)
      fCtrlBits |= BIT_ENABLE_TRIGGER1;
   else
      fCtrlBits &= ~BIT_ENABLE_TRIGGER1;

   if (flag2)
      fCtrlBits |= BIT_ENABLE_TRIGGER2;
   else
      fCtrlBits &= ~BIT_ENABLE_TRIGGER2;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDelayedTrigger(int flag)
{
   // Select delayed trigger from trigger bus
   if (flag)
      fCtrlBits |= BIT_TRIGGER_DELAYED;
   else
      fCtrlBits &= ~BIT_TRIGGER_DELAYED;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetTriggerPolarity(bool negative)
{
   if (fBoardType == 5 || fBoardType == 7) {
      fTcalLevel = negative;
     
      if (negative)
         fCtrlBits |= BIT_NEG_TRIGGER;
      else
         fCtrlBits &= ~BIT_NEG_TRIGGER;
     
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
     
      return 1;
   } else if (fBoardType == 8 || fBoardType == 9) {
      fTcalLevel = negative;
     
      if (negative)
         fCtrlBits |= BIT_NEG_TRIGGER;
      else
         fCtrlBits &= ~BIT_NEG_TRIGGER;
     
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
      return 1;
   }
   
   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetTriggerLevel(double voltage)
{
   if (fBoardType == 5 || fBoardType == 7) {
      return SetDAC(fDAC_TLEVEL, voltage/2 + 0.8);
   } else if (fBoardType == 8 || fBoardType == 9) {
      SetIndividualTriggerLevel(0, voltage);
      SetIndividualTriggerLevel(1, voltage);
      SetIndividualTriggerLevel(2, voltage);
      SetIndividualTriggerLevel(3, voltage);
      return 1;
   }

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetIndividualTriggerLevel(int channel, double voltage)
{
   if (fBoardType == 8 || fBoardType == 9) {
      switch (channel) {
            case 0: SetDAC(fDAC_TLEVEL1, voltage/2 + 0.8); break;
            case 1: SetDAC(fDAC_TLEVEL2, voltage/2 + 0.8); break;
            case 2: SetDAC(fDAC_TLEVEL3, voltage/2 + 0.8); break;
            case 3: SetDAC(fDAC_TLEVEL4, voltage/2 + 0.8); break;
         default: return -1;
      }
   }
   
   return 0;
}

/*------------------------------------------------------------------*/

#define LUT_DELAY_S3_8 6.2  // Spartan 3 Octal LUTs 2 GSPS
#define LUT_DELAY_S3_4 2.1  // Spartan 3 Quad LUTs
#define LUT_DELAY_V2_8 4.6  // Virtex PRO II Octal LUTs
#define LUT_DELAY_V2_4 2.3  // Virtex PRO II Quad LUTs


int DRSBoard::SetTriggerDelayPercent(int delay)
/* set trigger delay in percent 0..100 */
{
   short ticks, reg;
   fTriggerDelay = delay;

   if (fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      // convert delay (0..100) into ticks
      ticks = (unsigned short) (delay/100.0*255+0.5);
      if (ticks > 255)
         ticks = 255;
      if (ticks < 0)
         ticks = 0;

      // convert delay into ns
      if (fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
         if (fFirmwareVersion >= 17147)
            fTriggerDelayNs = ticks * LUT_DELAY_S3_8;
         else
            fTriggerDelayNs = ticks * LUT_DELAY_S3_4;
      } else {
         if (fFirmwareVersion >= 17382)
            fTriggerDelayNs = ticks * LUT_DELAY_V2_8;
         else
            fTriggerDelayNs = ticks * LUT_DELAY_V2_4;
      }

      // adjust for fixed delay, measured and approximated experimentally
      fTriggerDelayNs += 23.5 + 28.2/fNominalFrequency;

      Read(T_CTRL, &reg, REG_TRG_DELAY, 2);
      reg = (reg & 0xFF00) | ticks;
      Write(T_CTRL, REG_TRG_DELAY, &ticks, 2);

      return 1;
   }

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetTriggerDelayNs(int delay)
/* set trigger delay in nanoseconds */
{
   short ticks, reg;
   fTriggerDelayNs = delay;

   if (fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {

      // convert delay in ns into ticks
      if (fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
         if (fFirmwareVersion >= 17147)
            ticks = (short int)(delay / LUT_DELAY_S3_8 + 0.5);
         else
            ticks = (short int)(delay / LUT_DELAY_S3_4 + 0.5);
      } else {
         if (fFirmwareVersion >= 17382)
            ticks = (short int)(delay / LUT_DELAY_V2_8 + 0.5);
         else
            ticks = (short int)(delay / LUT_DELAY_V2_4 + 0.5);
      }

      if (ticks > 255)
         ticks = 255;
      if (ticks < 0)
         ticks = 0;

      fTriggerDelay = ticks / 255 * 100;

      Read(T_CTRL, &reg, REG_TRG_DELAY, 2);
      reg = (reg & 0xFF00) | ticks;
      Write(T_CTRL, REG_TRG_DELAY, &ticks, 2);

      return 1;
   }

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetSyncDelay(int ticks)
{
   short int reg;

   if (fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      Read(T_CTRL, &reg, REG_TRG_DELAY, 2);
      reg = (reg & 0xFF) | (ticks << 8);
      Write(T_CTRL, REG_TRG_DELAY, &reg, 2);

      return 1;
   }

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetTriggerSource(int source)
{
   short int reg;

   fTriggerSource = source;
   if (fBoardType == 5 || fBoardType == 7) {
      // Set trigger source
      // 0=CH1, 1=CH2, 2=CH3, 3=CH4
      if (source & 1)
         fCtrlBits |= BIT_TR_SOURCE1;
      else
         fCtrlBits &= ~BIT_TR_SOURCE1;
      if (source & 2)
         fCtrlBits |= BIT_TR_SOURCE2;
      else
         fCtrlBits &= ~BIT_TR_SOURCE2;

      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   } else if (fBoardType == 8 || fBoardType == 9) {
      // Set trigger configuration
      // OR  Bit0=CH1, Bit1=CH2,  Bit2=CH3,  Bit3=CH4,  Bit4=EXT
      // AND Bit8=CH1, Bit9=CH2, Bit10=CH3, Bit11=CH4, Bit12=EXT
      // TRANSP Bit15
      reg = (unsigned short) source;
      Write(T_CTRL, REG_TRG_CONFIG, &reg, 2);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDelayedStart(int flag)
{
   // Enable external trigger
   fDelayedStart = flag;
   if (flag)
      fCtrlBits |= BIT_DELAYED_START;
   else
      fCtrlBits &= ~BIT_DELAYED_START;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetTranspMode(int flag)
{
   // Enable/disable transparent mode
   fTranspMode = flag;
   if (flag)
      fCtrlBits |= BIT_TRANSP_MODE;
   else
      fCtrlBits &= ~BIT_TRANSP_MODE;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetStandbyMode(int flag)
{
   // Enable/disable standby mode
   fTranspMode = flag;
   if (flag)
      fCtrlBits |= BIT_STANDBY_MODE;
   else
      fCtrlBits &= ~BIT_STANDBY_MODE;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetDecimation(int flag)
{
   // Drop every odd sample
   fDecimation = flag;
   if (flag)
      fCtrlBits |= BIT_DECIMATION;
   else
      fCtrlBits &= ~BIT_DECIMATION;

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   // Calculate channel depth
   fChannelDepth = fChannelCascading * (fDecimation ? kNumberOfBins/2 : kNumberOfBins);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::IsBusy()
{
   // Get running flag
   unsigned int status;

   Read(T_STATUS, &status, REG_STATUS, 4);
   return (status & BIT_RUNNING) > 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::IsEventAvailable()
{
   if (!fMultiBuffer)
      return !IsBusy();

   return GetMultiBufferWP() != fReadPointer;
}

/*------------------------------------------------------------------*/

int DRSBoard::IsPLLLocked()
{
   // Get running flag
   unsigned int status;

   Read(T_STATUS, &status, REG_STATUS, 4);
   if (GetBoardType() == 6)
      return ((status >> 1) & 0x0F) == 0x0F;
   return (status & BIT_PLL_LOCKED0) > 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::IsLMKLocked()
{
   // Get running flag
   unsigned int status;

   Read(T_STATUS, &status, REG_STATUS, 4);
   if (GetBoardType() == 6)
      return (status & BIT_LMK_LOCKED) > 0;
   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::IsNewFreq(unsigned char chipIndex)
{
   unsigned int status;

   Read(T_STATUS, &status, REG_STATUS, 4);
   if (chipIndex == 0)
      return (status & BIT_NEW_FREQ1) > 0;
   return (status & BIT_NEW_FREQ2) > 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::ReadFrequency(unsigned char chipIndex, double *f)
{
   if (fDRSType == 4) {

      if (fBoardType == 6) {
         *f = fNominalFrequency;
         return 1;
      }

      unsigned short ticks;

      Read(T_CTRL, &ticks, REG_FREQ_SET, 2);
      ticks += 2;

      /* convert rounded ticks back to frequency */
      if (ticks > 2)
         *f = 1.024 / ticks * fRefClock;
      else
         *f = 0;
   } else {
      // Read domino sampling frequency
      unsigned char buffer[2];

      if (chipIndex == 0)
         Read(T_STATUS, buffer, REG_FREQ1, 2);
      else
         Read(T_STATUS, buffer, REG_FREQ2, 2);

      *f = (static_cast < unsigned int >(buffer[1]) << 8) +buffer[0];

      /* convert counts to frequency */
      if (*f != 0)
         *f = 1024 * 200 * (32.768E6 * 4) / (*f) / 1E9;
   }

   return 1;
}

/*------------------------------------------------------------------*/

double DRSBoard::VoltToFreq(double volt)
{
   if (fDRSType == 3) {
      if (volt <= 1.2001)
         return (volt - 0.6) / 0.2;
      else
         return 0.73 / 0.28 + sqrt((0.73 / 0.28) * (0.73 / 0.28) - 2.2 / 0.14 + volt / 0.14);
   } else
      return (volt - 0.5) / 0.2;
}

/*------------------------------------------------------------------*/

double DRSBoard::FreqToVolt(double freq)
{
   if (fDRSType == 3) {
      if (freq <= 3)
         return 0.6 + 0.2 * freq;
      else
         return 2.2 - 0.73 * freq + 0.14 * freq * freq;
   } else
      return 0.55 + 0.25 * freq;
}

/*------------------------------------------------------------------*/

int DRSBoard::ConfigureLMK(double sampFreq, bool freqChange, int calFreq, int calPhase)
{
   unsigned int data[] = { 0x80000100,   // RESET=1
                           0x0007FF00,   // CLKOUT0: EN=1, DIV=FF (=510) MUX=Div&Delay
                           0x00000101,   // CLKOUT1: Disabled
                           0x0082000B,   // R11: DIV4=0
                           0x028780AD,   // R13: VCO settings
                           0x0830000E,   // R14: PLL settings
                           0xC000000F }; // R15: PLL settings

   /* calculate dividing ratio */
   int divider, vco_divider, n_counter, r_counter;
   unsigned int status;
   double clk, vco;

   if (fTransport == TR_USB2) {
      /* 30 MHz clock */
      data[4]     = 0x028780AD;  // R13 according to CodeLoader 4
      clk         = 30;
      if (sampFreq < 1) {
         r_counter   = 1;
         vco_divider = 8;
         n_counter   = 5;
      } else {
         r_counter   = 1;
         vco_divider = 5;
         n_counter   = 8;
      }
   } else {
     
      if (fCtrlBits & BIT_REFCLK_SOURCE) {
         /* 19.44 MHz clock */
         data[4]     = 0x0284C0AD;  // R13 according to CodeLoader 4
         clk         = 19.44; // global clock through P2

         r_counter   = 2;
         vco_divider = 8;
         n_counter   = 16;
      } else {
         /* 33 MHz clock */
         data[4]     = 0x028840AD;  // R13 according to CodeLoader 4
         clk         = 33; // FPGA clock

         r_counter   = 2;
         vco_divider = 8;
         n_counter   = 9;
      }
   }

   vco = clk/r_counter*n_counter*vco_divider;
   divider = (int) ((vco / vco_divider / (sampFreq/2.048) / 2.0) + 0.5);

   /* return exact frequency */
   fNominalFrequency = vco/vco_divider/(divider*2)*2.048;

   /* return exact timing calibration frequency */
   fTCALFrequency = vco/vco_divider;

   /* change registers accordingly */
   data[1] = 0x00070000 | (divider << 8);   // R0
   data[5] = 0x0830000E | (r_counter << 8); // R14
   data[6] = 0xC000000F | (n_counter << 8) | (vco_divider << 26); // R15

   /* enable TCA output if requested */
   if (calFreq) {
      if (calFreq == 1)
         data[2] = 0x00050001 | (  1<<8) ; // 148.5 MHz  (33 MHz PLL)
                                           // 150 MHz    (30 MHz PLL)
                                           // 155.52 MHz (19.44 MHz PLL)
      else if (calFreq == 2) {
         data[2] = 0x00070001 | (  4<<8);  // above values divided by 8
         fTCALFrequency /= 8;
      } else if (calFreq == 3) {
         data[2] = 0x00070001 | (255<<8);  // above values divided by 510
         fTCALFrequency /= 510;
      }
   }

   /* set delay to adjsut phase */
   if (calPhase > 0)
      data[2] |= (( calPhase & 0x0F) << 4);
   else if (calPhase < 0)
      data[1] |= ((-calPhase & 0x0F) << 4);

   if (freqChange) {
      /* set all registers */    
      for (int i=0 ; i<(int)(sizeof(data)/sizeof(unsigned int)) ; i++) {
         Write(T_CTRL, REG_LMK_LSB, &data[i], 2);
         Write(T_CTRL, REG_LMK_MSB, ((char *)&data[i])+2, 2);
         // poll on serial_busy flag
         for (int j=0 ; j<100 ; j++) {
            Read(T_STATUS, &status, REG_STATUS, 4);
            if ((status & BIT_SERIAL_BUSY) == 0)
               break;
         }
      }
   } else {
      /* only enable/disable timing calibration frequency */
      Write(T_CTRL, REG_LMK_LSB, &data[1], 2);
      Write(T_CTRL, REG_LMK_MSB, ((char *)&data[1])+2, 2);

      /* poll on serial_busy flag */
      for (int j=0 ; j<100 ; j++) {
         Read(T_STATUS, &status, REG_STATUS, 4);
         if ((status & BIT_SERIAL_BUSY) == 0)
            break;
      }

      Write(T_CTRL, REG_LMK_LSB, &data[2], 2);
      Write(T_CTRL, REG_LMK_MSB, ((char *)&data[2])+2, 2);

      /* poll on serial_busy flag */
      for (int j=0 ; j<100 ; j++) {
         Read(T_STATUS, &status, REG_STATUS, 4);
         if ((status & BIT_SERIAL_BUSY) == 0)
            break;
      }
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetFrequency(double demand, bool wait)
{
   // Set domino sampling frequency
   double freq, voltage, delta_voltage;
   unsigned short ticks;
   int i, index, timeout;
   int dominoModeSave = fDominoMode;
   int triggerEnableSave1 = fTriggerEnable1;
   int triggerEnableSave2 = fTriggerEnable2;

   if (fDRSType == 4) {
      /* allowed range is 100 MHz to 6 GHz */
      if (demand > 6 || demand < 0.1)
         return 0;

      if (fBoardType == 6) {
         for (i=1 ; i<100 ; i++) {
            ConfigureLMK(demand, true, fTcalFreq, fTcalPhase);
            Sleep(10);
            if (IsLMKLocked())
               return 1;
            printf("Re-start LMK in VME slot %2d %s\n",  
                    (GetSlotNumber() >> 1)+2, ((GetSlotNumber() & 1) == 0) ? "upper" : "lower");
         }
         return 0;
      }

      /* convert frequency in GHz into ticks counted by reference clock */
      if (demand == 0)
         ticks = 0;             // turn off frequency generation
      else
         ticks = static_cast < unsigned short >(1.024 / demand * fRefClock + 0.5);

      ticks -= 2;               // firmware counter need two additional clock cycles
      Write(T_CTRL, REG_FREQ_SET, &ticks, 2);
      ticks += 2;

      /* convert rounded ticks back to frequency */
      if (demand > 0)
         demand = 1.024 / ticks * fRefClock;
      fNominalFrequency = demand;

      /* wait for PLL lock if asked */
      if (wait) {
         StartDomino();
         for (i=0 ; i<1000 ; i++)
            if (GetStatusReg() & BIT_PLL_LOCKED0)
               break;
         SoftTrigger();
         if (i == 1000) {
            printf("PLL did not lock for frequency %lf\n", demand);
            return 0;
         }
      }
   } else {                     // fDRSType == 4
      SetDominoMode(1);
      EnableTrigger(0, 0);
      EnableAcal(0, 0);

      fNominalFrequency = demand;

      /* turn automatic adjustment off */
      fCtrlBits &= ~BIT_FREQ_AUTO_ADJ;

      /* disable external trigger */
      fCtrlBits &= ~BIT_ENABLE_TRIGGER1;
      fCtrlBits &= ~BIT_ENABLE_TRIGGER2;

      /* set start pulse length for future DRSBoard_domino_start() */
      if (fDRSType == 2) {
         if (demand < 0.8)
            fCtrlBits |= BIT_LONG_START_PULSE;
         else
            fCtrlBits &= ~BIT_LONG_START_PULSE;

         Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
      }

      /* stop any running domino wave */
      Reinit();

      /* estimate DAC setting */
      voltage = FreqToVolt(demand);

      SetDAC(fDAC_DSA, voltage);
      SetDAC(fDAC_DSB, voltage);

      /* wait until new DAC value has settled */
      Sleep(10);

      /* restart domino wave */
      StartDomino();

      ticks = static_cast < unsigned short >(1024 * 200 * (32.768E6 * 4) / demand / 1E9);

      /* iterate over both DRS chips */
      for (index = 0; index < 2; index++) {

         /* starting voltage */
         voltage = FreqToVolt(demand);

         for (i = 0; i < 100; i++) {

            /* wait until measurement finished */
            for (timeout = 0; timeout < 1000; timeout++)
               if (IsNewFreq(index))
                  break;

            freq = 0;
            if (timeout == 1000)
               break;

            ReadFrequency(index, &freq);

            delta_voltage = FreqToVolt(demand) - FreqToVolt(freq);

            if (fDebug) {
               if (fabs(freq - demand) < 0.001)
                  printf("CHIP-%d, iter%3d: %1.5lf(%05d) %7.5lf\n", index, i, voltage,
                         static_cast < int >(voltage / 2.5 * 65535 + 0.5), freq);
               else
                  printf("CHIP-%d, iter%3d: %1.5lf(%05d) %7.5lf %+5d\n", index, i, voltage,
                         static_cast < int >(voltage / 2.5 * 65535 + 0.5), freq,
                         static_cast < int >(delta_voltage / 2.5 * 65535 + 0.5));
            }

            if (fabs(freq - demand) < 0.001)
               break;

            voltage += delta_voltage;
            if (voltage > 2.5)
               voltage = 2.5;
            if (voltage < 0)
               voltage = 0;

            if (freq == 0)
               break;

            if (index == 0)
               SetDAC(fDAC_DSA, voltage);
            else
               SetDAC(fDAC_DSB, voltage);

            Sleep(10);
         }
         if (i == 100 || freq == 0 || timeout == 1000) {
            printf("Board %d --> Could not set frequency of CHIP-#%d to %1.3f GHz\n", GetBoardSerialNumber(),
                   index, demand);
            return 0;
         }
      }

      SetDominoMode(dominoModeSave);
      EnableTrigger(triggerEnableSave1, triggerEnableSave2);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::RegulateFrequency(double demand)
{
   // Set frequency regulation
   unsigned short target, target_hi, target_lo;

   if (demand < 0.42 || demand > 5.2)
      return 0;

   fNominalFrequency = demand;

   /* first iterate DAC value from host */
   if (!SetFrequency(demand, true))
      return 0;

   /* convert frequency in GHz into counts for 200 cycles */
   target = static_cast < unsigned short >(1024 * 200 * (32.768E6 * 4) / demand / 1E9);
   target_hi = target + 6;
   target_lo = target - 6;
   Write(T_CTRL, REG_FREQ_SET_HI, &target_hi, 2);
   Write(T_CTRL, REG_FREQ_SET_LO, &target_lo, 2);

   /* turn on regulation */
   fCtrlBits |= BIT_FREQ_AUTO_ADJ;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

   /* optional monitoring code ... */
#if 0
   do {
      double freq;
      unsigned short dac, cnt;

      ReadFrequency(0, &freq);

      if (fBoardType == 1)
         Read(T_STATUS, &dac, REG_RDAC3, 2);
      else if (fBoardType == 2 || fBoardType == 3)
         Read(T_STATUS, &dac, REG_RDAC1, 2);

      Read(T_STATUS, &cnt, REG_FREQ1, 2);

      if (cnt < 65535)
         printf("%5d %5d %5d %1.5lf\n", dac, target, cnt, freq);

      Sleep(500);
   } while (1);
#endif

   return 1;
}

/*------------------------------------------------------------------*/

void DRSBoard::RegisterTest()
{
   // Register test
#define N_REG 8

   int i, n, n_err;
   unsigned int buffer[N_REG], ret[N_REG];

   /* test single register */
   buffer[0] = 0x12345678;
   Write(T_CTRL, 0, buffer, 4);
   memset(ret, 0, sizeof(ret));
   i = Read(T_CTRL, ret, 0, 4);
   while (i != 4)
      printf("Read error single register!\n");

   printf("Reg.0: %08X - %08X\n", buffer[0], ret[0]);

   n_err = 0;
   for (n = 0; n < 100; n++) {
      for (i = 0; i < N_REG; i++)
         buffer[i] = (rand() << 16) | rand();
      Write(T_CTRL, 0, buffer, sizeof(buffer));

      memset(ret, 0, sizeof(ret));
      i = Read(T_CTRL, ret, 0, sizeof(ret));
      while (i != sizeof(ret)) {
         printf("Read error!\n");
         return;
      }

      for (i = 0; i < N_REG; i++) {
         if (n == 0)
            printf("Reg.%d: %08X - %08X\n", i, buffer[i], ret[i]);
         if (buffer[i] != ret[i]) {
            n_err++;
         }
      }
   }

   printf("Register test: %d errors\n", n_err);
}

/*------------------------------------------------------------------*/

int DRSBoard::RAMTest(int flag)
{
#define MAX_N_BYTES  128*1024   // 128 kB

   int i, j, n, bits, n_bytes, n_words, n_dwords;
   unsigned int buffer[MAX_N_BYTES/4], ret[MAX_N_BYTES/4];
   time_t now;

   if (fBoardType == 6 && fTransport == TR_VME) {
      bits = 32;
      n_bytes = 128*1024; // test full 128 kB
      n_words = n_bytes/2;
      n_dwords = n_words/2;
   }  else {
      bits = 24;
      n_words = 9*1024;
      n_bytes = n_words * 2;
      n_dwords = n_words/2;
   }

   if (flag & 1) {
      /* integrety test */
      printf("Buffer size: %d (%1.1lfk)\n", n_words * 2, n_words * 2 / 1024.0);
      if (flag & 1) {
         for (i = 0; i < n_dwords; i++) {
            if (bits == 24)
               buffer[i] = (rand() | rand() << 16) & 0x00FFFFFF;   // random 24-bit values
            else
               buffer[i] = (rand() | rand() << 16);                // random 32-bit values
         }

         Reinit();
         Write(T_RAM, 0, buffer, n_bytes);
         memset(ret, 0, n_bytes);
         Read(T_RAM, ret, 0, n_bytes);
         Reinit();

         for (i = n = 0; i < n_dwords; i++) {
            if (buffer[i] != ret[i]) {
               n++;
            }
            if (i < 10)
               printf("written: %08X   read: %08X\n", buffer[i], ret[i]);
         }

         printf("RAM test: %d errors\n", n);
      }
   }

   /* speed test */
   if (flag & 2) {
      /* read continously to determine speed */
      time(&now);
      while (now == time(NULL));
      time(&now);
      i = n = 0;
      do {
         memset(ret, 0, n_bytes);

         for (j = 0; j < 10; j++) {
            Read(T_RAM, ret, 0, n_bytes);
            i += n_bytes;
         }

         if (flag & 1) {
            for (j = 0; j < n_dwords; j++)
               if (buffer[j] != ret[j])
                  n++;
         }

         if (now != time(NULL)) {
            if (flag & 1)
               printf("%d read/sec, %1.2lf MB/sec, %d errors\n", static_cast < int >(i / n_bytes),
                      i / 1024.0 / 1024.0, n);
            else
               printf("%d read/sec, %1.2lf MB/sec\n", static_cast < int >(i / n_bytes),
                      i / 1024.0 / 1024.0);
            time(&now);
            i = 0;
         }

         if (drs_kbhit())
            break;

      } while (1);

      while (drs_kbhit())
         getch();
   }

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::ChipTest()
{
   int i, j, t;
   double freq, old_freq, min, max, mean, std;
   float  waveform[1024];

   Init();
   SetChannelConfig(0, 8, 8);
   SetDominoMode(1);
   SetReadoutMode(1);
   SetDominoActive(1);
   SetTranspMode(0);
   EnableTrigger(0, 0);
   EnableTcal(1, 0);
   SelectClockSource(0);
   EnableAcal(1, 0);

   /* test 1 GHz */
   SetFrequency(1, true);
   StartDomino();
   Sleep(100);
   if (!(GetStatusReg() & BIT_PLL_LOCKED0)) {
      puts("PLL did not lock at 1 GHz");
      return 0;
   }

   /* test up to 6 GHz */
   for (freq = 5 ; freq < 6 ; freq += 0.1) {
      SetFrequency(freq, false);
      Sleep(10);
      if (!(GetStatusReg() & BIT_PLL_LOCKED0)) {
         printf("Max. frequency is %1.1lf GHz\n", old_freq);
         break;
      }
      ReadFrequency(0, &old_freq);
   }

   /* read and check at 0 calibration voltage */
   SetFrequency(5, true);
   Sleep(10);
   SoftTrigger();
   while (IsBusy());
   TransferWaves(0, 8);

   for (i=0 ; i<8 ; i++) {
      t = GetStopCell(0);
      GetWave(0, i, waveform, false, t, 0, false);
      for (j=0 ; j<1024; j++)
         if (waveform[j] < -100 || waveform[j] > 100) {
            if (j<5) {
               /* skip this cells */
            } else {
               printf("Cell error on channel %d, cell %d: %1.1lf mV instead 0 mV\n", i, j, waveform[j]);
               return 0;
            }
         }
   }

   /* read and check at +0.5V calibration voltage */
   EnableAcal(1, 0.5);
   StartDomino();
   SoftTrigger();
   while (IsBusy());
   TransferWaves(0, 8);

   for (i=0 ; i<8 ; i++) {
      t = GetStopCell(0);
      GetWave(0, i, waveform, false, t, 0, false);
      for (j=0 ; j<1024; j++)
         if (waveform[j] < 350) {
            if (j<5) {
               /* skip this cell */
            } else {
               printf("Cell error on channel %d, cell %d: %1.1lf mV instead 400 mV\n", i, j, waveform[j]);
               return 0;
            }
         }
   }

   /* read and check at -0.5V calibration voltage */
   EnableAcal(1, -0.5);
   StartDomino();
   Sleep(10);
   SoftTrigger();
   while (IsBusy());
   TransferWaves(0, 8);

   for (i=0 ; i<8 ; i++) {
      t = GetStopCell(0);
      GetWave(0, i, waveform, false, t, 0, false);
      for (j=0 ; j<1024; j++)
         if (waveform[j] > -350) {
            if (j<5) {
               /* skip this cell */
            } else {
               printf("Cell error on channel %d, cell %d: %1.1lf mV instead -400mV\n", i, j, waveform[j]);
               return 0;
            }
         }
   }

   /* check clock channel */
   GetWave(0, 8, waveform, false, 0, 0);
   min = max = mean = std = 0;
   for (j=0 ; j<1024 ; j++) {
      if (waveform[j] > max)
         max = waveform[j];
      if (waveform[j] < min)
         min = waveform[j];
      mean += waveform[j];
   }
   mean /= 1024.0;
   for (j=0 ; j<1024 ; j++)
      std += (waveform[j] - mean) * (waveform[j] - mean);
   std = sqrt(std/1024);

   if (max - min < 400) {
      printf("Error on clock channel amplitude: %1.1lf mV\n", max-min);
      return 0;
   }

   if (std < 100 || std > 300) {
      printf("Error on clock channel Std: %1.1lf mV\n", std);
      return 0;
   }

   return 1;
}

/*------------------------------------------------------------------*/

void DRSBoard::SetVoltageOffset(double offset1, double offset2)
{
   if (fDRSType == 3) {
      SetDAC(fDAC_ROFS_1, 0.95 - offset1);
      SetDAC(fDAC_ROFS_2, 0.95 - offset2);
   } else if (fDRSType == 2)
      SetDAC(fDAC_COFS, 0.9 - offset1);

   // let DAC settle
   Sleep(100);
}

/*------------------------------------------------------------------*/

int DRSBoard::SetInputRange(double center)
{
   if (fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      // DRS4 USB Evaluation Boards + Mezzanine Board

      // only allow -0.5...0.5 to 0...1.0
      if (center < 0 || center > 0.5)
         return 0;

      // remember range
      fRange = center;

      // correct for sampling cell charge injection
      center *= 1.125;

      // set readout offset
      fROFS = 1.6 - center;
      SetDAC(fDAC_ROFS_1, fROFS);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetExternalClockFrequency(double frequencyMHz)
{
   // Set the frequency of the external clock
   fExternalClockFrequency = frequencyMHz;
   return 0;
}

/*------------------------------------------------------------------*/

double DRSBoard::GetExternalClockFrequency()
{
   // Return the frequency of the external clock
   return fExternalClockFrequency;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetMultiBuffer(int flag)
{
   if (fHasMultiBuffer) {
      // Enable/disable multi-buffering
      fMultiBuffer = flag;
      if (flag)
         fCtrlBits |= BIT_MULTI_BUFFER;
      else
         fCtrlBits &= ~BIT_MULTI_BUFFER;

      if (flag) {
         if (fBoardType == 6)
            fNMultiBuffer = 3; // 3 buffers for VME board
      } else
         fNMultiBuffer = 0;

      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   }
   return 1;
}

/*------------------------------------------------------------------*/

void DRSBoard::ResetMultiBuffer(void)
{
   Reinit(); // set WP=0
   fReadPointer = 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetMultiBufferRP(void)
{
   return fReadPointer;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetMultiBufferRP(unsigned short rp)
{
   if (fHasMultiBuffer) {
      fReadPointer = rp;
      Write(T_CTRL, REG_READ_POINTER, &rp, 2);
   }
   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetMultiBufferWP(void)
{
   unsigned short wp = 0;

   if (fHasMultiBuffer)
      Read(T_STATUS, &wp, REG_WRITE_POINTER, 2);
   else
      wp = 0;
   return wp;
}

/*------------------------------------------------------------------*/

void DRSBoard::IncrementMultiBufferRP()
{
   if (fHasMultiBuffer && fMultiBuffer)
      SetMultiBufferRP((fReadPointer + 1) % fNMultiBuffer);
}

/*------------------------------------------------------------------*/

int DRSBoard::TransferWaves(int numberOfChannels)
{
   return TransferWaves(fWaveforms, numberOfChannels);
}

/*------------------------------------------------------------------*/

int DRSBoard::TransferWaves(unsigned char *p, int numberOfChannels)
{
   return TransferWaves(p, 0, numberOfChannels - 1);
}

/*------------------------------------------------------------------*/

int DRSBoard::TransferWaves(int firstChannel, int lastChannel)
{
   int offset;

   if (fTransport == TR_USB)
      offset = firstChannel * sizeof(short int) * kNumberOfBins;
   else
      offset = 0;               //in VME and USB2, always start from zero

   return TransferWaves(fWaveforms + offset, firstChannel, lastChannel);
}

/*------------------------------------------------------------------*/

int DRSBoard::TransferWaves(unsigned char *p, int firstChannel, int lastChannel)
{
   // Transfer all waveforms at once from VME or USB to location
   int n, i, offset, n_requested, n_bins;
   unsigned int   dw;
   unsigned short w;
   unsigned char *ptr;

   if (lastChannel >= fNumberOfChips * fNumberOfChannels)
      lastChannel = fNumberOfChips * fNumberOfChannels - 1;
   if (lastChannel < 0) {
      printf("Error: Invalid channel index %d\n", lastChannel);
      return 0;
   }

   if (firstChannel < 0 || firstChannel > fNumberOfChips * fNumberOfChannels) {
      printf("Error: Invalid channel index %d\n", firstChannel);
      return 0;
   }

   if (fTransport == TR_VME) {
      /* in VME, always transfer all waveforms, since channels sit 'next' to each other */
      firstChannel = 0;
      lastChannel = fNumberOfChips * fNumberOfChannels - 1;
      if (fReadoutChannelConfig == 4)
         lastChannel = fNumberOfChips * 5 - 1; // special mode to read only even channels + clock
   }

   else if (fTransport == TR_USB2) {
      /* USB2 FPGA contains 9 (Eval) or 10 (Mezz) channels */
      firstChannel = 0;
      if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
         lastChannel = 8;
      else if (fBoardType == 6)
         lastChannel = 9;
   }

   else if (fTransport == TR_USB) {
      /* USB1 FPGA contains only 16 channels */
      if (lastChannel > 15)
         lastChannel = 15;
   }

   n_bins = fDecimation ? kNumberOfBins/2 : kNumberOfBins;
   n_requested = (lastChannel - firstChannel + 1) * sizeof(short int) * n_bins;
   offset = firstChannel * sizeof(short int) * n_bins;

   if (fBoardType == 6 && fFirmwareVersion >= 17147)
      n_requested += 16; // add trailer four chips
     
   if ((fBoardType == 7  || fBoardType == 8 || fBoardType == 9) && fFirmwareVersion >= 17147)
      n_requested += 4;  // add trailer one chip  

   if (fMultiBuffer)
      offset += n_requested * fReadPointer;

   n = Read(T_RAM, p, offset, n_requested);

   if (fMultiBuffer)
      IncrementMultiBufferRP();

   if (n != n_requested) {
      printf("Error: only %d bytes read instead of %d\n", n, n_requested);
      return n;
   }

   // read trigger cells
   if (fDRSType == 4) {
      if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
         if ((fBoardType == 7  || fBoardType == 8 || fBoardType == 9) && fFirmwareVersion >= 17147) {
            // new code reading trailer
            ptr = p + n_requested - 4;
            fStopCell[0] = *((unsigned short *)(ptr));
            fStopWSR[0]  = *(ptr + 2);
         } else {
            // old code reading status register
            Read(T_STATUS, fStopCell, REG_STOP_CELL0, 2);
            Read(T_STATUS, &w, REG_STOP_WSR0, 2);
            fStopWSR[0] = (w >> 8) & 0xFFFF;
         }
      } else {

         if (fBoardType == 6) {
            // new code reading trailer
            ptr = p + n_requested - 16;
            for (i=0 ; i<4 ; i++) {
               fStopCell[i] = *((unsigned short *)(ptr + i*2));
               fStopWSR[i]  = *(ptr + 8 + i);
            }
            fTriggerBus = *((unsigned short *)(ptr + 12));
         } else {
            // old code reading registers
            Read(T_STATUS, &dw, REG_STOP_CELL0, 4);
            fStopCell[0] = (dw >> 16) & 0xFFFF;
            fStopCell[1] = (dw >>  0) & 0xFFFF;
            Read(T_STATUS, &dw, REG_STOP_CELL2, 4);
            fStopCell[2] = (dw >> 16) & 0xFFFF;
            fStopCell[3] = (dw >>  0) & 0xFFFF;

            Read(T_STATUS, &dw, REG_STOP_WSR0, 4);
            fStopWSR[0] = (dw >> 24) & 0xFF;
            fStopWSR[1] = (dw >> 16) & 0xFF;
            fStopWSR[2] = (dw >>  8) & 0xFF;
            fStopWSR[3] = (dw >>  0) & 0xFF;

            Read(T_STATUS, &fTriggerBus, REG_TRIGGER_BUS, 2);
         }
      }
   }
 
   return n;
}

/*------------------------------------------------------------------*/

int DRSBoard::DecodeWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform)
{
   return DecodeWave(fWaveforms, chipIndex, channel, waveform);
}

/*------------------------------------------------------------------*/

int DRSBoard::DecodeWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
                         unsigned short *waveform)
{
   // Get waveform
   int i, offset=0, ind, n_bins;

   /* check valid parameters */
   assert((int)channel < fNumberOfChannels);
   assert((int)chipIndex < fNumberOfChips);

   /* remap channel */
   if (fBoardType == 1) {
      if (channel < 8)
         channel = 7 - channel;
      else
         channel = 16 - channel;
   } else if (fBoardType == 6) {
      if (fReadoutChannelConfig == 7) {
         if (channel < 8)
            channel = 7-channel;
      } else if (fReadoutChannelConfig == 4) {
         if (channel == 8)
            channel = 4;
         else
            channel = 3 - channel/2;
      } else {
         channel = channel / 2;
         if (channel != 4)
           channel = 3-channel;
      }
   } /* else
      channel = channel; */


   // Read channel
   if (fTransport == TR_USB) {
      offset = kNumberOfBins * 2 * (chipIndex * 16 + channel);
      for (i = 0; i < kNumberOfBins; i++) {
         // 12-bit data
         waveform[i] = ((waveforms[i * 2 + 1 + offset] & 0x0f) << 8) + waveforms[i * 2 + offset];
      }
   } else if (fTransport == TR_USB2) {

      if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
         // see dpram_map_eval1.xls
         offset = kNumberOfBins * 2 * (chipIndex * 16 + channel);
      else if (fBoardType == 6) {
         // see dpram_map_mezz1.xls mode 0-3
         offset = (kNumberOfBins * 4) * (channel % 9) + 2 * (chipIndex/2);
      }
      for (i = 0; i < kNumberOfBins; i++) {
         // 16-bit data
         if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
            waveform[i] = ((waveforms[i * 2 + 1 + offset] & 0xff) << 8) + waveforms[i * 2 + offset];
         else if (fBoardType == 6)
            waveform[i] = ((waveforms[i * 4 + 1 + offset] & 0xff) << 8) + waveforms[i * 4 + offset];
      }

   } else if (fTransport == TR_VME) {

      if (fBoardType == 6) {
         n_bins = fDecimation ? kNumberOfBins/2 : kNumberOfBins;
         if (fReadoutChannelConfig == 7)       // see dpram_map_mezz1.xls mode 7
            offset = (n_bins * 4) * (channel % 9 + 9*(chipIndex % 2)) + 2 * (chipIndex/2);
         else if (fReadoutChannelConfig == 4)  // see dpram_map_mezz1.xls mode 4
            offset = (n_bins * 4) * (channel % 5 + 5*(chipIndex % 2)) + 2 * (chipIndex/2);
         for (i = 0; i < n_bins; i++)
            waveform[i] = ((waveforms[i * 4 + 1 + offset] & 0xff) << 8) + waveforms[i * 4 + offset];
      } else {
         offset = (kNumberOfBins * 4) * channel;
         for (i = 0; i < kNumberOfBins; i++) {
            ind = i * 4 + offset;
            if (chipIndex == 0)
               // lower 12 bit
               waveform[i] = ((waveforms[ind + 1] & 0x0f) << 8) | waveforms[ind];
            else
               // upper 12 bit
               waveform[i] = (waveforms[ind + 2] << 4) | (waveforms[ind + 1] >> 4);
         }
      }
   } else {
      printf("Error: invalid transport %d\n", fTransport);
      return kInvalidTransport;
   }
   return kSuccess;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, float *waveform)
{
   return GetWave(chipIndex, channel, waveform, true, fStopCell[chipIndex], -1, false, 0, true);
}

/*------------------------------------------------------------------*/

int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, short *waveform, bool responseCalib,
                      int triggerCell, int wsr, bool adjustToClock, float threshold, bool offsetCalib)
{
   return GetWave(fWaveforms, chipIndex, channel, waveform, responseCalib, triggerCell, wsr, adjustToClock,
                  threshold, offsetCalib);
}

/*------------------------------------------------------------------*/

int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, float *waveform, bool responseCalib,
                      int triggerCell, int wsr, bool adjustToClock, float threshold, bool offsetCalib)
{
   return GetWave(fWaveforms, chipIndex, channel, waveform, responseCalib, triggerCell, wsr, adjustToClock, threshold,
               offsetCalib);
}

/*------------------------------------------------------------------*/

int DRSBoard::GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
                      float *waveform, bool responseCalib, int triggerCell, int wsr, bool adjustToClock,
                      float threshold, bool offsetCalib)
{
   int ret, i;
   short waveS[2*kNumberOfBins];
   ret =
       GetWave(waveforms, chipIndex, channel, waveS, responseCalib, triggerCell, wsr, adjustToClock, threshold,
               offsetCalib);
   if (responseCalib)
      for (i = 0; i < fChannelDepth ; i++)
         waveform[i] = static_cast < float >(static_cast <short> (waveS[i]) * GetPrecision());
   else {
      for (i = 0; i < fChannelDepth ; i++) {
         if (fBoardType == 4 || fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
            waveform[i] = static_cast < float >(waveS[i] * GetPrecision());
         } else
            waveform[i] = static_cast < float >(waveS[i]);
      }
   }
   return ret;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
                      short *waveform, bool responseCalib, int triggerCell, int wsr, bool adjustToClock,
                      float threshold, bool offsetCalib)
{
   unsigned short adcWaveform[kNumberOfBins];
   int i, ret;

   if (fChannelCascading == 1 || channel == 8) {
      /* single channel configuration */
      ret = DecodeWave(waveforms, chipIndex, channel, adcWaveform);
      if (ret != kSuccess)
         return ret;

      ret = CalibrateWaveform(chipIndex, channel, adcWaveform, waveform, responseCalib,
                              triggerCell, adjustToClock, threshold, offsetCalib);

      return ret;
   } else if (fChannelCascading == 2) {
      /* double channel configuration */
      short wf1[kNumberOfBins];
      short wf2[kNumberOfBins];

      // first half
      ret = DecodeWave(waveforms, chipIndex, 2*channel, adcWaveform);
      if (ret != kSuccess)
         return ret;

      ret = CalibrateWaveform(chipIndex, 2*channel, adcWaveform, wf1, responseCalib,
                              triggerCell, adjustToClock, threshold, offsetCalib);

      // second half
      ret = DecodeWave(waveforms, chipIndex, 2*channel+1, adcWaveform);
      if (ret != kSuccess)
         return ret;

      ret = CalibrateWaveform(chipIndex, 2*channel+1, adcWaveform, wf2, responseCalib,
                              triggerCell, adjustToClock, threshold, offsetCalib);


      // combine two halfs correctly, see 2048_mode.ppt
      if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
         if ((wsr == 0 && triggerCell < 767) ||
             (wsr == 1 && triggerCell >= 767)) {
            for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
               waveform[i] = wf1[i];
            for (; i<kNumberOfBins; i++)
               waveform[i] = wf2[i];
            for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
               waveform[i+kNumberOfBins] = wf2[i];
            for (; i<kNumberOfBins; i++)
               waveform[i+kNumberOfBins] = wf1[i];
         } else {
            for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
               waveform[i] = wf2[i];
            for (; i<kNumberOfBins; i++)
               waveform[i] = wf1[i];
            for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
               waveform[i+kNumberOfBins] = wf1[i];
            for (; i<kNumberOfBins; i++)
               waveform[i+kNumberOfBins] = wf2[i];
         }
      } else {
         if (wsr == 1) {
            if (fDecimation) {
               for (i=0 ; i<kNumberOfBins/2-triggerCell/2 ; i++)
                  waveform[i] = wf1[i];
               for (; i<kNumberOfBins/2; i++)
                  waveform[i] = wf2[i];
               for (i=0 ; i<kNumberOfBins/2-triggerCell/2 ; i++)
                  waveform[i+kNumberOfBins/2] = wf2[i];
               for (; i<kNumberOfBins/2; i++)
                  waveform[i+kNumberOfBins/2] = wf1[i];
            } else {
               for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
                  waveform[i] = wf1[i];
               for (; i<kNumberOfBins; i++)
                  waveform[i] = wf2[i];
               for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
                  waveform[i+kNumberOfBins] = wf2[i];
               for (; i<kNumberOfBins; i++)
                  waveform[i+kNumberOfBins] = wf1[i];
            }
         } else {
            if (fDecimation) {
               for (i=0 ; i<kNumberOfBins/2-triggerCell/2 ; i++)
                  waveform[i] = wf2[i];
               for (; i<kNumberOfBins/2; i++)
                  waveform[i] = wf1[i];
               for (i=0 ; i<kNumberOfBins/2-triggerCell/2 ; i++)
                  waveform[i+kNumberOfBins/2] = wf1[i];
               for (; i<kNumberOfBins/2; i++)
                  waveform[i+kNumberOfBins/2] = wf2[i];
            } else {
               for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
                  waveform[i] = wf2[i];
               for (; i<kNumberOfBins; i++)
                  waveform[i] = wf1[i];
               for (i=0 ; i<kNumberOfBins-triggerCell ; i++)
                  waveform[i+kNumberOfBins] = wf1[i];
               for (; i<kNumberOfBins; i++)
                  waveform[i+kNumberOfBins] = wf2[i];
            }
         }
      }

      return ret;
   } else
      assert(!"Not implemented");

   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetRawWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform,
                         bool adjustToClock)
{
   return GetRawWave(fWaveforms, chipIndex, channel, waveform, adjustToClock);
}

/*------------------------------------------------------------------*/

int DRSBoard::GetRawWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
                         unsigned short *waveform, bool adjustToClock)
{
   int i, status, tc;
   unsigned short wf[kNumberOfBins];

   status = DecodeWave(waveforms, chipIndex, channel,  wf);

   if (adjustToClock) {
      tc = GetTriggerCell(chipIndex);
      for (i = 0 ; i < kNumberOfBins; i++)
         waveform[(i + tc) % kNumberOfBins] = wf[i];
   } else {
      for (i = 0 ; i < kNumberOfBins; i++)
         waveform[i] = wf[i];
   }

   return status;
}

/*------------------------------------------------------------------*/

int DRSBoard::CalibrateWaveform(unsigned int chipIndex, unsigned char channel, unsigned short *adcWaveform,
                                short *waveform, bool responseCalib,
                                int triggerCell, bool adjustToClock, float threshold, bool offsetCalib)
{
   int j, n_bins, skip;
   double value;
   short left, right;

   // calibrate waveform
   if (responseCalib && fVoltageCalibrationValid) {
      if (GetDRSType() == 4) {
         // if Mezz though USB2 -> select correct calibration channel
         if (fBoardType == 6 && (fReadoutChannelConfig == 0 || fReadoutChannelConfig == 2) &&
             channel != 8)
            channel++;

         // Channel readout mode #4 -> select correct calibration channel
         if (fBoardType == 6 && fReadoutChannelConfig == 4 && channel % 2 == 0 && channel != 8)
            channel++;

         n_bins = fDecimation ? kNumberOfBins/2 : kNumberOfBins;
         skip = fDecimation ? 2 : 1;
         for (j = 0; j < n_bins; j++) {
            value = adcWaveform[j] - fCellOffset[channel+chipIndex*9][(j*skip + triggerCell) % kNumberOfBins];
            value = value / fCellGain[channel+chipIndex*9][(j*skip + triggerCell) % kNumberOfBins];
            if (offsetCalib && channel != 8)
               value = value - fCellOffset2[channel+chipIndex*9][j*skip] + 32768;

            /* convert to units of 0.1 mV */
            value = value / 65536.0 * 1000 * 10;

            /* apply clipping */
            if (channel != 8) {
               if (adcWaveform[j] >= 0xFFF0 || value > (fRange * 1000 + 500) * 10)
                  value = (fRange * 1000 + 500) * 10;
               if (adcWaveform[j] <  0x0010 || value < (fRange * 1000 - 500) * 10)
                  value = (fRange * 1000 - 500) * 10;
            }

            if (adjustToClock)          
               waveform[(j + triggerCell) % kNumberOfBins] = (short) (value + 0.5);
            else
               waveform[j] = (short) (value + 0.5);
         }

         // check for stuck pixels and replace by average of neighbors
         for (j = 0 ; j < n_bins; j++) {
            if (adjustToClock) {
               if (fCellOffset[channel+chipIndex*9][j*skip] == 0) {
                  left = waveform[(j-1+kNumberOfBins) % kNumberOfBins];
                  right = waveform[(j+1) % kNumberOfBins];
                  waveform[j] = (short) ((left+right)/2);
               }
            } else {
               if (fCellOffset[channel+chipIndex*9][(j*skip + triggerCell) % kNumberOfBins] == 0) {
                  left = waveform[(j-1+kNumberOfBins) % kNumberOfBins];
                  right = waveform[(j+1) % kNumberOfBins];
                  waveform[j] = (short) ((left+right)/2);
               }
            }
         }

      } else {
         if (!fResponseCalibration->
             Calibrate(chipIndex, channel % 10, adcWaveform, waveform, triggerCell, threshold, offsetCalib))
            return kZeroSuppression;       // return immediately if below threshold
      }
   } else {
      if (GetDRSType() == 4) {
         // if Mezz though USB2 -> select correct calibration channel
         if (fBoardType == 6 && (fReadoutChannelConfig == 0 || fReadoutChannelConfig == 2) &&
             channel != 8)
            channel++;
         for (j = 0 ; j < kNumberOfBins; j++) {
            value = adcWaveform[j];

            /* convert to units of 0.1 mV */
            value = (value - 32768) / 65536.0 * 1000 * 10;

            /* correct for range */
            value += fRange * 1000 * 10;

            if (adjustToClock)          
               waveform[(j + triggerCell) % kNumberOfBins] = (short) (value + 0.5);
            else
               waveform[j] = (short) (value + 0.5);
         }
      } else {
         for (j = 0; j < kNumberOfBins; j++) {
            if (adjustToClock) {
               // rotate waveform such that waveform[0] corresponds to bin #0 on the chip
               waveform[j] = adcWaveform[(kNumberOfBins-triggerCell+j) % kNumberOfBins];
            } else {
               waveform[j] = adcWaveform[j];
            }
         }
      }
   }

   // fix bad cells for single turn mode
   if (GetDRSType() == 2) {
      if (fDominoMode == 0 && triggerCell == -1) {
         waveform[0] = 2 * waveform[1] - waveform[2];
         short m1 = (waveform[kNumberOfBins - 5] + waveform[kNumberOfBins - 6]) / 2;
         short m2 = (waveform[kNumberOfBins - 6] + waveform[kNumberOfBins - 7]) / 2;
         waveform[kNumberOfBins - 4] = m1 - 1 * (m2 - m1);
         waveform[kNumberOfBins - 3] = m1 - 2 * (m2 - m1);
         waveform[kNumberOfBins - 2] = m1 - 3 * (m2 - m1);
         waveform[kNumberOfBins - 1] = m1 - 4 * (m2 - m1);
      }
   }

   return kSuccess;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetStretchedTime(float *time, float *measurement, int numberOfMeasurements, float period)
{
   int j;
   if (*time >= measurement[numberOfMeasurements - 1]) {
      *time -= measurement[numberOfMeasurements - 1];
      return 1;
   }
   if (*time < measurement[0]) {
      *time = *time - measurement[0] - (numberOfMeasurements - 1) * period / 2;
      return 1;
   }
   for (j = 0; j < numberOfMeasurements - 1; j++) {
      if (*time > measurement[j] && *time <= measurement[j + 1]) {
         *time =
             (period / 2) / (measurement[j + 1] - measurement[j]) * (*time - measurement[j + 1]) -
             (numberOfMeasurements - 2 - j) * period / 2;
         return 1;
      }
   }
   return 0;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTriggerCell(unsigned int chipIndex)
{
   if (fDRSType == 4)
      return GetStopCell(chipIndex);

   return GetTriggerCell(fWaveforms, chipIndex);
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTriggerCell(unsigned char *waveforms, unsigned int chipIndex)
{
   int j, triggerCell;
   bool calib;
   unsigned short baseLevel = 1000;
   unsigned short triggerChannel[1024];

   if (fDRSType == 4)
      return GetStopCell(chipIndex);

   GetRawWave(waveforms, chipIndex, 8, triggerChannel);
   calib = fResponseCalibration->SubtractADCOffset(chipIndex, 8, triggerChannel, triggerChannel, baseLevel);

   triggerCell = -1;
   for (j = 0; j < kNumberOfBins; j++) {
      if (calib) {
         if (triggerChannel[j] <= baseLevel + 200
             && triggerChannel[(j + 1) % kNumberOfBins] > baseLevel + 200) {
            triggerCell = j;
            break;
         }
      } else {
         if (fDRSType == 3) {
            if (triggerChannel[j] <= 2000 && triggerChannel[(j + 1) % kNumberOfBins] > 2000) {
               triggerCell = j;
               break;
            }
         } else {
            if (triggerChannel[j] >= 2000 && triggerChannel[(j + 1) % kNumberOfBins] < 2000) {
               triggerCell = j;
               break;
            }
         }
      }
   }
   if (triggerCell == -1) {
      return kInvalidTriggerSignal;
   }
   fStopCell[0] = triggerCell;
   return triggerCell;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetStopCell(unsigned int chipIndex)
{
   return fStopCell[chipIndex];
}

/*------------------------------------------------------------------*/

unsigned char DRSBoard::GetStopWSR(unsigned int chipIndex)
{
   return fStopWSR[chipIndex];
}

/*------------------------------------------------------------------*/

void DRSBoard::TestDAC(int channel)
{
   // Test DAC
   int status;

   do {
      status = SetDAC(channel, 0);
      Sleep(1000);
      status = SetDAC(channel, 0.5);
      Sleep(1000);
      status = SetDAC(channel, 1);
      Sleep(1000);
      status = SetDAC(channel, 1.5);
      Sleep(1000);
      status = SetDAC(channel, 2);
      Sleep(1000);
      status = SetDAC(channel, 2.5);
      Sleep(1000);
   } while (status);
}

/*------------------------------------------------------------------*/

void DRSBoard::MeasureSpeed()
{
   // Measure domino sampling speed
   FILE *f;
   double vdr, vds, freq;

   f = fopen("speed.txt", "wt");
   fprintf(f, "\t");
   printf("\t");
   for (vdr = 0.5; vdr <= 2.501; vdr += 0.05) {
      fprintf(f, "%1.2lf\t", vdr);
      printf("%1.2lf\t", vdr);
   }
   fprintf(f, "\n");
   printf("\n");

   for (vds = 0.5; vds <= 2.501; vds += 0.05) {
      fprintf(f, "%1.2lf\t", vds);
      printf("%1.2lf\t", vds);

      SetDAC(fDAC_DSA, vds);
      StartDomino();
      Sleep(1000);
      ReadFrequency(0, &freq);

      fprintf(f, "%1.3lf\t", freq);
      printf("%1.3lf\t", freq);

      fprintf(f, "\n");
      printf("\n");
      fflush(f);
   }
}

/*------------------------------------------------------------------*/

void DRSBoard::InteractSpeed()
{
   int status, i;
   double freq, vds;

   do {
      printf("DS: ");
      scanf("%lf", &vds);
      if (vds == 0)
         break;

      SetDAC(fDAC_DSA, vds);
      SetDAC(fDAC_DSB, vds);

      StartDomino();
      for (i = 0; i < 4; i++) {
         Sleep(1000);

         status = ReadFrequency(0, &freq);
         if (!status)
            break;
         printf("%1.6lf GHz\n", freq);
      }

      /* turn BOARD_LED off */
      SetLED(0);

   } while (1);
}

/*------------------------------------------------------------------*/

void DRSBoard::MonitorFrequency()
{
   // Monitor domino sampling frequency
   int status;
   unsigned int data;
   double freq, dac;
   FILE *f;
   time_t now;
   char str[256];

   f = fopen("DRSBoard.log", "w");

   do {
      Sleep(1000);

      status = ReadFrequency(0, &freq);
      if (!status)
         break;

      data = 0;
      if (fBoardType == 1)
         Read(T_STATUS, &data, REG_RDAC3, 2);
      else if (fBoardType == 2 || fBoardType == 3)
         Read(T_STATUS, &data, REG_RDAC1, 2);

      dac = data / 65536.0 * 2.5;
      printf("%1.6lf GHz, %1.4lf V\n", freq, dac);
      time(&now);
      strcpy(str, ctime(&now) + 11);
      str[8] = 0;

      fprintf(f, "%s %1.6lf GHz, %1.4lf V\n", str, freq, dac);
      fflush(f);

   } while (!drs_kbhit());

   fclose(f);
}

/*------------------------------------------------------------------*/

int DRSBoard::TestShift(int n)
{
   // Test shift register
   unsigned char buffer[3];

   memset(buffer, 0, sizeof(buffer));

#if 0
   buffer[0] = CMD_TESTSHIFT;
   buffer[1] = n;

   status = msend_usb(buffer, 2);
   if (status != 2)
      return status;

   status = mrecv_usb(buffer, sizeof(buffer));
   if (status != 1)
      return status;
#endif

   if (buffer[0] == 1)
      printf("Shift register %c works correctly\n", 'A' + n);
   else if (buffer[0] == 2)
      printf("SROUT%c does hot go high after reset\n", 'A' + n);
   else if (buffer[0] == 3)
      printf("SROUT%c does hot go low after 1024 clocks\n", 'A' + n);

   return 1;
}

/*------------------------------------------------------------------*/

unsigned int DRSBoard::GetCtrlReg()
{
   unsigned int status;

   Read(T_CTRL, &status, REG_CTRL, 4);
   return status;
}

/*------------------------------------------------------------------*/

unsigned short DRSBoard::GetConfigReg()
{
   unsigned short status;

   Read(T_CTRL, &status, REG_CONFIG, 2);
   return status;
}

/*------------------------------------------------------------------*/

unsigned int DRSBoard::GetStatusReg()
{
   unsigned int status;

   Read(T_STATUS, &status, REG_STATUS, 4);
   return status;
}

/*------------------------------------------------------------------*/

int DRSBoard::EnableTcal(int freq, int level, int phase)
{
   fTcalFreq = freq;
   fTcalLevel = level;
   fTcalPhase = phase;

   if (fBoardType == 6) {
      ConfigureLMK(fNominalFrequency, false, freq, phase);
   } else {
      if (fBoardType == 9) {
         // Enable clock a switch channel multiplexers
         if (freq) {
            fCtrlBits |= (BIT_TCAL_EN | BIT_ACAL_EN);
         } else
            fCtrlBits &= ~(BIT_TCAL_EN | BIT_ACAL_EN);
         
      } else {
         // Enable clock channel
         if (freq)
            fCtrlBits |= BIT_TCAL_EN;
         else
            fCtrlBits &= ~(BIT_TCAL_EN | BIT_TCAL_SOURCE);
         
         // Set output level, needed for gain calibration
         if (fDRSType == 4) {
            if (level)
               fCtrlBits |= BIT_NEG_TRIGGER;
            else
               fCtrlBits &= ~BIT_NEG_TRIGGER;
         }
      }

      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SelectClockSource(int source)
{
   fTcalSource = source;

   // Select clock source:
   // EVAL1: synchronous (0) or asynchronous (1) (2nd quartz)
   if (fBoardType <= 8) {
      if (source)
         fCtrlBits |= BIT_TCAL_SOURCE;
      else
         fCtrlBits &= ~BIT_TCAL_SOURCE;
     
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetRefclk(int source)
{
   // Select reference clock source to internal FPGA (0) or external P2 (1)
   if (fBoardType == 6) {
      if (source)
         fCtrlBits |= BIT_REFCLK_SOURCE;
      else
         fCtrlBits &= ~BIT_REFCLK_SOURCE;
   } else if (fBoardType == 8 || fBoardType == 9) {
      if (source)
         fCtrlBits |= BIT_REFCLK_EXT;
      else
         fCtrlBits &= ~BIT_REFCLK_EXT;
   }

   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fRefclk = source;

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::EnableAcal(int mode, double voltage)
{
   double t1, t2;

   fAcalMode = mode;
   fAcalVolt = voltage;

   if (mode == 0) {
      /* turn calibration off */
      SetCalibTiming(0, 0);
      if (fBoardType == 5 || fBoardType == 6) {
         /* turn voltages off (50 Ohm analog switch!) */
         SetDAC(fDAC_CALP, 0);
         SetDAC(fDAC_CALN, 0);
      }
      if (fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
         SetCalibVoltage(0);

      fCtrlBits &= ~BIT_ACAL_EN;
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   } else if (mode == 1) {
      /* static calibration */
      SetCalibVoltage(voltage);
      SetCalibTiming(0, 0);
      fCtrlBits |= BIT_ACAL_EN;
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   } else if (mode == 2) {
      /* first part calibration:
         stop domino wave after 1.2 revolutions
         turn on calibration voltage after 0.1 revolutions */


      /* ensure circulating domino wave */
      SetDominoMode(1);

      /* set calibration voltage but do not turn it on now */
      SetCalibVoltage(voltage);
      fCtrlBits &= ~BIT_ACAL_EN;
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

      /* calculate duration of DENABLE signal as 1.2 revolutions */
      t1 = 1 / fNominalFrequency * 1024 * 1.2; // ns
      t1 = static_cast < int >((t1 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
      t2 = 1 / fNominalFrequency * 1024 * 0.1; // ns
      t2 = static_cast < int >((t2 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
      SetCalibTiming(static_cast < int >(t1), static_cast < int >(t2));

   } else if (mode == 3) {
      /* second part calibration:
         stop domino wave after 1.05 revolutions */


      /* ensure circulating domino wave */
      SetDominoMode(1);

      /* turn on and let settle calibration voltage */
      SetCalibVoltage(voltage);
      fCtrlBits |= BIT_ACAL_EN;
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);

      /* calculate duration of DENABLE signal as 1.1 revolutions */
      t1 = 1 / fNominalFrequency * 1024 * 1.05;        // ns
      t1 = static_cast < int >((t1 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
      SetCalibTiming(static_cast < int >(t1), 0);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetCalibTiming(int t_enable, int t_cal)
{
   unsigned short d;

   if (fDRSType == 2) {
      d = t_cal | (t_enable << 8);
      Write(T_CTRL, REG_CALIB_TIMING, &d, 2);
   }

   if (fDRSType == 3) {
      d = t_cal;
      Write(T_CTRL, REG_CALIB_TIMING, &d, 2);
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::SetCalibVoltage(double value)
{
   // Set Calibration Voltage
   if (fBoardType == 5 || fBoardType == 6 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      if (fBoardType == 5)
         value = value * (1+fNominalFrequency/65); // rough correction factor for input current
      if (fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
         value = value * (1+fNominalFrequency/47); // rough correction factor for input current
      SetDAC(fDAC_CALP, fCommonMode + value / 2);
      SetDAC(fDAC_CALN, fCommonMode - value / 2);
   } else
      SetDAC(fDAC_ACALIB, value);
   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::StartClearCycle()
{
   /* clear cycle is necessary for DRS4 to reduce noise */

   fbkAcalVolt  = fAcalVolt;
   fbkAcalMode  = fAcalMode;
   fbkTcalFreq  = fTcalFreq;
   fbkTcalLevel = fTcalLevel;

   /* switch all inputs to zero */
   EnableAcal(1, 0);

   /* start, stop and readout of zero */
   StartDomino();
   SoftTrigger();

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::FinishClearCycle()
{
   while (IsBusy());

   /* restore old values */
   EnableAcal(fbkAcalMode, fbkAcalVolt);

   return 1;
}

/*------------------------------------------------------------------*/

double DRSBoard::GetTemperature()
{
   // Read Out Temperature Sensor
   unsigned char buffer[2];
   unsigned short d;
   double temperature;

   Read(T_STATUS, buffer, REG_TEMPERATURE, 2);

   d = (static_cast < unsigned int >(buffer[1]) << 8) +buffer[0];
   temperature = ((d >> 3) & 0x0FFF) * 0.0625;

   return temperature;
}

/*------------------------------------------------------------------*/

int DRSBoard::Is2048ModeCapable()
{
   unsigned int status;
   
   if (fFirmwareVersion < 21305)
      return 0;
   
   // Read pin J44 and return 1 if 2048 mode has been soldered
   Read(T_STATUS, &status, REG_STATUS, 4);
   if ((status & BIT_2048_MODE))
      return 0;
   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTriggerBus()
{
   unsigned size, d;

   if (fBoardType == 6 && fTransport == TR_VME) {
      if (fReadoutChannelConfig == 4)
         size = (20 * sizeof(short int) * (fDecimation ? kNumberOfBins/2 : kNumberOfBins) + 16);
      else
         size = (36 * sizeof(short int) * (fDecimation ? kNumberOfBins/2 : kNumberOfBins) + 16);

      Read(T_RAM, &d, size * fReadPointer + size - 16 + 12, 4);
      fTriggerBus = (unsigned short)d;
   } else {
      Read(T_STATUS, &fTriggerBus, REG_TRIGGER_BUS, 2);
   }
   return static_cast < int >(fTriggerBus);
}


/*------------------------------------------------------------------*/

unsigned int DRSBoard::GetScaler(int channel)
{
   int reg = 0;
   unsigned d;
   
   if (fBoardType < 9 || fFirmwareVersion < 21000 || fTransport != TR_USB2)
      return 0;
   
   switch (channel ) {
      case 0: reg = REG_SCALER0; break;
      case 1: reg = REG_SCALER1; break;
      case 2: reg = REG_SCALER2; break;
      case 3: reg = REG_SCALER3; break;
      case 4: reg = REG_SCALER4; break;
      case 5: reg = REG_SCALER5; break;
   }
   
   Read(T_STATUS, &d, reg, 4);

   return static_cast < unsigned int >(d * 10); // measurement clock is 10 Hz
}


/*------------------------------------------------------------------*/

int DRSBoard::SetBoardSerialNumber(unsigned short serialNumber)
{
   unsigned char buf[32768];

   unsigned short dac;

   if (fDRSType < 4) {
      // read current DAC register
      Read(T_CTRL, &dac, REG_DAC0, 2);

      // put serial in DAC register
      Write(T_CTRL, REG_DAC0, &serialNumber, 2);

      // execute flash
      fCtrlBits |= BIT_EEPROM_WRITE_TRIG;
      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
      fCtrlBits &= ~BIT_EEPROM_WRITE_TRIG;

      // wait 6ms per word
      Sleep(20);

      // write back old DAC registers
      Write(T_CTRL, REG_DAC0, &dac, 2);

      // read back serial number
      ReadSerialNumber();

   } else if (fDRSType == 4) {
      /* merge serial number into eeprom page #0 */
      ReadEEPROM(0, buf, sizeof(buf));
      buf[0] = serialNumber & 0xFF;
      buf[1] = serialNumber >> 8;
      WriteEEPROM(0, buf, sizeof(buf));

      /* erase DPRAM */
      memset(buf, 0, sizeof(buf));
      Write(T_RAM, 0, buf, sizeof(buf));

      /* read back EEPROM */
      ReadEEPROM(0, buf, sizeof(buf));

      /* check if correctly set */
      if (((buf[1] << 8) | buf[0]) != serialNumber)
         return 0;

      fBoardSerialNumber = serialNumber;
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::ReadEEPROM(unsigned short page, void *buffer, int size)
{
   int i;
   unsigned long status;
   // write eeprom page number
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
      Write(T_CTRL, REG_EEPROM_PAGE_EVAL, &page, 2);
   else if (fBoardType == 6)
      Write(T_CTRL, REG_EEPROM_PAGE_MEZZ, &page, 2);
   else
      return -1;

   // execute eeprom read
   fCtrlBits |= BIT_EEPROM_READ_TRIG;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_EEPROM_READ_TRIG;

   // poll on serial_busy flag
   for (i=0 ; i<100 ; i++) {
      Read(T_STATUS, &status, REG_STATUS, 4);
      if ((status & BIT_SERIAL_BUSY) == 0)
         break;
      Sleep(10);
   }

   return Read(T_RAM, buffer, 0, size);
}

/*------------------------------------------------------------------*/

int DRSBoard::WriteEEPROM(unsigned short page, void *buffer, int size)
{
   int i;
   unsigned long status;
   unsigned char buf[32768];

   // read previous page
   ReadEEPROM(page, buf, sizeof(buf));
   
   // combine with new page
   memcpy(buf, buffer, size);
   
   // write eeprom page number
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
      Write(T_CTRL, REG_EEPROM_PAGE_EVAL, &page, 2);
   else if (fBoardType == 6)
      Write(T_CTRL, REG_EEPROM_PAGE_MEZZ, &page, 2);
   else
      return -1;

   // write eeprom page to RAM
   Write(T_RAM, 0, buf, size);

   // execute eeprom write
   fCtrlBits |= BIT_EEPROM_WRITE_TRIG;
   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
   fCtrlBits &= ~BIT_EEPROM_WRITE_TRIG;

   // poll on serail_busy flag
   for (i=0 ; i<500 ; i++) {
      Read(T_STATUS, &status, REG_STATUS, 4);
      if ((status & BIT_SERIAL_BUSY) == 0)
         break;
      Sleep(10);
   }

   return 1;
}

/*------------------------------------------------------------------*/

bool DRSBoard::IsTimingCalibrationValid()
{
   return fabs(fNominalFrequency - fTimingCalibratedFrequency) < 0.001;
}

double DRSBoard::GetTrueFrequency()
{
   int i;
   double f;
   
   if (IsTimingCalibrationValid()) {
      /* calculate true frequency */
      for (i=0,f=0 ; i<1024 ; i++)
         f += fCellDT[0][0][i];
      f = 1024.0 / f;
   } else
      f = fNominalFrequency;
   
   return f;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTime(unsigned int chipIndex, int channelIndex, int tc, float *time, bool tcalibrated, bool rotated)
{
   int i, scale, iend;
   double gt0, gt;

   /* for DRS2, please use function below */
   if (fDRSType < 4)
      return GetTime(chipIndex, channelIndex, fNominalFrequency, tc, time, tcalibrated, rotated);

   scale = fDecimation ? 2 : 1;

   if (!IsTimingCalibrationValid() || !tcalibrated) {
      double t0 = tc / fNominalFrequency;
      for (i = 0; i < fChannelDepth; i++) {
         if (rotated)
            time[i] = static_cast < float >(((i*scale+tc) % kNumberOfBins) / fNominalFrequency - t0);
         else
            time[i] = static_cast < float >((i*scale) / fNominalFrequency);
         if (time[i] < 0)
            time[i] += static_cast < float > (kNumberOfBins / fNominalFrequency);
         if (i*scale >= kNumberOfBins)
            time[i] += static_cast < float > (kNumberOfBins / fNominalFrequency);
      }
      return 1;
   }

   time[0] = 0;
   for (i=1 ; i<fChannelDepth ; i++) {
      if (rotated)
         time[i] = time[i-1] + (float)fCellDT[chipIndex][channelIndex][(i-1+tc) % kNumberOfBins];
      else
         time[i] = time[i-1] + (float)fCellDT[chipIndex][channelIndex][(i-1) % kNumberOfADCBins];
   }

   if (channelIndex > 0) {
      // correct all channels to channel 0 (Daniel's method)
      iend = tc >= 700 ? 700+1024 : 700;
      for (i=tc,gt0=0 ; i<iend ; i++)
         gt0 += fCellDT[chipIndex][0][i % 1024];
     
      for (i=tc,gt=0 ; i<iend ; i++)
         gt += fCellDT[chipIndex][channelIndex][i % 1024];
     
      for (i=0 ; i<fChannelDepth ; i++)
         time[i] += (float)(gt0 - gt);
   }
     
   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTimeCalibration(unsigned int chipIndex, int channelIndex, int mode, float *time, bool force)
{
   int i;
   float tint;

   /* not implemented for DRS2 */
   if (fDRSType < 4)
      return -1;

   if (!force && !IsTimingCalibrationValid()) {
      for (i = 0; i < kNumberOfBins; i++)
         time[i] = (float) (1/fNominalFrequency);
      return 1;
   }

   if (mode == 0) {
      /* differential nonlinearity */
      for (i=0 ; i<kNumberOfBins ; i++)
         time[i] = static_cast < float > (fCellDT[chipIndex][channelIndex][i]);
   } else {
      /* integral nonlinearity */
      for (i=0,tint=0; i<kNumberOfBins ; i++) {
         time[i] = static_cast < float > (tint - i/fNominalFrequency);
         tint += (float)fCellDT[chipIndex][channelIndex][i];
      }
   }

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::GetTime(unsigned int chipIndex, int channelIndex, double freqGHz, int tc, float *time, bool tcalibrated, bool rotated)
{
   /* for DRS4, use function above */
   if (fDRSType == 4)
      return GetTime(chipIndex, channelIndex, tc, time, tcalibrated, rotated);

   int i, irot;
   DRSBoard::TimeData * init;
   DRSBoard::TimeData::FrequencyData * freq;
   int frequencyMHz = (int)(freqGHz*1000);

   init = GetTimeCalibration(chipIndex);

   if (init == NULL) {
      for (i = 0; i < kNumberOfBins; i++)
         time[i] = static_cast < float >(i / fNominalFrequency);
      return 1;
   }
   freq = NULL;
   for (i = 0; i < init->fNumberOfFrequencies; i++) {
      if (init->fFrequency[i]->fFrequency == frequencyMHz) {
         freq = init->fFrequency[i];
         break;
      }
   }
   if (freq == NULL) {
      for (i = 0; i < kNumberOfBins; i++)
         time[i] = static_cast < float >(i / fNominalFrequency);
      return 1;
   }
   for (i = 0; i < kNumberOfBins; i++) {
      irot = (fStopCell[chipIndex] + i) % kNumberOfBins;
      if (fStopCell[chipIndex] + i < kNumberOfBins)
         time[i] = static_cast < float >((freq->fBin[irot] - freq->fBin[fStopCell[chipIndex]]) / fNominalFrequency);
      else
      time[i] =
          static_cast <
          float
          >((freq->fBin[irot] - freq->fBin[fStopCell[chipIndex]] + freq->fBin[kNumberOfBins - 1] - 2 * freq->fBin[0] +
             freq->fBin[1]) / fNominalFrequency);
   }
   return 1;
}

/*------------------------------------------------------------------*/

bool DRSBoard::InitTimeCalibration(unsigned int chipIndex)
{
   return GetTimeCalibration(chipIndex, true) != NULL;
}

/*------------------------------------------------------------------*/

DRSBoard::TimeData * DRSBoard::GetTimeCalibration(unsigned int chipIndex, bool reinit)
{
   int i, l, index;
   char *cstop;
   char fileName[500];
   char error[240];
   PMXML_NODE node, rootNode, mainNode;

   index = fNumberOfTimeData;
   for (i = 0; i < fNumberOfTimeData; i++) {
      if (fTimeData[i]->fChip == static_cast < int >(chipIndex)) {
         if (!reinit)
            return fTimeData[i];
         else {
            index = i;
            break;
         }
      }
   }

   fTimeData[index] = new DRSBoard::TimeData();
   DRSBoard::TimeData * init = fTimeData[index];

   init->fChip = chipIndex;

   for (i = 0; i < init->kMaxNumberOfFrequencies; i++) {
      if (i <= 499 || (i >= 501 && i <= 999) || (i >= 1001 && i <= 1499) || (i >= 1501 && i <= 1999) ||
          (i >= 2001 && i <= 2499) || i >= 2501)
         continue;
      sprintf(fileName, "%s/board%d/TimeCalib_board%d_chip%d_%dMHz.xml", fCalibDirectory, fBoardSerialNumber,
              fBoardSerialNumber, chipIndex, i);
      rootNode = mxml_parse_file(fileName, error, sizeof(error), NULL);
      if (rootNode == NULL)
         continue;

      init->fFrequency[init->fNumberOfFrequencies] = new DRSBoard::TimeData::FrequencyData();
      init->fFrequency[init->fNumberOfFrequencies]->fFrequency = i;

      mainNode = mxml_find_node(rootNode, "/DRSTimeCalibration");

      for (l = 0; l < kNumberOfBins; l++) {
         node = mxml_subnode(mainNode, l + 2);
         init->fFrequency[init->fNumberOfFrequencies]->fBin[l] = strtod(mxml_get_value(node), &cstop);
      }
      mxml_free_tree(rootNode);
      init->fNumberOfFrequencies++;
   }
   if (init->fNumberOfFrequencies == 0) {
      printf("Board %d --> Could not find time calibration file\n", GetBoardSerialNumber());
   }

   if (index == fNumberOfTimeData)
      fNumberOfTimeData++;

   return fTimeData[index];
}

/*------------------------------------------------------------------*/

void DRSBoard::SetCalibrationDirectory(const char *calibrationDirectoryPath)
{
   strncpy(fCalibDirectory, calibrationDirectoryPath, strlen(calibrationDirectoryPath));
   fCalibDirectory[strlen(calibrationDirectoryPath)] = 0;
};

/*------------------------------------------------------------------*/

void DRSBoard::GetCalibrationDirectory(char *calibrationDirectoryPath)
{
   strncpy(calibrationDirectoryPath, fCalibDirectory, strlen(fCalibDirectory));
   calibrationDirectoryPath[strlen(fCalibDirectory)] = 0;
};

/*------------------------------------------------------------------*/

void DRSBoard::LinearRegression(double *x, double *y, int n, double *a, double *b)
{
   int i;
   double sx, sxx, sy, sxy;

   sx = sxx = sy = sxy = 0;
   for (i = 0; i < n; i++) {
      sx += x[i];
      sxx += x[i] * x[i];
      sy += y[i];
      sxy += x[i] * y[i];
   }

   *a = (n * sxy - sx * sy) / (n * sxx - sx * sx);
   *b = (sy - *a * sx) / n;
}

/*------------------------------------------------------------------*/

void DRSBoard::ReadSingleWaveform(int nChip, int nChan,
                                  unsigned short wf[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins],
                                  bool rotated)
{
   int i, j, k, tc;

   StartDomino();
   SoftTrigger();
   while (IsBusy());
   TransferWaves();

   for (i=0 ; i<nChip ; i++) {
      tc = GetTriggerCell(i);

      for (j=0 ; j<nChan ; j++) {
         GetRawWave(i, j, wf[i][j], rotated);
         if (!rotated) {
            for (k=0 ; k<kNumberOfBins ; k++) {
               /* do primary offset calibration */
               wf[i][j][k] = wf[i][j][k] - fCellOffset[j+i*9][(k + tc) % kNumberOfBins] + 32768;
            }
         }
      }
   }
}
     
static unsigned short swf[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
static float          center[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];

int DRSBoard::AverageWaveforms(DRSCallback *pcb, int nChip, int nChan,
                               int prog1, int prog2, unsigned short *awf, int n, bool rotated)
{
   int i, j, k, l, prog, old_prog = 0;
   float cm;

   if (pcb != NULL)
      pcb->Progress(prog1);

   memset(center, 0, sizeof(center));

   for (i=0 ; i<n; i++) {
      ReadSingleWaveform(nChip, nChan, swf, rotated);

      for (j=0 ; j<nChip ; j++) {
         for (k=0 ; k<nChan ; k++) {
            if (i > 5) {
               /* calculate and subtract common mode */
               for (l=0,cm=0 ; l<kNumberOfBins ; l++)
                  cm += swf[j][k][l] - 32768;
               cm /= kNumberOfBins;
               for (l=0 ; l<kNumberOfBins ; l++)
                  center[j][k][l] += swf[j][k][l]- cm;
            }
         }
      }

      prog = (int)(((double)i/n)*(prog2-prog1)+prog1);
      if (prog > old_prog) {
         old_prog = prog;
         if (pcb != NULL)
            pcb->Progress(prog);
      }
   }

   for (i=0 ; i<nChip ; i++)
      for (j=0 ; j<nChan ; j++)
         for (k=0 ; k<kNumberOfBins ; k++)
            awf[(i*nChan+j)*kNumberOfBins+k] = (unsigned short)(center[i][j][k]/(n-6) + 0.5);
   
   return 1;
}

int DRSBoard::RobustAverageWaveforms(DRSCallback *pcb, int nChip, int nChan,
                               int prog1, int prog2, unsigned short *awf, int n, bool rotated)
{
   int i, j, k, l, prog, old_prog = 0;

   if (pcb != NULL)
      pcb->Progress(prog1);

   Averager *ave = new Averager(nChip, nChan, kNumberOfBins, 200);
                               
   /* fill histograms */
   for (i=0 ; i<n ; i++) {
      ReadSingleWaveform(nChip, nChan, swf, rotated);
      for (j=0 ; j<nChip ; j++)
         for (k=0 ; k<nChan ; k++)
            for (l=0 ; l<kNumberOfBins ; l++)
               ave->Add(j, k, l, swf[j][k][l]);
               
      /* update progress bar */
      prog = (int)(((double)(i+10)/(n+10))*(prog2-prog1)+prog1);
      if (prog > old_prog) {
         old_prog = prog;
         if (pcb != NULL)
            pcb->Progress(prog);
      }
   }

   /*
   FILE *fh = fopen("calib.csv", "wt");
   for (i=40 ; i<60 ; i++) {
      for (j=0 ; j<WFH_SIZE ; j++)
         fprintf(fh, "%d;", wfh[0][0][i][j]);
      fprintf(fh, "\n");
   }
   fclose(fh);
   */


   for (i=0 ; i<nChip ; i++)
      for (j=0 ; j<nChan ; j++)
         for (k=0 ; k<kNumberOfBins ; k++)
            awf[(i*nChan+j)*kNumberOfBins+k] = (unsigned short)ave->RobustAverage(100, i, j, k);

   ave->SaveNormalizedDistribution("wv.csv", 0, 100);
   
   /*
   FILE *fh = fopen("calib.csv", "wt");
   for (i=40 ; i<60 ; i++) {
      fprintf(fh, "%d;", icenter[0][0][0] + (i - WFH_SIZE/2)*16);
      fprintf(fh, "%d;", wfh[0][0][0][i]);
      if (i == 50)
         fprintf(fh, "%d;", awf[0]);
      fprintf(fh, "\n");
   }
   fclose(fh);
   */


   if (pcb != NULL)
      pcb->Progress(prog2);
   
   delete ave;

   return 1;
}

/*------------------------------------------------------------------*/

int idx[4][10] = {
   {  0,  2,  4,  6,  8, 18, 20, 22, 24, 26 },
   {  1,  3,  5,  7, 39, 19, 21, 23, 25, 39 },
   {  9, 11, 13, 15, 17, 27, 29, 31, 33, 35 },
   { 10, 12, 14, 16, 39, 28, 30, 32, 34, 39 },
};

#define F1(x) ((int) (84.0/24 * (x)))
#define F2(x) ((int) (92.0/8 * (x)))

static unsigned short wft[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024],
                      wf1[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024],
                      wf2[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024],
                      wf3[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024];

int DRSBoard::CalibrateVolt(DRSCallback *pcb)
{
int    i, j, nChan, timingChan, chip, config, p, clkon, refclk, trg1, trg2, n_stuck, readchn, casc;
double f, r;
unsigned short buf[1024*16]; // 32 kB
   
   f       = fNominalFrequency;
   r       = fRange;
   clkon   = (GetCtrlReg() & BIT_TCAL_EN) > 0;
   refclk  = (GetCtrlReg() & BIT_REFCLK_SOURCE) > 0;
   trg1    = fTriggerEnable1;
   trg2    = fTriggerEnable2;
   readchn = fNumberOfReadoutChannels;
   casc    = fChannelCascading;

   Init();
   fNominalFrequency = f;
   SetRefclk(refclk);
   SetFrequency(fNominalFrequency, true);
   SetDominoMode(1);
   SetDominoActive(1);
   SetReadoutMode(1);
   SetInputRange(r);
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
      SelectClockSource(0);
   else if (fBoardType == 6)
      SetRefclk(refclk);
   EnableTrigger(0, 0);
   if (readchn == 5)
      SetChannelConfig(4, 8, 8); // even channel readout mode
   
   // WSROUT toggling causes some noise, so calibrate that out
   if (casc == 2) {
      if (fTransport == TR_USB2)
         SetChannelConfig(0, 8, 4);
      else
         SetChannelConfig(7, 8, 4);
   }

   StartDomino();

   nChan = 0;
   timingChan = 0;

   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      if (fBoardType == 9) {
         nChan = 8;
         timingChan = -1;
      } else {
         nChan = 9;
         timingChan = 8;
      }

      /* measure offset */
      if (fBoardType == 5)
         EnableAcal(0, 0); // no inputs signal is allowed during calibration!
      else
         EnableAcal(1, 0); // disconnect analog inputs
      EnableTcal(0, 0);
      Sleep(100);
      RobustAverageWaveforms(pcb, 1, nChan, 0, 25, wf1[0], 200, true);

      /* measure gain at upper range */
      EnableAcal(1, fRange+0.4);
      EnableTcal(0, 1);
      Sleep(100);
      RobustAverageWaveforms(pcb, 1, nChan, 25, 50, wf2[0], 200, true);

   } else if (fBoardType == 6) {
      if (fTransport == TR_USB2) {
         nChan = 36;
         timingChan = 8;
         memset(wf1, 0, sizeof(wf1));
         memset(wf2, 0, sizeof(wf2));
         memset(wf3, 0, sizeof(wf3));
         for (config=p=0 ; config<4 ; config++) {
            SetChannelConfig(config, 8, 8);

            /* measure offset */
            EnableAcal(1, 0);
            EnableTcal(0, 0);
            Sleep(100);
            RobustAverageWaveforms(pcb, 0, 10, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<5 ; i++)
               memcpy(wf1[idx[config][i]], wft[i*2], sizeof(float)*kNumberOfBins);
            RobustAverageWaveforms(pcb, 2, 10, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<5 ; i++)
               memcpy(wf1[idx[config][i+5]], wft[i*2], sizeof(float)*kNumberOfBins);

            /* measure gain at +400 mV */
            EnableAcal(1, 0.4);
            EnableTcal(0, 0);
            Sleep(100);
            RobustAverageWaveforms(pcb, 0, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<4 ; i++)
               memcpy(wf2[idx[config][i]], wft[i*2], sizeof(float)*kNumberOfBins);
            RobustAverageWaveforms(pcb, 2, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<4 ; i++)
               memcpy(wf2[idx[config][i+5]], wft[i*2], sizeof(float)*kNumberOfBins);

            /* measure gain at -400 mV */
            EnableAcal(1, -0.4);
            EnableTcal(0, 1);
            Sleep(100);
            RobustAverageWaveforms(pcb, 0, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<4 ; i++)
               memcpy(wf3[idx[config][i]], wft[i], sizeof(float)*kNumberOfBins);
            RobustAverageWaveforms(pcb, 2, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
            for (i=0 ; i<4 ; i++)
               memcpy(wf3[idx[config][i+5]], wft[i], sizeof(float)*kNumberOfBins);
         }
      } else {
         nChan = 36;
         timingChan = 8;
       
         /* measure offset */
         EnableAcal(0, 0); // no inputs signal is allowed during calibration!
         EnableTcal(0, 0);
         Sleep(100);
         RobustAverageWaveforms(pcb, 4, 9, 0, 25, wf1[0], 500, true);

         /* measure gain at upper range */
         EnableAcal(1, fRange+0.4);
         EnableTcal(0, 0);
         Sleep(100);
         RobustAverageWaveforms(pcb, 4, 9, 25, 50, wf2[0], 500, true);
      }
   }

   /* convert offsets and gains to 16-bit values */
   memset(fCellOffset, 0, sizeof(fCellOffset));
   n_stuck = 0;
   for (i=0 ; i<nChan ; i++) {
      for (j=0 ; j<kNumberOfBins; j++) {
         if (i % 9 == timingChan) {
            /* calculate offset and gain for timing channel */
            if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
               /* we have a +325mV and a -325mV value */
               fCellOffset[i][j] = (unsigned short) ((wf1[i][j]+wf2[i][j])/2+0.5);
               fCellGain[i][j]   = (wf2[i][j] - wf1[i][j])/65536.0*1000 / 650.0;
            } else {
               /* only have offset */
               fCellOffset[i][j] = wf1[i][j];
               fCellGain[i][j]   = 1;
            }
         } else {
            /* calculate offset and gain for data channel */
            fCellOffset[i][j] = wf1[i][j];
            if (fCellOffset[i][j] < 100) {
               // mark stuck pixel
               n_stuck ++;
               fCellOffset[i][j] = 0;
               fCellGain[i][j] = 1;
            } else
               fCellGain[i][j] = (wf2[i][j] - fCellOffset[i][j])/65536.0*1000 / ((0.4+fRange)*1000);
         }

         /* check gain */
         if (fCellGain[i][j] < 0.5 || fCellGain[i][j] > 1.1) {
            if ((fBoardType == 7 || fBoardType == 8 || fBoardType == 9) && i % 2 == 1) {
               /* channels are not connected, so don't print error */
            } else {
               printf("Gain of %6.3lf for channel %2d, cell %4d out of range 0.5 ... 1.1\n",
                  fCellGain[i][j], i, j);
            }
            fCellGain[i][j] = 1;
         }
      }
   }

   /*
   FILE *fh = fopen("calib.txt", "wt");
   for (i=0 ; i<nChan ; i++) {
      fprintf(fh, "CH%02d:", i);
      for (j=0 ; j<20 ; j++)
         fprintf(fh, " %5d", fCellOffset[i][j]-32768);
      fprintf(fh, "\n");
   }
   fclose(fh);
   */


   /* perform secondary calibration */
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8 || fBoardType == 9) {
      nChan = 9;
      timingChan = 8;

      /* measure offset */
      if (fBoardType == 5)
         EnableAcal(0, 0); // no inputs signal is allowed during calibration!
      else
         EnableAcal(1, 0); // disconnect analog inputs
      EnableTcal(0, 0);
      Sleep(100);
      AverageWaveforms(pcb, 1, 9, 50, 90, wf1[0], 500, false);
   } else if (fBoardType == 6 && fTransport == TR_VME) {
      nChan = 36;
      timingChan = 8;

      /* measure offset */
      EnableAcal(0, 0); // no inputs signal is allowed during calibration!
      EnableTcal(0, 0);
      Sleep(100);
      AverageWaveforms(pcb, 4, 9, 50, 75, wf1[0], 500, false);
   }

   /* convert offset to 16-bit values */
   memset(fCellOffset2, 0, sizeof(fCellOffset2));
   for (i=0 ; i<nChan ; i++)
      for (j=0 ; j<kNumberOfBins; j++)
         if (i % 9 != timingChan)
            fCellOffset2[i][j] = wf1[i][j];

   /*
   FILE *fh = fopen("calib.txt", "wt");
   for (i=0 ; i<nChan ; i++) {
      for (j=0 ; j<kNumberOfBins; j++)
         fprintf(fh, "%5d: %5d %5d\n", j, fCellOffset2[0][j]-32768, fCellOffset2[1][j]-32768);
      fprintf(fh, "\n");
   }
   fclose(fh);
   */


   if (fBoardType == 9) {
      /* write calibration CH0-CH7 to EEPROM page 1 */
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++) {
            buf[(i*1024+j)*2]   = fCellOffset[i][j];
            buf[(i*1024+j)*2+1] = (unsigned short) ((fCellGain[i][j] - 0.7) / 0.4 * 65535);
         }
      WriteEEPROM(1, buf, 1024*32);
      if (pcb != NULL)
         pcb->Progress(93);

     
      /* write secondary calibration to EEPROM page 2 */
      ReadEEPROM(2, buf, 1024*32);
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++)
            buf[(i*1024+j)*2] = fCellOffset2[i][j];
      WriteEEPROM(2, buf, 1024*32);
      if (pcb != NULL)
         pcb->Progress(96);

      /* update page # 0 */
      ReadEEPROM(0, buf, 4096); // 0-0x0FFF
      /* write calibration method */
      buf[2]  = (buf[2] & 0xFF00) | VCALIB_METHOD;
      /* write temperature and range */
      buf[10] = ((unsigned short) (GetTemperature() * 2 + 0.5) << 8) | ((signed char)(fRange * 100));
      buf[11] = 1; // EEPROM page #1+2
      WriteEEPROM(0, buf, 4096);
      fCellCalibratedRange = fRange;
      if (pcb != NULL)
         pcb->Progress(100);

     
   } else if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8) {
      /* write calibration CH0-CH7 to EEPROM page 1 */
      for (i=0 ; i<8 ; i++)
         for (j=0 ; j<1024; j++) {
            buf[(i*1024+j)*2]   = fCellOffset[i][j];
            buf[(i*1024+j)*2+1] = (unsigned short) ((fCellGain[i][j] - 0.7) / 0.4 * 65535);
         }
      WriteEEPROM(1, buf, 1024*32);

      /* write calibration CH8 and secondary calibration to EEPROM page 2 */
      for (j=0 ; j<1024; j++) {
         buf[j*2]   = fCellOffset[8][j];
         buf[j*2+1] = (unsigned short) ((fCellGain[8][j] - 0.7) / 0.4 * 65535);
      }
      for (i=0 ; i<4 ; i++)
         for (j=0 ; j<1024; j++) {
            buf[2*1024+(i*1024+j)*2]   = fCellOffset2[i*2][j];
            buf[2*1024+(i*1024+j)*2+1] = fCellOffset2[i*2+1][j];
         }
      WriteEEPROM(2, buf, 1024*5*4);

      /* write calibration method and range */
      ReadEEPROM(0, buf, 2048); // 0-0x0FFF
      buf[2] = VCALIB_METHOD_V4 | ((signed char)(fRange * 100)) << 8;
      WriteEEPROM(0, buf, 2048);
      fCellCalibratedRange = fRange;

   } else if (fBoardType == 6) {
      for (chip=0 ; chip<4 ; chip++) {
         /* write calibration of A0 to A7 to EEPROM page 1
                                 B0 to B7 to EEPROM page 2 and so on */

         for (i=0 ; i<8 ; i++)
            for (j=0 ; j<1024; j++) {
               buf[(i*1024+j)*2]   = fCellOffset[i+chip*9][j];
               buf[(i*1024+j)*2+1] = (unsigned short) ((fCellGain[i+chip*9][j] - 0.7) / 0.4 * 65535);
            }
         WriteEEPROM(1+chip, buf, 1024*32);
         if (pcb != NULL)
            pcb->Progress(75+chip*4);
       }

      /* write calibration A/B/C/D/CLK to EEPROM page 5 */
      ReadEEPROM(5, buf, 1024*4*4);
      for (chip=0 ; chip<4 ; chip++) {
         for (j=0 ; j<1024; j++) {
            buf[j*2+chip*0x0800]   = fCellOffset[8+chip*9][j];
            buf[j*2+1+chip*0x0800] = (unsigned short) ((fCellGain[8+chip*9][j] - 0.7) / 0.4 * 65535);
         }
      }
      WriteEEPROM(5, buf, 1024*4*4);
      if (pcb != NULL)
         pcb->Progress(90);

      /* write secondary calibration to EEPROM page 7 and 8 */
      for (i=0 ; i<8 ; i++) {
         for (j=0 ; j<1024; j++) {
            buf[i*0x800 + j*2]   = fCellOffset2[i][j];
            buf[i*0x800 + j*2+1] = fCellOffset2[i+9][j];
         }
      }
      WriteEEPROM(7, buf, 1024*32);
      if (pcb != NULL)
         pcb->Progress(94);

      for (i=0 ; i<8 ; i++) {
         for (j=0 ; j<1024; j++) {
            buf[i*0x800 + j*2]   = fCellOffset2[i+18][j];
            buf[i*0x800 + j*2+1] = fCellOffset2[i+27][j];
         }
      }
      WriteEEPROM(8, buf, 1024*32);
      if (pcb != NULL)
         pcb->Progress(98);

      /* write calibration method and range */
      ReadEEPROM(0, buf, 2048); // 0-0x0FFF
      buf[2] = VCALIB_METHOD | ((signed char)(fRange * 100)) << 8;
      WriteEEPROM(0, buf, 2048);
      fCellCalibratedRange = fRange;
      if (pcb != NULL)
         pcb->Progress(100);
   }

   if (n_stuck)
      printf("\nFound %d stuck pixels on this board\n", n_stuck);

   fVoltageCalibrationValid = true;

   /* remove calibration voltage */
   EnableAcal(0, 0);
   EnableTcal(clkon, 0);
   EnableTrigger(trg1, trg2);

   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::AnalyzeSlope(Averager *ave, int iIter, int nIter, int channel, float wf[kNumberOfBins], int tCell,
                           double cellDV[kNumberOfBins], double cellDT[kNumberOfBins])
{
   int i;
   float dv, llim, ulim;
   double sum, dtCell;
   
   if (fNominalFrequency > 3) {
      llim = -100;
      ulim =  100;
   } else {
      llim = -300;
      ulim =  300;
   }

   // rising edges ----

   // skip first cells after trigger cell
   for (i=tCell+5 ; i<tCell+kNumberOfBins-5 ; i++) {
      // test slope between previous and next cell to allow for negative cell width
      if (wf[(i+kNumberOfBins-1) % kNumberOfBins] < wf[(i+2) % kNumberOfBins] &&
          wf[i % kNumberOfBins] > llim &&
          wf[(i+1) % kNumberOfBins] < ulim) {
         
         // calculate delta_v
         dv = wf[(i+1) % kNumberOfBins] - wf[i % kNumberOfBins];
   
         // average delta_v
         ave->Add(0, channel, i % kNumberOfBins, dv);
      }
   }

   // falling edges ----
   
   // skip first cells after trigger cell
   for (i=tCell+5 ; i<tCell+kNumberOfBins-5 ; i++) {
      // test slope between previous and next cell to allow for negative cell width
      if (wf[(i+kNumberOfBins-1) % kNumberOfBins] > wf[(i+2) % kNumberOfBins] &&
          wf[i % kNumberOfBins] < ulim &&
          wf[(i+1) % kNumberOfBins] > llim) {
         
         // calcualte delta_v
         dv = wf[(i+1) % kNumberOfBins] - wf[i % kNumberOfBins];
         
         ave->Add(0, channel, i % kNumberOfBins, -dv);
      }
   }
   
   // calculate calibration every 100 events
   if ((iIter + 1) % 100 == 0) {
      // average over all 1024 dU
      sum = 0;
      for (i=0 ; i<kNumberOfBins ; i++) {
         
         if (fBoardType == 8)
            cellDV[i] = ave->Median(0, channel, i);
         else {
            // use empirically found limits for averaging
            if (fNominalFrequency >= 4)
               cellDV[i] = ave->RobustAverage(3, 0, channel, i);
            else if (fNominalFrequency >= 3)
               cellDV[i] = ave->RobustAverage(6, 0, channel, i);
            else
               cellDV[i] = ave->Median(0, channel, i);
         }
         
         sum += cellDV[i];
      }
     
      sum /= kNumberOfBins;
      dtCell = (float)1/fNominalFrequency;
     
      // here comes the central calculation, dT = dV/average * dt_cell
      for (i=0 ; i<kNumberOfBins ; i++)
         cellDT[i] = cellDV[i] / sum * dtCell;
   }
   
   return 1;
}

/*------------------------------------------------------------------*/

int DRSBoard::AnalyzePeriod(Averager *ave, int iIter, int nIter, int channel, float wf[kNumberOfBins], int tCell,
                            double cellDV[kNumberOfBins], double cellDT[kNumberOfBins])
{
int    i, i1, i2, j, nzx, zeroXing[1000], edge, n_correct, nest;
double damping, zeroLevel, tPeriod, corr, dv, dv_limit, corr_limit;
   
   /* calculate zero level */
   for (i=0,zeroLevel=0 ; i<1024 ; i++)
      zeroLevel += wf[i];
   zeroLevel /= 1024;

   /* correct for zero common mode */
   for (i=0 ; i<1024 ; i++)
      wf[i] -= (float)zeroLevel;

   /* estimate damping factor */
   if (fBoardType == 9)
      damping = 0.01;
   else
      damping = fNominalFrequency / nIter * 2   ;

   /* estimate number of zero crossings */
   nest = (int) (1/fNominalFrequency*1024 / (1/fTCALFrequency*1000));
   
   if (fNominalFrequency >= 4)
      dv_limit = 4;
   else if (fNominalFrequency >= 3)
      dv_limit = 6;
   else
      dv_limit = 10000;

   for (edge = 0 ; edge < 2 ; edge ++) {

      /* find edge zero crossing with wrap-around */
      for (i=tCell+5,nzx=0 ; i<tCell+1023-5 && nzx < (int)(sizeof(zeroXing)/sizeof(int)) ; i++) {
         dv = fabs(wf[(i+1) % 1024] - wf[i % 1024]);
         
         if (edge == 0) {
            if (wf[(i+1) % 1024] < 0 && wf[i % 1024] > 0) { // falling edge
               if (fBoardType != 9 || fabs(dv-cellDV[i % 1024]) < dv_limit) // only if DV is not more the dv_limit away from average
                  zeroXing[nzx++] = i % 1024;
            }
         } else {
            if (wf[(i+1) % 1024] > 0 && wf[i % 1024] < 0) { // rising edge
               if (fBoardType != 9 || fabs(dv-cellDV[i % 1024]) < dv_limit)
                  zeroXing[nzx++] = i % 1024;
            }
         }
      }

      /* abort if uncorrect number of edges is found */
      if (abs(nest - nzx) > nest / 3)
         return 0;

      for (i=n_correct=0 ; i<nzx-1 ; i++) {
         i1 = zeroXing[i];
         i2 = zeroXing[i+1];
         if (i1 == 1023 || i2 == 1023)
            continue;
         
         if (wf[(i1 + 1) % 1024] == 0 || wf[i2 % 1024] == 0)
            continue;
         
         /* first partial cell */
         tPeriod = cellDT[i1]*(1/(1-wf[i1]/wf[(i1 + 1) % 1024]));

         /* full cells between i1 and i2 */
         if (i2 < i1)
            i2 += 1024;
         
         for (j=i1+1 ; j<i2 ; j++)
            tPeriod += cellDT[j % 1024];
         
         /* second partial cell */
         tPeriod += cellDT[i2 % 1024]*(1/(1-wf[(i2+1) % 1024]/wf[i2 % 1024]));

         /* calculate correction to nominal period as a fraction */
         corr = (1/fTCALFrequency*1000) / tPeriod;

         /* skip very large corrections (noise) */
         if (fBoardType == 9 && fNominalFrequency >= 2)
            corr_limit = 0.001;
         else if (fBoardType == 9)
            corr_limit = 0.004;
         else
            corr_limit = 0.01;

         if (fBoardType == 9 && fabs(1-corr) > corr_limit)
            continue;

         /* remeber number of valid corrections */
         n_correct++;

         /* apply damping factor */
         corr = (corr-1)*damping + 1;

         /* apply from i1 to i2-1 inclusive */
         if (i1 == i2)
            continue;

         /* distribute correciton equally into bins inside the region ... */
         for (j=i1 ; j<=i2 ; j++)
            cellDT[j % 1024] *= corr;
           
         /* test correction */
         tPeriod = cellDT[i1]*(1/(1-wf[i1]/wf[(i1 + 1) % 1024]));
         for (j=i1+1 ; j<i2 ; j++)
            tPeriod += cellDT[j % 1024];
         tPeriod += cellDT[i2]*(1/(1-wf[(i2+1) % 1024]/wf[i2]));
      }

      if (n_correct < nzx/3)
         return 0;
   }

   return 1;
}

/*------------------------------------------------------------------*/


int DRSBoard::CalibrateTiming(DRSCallback *pcb)
{
   int    index, status, error, tCell, i, j, c, chip, mode, nIterPeriod, nIterSlope, clkon, phase, refclk, trg1, trg2, n_error, channel;
   double f, range, tTrue, tRounded, dT, t1[8], t2[8], cellDV[kNumberOfChipsMax*kNumberOfChannelsMax][kNumberOfBins];
   unsigned short buf[1024*16]; // 32 kB
   float  wf[1024];
   Averager *ave = NULL;
   
   nIterPeriod = 5000;
   nIterSlope  = 5000;
   n_error = 0;
   refclk  = 0;
   f       = fNominalFrequency;
   range   = fRange;
   clkon   = (GetCtrlReg() & BIT_TCAL_EN) > 0;
   if (fBoardType == 6)
      refclk  = (GetCtrlReg() & BIT_REFCLK_SOURCE) > 0;
   trg1    = fTriggerEnable1;
   trg2    = fTriggerEnable2;

   Init();
   fNominalFrequency = f;
   fTimingCalibratedFrequency = 0;
   if (fBoardType == 6) // don't set refclk for evaluation boards
      SetRefclk(refclk);
   SetFrequency(fNominalFrequency, true);
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8)
      fTCALFrequency = 132; // 132 MHz for EVAL1, for MEZZ this is set by ConfigureLMK
   else if (fBoardType == 9)
      fTCALFrequency = 100;
   SetDominoMode(1);
   SetDominoActive(1);
   SetReadoutMode(1);
   EnableTrigger(0, 0);
   if (fBoardType == 5 || fBoardType == 7) {
      EnableTcal(1, 0, 0);
      SelectClockSource(1); // 2nd quartz
   } else if (fBoardType == 8) {
      nIterSlope  = 0;
      nIterPeriod = 1500;
      EnableTcal(1, 0, 0);
      SelectClockSource(1); // 2nd quartz
      ave = new Averager(1, 1, 1024, 500); // one chip, 1 channel @ 1024 bins
   } else if (fBoardType == 9) {
      EnableTcal(1);
      nIterSlope  = 500;
      nIterPeriod = 500;
      ave = new Averager(1, 9, 1024, 500); // one chip, 9 channels @ 1024 bins
   }
   StartDomino();

   /* initialize time array */
   for (i=0 ; i<1024 ; i++)
      for (chip=0 ; chip<4 ; chip++)
         for (channel = 0 ; channel < 8 ; channel++) {
            fCellDT[chip][channel][i] = (float)1/fNominalFrequency;  // [ns]
         }

   error = 0;
   
   for (index = 0 ; index < nIterSlope+nIterPeriod ; index++) {
      if (index % 10 == 0)
         if (pcb)
            pcb->Progress(100*index/(nIterSlope+nIterPeriod));

      if (fTransport == TR_VME) {
         SoftTrigger();
         while (IsBusy());

         /* select random phase */
         phase = (rand() % 30) - 15;
         if (phase == 0)
            phase = 15;
         EnableTcal(1, 0, phase);

         StartDomino();
         TransferWaves();

         for (chip=0 ; chip<4 ; chip++) {
            tCell = GetStopCell(chip);
            GetWave(chip, 8, wf, true, tCell, 0, true);
            status = AnalyzePeriod(ave, index, nIterPeriod, 0, wf, tCell, cellDV[chip], fCellDT[chip][0]);

            if (!status)
               n_error++;

            if (n_error > nIterPeriod / 10) {
               error = 1;
               break;
            }
         }
      } else {
         if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8) { // DRS4 Evaluation board: 1 Chip
            SoftTrigger();
            while (IsBusy());

            StartDomino();
            TransferWaves();

            tCell = GetStopCell(0);
            GetWave(0, 8, wf, true, tCell, 0, true);
           
            if (index < nIterSlope)
               status = AnalyzeSlope(ave, index, nIterSlope, 0, wf, tCell, cellDV[0], fCellDT[0][0]);
            else
               status = AnalyzePeriod(ave, index, nIterPeriod, 0, wf, tCell, cellDV[0], fCellDT[0][0]);

            if (!status)
               n_error++;

            if (n_error > nIterPeriod / 10) {
               error = 1;
               break;
            }
         } else if (fBoardType == 9) { // DRS4 Evaluation board V5: all channels from one chip
            SoftTrigger();
            while (IsBusy());
           
            StartDomino();
            TransferWaves();
           
            // calibrate all channels individually
            for (channel = 0 ; channel < 8 ; channel+=2) {
               tCell = GetStopCell(0);
               GetWave(0, channel, wf, true, tCell, 0, true);
               
               if (index < nIterSlope)
                  status = AnalyzeSlope(ave, index, nIterSlope, channel, wf, tCell, cellDV[channel], fCellDT[0][channel]);
               else
                  status = AnalyzePeriod(ave, index, nIterPeriod, channel, wf, tCell, cellDV[channel], fCellDT[0][channel]);
               
               if (!status)
                  n_error++;
               
               if (n_error > nIterPeriod / 2) {
                  error = 1;
                  break;
               }
            }
            if (!status)
               break;

         } else {               // DRS4 Mezzanine board: 4 Chips
            for (mode=0 ; mode<2 ; mode++) {
               SetChannelConfig(mode*2, 8, 8);
               SoftTrigger();
               while (IsBusy());

               /* select random phase */
               phase = (rand() % 30) - 15;
               if (phase == 0)
                  phase = 15;
               EnableTcal(1, 0, phase);

               StartDomino();
               TransferWaves();

               for (chip=0 ; chip<4 ; chip+=2) {
                  tCell = GetStopCell(chip+mode);
                  GetWave(chip+mode, 8, wf, true, tCell, 0, true);
                  status = AnalyzePeriod(ave, index, nIterPeriod, 0, wf, tCell, cellDV[chip+mode], fCellDT[chip+mode][0]);

                  if (!status) {
                     error = 1;
                     break;
                  }
               }
               if (!status)
                  break;

            }
         }
      }
   }

   if (pcb)
      pcb->Progress(100);
   
   // DRS4 Evaluation board V5: copy even channels to odd channels (usually not connected)
   if (fBoardType == 9) {
      for (channel = 0 ; channel < 8 ; channel+=2)
         memcpy(fCellDT[0][channel+1], fCellDT[0][channel], sizeof(unsigned short)*1024);
   }

   // use following lines to save calibration into an ASCII file
#if 0
   FILE *fh;

   fh = fopen("cellt.csv", "wt");
   if (!fh)
      printf("Cannot open file \"cellt.csv\"\n");
   else {
      fprintf(fh, "index,dt_ch1,dt_ch2,dt_ch3,dt_ch4\n");
      for (i=0 ; i<1024 ; i++)
         fprintf(fh, "%4d,%5.3lf,%5.3lf,%5.3lf,%5.3lf\n", i, fCellDT[0][0][i], fCellDT[0][2][i], fCellDT[0][4][i], fCellDT[0][6][i]);
      fclose(fh);
   }
#endif
   
   if (fBoardType == 5 || fBoardType == 7 || fBoardType == 8) {
      /* write timing calibration to EEPROM page 0 */
      ReadEEPROM(0, buf, sizeof(buf));
      for (i=0,t1[0]=0 ; i<1024; i++)
         buf[i*2+1] = (unsigned short) (fCellDT[0][0][i] * 10000 + 0.5);

      /* write calibration method and frequency */
      buf[4] = TCALIB_METHOD_V4;
      buf[6] = (unsigned short)(fNominalFrequency*1000+0.5);
     
      fTimingCalibratedFrequency = fNominalFrequency;
      WriteEEPROM(0, buf, sizeof(buf));
     
      // copy calibration to all channels
      for (i=1 ; i<8 ; i++)
         for (j=0 ; j<1024; j++)
            fCellDT[0][i][j] = fCellDT[0][0][j];

   } else if (fBoardType == 9) {
 
      /* write timing calibration to EEPROM page 2 */
      ReadEEPROM(2, buf, sizeof(buf));
      for (i=0 ; i<8 ; i++) {
         tTrue = 0;    // true cellT
         tRounded = 0; // rounded cellT
         for (j=0 ; j<1024; j++) {
            tTrue += fCellDT[0][i][j];
            dT = tTrue - tRounded;
            // shift by 1 ns to allow negative widths
            dT = (unsigned short) (dT*10000+1000+0.5);
            tRounded += (dT - 1000) / 10000.0;
            buf[(i*1024+j)*2+1] = (unsigned short) dT;
         }
      }
      WriteEEPROM(2, buf, sizeof(buf));
     
      /* write calibration method and frequency to EEPROM page 0 */
      ReadEEPROM(0, buf, sizeof(buf));
      buf[4] = 1; // number of calibrations
      buf[2] = (TCALIB_METHOD << 8) | (buf[2] & 0xFF); // calibration method
      float fl = (float) fNominalFrequency;
      memcpy(&buf[8], &fl, sizeof(float)); // exact freqeuncy
     
      fTimingCalibratedFrequency = fNominalFrequency;
      WriteEEPROM(0, buf, sizeof(buf));
   
   } else {
   
      /* write timing calibration to EEPROM page 6 */
      ReadEEPROM(6, buf, sizeof(buf));
      for (c=0 ; c<4 ; c++)
         t1[c] = 0;
      for (i=0 ; i<1024; i++) {
         for (c=0 ; c<4 ; c++) {
            t2[c] = fCellDT[0][c][i] - t1[c];
            t2[c] = (unsigned short) (t2[c] * 10000 + 0.5);
            t1[c] += t2[c] / 10000.0;
         }
         buf[i*2]         = (unsigned short) t2[0];
         buf[i*2+1]       = (unsigned short) t2[1];
         buf[i*2+0x800]   = (unsigned short) t2[2];
         buf[i*2+0x800+1] = (unsigned short) t2[3];
      }
      WriteEEPROM(6, buf, sizeof(buf));

      /* write calibration method and frequency */
      ReadEEPROM(0, buf, 16);
      buf[4] = TCALIB_METHOD;
      buf[6] = (unsigned short) (fNominalFrequency * 1000 + 0.5);
      fTimingCalibratedFrequency = buf[6] / 1000.0;
      WriteEEPROM(0, buf, 16);
   }

   if (ave)
      delete ave;

   /* remove calibration voltage */
   EnableAcal(0, 0);
   EnableTcal(clkon, 0);
   SetInputRange(range);
   EnableTrigger(trg1, trg2);

   if (error)
      return 0;

   return 1;
}


/*------------------------------------------------------------------*/


void DRSBoard::RemoveSymmetricSpikes(short **wf, int nwf,
                                     short diffThreshold, int spikeWidth,
                                     short maxPeakToPeak, short spikeVoltage,
                                     int nTimeRegionThreshold)
{
   // Remove a specific kind of spike on DRS4.
   // This spike has some features,
   //  - Common on all the channels on a chip
   //  - Constant heigh and width
   //  - Two spikes per channel
   //  - Symmetric to cell #0.
   //
   // This is not general purpose spike-removing function.
   //
   // wf                   : Waveform data. cell#0 must be at bin0,
   //                        and number of bins must be kNumberOfBins.
   // nwf                  : Number of channels which "wf" holds.
   // diffThreshold        : Amplitude threshold to find peak
   // spikeWidth           : Width of spike
   // maxPeakToPeak        : When peak-to-peak is larger than this, the channel
   //                        is not used to find spikes.
   // spikeVoltage         : Amplitude of spikes. When it is 0, it is calculated in this function
   //                        from voltage difference from neighboring bins.
   // nTimeRegionThreshold : Requirement of number of time regions having spike at common position.
   //                        Total number of time regions is 2*"nwf".

   if (!wf || !nwf || !diffThreshold || !spikeWidth) {
      return;
   }

   int          ibin, jbin, kbin;
   double       v;
   int          nbin;
   int          iwf;
   short        maximum, minimum;
   int          spikeCount[kNumberOfBins / 2];
   int          spikeCountSum[kNumberOfBins / 2] = {0};
   bool         largePulse[kNumberOfChannelsMax * 2] = {0};
   const short  diffThreshold2 = diffThreshold + diffThreshold;

   const short  maxShort = 0xFFFF>>1;
   const short  minShort = -maxShort - 1;

   // search spike
   for (iwf = 0; iwf < nwf; iwf++) {
      // first half
      memset(spikeCount, 0, sizeof(spikeCount));
      maximum = minShort;
      minimum = maxShort;
      for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
         jbin = ibin;
         maximum = max(maximum, wf[iwf][jbin]);
         minimum = min(minimum, wf[iwf][jbin]);
         if (jbin - 1 >= 0 && jbin + spikeWidth < kNumberOfBins) {
            v = 0;
            nbin = 0;
            for (kbin = 0; kbin < spikeWidth; kbin++) {
               v += wf[iwf][jbin + kbin];
               nbin++;
            }
            if ((nbin == 2 && v - (wf[iwf][jbin - 1] + wf[iwf][jbin + spikeWidth]) > diffThreshold2) ||
                (nbin != 2 && nbin && v / nbin - (wf[iwf][jbin - 1] + wf[iwf][jbin + spikeWidth]) / 2 > diffThreshold)) {
               spikeCount[ibin]++;
            }
         }
      }
      if (maximum != minShort && minimum != maxShort &&
          (!maxPeakToPeak || maximum - minimum < maxPeakToPeak)) {
         for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
            spikeCountSum[ibin] += spikeCount[ibin];
         }
         largePulse[iwf] = false;
#if 0 /* this part can be enabled to skip checking other channels */
         if (maximum != minShort && minimum != maxShort &&
             maximum - minimum < diffThreshold) {
            return;
         }
#endif
      } else {
         largePulse[iwf] = true;
      }

      // second half
      memset(spikeCount, 0, sizeof(spikeCount));
      maximum = minShort;
      minimum = maxShort;
      for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
         jbin = kNumberOfBins - 1 - ibin;
         maximum = max(maximum, wf[iwf][jbin]);
         minimum = min(minimum, wf[iwf][jbin]);
         if (jbin + 1 < kNumberOfBins && jbin - spikeWidth >= 0) {
            v = 0;
            nbin = 0;
            for (kbin = 0; kbin < spikeWidth; kbin++) {
               v += wf[iwf][jbin - kbin];
               nbin++;
            }
            if ((nbin == 2 && v - (wf[iwf][jbin + 1] + wf[iwf][jbin - spikeWidth]) > diffThreshold2) ||
                (nbin != 2 && nbin && v / nbin - (wf[iwf][jbin + 1] + wf[iwf][jbin - spikeWidth]) / 2 > diffThreshold)) {
               spikeCount[ibin]++;
            }
         }
      }
      if (maximum != minShort && minimum != maxShort &&
          maximum - minimum < maxPeakToPeak) {
         for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
            spikeCountSum[ibin] += spikeCount[ibin];
         }
         largePulse[iwf + nwf] = false;
#if 0 /* this part can be enabled to skip checking other channels */
         if (maximum != minShort && minimum != maxShort &&
             maximum - minimum < diffThreshold) {
            return;
         }
#endif
      } else {
         largePulse[iwf + nwf] = true;
      }
   }

   // Find common spike
   int commonSpikeBin = -1;
   int commonSpikeMax = -1;
   for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
      if (commonSpikeMax < spikeCountSum[ibin]) {
         commonSpikeMax = spikeCountSum[ibin];
         commonSpikeBin = ibin;
      }
   }

   if (spikeCountSum[commonSpikeBin] >= nTimeRegionThreshold) {
      if (spikeVoltage == 0) {
         // Estimate spike amplitude
         double  baseline      = 0;
         int    nBaseline      = 0;
         double  peakAmplitude = 0;
         int    nPeakAmplitude = 0;
         for (iwf = 0; iwf < nwf; iwf++) {
            // first half
            if (!largePulse[iwf]) {
               // baseline
               if ((jbin = commonSpikeBin - 1) >= 0 && jbin < kNumberOfBins) {
                  baseline += wf[iwf][jbin];
                  nBaseline++;
               }
               if ((jbin = commonSpikeBin + spikeWidth + 1) >= 0 && jbin < kNumberOfBins) {
                  baseline += wf[iwf][jbin];
                  nBaseline++;
               }
               // spike
               for (ibin = 0; ibin < spikeWidth; ibin++) {
                  if ((jbin = commonSpikeBin + ibin) >= 0 && jbin < kNumberOfBins) {
                     peakAmplitude += wf[iwf][jbin];
                     nPeakAmplitude++;
                  }
               }
            }

            // second half
            if (!largePulse[iwf + nwf]) {
               // baseline
               if ((jbin = kNumberOfBins - 1 - commonSpikeBin + 1) >= 0 && jbin < kNumberOfBins) {
                  baseline += wf[iwf][jbin];
                  nBaseline++;
               }
               if ((jbin = kNumberOfBins - 1 - commonSpikeBin - spikeWidth - 1) >= 0 && jbin < kNumberOfBins) {
                  baseline += wf[iwf][jbin];
                  nBaseline++;
               }
               // spike
               for (ibin = 0; ibin < spikeWidth; ibin++) {
                  if ((jbin = kNumberOfBins - 1 - commonSpikeBin - ibin) >= 0 && jbin < kNumberOfBins) {
                     peakAmplitude += wf[iwf][jbin];
                     nPeakAmplitude++;
                  }
               }
            }
         }
         if (nBaseline && nPeakAmplitude) {
            baseline /= nBaseline;
            peakAmplitude /= nPeakAmplitude;
            spikeVoltage = static_cast<short>(peakAmplitude - baseline);
         } else {
            spikeVoltage = 0;
         }
      }

      // Remove spike
      if (spikeVoltage > 0) {
         for (iwf = 0; iwf < nwf; iwf++) {
            for (ibin = 0; ibin < spikeWidth; ibin++) {
               if ((jbin = commonSpikeBin + ibin) >= 0 && jbin < kNumberOfBins) {
                  wf[iwf][jbin] -= spikeVoltage;
               }
               if ((jbin = kNumberOfBins - 1 - commonSpikeBin - ibin) >= 0 && jbin < kNumberOfBins) {
                  wf[iwf][jbin] -= spikeVoltage;
               }
            }
         }
      }
   }
}

/*------------------------------------------------------------------*/

void ResponseCalibration::SetCalibrationParameters(int numberOfPointsLowVolt, int numberOfPoints,
                                                   int numberOfMode2Bins, int numberOfSamples,
                                                   int numberOfGridPoints, int numberOfXConstPoints,
                                                   int numberOfXConstGridPoints, double triggerFrequency,
                                                   int showStatistics)
{
   DeleteFields();
   InitFields(numberOfPointsLowVolt, numberOfPoints, numberOfMode2Bins, numberOfSamples, numberOfGridPoints,
              numberOfXConstPoints, numberOfXConstGridPoints, triggerFrequency, showStatistics);
}

/*------------------------------------------------------------------*/

void ResponseCalibration::ResetCalibration()
{
   int i;
   for (i = 0; i < kNumberOfChipsMax; i++)
      fCalibrationData[i]->fRead = false;
   fCurrentPoint = 0;
   fCurrentLowVoltPoint = 0;
   fCurrentSample = 0;
   fCurrentFitChannel = 0;
   fCurrentFitBin = 0;
   fRecorded = false;
   fFitted = false;
   fOffset = false;
};

/*------------------------------------------------------------------*/

bool ResponseCalibration::WriteCalibration(unsigned int chipIndex)
{
   if (!fOffset)
      return false;
   if (fBoard->GetDRSType() == 3)
      return WriteCalibrationV4(chipIndex);
   else
      return WriteCalibrationV3(chipIndex);
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::WriteCalibrationV3(unsigned int chipIndex)
{
   if (!fOffset)
      return false;

   int ii, j, k;
   char str[1000];
   char strt[1000];
   short tempShort;
   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;

   // Open File
   fBoard->GetCalibrationDirectory(strt);
   sprintf(str, "%s/board%d", strt, fBoard->GetBoardSerialNumber());
   if (MakeDir(str) == -1) {
      printf("Error: Cannot create directory \"%s\"\n", str);
      return false;
   }
   sprintf(str, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", strt, fBoard->GetBoardSerialNumber(),
           fBoard->GetBoardSerialNumber(), chipIndex, static_cast < int >(fBoard->GetNominalFrequency() * 1000));
   fCalibFile = fopen(str, "wb");
   if (fCalibFile == NULL) {
      printf("Error: Cannot write to file \"%s\"\n", str);
      return false;
   }
   // Write File
   fwrite(&data->fNumberOfGridPoints, 1, 1, fCalibFile);
   tempShort = static_cast < short >(data->fStartTemperature) * 10;
   fwrite(&tempShort, 2, 1, fCalibFile);
   tempShort = static_cast < short >(data->fEndTemperature) * 10;
   fwrite(&tempShort, 2, 1, fCalibFile);
   fwrite(&data->fMin, 4, 1, fCalibFile);
   fwrite(&data->fMax, 4, 1, fCalibFile);
   fwrite(&data->fNumberOfLimitGroups, 1, 1, fCalibFile);

   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      chn = data->fChannel[ii];
      for (j = 0; j < kNumberOfBins; j++) {
         fwrite(&chn->fLimitGroup[j], 1, 1, fCalibFile);
         fwrite(&chn->fLookUpOffset[j], 2, 1, fCalibFile);
         fwrite(&chn->fNumberOfLookUpPoints[j], 1, 1, fCalibFile);
         for (k = 0; k < chn->fNumberOfLookUpPoints[j]; k++) {
            fwrite(&chn->fLookUp[j][k], 1, 1, fCalibFile);
         }
         for (k = 0; k < data->fNumberOfGridPoints; k++) {
            fwrite(&chn->fData[j][k], 2, 1, fCalibFile);
         }
         fwrite(&chn->fOffsetADC[j], 2, 1, fCalibFile);
         fwrite(&chn->fOffset[j], 2, 1, fCalibFile);
      }
   }
   fclose(fCalibFile);

   printf("Calibration successfully written to\n\"%s\"\n", str);
   return true;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::WriteCalibrationV4(unsigned int chipIndex)
{
   if (!fOffset)
      return false;

   int ii, j;
   char str[1000];
   char strt[1000];
   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;

   // Open File
   fBoard->GetCalibrationDirectory(strt);
   sprintf(str, "%s/board%d", strt, fBoard->GetBoardSerialNumber());
   if (MakeDir(str) == -1) {
      printf("Error: Cannot create directory \"%s\"\n", str);
      return false;
   }
   sprintf(str, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", strt, fBoard->GetBoardSerialNumber(),
           fBoard->GetBoardSerialNumber(), chipIndex, static_cast < int >(fBoard->GetNominalFrequency() * 1000));
   fCalibFile = fopen(str, "wb");
   if (fCalibFile == NULL) {
      printf("Error: Cannot write to file \"%s\"\n", str);
      return false;
   }
   // Write File
   for (ii = 0; ii < kNumberOfCalibChannelsV4; ii++) {
      chn = data->fChannel[ii];
      for (j = 0; j < kNumberOfBins; j++) {
         fwrite(&chn->fOffset[j], 2, 1, fCalibFile);
         fwrite(&chn->fGain[j], 2, 1, fCalibFile);
      }
   }
   fclose(fCalibFile);

   printf("Calibration successfully written to\n\"%s\"\n", str);
   return true;
}

/*------------------------------------------------------------------*/

void ResponseCalibration::CalibrationTrigger(int mode, double voltage)
{
   fBoard->Reinit();
   fBoard->EnableAcal(mode, voltage);
   fBoard->StartDomino();
   fBoard->SoftTrigger();
   while (fBoard->IsBusy()) {
   }
}

/*------------------------------------------------------------------*/

void ResponseCalibration::CalibrationStart(double voltage)
{
   fBoard->SetDominoMode(1);
   fBoard->EnableAcal(0, voltage);
   fBoard->StartDomino();
   fBoard->IsBusy();
   fBoard->IsBusy();
   fBoard->IsBusy();
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::RecordCalibrationPoints(int chipNumber)
{
   if (!fInitialized)
      return true;
   if (fBoard->GetDRSType() == 3)
      return RecordCalibrationPointsV4(chipNumber);
   else
      return RecordCalibrationPointsV3(chipNumber);
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::RecordCalibrationPointsV3(int chipNumber)
{
   int j, k, ii;
   int notdone, nsample;
   double voltage;
   float mean;
   const double minVolt = 0.006;
   const double xpos[50] =
       { 0.010, 0.027, 0.052, 0.074, 0.096, 0.117, 0.136, 0.155, 0.173, 0.191, 0.208, 0.226, 0.243, 0.260,
      0.277, 0.294, 0.310,
      0.325, 0.342, 0.358, 0.374, 0.390, 0.406, 0.422, 0.439, 0.457, 0.477, 0.497, 0.520, 0.546, 0.577, 0.611,
      0.656, 0.710,
      0.772, 0.842, 0.916,
      0.995, 1.075, 1.157, 1.240, 1.323, 1.407, 1.490, 1.575, 1.659, 1.744, 1.829, 1.914, 2.000
   };

   // Initialisations
   if (fCurrentLowVoltPoint == 0) {
      fBoard->SetDAC(fBoard->fDAC_CLKOFS, 0);
      // Record Temperature
      fCalibrationData[chipNumber]->fStartTemperature = static_cast < float >(fBoard->GetTemperature());
   }
   // Record current Voltage
   if (fCurrentLowVoltPoint < fNumberOfPointsLowVolt)
      voltage =
          (xpos[0] - minVolt) * fCurrentLowVoltPoint / static_cast <
          double >(fNumberOfPointsLowVolt) + minVolt;
   else
   voltage = xpos[fCurrentPoint];
   fBoard->SetCalibVoltage(voltage);
   fResponseY[fCurrentPoint + fCurrentLowVoltPoint] = static_cast < float >(voltage) * 1000;

   // Loop Over Number Of Samples For Statistics
   for (j = 0; j < fNumberOfSamples; j++) {
      // Read Out Second Part of the Waveform
      CalibrationTrigger(3, voltage);
      fBoard->TransferWaves();
      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
         fBoard->GetRawWave(chipNumber, ii, fWaveFormMode3[ii][j]);
      }
      // Read Out First Part of the Waveform
      CalibrationStart(voltage);
      CalibrationTrigger(2, voltage);
      fBoard->TransferWaves();
      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
         fBoard->GetRawWave(chipNumber, ii, fWaveFormMode2[ii][j]);
      }
      CalibrationStart(voltage);
   }
   // Average Sample Points
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      for (k = 0; k < kNumberOfBins; k++) {
         fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] = 0;
         for (j = 0; j < fNumberOfSamples; j++) {
            fSampleUsed[j] = 1;
            if (k < fNumberOfMode2Bins)
               fSamples[j] = fWaveFormMode2[ii][j][k];
            else
               fSamples[j] = fWaveFormMode3[ii][j][k];
            fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] += fSamples[j];
         }
         mean = fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] / fNumberOfSamples;
         notdone = 1;
         nsample = fNumberOfSamples;
         while (notdone) {
            notdone = 0;
            for (j = 0; j < fNumberOfSamples; j++) {
               if (fSampleUsed[j] && abs(static_cast < int >(fSamples[j] - mean)) > 3) {
                  notdone = 1;
                  fSampleUsed[j] = 0;
                  nsample--;
                  fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] -= fSamples[j];
                  mean = fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] / nsample;
               }
            }
         }
         fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] = mean;
      }
   }
   if (fCurrentLowVoltPoint < fNumberOfPointsLowVolt)
      fCurrentLowVoltPoint++;
   else
      fCurrentPoint++;

   if (fCurrentPoint == fNumberOfPoints) {
      fCalibrationData[chipNumber]->fEndTemperature = static_cast < float >(fBoard->GetTemperature());
      fRecorded = true;
      fFitted = false;
      fOffset = false;
      fCalibrationData[chipNumber]->fRead = false;
      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
      fBoard->SetCalibVoltage(0.0);
      fBoard->EnableAcal(1, 0.0);
      fBoard->SetDAC(fBoard->fDAC_CLKOFS, 0.0);
      return true;
   }

   return false;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::RecordCalibrationPointsV4(int chipNumber)
{
   int i, j, k, n;
   double voltage, s, s2, average;

   if (fCurrentPoint == 0) {
      fBoard->SetDominoMode(1);
      fBoard->EnableAcal(1, 0);
      fBoard->SoftTrigger();
      while (fBoard->IsBusy());
      fBoard->StartDomino();
      fCalibrationData[chipNumber]->fStartTemperature = static_cast < float >(fBoard->GetTemperature());
   }
   voltage = 1.0 * fCurrentPoint / (static_cast < double >(fNumberOfPoints) - 1) +0.1;
   fBoard->SetCalibVoltage(voltage);
   Sleep(10);
   fBoard->SetCalibVoltage(voltage);
   Sleep(10);

   // One dummy cycle for unknown reasons
   fBoard->SoftTrigger();
   while (fBoard->IsBusy());
   fBoard->StartDomino();
   Sleep(50);
   fBoard->TransferWaves();

   // Loop over number of samples for statistics
   for (i = 0; i < fNumberOfSamples; i++) {
      if (fBoard->Debug()) {
         printf("%02d:%02d\r", fNumberOfPoints - fCurrentPoint, fNumberOfSamples - i);
         fflush(stdout);
      }


      fBoard->SoftTrigger();
      while (fBoard->IsBusy());
      fBoard->StartDomino();
      Sleep(50);
      fBoard->TransferWaves();
      for (j = 0; j < kNumberOfCalibChannelsV4; j++) {
         fBoard->GetRawWave(chipNumber, j, fWaveFormMode3[j][i]);
      }
   }

   // Calculate averages
   for (i = 0; i < kNumberOfCalibChannelsV4; i++) {
      for (k = 0; k < kNumberOfBins; k++) {
         s = s2 = 0;

         for (j = 0; j < fNumberOfSamples; j++) {
            s += fWaveFormMode3[i][j][k];
            s2 += fWaveFormMode3[i][j][k] * fWaveFormMode3[i][j][k];
         }
         n = fNumberOfSamples;
         average = s / n;

         fResponseX[i][k][fCurrentPoint] = static_cast < float >(average);
      }
   }

   fCurrentPoint++;
   if (fCurrentPoint == fNumberOfPoints) {
      fCalibrationData[chipNumber]->fEndTemperature = static_cast < float >(fBoard->GetTemperature());
      fRecorded = true;
      return true;
   }

   return false;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::FitCalibrationPoints(int chipNumber)
{
   if (!fRecorded || fFitted)
      return true;
   if (fBoard->GetDRSType() == 3)
      return FitCalibrationPointsV4(chipNumber);
   else
      return FitCalibrationPointsV3(chipNumber);
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::FitCalibrationPointsV3(int chipNumber)
{
   int i, j, k;
   float x1, x2, y1, y2;
   float uu;
   float yc, yr;
   float xminExt, xrangeExt;
   float xmin, xrange;
   float average, averageError, averageExt, averageErrorExt;
   unsigned short i0, i1;

   CalibrationData *data = fCalibrationData[chipNumber];
   CalibrationData::CalibrationDataChannel * chn = data->fChannel[fCurrentFitChannel];

   data->DeletePreCalculatedBSpline();

   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0) {
      data->fNumberOfLimitGroups = 0;
      data->fMin = 100000;
      data->fMax = -100000;
      for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
         for (j = 0; j < kNumberOfBins; j++) {
            if (data->fMin > fResponseX[i][j][fNumberOfPointsLowVolt + fNumberOfPoints - 1])
               data->fMin = fResponseX[i][j][fNumberOfPointsLowVolt + fNumberOfPoints - 1];
            if (data->fMax < fResponseX[i][j][fNumberOfPointsLowVolt])
               data->fMax = fResponseX[i][j][fNumberOfPointsLowVolt];
         }
      }
   }
   // Low Volt
   i0 = static_cast < unsigned short >(fResponseX[fCurrentFitChannel][fCurrentFitBin][0]);
   i1 = static_cast <
       unsigned short >(fResponseX[fCurrentFitChannel][fCurrentFitBin][fNumberOfPointsLowVolt]) + 1;
   chn->fLookUpOffset[fCurrentFitBin] = i0;
   delete chn->fLookUp[fCurrentFitBin];
   if (i0 - i1 + 1 < 2) {
      chn->fNumberOfLookUpPoints[fCurrentFitBin] = 2;
      chn->fLookUp[fCurrentFitBin] = new unsigned char[2];
      chn->fLookUp[fCurrentFitBin][0] = 0;
      chn->fLookUp[fCurrentFitBin][1] = 0;
   } else {
      chn->fNumberOfLookUpPoints[fCurrentFitBin] = i0 - i1 + 1;
      chn->fLookUp[fCurrentFitBin] = new unsigned char[i0 - i1 + 1];
      for (i = 0; i < i0 - i1 + 1; i++) {
         for (j = 0; j < fNumberOfPointsLowVolt; j++) {
            if (i0 - i >= fResponseX[fCurrentFitChannel][fCurrentFitBin][j + 1]) {
               x1 = fResponseX[fCurrentFitChannel][fCurrentFitBin][j];
               x2 = fResponseX[fCurrentFitChannel][fCurrentFitBin][j + 1];
               y1 = fResponseY[j];
               y2 = fResponseY[j + 1];
               chn->fLookUp[fCurrentFitBin][i] =
                   static_cast < unsigned char >(((y2 - y1) * (i0 - i - x1) / (x2 - x1) + y1) / fPrecision);
               break;
            }
         }
      }
   }

   // Copy Points
   for (i = 0; i < fNumberOfPoints; i++) {
      fPntX[0][i] = fResponseX[fCurrentFitChannel][fCurrentFitBin][fNumberOfPointsLowVolt + i];
      fPntY[0][i] = fResponseY[fNumberOfPointsLowVolt + i];
   }
   // Fit BSpline
   for (i = 0; i < fNumberOfPoints; i++) {
      fUValues[0][i] = static_cast < float >(1 - i / (fNumberOfPoints - 1.));
   }
   if (!Approx(fPntX[0], fUValues[0], fNumberOfPoints, fNumberOfGridPoints, fResX[fCurrentFitBin]))
      return true;
   if (!Approx(fPntY[0], fUValues[0], fNumberOfPoints, fNumberOfGridPoints, fRes[fCurrentFitBin]))
      return true;

   // X constant fit
   for (k = 0; k < fNumberOfXConstPoints - 2; k++) {
      fPntX[1][k + 1] =
          GetValue(fResX[fCurrentFitBin],
                   static_cast < float >(1 - k / static_cast < float >(fNumberOfXConstPoints - 3)),
                   fNumberOfGridPoints);
      fPntY[1][k + 1] =
          GetValue(fRes[fCurrentFitBin],
                   static_cast < float >(1 - k / static_cast < float >(fNumberOfXConstPoints - 3)),
                   fNumberOfGridPoints);
   }
   xmin = fPntX[1][fNumberOfXConstPoints - 2];
   xrange = fPntX[1][1] - xmin;

   for (i = 0; i < fNumberOfXConstPoints - 2; i++) {
      fUValues[1][i + 1] = (fPntX[1][i + 1] - xmin) / xrange;
   }

   if (!Approx
       (&fPntY[1][1], &fUValues[1][1], fNumberOfXConstPoints - 2, fNumberOfXConstGridPoints, chn->fTempData))
      return true;

   // error statistics
   if (fShowStatistics) {
      for (i = 0; i < fNumberOfPoints; i++) {
         uu = (fPntX[0][i] - xmin) / xrange;
         yc = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
         yr = fPntY[0][i];
         fStatisticsApprox[i][fCurrentFitBin + fCurrentFitChannel * kNumberOfBins] = yc - yr;
      }
   }
   // Add min and max point
   chn->fLimitGroup[fCurrentFitBin] = 0;
   while (xmin - kBSplineXMinOffset > data->fMin + kBSplineXMinOffset * chn->fLimitGroup[fCurrentFitBin]) {
      chn->fLimitGroup[fCurrentFitBin]++;
   }
   if (data->fNumberOfLimitGroups <= chn->fLimitGroup[fCurrentFitBin])
      data->fNumberOfLimitGroups = chn->fLimitGroup[fCurrentFitBin] + 1;
   xminExt = data->fMin + kBSplineXMinOffset * chn->fLimitGroup[fCurrentFitBin];
   xrangeExt = data->fMax - xminExt;

   fPntX[1][0] = data->fMax;
   uu = (fPntX[1][0] - xmin) / xrange;
   fPntY[1][0] = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);

   fPntX[1][fNumberOfXConstPoints - 1] = xminExt;
   uu = (fPntX[1][fNumberOfXConstPoints - 1] - xmin) / xrange;
   fPntY[1][fNumberOfXConstPoints - 1] = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);

   for (i = 0; i < fNumberOfXConstPoints; i++) {
      fUValues[1][i] = (fPntX[1][i] - xminExt) / xrangeExt;
   }

   if (!Approx(fPntY[1], fUValues[1], fNumberOfXConstPoints, fNumberOfXConstGridPoints, chn->fTempData))
      return true;

   // error statistics
   if (fShowStatistics) {
      for (i = 0; i < fNumberOfPoints; i++) {
         uu = (fPntX[0][i] - xminExt) / xrangeExt;
         yc = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
         yr = fPntY[0][i];
         fStatisticsApproxExt[i][fCurrentFitBin + fCurrentFitChannel * kNumberOfBins] = yc - yr;
      }
   }
   for (i = 0; i < fNumberOfXConstGridPoints; i++) {
      chn->fData[fCurrentFitBin][i] = static_cast < short >(chn->fTempData[i] / fPrecision);
   }

   // write end of file
   fCurrentFitBin++;
   if (fCurrentFitBin == kNumberOfBins) {
      fCurrentFitChannel++;
      fCurrentFitBin = 0;
   }
   if (fCurrentFitChannel == kNumberOfCalibChannelsV3) {
      if (fShowStatistics) {
         for (i = 0; i < fNumberOfPoints; i++) {
            average = 0;
            averageError = 0;
            averageExt = 0;
            averageErrorExt = 0;
            for (j = 0; j < kNumberOfCalibChannelsV3 * kNumberOfBins; j++) {
               average += fStatisticsApprox[i][j];
               averageError += fStatisticsApprox[i][j] * fStatisticsApprox[i][j];
               averageExt += fStatisticsApproxExt[i][j];
               averageErrorExt += fStatisticsApproxExt[i][j] * fStatisticsApproxExt[i][j];
            }
            average /= kNumberOfCalibChannelsV3 * kNumberOfBins;
            averageError =
                sqrt((averageError -
                      average * average / kNumberOfCalibChannelsV3 * kNumberOfBins) /
                     (kNumberOfCalibChannelsV3 * kNumberOfBins - 1));
            averageExt /= kNumberOfCalibChannelsV3 * kNumberOfBins;
            averageErrorExt =
                sqrt((averageErrorExt -
                      averageExt * averageExt / kNumberOfCalibChannelsV3 * kNumberOfBins) /
                     (kNumberOfCalibChannelsV3 * kNumberOfBins - 1));
            printf("Error at %3.1f V : % 2.3f +- % 2.3f ; % 2.3f +- % 2.3f\n", fPntY[0][i], average,
                   averageError, averageExt, averageErrorExt);
         }
      }
      fFitted = true;
      fOffset = false;
      fCalibrationData[chipNumber]->fRead = true;
      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
      data->PreCalculateBSpline();
      return true;
   }
   return false;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::FitCalibrationPointsV4(int chipNumber)
{
   if (!fRecorded || fFitted)
      return true;
   int i;
   double par[2];
   static int error;

   CalibrationData *data = fCalibrationData[chipNumber];
   CalibrationData::CalibrationDataChannel * chn = data->fChannel[fCurrentFitChannel];

   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0) {
      error = 0;
      for (i = 0; i < fNumberOfPoints; i++)
         fWWFit[i] = 1;
   }

   for (i = 0; i < fNumberOfPoints; i++) {
      fXXFit[i] = 1.0 * i / (static_cast < double >(fNumberOfPoints) - 1) +0.1;
      fYYFit[i] = fResponseX[fCurrentFitChannel][fCurrentFitBin][i];
      if (fCurrentFitBin == 10 && fCurrentFitChannel == 1) {
         fXXSave[i] = fXXFit[i];
         fYYSave[i] = fYYFit[i];
      }
   }

   // DRSBoard::LinearRegression(fXXFit, fYYFit, fNumberOfPoints, &par[1], &par[0]);
   // exclude first two points (sometimes are on limit of FADC)
   DRSBoard::LinearRegression(fXXFit + 2, fYYFit + 2, fNumberOfPoints - 2, &par[1], &par[0]);

   chn->fOffset[fCurrentFitBin] = static_cast < unsigned short >(par[0] + 0.5);
   chn->fGain[fCurrentFitBin] = static_cast < unsigned short >(par[1] + 0.5);

   // Remember min/max of gain
   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0)
      fGainMin = fGainMax = chn->fGain[0];
   if (chn->fGain[fCurrentFitBin] < fGainMin)
      fGainMin = chn->fGain[fCurrentFitBin];
   if (chn->fGain[fCurrentFitBin] > fGainMax)
      fGainMax = chn->fGain[fCurrentFitBin];

   // abort if outside normal region
   if (chn->fGain[fCurrentFitBin] / 4096.0 < 0.8 || chn->fGain[fCurrentFitBin] / 4096.0 > 1) {
      error++;

      if (error < 20)
         printf("Gain=%1.3lf for bin %d on channel %d on chip %d outside valid region\n",
                chn->fGain[fCurrentFitBin] / 4096.0, fCurrentFitBin, fCurrentFitChannel, chipNumber);
   }

   if (fCurrentFitChannel == 1 && fCurrentFitBin == 10) {
      for (i = 0; i < fNumberOfPoints; i++) {
         fXXSave[i] = fXXFit[i];
         fYYSave[i] = (fYYFit[i] - chn->fOffset[10]) / chn->fGain[10] - fXXFit[i];
      }
   }

   fCurrentFitBin++;
   if (fCurrentFitBin == kNumberOfBins) {
      fCurrentFitChannel++;
      fCurrentFitBin = 0;
   }
   if (fCurrentFitChannel == kNumberOfCalibChannelsV4) {

      if (fBoard->Debug()) {
         printf("Gain min=%1.3lf max=%1.3lf\n", fGainMin / 4096.0, fGainMax / 4096.0);
         fflush(stdout);
      }
      // allow up to three bad bins
      if (error > 3) {
         printf("Aborting calibration!\n");
         return true;
      }

      fFitted = true;
      fOffset = false;
      fCalibrationData[chipNumber]->fRead = true;
      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
      return true;
   }

   return false;
}

unsigned int millitime()
{
#ifdef _MSC_VER

   return (int) GetTickCount();

#else
   struct timeval tv;

   gettimeofday(&tv, NULL);

   return tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
   return 0;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::OffsetCalibration(int chipNumber)
{
   if (!fFitted || fOffset)
      return true;
   if (fBoard->GetDRSType() == 3)
      return OffsetCalibrationV4(chipNumber);
   else
      return OffsetCalibrationV3(chipNumber);
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::OffsetCalibrationV3(int chipNumber)
{
   int k, ii, j;
   int t1, t2;
   float mean, error;
   CalibrationData *data = fCalibrationData[chipNumber];
   CalibrationData::CalibrationDataChannel * chn;

   if (fCurrentSample == 0) {
      data->fHasOffsetCalibration = false;
      fBoard->SetCalibVoltage(0.0);
      fBoard->EnableAcal(0, 0.0);
   }
   // Loop Over Number Of Samples For Statistics
   t1 = millitime();
   fBoard->SoftTrigger();
   while (fBoard->IsBusy()) {
   }
   fBoard->TransferWaves();
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      fBoard->GetRawWave(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample]);
      fBoard->CalibrateWaveform(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample],
                                fWaveFormOffset[ii][fCurrentSample], true, false, false, 0, true);
   }
   fBoard->StartDomino();
   fBoard->IsBusy();
   fBoard->IsBusy();
   fBoard->IsBusy();
   t2 = millitime();
   while (t2 - t1 < (1000 / fTriggerFrequency)) {
      t2 = millitime();
   }
   fCurrentSample++;

   if (fCurrentSample == fNumberOfSamples) {
      // Average Sample Points
      float *sample = new float[fNumberOfSamples];
      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
         chn = data->fChannel[ii];
         for (k = 0; k < kNumberOfBins; k++) {
            for (j = 0; j < fNumberOfSamples; j++)
               sample[j] = static_cast < float >(fWaveFormOffset[ii][j][k]);
            Average(1, sample, fNumberOfSamples, mean, error, 2);
            chn->fOffset[k] = static_cast < short >(mean);
            for (j = 0; j < fNumberOfSamples; j++)
               sample[j] = fWaveFormOffsetADC[ii][j][k];
            Average(1, sample, fNumberOfSamples, mean, error, 2);
            chn->fOffsetADC[k] = static_cast < unsigned short >(mean);
         }
      }
      fOffset = true;
      fCalibrationData[chipNumber]->fHasOffsetCalibration = true;
      delete[] sample;
      return true;
   }

   return false;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::OffsetCalibrationV4(int chipNumber)
{
   int k, ii, j;
   float mean, error;
   CalibrationData *data = fCalibrationData[chipNumber];
   CalibrationData::CalibrationDataChannel * chn;

   /* switch DRS to input, hope that no real signal occurs */
   if (fCurrentSample == 0) {
      data->fHasOffsetCalibration = false;
      fBoard->SetCalibVoltage(0.0);
      fBoard->EnableAcal(0, 0.0);
      /* one dummy trigger for unknown reasons */
      fBoard->SoftTrigger();
      while (fBoard->IsBusy());
      fBoard->StartDomino();
      Sleep(50);
   }
   // Loop Over Number Of Samples For Statistics
   fBoard->SoftTrigger();
   while (fBoard->IsBusy());
   fBoard->TransferWaves();
   for (ii = 0; ii < kNumberOfCalibChannelsV4; ii++)
      fBoard->GetRawWave(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample]);

   fBoard->StartDomino();
   Sleep(50);
   fCurrentSample++;

   if (fBoard->Debug()) {
      printf("%02d\r", fNumberOfSamples - fCurrentSample);
      fflush(stdout);
   }

   if (fCurrentSample == fNumberOfSamples) {
      // Average Sample Points
      float *sample = new float[fNumberOfSamples];
      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
         chn = data->fChannel[ii];
         for (k = 0; k < kNumberOfBins; k++) {
            for (j = 0; j < fNumberOfSamples; j++)
               sample[j] = static_cast < float >(fWaveFormOffsetADC[ii][j][k]);
            Average(1, sample, fNumberOfSamples, mean, error, 2);
            chn->fOffset[k] = static_cast < unsigned short >(mean);
         }
      }
      fOffset = true;
      fCalibrationData[chipNumber]->fHasOffsetCalibration = true;
      delete[] sample;
      return true;
   }

   return false;
}

/*------------------------------------------------------------------*/

void ResponseCalibration::InitFields(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins,
                                     int numberOfSamples, int numberOfGridPoints, int numberOfXConstPoints,
                                     int numberOfXConstGridPoints, double triggerFrequency,
                                     int showStatistics)
{
   int ii, j, i;
   fInitialized = true;
   fNumberOfPointsLowVolt = numberOfPointsLowVolt;
   fNumberOfPoints = numberOfPoints;
   fNumberOfMode2Bins = numberOfMode2Bins;
   fNumberOfSamples = numberOfSamples;
   fNumberOfGridPoints = numberOfGridPoints;
   fNumberOfXConstPoints = numberOfXConstPoints;
   fNumberOfXConstGridPoints = numberOfXConstGridPoints;
   fTriggerFrequency = triggerFrequency;
   fShowStatistics = showStatistics;
   fCurrentPoint = 0;
   fCurrentSample = 0;
   fCurrentFitChannel = 0;
   fCurrentFitBin = 0;
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      for (j = 0; j < kNumberOfBins; j++) {
         fResponseX[ii][j] = new float[fNumberOfPoints + fNumberOfPointsLowVolt];
      }
   }
   fResponseY = new float[fNumberOfPoints + fNumberOfPointsLowVolt];
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      fWaveFormMode3[ii] = new unsigned short *[fNumberOfSamples];
      fWaveFormMode2[ii] = new unsigned short *[fNumberOfSamples];
      fWaveFormOffset[ii] = new short *[fNumberOfSamples];
      fWaveFormOffsetADC[ii] = new unsigned short *[fNumberOfSamples];
      for (i = 0; i < fNumberOfSamples; i++) {
         fWaveFormMode3[ii][i] = new unsigned short[kNumberOfBins];
         fWaveFormMode2[ii][i] = new unsigned short[kNumberOfBins];
         fWaveFormOffset[ii][i] = new short[kNumberOfBins];
         fWaveFormOffsetADC[ii][i] = new unsigned short[kNumberOfBins];
      }
   }
   fSamples = new unsigned short[fNumberOfSamples];
   fSampleUsed = new int[fNumberOfSamples];

   for (j = 0; j < kNumberOfBins; j++) {
      fRes[j] = new float[fNumberOfGridPoints];
      fResX[j] = new float[fNumberOfGridPoints];
   }
   for (i = 0; i < 2; i++) {
      fPntX[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
      fPntY[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
      fUValues[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
   }
   fXXFit = new double[fNumberOfPoints];
   fYYFit = new double[fNumberOfPoints];
   fWWFit = new double[fNumberOfPoints];
   fYYFitRes = new double[fNumberOfPoints];
   fYYSave = new double[fNumberOfPoints];
   fXXSave = new double[fNumberOfPoints];

   fStatisticsApprox = new float *[fNumberOfPoints];
   fStatisticsApproxExt = new float *[fNumberOfPoints];
   for (i = 0; i < fNumberOfPoints; i++) {
      fStatisticsApprox[i] = new float[kNumberOfCalibChannelsV3 * kNumberOfBins];
      fStatisticsApproxExt[i] = new float[kNumberOfCalibChannelsV3 * kNumberOfBins];
   }
   for (i = 0; i < kNumberOfChipsMax; i++) {
      fCalibrationData[i] = new CalibrationData(numberOfXConstGridPoints);
   }
}

/*------------------------------------------------------------------*/

void ResponseCalibration::DeleteFields()
{
   if (!fInitialized)
      return;
   fInitialized = false;
   int ii, j, i;
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      for (j = 0; j < kNumberOfBins; j++) {
         delete fResponseX[ii][j];
      }
   }
   delete fResponseY;
   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
      for (i = 0; i < fNumberOfSamples; i++) {
         if (fWaveFormMode3[ii] != NULL)
            delete fWaveFormMode3[ii][i];
         if (fWaveFormMode2[ii] != NULL)
            delete fWaveFormMode2[ii][i];
         if (fWaveFormOffset[ii] != NULL)
            delete fWaveFormOffset[ii][i];
         if (fWaveFormOffsetADC[ii] != NULL)
            delete fWaveFormOffsetADC[ii][i];
      }
      delete fWaveFormMode3[ii];
      delete fWaveFormMode2[ii];
      delete fWaveFormOffset[ii];
      delete fWaveFormOffsetADC[ii];
   }
   delete fSamples;
   delete fSampleUsed;

   for (j = 0; j < kNumberOfBins; j++) {
      delete fRes[j];
      delete fResX[j];
   }
   for (i = 0; i < 2; i++) {
      delete fPntX[i];
      delete fPntY[i];
      delete fUValues[i];
   }
   delete fXXFit;
   delete fYYFit;
   delete fWWFit;
   delete fYYFitRes;
   delete fYYSave;
   delete fXXSave;

   for (i = 0; i < fNumberOfPoints; i++) {
      delete fStatisticsApprox[i];
      delete fStatisticsApproxExt[i];
   }
   delete fStatisticsApprox;
   delete fStatisticsApproxExt;
   for (i = 0; i < kNumberOfChipsMax; i++)
      delete fCalibrationData[i];
}

/*------------------------------------------------------------------*/

double ResponseCalibration::GetTemperature(unsigned int chipIndex)
{
   if (fCalibrationData[chipIndex] == NULL)
      return 0;
   if (!fCalibrationData[chipIndex]->fRead)
      return 0;
   return (fCalibrationData[chipIndex]->fStartTemperature + fCalibrationData[chipIndex]->fEndTemperature) / 2;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::Calibrate(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform,
                                    short *uWaveform, int triggerCell, float threshold, bool offsetCalib)
{
   int i;
   unsigned int NumberOfCalibChannels;
   int hasOffset;
   bool aboveThreshold;
   float wave, v;
   int j, irot;

   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;

   if (fBoard->GetDRSType() == 3)
      NumberOfCalibChannels = kNumberOfCalibChannelsV4;
   else
      NumberOfCalibChannels = kNumberOfCalibChannelsV3;

   if (channel >= NumberOfCalibChannels || data == NULL) {
      for (i = 0; i < kNumberOfBins; i++) {
         irot = i;
         if (triggerCell > -1)
            irot = (triggerCell + i) % kNumberOfBins;

         uWaveform[i] = adcWaveform[irot];
      }
      return true;
   }
   if (!data->fRead) {
      for (i = 0; i < kNumberOfBins; i++) {
         uWaveform[i] = adcWaveform[i];
      }
      return true;
   }

   chn = data->fChannel[channel];

   hasOffset = data->fHasOffsetCalibration;
   aboveThreshold = (threshold == 0);   // if threshold equal zero, always return true

   short offset;

   // Calibrate
   for (i = 0; i < kNumberOfBins; i++) {
      if (fBoard->GetDRSType() != 3) {
         irot = i;
         if (triggerCell > -1)
            irot = (triggerCell + i) % kNumberOfBins;
         offset = offsetCalib ? chn->fOffset[irot] : 0;
         if (adcWaveform[irot] > chn->fLookUpOffset[irot]) {
            uWaveform[i] =
                ((chn->fLookUp[irot][0] - chn->fLookUp[irot][1]) * (adcWaveform[irot] -
                                                                    chn->fLookUpOffset[irot]) +
                 chn->fLookUp[irot][0]);
         } else if (adcWaveform[irot] <= chn->fLookUpOffset[irot]
                    && adcWaveform[irot] > chn->fLookUpOffset[irot] - chn->fNumberOfLookUpPoints[irot]) {
            uWaveform[i] = chn->fLookUp[irot][chn->fLookUpOffset[irot] - adcWaveform[irot]];
         } else {
            wave = 0;
            for (j = 0; j < kBSplineOrder; j++) {
               wave +=
                   chn->fData[irot][data->fBSplineOffsetLookUp[adcWaveform[irot]][chn->fLimitGroup[irot]] + j]
                   * data->fBSplineLookUp[adcWaveform[irot]][chn->fLimitGroup[irot]][j];
            }
            uWaveform[i] = static_cast < short >(wave);
         }
         // Offset Calibration
         if (hasOffset)
            uWaveform[i] -= offset;
      } else {
         irot = i;
         if (triggerCell > -1)
            irot = (triggerCell + i) % kNumberOfBins;
#if 0                           /* not enabled yet for DRS3 */
         offset = offsetCalib ? chn->fOffset[irot] : 0;
#else
         offset = chn->fOffset[irot];
#endif
         v = static_cast < float >(adcWaveform[irot] - offset) / chn->fGain[irot];
         uWaveform[i] = static_cast < short >(v * 1000 / GetPrecision() + 0.5);
      }

      // Check for Threshold
      if (!aboveThreshold) {
         if (uWaveform[i] >= threshold)
            aboveThreshold = true;
      }
   }
   return aboveThreshold;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::SubtractADCOffset(unsigned int chipIndex, unsigned int channel,
                                            unsigned short *adcWaveform,
                                            unsigned short *adcCalibratedWaveform,
                                            unsigned short newBaseLevel)
{
   int i;
   unsigned int NumberOfCalibChannels;
   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;

   if (fBoard->GetDRSType() == 3)
      NumberOfCalibChannels = kNumberOfCalibChannelsV4;
   else
      NumberOfCalibChannels = kNumberOfCalibChannelsV3;

   if (channel >= NumberOfCalibChannels || data == NULL)
      return false;
   if (!data->fRead || !data->fHasOffsetCalibration)
      return false;

   chn = data->fChannel[channel];
   for (i = 0; i < kNumberOfBins; i++)
      adcCalibratedWaveform[i] = adcWaveform[i] - chn->fOffsetADC[i] + newBaseLevel;
   return true;
}


/*------------------------------------------------------------------*/

bool ResponseCalibration::ReadCalibration(unsigned int chipIndex)
{
   if (fBoard->GetDRSType() == 3)
      return ReadCalibrationV4(chipIndex);
   else
      return ReadCalibrationV3(chipIndex);
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::ReadCalibrationV3(unsigned int chipIndex)
{
   int k, l, m, num;
   unsigned char ng;
   short tempShort;
   char fileName[2000];
   FILE *fileHandle;
   char calibDir[1000];

   // Read Response Calibration
   delete fCalibrationData[chipIndex];
   fCalibrationData[chipIndex] = NULL;

   fBoard->GetCalibrationDirectory(calibDir);
   sprintf(fileName, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", calibDir,
           fBoard->GetBoardSerialNumber(), fBoard->GetBoardSerialNumber(), chipIndex,
           static_cast < int >(fBoard->GetNominalFrequency() * 1000));

   fileHandle = fopen(fileName, "rb");
   if (fileHandle == NULL) {
      printf("Board %d --> Could not find response calibration file:\n", fBoard->GetBoardSerialNumber());
      printf("%s\n", fileName);
      return false;
   }
   // Number Of Grid Points
   num = fread(&ng, 1, 1, fileHandle);
   if (num != 1) {
      printf("Error while reading response calibration file '%s'\n", fileName);
      printf("   at 'NumberOfGridPoints'.\n");
      return false;
   }

   fCalibrationData[chipIndex] = new CalibrationData(ng);
   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;
   data->fRead = true;
   data->fHasOffsetCalibration = 1;
   data->DeletePreCalculatedBSpline();
   fCalibrationValid[chipIndex] = true;

   // Start Temperature
   num = fread(&tempShort, 2, 1, fileHandle);
   if (num != 1) {
      printf("Error while reading response calibration file '%s'\n", fileName);
      printf("   at 'StartTemperature'.\n");
      return false;
   }
   data->fStartTemperature = static_cast < float >(tempShort) / 10;
   // End Temperature
   num = fread(&tempShort, 2, 1, fileHandle);
   if (num != 1) {
      printf("Error while reading response calibration file '%s'\n", fileName);
      printf("   at 'EndTemperature'.\n");
      return false;
   }
   data->fEndTemperature = static_cast < float >(tempShort) / 10;
   if (fBoard->GetDRSType() != 3) {
      // Min
      num = fread(&data->fMin, 4, 1, fileHandle);
      if (num != 1) {
         printf("Error while reading response calibration file '%s'\n", fileName);
         printf("   at 'Min'.\n");
         return false;
      }
      // Max
      num = fread(&data->fMax, 4, 1, fileHandle);
      if (num != 1) {
         printf("Error while reading response calibration file '%s'\n", fileName);
         printf("   at 'Max'.\n");
         return false;
      }
      // Number Of Limit Groups
      num = fread(&data->fNumberOfLimitGroups, 1, 1, fileHandle);
      if (num != 1) {
         printf("Error while reading response calibration file '%s'\n", fileName);
         printf("   at 'NumberOfLimitGroups'.\n");
         return false;
      }
   }
   // read channel
   for (k = 0; k < kNumberOfCalibChannelsV3; k++) {
      chn = data->fChannel[k];
      for (l = 0; l < kNumberOfBins; l++) {
         if (fBoard->GetDRSType() != 3) {
            // Range Group
            num = fread(&chn->fLimitGroup[l], 1, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'RangeGroup' of channel %d bin %d.\n", k, l);
               return false;
            }
            // Look Up Offset
            num = fread(&chn->fLookUpOffset[l], 2, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'LookUpOffset' of channel %d bin %d.\n", k, l);
               return false;
            }
            // Number Of Look Up Points
            num = fread(&chn->fNumberOfLookUpPoints[l], 1, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'NumberOfLookUpPoints' of channel %d bin %d.\n", k, l);
               return false;
            }
            // Look Up Points
            delete chn->fLookUp[l];
            chn->fLookUp[l] = new unsigned char[chn->fNumberOfLookUpPoints[l]];
            for (m = 0; m < chn->fNumberOfLookUpPoints[l]; m++) {
               num = fread(&chn->fLookUp[l][m], 1, 1, fileHandle);
               if (num != 1) {
                  printf("Error while reading response calibration file '%s'\n", fileName);
                  printf("   at 'LookUp %d' of channel %d bin %d.\n", m, k, l);
                  return false;
               }
            }
            // Points
            for (m = 0; m < data->fNumberOfGridPoints; m++) {
               num = fread(&chn->fData[l][m], 2, 1, fileHandle);
               if (num != 1) {
                  printf("Error while reading response calibration file '%s'\n", fileName);
                  printf("   at 'Point %d' of channel %d bin %d.\n", m, k, l);
                  return false;
               }
            }
            // ADC Offset
            num = fread(&chn->fOffsetADC[l], 2, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'ADC Offset' of channel %d bin %d.\n", k, l);
               return false;
            }
         }
         // Offset
         num = fread(&chn->fOffset[l], 2, 1, fileHandle);
         if (num != 1) {
            printf("Error while reading response calibration file '%s'\n", fileName);
            printf("   at 'Offset' of channel %d bin %d.\n", k, l);
            return false;
         }
         if (fBoard->GetDRSType() == 3) {
            // Gain
            num = fread(&chn->fGain[l], 2, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'Gain' of channel %d bin %d.\n", k, l);
               return false;
            }
         }
      }
   }
   fclose(fileHandle);

   if (fBoard->GetDRSType() != 3) {
      data->PreCalculateBSpline();
   }

   return true;
}

/*------------------------------------------------------------------*/

bool ResponseCalibration::ReadCalibrationV4(unsigned int chipIndex)
{
   int k, l, num;
   char fileName[2000];
   FILE *fileHandle;
   char calibDir[1000];

   // Read Response Calibration

   fBoard->GetCalibrationDirectory(calibDir);
   sprintf(fileName, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", calibDir,
           fBoard->GetBoardSerialNumber(), fBoard->GetBoardSerialNumber(), chipIndex,
           static_cast < int >(fBoard->GetNominalFrequency() * 1000));

   fileHandle = fopen(fileName, "rb");
   if (fileHandle == NULL) {
      printf("Board %d --> Could not find response calibration file:\n", fBoard->GetBoardSerialNumber());
      printf("%s\n", fileName);
      return false;
   }

   if (fInitialized)
      delete fCalibrationData[chipIndex];
   fCalibrationData[chipIndex] = new CalibrationData(1);
   CalibrationData *data = fCalibrationData[chipIndex];
   CalibrationData::CalibrationDataChannel * chn;
   data->fRead = true;
   data->fHasOffsetCalibration = 1;
   fCalibrationValid[chipIndex] = true;
   data->fStartTemperature = 0;
   data->fEndTemperature = 0;

   // read channel
   for (k = 0; k < kNumberOfCalibChannelsV4; k++) {
      chn = data->fChannel[k];
      for (l = 0; l < kNumberOfBins; l++) {
         // Offset
         num = fread(&chn->fOffset[l], 2, 1, fileHandle);
         if (num != 1) {
            printf("Error while reading response calibration file '%s'\n", fileName);
            printf("   at 'Offset' of channel %d bin %d.\n", k, l);
            return false;
         }
         if (fBoard->GetDRSType() == 3) {
            // Gain
            num = fread(&chn->fGain[l], 2, 1, fileHandle);
            if (num != 1) {
               printf("Error while reading response calibration file '%s'\n", fileName);
               printf("   at 'Gain' of channel %d bin %d.\n", k, l);
               return false;
            }
         }
      }
   }

   fclose(fileHandle);
   return true;
}

/*------------------------------------------------------------------*/

float ResponseCalibration::GetValue(float *coefficients, float u, int n)
{
   int j, ii;
   float bsplines[4];
   ii = CalibrationData::CalculateBSpline(n, u, bsplines);

   float s = 0;
   for (j = 0; j < kBSplineOrder; j++) {
      s += coefficients[ii + j] * bsplines[j];
   }
   return s;
}

/*------------------------------------------------------------------*/

int ResponseCalibration::Approx(float *p, float *uu, int np, int nu, float *coef)
{
   int i, iu, j;

   const int mbloc = 50;
   int ip = 0;
   int ir = 0;
   int mt = 0;
   int ileft, irow;
   float bu[kBSplineOrder];
   float *matrix[kBSplineOrder + 2];
   for (i = 0; i < kBSplineOrder + 2; i++)
      matrix[i] = new float[mbloc + nu + 1];
   for (iu = kBSplineOrder - 1; iu < nu; iu++) {
      for (i = 0; i < np; i++) {
         if (1 <= uu[i])
            ileft = nu - 1;
         else if (uu[i] < 0)
            ileft = kBSplineOrder - 2;
         else
            ileft = kBSplineOrder - 1 + static_cast < int >(uu[i] * (nu - kBSplineOrder + 1));
         if (ileft != iu)
            continue;
         irow = ir + mt;
         mt++;
         CalibrationData::CalculateBSpline(nu, uu[i], bu);
         for (j = 0; j < kBSplineOrder; j++) {
            matrix[j][irow] = bu[j];
         }
         matrix[kBSplineOrder][irow] = p[i];
         if (mt < mbloc)
            continue;
         LeastSquaresAccumulation(matrix, kBSplineOrder, &ip, &ir, mt, iu - kBSplineOrder + 1);
         mt = 0;
      }
      if (mt == 0)
         continue;
      LeastSquaresAccumulation(matrix, kBSplineOrder, &ip, &ir, mt, iu - kBSplineOrder + 1);
      mt = 0;
   }
   if (!LeastSquaresSolving(matrix, kBSplineOrder, ip, ir, coef, nu)) {
      for (i = 0; i < kBSplineOrder + 2; i++)
         delete matrix[i];
      return 0;
   }

   for (i = 0; i < kBSplineOrder + 2; i++)
      delete matrix[i];
   return 1;
}

/*------------------------------------------------------------------*/

void ResponseCalibration::LeastSquaresAccumulation(float **matrix, int nb, int *ip, int *ir, int mt, int jt)
{
   int i, j, l, mu, k, kh;
   float rho;

   if (mt <= 0)
      return;
   if (jt != *ip) {
      if (jt > (*ir)) {
         for (i = 0; i < mt; i++) {
            for (j = 0; j < nb + 1; j++) {
               matrix[j][jt + mt - i] = matrix[j][(*ir) + mt - i];
            }
         }
         for (i = 0; i < jt - (*ir); i++) {
            for (j = 0; j < nb + 1; j++) {
               matrix[j][(*ir) + i] = 0;
            }
         }
         *ir = jt;
      }
      mu = min(nb - 1, (*ir) - (*ip) - 1);
      if (mu != 0) {
         for (l = 0; l < mu; l++) {
            k = min(l + 1, jt - (*ip));
            for (i = l + 1; i < nb; i++) {
               matrix[i - k][(*ip) + l + 1] = matrix[i][(*ip) + l + 1];
            }
            for (i = 0; i < k; i++) {
               matrix[nb - i - 1][(*ip) + l + 1] = 0;
            }
         }
      }
      *ip = jt;
   }
   kh = min(nb + 1, (*ir) + mt - (*ip));

   for (i = 0; i < kh; i++) {
      Housholder(i, max(i + 1, (*ir) - (*ip)), (*ir) + mt - (*ip), matrix, i, (*ip), &rho, matrix, i + 1,
                 (*ip), 1, nb - i);
   }

   *ir = (*ip) + kh;
   if (kh < nb + 1)
      return;
   for (i = 0; i < nb; i++) {
      matrix[i][(*ir) - 1] = 0;
   }
}

/*------------------------------------------------------------------*/

int ResponseCalibration::LeastSquaresSolving(float **matrix, int nb, int ip, int ir, float *x, int n)
{
   int i, j, l, ii;
   float s, rsq;
   for (j = 0; j < n; j++) {
      x[j] = matrix[nb][j];
   }
   rsq = 0;
   if (n <= ir - 1) {
      for (j = n; j < ir; j++) {
         rsq += pow(matrix[nb][j], 2);
      }
   }

   for (ii = 0; ii < n; ii++) {
      i = n - ii - 1;
      s = 0;
      l = max(0, i - ip);
      if (i != n - 1) {
         for (j = 1; j < min(n - i, nb); j++) {
            s += matrix[j + l][i] * x[i + j];
         }
      }
      if (matrix[l][i] == 0) {
         printf("Error in LeastSquaresSolving.\n");
         return 0;
      }
      x[i] = (x[i] - s) / matrix[l][i];
   }
   return 1;
}

/*------------------------------------------------------------------*/

void ResponseCalibration::Housholder(int lpivot, int l1, int m, float **u, int iU1, int iU2, float *up,
                                     float **c, int iC1, int iC2, int ice, int ncv)
{
   int i, j, incr;
   float tol = static_cast < float >(1e-20);
   float tolb = static_cast < float >(1e-24);
   float cl, clinv, sm, b;

   if (lpivot < 0 || lpivot >= l1 || l1 > m - 1)
      return;
   cl = fabs(u[iU1][iU2 + lpivot]);

   // Construct the transformation
   for (j = l1 - 1; j < m; j++)
      cl = max(fabsf(u[iU1][iU2 + j]), cl);
   if (cl < tol)
      return;
   clinv = 1 / cl;
   sm = pow(u[iU1][iU2 + lpivot] * clinv, 2);
   for (j = l1; j < m; j++) {
      sm = sm + pow(u[iU1][iU2 + j] * clinv, 2);
   }
   cl *= sqrt(sm);
   if (u[iU1][iU2 + lpivot] > 0)
      cl = -cl;
   *up = u[iU1][iU2 + lpivot] - cl;
   u[iU1][iU2 + lpivot] = cl;

   if (ncv <= 0)
      return;
   b = (*up) * u[iU1][iU2 + lpivot];
   if (fabs(b) < tolb)
      return;
   if (b >= 0)
      return;
   b = 1 / b;
   incr = ice * (l1 - lpivot);
   for (j = 0; j < ncv; j++) {
      sm = c[iC1 + j][iC2 + lpivot] * (*up);
      for (i = l1; i < m; i++) {
         sm = sm + c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] * u[iU1][iU2 + i];
      }
      if (sm == 0)
         continue;
      sm *= b;
      c[iC1 + j][iC2 + lpivot] = c[iC1 + j][iC2 + lpivot] + sm * (*up);
      for (i = l1; i < m; i++) {
         c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] =
             c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] + sm * u[iU1][iU2 + i];
      }
   }
}

/*------------------------------------------------------------------*/

int ResponseCalibration::MakeDir(const char *path)
{
   struct stat buf;
   if (stat(path, &buf)) {
#ifdef _MSC_VER
      return mkdir(path);
#else
      return mkdir(path, 0711);
#endif                          // R__UNIX
   }
   return 0;
}

/*------------------------------------------------------------------*/

ResponseCalibration::ResponseCalibration(DRSBoard * board)
:  fBoard(board)
    , fPrecision(0.1)           // mV
    , fInitialized(false)
    , fRecorded(false)
    , fFitted(false)
    , fOffset(false)
    , fNumberOfPointsLowVolt(0)
    , fNumberOfPoints(0)
    , fNumberOfMode2Bins(0)
    , fNumberOfSamples(0)
    , fNumberOfGridPoints(0)
    , fNumberOfXConstPoints(0)
    , fNumberOfXConstGridPoints(0)
    , fTriggerFrequency(0)
    , fShowStatistics(0)
    , fCalibFile(0)
    , fCurrentLowVoltPoint(0)
    , fCurrentPoint(0)
    , fCurrentSample(0)
    , fCurrentFitChannel(0)
    , fCurrentFitBin(0)
    , fResponseY(0)
    , fSamples(0)
    , fSampleUsed(0)
    , fXXFit(0)
    , fYYFit(0)
    , fWWFit(0)
    , fYYFitRes(0)
    , fYYSave(0)
    , fXXSave(0)
    , fStatisticsApprox(0)
    , fStatisticsApproxExt(0)
{
   int i;
   // Initializing the Calibration Class
   CalibrationData::fIntRevers[0] = 0;
   for (i = 1; i < 2 * kBSplineOrder - 2; i++) {
      CalibrationData::fIntRevers[i] = static_cast < float >(1.) / i;
   }
   for (i = 0; i < kNumberOfChipsMax; i++) {
      fCalibrationData[i] = NULL;
   }
   // Initializing the Calibration Creation
   fCalibrationValid[0] = false;
   fCalibrationValid[1] = false;
}

/*------------------------------------------------------------------*/

ResponseCalibration::~ResponseCalibration()
{
   // Delete the Calibration
   for (int i=0 ; i<kNumberOfChipsMax ; i++)
      delete fCalibrationData[i];

   // Deleting the Calibration Creation
   DeleteFields();
}

/*------------------------------------------------------------------*/

float ResponseCalibration::CalibrationData::fIntRevers[2 * kBSplineOrder - 2];
ResponseCalibration::CalibrationData::CalibrationData(int numberOfGridPoints)
:fRead(false)
, fNumberOfGridPoints(numberOfGridPoints)
, fHasOffsetCalibration(0)
, fStartTemperature(0)
, fEndTemperature(0)
, fMin(0)
, fMax(0)
, fNumberOfLimitGroups(0)
{
   int i;
   for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
      fChannel[i] = new CalibrationDataChannel(numberOfGridPoints);
   }
   for (i = 0; i < kNumberOfADCBins; i++) {
      fBSplineOffsetLookUp[i] = NULL;
      fBSplineLookUp[i] = NULL;
   }
};

/*------------------------------------------------------------------*/

void ResponseCalibration::CalibrationData::PreCalculateBSpline()
{
   int i, j;
   float uu;
   float xmin, xrange;
   int nk = fNumberOfGridPoints - kBSplineOrder + 1;
   for (i = 0; i < kNumberOfADCBins; i++) {
      fBSplineLookUp[i] = new float *[fNumberOfLimitGroups];
      fBSplineOffsetLookUp[i] = new int[fNumberOfLimitGroups];
      for (j = 0; j < fNumberOfLimitGroups; j++) {
         fBSplineLookUp[i][j] = new float[kBSplineOrder];
         xmin = fMin + j * kBSplineXMinOffset;
         xrange = fMax - xmin;
         uu = (i - xmin) / xrange;
         if (i < xmin) {
            uu = 0;
         }
         if (i - xmin > xrange) {
            uu = 1;
         }
         fBSplineOffsetLookUp[i][j] = static_cast < int >(uu * nk);
         CalculateBSpline(fNumberOfGridPoints, uu, fBSplineLookUp[i][j]);
      }
   }
}

/*------------------------------------------------------------------*/

void ResponseCalibration::CalibrationData::DeletePreCalculatedBSpline()
{
   int i, j;
   for (i = 0; i < kNumberOfADCBins; i++) {
      if (fBSplineLookUp[i] != NULL) {
         for (j = 0; j < fNumberOfLimitGroups; j++)
            delete fBSplineLookUp[i][j];
      }
      delete fBSplineLookUp[i];
      delete fBSplineOffsetLookUp[i];
   }
}

/*------------------------------------------------------------------*/

ResponseCalibration::CalibrationData::~CalibrationData()
{
   int i, j;
   for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
      delete fChannel[i];
   }
   for (i = 0; i < kNumberOfADCBins; i++) {
      if (fBSplineLookUp[i] != NULL) {
         for (j = 0; j < fNumberOfLimitGroups; j++) {
            delete fBSplineLookUp[i][j];
         }
      }
      delete fBSplineLookUp[i];
      delete fBSplineOffsetLookUp[i];
   }
};

/*------------------------------------------------------------------*/

int ResponseCalibration::CalibrationData::CalculateBSpline(int nGrid, float value, float *bsplines)
{
   int minimum;
   int maximum;
   float xl;

   int nk = nGrid - kBSplineOrder + 1;
   float vl = value * nk;
   int ivl = static_cast < int >(vl);

   if (1 <= value) {
      xl = vl - nk + 1;
      minimum = 1 - nk;
   } else if (value < 0) {
      xl = vl;
      minimum = 0;
   } else {
      xl = vl - ivl;
      minimum = -ivl;
   }
   maximum = nk + minimum;

//   printf("xl = %f\n",xl);
   float vm, vmprev;
   int jl, ju;
   int nb = 0;

   bsplines[0] = 1;
   for (int i = 0; i < kBSplineOrder - 1; i++) {
      vmprev = 0;
      for (int j = 0; j < nb + 1; j++) {
         jl = max(minimum, j - nb);
         ju = min(maximum, j + 1);
         vm = bsplines[j] * fIntRevers[ju - jl];
         bsplines[j] = vm * (ju - xl) + vmprev;
         vmprev = vm * (xl - jl);
      }
      nb++;
      bsplines[nb] = vmprev;
   }
   return -minimum;
}

/*------------------------------------------------------------------*/

void ResponseCalibration::Average(int method, float *points, int numberOfPoints, float &mean, float &error,
                                  float sigmaBoundary)
{
   // Methods :
   // 0 : Average
   // 1 : Average inside sigmaBoundary*sigma
   int i;
   float sum = 0;
   float sumSquare = 0;

   if (method == 0 || method == 1) {
      for (i = 0; i < numberOfPoints; i++) {
         sum += points[i];
         sumSquare += points[i] * points[i];
      }

      mean = sum / numberOfPoints;
      error = sqrt((sumSquare - sum * sum / numberOfPoints) / (numberOfPoints - 1));
   }
   if (method == 1) {
      int numberOfGoodPoints = numberOfPoints;
      bool found = true;
      bool *goodSample = new bool[numberOfGoodPoints];
      for (i = 0; i < numberOfGoodPoints; i++)
         goodSample[i] = true;

      while (found) {
         found = false;
         for (i = 0; i < numberOfPoints; i++) {
            if (goodSample[i] && fabs(points[i] - mean) > sigmaBoundary * error) {
               found = true;
               goodSample[i] = false;
               numberOfGoodPoints--;
               sum -= points[i];
               sumSquare -= points[i] * points[i];
               mean = sum / numberOfGoodPoints;
               error = sqrt((sumSquare - sum * sum / numberOfGoodPoints) / (numberOfGoodPoints - 1));
            }
         }
      }
      delete[] goodSample;
   }
}