/********************************************************************
  DRS.h, S.Ritt, M. Schneebeli - PSI

  $Id: DRS.h 21309 2014-04-11 14:51:29Z ritt $

********************************************************************/
#ifndef DRS_H
#define DRS_H
#include <stdio.h>
#include <string.h>
#include "averager.h"

#ifdef HAVE_LIBUSB
#   ifndef HAVE_USB
#      define HAVE_USB
#   endif
#endif

#ifdef HAVE_USB
#   include <musbstd.h>
#endif                          // HAVE_USB

#ifdef HAVE_VME
#   include <mvmestd.h>
#endif                          // HAVE_VME

/* disable "deprecated" warning */
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif

#ifndef NULL
#define NULL 0
#endif

int drs_kbhit();
unsigned int millitime();

/* transport mode */
#define TR_VME   1
#define TR_USB   2
#define TR_USB2  3

/* address types */
#ifndef T_CTRL
#define T_CTRL   1
#define T_STATUS 2
#define T_RAM    3
#define T_FIFO   4
#endif

/*---- Register addresses ------------------------------------------*/

#define REG_CTRL                     0x00000    /* 32 bit control reg */
#define REG_DAC_OFS                  0x00004
#define REG_DAC0                     0x00004
#define REG_DAC1                     0x00006
#define REG_DAC2                     0x00008
#define REG_DAC3                     0x0000A
#define REG_DAC4                     0x0000C
#define REG_DAC5                     0x0000E
#define REG_DAC6                     0x00010
#define REG_DAC7                     0x00012
#define REG_CHANNEL_CONFIG           0x00014    // low byte
#define REG_CONFIG                   0x00014    // high byte
#define REG_CHANNEL_MODE             0x00016
#define REG_ADCCLK_PHASE             0x00016
#define REG_FREQ_SET_HI              0x00018    // DRS2
#define REG_FREQ_SET_LO              0x0001A    // DRS2
#define REG_TRG_DELAY                0x00018    // DRS4
#define REG_FREQ_SET                 0x0001A    // DRS4
#define REG_TRIG_DELAY               0x0001C
#define REG_LMK_MSB                  0x0001C    // DRS4 Mezz
#define REG_CALIB_TIMING             0x0001E    // DRS2
#define REG_EEPROM_PAGE_EVAL         0x0001E    // DRS4 Eval
#define REG_EEPROM_PAGE_MEZZ         0x0001A    // DRS4 Mezz
#define REG_TRG_CONFIG               0x0001C    // DRS4 Eval4
#define REG_LMK_LSB                  0x0001E    // DRS4 Mezz
#define REG_READOUT_DELAY            0x00020    // DRS4 Eval5
#define REG_WARMUP                   0x00020    // DRS4 Mezz
#define REG_COOLDOWN                 0x00022    // DRS4 Mezz
#define REG_READ_POINTER             0x00026    // DRS4 Mezz

#define REG_MAGIC                    0x00000
#define REG_BOARD_TYPE               0x00002
#define REG_STATUS                   0x00004
#define REG_RDAC_OFS                 0x0000E
#define REG_RDAC0                    0x00008
#define REG_STOP_CELL0               0x00008
#define REG_RDAC1                    0x0000A
#define REG_STOP_CELL1               0x0000A
#define REG_RDAC2                    0x0000C
#define REG_STOP_CELL2               0x0000C
#define REG_RDAC3                    0x0000E
#define REG_STOP_CELL3               0x0000E
#define REG_RDAC4                    0x00000
#define REG_RDAC5                    0x00002
#define REG_STOP_WSR0                0x00010
#define REG_STOP_WSR1                0x00011
#define REG_STOP_WSR2                0x00012
#define REG_STOP_WSR3                0x00013
#define REG_RDAC6                    0x00014
#define REG_RDAC7                    0x00016
#define REG_EVENTS_IN_FIFO           0x00018
#define REG_EVENT_COUNT              0x0001A
#define REG_FREQ1                    0x0001C
#define REG_FREQ2                    0x0001E
#define REG_WRITE_POINTER            0x0001E
#define REG_TEMPERATURE              0x00020
#define REG_TRIGGER_BUS              0x00022
#define REG_SERIAL_BOARD             0x00024
#define REG_VERSION_FW               0x00026
#define REG_SCALER0                  0x00028
#define REG_SCALER1                  0x0002C
#define REG_SCALER2                  0x00030
#define REG_SCALER3                  0x00034
#define REG_SCALER4                  0x00038
#define REG_SCALER5                  0x0003C

/*---- Control register bit definitions ----------------------------*/

