source: drsdaq/DAQReadout.cc@ 272

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