00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2002 00004 # by 00005 # The Board of Trustees of the 00006 # Leland Stanford Junior University. 00007 # All rights reserved. 00008 # 00009 00010 __facility__ = "Online" 00011 __abstract__ = "FITS writer class" 00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = ("$Date: 2005/01/21 23:50:49 $").split(' ')[1] 00014 __version__ = "$Revision: 2.5 $" 00015 __release__ = "$Name: R04-12-00 $" 00016 __credits__ = "SLAC" 00017 00018 import LATTE.copyright_SLAC 00019 00020 import os.path 00021 #import pyfits 00022 #import numarray 00023 from cfitsio import * 00024 import time 00025 import struct 00026 import types 00027 import sys 00028 import rcReportGen 00029 00030 FORMAT_WORD = 0 00031 FORMAT_BYTE = 1 00032 00033 MODE_CREATE = 0 00034 MODE_READONLY = 2 00035 00036 RUN_REPORT_VERSION = "1.0" 00037 00038 class rcFitsWriter(object): 00039 #~ def __init__(self, fileName=None): 00040 #~ if fileMame is None: 00041 #~ ts = strftime('%y%m%d%H%M%S', gmtime()) 00042 #~ self.__fileName = 'ebf' + ts + '.fits' 00043 #~ else: 00044 #~ self.__fileName = fileName 00045 #~ self.__fitsobj = pyfits.HDUList() 00046 #~ hdu = pyfits.PrimaryHDU() 00047 #~ self.__fitsobj.append(hdu) 00048 00049 #~ def write(self, eventSummary, rawEventData): 00050 #~ hdu = pyfits.ImageHDU(numarray.fromstring(rawEventData, numarray.Int32)) 00051 #~ #col1 = pyfits.Column(name='event_data', format='i', 00052 #~ # array=numarray.fromstring(rawEventData, numarray.UInt32)) 00053 #~ #hdu = pyfits.new_table([col1]) 00054 #~ #hdu.data.field(0).byteswap() 00055 #~ hdu.header.update('EVT_NO', eventSummary.evNumber, "Event number") 00056 #~ hdu.header.update('EVT_TAG', eventSummary.tag, "Event tag") 00057 #~ self.__fitsobj.append(hdu) 00058 00059 #~ def close(self): 00060 #~ self.__fitsobj.writeto(self.__fileName) 00061 00062 def __init__(self, filePath=None, fileName=None, mode=MODE_CREATE, format=FORMAT_WORD, tableSize=1000, compress=0, flushInterval=0, reportGen=None): 00063 if fileName is None: 00064 ts = time.strftime('%y%m%d%H%M%S', time.gmtime()) 00065 self.__fileName = getFileNameFromTS(ts) 00066 else: 00067 self.__fileName = fileName 00068 if compress: self.__fileName += '.gz' 00069 if filePath is None: 00070 self.__filePath = "" 00071 else: 00072 self.__filePath = filePath 00073 self.__format = format 00074 self.__tableSize = tableSize 00075 self.__mode = mode 00076 self.__flushInterval = flushInterval 00077 self.__reportGen = reportGen 00078 if mode == MODE_READONLY: 00079 if format != FORMAT_BYTE: 00080 raise RuntimeError, "Sorry FITS file reading currently only works in BYTE mode" 00081 (self.__status, self.__fptr) = cfitsio.fits_open_file(os.path.join(self.__filePath, self.__fileName), cfitsio.READONLY) 00082 if self.__status != 0: 00083 raise IOError, "fits_open_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00084 else: 00085 if reportGen is not None: 00086 self.getHeaders() 00087 self.__tablenum = 2 00088 status = self.moveToHDU(self.__tablenum) 00089 self.__rownum = 0 00090 elif mode == MODE_CREATE: 00091 (self.__status, self.__fptr) = cfitsio.fits_create_file(os.path.join(self.__filePath, self.__fileName)) 00092 if self.__status != 0: 00093 raise IOError, "fits_create_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00094 else: 00095 naxis = 0 00096 naxes = [] 00097 self.__status = cfitsio.fits_create_img(self.__fptr, cfitsio.SHORT_IMG, naxis, naxes) 00098 if self.__status != 0: 00099 raise IOError, "fits_create_img: %s" % cfitsio.fits_get_errstatus(self.__status) 00100 else: 00101 try: 00102 from LDF import LDF 00103 except: 00104 raise RuntimeError, (__name__ + "Can't find LDF, aborting...") 00105 comment1 = "The data in this file can be parsed using %s of the Online software" % LDF.__version__ 00106 comment2 = "package LDF or a similar package designed according to LAT-TD-00606-D1," 00107 comment3 = " V/I 2.1/1; LAT-TD-00605-D1, V/I 3.1/1; LAT-TD-00639-D1, V/I 2.9/1 and" 00108 comment4 = " LAT-TD-01545, V/I 2.0/2 ." 00109 status = cfitsio.fits_write_comment(self.__fptr, " ") 00110 status = cfitsio.fits_write_comment(self.__fptr, comment1) 00111 status = cfitsio.fits_write_comment(self.__fptr, comment2) 00112 status = cfitsio.fits_write_comment(self.__fptr, comment3) 00113 status = cfitsio.fits_write_comment(self.__fptr, comment4) 00114 self.__tablenum = 0 00115 else: 00116 raise RuntimeError, "Invalid FITS file mode" 00117 00118 00119 def createNewTable(self, datagram_id=None, version_info=None): 00120 if datagram_id is not None: 00121 tmp = self.bcLong2Int(datagram_id) 00122 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "DGRAMID", 00123 tmp, "Datagram Identity") 00124 if version_info is not None: 00125 tmp = self.bcLong2Int(version_info) 00126 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "EVTVERSN", 00127 tmp, "Event Version") 00128 ttype = ['event_data'] 00129 if self.__format == FORMAT_BYTE: 00130 tform = ['1PB(4096)'] 00131 else: 00132 tform = ['1PV(1024)'] 00133 tunit=[' '] 00134 extname='Event Data' 00135 tfields=1 00136 self.__status = cfitsio.fits_create_tbl(self.__fptr, cfitsio.BINARY_TBL, 00137 self.__tableSize, tfields, ttype, tform, tunit, extname) 00138 if self.__status != 0: 00139 raise IOError, "fits_create_tbl: %s" % cfitsio.fits_get_errstatus(self.__status) 00140 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "EVTCOUNT", 00141 0, "Event Count") 00142 if self.__status != 0: 00143 raise IOError, "fits_write_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00144 self.__rownum = 1 00145 self.__tablenum += 1 00146 00147 def bcLong2Int(self, datum): 00148 # cfitsio has a problem with putting any long integer with bit 32 set into 00149 # a key. (fits_write_key_ulng does not exist in nasa space) 00150 # do some bizarre ass bit conversions with integer arithmetic to make sure that the type 00151 # stays "int" 00152 if datum & 0x80000000L: 00153 shiftword = -2147483648 # - 2^31, really 0x8000000 expressed as an int 00154 truncated = int(datum & 0x7fffffff) # Slice off bit 31 00155 datum = truncated+shiftword # add on bit 31 00156 return datum 00157 00158 00159 def write(self, rawEventData): 00160 if self.__tablenum == 0: 00161 (datagram_id, datagram_length, version_info) = struct.unpack('!III', rawEventData[:12]) 00162 self.createNewTable(datagram_id, version_info) 00163 elif self.__rownum > self.__tableSize: 00164 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "EVTCOUNT", 00165 self.__rownum - 1, "Event Count") 00166 if self.__status != 0: 00167 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00168 if self.__flushInterval > 0: 00169 self.__status = cfitsio.fits_flush_file(self.__fptr) 00170 if self.__status != 0: 00171 raise IOError, "fits_flush_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00172 self.createNewTable() 00173 l = len(rawEventData) 00174 if self.__format == FORMAT_BYTE: 00175 self.__status = cfitsio.fits_write_col_byt(self.__fptr, 1, self.__rownum, 00176 1, l, rawEventData) 00177 if self.__status != 0: 00178 raise IOError, "fits_write_col_byt: %s" % cfitsio.fits_get_errstatus(self.__status) 00179 else: 00180 extra = l % 4 00181 nelem = (l - extra) / 4 00182 if extra != 0: 00183 data = list(struct.unpack('!'+'i'*nelem, rawEventData[:-extra])) 00184 else: 00185 data = list(struct.unpack('!'+'i'*nelem, rawEventData)) 00186 self.__status = cfitsio.fits_write_col_uint(self.__fptr, 1, self.__rownum, 00187 1, nelem, data) 00188 if self.__status != 0: 00189 raise IOError, "fits_write_col_uint: %s" % cfitsio.fits_get_errstatus(self.__status) 00190 if (self.__flushInterval > 0) and (self.__rownum % self.__flushInterval == 0): 00191 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "EVTCOUNT", 00192 self.__rownum, "Event Count") 00193 if self.__status != 0: 00194 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00195 self.__status = cfitsio.fits_flush_buffer(self.__fptr, 0) 00196 if self.__status != 0: 00197 raise IOError, "fits_flush_buffer: %s" % cfitsio.fits_get_errstatus(self.__status) 00198 self.__rownum +=1 00199 00200 00201 def moveToHDU(self, tablenum): 00202 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, tablenum) 00203 if self.__status != 0: 00204 return self.__status 00205 (self.__status, self.__eventCount, comment) = cfitsio.fits_read_key_lng(self.__fptr, 'EVTCOUNT') 00206 # Skip the exception if there are tables after the event data (202 is keyword not found) 00207 if self.__status != 0 and self.__status != 202: 00208 raise IOError, "fits_read_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00209 return self.__status 00210 00211 00212 def read(self): 00213 self.__rownum += 1 00214 if self.__rownum > self.__eventCount: 00215 self.__tablenum += 1 00216 status = self.moveToHDU(self.__tablenum) 00217 if status != 0: 00218 return None 00219 self.__rownum = 1 00220 (self.__status, event_size, heap_addr) = cfitsio.fits_read_descript(self.__fptr, 1, self.__rownum) 00221 if self.__status != 0: 00222 raise IOError, "fits_read_descript: %s" % cfitsio.fits_get_errstatus(self.__status) 00223 (self.__status, dat) = cfitsio.fits_read_col_byt(self.__fptr, 1, self.__rownum, 1, event_size, None) 00224 if self.__status != 0: 00225 raise IOError, "fits_read_col_byt: %s" % cfitsio.fits_get_errstatus(self.__status) 00226 return dat 00227 00228 00229 def close(self, inShutdown=0): 00230 if self.__mode != MODE_READONLY: 00231 if self.__tablenum > 0: 00232 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "EVTCOUNT", 00233 self.__rownum - 1, "Event Count") 00234 if self.__status != 0: 00235 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00236 if self.__reportGen is not None and not inShutdown: 00237 self.updateRunReportFields() 00238 00239 self.__status = cfitsio.fits_close_file(self.__fptr) 00240 if self.__status != 0: 00241 raise IOError, "fits_close_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00242 self.__fptr = None 00243 00244 def updateRunReportFields(self): 00245 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, 1) 00246 00247 # Write the version for the run report keyword/value dump in case 00248 # we need to modify it in the future 00249 self.__status = cfitsio.fits_write_key_str(self.__fptr, "RPTVERSN", 00250 RUN_REPORT_VERSION, 00251 "Run Report Fields Version") 00252 if self.__status != 0: 00253 raise IOError, "fits_write_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00254 00255 reportFields = self.__reportGen.getReportFields() 00256 reportData = self.__reportGen.getReportData() 00257 reportUnits = self.__reportGen.getReportUnits() 00258 00259 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "NUM_KVS", 00260 len(reportFields), 00261 "Number of Key/Value pairs") 00262 if self.__status != 0: 00263 raise IOError, "fits_write_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00264 00265 for i in range(len(reportFields)): 00266 listType = 0 00267 dictType = 0 00268 x = None 00269 # self.__status = cfitsio.fits_write_key_str(self.__fptr, 00270 # reportShortNames[i], 00271 # reportData[i], 00272 # reportFields[i]) 00273 try: 00274 exec("x=" + reportData[i]) 00275 if type(x) == types.ListType: 00276 listType = 1 00277 elif type(x) == types.DictType: 00278 dictType = 1 00279 except: 00280 #print sys.exc_info() 00281 pass 00282 if listType and len(x) > 0: 00283 ttype = ['list_value'] 00284 tform = ['80A'] 00285 tunit=[' '] 00286 extname= reportFields[i] 00287 tfields=1 00288 self.__status = cfitsio.fits_create_tbl(self.__fptr, cfitsio.BINARY_TBL, 00289 len(x), tfields, ttype, tform, tunit, extname) 00290 if self.__status != 0: 00291 raise IOError, "fits_create_tbl: %s" % cfitsio.fits_get_errstatus(self.__status) 00292 00293 self.__status = cfitsio.fits_write_col_str(self.__fptr, 1, 1, 1, map(str, x)) 00294 if self.__status != 0: 00295 raise IOError, "fits_write_col_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00296 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, 1) 00297 if self.__status != 0: 00298 raise IOError, "fits_movabs_hdu %s" % cfitsio.fits_get_errstatus(self.__status) 00299 elif dictType and len(x) > 0: 00300 ttype = ['dict_key', 'dict_value'] 00301 tform = ['80A','80A'] 00302 tunit=[' ',' '] 00303 extname= reportFields[i] 00304 tfields=2 00305 self.__status = cfitsio.fits_create_tbl(self.__fptr, cfitsio.BINARY_TBL, 00306 len(x), tfields, ttype, tform, tunit, extname) 00307 if self.__status != 0: 00308 raise IOError, "fits_create_tbl: %s" % cfitsio.fits_get_errstatus(self.__status) 00309 00310 self.__status = cfitsio.fits_write_col_str(self.__fptr, 1, 1, 1, map(str, x.keys())) 00311 if self.__status != 0: 00312 raise IOError, "fits_write_col_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00313 self.__status = cfitsio.fits_write_col_str(self.__fptr, 2, 1, 1, map(str, x.values())) 00314 if self.__status != 0: 00315 raise IOError, "fits_write_col_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00316 00317 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, 1) 00318 if self.__status != 0: 00319 raise IOError, "fits_movabs_hdu: %s" % cfitsio.fits_get_errstatus(self.__status) 00320 self.__status = cfitsio.fits_write_key_str(self.__fptr, 00321 "KEY_%04d" % i, 00322 reportFields[i], 00323 "") 00324 if self.__status != 0: 00325 raise IOError, "fits_write_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00326 00327 if (listType or dictType): 00328 if len(x) > 0: 00329 self.__status = cfitsio.fits_write_key_str(self.__fptr, 00330 "VAL_%04d" % i, 00331 "", 00332 "See table '" + reportFields[i] + "'") 00333 else: 00334 self.__status = cfitsio.fits_write_key_str(self.__fptr, 00335 "VAL_%04d" % i, 00336 "", 00337 "") 00338 if self.__status != 0: 00339 raise IOError, "fits_write_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00340 else: 00341 if reportUnits[i] == rcReportGen.UNITS_STRING: 00342 self.__status = cfitsio.fits_write_key_str(self.__fptr, 00343 "VAL_%04d" % i, 00344 reportData[i], 00345 "") 00346 if self.__status != 0: 00347 raise IOError, "fits_write_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00348 00349 elif reportUnits[i] in (rcReportGen.UNITS_INT, rcReportGen.UNITS_LONG): 00350 self.__status = cfitsio.fits_write_key_lng(self.__fptr, 00351 "VAL_%04d" % i, 00352 long(reportData[i]), 00353 "") 00354 if self.__status != 0: 00355 raise IOError, "fits_write_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00356 elif reportUnits[i] == rcReportGen.UNITS_FLOAT: 00357 self.__status = cfitsio.fits_write_key_fixflt(self.__fptr, 00358 "VAL_%04d" % i, 00359 float(reportData[i]), 00360 4, 00361 "") 00362 if self.__status != 0: 00363 raise IOError, "fits_write_key_flt: %s" % cfitsio.fits_get_errstatus(self.__status) 00364 else: 00365 self.__status = cfitsio.fits_write_key_str(self.__fptr, 00366 "VAL_%04d" % i, 00367 reportData[i], 00368 "") 00369 if self.__status != 0: 00370 raise IOError, "fits_write_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00371 00372 00373 def setFlushInterval(self, flushInterval): 00374 self.__flushInterval = flushInterval 00375 00376 def getStatus(self): 00377 return self.__status 00378 00379 def getFileName(self): 00380 return self.__fileName 00381 00382 def getFilePath(self): 00383 return self.__filePath 00384 00385 def getHeaders(self): 00386 """If we are in read only mode and reportGen is provided then read the headers of off the FITS file 00387 00388 """ 00389 (self.__status, nkv, comment) = cfitsio.fits_read_key_lng(self.__fptr, "NUM_KVS") 00390 if self.__status != 0: 00391 raise IOError, "fits_read_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00392 #print "NUMBER OF KEYWORDS=", nkv 00393 for i in range(nkv): 00394 (self.__status, keyValue, comment) = cfitsio.fits_read_key_str(self.__fptr, "KEY_%04d" % i) 00395 if self.__status != 0: 00396 raise IOError, "fits_read_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00397 #print "KEY_%04d=%s %s" % (i,keyValue,comment) 00398 (self.__status, valValue, comment) = cfitsio.fits_read_key_str(self.__fptr, "VAL_%04d" % i) 00399 if self.__status != 0: 00400 raise IOError, "fits_read_key_str: %s" % cfitsio.fits_get_errstatus(self.__status) 00401 #print "VAL_%04d=%s %s" % (i,valValue,comment) 00402 if valValue == '': 00403 #print "Moving to HDU", keyValue 00404 self.__status = cfitsio.fits_movnam_hdu(self.__fptr, cfitsio.ANY_HDU, keyValue, 0) 00405 if self.__status != 0: 00406 #print "fits_movnam_hdu %s" % cfitsio.fits_get_errstatus(self.__status) 00407 self.__reportGen.addField(keyValue, "[]") 00408 else: 00409 repData = self.processTable() 00410 self.__reportGen.addField(keyValue, str(repData)) 00411 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, 1) 00412 else: 00413 self.__reportGen.addField(keyValue, str(valValue)) 00414 00415 def processTable(self): 00416 (self.__status, nCols) = cfitsio.fits_get_num_cols(self.__fptr) 00417 if self.__status != 0: 00418 raise IOError, "fits_get_num_cols %s" % cfitsio.fits_get_errstatus(self.__status) 00419 #print "Number of columns=", nCols 00420 (self.__status, nRows) = cfitsio.fits_get_num_rows(self.__fptr) 00421 if self.__status != 0: 00422 raise IOError, "fits_get_num_rows %s" % cfitsio.fits_get_errstatus(self.__status) 00423 #print "Number of rows=", nRows 00424 if nCols == 1: 00425 (self.__status, repList) = cfitsio.fits_read_col_str(self.__fptr, 1, 1, 1, nRows) 00426 if self.__status != 0: 00427 raise IOError, "fits_read_col_str %s" % cfitsio.fits_get_errstatus(self.__status) 00428 return repList 00429 elif nCols == 2: 00430 (self.__status, keyList) = cfitsio.fits_read_col_str(self.__fptr, 1, 1, 1, nRows) 00431 if self.__status != 0: 00432 raise IOError, "fits_read_col_str %s" % cfitsio.fits_get_errstatus(self.__status) 00433 (self.__status, valList) = cfitsio.fits_read_col_str(self.__fptr, 2, 1, 1, nRows) 00434 if self.__status != 0: 00435 raise IOError, "fits_read_col_str %s" % cfitsio.fits_get_errstatus(self.__status) 00436 repDict={} 00437 for key,value in map(None,keyList,valList): 00438 repDict[key]=value 00439 return repDict 00440 00441 # ----- End rcFitsWriter ------ 00442 00443 def getFileNameFromTS(ts): 00444 return "ebf" + ts + ".fits" 00445