#define BIT_START_TRIG        (1<<0)    // write a "1" to start domino wave
#define BIT_REINIT_TRIG       (1<<1)    // write a "1" to stop & reset DRS
#define BIT_SOFT_TRIG         (1<<2)    // write a "1" to stop and read data to RAM
#define BIT_EEPROM_WRITE_TRIG (1<<3)    // write a "1" to write into serial EEPROM
#define BIT_EEPROM_READ_TRIG  (1<<4)    // write a "1" to read from serial EEPROM
#define BIT_MULTI_BUFFER     (1<<16)    // Use multi buffering when "1"
#define BIT_DMODE            (1<<17)    // (*DRS2*) 0: single shot, 1: circular
#define BIT_ADC_ACTIVE       (1<<17)    // (*DRS4*) 0: stop ADC when running, 1: ADC always clocked
#define BIT_LED              (1<<18)    // 1=on, 0=blink during readout
#define BIT_TCAL_EN          (1<<19)    // switch on (1) / off (0) for 33 MHz calib signal
#define BIT_TCAL_SOURCE      (1<<20)
#define BIT_REFCLK_SOURCE    (1<<20)
#define BIT_FREQ_AUTO_ADJ    (1<<21)    // DRS2/3
#define BIT_TRANSP_MODE      (1<<21)    // DRS4
#define BIT_ENABLE_TRIGGER1  (1<<22)    // External LEMO/FP/TRBUS trigger
#define BIT_LONG_START_PULSE (1<<23)    // (*DRS2*) 0:short start pulse (>0.8GHz), 1:long start pulse (<0.8GHz)
#define BIT_READOUT_MODE     (1<<23)    // (*DRS3*,*DRS4*) 0:start from first bin, 1:start from domino stop
#define BIT_DELAYED_START    (1<<24)    // DRS2: start domino wave 400ns after soft trigger, used for waveform
                                        // generator startup
#define BIT_NEG_TRIGGER      (1<<24)    // DRS4: use high-to-low trigger if set
#define BIT_ACAL_EN          (1<<25)    // connect DRS to inputs (0) or to DAC6 (1)
#define BIT_TRIGGER_DELAYED  (1<<26)    // select delayed trigger from trigger bus
#define BIT_ADCCLK_INVERT    (1<<26)    // invert ADC clock
#define BIT_REFCLK_EXT       (1<<26)    // use external MMCX CLKIN refclk
#define BIT_DACTIVE          (1<<27)    // keep domino wave running during readout
#define BIT_STANDBY_MODE     (1<<28)    // put chip in standby mode
#define BIT_TR_SOURCE1       (1<<29)    // trigger source selection bits
#define BIT_DECIMATION       (1<<29)    // drop all odd samples (DRS4 mezz.)
#define BIT_TR_SOURCE2       (1<<30)    // trigger source selection bits
#define BIT_ENABLE_TRIGGER2  (1<<31)    // analog threshold (internal) trigger

/* DRS4 configuration register bit definitions */
#define BIT_CONFIG_DMODE      (1<<8)    // 0: single shot, 1: circular
#define BIT_CONFIG_PLLEN      (1<<9)    // write a "1" to enable the internal PLL
#define BIT_CONFIG_WSRLOOP   (1<<10)    // write a "1" to connect WSROUT to WSRIN internally

/*---- Status register bit definitions -----------------------------*/

#define BIT_RUNNING           (1<<0)    // one if domino wave running or readout in progress
#define BIT_NEW_FREQ1         (1<<1)    // one if new frequency measurement available
#define BIT_NEW_FREQ2         (1<<2)
#define BIT_PLL_LOCKED0       (1<<1)    // 1 if PLL has locked (DRS4 evaluation board only)
#define BIT_PLL_LOCKED1       (1<<2)    // 1 if PLL DRS4 B has locked (DRS4 mezzanine board only)
#define BIT_PLL_LOCKED2       (1<<3)    // 1 if PLL DRS4 C has locked (DRS4 mezzanine board only)
#define BIT_PLL_LOCKED3       (1<<4)    // 1 if PLL DRS4 D has locked (DRS4 mezzanine board only)
#define BIT_SERIAL_BUSY       (1<<5)    // 1 if EEPROM operation in progress
#define BIT_LMK_LOCKED        (1<<6)    // 1 if PLL of LMK chip has locked (DRS4 mezzanine board only)
#define BIT_2048_MODE         (1<<7)    // 1 if 2048-bin mode has been soldered

enum DRSBoardConstants {
   kNumberOfChannelsMax         =   10,
   kNumberOfCalibChannelsV3     =   10,
   kNumberOfCalibChannelsV4     =    8,
   kNumberOfBins                = 1024,
   kNumberOfChipsMax            =    4,
   kFrequencyCacheSize          =   10,
   kBSplineOrder                =    4,
   kPreCaliculatedBSplines      = 1000,
   kPreCaliculatedBSplineGroups =    5,
   kNumberOfADCBins             = 4096,
   kBSplineXMinOffset           =   20,
   kMaxNumberOfClockCycles      =  100,
};

enum DRSErrorCodes {
   kSuccess                     =  0,
   kInvalidTriggerSignal        = -1,
   kWrongChannelOrChip          = -2,
   kInvalidTransport            = -3,
   kZeroSuppression             = -4,
   kWaveNotAvailable            = -5
};

/*---- callback class ----*/

class DRSCallback
{
public:
   virtual void Progress(int value) = 0;
   virtual ~DRSCallback() {};
};

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

class DRSBoard;

class ResponseCalibration {
protected:

