source: drsdaq/DAQReadout.cc@ 212

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