source: fact/drsdaq/DAQReadout.cc@ 10077

Last change on this file since 10077 was 274, checked in by ogrimm, 14 years ago
drsdaq publishes some more run-related services
File size: 48.8 KB
Line 
1/********************************************************************\
2
3 DAQReadout.cc
4
5 Main DAQ routines.
6
7 Sebastian Commichau, Oliver Grimm
8
9\********************************************************************/
10
11#include "DAQReadout.h"
12
13static const char* daq_state_str[] = {"active", "stopped"};
14static const char* daq_runtype_str[] = {"data", "pedestal", "reserved", "test"};
15
16static const struct CL_Struct { const char *Name;
17 void (DAQReadout::*CommandPointer)();
18 bool NeedNotBusy;
19 const char *Parameters;
20 const char *Help;
21 } CommandList[] =
22 {{"board", &DAQReadout::cmd_board, true, "<i> [j] | <all>" ,"Address board i, boards i-j, all boards"},
23 {"status", &DAQReadout::cmd_status, false, "[daq|drs]", "Show DAQ/DRS status information"},
24 {"frequency", &DAQReadout::cmd_freq, true, "<GHz> [reg]", "Set DRS sampling frequency (regulated)"},
25 {"acalib", &DAQReadout::cmd_acalib, true, "<trig rate> ", "Amplitude calibration"},
26 {"tcalib", &DAQReadout::cmd_tcalib, true, "<trig rate> ", "Amplitude calibration"},
27 {"trigger", &DAQReadout::cmd_trigger, true, "<on|off>", "Hardware trigger on or off"},
28 {"wmode", &DAQReadout::cmd_wmode, true, "<run|stop>", "Domino wave running or stopped during read out"},
29 {"rmode", &DAQReadout::cmd_rmode, true, "<first|stop>", "Readout start at first bin or stop position (DRS4)"},
30 {"dmode", &DAQReadout::cmd_dmode, true, "<single|continuous>", "Domino wave single shot or continuous"},
31 {"centre", &DAQReadout::cmd_centre, true, "<V>", "Set centre of input range (DRS4)"},
32 {"refclk", &DAQReadout::cmd_refclk, true, "<FPGA|extern>", "Set reference clock to FPGA or external (DRS4)"},
33 {"read", &DAQReadout::cmd_read, false, "<brd chip chan> [res]", "Read current data (and restart if DAQ not active)"},
34 {"take", &DAQReadout::cmd_take, false, "<d|p|t> [n] [source]", "Start run (data, pedestal or test) with n events"},
35 {"events", &DAQReadout::cmd_events, false, "", "Number of events in current run"},
36 {"start", &DAQReadout::cmd_take, false, "", "Start run without disk writing"},
37 {"stop", &DAQReadout::cmd_stop, false, "", "Issue soft trigger and stop DAQ"},
38 {"regtest", &DAQReadout::cmd_regtest, true, "", "DRS register test"},
39 {"ramtest", &DAQReadout::cmd_ramtest, true, "", "DRS RAM integrity and speed test"},
40 {"chiptest", &DAQReadout::cmd_chiptest, true, "", "Chip test"},
41 {"eepromtest", &DAQReadout::cmd_eepromtest, true, "", "EEPROM test"},
42 {"led", &DAQReadout::cmd_led, true, "<on|off>", "Turn LED on or off"},
43 {"config", &DAQReadout::cmd_config, false, "", "Print drsdaq configuration"},
44 {"serial", &DAQReadout::cmd_serial, true, "<i> <n>", "Set serial# of board <i> to <n> (experts only)"},
45 {"disk", &DAQReadout::cmd_disk, false, "" ,"Remaining disk space"},
46 {"uptime", &DAQReadout::cmd_uptime, false, "", "Get DAQ uptime"},
47 {"update", &DAQReadout::cmd_update, false, "<sec>", "Minimum delay between updates to DIM event service"},
48 {"exit", &DAQReadout::cmd_exit, false, "", "Exit program"},
49 {"help", &DAQReadout::cmd_help, false, "", "Print help"}};
50
51
52// Global pointer for thread entry routines
53class DAQReadout *ThisClass;
54
55// -----------------------------------------------
56// ***** Constructor: Class initialisation *****
57// -----------------------------------------------
58//
59
60DAQReadout::DAQReadout(): EvidenceServer(SERVER_NAME) {
61
62 // Initialization
63 ThisClass = this;
64 MainThread = pthread_self();
65 ConsoleText = NULL;
66 time(&StartTime);
67
68 // DIM console service used in PrintMessage()
69 ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
70
71 // Initialize status structure
72 daq_state = stopped;
73 daq_runtype = data;
74 NumEvents = 0;
75 NumEventsRequested = 0;
76 NumBoards = 0;
77 FirstBoard = 0;
78 LastBoard = -1;
79 MinDelay = 1;
80 RunNumber = -1;
81
82 // Get configuration data (static needed for c_str() pointers to remain valid after constructor finished)
83 static std::string _RawDataPath = GetConfig("RawDataPath");
84 static std::string _CalibDataPath = GetConfig("CalibDataPath");
85 fRawDataPath = _RawDataPath.c_str();
86 fCalibDataPath = _CalibDataPath.c_str();
87 fFirstSample = atoi(GetConfig("FirstSample").c_str());
88 fSamples = atoi(GetConfig("Samples").c_str());
89 fMinDiskSpaceMB = atoi(GetConfig("MinDiskSpaceMB").c_str());
90 fMaxFileSizeMB = atoi(GetConfig("MaxFileSizeMB").c_str());
91 fDefaultFrequency = atof(GetConfig("DefaultFrequency").c_str());
92
93 fLedTrigBoard = atoi(GetConfig("TrigBoard").c_str());
94 fLedTrigChannel = atoi(GetConfig("TrigChannel").c_str());
95 fLedTrigChip = atoi(GetConfig("TrigChip").c_str());
96 fLedTrigSample = atoi(GetConfig("TrigSample").c_str());
97 fLedTrigThreshold = atoi(GetConfig("TrigThreshold").c_str());
98 fLedSignalSample = atoi(GetConfig("SignalSample").c_str());
99 fLedBaselineSample = atoi(GetConfig("BaselineSample").c_str());
100 fIntHalfWidth = atoi(GetConfig("IntHalfWidth").c_str());
101
102 if (fFirstSample < 0 || fFirstSample > kNumberOfBins || fSamples > kNumberOfBins) {
103 PrintMessage("Warning: Sample range in configuration beyond limits, setting to full range\n");
104 fFirstSample = kNumberOfBins;
105 fSamples = kNumberOfBins;
106 }
107 snprintf(CalibInfoFilename,sizeof(CalibInfoFilename), "%s/CalibInfo", fCalibDataPath);
108
109 // Allocate headers and initialise to zero
110 RHeader = new RunHeader;
111 memset(RHeader, 0, sizeof(RunHeader));
112 EHeader = new EventHeader;
113 memset(EHeader, 0, sizeof(EventHeader));
114
115 // Scan for DRS boards
116 NumBoards = GetNumberOfBoards();
117 DRSFreq = new float [NumBoards];
118 ACalib = new bool [NumBoards];
119 ACalibTemp = new double [NumBoards];
120 TCalib = new bool [NumBoards];
121
122 if (NumBoards == 0) PrintMessage("No DRS boards found\n");
123
124 for (int i=0; i<NumBoards; i++) {
125 LastBoard++;
126 GetBoard(i)->Init();
127 DRSFreq[i] = 0;
128 ACalib[i] = false;
129 ACalibTemp[i] = -999;
130 TCalib[i] = false;
131 }
132 BStruct = new BoardStructure [NumBoards == 0 ? 1:NumBoards];
133 memset(BStruct, 0, sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards));
134
135 WaveForm = new short [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
136 TriggerCell = new int [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax] (); // Zero initialised
137
138 DIMEventData = new char [sizeof(RunHeader) + sizeof(EventHeader) +
139 sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards) +
140 sizeof(int)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax +
141 sizeof(short)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfBins];
142
143 // Create DIM event data service
144 EventService = new DimService (SERVER_NAME"/EventData", (char *) "C", DIMEventData, 0);
145 RunNumService = new DimService (SERVER_NAME"/RunNumber", RunNumber);
146
147 // Install DIM command (after all initialized)
148 Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
149}
150
151// ------------------------
152// ***** Destructor *****
153// ------------------------
154
155DAQReadout::~DAQReadout() {
156
157 delete Command;
158
159 delete RunNumService;
160 delete EventService; delete[] DIMEventData;
161 delete RHeader; delete EHeader;
162 delete[] ACalibTemp;
163 delete[] ACalib; delete[] TCalib;
164 delete[] DRSFreq; delete[] BStruct;
165 delete[] WaveForm; delete[] TriggerCell;
166
167 delete ConsoleOut;
168 free(ConsoleText);
169}
170
171// --------------------------------
172// ***** Command evaluation *****
173// --------------------------------
174
175void DAQReadout::Execute(char *Command) {
176
177 if (Command[0]=='.') { // Shell command
178 system(&(Command[1]));
179 return;
180 }
181
182 for (int i=0; i<MAX_NUM_TOKEN; i++) Param[i] = ""; // All pointers point initially to empty string
183 NParam = ParseInput(Command, Param);
184
185 // Search for command
186 unsigned int Count;
187 for (Count=0; Count<sizeof(CommandList)/sizeof(CL_Struct); Count++) {
188 if (Match(Param[0], CommandList[Count].Name)) break;
189 }
190
191 // Command not found?
192 if (Count == sizeof(CommandList)/sizeof(CL_Struct)) {
193 PrintMessage("Unknown command '%s'\n", Param[0]);
194 return;
195 }
196
197 if(CommandList[Count].NeedNotBusy && daq_state==active) PrintMessage("DAQ is busy\n");
198 else if(CommandList[Count].NeedNotBusy && NumBoards==0) PrintMessage("No boards available\n");
199 else {
200 CmdNumber = Count;
201 (this->*CommandList[CmdNumber].CommandPointer)();
202 }
203}
204
205// Get uptime
206void DAQReadout::cmd_uptime() {
207 time_t ActualT;
208 time (&ActualT);
209 PrintMessage("%02d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
210}
211
212// Print disk space
213void DAQReadout::cmd_disk() {
214 PrintMessage("Free disk space (%s) [MB]: %lu\n", fRawDataPath, CheckDisk(fRawDataPath));
215}
216
217// Print current number of events
218void DAQReadout::cmd_events() {
219 if(daq_state != active) PrintMessage("DAQ not active\n");
220 else PrintMessage("Current number of events: %d (of %d requested)\n", NumEvents, NumEventsRequested);
221}
222
223// Print DAQ configuration
224void DAQReadout::cmd_config() {
225
226 PrintMessage("RawDataPath: %s\n"
227 "DefaultFrequency: %.2f\tFirstSample: %d\tSamples: %u\n"
228 "MinDiskSpaceMB: %u\tMaxFileSizeMB: %d\n"
229 "CalibDataPath: %s\n\n"
230 "LedTrigBoard: %d\t\tLedTrigChip: %d\t\tLedTrigChannel: %d\n"
231 "LedTrigSample: %d\tLedTrigThreshold: %.2f\n"
232 "LedSignalSample: %d\tLedBaselineSample: %d\tIntHalfWidth:%u\n",
233 fRawDataPath,fDefaultFrequency,fFirstSample,fSamples,fMinDiskSpaceMB,
234 fMaxFileSizeMB,fCalibDataPath,
235 fLedTrigBoard, fLedTrigChip, fLedTrigChannel, fLedTrigSample,
236 fLedTrigThreshold, fLedSignalSample, fLedBaselineSample, fIntHalfWidth);
237}
238
239// Start DAQ
240void DAQReadout::cmd_take() {
241
242 // Check conditions if this is not a test run
243 if(!Match(Param[1],"test")) {
244 if (daq_state==active || NumBoards==0) {
245 PrintMessage("DAQ is busy or no boards available\n");
246 return;
247 }
248 // Check if all boards have the same number of DRS chips and channels
249 // (restriction of current data format)
250 for (int i=FirstBoard; i<=LastBoard-1; i++) {
251 if ((GetBoard(i)->GetNumberOfChannels() != GetBoard(i+1)->GetNumberOfChannels()) || (GetBoard(i)->GetNumberOfChips() != GetBoard(i+1)->GetNumberOfChips())) {
252 PrintMessage("Cannot take data if not all boards have the same number of DRS chips and channels due to data format restriction\n");
253 return;
254 }
255 }
256 if (!IsDRSFreqSet()) {
257 PrintMessage("Setting default frequency\n");
258 SetDRSFrequency(fDefaultFrequency, true);
259 }
260 if (!ReadCalibration()) {
261 PrintMessage("Cannot start run if response calibration not read\n");
262 //return;
263 }
264 // Readout from domino wave stop position (ignored if not DRS4)
265 SetDOMINOReadMode(1);
266 }
267
268 if (Match(Param[1],"data")) {
269 HWTrigger(1);
270 daq_runtype = data;
271 }
272 else if (Match(Param[1],"pedestal")) {
273 HWTrigger(0);
274 daq_runtype = pedestal;
275 }
276 else if (Match(Param[1],"test")) {
277 daq_runtype = test;
278 }
279 else {
280 PrintUsage();
281 return;
282 }
283
284 if (NParam==3) NumEventsRequested = atoi(Param[2]);
285 if (NParam==4) {
286 NumEventsRequested = atoi(Param[2]);
287 strncpy(RHeader->Description, Param[3], sizeof(RHeader->Description));
288 }
289 else snprintf(RHeader->Description,sizeof(RHeader->Description),"DUMMY");
290
291 // Request new run number (if command was 'start', set run number to -1 --> no disk writing)
292 if (Match(Param[0], "take")) {
293 DimRpcInfo RunNumRPC((char *) "NextRunNumber", -1);
294
295 RunNumRPC.setData((char *) "");printf("and least here\n");
296
297 RunNumber = RunNumRPC.getInt();
298 if(RunNumber < 1) {
299 PrintMessage("Error: No connection to run number dispatcher or received number smaller than 1\n");
300 return;
301 }
302 }
303 else RunNumber = -1;
304 RunNumService->updateService();
305
306 // Create DAQ thread
307 pthread_t Thread;
308 int Code;
309 if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::DAQ,(void *) this)) != 0) {
310 PrintMessage("pthread_create() failed with DAQ thread (error code %d)\n", Code);
311 }
312 else {
313 daq_state = active;
314 Stop = false;
315 if ((Code = pthread_detach(Thread)) != 0) {
316 PrintMessage("pthread_detach() failed with DAQ thread (error code %d)\n", Code);
317 }
318 }
319}
320
321// EEPROM test
322void DAQReadout::cmd_eepromtest() {
323
324 unsigned short buf[16384],rbuf[16384];
325 int n = 0;
326
327 for (int i=FirstBoard; i<=LastBoard; i++) {
328 PrintMessage("EEPROM test of board #%d:\n\r",i);
329 for (int j=0; j<16384; j++) buf[j] = rand();
330 GetBoard(i)->WriteEEPROM(1, buf, sizeof(buf));
331 memset(rbuf, 0, sizeof(rbuf));
332 GetBoard(i)->Write(T_RAM, 0, rbuf, sizeof(rbuf));
333 GetBoard(i)->ReadEEPROM(1, rbuf, sizeof(rbuf));
334
335 for (int j=0; j<16384; j++) if (buf[j] != rbuf[j]) n++;
336 printf("32 kByte written, %d errors\n", n);
337 }
338}
339
340// RAM test
341void DAQReadout::cmd_ramtest() {
342 for (int i=FirstBoard; i<=LastBoard; i++) {
343 PrintMessage("RAM integrity and speed test of board #%d:\n",i);
344 GetBoard(i)->RAMTest(3);
345 }
346}
347
348// Register test
349void DAQReadout::cmd_regtest() {
350 for (int i=FirstBoard; i<=LastBoard; i++) {
351 PrintMessage("Register test of board #%d:\n\r",i);
352 GetBoard(i)->RegisterTest();
353 }
354}
355
356// Chip test
357void DAQReadout::cmd_chiptest() {
358 for (int i=FirstBoard; i<=LastBoard; i++) {
359 if (GetBoard(i)->ChipTest()) PrintMessage("Chip test of board %i successful\n", i);
360 else PrintMessage("Chip test of board %i failed\n", i);
361 }
362}
363
364// Stop DAQ
365void DAQReadout::cmd_stop() {
366 if(!daq_state==active && !IsDRSBusy()) PrintMessage("Nothing to stop\n");
367 if (daq_state==active) StopRun();
368 if (IsDRSBusy()) {
369 StopDRS();
370 PrintMessage("Domino wave stopped\n");
371 }
372}
373
374// Read current data
375// For socket transmission: all numbers must be separated by exactly one
376// whitespace; the first number is the number of numbers that follow, the
377// second number the sampling frequency in GHz, the third the conversion factor
378
379void DAQReadout::cmd_read() {
380 if(NumBoards==0) {
381 PrintMessage("No mezzanine boards available\n");
382 return;
383 }
384 if (NParam<4) {
385 PrintUsage();
386 return;
387 }
388 int Board = atoi(Param[1]), Chip = atoi(Param[2]), Channel = atoi(Param[3]);
389
390 if (Board>LastBoard || Board<FirstBoard) {
391 PrintMessage("Error: Board number out of range\n");
392 return;
393 }
394 if (Channel<0 || Channel>=GetBoard(Board)->GetNumberOfChannels()) {
395 PrintMessage("Error: Channel number out of range\n");
396 return;
397 }
398 if (Chip<0 || Chip>=GetBoard(Board)->GetNumberOfChips()) {
399 PrintMessage("Error: Chip number out of range\n");
400 return;
401 }
402
403 if (daq_state != active) {
404 ReadCalibration();
405 if(NParam==5) StopDRS();
406 ReadCalibratedDRSData();
407 if(NParam==5) StartDRS();
408 }
409
410 PrintMessage("==START== %d %.2f %.2f ",kNumberOfBins+2,DRSFreq[Board],GetBoard(Board)->GetPrecision());
411 for (int k=0; k<kNumberOfBins; k++) {
412 PrintMessage("%.1f ", (float) WaveForm[Board][Chip][Channel][k]);
413 }
414 PrintMessage("==END==");
415 PrintMessage("\n");
416 PrintMessage("Trigger cell: %d\n", TriggerCell[Board][Chip]);
417}
418
419// Set Domino mode
420void DAQReadout::cmd_dmode() {
421 if (Match(Param[1],"continuous")) SetDOMINOMode(1);
422 else if (Match(Param[1],"single")) SetDOMINOMode(0);
423 else PrintUsage();
424}
425
426// Set Domino readout mode
427void DAQReadout::cmd_rmode() {
428 if (Match(Param[1],"first")) SetDOMINOReadMode(0);
429 else if (Match(Param[1],"stop")) SetDOMINOReadMode(1);
430 else PrintUsage();
431}
432
433// Set Domino wave mode
434void DAQReadout::cmd_wmode() {
435 if (Match(Param[1],"run")) SetDOMINOWaveMode(1);
436 else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0);
437 else PrintUsage();
438}
439
440// Set input range
441void DAQReadout::cmd_centre() {
442
443 if (NParam!=2) {
444 PrintUsage();
445 return;
446 }
447
448 for (int i=FirstBoard; i<=LastBoard; i++) {
449 if (GetBoard(i)->SetInputRange(atof(Param[1])) == 1) {
450 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]));
451 }
452 else PrintMessage("Centre voltage of board %d out of range\n", i);
453 }
454}
455
456// Set refclock source
457void DAQReadout::cmd_refclk() {
458
459 if (NParam!=2) {
460 PrintUsage();
461 return;
462 }
463
464 for (int i=FirstBoard; i<=LastBoard; i++) {
465 if (Match(Param[1], "extern")) GetBoard(i)->SetRefclk(1);
466 else GetBoard(i)->SetRefclk(0);
467 }
468}
469
470// Set trigger mode
471void DAQReadout::cmd_trigger() {
472 if (Match(Param[1],"on")) HWTrigger(1);
473 else if (Match(Param[1],"off")) HWTrigger(0);
474 else PrintUsage();
475}
476
477// Set serial number of board
478void DAQReadout::cmd_serial() {
479 if (NParam==4 && Match(Param[3], "expert")) {
480 if ((atoi(Param[1]) < FirstBoard) || (atoi(Param[1]) > LastBoard))
481 PrintMessage("Board number out of range (%d...%d)!\n",FirstBoard,LastBoard);
482 else if (atoi(Param[2]) < 100 || atoi(Param[2]) >= 1000)
483 PrintMessage("Serial number out of range (100...999)!\n");
484 else {
485 PrintMessage("Flashing EEPROM of board %d...\n", atoi(Param[1]));
486 (GetBoard(atoi(Param[1])))->SetBoardSerialNumber(atoi(Param[2]));
487 }
488 }
489 else PrintMessage("You are not allowed to change the serial number!\n");
490}
491
492// Do amplitude calibration
493void DAQReadout::cmd_acalib() {
494
495 if (!IsDRSFreqSet()) {
496 PrintMessage("Set sampling frequency for all boards first\n");
497 return;
498 }
499
500 for (int i=FirstBoard; i<=LastBoard; i++) {
501 if (GetBoard(i)->GetDRSType()==2 && (NParam!=2 || !atof(Param[1]))) {
502 PrintUsage();
503 return;
504 }
505
506 PrintMessage("Creating amplitude calibration of board %d (serial #%04d)\n Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber());
507
508 GetBoard(i)->EnableTcal(0);
509 GetBoard(i)->SelectClockSource(0);
510 if (GetBoard(i)->GetDRSType() == 4) {
511 GetBoard(i)->SetFrequency(DRSFreq[i], true);
512 GetBoard(i)->CalibrateVolt(this);
513 } else {
514 GetBoard(i)->Init();
515 GetBoard(i)->SetFrequency(DRSFreq[i], true);
516 GetBoard(i)->SoftTrigger();
517
518 if (GetBoard(i)->GetDRSType() == 3) {
519 GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,11,0,20,0,0,0,0,0);
520 }
521 else GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,36,110,20,19,40,15,atof(Param[1]),0);
522
523 GetBoard(i)->SetCalibrationDirectory(fCalibDataPath);
524 for (int j=0; j<2; j++) {
525 GetBoard(i)->GetResponseCalibration()->ResetCalibration();
526 while (!GetBoard(i)->GetResponseCalibration()->RecordCalibrationPoints(j)) {}
527 PrintMessage("Calibration points recorded.\n");
528 while (!GetBoard(i)->GetResponseCalibration()->FitCalibrationPoints(j)) {}
529 PrintMessage("Calibration points fitted.\n");
530 while (!GetBoard(i)->GetResponseCalibration()->OffsetCalibration(j)) {}
531 PrintMessage("Offset calibration done.\n");
532 if (!GetBoard(i)->GetResponseCalibration()->WriteCalibration(j)) break;
533 }
534 } // else for DRS2/3
535 ACalib[i] = true;
536 ACalibTemp[i] = GetBoard(i)->GetTemperature();
537
538 } // Loop over boards
539 PrintMessage("Amplitude calibration finished\n");
540
541 // Write short calibration information
542 time_t Time = time(NULL);
543 FILE *InfoFile = fopen(CalibInfoFilename, "w");
544 if (InfoFile != NULL) {
545 fprintf(InfoFile, "# Calibration information as of %s\n", ctime(&Time));
546 for (int i=0; i<GetNumberOfBoards(); i++) {
547 fprintf(InfoFile, "%d %d %.1f %d %.2f\n", GetBoard(i)->GetBoardSerialNumber(), ACalib[i], ACalibTemp[i], TCalib[i], DRSFreq[i]);
548 }
549 fclose(InfoFile);
550 }
551 else PrintMessage("Could not write calibration information to file '%s'\n", CalibInfoFilename);
552
553}
554
555// Do time calibration
556void DAQReadout::cmd_tcalib() {
557
558 if (!IsDRSFreqSet()) {
559 PrintMessage("Set sampling frequency for all boards first\n");
560 return;
561 }
562 if (!ReadCalibration()) {
563 PrintMessage("Amplitude calibration has to be done first\n");
564 return;
565 }
566
567 for (int i=FirstBoard; i<=LastBoard; i++) {
568 if (GetBoard(i)->GetDRSType() != 4 || GetBoard(i)->GetFirmwareVersion() < 13279) {
569 PrintMessage("Time calibration needs DRS4 and minimum firmware version 13279, skipping board %d\n", i);
570 continue;
571 }
572 PrintMessage("Creating time calibration of board %d (serial #%04d)\n Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber());
573
574 GetBoard(i)->SetFrequency(DRSFreq[i], true);
575 if (GetBoard(i)->CalibrateTiming(this) != 1) {
576 PrintMessage("Time calibration method returned error status, stopping calibration\n");
577 return;
578 }
579
580 TCalib[i] = true;
581
582 // Write calibration data to file
583 float Time[kNumberOfChipsMax][kNumberOfBins];
584 char *Filename;
585 FILE *Calibfile;
586 bool WriteOK = true;
587
588 // Copy calibration data into array
589 for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
590 GetBoard(i)->GetTime(Chip, Time[Chip], true, false);
591 }
592
593 // Write calibration data to file
594 if (asprintf(&Filename, "%s/TCalib_%d_%.2fGHz.txt", fCalibDataPath, GetBoard(i)->GetBoardSerialNumber(), DRSFreq[i]) == -1) {
595 PrintMessage("Error: asprintf() failed, cannot generate filename (%s)\n", strerror(errno));
596 return;
597 }
598 if ((Calibfile=fopen(Filename,"w")) == NULL) {
599 PrintMessage("Error: Could not open file '%s' \n", Filename);
600 }
601 else {
602 if(fprintf(Calibfile, "# DRS time calibration\n") == -1) WriteOK = false;
603
604 for (int Bin=0; Bin<kNumberOfBins; Bin++) {
605 for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
606 if(fprintf(Calibfile, "%.2f ", Time[Chip][Bin]) == -1) WriteOK = false;
607 }
608 if(fprintf(Calibfile, "\n") == -1) WriteOK = false;
609 }
610 if (fclose(Calibfile) != 0) PrintMessage("Error closing file '%s'\n", Filename);
611 }
612 if (!WriteOK) PrintMessage("Error writing to file '%s'\n", Filename);
613 else PrintMessage("Calibration written to file '%s'\n", Filename);
614
615 free(Filename);
616 }
617 PrintMessage("Time calibration finished\n");
618}
619
620// Set DRS sampling frequency
621void DAQReadout::cmd_freq() {
622 if (NParam>=2 && atof(Param[1])) {
623 SetDRSFrequency(atof(Param[1]), NParam==2 ? false : true);
624 }
625 else PrintUsage();
626}
627
628// Set LED
629void DAQReadout::cmd_led() {
630 if (Match(Param[1], "on") || Match(Param[1], "off"))
631 for (int i=FirstBoard; i<=LastBoard; i++)
632 (GetBoard(i))->SetLED(Match(Param[1], "on") ? 1 : 0);
633 else PrintUsage();
634}
635
636// Print status
637void DAQReadout::cmd_status() {
638
639 double freq;
640
641 if(NParam==1 || Match(Param[1],"daq")) {
642 PrintMessage("********** DAQ STATUS **********\n"
643 " DAQ: %s\n"
644 " Run number: %d\n"
645 " Run type: %s\n"
646 " Event: %d\n"
647 " Requested events per run: %d\n"
648 " Storage directory: %s\n"
649 " Disk space: %lu MByte\n"
650 " Total number of DRS boards: %d\n"
651 " Active DRS boards: %d\n",
652 daq_state_str[daq_state], daq_state==active ? (int) RunNumber:-1,
653 daq_state==active ? daq_runtype_str[daq_runtype]:"n/a", NumEvents,
654 NumEventsRequested, fRawDataPath,
655 CheckDisk(fRawDataPath), NumBoards, LastBoard - FirstBoard + 1);
656
657 for (int i=FirstBoard;i<=LastBoard;i++)
658 PrintMessage(" Frequency of board %d set: %s\n",i,(DRSFreq[i]!=0 ? "yes":"no"));
659 }
660
661 if(NParam==1 || Match(Param[1],"drs")) {
662 PrintMessage("\n********** DRS STATUS **********\n");
663 if (NumBoards) {
664 for (int i=FirstBoard; i<=LastBoard; i++) {
665
666 PrintMessage("Board #%d in slot %d %s\n",i,((GetBoard(i))->GetSlotNumber() >> 1)+2,((GetBoard(i))->GetSlotNumber() & 1)==0 ? "upper":"lower");
667 PrintMessage(" DRS%d serial %d, firmware %d\n"
668 " Actual temperature: %1.1lf C\n"
669 " Calibration temp.: %1.1lf C\n"
670 " Status reg.: 0x%08X\n",
671 GetBoard(i)->GetDRSType(),
672 GetBoard(i)->GetBoardSerialNumber(),
673 GetBoard(i)->GetFirmwareVersion(),
674 GetBoard(i)->GetTemperature(),
675 ACalibTemp[i],
676 GetBoard(i)->GetStatusReg());
677
678
679 if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
680 PrintMessage(" Domino wave running\n");
681 if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ1)
682 PrintMessage(" New Freq1 ready\n");
683 if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ2)
684 PrintMessage(" New Freq2 ready\n");
685
686 PrintMessage(" Control reg.: 0x%08X\n", (GetBoard(i))->GetCtrlReg());
687 if (GetBoard(i)->GetCtrlReg() & BIT_AUTOSTART)
688 PrintMessage(" AUTOSTART enabled\n");
689 if (GetBoard(i)->GetCtrlReg() & BIT_DMODE)
690 PrintMessage(" DMODE circular\n");
691 else
692 PrintMessage(" DMODE single shot\n");
693 if (GetBoard(i)->GetCtrlReg() & BIT_LED)
694 PrintMessage(" LED\n");
695 if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_EN)
696 PrintMessage(" TCAL enabled\n");
697 if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_SOURCE)
698 PrintMessage(" TCAL_SOURCE enabled\n");
699 if (GetBoard(i)->GetCtrlReg() & BIT_FREQ_AUTO_ADJ)
700 PrintMessage(" FREQ_AUTO_ADJ enabled\n");
701 if (GetBoard(i)->GetCtrlReg() & BIT_ENABLE_TRIGGER1)
702 PrintMessage(" ENABLE_TRIGGER\n");
703 if (GetBoard(i)->GetCtrlReg() & BIT_LONG_START_PULSE)
704 PrintMessage(" LONG_START_PULSE\n");
705 if (GetBoard(i)->GetCtrlReg() & BIT_ACAL_EN)
706 PrintMessage(" ACAL enabled\n");
707 PrintMessage(" Trigger bus: 0x%08X\n", GetBoard(i)->GetTriggerBus());
708 if (GetBoard(i)->IsBusy()) {
709 GetBoard(i)->ReadFrequency(0, &freq);
710 PrintMessage(" Frequency0: %1.4lf GHz\n", freq);
711 GetBoard(i)->ReadFrequency(1, &freq);
712 PrintMessage(" Frequency1: %1.4lf GHz\n", freq);
713 }
714 else PrintMessage(" Domino wave stopped\n");
715 }
716 }
717 else PrintMessage("No DRS boards available!\n\n");
718 }
719}
720
721// Adress DRS boards
722void DAQReadout::cmd_board() {
723 if (Match(Param[1],"all")) {
724 FirstBoard = 0;
725 LastBoard = GetNumberOfBoards()-1;
726 }
727 else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])<NumBoards) {
728 FirstBoard = atoi(Param[1]);
729 LastBoard = FirstBoard;
730 }
731 else if (NParam==3 && atoi(Param[1])>=0 && atoi(Param[1])<NumBoards &&
732 atoi(Param[2])>0 && atoi(Param[2])<NumBoards) {
733 FirstBoard = atoi(Param[1]);
734 LastBoard = atoi(Param[2]);
735 }
736 else PrintMessage("Cannot address board(s), out of range.\n");
737}
738
739// Set input range
740void DAQReadout::cmd_update() {
741
742 if (NParam != 2 || atoi(Param[1]) < 0) {
743 PrintUsage();
744 return;
745 }
746 MinDelay = atoi(Param[1]);
747}
748
749// Print help
750void DAQReadout::cmd_help() {
751
752 char Buffer[MAX_COM_SIZE];
753
754 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
755 snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
756 PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
757 }
758 PrintMessage(".<command> Execute shell command\n\n"
759 "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n"
760 "Test data can also be written if no DRS boards are available.\n"
761 "Strings containing spaces have to be enclosed in \"double quotes\".\n");
762}
763
764// Exit programm - SIGTERM sets ExitRequest flag
765// If command comes from DimCommand thread, SIGTERM also makes readline() return
766void DAQReadout::cmd_exit() {
767
768 if (daq_state == active) PrintMessage("Issue 'stop' first to stop daq\n");
769 else pthread_kill(MainThread, SIGTERM);
770}
771
772
773// ----------------------------------------------
774// ***** Utility function for DRS control *****
775// ----------------------------------------------
776
777// Start domino wave
778void DAQReadout::StartDRS() {
779 for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->StartDomino();
780}
781
782// Stop domino wave
783void DAQReadout::StopDRS() {
784 for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->SoftTrigger();
785}
786
787// Transfer amplitude-calibrated waveforms to memory
788void DAQReadout::ReadCalibratedDRSData() {
789
790 for (int i=FirstBoard; i<=LastBoard; i++) {
791 GetBoard(i)->TransferWaves(GetBoard(i)->GetNumberOfChannels()*GetBoard(i)->GetNumberOfChips());
792
793 for (int k=0; k<GetBoard(i)->GetNumberOfChips(); k++) {
794 TriggerCell[i][k] = GetBoard(i)->GetTriggerCell(k);
795 for (int j=0; j<GetBoard(i)->GetNumberOfChannels(); j++) {
796 GetBoard(i)->GetWave(k, j, WaveForm[i][k][j], true, TriggerCell[i][k]);
797 }
798 }
799 }
800}
801
802// Read calibration data
803bool DAQReadout::ReadCalibration() {
804
805 static char Buffer[MAX_COM_SIZE];
806 int Serial, Calib;
807 float Temp, Freq;
808
809 for (int i=FirstBoard; i<=LastBoard; i++) {
810 if (GetBoard(i)->GetDRSType() == 4) {
811 if (ACalib[i] == false) {
812 // Check calibration info file if EEPROM data on DRS board still valild
813 FILE *CalibInfo = fopen(CalibInfoFilename, "r");
814 if (CalibInfo == NULL) return false;
815 fgets(Buffer, sizeof(Buffer), CalibInfo); // skip first two lines
816 fgets(Buffer, sizeof(Buffer), CalibInfo);
817
818 while (fgets(Buffer, sizeof(Buffer), CalibInfo) != NULL) {
819 if (sscanf(Buffer, "%d %d %f %*d %f", &Serial, &Calib, &Temp, &Freq) != 4) {
820 fclose(CalibInfo);
821 return false;
822 }
823
824 if (Serial==GetBoard(i)->GetBoardSerialNumber() && int(Freq*100)==int(DRSFreq[i]*100) && Calib==1) {
825 ACalib[i] = true;
826 ACalibTemp[i] = Temp;
827 break;
828 }
829 }
830 fclose(CalibInfo);
831 }
832 }
833 else {
834 if (!ACalib[i]) {
835 GetBoard(i)->SetCalibrationDirectory(fCalibDataPath);
836 PrintMessage("Reading response calibration file for board %d from: \"%s\"\n", i, fCalibDataPath);
837 for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
838 if (GetBoard(i)->GetResponseCalibration()->ReadCalibration(Chip) == false) return false;
839 }
840 ACalib[i] = true;
841 }
842 }
843 if (fabs(ACalibTemp[i]-GetBoard(i)->GetTemperature())>2) PrintMessage("Warning: Large difference to calibration temperature for board %d\n", i);
844 } // Loop over boards
845 return true;
846}
847
848// Stop DAQ
849void DAQReadout::StopRun() {
850
851 if(daq_state != active) PrintMessage("DAQ is not active.\n");
852 else {
853 Stop = true;
854 PrintMessage("DAQ will stop.\n");
855 }
856}
857
858// Set DOMINO mode
859void DAQReadout::SetDOMINOMode(int mode) {
860
861 for (int i=FirstBoard; i<=LastBoard; i++) {
862 GetBoard(i)->SetDominoMode(mode==1 ? 1:0);
863 PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot");
864 }
865}
866
867// Set DOMINO readout mode
868void DAQReadout::SetDOMINOReadMode(int mode) {
869
870 for (int i=FirstBoard; i<=LastBoard; i++) {
871 GetBoard(i)->SetReadoutMode(mode);
872 PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position");
873 }
874}
875
876// Set DOMINO wave mode
877void DAQReadout::SetDOMINOWaveMode(int mode) {
878
879 for (int i=FirstBoard; i<=LastBoard; i++) {
880 GetBoard(i)->SetDominoActive(mode);
881 PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped");
882 }
883}
884
885// Enable hardware trigger of all boards
886void DAQReadout::HWTrigger(int mode) {
887
888 if (NumBoards)
889 for (int i=FirstBoard; i<=LastBoard; i++) {
890 GetBoard(i)->EnableTrigger(mode, 0);
891 PrintMessage("Hardware trigger of board %d %s\n",i,mode==1 ? "enabled":"disabled");
892 }
893 else PrintMessage("No DRS boards available\n");
894}
895
896// Set DRS sampling frequency
897void DAQReadout::SetDRSFrequency(double freq, bool Regulation) {
898
899 PrintMessage("Setting frequency %s regulation\n",Regulation ? "with":"without");
900
901 for (int i=FirstBoard; i<=LastBoard; i++) {
902 if ((Regulation ? GetBoard(i)->RegulateFrequency(freq) :
903 GetBoard(i)->SetFrequency(freq, true)) == 0) {
904 PrintMessage("Warning: Board %d has not reached the requested value\n",i);
905 }
906
907 double f;
908 GetBoard(i)->ReadFrequency(0, &f);
909 DRSFreq[i] = f;
910 ACalib[i] = false;
911 TCalib[i] = false;
912 PrintMessage("Board %d is running at %1.3lf GHz\n",i,f);
913 }
914}
915
916// Check if DRS is sampling
917bool DAQReadout::IsDRSBusy() {
918
919 for (int i=FirstBoard; i<=LastBoard; i++)
920 if ((GetBoard(i))->IsBusy()) return true;
921 return false;
922}
923
924// Check if DRS frequency is set
925bool DAQReadout::IsDRSFreqSet() {
926
927 for (int i=FirstBoard; i<=LastBoard; i++)
928 if (DRSFreq[i] == 0) {
929 PrintMessage("Sampling frequency of DRS board %d not set\n", i);
930 return false;
931 }
932 return true;
933}
934
935// Open new raw data file (if RunNumber == -1, data will be written to /dev/null)
936bool DAQReadout::OpenRawFile() {
937
938 time_t rawtime;
939 struct tm *timeinfo;
940 char RunDate[MAX_COM_SIZE], Buffer[MAX_COM_SIZE];
941
942 // Write run date to status structure (if after 13:00, use next day)
943 time(&rawtime);
944 timeinfo = gmtime(&rawtime);
945 if(timeinfo->tm_hour>=13) rawtime += 12*60*60;
946 timeinfo = gmtime(&rawtime);
947 snprintf(RunDate,sizeof(RunDate),"%d%02d%02d",timeinfo->tm_year+1900,timeinfo->tm_mon + 1,timeinfo->tm_mday);
948
949 // Create direcory if not existing (ignore error if already existing) and change to it
950 if (RunNumber != -1) {
951 snprintf(Buffer, sizeof(Buffer), "%s/%s", fRawDataPath, RunDate);
952 if(mkdir(Buffer, S_IRWXU|S_IRWXG)==-1 && errno!=EEXIST) {
953 PrintMessage("\rError: Could not create direcory \"%s\" (%s)\n", Buffer, strerror(errno));
954 return false;
955 }
956 }
957
958 // Generate filename
959 if (RunNumber == -1) snprintf(FileName, sizeof(FileName), "/dev/null");
960 else snprintf(FileName, sizeof(FileName),"%s/%s/%s_D1_%.8u.%.3u_%c_%s.raw", fRawDataPath, RunDate,
961 RunDate, RunNumber, FileNumber, toupper(daq_runtype_str[daq_runtype][0]), RHeader->Description);
962
963 // Open file with rwx right for owner and group, never overwrite file
964 Rawfile = open(FileName,O_WRONLY|O_CREAT|(RunNumber==-1?0:O_EXCL), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
965 if(Rawfile==-1) {
966 PrintMessage("\rError: Could not open file \"%s\" (%s)\n", FileName, strerror(errno));
967 return false;
968 }
969 return true;
970}
971
972// Write run header and board structures (revision number is zero for svn modified working copy)
973bool DAQReadout::WriteRunHeader() {
974
975 struct timeval Time;
976
977 RHeader->MagicNum = MAGICNUM_OPEN;
978 RHeader->DataFormat = DATA_FORMAT;
979 RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1);
980
981 RHeader->RunHeaderSize = sizeof(RunHeader);
982 RHeader->EventHeaderSize = sizeof(EventHeader);
983 RHeader->BoardStructureSize = sizeof(BoardStructure);
984
985 RHeader->Identification = IDENTIFICATION;
986 RHeader->Type = daq_runtype;
987 RHeader->RunNumber = RunNumber;
988 RHeader->FileNumber = FileNumber;
989
990 gettimeofday(&Time, NULL);
991 RHeader->StartSecond = Time.tv_sec;
992 RHeader->StartMicrosecond = Time.tv_usec;
993
994 RHeader->NBoards = NumBoards==0 && daq_runtype==test ? 1 : (LastBoard - FirstBoard) + 1;
995 RHeader->NChips = NumBoards==0 ? kNumberOfChipsMax : GetBoard(0)->GetNumberOfChips();
996 RHeader->NChannels = NumBoards==0 ? kNumberOfChannelsMax : GetBoard(0)->GetNumberOfChannels();
997 RHeader->NBytes = sizeof(short);
998
999 RHeader->Offset = fFirstSample;
1000 RHeader->Samples = fSamples;
1001
1002 if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
1003 PrintMessage("Error: Could not write run header, terminating run (%s)\n", strerror(errno));
1004 return false;
1005 }
1006
1007 for (int i=FirstBoard; i<=LastBoard; i++) {
1008 BStruct[i].SerialNo = GetBoard(i)->GetBoardSerialNumber();
1009 BStruct[i].BoardTemp = GetBoard(i)->GetTemperature();
1010 BStruct[i].NomFreq = DRSFreq[i];
1011 BStruct[i].ScaleFactor = GetBoard(i)->GetPrecision();
1012 }
1013
1014 // In case no boards are available, dummy data is written for one board structure
1015 if (NumBoards == 0) {
1016 BStruct[0].NomFreq = 1;
1017 BStruct[0].ScaleFactor = 0.1;
1018 }
1019
1020 if(write(Rawfile, &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) != (ssize_t) sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) {
1021 PrintMessage("Error: Could not write (all) board structures, terminating run (%s)\n", strerror(errno));
1022 return false;
1023 }
1024 return true;
1025}
1026
1027// Update run header before closing file
1028bool DAQReadout::UpdateRunHeader(unsigned int Events, bool Error) {
1029
1030 struct timeval Time;
1031
1032 RHeader->MagicNum = Error==false ? MAGICNUM_CLOSED:MAGICNUM_ERROR;
1033 RHeader->Events = Events;
1034
1035 gettimeofday(&Time, NULL);
1036 RHeader->EndSecond = Time.tv_sec;
1037 RHeader->EndMicrosecond = Time.tv_usec;
1038
1039 if(lseek(Rawfile,0,SEEK_SET)==-1) {
1040 PrintMessage("Error: Could not rewind file to write updated run header, terminating run (%s)\n", strerror(errno));
1041 return false;
1042 }
1043
1044 if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
1045 PrintMessage("Error: Could not write updated run header, terminating run (%s)\n", strerror(errno));
1046 return false;
1047 }
1048 return true;
1049}
1050
1051// Write event
1052bool DAQReadout::WriteEvent() {
1053
1054 // Event header
1055 struct timeval Time;
1056
1057 gettimeofday(&Time, NULL);
1058
1059 EHeader->EventNumber = NumEvents;
1060 EHeader->TriggerType = daq_runtype==data ? 0 : 1;
1061 EHeader->Second = Time.tv_sec;
1062 EHeader->Microsecond = Time.tv_usec;
1063 EHeader->EventSize = sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples +
1064 sizeof(int)*RHeader->NBoards*RHeader->NChips;
1065
1066 if(write(Rawfile, EHeader, sizeof(EventHeader)) != sizeof(EventHeader)) {
1067 PrintMessage("Error: Could not write event header, terminating run (%s)\n", strerror(errno));
1068 return false;
1069 }
1070
1071 unsigned int Start, Count = 0;
1072 ssize_t WriteResult, Size = 0;
1073 struct iovec DataPart[IOV_MAX];
1074
1075 // Write trigger cells
1076 for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
1077 for (unsigned int k=0; k<RHeader->NChips; k++) {
1078 if ((WriteResult=write(Rawfile, &TriggerCell[i][k], sizeof(int))) != sizeof(int)) {
1079 if (WriteResult == -1) PrintMessage("Error: Could not write trigger cells, terminating run (%s)\n", strerror(errno));
1080 else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult, sizeof(int));
1081 return false;
1082 }
1083 }
1084 }
1085
1086 // ADC data (two chucks per channel if wrap around of pipeline occurred, therefore
1087 // IOV_MAX>=2 is checked at startup
1088 for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
1089 for (unsigned int k=0; k<RHeader->NChips; k++) {
1090 // Start bin
1091 if (GetBoard(i)->GetDRSType() == 4) Start = 0;
1092 else Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins;
1093 for (unsigned int l=0; l<RHeader->NChannels; l++) {
1094 DataPart[Count].iov_base = &WaveForm[i][k][l][Start];
1095 DataPart[Count++].iov_len = (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short);
1096 Size += DataPart[Count-1].iov_len;
1097 // In case second part of waveform still missing, write now
1098 if(DataPart[Count-1].iov_len < fSamples * sizeof(short)) {
1099 DataPart[Count].iov_base = &WaveForm[i][k][l][0];
1100 DataPart[Count++].iov_len = (fSamples-(kNumberOfBins-Start)) * sizeof(short);
1101 Size += DataPart[Count-1].iov_len;
1102 }
1103
1104 // Write to disk if either maximum size of DataPart[] array or last loop interation is reached
1105 // Possibly 2 chunks are entered in array in the previous lines of code, therefore IOV_MAX-1
1106 if (Count>=IOV_MAX-1 || (l==(RHeader->NChannels-1) && k==(RHeader->NChips-1) && i==(LastBoard+(NumBoards==0)))) {
1107 if ((WriteResult=writev(Rawfile, DataPart, Count)) != (int) Size) {
1108 if (WriteResult==-1) PrintMessage("Error: Could not write event data, terminating run (%s)\n", strerror(errno));
1109 else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult,Count*DataPart[0].iov_len);
1110 return false;
1111 }
1112 Count = 0; Size = 0;
1113 }
1114 } // Channels
1115 } // Chips
1116 } // Boards
1117
1118 return true;
1119}
1120
1121// Print usage text for command
1122void DAQReadout::PrintUsage() {
1123 PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
1124}
1125
1126// Print progress (used in DRS class)
1127void DAQReadout::Progress(int Progress) {
1128 PrintMessage("\rProgress: %d%% ", Progress);
1129 fflush(stdout);
1130};
1131
1132// Print message to console only
1133void DAQReadout::PrintMessage(const char *Format, ...) {
1134
1135 static char Error[] = "vasprintf() failed in PrintMessage()";
1136 char *Text;
1137
1138 // Evaluate arguments
1139 va_list ArgumentPointer;
1140 va_start(ArgumentPointer, Format);
1141 if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
1142 va_end(ArgumentPointer);
1143
1144 // Print to console
1145 if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt); // New prompt
1146 else printf("%s", Text);
1147 fflush(stdout);
1148
1149 // Send to DIM text service
1150 ConsoleOut->updateService(Text);
1151
1152 // Free old text
1153 if (ConsoleText != Error) free(ConsoleText);
1154 ConsoleText = Text;
1155}
1156
1157// DIM command handler (must be non-blocking, otherwise a DIM rpc would dead-lock)
1158void DAQReadout::commandHandler() {
1159
1160 // Ignore empty or illegal strings
1161 if (getCommand()->getSize() == 0 ||
1162 *((char *) getCommand()->getData()+getCommand()->getSize()-1) != '\0' ||
1163 strlen(getCommand()->getString()) == 0) return;
1164
1165 // Copy command to new buffer (will be freed by global Execute() function)
1166 char *Command;
1167 if (asprintf(&Command, "%s", getCommand()->getString()) == -1) {
1168 PrintMessage("asprintf() failed in DRSReadout::commandHandler() (%s)\n", strerror(errno));
1169 return;
1170 }
1171
1172 // Create detached command handling thread
1173 pthread_t Thread;
1174 int Code;
1175 if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::Execute,(void *) Command)) != 0) {
1176 PrintMessage("pthread_create() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code));
1177 }
1178 else if ((Code = pthread_detach(Thread)) != 0) {
1179 PrintMessage("pthread_detach() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code));
1180 }
1181}
1182
1183/********************************************************************\
1184
1185 DAQ Thread
1186
1187 This thread takes data until the requested number of events is reached,
1188 until no more disk space is available or until data taking is stopped.
1189 No mutex mechanism is used since variables will never be written
1190 simultaneoously by this and the main thread.
1191
1192\********************************************************************/
1193
1194void DAQReadout::DAQ() {
1195
1196 struct timeval StartTime, StopTime;
1197 unsigned int EventsInFile;
1198 unsigned long long RunSize = 0;
1199 bool WriteError = false;
1200 time_t LastDIMUpdate = 0;
1201 off_t FileSize;
1202 int DIMSize;
1203 float RunSizeMB=0, FileSizeMB=0;
1204
1205 // Initialize run
1206 NumEvents = 0;
1207 FileNumber = 0;
1208 DimClient::sendCommandNB("Feedback/Command", "clear");
1209
1210 DimService RunSizeService(SERVER_NAME"/RunSizeMB", RunSizeMB);
1211 DimService FileSizeService(SERVER_NAME"/FileSizeMB", FileSizeMB);
1212 DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents);
1213 DimService FilenameService(SERVER_NAME"/FileName", FileName);
1214
1215 gettimeofday(&StartTime, NULL);
1216 PrintMessage("\rStarting run #%d (%s) with %u event(s)\n", RunNumber, daq_runtype_str[daq_runtype], NumEventsRequested);
1217
1218 do {
1219 // Check if enough disk space is left
1220 if (CheckDisk(fRawDataPath) <= fMinDiskSpaceMB+fMaxFileSizeMB) {
1221 PrintMessage("\rError: Disk space after next file (max. %d MByte) below %d MByte\n", fMaxFileSizeMB, fMinDiskSpaceMB);
1222 break;
1223 }
1224
1225 // Init run header, open raw file, write run header
1226 if (!OpenRawFile()) break;
1227 PrintMessage("\rData file \"%s\" opened.\n", FileName);
1228 EventsInFile = 0;
1229 FileSize = 0;
1230 FilenameService.updateService();
1231
1232 WriteError |= !WriteRunHeader();
1233 StartDRS();
1234
1235 // Copy run header and board structures to buffer for DIM event service
1236 memcpy(DIMEventData, RHeader, sizeof(RunHeader));
1237 ((RunHeader *) DIMEventData)->Events = 1; // always contains 1 event
1238 memcpy(DIMEventData+sizeof(RunHeader), &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0)));
1239
1240 // Service size for DIM
1241 DIMSize = sizeof(RunHeader) + sizeof(EventHeader) +
1242 sizeof(BoardStructure)*RHeader->NBoards +
1243 sizeof(int)*RHeader->NBoards*RHeader->NChips +
1244 sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples;
1245
1246 // Take data until finished, stopped or file too large
1247 while ((NumEvents<NumEventsRequested || NumEventsRequested==0) &&
1248 !Stop && FileSize/1024/1024<fMaxFileSizeMB && !WriteError) {
1249
1250 if (daq_runtype == data) while (IsDRSBusy()); // Wait for hardware trigger (if DAQ stopped, DRS will not be busy anymore)
1251 else if (daq_runtype == pedestal) StopDRS(); // ..or for software trigger
1252
1253 // Read event data and restart (reduces dead-time because waiting for next trigger while writing)
1254 ReadCalibratedDRSData();
1255 StartDRS();
1256
1257 // Write event to disk and update file size
1258 EventsInFile++;
1259 NumEvents++;
1260 WriteError |= !WriteEvent();
1261
1262 if((FileSize = lseek(Rawfile, 0, SEEK_CUR)) == -1) {
1263 PrintMessage("Error: Could not determine file size, terminating run (%s)\n", strerror(errno));
1264 WriteError = true;
1265 }
1266
1267 // Check for LED trigger
1268 if (WaveForm[fLedTrigBoard][fLedTrigChip][fLedTrigChannel][fLedTrigSample] > fLedTrigThreshold) {
1269 std::stringstream Cmd;
1270 float Integral;
1271
1272 // Calculate feedback signal
1273 for (int i=FirstBoard; i<=LastBoard; i++) {
1274 for (unsigned int j=0; j<RHeader->NChips; j++) {
1275 for (unsigned int k=0; k<RHeader->NChannels; k++) {
1276 Integral = 0.0;
1277 for (int q=-fIntHalfWidth; q<=(int) fIntHalfWidth; q++) {
1278 Integral += (WaveForm[i][j][k][fLedSignalSample+q] - WaveForm[i][j][k][fLedBaselineSample+q])*GetBoard(i)->GetPrecision();
1279 }
1280 Integral /= 2*fIntHalfWidth+1;
1281 Cmd << Integral << " ";
1282 }
1283 }
1284 }
1285
1286 // Send data to feedback
1287 DimClient::sendCommandNB("Feedback/Command", (char *) ("data "+Cmd.str()).c_str());
1288 }
1289
1290 // Update DIM services (update rate is limited)
1291 if (time(NULL) - LastDIMUpdate < MinDelay) continue;
1292 LastDIMUpdate = time(NULL);
1293
1294 RunSizeMB = (RunSize+FileSize)/1024.0/1024.0;
1295 RunSizeService.updateService();
1296 FileSizeMB = FileSize/1024.0/1024.0;
1297 FileSizeService.updateService();
1298 EventNumService.updateService();
1299
1300 // Copy new event header
1301 char *Pnt = DIMEventData+sizeof(RunHeader)+RHeader->NBoards*sizeof(BoardStructure);
1302 memcpy(Pnt, EHeader, sizeof(EventHeader));
1303 Pnt += sizeof(EventHeader);
1304
1305 // Copy new trigger cells
1306 for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
1307 for (unsigned int k=0; k<RHeader->NChips; k++) {
1308 *((int *) Pnt) = TriggerCell[i][k];
1309 Pnt += sizeof(int);
1310 }
1311 }
1312
1313 // Copy event data
1314 unsigned int Start;
1315 for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
1316 for (unsigned int k=0; k<RHeader->NChips; k++) {
1317 // Start bin
1318 if (GetBoard(i)->GetDRSType() == 4) Start = 0;
1319 else Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins;
1320
1321 for (unsigned int l=0; l<RHeader->NChannels; l++) {
1322 memcpy(Pnt, &WaveForm[i][k][l][Start], (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short));
1323 Pnt += (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short);
1324 // In case second part of waveform still missing, write now
1325 if((Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short) < fSamples * sizeof(short)) {
1326 memcpy(Pnt, &WaveForm[i][k][l][0], (fSamples-(kNumberOfBins-Start)) * sizeof(short));
1327 }
1328 }
1329 }
1330 }
1331 EventService->updateService((void *) DIMEventData, DIMSize);
1332 }
1333
1334 // Write updated run header, close file
1335 RunSize += FileSize;
1336 WriteError |= !UpdateRunHeader(EventsInFile, WriteError);
1337 if(close(Rawfile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno));
1338 else PrintMessage("Data file closed (size %lu MByte).\n", FileSize/1024/1024);
1339
1340 FileNumber += 1;
1341 } while((NumEvents<NumEventsRequested || NumEventsRequested==0) && !Stop && !WriteError);
1342
1343 StopDRS();
1344
1345 // Print run summary to screen
1346 if(!WriteError) PrintMessage("\rRun #%d (%s) %s, %d events\n", RunNumber, daq_runtype_str[daq_runtype], (NumEvents == NumEventsRequested) ? "completed":"stopped", NumEvents);
1347 else PrintMessage("\rRun #%d (%s) aborted due to error after %d events\n", RunNumber, daq_runtype_str[daq_runtype], NumEvents);
1348
1349 // Print run statistics
1350 if (NumEvents>0 && !WriteError) {
1351 gettimeofday(&StopTime, NULL);
1352 float RunTime = StopTime.tv_sec-StartTime.tv_sec + (StopTime.tv_usec-StartTime.tv_usec)*1e-6;
1353 PrintMessage("Time for run %.2f seconds, trigger rate %.2f Hz.\n", RunTime, NumEvents/RunTime);
1354 PrintMessage("Run size %llu MByte, data rate %.1f MByte/s.\n", RunSize/1024/1024, RunSize/1024.0/1024/RunTime);
1355 }
1356
1357 RunNumber = -1;
1358 RunNumService->updateService();
1359
1360 daq_state = stopped;
1361}
1362
1363// ---------------------------------------
1364// ***** Various utility functions *****
1365// ---------------------------------------
1366
1367// Check if two strings match (min 1 character must match)
1368bool Match(const char *str, const char *cmd) {
1369 return strncasecmp(str,cmd,strlen(str)==0 ? 1:strlen(str)) ? false:true;
1370}
1371
1372// Return current available storage space in given directory
1373int CheckDisk(const char *Directory) {
1374 struct statfs FileSystemStats;
1375
1376 statfs(Directory, &FileSystemStats);
1377 return FileSystemStats.f_bavail / 1024 * (FileSystemStats.f_bsize / 1024);
1378}
1379
1380// Parse command line for white space and double-quote separated tokens
1381int ParseInput(char* Command, const char *Param[]) {
1382 int Count=0;
1383
1384 while(Count<MAX_NUM_TOKEN) {
1385 while (isspace(*Command)) Command++; // Ignore initial white spaces
1386 if(*Command=='\0') break;
1387 if (*Command == '\"') {
1388 Param[Count] = ++Command;
1389 while(*Command!='\"' && *Command!='\0') Command++;
1390 }
1391 else {
1392 Param[Count] = Command;
1393 while(!isspace(*Command) && *Command!='\0') Command++;
1394 }
1395 if(*Command != '\0') *Command++ = '\0';
1396 Count++;
1397 }
1398 return Count;
1399}
1400
1401// Thread entry routines (non-static class methods cannot be directly executed as thread)
1402void DAQ(DAQReadout *m) {
1403
1404 m->DAQ();
1405}
1406
1407// Stub to call Execute() metho of class and free command memory
1408void Execute(char *Command) {
1409
1410 ThisClass->Lock();
1411 ThisClass->Execute(Command);
1412 free(Command);
1413 ThisClass->Unlock();
1414}
Note: See TracBrowser for help on using the repository browser.