   class CalibrationData {
   public:
      class CalibrationDataChannel {
      public:
         unsigned char   fLimitGroup[kNumberOfBins];           //!
         float           fMin[kNumberOfBins];                  //!
         float           fRange[kNumberOfBins];                //!
         short           fOffset[kNumberOfBins];               //!
         short           fGain[kNumberOfBins];                 //!
         unsigned short  fOffsetADC[kNumberOfBins];            //!
         short          *fData[kNumberOfBins];                 //!
         unsigned char  *fLookUp[kNumberOfBins];               //!
         unsigned short  fLookUpOffset[kNumberOfBins];         //!
         unsigned char   fNumberOfLookUpPoints[kNumberOfBins]; //!
         float          *fTempData;                            //!

      private:
         CalibrationDataChannel(const CalibrationDataChannel &c);              // not implemented
         CalibrationDataChannel &operator=(const CalibrationDataChannel &rhs); // not implemented

      public:
         CalibrationDataChannel(int numberOfGridPoints)
         :fTempData(new float[numberOfGridPoints]) {
            int i;
            for (i = 0; i < kNumberOfBins; i++) {
               fData[i] = new short[numberOfGridPoints];
            }
            memset(fLimitGroup,           0, sizeof(fLimitGroup));
            memset(fMin,                  0, sizeof(fMin));
            memset(fRange,                0, sizeof(fRange));
            memset(fOffset,               0, sizeof(fOffset));
            memset(fGain,                 0, sizeof(fGain));
            memset(fOffsetADC,            0, sizeof(fOffsetADC));
            memset(fLookUp,               0, sizeof(fLookUp));
            memset(fLookUpOffset,         0, sizeof(fLookUpOffset));
            memset(fNumberOfLookUpPoints, 0, sizeof(fNumberOfLookUpPoints));
         }
         ~CalibrationDataChannel() {
            int i;
            delete fTempData;
            for (i = 0; i < kNumberOfBins; i++) {
               delete fData[i];
               delete fLookUp[i];
            }
         }
      };

      bool                    fRead;                                  //!
      CalibrationDataChannel *fChannel[10];                           //!
      unsigned char           fNumberOfGridPoints;                    //!
      int                     fHasOffsetCalibration;                  //!
      float                   fStartTemperature;                      //!
      float                   fEndTemperature;                        //!
      int                    *fBSplineOffsetLookUp[kNumberOfADCBins]; //!
      float                 **fBSplineLookUp[kNumberOfADCBins];       //!
      float                   fMin;                                   //!
      float                   fMax;                                   //!
      unsigned char           fNumberOfLimitGroups;                   //!
      static float            fIntRevers[2 * kBSplineOrder - 2];

   private:
      CalibrationData(const CalibrationData &c);              // not implemented
      CalibrationData &operator=(const CalibrationData &rhs); // not implemented

   public:
      CalibrationData(int numberOfGridPoints);
      ~CalibrationData();
      static int CalculateBSpline(int nGrid, float value, float *bsplines);
      void       PreCalculateBSpline();
      void       DeletePreCalculatedBSpline();
   };

   // General Fields
   DRSBoard        *fBoard;

   double           fPrecision;

   // Fields for creating the Calibration
   bool             fInitialized;
   bool             fRecorded;
   bool             fFitted;
   bool             fOffset;
   bool             fCalibrationValid[2];

   int              fNumberOfPointsLowVolt;
   int              fNumberOfPoints;
   int              fNumberOfMode2Bins;
   int              fNumberOfSamples;
   int              fNumberOfGridPoints;
   int              fNumberOfXConstPoints;
   int              fNumberOfXConstGridPoints;
   double           fTriggerFrequency;
   int              fShowStatistics;
   FILE            *fCalibFile;

   int              fCurrentLowVoltPoint;
   int              fCurrentPoint;
   int              fCurrentSample;
   int              fCurrentFitChannel;
   int              fCurrentFitBin;

   float           *fResponseX[10][kNumberOfBins];
   float           *fResponseY;
   unsigned short **fWaveFormMode3[10];
   unsigned short **fWaveFormMode2[10];
   short          **fWaveFormOffset[10];
   unsigned short **fWaveFormOffsetADC[10];
   unsigned short  *fSamples;
   int             *fSampleUsed;

   float           *fPntX[2];
   float           *fPntY[2];
   float           *fUValues[2];
   float           *fRes[kNumberOfBins];
   float           *fResX[kNumberOfBins];

   double          *fXXFit;
   double          *fYYFit;
   double          *fWWFit;
   double          *fYYFitRes;
   double          *fYYSave;
   double          *fXXSave;
   double          fGainMin;
   double          fGainMax;

   float          **fStatisticsApprox;
   float          **fStatisticsApproxExt;

   // Fields for applying the Calibration
   CalibrationData *fCalibrationData[kNumberOfChipsMax];

private:
         ResponseCalibration(const ResponseCalibration &c);              // not implemented
         ResponseCalibration &operator=(const ResponseCalibration &rhs); // not implemented

public:
   ResponseCalibration(DRSBoard* board);
   ~ResponseCalibration();

