source: drsdaq/DAQReadout.cc@ 257

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