/********************************************************************\ 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_take, false, "", "Start run without disk writing"}, {"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"}, {"update", &DAQReadout::cmd_update, false, "", "Minimum delay between updates to DIM event service"}, {"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"}}; // Global pointer for thread entry routines class DAQReadout *ThisClass; // ----------------------------------------------- // ***** Constructor: Class initialisation ***** // ----------------------------------------------- // DAQReadout::DAQReadout(): EvidenceServer(SERVER_NAME) { // Initialization ThisClass = this; MainThread = pthread_self(); ConsoleText = NULL; time(&StartTime); // DIM console service used in PrintMessage() ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) ""); // Initialize status structure daq_state = stopped; daq_runtype = data; NumEvents = 0; NumEventsRequested = 0; NumBoards = 0; FirstBoard = 0; LastBoard = -1; MinDelay = 1; // Get configuration data (static needed for c_str() pointers to remain valid after constructor finished) static std::string _RawDataPath = GetConfig("RawDataPath"); static std::string _CalibDataPath = GetConfig("CalibDataPath"); fRawDataPath = _RawDataPath.c_str(); fCalibDataPath = _CalibDataPath.c_str(); fFirstSample = atoi(GetConfig("FirstSample").c_str()); fSamples = atoi(GetConfig("Samples").c_str()); fMinDiskSpaceMB = atoi(GetConfig("MinDiskSpaceMB").c_str()); fMaxFileSizeMB = atoi(GetConfig("MaxFileSizeMB").c_str()); fDefaultFrequency = atof(GetConfig("DefaultFrequency").c_str()); 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\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 DIMEventData = new char [sizeof(RunHeader) + sizeof(EventHeader) + sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards) + sizeof(int)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax + sizeof(short)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfBins]; // Create DIM event data service EventService = new DimService (SERVER_NAME"/EventData", (char *) "C", DIMEventData, 0); // Create instance of HV feedback (must be called after board detection) HVFB = new HVFeedback(this); // Install DIM command (after all initialized) Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this); } // ------------------------ // ***** Destructor ***** // ------------------------ DAQReadout::~DAQReadout() { delete Command; delete EventService; delete[] DIMEventData; delete RHeader; delete EHeader; delete HVFB; delete[] ACalibTemp; delete[] ACalib; delete[] TCalib; delete[] DRSFreq; delete[] BStruct; delete[] WaveForm; delete[] TriggerCell; delete ConsoleOut; free(ConsoleText); } // -------------------------------- // ***** Command evaluation ***** // -------------------------------- void DAQReadout::Execute(char *Command) { if (Command[0]=='.') { // Shell command system(&(Command[1])); return; } for (int i=0; i*CommandList[CmdNumber].CommandPointer)(); } } // Get uptime void DAQReadout::cmd_uptime() { time_t ActualT; time (&ActualT); PrintMessage("%02d:%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(); } // 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 if 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"); // Request new run number (if command was 'start', set run number to -1 --> no disk writing) if (Match(Param[0], "take")) { DimRpcInfo RunNumRPC((char *) "NextRunNumber", -1); RunNumRPC.setData((char *) "");printf("and least here\n"); RunNumber = RunNumRPC.getInt(); if(RunNumber < 1) { PrintMessage("Error: No connection to run number dispatcher or received number smaller than 1\n"); return; } } else RunNumber = -1; // Create DAQ thread pthread_t Thread; int Code; if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::DAQ,(void *) this)) != 0) { PrintMessage("pthread_create() failed with DAQ thread (error code %d)\n", Code); } else { daq_state = active; Stop = false; if ((Code = pthread_detach(Thread)) != 0) { PrintMessage("pthread_detach() failed with DAQ thread (error code %d)\n", Code); } } } // 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("==START== %d %.2f %.2f ",kNumberOfBins+2,DRSFreq[Board],GetBoard(Board)->GetPrecision()); 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" " 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), 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 - SIGTERM sets ExitRequest flag // If command comes from DimCommand thread, SIGTERM also makes readline() return void DAQReadout::cmd_exit() { if (daq_state == active) PrintMessage("Issue 'stop' first to stop daq\n"); else pthread_kill(MainThread, SIGTERM); } // Set/get mode of feedback void DAQReadout::cmd_fmode() { if(Match(Param[1],"off")) HVFB->SetFBMode(FB_Off); else if(Match(Param[1],"active")) HVFB->SetFBMode(FB_Active); else if(Match(Param[1],"targets")) HVFB->SetFBMode(FB_Targets); else 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(); } // ---------------------------------------------- // ***** 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 (if RunNumber == -1, data will be written to /dev/null) 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 if (RunNumber != -1) { 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 if (RunNumber == -1) snprintf(FileName, sizeof(FileName), "/dev/null"); else 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|(RunNumber==-1?0: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; } unsigned int Start, Count = 0; ssize_t WriteResult, Size = 0; struct iovec DataPart[IOV_MAX]; // Write trigger cells for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) { for (unsigned int k=0; kNChips; k++) { if ((WriteResult=write(Rawfile, &TriggerCell[i][k], sizeof(int))) != sizeof(int)) { if (WriteResult == -1) PrintMessage("Error: Could not write trigger cells, terminating run (%s)\n", strerror(errno)); else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult, sizeof(int)); return false; } } } // ADC data (two chucks per channel if wrap around of pipeline occurred, therefore // IOV_MAX>=2 is checked at startup 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() { PrintMessage("RawDataPath: %s\n" "DefaultFrequency: %.2f\tFirstSample: %d\tSamples: %u\n" "MinDiskSpaceMB: %u\tMaxFileSizeMB: %d\n" "CalibDataPath: %s\n", fRawDataPath,fDefaultFrequency,fFirstSample,fSamples,fMinDiskSpaceMB, fMaxFileSizeMB,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("\rProgress: %d%% ", Progress); fflush(stdout); }; // Print message to console only void DAQReadout::PrintMessage(const char *Format, ...) { static char Error[] = "vasprintf() failed in PrintMessage()"; char *Text; // Evaluate arguments va_list ArgumentPointer; va_start(ArgumentPointer, Format); if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error; va_end(ArgumentPointer); // Print to console if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt); // New prompt else printf("%s", Text); fflush(stdout); // Send to DIM text service ConsoleOut->updateService(Text); // Free old text if (ConsoleText != Error) free(ConsoleText); ConsoleText = Text; } // DIM command handler (must be non-blocking, otherwise a DIM rpc would dead-lock) void DAQReadout::commandHandler() { // Ignore empty or illegal strings if (getCommand()->getSize() == 0 || *((char *) getCommand()->getData()+getCommand()->getSize()-1) != '\0' || strlen(getCommand()->getString()) == 0) return; // Copy command to new buffer (will be freed by global Execute() function) char *Command; if (asprintf(&Command, "%s", getCommand()->getString()) == -1) { PrintMessage("asprintf() failed in DRSReadout::commandHandler() (%s)\n", strerror(errno)); return; } // Create detached command handling thread pthread_t Thread; int Code; if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::Execute,(void *) Command)) != 0) { PrintMessage("pthread_create() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code)); } else if ((Code = pthread_detach(Thread)) != 0) { PrintMessage("pthread_detach() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code)); } } /********************************************************************\ DAQ Thread This thread takes data until the requested number of events is reached, until no more disk space is available or until data taking is stopped. No mutex mechanism is used since variables will never be written simultaneoously by this and the main thread. \********************************************************************/ void DAQReadout::DAQ() { struct timeval StartTime, StopTime; unsigned int EventsInFile; unsigned long long RunSize = 0; bool WriteError = false; time_t LastDIMUpdate = 0; off_t FileSize; int DIMSize; NumEvents = 0; FileNumber = 0; HVFB->ClearAverages(); gettimeofday(&StartTime, NULL); PrintMessage("\rStarting run #%d (%s) with %u event(s)\n", RunNumber, daq_runtype_str[daq_runtype], NumEventsRequested); do { // Check if enough disk space is left if (CheckDisk(fRawDataPath) <= fMinDiskSpaceMB+fMaxFileSizeMB) { PrintMessage("\rError: Disk space after next file (max. %d MByte) below %d MByte\n", fMaxFileSizeMB, fMinDiskSpaceMB); break; } // Init run header, open raw file, write run header if (!OpenRawFile()) break; PrintMessage("\rData file \"%s\" opened.\n", FileName); EventsInFile = 0; FileSize = 0; WriteError |= !WriteRunHeader(); StartDRS(); // Copy run header and board structures to buffer for DIM event service memcpy(DIMEventData, RHeader, sizeof(RunHeader)); ((RunHeader *) DIMEventData)->Events = 1; // always contains 1 event memcpy(DIMEventData+sizeof(RunHeader), &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))); // Service size for DIM DIMSize = sizeof(RunHeader) + sizeof(EventHeader) + sizeof(BoardStructure)*RHeader->NBoards + sizeof(int)*RHeader->NBoards*RHeader->NChips + sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples; // Take data until finished, stopped or file too large while ((NumEventsProcessEvent(); // Call routine to update DIM service (update rate is limited) if (time(NULL) - LastDIMUpdate < MinDelay) continue; LastDIMUpdate = time(NULL); // Copy new event header char *Pnt = DIMEventData+sizeof(RunHeader)+RHeader->NBoards*sizeof(BoardStructure); memcpy(Pnt, EHeader, sizeof(EventHeader)); Pnt += sizeof(EventHeader); // Copy new trigger cells for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) { for (unsigned int k=0; kNChips; k++) { *((int *) Pnt) = TriggerCell[i][k]; Pnt += sizeof(int); } } // Copy event data unsigned int Start; 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++) { memcpy(Pnt, &WaveForm[i][k][l][Start], (Start+fSamplesupdateService((void *) DIMEventData, DIMSize); } // Write updated run header, close file RunSize += FileSize; WriteError |= !UpdateRunHeader(EventsInFile, WriteError); if(close(Rawfile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno)); else PrintMessage("Data file closed (size %lu MByte).\n", FileSize/1024/1024); FileNumber += 1; } while((NumEvents0 && !WriteError) { gettimeofday(&StopTime, NULL); float RunTime = StopTime.tv_sec-StartTime.tv_sec + (StopTime.tv_usec-StartTime.tv_usec)*1e-6; PrintMessage("Time for run %.2f seconds, trigger rate %.2f Hz.\n", RunTime, NumEvents/RunTime); PrintMessage("Run size %llu MByte, data rate %.1f MByte/s.\n", RunSize/1024/1024, RunSize/1024.0/1024/RunTime); } daq_state = stopped; } // --------------------------------------- // ***** 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(const 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(CountDAQ(); } // Stub to call Execute() metho of class and free command memory void Execute(char *Command) { ThisClass->Lock(); ThisClass->Execute(Command); free(Command); ThisClass->Unlock(); }