   void   SetCalibrationParameters(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins,
                                   int numberOfSamples, int numberOfGridPoints, int numberOfXConstPoints,
                                   int numberOfXConstGridPoints, double triggerFrequency, int showStatistics = 0);
   void   ResetCalibration();
   bool   RecordCalibrationPoints(int chipNumber);
   bool   RecordCalibrationPointsV3(int chipNumber);
   bool   RecordCalibrationPointsV4(int chipNumber);
   bool   FitCalibrationPoints(int chipNumber);
   bool   FitCalibrationPointsV3(int chipNumber);
   bool   FitCalibrationPointsV4(int chipNumber);
   bool   OffsetCalibration(int chipNumber);
   bool   OffsetCalibrationV3(int chipNumber);
   bool   OffsetCalibrationV4(int chipNumber);
   double GetTemperature(unsigned int chipIndex);

   bool   WriteCalibration(unsigned int chipIndex);
   bool   WriteCalibrationV3(unsigned int chipIndex);
   bool   WriteCalibrationV4(unsigned int chipIndex);
   bool   ReadCalibration(unsigned int chipIndex);
   bool   ReadCalibrationV3(unsigned int chipIndex);
   bool   ReadCalibrationV4(unsigned int chipIndex);
   bool   Calibrate(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform, short *uWaveform,
                    int triggerCell, float threshold, bool offsetCalib);
   bool   SubtractADCOffset(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform,
                            unsigned short *adcCalibratedWaveform, unsigned short newBaseLevel);
   bool   IsRead(int chipIndex) const { return fCalibrationValid[chipIndex]; }
   double GetPrecision() const { return fPrecision; };

   double GetOffsetAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fOffset[bin]; };
   double GetGainAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fGain[bin]; };
   double GetMeasPointXAt(int ip) const { return fXXSave[ip]; };
   double GetMeasPointYAt(int ip) const { return fYYSave[ip]; };

protected:
   void   InitFields(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins, int numberOfSamples,
                     int numberOfGridPoints, int numberOfXConstPoints, int numberOfXConstGridPoints,
                     double triggerFrequency, int showStatistics);
   void   DeleteFields();
   void   CalibrationTrigger(int mode, double voltage);
   void   CalibrationStart(double voltage);

   static float  GetValue(float *coefficients, float u, int n);
   static int    Approx(float *p, float *uu, int np, int nu, float *coef);
   static void   LeastSquaresAccumulation(float **matrix, int nb, int *ip, int *ir, int mt, int jt);
   static int    LeastSquaresSolving(float **matrix, int nb, int ip, int ir, float *x, int n);
   static void   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);

   static int    MakeDir(const char *path);
   static void   Average(int method,float *samples,int numberOfSamples,float &mean,float &error,float sigmaBoundary);
};


class DRSBoard {
protected:
   class TimeData {
   public:
      class FrequencyData {
      public:
         int    fFrequency;
         double fBin[kNumberOfBins];
      };

      enum {
         kMaxNumberOfFrequencies = 4000
      };
      int            fChip;
      int            fNumberOfFrequencies;
      FrequencyData *fFrequency[kMaxNumberOfFrequencies];

   private:
      TimeData(const TimeData &c);              // not implemented
      TimeData &operator=(const TimeData &rhs); // not implemented

