/////////////////////////////////////////////////////////////////////// // zdab read functions // Notes: ZDAB external format is big-endian. // ZDAB native format is platform dependent. // // Author: Phil Harvey (some code from Yuendat Chan's CM_ZDAB.cp) // // Revisions: // 06/26/98 - PH Created // 01/29/99 - PH Upgraded to a C++ object // 11/26/99 - PH Added ability to read MAST bank records // 12/01/99 - PH Generalized to remove MAST-specific knowledge // 11/18/04 - PH Fixed reading problem by updating from snobuilder version // /////////////////////////////////////////////////////////////////////// #include #include #include using RAT::warn; using RAT::info; using RAT::detail; using RAT::debug; using RAT::Log; #include #include #include /* constants */ #define BASE_BUFFSIZE 32768UL // base size of zdab record buffer #define MAX_BUFFSIZE 0x400000UL // maximum size of zdab record buffer (4 MB) // the builder won't put out events with NHIT > 10000 // (note that these are possible due to hardware problems) // but XSNOED can write an event with up to 10240 channels #define MAX_NHIT 10240 //---------------------------------------------------------------------- // PZdabFile constructor // PZdabFile::PZdabFile() { mRecBuffsize = 0; mFile = NULL; mWordOffset = 0; mBlockCount = 0; mRecordCount = 0; mBufferEmpty = 0; mRecBuffer = 0; mRecBuffsize = 0; // size of temporary ZDAB buffer mBuffPtr32 = 0; mBytesRead = 0; mWordsTotal = 0; mBytesTotal = 0; mLastGTID = 0; mLastRecord = NULL; } PZdabFile::~PZdabFile() { Free(); } void PZdabFile::Free() { if (mRecBuffsize) { free(mRecBuffer); mRecBuffer = NULL; mRecBuffsize = 0; } } // initialize for reading from zdab file // returns < 0 on error int PZdabFile::Init( FILE *inFile ) { mFile = inFile; if( inFile && !fseek( inFile, 0L, SEEK_SET ) ) { mWordOffset = 0; mBlockCount = 0; mRecordCount= 0; mBufferEmpty = 1; mLastGTID = 0; mLastRecord = NULL; // set up zdab record buffer if not already done if (!mRecBuffsize) { mRecBuffsize = BASE_BUFFSIZE; mRecBuffer = (uint32_t *)malloc(mRecBuffsize * sizeof(uint32_t)); if (!mRecBuffer) { mRecBuffsize = 0; RAT::Log::Die("Out of memory for zdab record buffer!"); return( -1 ); } } return( 0 ); } else { return( -1 ); } } // NextRecord - get next record in ZDAB file (based on code by Yuen-Dat Chan) // Returns: pointer to nZDAB record (native format) with trailing data // (external format) nZDAB *PZdabFile::NextRecord() { int n; uint32_t recLength, recType, nb_to_read, nw_count, block_size; uint32_t *skip32Ptr, *new_buffer, new_buffsize; nZDABPtr nzdabPtr; CONTROLPtr controlPtr; PILOTPtr pilotPtr; pilotHeaderPtr pHPtr; ZEBRA_ST daqST; // Steering block control words (ZEBRA FZ must) if (!mFile) return(0); /* ** return the next bank within this physical record if available - PH 12/01/99 ** - on any error, drop through to read the next record from file */ if ((nzdabPtr = mLastRecord) != NULL) { // reset our last record pointer mLastRecord = NULL; // get pointer to first word after bank data (the i/o control word) uint32_t *ioControlPtr = (uint32_t*)(nzdabPtr + 1) + nzdabPtr->data_words; // make sure the ioControlPtr is in the buffer if (ioControlPtr < mBuffPtr32) { // swap the i/o control word SWAP_INT32(ioControlPtr,1); // get length of next zdab header (lower 16 bits of i/o control word) // - for some reason this is actually 3 greater than the actual // size of the header which is a minimum of 9 words // (not counting the i/o control word as part of the header) uint32_t hdrLen = (*ioControlPtr & 0x0000ffffUL); // make sure the header is at least the minimum size if (hdrLen >= 12) { // get pointer to next bank header nzdabPtr = (nZDAB *)(ioControlPtr + hdrLen - 2) - 1; // make sure the next header is contained in the buffer if ((uint32_t *)(nzdabPtr+1) <= mBuffPtr32) { // swap the next zdab header SWAP_INT32(nzdabPtr, NZDAB_WORD_SIZE); // make sure the data is contained in the buffer if ((uint32_t*)(nzdabPtr+1)+nzdabPtr->data_words<=mBuffPtr32){ detail << "-- same block --\n"; // success!! -- we have a good zdab record. // save the pointer in mLastRecord and return it return(mLastRecord = nzdabPtr); } } } } } /* ** loop through Zebra records from file */ while( 1 ) { if( mBufferEmpty ) { // need to read in new buffer n = fread( &daqST, sizeof(daqST), 1, mFile ); if (n != 1) { warn << "Unexpected EOF while reading zdab file!\n"; return(0); } SWAP_INT32( &daqST, 8 ); /* check zebra signature - PH 07/03/98 */ if( daqST.MPR[0] != ZEBRA_SIG0 || daqST.MPR[1] != ZEBRA_SIG1 || daqST.MPR[2] != ZEBRA_SIG2 || daqST.MPR[3] != ZEBRA_SIG3 ) { warn << RAT::BRED; warn << dformat("Invalid ZEBRA steering block: " "0x%08x %08x %08x %08x\n", daqST.MPR[0], daqST.MPR[1], daqST.MPR[2], daqST.MPR[3]); if(mBlockCount == 0){ warn << "No records read. "; if(daqST.MPR[0] == 0x726f6f74) // "root" warn << "Looks like a ROOT file.\n"; else warn << "File is corrupted or of the wrong type.\n"; } else{ warn << "File seems to be corrupted.\n"; } Log::Die(RAT::CLR); } if( daqST.MPR[4] & ( ZEBRA_EMERGENCY_STOP | ZEBRA_END_OF_RUN ) ) { warn << dformat("ZEBRA EOF after [%ld] blocks and [%ld] records\n", (long)mBlockCount, (long)mRecordCount ); return(0); } block_size=daqST.MPR[4] & ZEBRA_BLOCK_SIZE_MASK; //Phys. rec. length if( block_size > ZEBRA_BLOCKSIZE ) { Log::Die(dformat( "%sIllegal ZEBRA block size %d (max %d)%s\n", RAT::BRED, block_size, ZEBRA_BLOCKSIZE, RAT::CLR)); } else { // subtract steering length -> real data length // - account for fast blocks (MPR(7)) - PH 07/03/98 nw_count = block_size * ( 1 + daqST.MPR[7] ) - 8; } if( daqST.MPR[5] != mBlockCount ) { warn << dformat("%sWrong ZEBRA bank number: " "%ld (should be %ld)\n%s", RAT::BRED, (long)daqST.MPR[5], (long)mBlockCount, RAT::CLR ); } mBlockCount++; if( ( mWordsTotal = nw_count + mWordOffset ) > mRecBuffsize ) { if (mWordsTotal < BASE_BUFFSIZE) { new_buffsize = BASE_BUFFSIZE; } else if (mWordsTotal > MAX_BUFFSIZE) { Log::Die(dformat( "%sZDAB record too large! %ld. corrupt file?\n%s", RAT::BRED, (long)mWordsTotal, RAT::CLR)); } else { new_buffsize = mWordsTotal; } new_buffer = (uint32_t *)malloc(new_buffsize * sizeof(uint32_t)); if (!new_buffer) { Log::Die("Out of memory for ZDAB record buffer!"); return(0); } // copy any old zdab data into new (larger) buffer if (mWordOffset) { memcpy(new_buffer, mRecBuffer, mWordOffset*sizeof(uint32_t)); } // install new buffer Free(); // free old zdab buffer mRecBuffer = new_buffer; mRecBuffsize = new_buffsize; } n = fread(mRecBuffer+mWordOffset, sizeof(uint32_t), nw_count, mFile); if ((uint32_t)n != nw_count) { if (!n) { Log::Die(dformat( "%sUnexpected EOF while reading zdab file!\n%s", RAT::BRED, RAT::CLR)); } nw_count = n; mWordsTotal = nw_count + mWordOffset; } mBuffPtr32 = mRecBuffer; mWordOffset = 0; mBytesRead = 0; mBytesTotal = mWordsTotal * sizeof(uint32_t); mBufferEmpty = 0; } else { // still has data in buffer, search for zdab banks // make sure we have enough data for the pilot header if( mBytesRead + sizeof(pilotHeader) < mBytesTotal ) { if( *mBuffPtr32 == 0 ) { //Handle 1 word padding records nb_to_read=mBytesRead+sizeof(uint32_t); //before swapping ... if( nb_to_read <= mBytesTotal ) { mBuffPtr32 += 1; mBytesRead = nb_to_read; continue; } } pHPtr = (pilotHeaderPtr)mBuffPtr32; SWAP_INT32( pHPtr, 12 ); controlPtr = &( pHPtr->control ); pilotPtr = &( pHPtr->pilot ); recLength = (uint32_t)( controlPtr->length ); recType = (uint32_t)( controlPtr->recordtype ); // handle different record types if( recType == 5 ) { nb_to_read = mBytesRead + (recLength+1)*sizeof(uint32_t); if( nb_to_read <= mBytesTotal ) { mBuffPtr32 += ( recLength + 1 ); mBytesRead = nb_to_read; SWAP_INT32( pHPtr, 12 ); //undo swapping ..... continue; } } else if( recType==2 || recType==3 || recType==4 ) { // normal data nb_to_read = mBytesRead + ( recLength + 2 ) * sizeof(uint32_t); if( nb_to_read <= mBytesTotal ) { // calculate pointer to start of zdab event, // skipping over control and pilot blocks, and // pilot6 + pilot9 words skip32Ptr = mBuffPtr32 + 12 + pilotPtr->pilot6 + pilotPtr->pilot9; /* new addition 07/03/98 */ if( skip32Ptr < mRecBuffer || skip32Ptr >= mRecBuffer+mRecBuffsize ) { Log::Die("Error 1 reading zdab file"); return(0); } SWAP_INT32( skip32Ptr, 1 ); /* swap zdab offset word */ /* get pointer to start of zdab record header */ skip32Ptr += ( *skip32Ptr & 0x0000ffff ) - 12 + 1; /* range check pointer again */ if( skip32Ptr < mRecBuffer || skip32Ptr > mRecBuffer+mRecBuffsize-9 ) { Log::Die("Error 2 reading zdab file"); return(0); } nzdabPtr = (nZDAB *)skip32Ptr; mBuffPtr32 += recLength + 2; // set up for next location mBytesRead = nb_to_read; // make sure the bank header is contained in our buffer if ((uint32_t *)(nzdabPtr+1) > mBuffPtr32) { Log::Die("Error 3 reading zdab file"); return(0); } SWAP_INT32(nzdabPtr, 9); // swap the zdab header // make sure the bank data is contained in our buffer if ((uint32_t *)(nzdabPtr+1)+nzdabPtr->data_words > mBuffPtr32) { Log::Die("Error 4 reading zdab file"); return(0); } // keep track of number of physical records found ++mRecordCount; // Done! - save the pointer in mLastRecord and return it return(mLastRecord = nzdabPtr); } } else if( recType == 1 ) { nb_to_read = mBytesRead + (recLength+2)*sizeof(uint32_t); if( nb_to_read <= mBytesTotal ) { mBuffPtr32 += ( recLength + 2 ); mBytesRead = nb_to_read; SWAP_INT32( pHPtr, 12 ); //undo swapping ..... continue; } } else { warn << dformat("Unknown record type 0x%lx, length 0x%lx\n", (long)recType, (long)recLength ); return(0); } // swap back pHPtr because we're going to try again SWAP_INT32( pHPtr, 12 ); } // calculate word offset of end of remaining record in buffer mWordOffset = ( mBytesTotal - mBytesRead ) / sizeof(uint32_t); // quit now if our remaining record is too large for buffer (double check) if( (uint32_t)(mWordOffset + mBuffPtr32 - mRecBuffer) > mRecBuffsize ) { warn << "Record too large!\n"; return(0); } // move remaining data to the beginning of buffer memmove((char*)mRecBuffer, (char*)mBuffPtr32, mWordOffset*sizeof(uint32_t)); mBufferEmpty = 1; } } } // get pointer to next PmtEventRecord in zdab file // Returns: pointer to PmtEventRecord (native format) or NULL on error or EOF PmtEventRecord *PZdabFile::NextPmt() { nZDABPtr nzdabPtr; PmtEventRecord* pmtRecord = NULL; do { nzdabPtr = NextRecord(); if (!nzdabPtr) break; // give up if no more records in file pmtRecord = GetPmtRecord(nzdabPtr); } while (!pmtRecord); // loop until we find a pmt record return(pmtRecord); } // NextBank - Return the next specified zdab bank from file // Returns: pointer to bank data (native format) or NULL if no more // matching banks uint32_t *PZdabFile::NextBank(uint32_t bank_name) { uint32_t *dataPt = NULL; do { nZDAB *nzdabPtr = NextRecord(); if (!nzdabPtr) break; dataPt = GetBank(nzdabPtr, bank_name); } while (!dataPt); return(dataPt); } // GetPmtRecord - analyze this nZDAB record // Accepts: pointer to nZDAB record (external format) // Returns: pointer to the PmtEventRecord (native format) if it has one. // Otherwise, returns NULL // Swaps: PmtEventRecord to native format PmtEventRecord *PZdabFile::GetPmtRecord(nZDAB *nzdabPtr) { PmtEventRecord *pmtEventPtr; // test the bank name if (nzdabPtr->bank_name == ZDAB_RECORD) { // extract 'ZDAB' PMT records if (Log::GetLogLevel() >= Log::DEBUG) DumpHex(nzdabPtr); pmtEventPtr = (PmtEventRecord *)(nzdabPtr + 1); SWAP_PMT_RECORD(pmtEventPtr); // swap PmtEventRecord into native format int npmt = pmtEventPtr->NPmtHit; if (npmt > MAX_NHIT) { warn << dformat("Read error: Bad ZDAB -- %d pmt hit!\n", npmt ); pmtEventPtr = NULL; // not a valid PmtEventRecord } else { // swap the hit data SWAP_INT32( pmtEventPtr + 1, 3 * npmt ); // swap the sub-fields uint32_t *sub_header = &pmtEventPtr->CalPckType; while (*sub_header & SUB_NOT_LAST) { sub_header += (*sub_header & SUB_LENGTH_MASK); SWAP_INT32( sub_header, 1 ); // swap the sub-field header // get number of data words (-1 because we don't want // to include header size) #ifndef WORDS_BIGENDIAN uint32_t data_words = (*sub_header & SUB_LENGTH_MASK) - 1; SWAP_INT32( sub_header+1, data_words ); #endif } // Supposing we ever wanted to read ZDABs from SNOMAN rather // than data coming from the builder (SNO or SNO+), we will // need to uncomment this line, because, as per Phil Harvey, // "SNOMAN used CalPckType for something entirely different, // but only the upper 8 bits were used. The lower 24 bits // were later used for the extended records." //pmtEventPtr->CalPckType &= ~SUB_NOT_LAST; // keep track of last valid event GTID uint32_t gtid = pmtEventPtr->TriggerCardData.BcGT; if (gtid) mLastGTID = gtid; } } else { if (Log::GetLogLevel() >= Log::DETAIL) DumpHex(nzdabPtr); pmtEventPtr = NULL; // not a valid PmtEventRecord } return(pmtEventPtr); // return the PmtEventRecord (or NULL if not a PmtRecord) } // GetBank - get specified type to bank data (or any type if bank_name is 0) // Accepts: pointer to nZDAB record (native format) with bank data (external format) // Returns: pointer to bank data (native format) // Swaps: data to native format if specified type uint32_t *PZdabFile::GetBank(nZDAB *nzdabPtr, uint32_t bank_name) { uint32_t *dataPt; // look for specified bank type if (!bank_name || nzdabPtr->bank_name==bank_name) { // get pointer to bank data dataPt = (uint32_t *)(nzdabPtr + 1); // swap the bank data SWAP_INT32(dataPt, nzdabPtr->data_words); } else { dataPt = NULL; // not the specified type of bank } // return pointer to the data return(dataPt); } // BankName - convert string to bank name (native format) // - string must be 4 characters long (this is not validated) uint32_t PZdabFile::BankName(char *bank_name_string) { return( ((uint32_t)bank_name_string[0] << 24) | ((uint32_t)bank_name_string[1] << 16) | ((uint32_t)bank_name_string[2] << 8) | ((uint32_t)bank_name_string[3]) ); } // BankNameString - convert from bank name (native format) to string char *PZdabFile::BankNameString(uint32_t bank_name) { static char rtnString[5]; for (int i=0; i<4; ++i) { char ch = (bank_name >> (8 * (3 - i))) & 0xff; if (ch >= ' ') { rtnString[i] = ch; } else { rtnString[i] = ' '; // substitute space for non-printable characters } } rtnString[4] = '\0'; // null terminate the string return(rtnString); } void PZdabFile::DumpHex(nZDAB *nzdabPtr, int numToPrint) { int i; int nl = (int)nzdabPtr->total_links; int nio; // extra i/o characteristic words uint32_t *tp = (uint32_t *)(nzdabPtr); #ifndef WORDS_BIGENDIAN uint32_t *tp2 = (uint32_t *)(nzdabPtr + 1); SWAP_INT32(tp2,numToPrint); // swap bytes #endif // print i/o control words // look for first word for (nio=0; nio<17; ++nio) { if ((int)(*(tp-nl-nio-1) & 0x0000ffffUL) == (12 + nio + nl)) break; } if (nio < 17) { detail << dformat("%2d i/o control words:", nio + 1); if (nio) { SWAP_INT32(tp-nl-nio, nio);} for (i=0; i<=nio; ++i) { detail << dformat(" %.8lx",(long)*(tp-nl-nio-1+i)); } if (nio) {SWAP_INT32(tp-nl-nio, nio);} detail << "\n"; if(Log::GetLogLevel() >= Log::DEBUG && nzdabPtr->bank_name == MAST_RECORD) { // print relocation table information for MAST bank SWAP_INT32(tp-nl-nio-3, 2); debug << dformat("Relocation table: %.8lx %.8lx\n", *(tp-nl-nio-3),*(tp-nl-nio-2)); SWAP_INT32(tp-nl-nio-3, 2); } } else { detail << "*** Error searching for i/o control start!\n"; } // print links detail << dformat("%2d links:",nl); if (nl) { SWAP_INT32(tp-nl, nl); for (i=0; iCalPckType; while (*sub_header & SUB_NOT_LAST) { sub_header += (*sub_header & SUB_LENGTH_MASK); if ((int)(*sub_header >> SUB_TYPE_BITNUM) == subType) { theData = (uint32_t *)(sub_header + 1); break; } } return(theData); } // GetSize - get the size of a PMT event record (including sub-fields) // pmtRecord - pointer to pmt record in native format uint32_t PZdabFile::GetSize(PmtEventRecord *pmtRecord) { /* create new buffer for event */ uint32_t event_size = sizeof(aPmtEventRecord) + 12 * pmtRecord->NPmtHit; /* make room for sub-headers */ uint32_t *sub_header = &pmtRecord->CalPckType; while (*sub_header & SUB_NOT_LAST) { sub_header += (*sub_header & SUB_LENGTH_MASK); event_size += (*sub_header & SUB_LENGTH_MASK) * sizeof(uint32_t); } return(event_size); } // byte-swap array of numbers - PH 09/02/03 void swap_bytes(char *valPt, int num, int size) { for (int n=0; n'9') return(-1); } /* filename is good, return the number */ return(atoi(pt+1)); } /* Set sub-run number in filename */ /* Note: before calling this routine, you should verify */ /* that the filename is in the proper format by prior call */ /* to zdab_get_subrun() */ int zdab_set_subrun(char *filename, int subrun) { char *pt; char buff[32]; if (subrun<0 || subrun>999) return(-1); pt = strstr(filename,".zdab"); if (!pt || pt-filename<4) return(-1); sprintf(buff,"%.3d",subrun); memcpy(pt-3, buff, 3); return(subrun); } /* return run number (-ve if filename doesn't conform to standard) */ long zdab_get_run(char *filename) { int i; char *pt = strstr(filename, ".zdab"); if (!pt || pt-filename<18) return(-1); pt -= 14; /* verify the file name format (must start with SNO_##########) */ if (memcmp(pt-4,"SNO_",4)) return(-1); for (i=0; i<10; ++i) { if (pt[i]<'0' || pt[i]>'9') return(-1); } /* filename is good, return the number */ return(atol(pt)); } /* Set run number in filename */ /* Note: before calling this routine, you should verify */ /* that the filename is in the proper format by prior call */ /* to zdab_get_run() */ long zdab_set_run(char *filename, long run) { char *pt; char buff[32]; if (run<0) return(-1); pt = strstr(filename,".zdab"); if (!pt || pt-filename<18) return(-1); sprintf(buff,"%.10ld",run); memcpy(pt-14, buff, 10); return(run); } // get 50 MHz time in sec from PMT event record double get50MHzTime(PmtEventRecord *pmtRecord) { return(((double) 2048.0 * pmtRecord->TriggerCardData.Bc50_2 + pmtRecord->TriggerCardData.Bc50_1) * 2e-8); } // get maximum value for 50 MHz clock (in sec) double get50MHzTimeMax() { return(4096.0 * 0x80000000UL * 2e-8); } // isOrphan - returns non-zero if the specified event is an orphan int isOrphan(PmtEventRecord *pmtRecord) { uint32_t *mtc_data = (uint32_t *)&pmtRecord->TriggerCardData; for (int i=0; i<6; ++i) { if (*mtc_data != 0) return(0); ++mtc_data; } return(1); }