/********************************************************************\ DAQReadout.cc Main DAQ routines. Sebastian Commichau, Oliver Grimm \********************************************************************/ #include "DAQReadout.h" static const char* daq_state_str[] = {"active", "stopped"}; static const char* daq_runtype_str[] = {"data", "pedestal", "reserved", "test"}; static const struct CL_Struct { const char *Name; void (DAQReadout::*CommandPointer)(); bool NeedNotBusy; const char *Parameters; const char *Help; } CommandList[] = {{"board", &DAQReadout::cmd_board, true, " [j] | " ,"Address board i, boards i-j, all boards"}, {"status", &DAQReadout::cmd_status, false, "[daq|drs]", "Show DAQ/DRS status information"}, {"frequency", &DAQReadout::cmd_freq, true, " [reg]", "Set DRS sampling frequency (regulated)"}, {"acalib", &DAQReadout::cmd_acalib, true, " ", "Amplitude calibration"}, {"tcalib", &DAQReadout::cmd_tcalib, true, " ", "Amplitude calibration"}, {"trigger", &DAQReadout::cmd_trigger, true, "", "Hardware trigger on or off"}, {"wmode", &DAQReadout::cmd_wmode, true, "", "Domino wave running or stopped during read out"}, {"rmode", &DAQReadout::cmd_rmode, true, "", "Readout start at first bin or stop position (DRS4)"}, {"dmode", &DAQReadout::cmd_dmode, true, "", "Domino wave single shot or continuous"}, {"centre", &DAQReadout::cmd_centre, true, "", "Set centre of input range (DRS4)"}, {"refclk", &DAQReadout::cmd_refclk, true, "", "Set reference clock to FPGA or external (DRS4)"}, {"read", &DAQReadout::cmd_read, false, " [res]", "Read current data (and restart if DAQ not active)"}, {"take", &DAQReadout::cmd_take, false, " [n] [source]", "Start run (data, pedestal or test) with n events"}, {"events", &DAQReadout::cmd_events, false, "", "Number of events in current run"}, {"start", &DAQReadout::cmd_start, true, "", "Start DRS and DAQ without disk writing (feedback will be called)"}, {"stop", &DAQReadout::cmd_stop, false, "", "Issue soft trigger and stop DAQ"}, {"regtest", &DAQReadout::cmd_regtest, true, "", "DRS register test"}, {"ramtest", &DAQReadout::cmd_ramtest, true, "", "DRS RAM integrity and speed test"}, {"chiptest", &DAQReadout::cmd_chiptest, true, "", "Chip test"}, {"eepromtest", &DAQReadout::cmd_eepromtest, true, "", "EEPROM test"}, {"led", &DAQReadout::cmd_led, true, "", "Turn LED on or off"}, {"config", &DAQReadout::cmd_config, false, "", "Print drsdaq configuration"}, {"serial", &DAQReadout::cmd_serial, true, " ", "Set serial# of board to (experts only)"}, {"disk", &DAQReadout::cmd_disk, false, "" ,"Remaining disk space"}, {"uptime", &DAQReadout::cmd_uptime, false, "", "Get DAQ uptime"}, {"exit", &DAQReadout::cmd_exit, false, "", "Exit program"}, {"fmode", &DAQReadout::cmd_fmode, false, "[off|active|targ]", "Set or get feedback mode"}, {"faverage", &DAQReadout::cmd_faverage, false, "[n]", "Set ot get number of averages for feedback"}, {"fgain", &DAQReadout::cmd_fgain, false, "[gain]", "Set ot get feedback gain"}, {"ftarget", &DAQReadout::cmd_ftarget, false, "[brd chip chan]", "Set or get target value (also 'all' supported)"}, {"fresponse", &DAQReadout::cmd_fresponse, false, "[voltage]", "Start response measurement with given voltage difference"}, {"fconfig", &DAQReadout::cmd_fconfig, false, "", "Print feedback configuration"}, {"help", &DAQReadout::cmd_help, false, "", "Print help"}}; // ----------------------------------------------- // ***** Constructor: Class initialisation ***** // ----------------------------------------------- // DAQReadout::DAQReadout() :EvidenceServer(SERVER_NAME) { time(&StartTime); // Start time of DAQ // Initialize status structure daq_state = stopped; daq_runtype = data; Socket = -1; Exit = false; NumEvents = 0; NumEventsRequested = 0; NumBoards = 0; FirstBoard = 0; LastBoard = -1; CmdFromSocket = false; // Get configuration data fRawDataPath = GetConfig("RawDataPath"); fCalibDataPath = GetConfig("CalibDataPath"); fFirstSample = atoi(GetConfig("FirstSample")); fSamples = atoi(GetConfig("Samples")); fMinDiskSpaceMB = atoi(GetConfig("MinDiskSpaceMB")); fMaxFileSizeMB = atoi(GetConfig("MaxFileSizeMB")); fCCPort = atoi(GetConfig("CCPort")); fDefaultFrequency = atof(GetConfig("DefaultFrequency")); if (fFirstSample < 0 || fFirstSample > kNumberOfBins || fSamples > kNumberOfBins) { PrintMessage("Warning: Sample range in configuration beyond limits, setting to full range\n"); fFirstSample = kNumberOfBins; fSamples = kNumberOfBins; } snprintf(CalibInfoFilename,sizeof(CalibInfoFilename), "%s/CalibInfo", fCalibDataPath); // Allocate headers and initialise to zero RHeader = new RunHeader; memset(RHeader, 0, sizeof(RunHeader)); EHeader = new EventHeader; memset(EHeader, 0, sizeof(EventHeader)); // Scan for DRS boards NumBoards = GetNumberOfBoards(); DRSFreq = new float [NumBoards]; ACalib = new bool [NumBoards]; ACalibTemp = new double [NumBoards]; TCalib = new bool [NumBoards]; if (NumBoards == 0) PrintMessage("No DRS boards found - check VME crate and configuration file!\n"); for (int i=0; iInit(); DRSFreq[i] = 0; ACalib[i] = false; ACalibTemp[i] = -999; TCalib[i] = false; } BStruct = new BoardStructure [NumBoards == 0 ? 1:NumBoards]; memset(BStruct, 0, sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards)); WaveForm = new short [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins]; TriggerCell = new int [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax] (); // Zero initialised // Create instance of HV feedback (must be called after CMC board detection) HVFB = new HVFeedback(this); } // ------------------------ // ***** Destructor ***** // ------------------------ DAQReadout::~DAQReadout() { delete RHeader; delete EHeader; delete HVFB; delete[] ACalibTemp; delete[] ACalib; delete[] TCalib; delete[] DRSFreq; delete[] BStruct; delete[] WaveForm; delete[] TriggerCell; } // -------------------------------- // ***** Command evaluation ***** // -------------------------------- void DAQReadout::CommandControl(char *Command) { if (strlen(Command)==0) return; // Ignore empty commands if(Command[0]=='.') { // Shell command system(&(Command[1])); return; } for(int i=0; i*CommandList[CmdNumber].CommandPointer)(); return; } PrintMessage("Unknown command '%s'\n",Param[0]); return; } // Get uptime void DAQReadout::cmd_uptime() { time_t ActualT; time (&ActualT); PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60); } // Print disk space void DAQReadout::cmd_disk() { PrintMessage("Free disk space (%s) [MB]: %lu\n", fRawDataPath, CheckDisk(fRawDataPath)); } // Print current number of events void DAQReadout::cmd_events() { if(daq_state != active) PrintMessage("DAQ not active\n"); else PrintMessage("Current number of events: %d (of %d requested)\n", NumEvents, NumEventsRequested); } // Print DAQ configuration void DAQReadout::cmd_config() { PrintConfig(CmdFromSocket ? MsgToSocket : MsgToConsole); } // Start DAQ void DAQReadout::cmd_take() { // Check conditions if this is not a test run if(!Match(Param[1],"test")) { if (daq_state==active || NumBoards==0) { PrintMessage("DAQ is busy or no boards available\n"); return; } // Check if all boards have the same number of DRS chips and channels // (restriction of current data format) for (int i=FirstBoard; i<=LastBoard-1; i++) { if ((GetBoard(i)->GetNumberOfChannels() != GetBoard(i+1)->GetNumberOfChannels()) || (GetBoard(i)->GetNumberOfChips() != GetBoard(i+1)->GetNumberOfChips())) { PrintMessage("Cannot take data is not all boards have the same number of DRS chips and channels due to data format restriction\n"); return; } } if (!IsDRSFreqSet()) { PrintMessage("Setting default frequency\n"); SetDRSFrequency(fDefaultFrequency, true); } if (!ReadCalibration()) { PrintMessage("Cannot start run if response calibration not read\n"); //return; } // Readout from domino wave stop position (ignored if not DRS4) SetDOMINOReadMode(1); } if (Match(Param[1],"data")) { HWTrigger(1); daq_runtype = data; } else if (Match(Param[1],"pedestal")) { HWTrigger(0); daq_runtype = pedestal; } else if (Match(Param[1],"test")) { daq_runtype = test; } else { PrintUsage(); return; } if (NParam==3) NumEventsRequested = atoi(Param[2]); if (NParam==4) { NumEventsRequested = atoi(Param[2]); strncpy(RHeader->Description, Param[3], sizeof(RHeader->Description)); } else snprintf(RHeader->Description,sizeof(RHeader->Description),"DUMMY"); // Determine new run number using the file given by RUN_NUM_FILE FILE *RunNumFile = fopen(RUN_NUM_FILE,"r+"); if(RunNumFile == NULL) { PrintMessage("Error: Could not open file '%s' that contains the last run number (%s)\n",RUN_NUM_FILE,strerror(errno)); return; } if(fscanf(RunNumFile,"%u", &RunNumber) != 1 ) { PrintMessage("Error: Could not read run number from file '%s'\n",RUN_NUM_FILE); fclose(RunNumFile); return; } RunNumber++; rewind(RunNumFile); if((fprintf(RunNumFile,"%.8u ",RunNumber) < 0) || (fclose(RunNumFile)!=0)) { PrintMessage("Error: Could not write to or close run number file '%s'\n",RUN_NUM_FILE); PrintMessage("*** This is a serious error because run numbers will get mixed. Fix it. DAQ will terminate.\n"); throw; } // Create DAQ thread if ((pthread_create(&thread_DAQ, NULL, (void * (*)(void *)) DAQ,(void *) this)) != 0) PrintMessage("pthread_create failed with DAQ thread (%s)\n",strerror(errno)); else { daq_state = active; Stop = false; pthread_detach(thread_DAQ); } } // Start DRS void DAQReadout::cmd_start() { if (IsDRSFreqSet()) { StartDRS(); PrintMessage("Domino wave started\n"); // Create DAQ thread if ((pthread_create(&thread_DAQ_Silent, NULL, (void * (*)(void *)) DAQ_Silent,(void *) this)) != 0) PrintMessage("pthread_create failed with DAQ_Silent thread (%s)\n",strerror(errno)); else { daq_state = active; Stop = false; pthread_detach(thread_DAQ_Silent); } } } // EEPROM test void DAQReadout::cmd_eepromtest() { unsigned short buf[16384],rbuf[16384]; int n = 0; for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("EEPROM test of board #%d:\n\r",i); for (int j=0; j<16384; j++) buf[j] = rand(); GetBoard(i)->WriteEEPROM(1, buf, sizeof(buf)); memset(rbuf, 0, sizeof(rbuf)); GetBoard(i)->Write(T_RAM, 0, rbuf, sizeof(rbuf)); GetBoard(i)->ReadEEPROM(1, rbuf, sizeof(rbuf)); for (int j=0; j<16384; j++) if (buf[j] != rbuf[j]) n++; printf("32 kByte written, %d errors\n", n); } } // RAM test void DAQReadout::cmd_ramtest() { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("RAM integrity and speed test of board #%d:\n",i); GetBoard(i)->RAMTest(3); } } // Register test void DAQReadout::cmd_regtest() { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("Register test of board #%d:\n\r",i); GetBoard(i)->RegisterTest(); } } // Chip test void DAQReadout::cmd_chiptest() { for (int i=FirstBoard; i<=LastBoard; i++) { if (GetBoard(i)->ChipTest()) PrintMessage("Chip test of board %i successful\n", i); else PrintMessage("Chip test of board %i failed\n", i); } } // Stop DAQ void DAQReadout::cmd_stop() { if(!daq_state==active && !IsDRSBusy()) PrintMessage("Nothing to stop\n"); if (daq_state==active) StopRun(); if (IsDRSBusy()) { StopDRS(); PrintMessage("Domino wave stopped\n"); } } // Read current data // For socket transmission: all numbers must be separated by exactly one // whitespace; the first number is the number of numbers that follow, the // second number the sampling frequency in GHz, the third the conversion factor void DAQReadout::cmd_read() { if(NumBoards==0) { PrintMessage("No mezzanine boards available\n"); return; } if (NParam<4) { PrintUsage(); return; } int Board = atoi(Param[1]), Chip = atoi(Param[2]), Channel = atoi(Param[3]); if (Board>LastBoard || Board=GetBoard(Board)->GetNumberOfChannels()) { PrintMessage("Error: Channel number out of range\n"); return; } if (Chip<0 || Chip>=GetBoard(Board)->GetNumberOfChips()) { PrintMessage("Error: Chip number out of range\n"); return; } if (daq_state != active) { ReadCalibration(); if(NParam==5) StopDRS(); ReadCalibratedDRSData(); if(NParam==5) StartDRS(); } PrintMessage(CmdFromSocket ? MsgToSocket:MsgToConsole|MsgToLog, "==START== %d %.2f %.2f ",kNumberOfBins+2,DRSFreq[Board],GetBoard(Board)->GetPrecision()); double mean=0,square=0.0; for (int k=0; kSetInputRange(atof(Param[1])) == 1) { PrintMessage("Range of board %d: %.2f to %.2f Volt (centre %.2f V)\n",i,atof(Param[1])-0.5, atof(Param[1])+0.5, atof(Param[1])); } else PrintMessage("Centre voltage of board %d out of range\n", i); } } // Set refclock source void DAQReadout::cmd_refclk() { if (NParam!=2) { PrintUsage(); return; } for (int i=FirstBoard; i<=LastBoard; i++) { if (Match(Param[1], "extern")) GetBoard(i)->SetRefclk(1); else GetBoard(i)->SetRefclk(0); } } // Set trigger mode void DAQReadout::cmd_trigger() { if (Match(Param[1],"on")) HWTrigger(1); else if (Match(Param[1],"off")) HWTrigger(0); else PrintUsage(); } // Set serial number of board void DAQReadout::cmd_serial() { if (NParam==4 && Match(Param[3], "expert")) { if ((atoi(Param[1]) < FirstBoard) || (atoi(Param[1]) > LastBoard)) PrintMessage("Board number out of range (%d...%d)!\n",FirstBoard,LastBoard); else if (atoi(Param[2]) < 100 || atoi(Param[2]) >= 1000) PrintMessage("Serial number out of range (100...999)!\n"); else { PrintMessage("Flashing EEPROM of board %d...\n", atoi(Param[1])); (GetBoard(atoi(Param[1])))->SetBoardSerialNumber(atoi(Param[2])); } } else PrintMessage("You are not allowed to change the serial number!\n"); } // Do amplitude calibration void DAQReadout::cmd_acalib() { if (!IsDRSFreqSet()) { PrintMessage("Set sampling frequency for all boards first\n"); return; } for (int i=FirstBoard; i<=LastBoard; i++) { if (GetBoard(i)->GetDRSType()==2 && (NParam!=2 || !atof(Param[1]))) { PrintUsage(); return; } PrintMessage("Creating amplitude calibration of board %d (serial #%04d)\n Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber()); GetBoard(i)->EnableTcal(0); GetBoard(i)->SelectClockSource(0); if (GetBoard(i)->GetDRSType() == 4) { GetBoard(i)->SetFrequency(DRSFreq[i], true); GetBoard(i)->CalibrateVolt(this); } else { GetBoard(i)->Init(); GetBoard(i)->SetFrequency(DRSFreq[i], true); GetBoard(i)->SoftTrigger(); if (GetBoard(i)->GetDRSType() == 3) { GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,11,0,20,0,0,0,0,0); } else GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,36,110,20,19,40,15,atof(Param[1]),0); GetBoard(i)->SetCalibrationDirectory(fCalibDataPath); for (int j=0; j<2; j++) { GetBoard(i)->GetResponseCalibration()->ResetCalibration(); while (!GetBoard(i)->GetResponseCalibration()->RecordCalibrationPoints(j)) {} PrintMessage("Calibration points recorded.\n"); while (!GetBoard(i)->GetResponseCalibration()->FitCalibrationPoints(j)) {} PrintMessage("Calibration points fitted.\n"); while (!GetBoard(i)->GetResponseCalibration()->OffsetCalibration(j)) {} PrintMessage("Offset calibration done.\n"); if (!GetBoard(i)->GetResponseCalibration()->WriteCalibration(j)) break; } } // else for DRS2/3 ACalib[i] = true; ACalibTemp[i] = GetBoard(i)->GetTemperature(); } // Loop over boards PrintMessage("Amplitude calibration finished\n"); // Write short calibration information time_t Time = time(NULL); FILE *InfoFile = fopen(CalibInfoFilename, "w"); if (InfoFile != NULL) { fprintf(InfoFile, "# Calibration information as of %s\n", ctime(&Time)); for (int i=0; iGetBoardSerialNumber(), ACalib[i], ACalibTemp[i], TCalib[i], DRSFreq[i]); } fclose(InfoFile); } else PrintMessage("Could not write calibration information to file '%s'\n", CalibInfoFilename); } // Do time calibration void DAQReadout::cmd_tcalib() { if (!IsDRSFreqSet()) { PrintMessage("Set sampling frequency for all boards first\n"); return; } if (!ReadCalibration()) { PrintMessage("Amplitude calibration has to be done first\n"); return; } for (int i=FirstBoard; i<=LastBoard; i++) { if (GetBoard(i)->GetDRSType() != 4 || GetBoard(i)->GetFirmwareVersion() < 13279) { PrintMessage("Time calibration needs DRS4 and minimum firmware version 13279, skipping board %d\n", i); continue; } PrintMessage("Creating time calibration of board %d (serial #%04d)\n Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber()); GetBoard(i)->SetFrequency(DRSFreq[i], true); if (GetBoard(i)->CalibrateTiming(this) != 1) { PrintMessage("Time calibration method returned error status, stopping calibration\n"); return; } TCalib[i] = true; // Write calibration data to file float Time[kNumberOfChipsMax][kNumberOfBins]; char *Filename; FILE *Calibfile; bool WriteOK = true; // Copy calibration data into array for (int Chip=0; ChipGetNumberOfChips(); Chip++) { GetBoard(i)->GetTime(Chip, Time[Chip], true, false); } // Write calibration data to file if (asprintf(&Filename, "%s/TCalib_%d_%.2fGHz.txt", fCalibDataPath, GetBoard(i)->GetBoardSerialNumber(), DRSFreq[i]) == -1) { PrintMessage("Error: asprintf() failed, cannot generate filename (%s)\n", strerror(errno)); return; } if ((Calibfile=fopen(Filename,"w")) == NULL) { PrintMessage("Error: Could not open file '%s' \n", Filename); } else { if(fprintf(Calibfile, "# DRS time calibration\n") == -1) WriteOK = false; for (int Bin=0; BinGetNumberOfChips(); Chip++) { if(fprintf(Calibfile, "%.2f ", Time[Chip][Bin]) == -1) WriteOK = false; } if(fprintf(Calibfile, "\n") == -1) WriteOK = false; } if (fclose(Calibfile) != 0) PrintMessage("Error closing file '%s'\n", Filename); } if (!WriteOK) PrintMessage("Error writing to file '%s'\n", Filename); else PrintMessage("Calibration written to file '%s'\n", Filename); free(Filename); } PrintMessage("Time calibration finished\n"); } // Set DRS sampling frequency void DAQReadout::cmd_freq() { if (NParam>=2 && atof(Param[1])) { SetDRSFrequency(atof(Param[1]), NParam==2 ? false : true); } else PrintUsage(); } // Set LED void DAQReadout::cmd_led() { if (Match(Param[1], "on") || Match(Param[1], "off")) for (int i=FirstBoard; i<=LastBoard; i++) (GetBoard(i))->SetLED(Match(Param[1], "on") ? 1 : 0); else PrintUsage(); } // Print status void DAQReadout::cmd_status() { double freq; if(NParam==1 || Match(Param[1],"daq")) { PrintMessage("********** DAQ STATUS **********\n" " DAQ: %s\n" " Run number: %d\n" " Run type: %s\n" " Event: %d\n" " Requested events per run: %d\n" " Storage directory: %s\n" " Disk space: %lu MByte\n" " Socket state: %s\n" " Total number of DRS boards: %d\n" " Active DRS boards: %d\n", daq_state_str[daq_state], daq_state==active ? (int) RunNumber:-1, daq_state==active ? daq_runtype_str[daq_runtype]:"n/a", NumEvents, NumEventsRequested, fRawDataPath, CheckDisk(fRawDataPath), Socket==-1 ? "disconnected":"connected", NumBoards, LastBoard - FirstBoard + 1); for (int i=FirstBoard;i<=LastBoard;i++) PrintMessage(" Frequency of board %d set: %s\n",i,(DRSFreq[i]!=0 ? "yes":"no")); } if(NParam==1 || Match(Param[1],"drs")) { PrintMessage("\n********** DRS STATUS **********\n"); if (NumBoards) { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("Board #%d in slot %d %s\n",i,((GetBoard(i))->GetSlotNumber() >> 1)+2,((GetBoard(i))->GetSlotNumber() & 1)==0 ? "upper":"lower"); PrintMessage(" DRS%d serial %d, firmware %d\n" " Actual temperature: %1.1lf C\n" " Calibration temp.: %1.1lf C\n" " Status reg.: 0x%08X\n", GetBoard(i)->GetDRSType(), GetBoard(i)->GetBoardSerialNumber(), GetBoard(i)->GetFirmwareVersion(), GetBoard(i)->GetTemperature(), ACalibTemp[i], GetBoard(i)->GetStatusReg()); if (GetBoard(i)->GetStatusReg() & BIT_RUNNING) PrintMessage(" Domino wave running\n"); if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ1) PrintMessage(" New Freq1 ready\n"); if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ2) PrintMessage(" New Freq2 ready\n"); PrintMessage(" Control reg.: 0x%08X\n", (GetBoard(i))->GetCtrlReg()); if (GetBoard(i)->GetCtrlReg() & BIT_AUTOSTART) PrintMessage(" AUTOSTART enabled\n"); if (GetBoard(i)->GetCtrlReg() & BIT_DMODE) PrintMessage(" DMODE circular\n"); else PrintMessage(" DMODE single shot\n"); if (GetBoard(i)->GetCtrlReg() & BIT_LED) PrintMessage(" LED\n"); if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_EN) PrintMessage(" TCAL enabled\n"); if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_SOURCE) PrintMessage(" TCAL_SOURCE enabled\n"); if (GetBoard(i)->GetCtrlReg() & BIT_FREQ_AUTO_ADJ) PrintMessage(" FREQ_AUTO_ADJ enabled\n"); if (GetBoard(i)->GetCtrlReg() & BIT_ENABLE_TRIGGER1) PrintMessage(" ENABLE_TRIGGER\n"); if (GetBoard(i)->GetCtrlReg() & BIT_LONG_START_PULSE) PrintMessage(" LONG_START_PULSE\n"); if (GetBoard(i)->GetCtrlReg() & BIT_ACAL_EN) PrintMessage(" ACAL enabled\n"); PrintMessage(" Trigger bus: 0x%08X\n", GetBoard(i)->GetTriggerBus()); if (GetBoard(i)->IsBusy()) { GetBoard(i)->ReadFrequency(0, &freq); PrintMessage(" Frequency0: %1.4lf GHz\n", freq); GetBoard(i)->ReadFrequency(1, &freq); PrintMessage(" Frequency1: %1.4lf GHz\n", freq); } else PrintMessage(" Domino wave stopped\n"); } } else PrintMessage("No DRS boards available!\n\n"); } } // Adress DRS boards void DAQReadout::cmd_board() { if (Match(Param[1],"all")) { FirstBoard = 0; LastBoard = GetNumberOfBoards()-1; } else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])=0 && atoi(Param[1])0 && atoi(Param[2]) Execute shell command\n\n" "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n" "Test data can also be written if no DRS boards are available.\n" "Strings containing spaces have to be enclosed in \"double quotes\".\n"); } // Exit programm void DAQReadout::cmd_exit() { if (CmdFromSocket) { PrintMessage("Exit command not allowed over socket.\n"); return; } if (daq_state==active) PrintMessage("Issue \"stop\" first to stop daq\n"); else { Exit = true; if(SocketThread != NULL) pthread_kill(*SocketThread, SIGUSR1); } } // Set/get mode of feedback void DAQReadout::cmd_fmode() { if(Match(Param[1],"off")) HVFB->SetFBMode(FB_Off); if(Match(Param[1],"active")) HVFB->SetFBMode(FB_Active); if(Match(Param[1],"targets")) HVFB->SetFBMode(FB_Targets); HVFB->GetFBMode(); } // Set/get current number of events void DAQReadout::cmd_faverage() { if(NParam==1) PrintMessage("Current number of feedback events: %u (acting when %u events are reached)\n", HVFB->GetCurrentCount(), HVFB->GetNumAverages()); else if(atoi(Param[1])>=0) HVFB->SetNumAverages(atoi(Param[1])); else PrintUsage(); } // Set/get feedback gain void DAQReadout::cmd_fgain() { if(NParam==2) HVFB->SetGain(atof(Param[1])); PrintMessage("Feedback gain is %.2f\n", HVFB->GetGain()); } // Set/get target value void DAQReadout::cmd_ftarget() { if(NParam==1) HVFB->GetTargets(); else if(NParam!=5) PrintUsage(); else for (int i=FirstBoard; i<=LastBoard; i++) for (int j=0; jGetNumberOfChips(); j++) for (int k=0; kGetNumberOfChannels(); k++) if ((atoi(Param[1])==i || Match(Param[1],"all")) && (atoi(Param[2])==j || Match(Param[2],"all")) && (atoi(Param[3])==k || Match(Param[3],"all"))) HVFB->SetTarget(i,j,k,atof(Param[4])); } // Start response measurement void DAQReadout::cmd_fresponse() { if(NParam==1) HVFB->GetResponse(); else if(atof(Param[1])) HVFB->MeasureResponse(atof(Param[1])); else PrintUsage(); } // Print feedback configuration void DAQReadout::cmd_fconfig() { HVFB->PrintConfig(CmdFromSocket ? MsgToSocket : MsgToConsole); } // ---------------------------------------------- // ***** Utility function for DRS control ***** // ---------------------------------------------- // Start domino wave void DAQReadout::StartDRS() { for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->StartDomino(); } // Stop domino wave void DAQReadout::StopDRS() { for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->SoftTrigger(); } // Transfer amplitude-calibrated waveforms to memory void DAQReadout::ReadCalibratedDRSData() { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->TransferWaves(GetBoard(i)->GetNumberOfChannels()*GetBoard(i)->GetNumberOfChips()); for (int k=0; kGetNumberOfChips(); k++) { TriggerCell[i][k] = GetBoard(i)->GetTriggerCell(k); for (int j=0; jGetNumberOfChannels(); j++) { GetBoard(i)->GetWave(k, j, WaveForm[i][k][j], true, TriggerCell[i][k]); } } } } // Read calibration data bool DAQReadout::ReadCalibration() { static char Buffer[MAX_COM_SIZE]; int Serial, Calib; float Temp, Freq; for (int i=FirstBoard; i<=LastBoard; i++) { if (GetBoard(i)->GetDRSType() == 4) { if (ACalib[i] == false) { // Check calibration info file if EEPROM data on DRS board still valild FILE *CalibInfo = fopen(CalibInfoFilename, "r"); if (CalibInfo == NULL) return false; fgets(Buffer, sizeof(Buffer), CalibInfo); // skip first two lines fgets(Buffer, sizeof(Buffer), CalibInfo); while (fgets(Buffer, sizeof(Buffer), CalibInfo) != NULL) { if (sscanf(Buffer, "%d %d %f %*d %f", &Serial, &Calib, &Temp, &Freq) != 4) { fclose(CalibInfo); return false; } if (Serial==GetBoard(i)->GetBoardSerialNumber() && int(Freq*100)==int(DRSFreq[i]*100) && Calib==1) { ACalib[i] = true; ACalibTemp[i] = Temp; break; } } fclose(CalibInfo); } } else { if (!ACalib[i]) { GetBoard(i)->SetCalibrationDirectory(fCalibDataPath); PrintMessage("Reading response calibration file for board %d from: \"%s\"\n", i, fCalibDataPath); for (int Chip=0; ChipGetNumberOfChips(); Chip++) { if (GetBoard(i)->GetResponseCalibration()->ReadCalibration(Chip) == false) return false; } ACalib[i] = true; } } if (fabs(ACalibTemp[i]-GetBoard(i)->GetTemperature())>2) PrintMessage("Warning: Large difference to calibration temperature for board %d\n", i); } // Loop over boards return true; } // Stop DAQ void DAQReadout::StopRun() { if(daq_state != active) PrintMessage("DAQ is not active.\n"); else { Stop = true; PrintMessage("DAQ will stop.\n"); } } // Set DOMINO mode void DAQReadout::SetDOMINOMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetDominoMode(mode==1 ? 1:0); PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot"); } } // Set DOMINO readout mode void DAQReadout::SetDOMINOReadMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetReadoutMode(mode); PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position"); } } // Set DOMINO wave mode void DAQReadout::SetDOMINOWaveMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetDominoActive(mode); PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped"); } } // Enable hardware trigger of all boards void DAQReadout::HWTrigger(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->EnableTrigger(mode, 0); PrintMessage("Hardware trigger of board %d %s\n",i,mode==1 ? "enabled":"disabled"); } else PrintMessage("No DRS boards available\n"); } // Set DRS sampling frequency void DAQReadout::SetDRSFrequency(double freq, bool Regulation) { PrintMessage("Setting frequency %s regulation\n",Regulation ? "with":"without"); for (int i=FirstBoard; i<=LastBoard; i++) { if ((Regulation ? GetBoard(i)->RegulateFrequency(freq) : GetBoard(i)->SetFrequency(freq, true)) == 0) { PrintMessage("Warning: Board %d has not reached the requested value\n",i); } double f; GetBoard(i)->ReadFrequency(0, &f); DRSFreq[i] = f; ACalib[i] = false; TCalib[i] = false; PrintMessage("Board %d is running at %1.3lf GHz\n",i,f); } } // Check if DRS is sampling bool DAQReadout::IsDRSBusy() { for (int i=FirstBoard; i<=LastBoard; i++) if ((GetBoard(i))->IsBusy()) return true; return false; } // Check if DRS frequency is set bool DAQReadout::IsDRSFreqSet() { for (int i=FirstBoard; i<=LastBoard; i++) if (DRSFreq[i] == 0) { PrintMessage("Sampling frequency of DRS board %d not set\n", i); return false; } return true; } // Open new raw data file bool DAQReadout::OpenRawFile() { time_t rawtime; struct tm *timeinfo; char RunDate[MAX_COM_SIZE], Buffer[MAX_COM_SIZE]; // Write run date to status structure (if after 13:00, use next day) time(&rawtime); timeinfo = gmtime(&rawtime); if(timeinfo->tm_hour>=13) rawtime += 12*60*60; timeinfo = gmtime(&rawtime); snprintf(RunDate,sizeof(RunDate),"%d%02d%02d",timeinfo->tm_year+1900,timeinfo->tm_mon + 1,timeinfo->tm_mday); // Create direcory if not existing (ignore error if already existing) and change to it snprintf(Buffer, sizeof(Buffer), "%s/%s", fRawDataPath, RunDate); if(mkdir(Buffer, S_IRWXU|S_IRWXG)==-1 && errno!=EEXIST) { PrintMessage("\rError: Could not create direcory \"%s\" (%s)\n", Buffer, strerror(errno)); return false; } // Generate filename snprintf(FileName,sizeof(FileName),"%s/%s/%s_D1_%.8u.%.3u_%c_%s.raw", fRawDataPath, RunDate, RunDate,RunNumber,FileNumber,toupper(daq_runtype_str[daq_runtype][0]),RHeader->Description); // Open file with rwx right for owner and group, never overwrite file Rawfile = open(FileName,O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if(Rawfile==-1) { PrintMessage("\rError: Could not open file \"%s\" (%s)\n", FileName, strerror(errno)); return false; } return true; } // Write run header and board structures (revision number is zero for svn modified working copy) bool DAQReadout::WriteRunHeader() { struct timeval Time; RHeader->MagicNum = MAGICNUM_OPEN; RHeader->DataFormat = DATA_FORMAT; RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1); RHeader->RunHeaderSize = sizeof(RunHeader); RHeader->EventHeaderSize = sizeof(EventHeader); RHeader->BoardStructureSize = sizeof(BoardStructure); RHeader->Identification = IDENTIFICATION; RHeader->Type = daq_runtype; RHeader->RunNumber = RunNumber; RHeader->FileNumber = FileNumber; gettimeofday(&Time, NULL); RHeader->StartSecond = Time.tv_sec; RHeader->StartMicrosecond = Time.tv_usec; RHeader->NBoards = NumBoards==0 && daq_runtype==test ? 1 : (LastBoard - FirstBoard) + 1; RHeader->NChips = NumBoards==0 ? kNumberOfChipsMax : GetBoard(0)->GetNumberOfChips(); RHeader->NChannels = NumBoards==0 ? kNumberOfChannelsMax : GetBoard(0)->GetNumberOfChannels(); RHeader->NBytes = sizeof(short); RHeader->Offset = fFirstSample; RHeader->Samples = fSamples; if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) { PrintMessage("Error: Could not write run header, terminating run (%s)\n", strerror(errno)); return false; } for (int i=FirstBoard; i<=LastBoard; i++) { BStruct[i].SerialNo = GetBoard(i)->GetBoardSerialNumber(); BStruct[i].BoardTemp = GetBoard(i)->GetTemperature(); BStruct[i].NomFreq = DRSFreq[i]; BStruct[i].ScaleFactor = GetBoard(i)->GetPrecision(); } // In case no boards are available, dummy data is written for one board structure if (NumBoards == 0) { BStruct[0].NomFreq = 1; BStruct[0].ScaleFactor = 0.1; } if(write(Rawfile, &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) != (ssize_t) sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) { PrintMessage("Error: Could not write (all) board structures, terminating run (%s)\n", strerror(errno)); return false; } return true; } // Update run header before closing file bool DAQReadout::UpdateRunHeader(unsigned int Events, bool Error) { struct timeval Time; RHeader->MagicNum = Error==false ? MAGICNUM_CLOSED:MAGICNUM_ERROR; RHeader->Events = Events; gettimeofday(&Time, NULL); RHeader->EndSecond = Time.tv_sec; RHeader->EndMicrosecond = Time.tv_usec; if(lseek(Rawfile,0,SEEK_SET)==-1) { PrintMessage("Error: Could not rewind file to write updated run header, terminating run (%s)\n", strerror(errno)); return false; } if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) { PrintMessage("Error: Could not write updated run header, terminating run (%s)\n", strerror(errno)); return false; } return true; } // Write event bool DAQReadout::WriteEvent() { // Event header struct timeval Time; gettimeofday(&Time, NULL); EHeader->EventNumber = NumEvents; EHeader->TriggerType = daq_runtype==data ? 0 : 1; EHeader->Second = Time.tv_sec; EHeader->Microsecond = Time.tv_usec; EHeader->EventSize = sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples + sizeof(int)*RHeader->NBoards*RHeader->NChips; if(write(Rawfile, EHeader, sizeof(EventHeader)) != sizeof(EventHeader)) { PrintMessage("Error: Could not write event header, terminating run (%s)\n", strerror(errno)); return false; } // Event data (It is required that at least three chunks can be written with writev(), therefore // IOV_MAX>=3 is checked at startup unsigned int Start, Count = 0; ssize_t WriteResult, Size = 0; struct iovec DataPart[IOV_MAX]; // First chunk: trigger cells DataPart[Count].iov_base = (char *) TriggerCell + FirstBoard*RHeader->NChips*sizeof(int); // TriggerCell is without cast a pointer to an 8-byte unit (two ints) ! DataPart[Count++].iov_len = RHeader->NBoards * RHeader->NChips * sizeof(int); Size += DataPart[Count-1].iov_len; // Remaining chunks: ADC data (two chucks per channel if wrap around of pipeline occurred) for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) { for (unsigned int k=0; kNChips; k++) { // Start bin if (GetBoard(i)->GetDRSType() == 4) Start = 0; else Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins; for (unsigned int l=0; lNChannels; l++) { DataPart[Count].iov_base = &WaveForm[i][k][l][Start]; DataPart[Count++].iov_len = (Start+fSamples=IOV_MAX-1 || (l==(RHeader->NChannels-1) && k==(RHeader->NChips-1) && i==(LastBoard+(NumBoards==0)))) { if ((WriteResult=writev(Rawfile, DataPart, Count)) != (int) Size) { if (WriteResult==-1) PrintMessage("Error: Could not write event data, terminating run (%s)\n", strerror(errno)); else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult,Count*DataPart[0].iov_len); return false; } Count = 0; Size = 0; } } // Channels } // Chips } // Boards return true; } // Print configuration to target void DAQReadout::PrintConfig(int Target) { PrintMessage(Target, "RawDataPath: %s\n" "DefaultFrequency: %.2f\tFirstSample: %d\tSamples: %u\n" "MinDiskSpaceMB: %u\tMaxFileSizeMB: %d\tCCPort: %d\n" "CalibDataPath: %s\n", fRawDataPath,fDefaultFrequency,fFirstSample,fSamples,fMinDiskSpaceMB, fMaxFileSizeMB,fCCPort,fCalibDataPath); } // Print usage text for command void DAQReadout::PrintUsage() { PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters); } // Print progress (used in DRS class) void DAQReadout::Progress(int Progress) { PrintMessage(MsgToConsole, "\rProgress: %d%% ", Progress); fflush(stdout); }; // Print message to selected target void DAQReadout::PrintMessage(int Target, const char *Format, ...) { va_list ArgumentPointer; va_start(ArgumentPointer, Format); DoPrintMessage(Format, ArgumentPointer, Target); va_end(ArgumentPointer); } // Print message to log file, and screen or socket (depending on command origin) void DAQReadout::PrintMessage(const char *Format, ...) { va_list ArgumentPointer; va_start(ArgumentPointer, Format); if(CmdFromSocket) DoPrintMessage(Format, ArgumentPointer, MsgToSocket); else DoPrintMessage(Format, ArgumentPointer, MsgToConsole); va_end(ArgumentPointer); } // Function doing the actual printing work // Note: Be careful when overloading variadic functions. va_list is // in gcc an int, which can be interpreted as char *... void DAQReadout::DoPrintMessage(const char *Format, va_list ArgumentPointer, int Target) { static char Textbuffer[MAX_COM_SIZE]; // static: it is only allocated once memset(Textbuffer, 0, sizeof(Textbuffer)); vsnprintf(Textbuffer, sizeof(Textbuffer), Format, ArgumentPointer); // Print to console if(Target & MsgToConsole) { if(strlen(Textbuffer)>0 && Textbuffer[strlen(Textbuffer)-1]=='\n') { printf("\r%s%s", Textbuffer, Prompt); // New prompt } else printf("%s", Textbuffer); fflush(stdout); } // Send to log if(Target & MsgToLog) { char *Buf; if (asprintf(&Buf, "%s %s", SERVER_NAME, Textbuffer) != -1) { DimClient::sendCommandNB("DColl/Log", Buf); free(Buf); } else DimClient::sendCommandNB("DColl/Log", SERVER_NAME" asprintf() failed"); } // Print to socket if((Target & MsgToSocket) && Socket!=-1) write(Socket, Textbuffer, strlen(Textbuffer)); } // --------------------------------------- // ***** Various utility functions ***** // --------------------------------------- // Check if two strings match (min 1 character must match) bool Match(const char *str, const char *cmd) { return strncasecmp(str,cmd,strlen(str)==0 ? 1:strlen(str)) ? false:true; } // Return current available storage space in given directory int CheckDisk(char *Directory) { struct statfs FileSystemStats; statfs(Directory, &FileSystemStats); return FileSystemStats.f_bavail / 1024 * (FileSystemStats.f_bsize / 1024); } // Parse command line for white space and double-quote separated tokens int ParseInput(char* Command, const char *Param[]) { int Count=0; while(CountNumEvents = 0; m->FileNumber = 0; m->HVFB->ClearAverages(); gettimeofday(&StartTime, NULL); m->PrintMessage("\rStarting run #%d (%s) with %u event(s)\n",m->RunNumber,daq_runtype_str[m->daq_runtype],m->NumEventsRequested); do { // Check if enough disk space is left if (CheckDisk(m->fRawDataPath) <= m->fMinDiskSpaceMB+m->fMaxFileSizeMB) { m->PrintMessage("\rError: Disk space after next file (max. %d MByte) below %d MByte\n",m->fMaxFileSizeMB,m->fMinDiskSpaceMB); break; } // Init run header, open raw file, write run header if (!m->OpenRawFile()) break; m->PrintMessage("\rData file \"%s\" opened.\n",m->FileName); EventsInFile = 0; FileSize = 0; WriteError |= !m->WriteRunHeader(); if (m->daq_runtype != test) m->StartDRS(); // Take data until finished, stopped or file too large while ((m->NumEventsNumEventsRequested || m->NumEventsRequested==0) && !m->Stop && FileSize/1024/1024fMaxFileSizeMB && !WriteError) { if (m->daq_runtype == data) while (m->IsDRSBusy()); // Wait for hardware trigger (if DAQ stopped, DRS will not be busy anymore) else if (m->daq_runtype == pedestal) m->StopDRS(); // ..or for software trigger // Read event data via VME or generate test data (for one board if no boards available) if (m->daq_runtype != test) { m->ReadCalibratedDRSData(); m->StartDRS(); // Restart here: writing data is in parallel to waiting for next trigger } else { double Period = ((double) rand())/RAND_MAX*20; for (long unsigned int i=0; iRHeader->NBoards*m->RHeader->NChips*m->RHeader->NChannels*m->RHeader->Samples; i++) *((short *) m->WaveForm+i) = (short) (sin(i/Period)*1000); } // Write event to disk and update file size EventsInFile++; m->NumEvents++; WriteError |= !m->WriteEvent(); if((FileSize = lseek(m->Rawfile, 0, SEEK_CUR)) == -1) { m->PrintMessage("Error: Could not determine file size, terminating run (%s)\n", strerror(errno)); WriteError = true; } // Call feedback to process event m->HVFB->ProcessEvent(); } // Write updated run header, close file RunSize += FileSize; WriteError |= !m->UpdateRunHeader(EventsInFile, WriteError); if(close(m->Rawfile)==-1) m->PrintMessage("Error: Could not close data file (%s)\n", strerror(errno)); else m->PrintMessage("Data file closed (size %lu MByte).\n", FileSize/1024/1024); m->FileNumber += 1; } while((m->NumEventsNumEventsRequested || m->NumEventsRequested==0) && !m->Stop && !WriteError); m->StopDRS(); // Print run summary to screen if(!WriteError) m->PrintMessage("\rRun #%d (%s) %s, %d events\n",m->RunNumber,daq_runtype_str[m->daq_runtype],(m->NumEvents == m->NumEventsRequested) ? "completed":"stopped",m->NumEvents); else m->PrintMessage("\rRun #%d (%s) aborted due to error after %d events\n",m->RunNumber,daq_runtype_str[m->daq_runtype],m->NumEvents); // Write run summary to slow data file //m->SlowDataClass->NewEntry("Runinfo"); //m->SlowDataClass->AddToEntry("%d %s %s %d %d %s", m->RunNumber, WriteError?"Error":"OK", daq_runtype_str[m->daq_runtype], m->NumEvents, m->FileNumber, m->RHeader->Description); // Print run statistics if (m->NumEvents>0 && !WriteError) { gettimeofday(&StopTime, NULL); float RunTime = StopTime.tv_sec-StartTime.tv_sec + (StopTime.tv_usec-StartTime.tv_usec)*1e-6; m->PrintMessage("Time for run %.2f seconds, trigger rate %.2f Hz.\n", RunTime, m->NumEvents/RunTime); m->PrintMessage("Run size %llu MByte, data rate %.1f MByte/s.\n", RunSize/1024/1024, RunSize/1024.0/1024/RunTime); } m->daq_state = stopped; } /********************************************************************\ DAQ Thread - no disk writing, only hardware trigger, for feedback tests \********************************************************************/ void DAQ_Silent(DAQReadout *m) { m->PrintMessage("\rData taking started\n"); do { // Start DRS and wait for hardware trigger m->StartDRS(); while (m->IsDRSBusy()); // Read event data via VME and call feedback m->ReadCalibratedDRSData(); m->HVFB->ProcessEvent(); } while(!m->Stop); m->PrintMessage("\rData taking stopped\n"); m->daq_state = stopped; }