   public:
      TimeData()
      :fChip(0)
      ,fNumberOfFrequencies(0) {
      }
      ~TimeData() {
         int i;
         for (i = 0; i < fNumberOfFrequencies; i++) {
            delete fFrequency[i];
         }
      }
   };

public:
   // DAC channels (CMC Version 1 : DAC_COFSA,DAC_COFSB,DAC_DRA,DAC_DSA,DAC_TLEVEL,DAC_ACALIB,DAC_DSB,DAC_DRB)
   unsigned int         fDAC_COFSA;
   unsigned int         fDAC_COFSB;
   unsigned int         fDAC_DRA;
   unsigned int         fDAC_DSA;
   unsigned int         fDAC_TLEVEL;
   unsigned int         fDAC_ACALIB;
   unsigned int         fDAC_DSB;
   unsigned int         fDAC_DRB;
   // DAC channels (CMC Version 2+3 : DAC_COFS,DAC_DSA,DAC_DSB,DAC_TLEVEL,DAC_ADCOFS,DAC_CLKOFS,DAC_ACALIB)
   unsigned int         fDAC_COFS;
   unsigned int         fDAC_ADCOFS;
   unsigned int         fDAC_CLKOFS;
   // DAC channels (CMC Version 4 : DAC_ROFS_1,DAC_DSA,DAC_DSB,DAC_ROFS_2,DAC_ADCOFS,DAC_ACALIB,DAC_INOFS,DAC_BIAS)
   unsigned int         fDAC_ROFS_1;
   unsigned int         fDAC_ROFS_2;
   unsigned int         fDAC_INOFS;
   unsigned int         fDAC_BIAS;
   // DAC channels (USB EVAL1 (fBoardType 5) : DAC_ROFS_1,DAC_CMOFS,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_TLEVEL,DAC_ONOFS)
   // DAC channels (USB EVAL3 (fBoardType 7) : DAC_ROFS_1,DAC_CMOFS,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_TLEVEL,DAC_ONOFS)
   unsigned int         fDAC_CMOFS;
   unsigned int         fDAC_CALN;
   unsigned int         fDAC_CALP;
   unsigned int         fDAC_ONOFS;
   // DAC channels (DRS4 MEZZ1 (fBoardType 6) : DAC_ONOFS,DAC_CMOFSP,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_CMOFSN,DAC_ROFS_1)
   unsigned int         fDAC_CMOFSP;
   unsigned int         fDAC_CMOFSN;
   // DAC channels (DRS4 EVAL4 (fBoardType 8) : DAC_ONOFS,DAC_TLEVEL4,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_TLEVEL1,DAC_TLEVEL2,DAC_TLEVEL3)
   unsigned int         fDAC_TLEVEL1;
   unsigned int         fDAC_TLEVEL2;
   unsigned int         fDAC_TLEVEL3;
   unsigned int         fDAC_TLEVEL4;

protected:
   // Fields for DRS
   int                  fDRSType;
   int                  fBoardType;
   int                  fNumberOfChips;
   int                  fNumberOfChannels;
   int                  fRequiredFirmwareVersion;
   int                  fFirmwareVersion;
   int                  fBoardSerialNumber;
   int                  fHasMultiBuffer;
   unsigned int         fTransport;
   unsigned int         fCtrlBits;
   int                  fNumberOfReadoutChannels;
   int                  fReadoutChannelConfig;
   int                  fADCClkPhase;
   bool                 fADCClkInvert;
   double               fExternalClockFrequency;
#ifdef HAVE_USB
   MUSB_INTERFACE      *fUsbInterface;
#endif
#ifdef HAVE_VME
   MVME_INTERFACE      *fVmeInterface;
   mvme_addr_t          fBaseAddress;
#endif
   int                  fSlotNumber;
   double               fNominalFrequency;
   double               fTrueFrequency;
   double               fTCALFrequency;
   double               fRefClock;
   int                  fMultiBuffer;
   int                  fDominoMode;
   int                  fDominoActive;
   int                  fADCActive;
   int                  fChannelConfig;
   int                  fChannelCascading;
   int                  fChannelDepth;
   int                  fWSRLoop;
   int                  fReadoutMode;
   unsigned short       fReadPointer;
   int                  fNMultiBuffer;
   int                  fTriggerEnable1;
   int                  fTriggerEnable2;
   int                  fTriggerConfig;
   int                  fTriggerDelay;
   double               fTriggerDelayNs;
   int                  fSyncDelay;
   int                  fDelayedStart;
   int                  fTranspMode;
   int                  fDecimation;
   unsigned short       fStopCell[4];
   unsigned char        fStopWSR[4];
   unsigned short       fTriggerBus;
   double               fROFS;
   double               fRange;
   double               fCommonMode;
   int                  fAcalMode;
   int                  fbkAcalMode;
   double               fAcalVolt;
   double               fbkAcalVolt;
   int                  fTcalFreq;
   int                  fbkTcalFreq;
   int                  fTcalLevel;
   int                  fbkTcalLevel;
   int                  fTcalPhase;
   int                  fTcalSource;
   int                  fRefclk;
   double               fTemperature;

   unsigned char        fWaveforms[kNumberOfChipsMax * kNumberOfChannelsMax * 2 * kNumberOfBins];

   // Fields for Calibration
   int                  fMaxChips;
   char                 fCalibDirectory[1000];

   // Fields for Response Calibration old method
   ResponseCalibration *fResponseCalibration;

   // Fields for Calibration new method
   bool                 fVoltageCalibrationValid;
   double               fCellCalibratedRange;
   double               fCellCalibratedTemperature;
   unsigned short       fCellOffset[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
   unsigned short       fCellOffset2[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
   double               fCellGain[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];

   double               fTimingCalibratedFrequency;
   double               fCellDT[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];

   // Fields for Time Calibration
   TimeData           **fTimeData;
   int                  fNumberOfTimeData;

   // General debugging flag
   int fDebug;

   // Fields for wave transfer
   bool                 fWaveTransferred[kNumberOfChipsMax * kNumberOfChannelsMax];

   // Waveform Rotation
   int                  fTriggerStartBin; // Start Bin of the trigger

private:
   DRSBoard(const DRSBoard &c);              // not implemented
   DRSBoard &operator=(const DRSBoard &rhs); // not implemented

public:
   // Public Methods
#ifdef HAVE_USB
   DRSBoard(MUSB_INTERFACE * musb_interface, int usb_slot);
#endif
#ifdef HAVE_VME
   DRSBoard(MVME_INTERFACE * mvme_interface, mvme_addr_t base_address, int slot_number);

   MVME_INTERFACE *GetVMEInterface() const { return fVmeInterface; };
#endif
   ~DRSBoard();

   int          SetBoardSerialNumber(unsigned short serialNumber);
   int          GetBoardSerialNumber() const { return fBoardSerialNumber; }
   int          HasMultiBuffer() const { return fHasMultiBuffer; }
   int          GetFirmwareVersion() const { return fFirmwareVersion; }
   int          GetRequiredFirmwareVersion() const { return fRequiredFirmwareVersion; }
   int          GetDRSType() const { return fDRSType; }
   int          GetBoardType() const { return fBoardType; }
   int          GetNumberOfChips() const { return fNumberOfChips; }
   // channel         : Flash ADC index
   // readout channel : VME readout index
   // input           : Input on board
   int          GetNumberOfChannels() const { return fNumberOfChannels; }
   int          GetChannelDepth() const { return fChannelDepth; }
   int          GetChannelCascading() const { return fChannelCascading; }
   inline int   GetNumberOfReadoutChannels() const;
   inline int   GetWaveformBufferSize() const;
   inline int   GetNumberOfInputs() const;
   inline int   GetNumberOfCalibInputs() const;
   inline int   GetClockChannel() const;
   inline int   GetTriggerChannel() const;
   inline int   GetClockInput() const { return Channel2Input(GetClockChannel()); }
   inline int   GetTriggerInput() const { return fDRSType < 4 ? Channel2Input(GetTriggerChannel()) : -1; }
   inline int   Channel2Input(int channel) const;
   inline int   Channel2ReadoutChannel(int channel) const;
   inline int   Input2Channel(int input, int ind = 0) const;
   inline int   Input2ReadoutChannel(int input, int ind = 0) const;
   inline int   ReadoutChannel2Channel(int readout) const;
   inline int   ReadoutChannel2Input(int readout) const;

   inline bool  IsCalibChannel(int ch) const;
   inline bool  IsCalibInput(int input) const;
   int          GetSlotNumber() const { return fSlotNumber; }
   int          InitFPGA(void);
   int          Write(int type, unsigned int addr, void *data, int size);
   int          Read(int type, void *data, unsigned int addr, int size);
   int          GetTransport() const { return fTransport; }
   void         RegisterTest(void);
   int          RAMTest(int flag);
   int          ChipTest(int flag);
   unsigned int GetCtrlReg(void);
   unsigned short GetConfigReg(void);
   unsigned int GetStatusReg(void);
   void         SetLED(int state);
   int          SetChannelConfig(int firstChannel, int lastChannel, int nConfigChannels);
   void         SetADCClkPhase(int phase, bool invert);
   void         SetWarmup(unsigned int ticks);
   void         SetCooldown(unsigned int ticks);
   int          GetReadoutChannelConfig() { return fReadoutChannelConfig; }
   void         SetNumberOfChannels(int nChannels);
   int          EnableTrigger(int flag1, int flag2);
   int          GetTriggerEnable(int i) { return i?fTriggerEnable2:fTriggerEnable1; }
   int          SetDelayedTrigger(int flag);
   int          SetTriggerDelayPercent(int delay);
   int          SetTriggerDelayNs(int delay);
   int          GetTriggerDelay() { return fTriggerDelay; }
   double       GetTriggerDelayNs() { return fTriggerDelayNs; }
   int          SetSyncDelay(int ticks);
   int          SetReadoutDelay(float milliseconds);
   int          SetTriggerLevel(double value);
   int          SetIndividualTriggerLevel(int channel, double voltage);
   int          SetTriggerPolarity(bool negative);
   int          SetTriggerConfig(int source);
   int          GetTriggerConfig() { return fTriggerConfig; }
   int          SetDelayedStart(int flag);
   int          SetTranspMode(int flag);
   int          SetStandbyMode(int flag);
   int          SetDecimation(int flag);
   int          GetDecimation() { return fDecimation; }
   int          IsBusy(void);
   int          IsEventAvailable(void);
   int          IsPLLLocked(void);
   int          IsLMKLocked(void);
   int          IsNewFreq(unsigned char chipIndex);
   int          SetDAC(unsigned char channel, double value);
   int          ReadDAC(unsigned char channel, double *value);
   int          GetRegulationDAC(double *value);
   int          StartDomino();
   int          StartClearCycle();
   int          FinishClearCycle();
   int          Reinit();
   int          Init();
   void         SetDebug(int debug) { fDebug = debug; }
   int          Debug() { return fDebug; }
   int          SetDominoMode(unsigned char mode);
   int          SetDominoActive(unsigned char mode);
   int          SetReadoutMode(unsigned char mode);
   int          SoftTrigger(void);
   int          ReadFrequency(unsigned char chipIndex, double *f);
   int          SetFrequency(double freq, bool wait);
   double       VoltToFreq(double volt);
   double       FreqToVolt(double freq);
   double       GetNominalFrequency() const { return fNominalFrequency; }
   double       GetTrueFrequency();
   int          RegulateFrequency(double freq);
   int          SetExternalClockFrequency(double frequencyMHz);
   double       GetExternalClockFrequency();
   int          SetMultiBuffer(int flag);
   int          IsMultiBuffer() { return fMultiBuffer; }
   void         ResetMultiBuffer(void);
   int          GetMultiBufferRP(void);
   int          SetMultiBufferRP(unsigned short rp);
   int          GetMultiBufferWP(void);
   void         IncrementMultiBufferRP(void);
   void         SetVoltageOffset(double offset1, double offset2);
   int          SetInputRange(double center);
   double       GetInputRange(void) { return fRange; }
   double       GetCalibratedInputRange(void) { return fCellCalibratedRange; }
   double       GetCalibratedTemperature(void) { return fCellCalibratedTemperature; }
   double       GetCalibratedFrequency(void) { return fTimingCalibratedFrequency; }
   int          TransferWaves(int numberOfChannels = kNumberOfChipsMax * kNumberOfChannelsMax);
   int          TransferWaves(unsigned char *p, int numberOfChannels = kNumberOfChipsMax * kNumberOfChannelsMax);
   int          TransferWaves(int firstChannel, int lastChannel);
   int          TransferWaves(unsigned char *p, int firstChannel, int lastChannel);
   int          DecodeWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
                           unsigned short *waveform);
   int          DecodeWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform);
   int          GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel, short *waveform,
                        bool responseCalib = false, int triggerCell = -1, int wsr = -1, bool adjustToClock = false,
                        float threshold = 0, bool offsetCalib = true);
   int          GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel, float *waveform,
                        bool responseCalib = false, int triggerCell = -1, int wsr = -1, bool adjustToClock = false,
                        float threshold = 0, bool offsetCalib = true);
   int          GetWave(unsigned int chipIndex, unsigned char channel, short *waveform, bool responseCalib = false,
                        int triggerCell = -1, int wsr = -1, bool adjustToClock = false, float threshold = 0, bool offsetCalib = true);
   int          GetWave(unsigned int chipIndex, unsigned char channel, float *waveform, bool responseCalib,
                        int triggerCell = -1, int wsr = -1, bool adjustToClock = false, float threshold = 0, bool offsetCalib = true);
   int          GetWave(unsigned int chipIndex, unsigned char channel, float *waveform);
   int          GetRawWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform, bool adjustToClock = false);
   int          GetRawWave(unsigned char *waveforms,unsigned int chipIndex, unsigned char channel,
                           unsigned short *waveform, bool adjustToClock = false);
   bool         IsTimingCalibrationValid(void);
   bool         IsVoltageCalibrationValid(void) { return fVoltageCalibrationValid; }
   int          GetTime(unsigned int chipIndex, int channelIndex, double freq, int tc, float *time, bool tcalibrated=true, bool rotated=true);
   int          GetTime(unsigned int chipIndex, int channelIndex, int tc, float *time, bool tcalibrated=true, bool rotated=true);
   int          GetTimeCalibration(unsigned int chipIndex, int channelIndex, int mode, float *time, bool force=false);
   int          GetTriggerCell(unsigned int chipIndex);
   int          GetStopCell(unsigned int chipIndex);
   unsigned char GetStopWSR(unsigned int chipIndex);
   int          GetTriggerCell(unsigned char *waveforms,unsigned int chipIndex);
   void         TestDAC(int channel);
   void         MeasureSpeed();
   void         InteractSpeed();
   void         MonitorFrequency();
   int          TestShift(int n);
   int          EnableAcal(int mode, double voltage);
   int          GetAcalMode() { return fAcalMode; }
   double       GetAcalVolt() { return fAcalVolt; }
   int          EnableTcal(int freq, int level=0, int phase=0);
   int          SelectClockSource(int source);
   int          SetRefclk(int source);
   int          GetRefclk() { return fRefclk; }
   int          GetTcalFreq() { return fTcalFreq; }
   int          GetTcalLevel() { return fTcalLevel; }
   int          GetTcalPhase() { return fTcalPhase; }
   int          GetTcalSource() { return fTcalSource; }
   int          SetCalibVoltage(double value);
   int          SetCalibTiming(int t1, int t2);
   double       GetTemperature() { return fTemperature; };
   int          ReadTemperature();
   int          Is2048ModeCapable();
   int          GetTriggerBus();
   unsigned int GetScaler(int channel);
   int          ReadEEPROM(unsigned short page, void *buffer, int size);
   int          WriteEEPROM(unsigned short page, void *buffer, int size);
   bool         HasCorrectFirmware();
   int          ConfigureLMK(double sampFreq, bool freqChange, int calFreq, int calPhase);

   bool         InitTimeCalibration(unsigned int chipIndex);
   void         SetCalibrationDirectory(const char *calibrationDirectoryPath);
   void         GetCalibrationDirectory(char *calibrationDirectoryPath);

   ResponseCalibration *GetResponseCalibration() const { return fResponseCalibration; }

   double       GetPrecision() const { return fResponseCalibration ? fResponseCalibration->GetPrecision() : 0.1; }
   int          CalibrateWaveform(unsigned int chipIndex, unsigned char channel, unsigned short *adcWaveform,
                                  short *waveform, bool responseCalib, int triggerCell, bool adjustToClock,
                                  float threshold, bool offsetCalib);

   static void  LinearRegression(double *x, double *y, int n, double *a, double *b);
   
   void         ReadSingleWaveform(int nChips, int nChan, 
                                  unsigned short wfu[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins], bool rotated);
   int          AverageWaveforms(DRSCallback *pcb, int chipIndex, int nChan, int prog1, int prog2, unsigned short *awf, int n, bool rotated);
   int          RobustAverageWaveforms(DRSCallback *pcb, int chipIndex, int nChan, int prog1, int prog2, unsigned short *awf, int n, bool rotated);
   int          CalibrateVolt(DRSCallback *pcb);
   int          AnalyzePeriod(Averager *ave, int iIter, int nIter, int channel, float wf[kNumberOfBins], int tCell, double cellDV[kNumberOfBins], double cellDT[kNumberOfBins]);
   int          AnalyzeSlope(Averager *ave, int iIter, int nIter, int channel, float wf[kNumberOfBins], int tCell, double cellDV[kNumberOfBins], double cellDT[kNumberOfBins]);
   int          CalibrateTiming(DRSCallback *pcb);
   static void  RemoveSymmetricSpikes(short **wf, int nwf,
                                      short diffThreshold, int spikeWidth,
                                      short maxPeakToPeak, short spikeVoltage,
                                      int nTimeRegionThreshold);
protected:
   // Protected Methods
   void         ConstructBoard();
   void         ReadSerialNumber();
   void         ReadCalibration(void);

   TimeData    *GetTimeCalibration(unsigned int chipIndex, bool reinit = false);

   int          GetStretchedTime(float *time, float *measurement, int numberOfMeasurements, float period);
};

int DRSBoard::GetNumberOfReadoutChannels() const
{
   return (fDRSType == 4 && fReadoutChannelConfig == 4) ? 5 : fNumberOfChannels;
}

int DRSBoard::GetWaveformBufferSize() const
{
   int nbin=0;
   if (fDRSType < 4) {
      nbin = fNumberOfChips * fNumberOfChannels * kNumberOfBins;
   } else {
      if (fBoardType == 6) {
         if (fDecimation) {
            nbin = fNumberOfChips * (4 * kNumberOfBins + kNumberOfBins / 2);
         } else {
            nbin = fNumberOfChips * 5 * kNumberOfBins;
         }
      } else if (fBoardType == 7 || fBoardType == 8 || fBoardType == 9)
         nbin = fNumberOfChips * fNumberOfChannels * kNumberOfBins;
   }
   return nbin * static_cast<int>(sizeof(short int));
}

int DRSBoard::GetNumberOfInputs() const
{
   // return number of input channels excluding clock and trigger channels.
   if (fDRSType < 4) {
      return fNumberOfChannels - 2;
   } else {
      return fNumberOfChannels / 2;
   }
}
int DRSBoard::GetNumberOfCalibInputs() const
{
   return (fDRSType < 4) ? 2 : 1;
}
int DRSBoard::GetClockChannel() const
{
   return fDRSType < 4 ? 9 : 8;
}
int DRSBoard::GetTriggerChannel() const
{
   return fDRSType < 4 ? 8 : -1;
}

int DRSBoard::Channel2Input(int channel) const
{
   return (fDRSType < 4) ? channel : channel / 2;
}
int DRSBoard::Channel2ReadoutChannel(int channel) const
{
   if (fDRSType < 4) {
      return channel;
   } else {
      if (fReadoutChannelConfig == 4) {
         return channel / 2;
      } else {
         return channel;
      }
   }
}
int DRSBoard::Input2Channel(int input, int ind) const
{
   if (fChannelCascading == 1) {
      return (fDRSType < 4) ? input : (input * 2 + ind);
   } else {
      if (input == 4) { // clock
         return 8;
      } else {
         return input;
      }
   }
}
int DRSBoard::Input2ReadoutChannel(int input, int ind) const
{
   if (fDRSType < 4) {
      return input;
   } else {
      if (fReadoutChannelConfig == 4) {
         return input;
      } else {
         return (input * 2 + ind);
      }
   }
}
int DRSBoard::ReadoutChannel2Channel(int readout) const
{
   if (fDRSType < 4) {
      return readout;
   } else {
      if (fReadoutChannelConfig == 4) {
         return readout * 2;
      } else {
         return readout;
      }
   }
}
int DRSBoard::ReadoutChannel2Input(int readout) const
{
   if (fDRSType < 4) {
      return readout;
   } else {
      if (fReadoutChannelConfig == 4) {
         return readout;
      } else {
         return readout / 2;
      }
   }
}
bool DRSBoard::IsCalibChannel(int ch) const
{
   // return if it is clock or trigger channel
   if (fDRSType < 4)
      return ch == GetClockChannel() || ch == GetTriggerChannel();
   else
      return ch == GetClockChannel();
}
bool DRSBoard::IsCalibInput(int input) const
{
   // return if it is clock or trigger channel
   int ch = Input2Channel(input);
   if (fDRSType < 4)
      return ch == GetClockChannel() || ch == GetTriggerChannel();
   else
      return ch == GetClockChannel();
}

class DRS {
protected:
   // constants
   enum {
      kMaxNumberOfBoards = 40
   };

protected:
   DRSBoard       *fBoard[kMaxNumberOfBoards];
   int             fNumberOfBoards;
   char            fError[256];
#ifdef HAVE_VME
   MVME_INTERFACE *fVmeInterface;
#endif

private:
   DRS(const DRS &c);              // not implemented
   DRS &operator=(const DRS &rhs); // not implemented

public:
   // Public Methods
   DRS();
   ~DRS();

   DRSBoard        *GetBoard(int i) { return fBoard[i]; }
   void             SetBoard(int i, DRSBoard *b);
   DRSBoard       **GetBoards() { return fBoard; }
   int              GetNumberOfBoards() const { return fNumberOfBoards; }
   bool             GetError(char *str, int size);
   void             SortBoards();

#ifdef HAVE_VME
   MVME_INTERFACE *GetVMEInterface() const { return fVmeInterface; };
#endif
};

#endif                          // DRS_H
