pro fits_read,file_or_fcb,data,header,group_par,noscale=noscale, $ exten_no=exten_no, extname=extname, $ extver=extver, extlevel=extlevel, xtension=xtension, $ no_abort=no_abort, message=message, first=first, last=last, $ group=group, header_only=header_only,data_only=data_only, $ no_pdu=no_pdu, enum = enum, no_unsigned = no_unsigned, pdu=pdu ;+ ; NAME: ; FITS_READ ; PURPOSE: ; To read a FITS file. ; ; CALLING SEQUENCE: ; FITS_READ, filename_or_fcb, data [,header, group_par] ; ; INPUTS: ; FILENAME_OR_FCB - this parameter can be the FITS Control Block (FCB) ; returned by FITS_OPEN or the file name of the FITS file. If ; a file name is supplied, FITS_READ will open the file with ; FITS_OPEN and close the file with FITS_CLOSE before exiting. ; When multiple extensions are to be read from the file, it is ; more efficient for the user to call FITS_OPEN and leave the ; file open until all extensions are read. FPACK ; ( http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) compressed FITS ; files can be read provided that the FPACK software is installed. ; ; OUTPUTS: ; DATA - data array. If /NOSCALE is specified, BSCALE and BZERO ; (if present in the header) will not be used to scale the data. ; If Keywords FIRST and LAST are used to read a portion of the ; data or the heap portion of an extension, no scaling is done ; and data is returned as a 1-D vector. The user can use the IDL ; function REFORM to convert the data to the correct dimensions ; if desired. If /DATA_ONLY is specified, no scaling is done. ; HEADER - FITS Header. The STScI inheritance convention is recognized ; http://fits.gsfc.nasa.gov/registry/inherit/fits_inheritance.txt ; If an extension is read, and the INHERIT keyword exists with a ; value of T, and the /NO_PDU keyword keyword is not supplied, ; then the primary data unit header and the extension header will ; be combined. The header will have the form: ; ; ; BEGIN MAIN HEADER -------------------------------- ; ; BEGIN EXTENSION HEADER --------------------------- ; 1. (Default=0, the first group) ; ; OUTPUT KEYWORD PARAMETERS: ; ENUM - Output extension number that was read. ; MESSAGE = value: Output error message ; ; NOTES: ; Determination or which extension to read. ; case 1: EXTEN_NO specified. EXTEN_NO will give the number of the ; extension to read. The primary data unit is refered ; to as extension 0. If EXTEN_NO is specified, XTENSION, ; EXTNAME, EXTVER, and EXTLEVEL parameters are ignored. ; case 2: if EXTEN_NO is not specified, the first extension ; with the specified XTENSION, EXTNAME, EXTVER, and ; EXTLEVEL will be read. If any of the 4 parameters ; are not specified, they will not be used in the search. ; Setting EXTLEVEL=0, EXTVER=0, EXTNAME='', or ; XTENSION='' is the same as not supplying them. ; case 3: if none of the keyword parameters, EXTEN_NO, XTENSION, ; EXTNAME, EXTVER, or EXTLEVEL are supplied. FITS_READ ; will read the next extension in the file. If the ; primary data unit (PDU), extension 0, is null, the ; first call to FITS_READ will read the first extension ; of the file. ; ; The only way to read a null PDU is to use EXTEN_NO = 0. ; ; If FIRST and LAST are specified, the data is returned without applying ; any scale factors (BSCALE and BZERO) and the data is returned in a ; 1-D vector. This will allow you to read any portion of a multiple ; dimension data set. Once returned, the IDL function REFORM can be ; used to place the correct dimensions on the data. ; ; IMPLICIT IMAGES: FITS_READ will construct an implicit image ; for cases where NAXIS=0 and the NPIX1, NPIX2, and PIXVALUE ; keywords are present. The output image will be: ; image = replicate(PIXVALUE,NPIX1,NPIX2) ; ; FPACK compressed files are always closed and reopened when exiting ; FITS_READ so that the pointer is set to the beginning of the file. (Since ; FPACK files are opened with a bidirectional pipe rather than OPEN, one ; cannot use POINT_LUN to move to a specified position in the file.) ; ; EXAMPLES: ; Read the primary data unit of a FITS file, if it is null read the ; first extension: ; FITS_READ, 'myfile.fits', data, header ; ; Read the first two extensions of a FITS file and the extension with ; EXTNAME = 'FLUX' and EXTVER = 4 ; FITS_OPEN, 'myfile.fits', fcb ; FITS_READ, fcb,data1, header2, exten_no = 1 ; FITS_READ, fcb,data1, header2, exten_no = 2 ; FITS_READ, fcb,data3, header3, extname='flux', extver=4 ; FITS_CLOSE, fcb ; ; Read the sixth image in a data cube for the fourth extension. ; ; FITS_OPEN, 'myfile.fits', fcb ; image_number = 6 ; ns = fcb.axis(0,4) ; nl = fcb.axis(1,4) ; i1 = (ns*nl)*(image_number-1) ; i2 = i2 + ns*nl-1 ; FITS_READ,fcb,image,header,first=i1,last=i2 ; image = reform(image,ns,nl,/overwrite) ; FITS_CLOSE ; ; PROCEDURES USED: ; FITS_CLOSE, FITS_OPEN ; SXADDPAR, SXDELPAR, SXPAR() ; WARNINGS: ; In Sep 2006, FITS_OPEN was modified to open FITS files using the ; /SWAP_IF_LITTLE_ENDIAN keyword to OPEN, so that subsequent routines ; (FITS_READ, FITS_WRITE) did not require any byte swapping. An error ; may result if an pre-Sep 2006 version of FITS_OPEN is used with a ; post Sep 2006 version of FITS_READ, FITS_WRITE or MODFITS. ; HISTORY: ; Written by: D. Lindler, August 1995 ; Avoid use of !ERR W. Landsman August 1999 ; Read unsigned datatypes, added /no_unsigned W. Landsman December 1999 ; Don't call FITS_CLOSE unless fcb is defined W. Landsman January 2000 ; Set BZERO = 0 for unsigned integer data W. Landsman January 2000 ; Only call IEEE_TO_HOST if needed W. Landsman February 2000 ; Ensure EXTEND keyword in primary header W. Landsman April 2001 ; Don't erase ERROR message when closing file W. Landsman April 2002 ; Assume at least V5.1 remove NANValue keyword W. Landsman November 2002 ; Work with compress files (read file size from fcb), ; requires updated (Jan 2003) version of FITS_OPEN W. Landsman Jan 2003 ; Do not modify BSCALE/BZERO for unsigned integers W. Landsman April 2006 ; Asuume FITS_OPEN has opened the file with /SWAP_IF_LITTLE_ENDIAN ; W. Landsman September 2006 ; Fix problem with /DATA_ONLY keyword M.Buie/W.Landsman October 2006 ; Only append primary header if INHERIT=T W. Landsman April 2007 ; Make ndata 64bit for very large files E. Hivon/W. Landsman May 2007 ; Added /PDU keyword to always append primary header W. Landsman June 2007 ; Use PRODUCT to compute # of data points W. Landsman May 2009 ; Make sure FIRST is long64 when computing position W.L. October 2009 ; Read FPACK compressed files, W.L. December 2010 ; Don't assume FCB has a FCOMPRESS tag W.L./Satori UeNO September 2012 ;- ; ;----------------------------------------------------------------------------- compile_opt idl2 ; print calling sequence ; if N_params() eq 0 then begin print,'Syntax - FITS_READ,file_or_fcb,data,header,group_par' print,' Input Keywords: /noscale, exten_no=, extname=, ' print,' extver=, extlevel=, xtension=, /no_abort, ' print,' first, last, group, /header_only, /no_pdu, /pdu' print,' Output Keywords: enum =, message=' return endif ; ; I/O error processing ; on_ioerror,ioerror ; ; set defaults ; message = '' if n_elements(noscale) eq 0 then noscale = 0 if n_elements(exten_no) eq 0 then exten_no = -1 if n_elements(extname) eq 0 then extname = '' if n_elements(extver) eq 0 then extver = 0 if n_elements(extlevel) eq 0 then extlevel = 0 if n_elements(first) eq 0 then first = 0 if n_elements(last) eq 0 then last = 0 if n_elements(no_abort) eq 0 then no_abort = 0 if n_elements(group) eq 0 then group = 0 if n_elements(header_only) eq 0 then header_only = 0 if n_elements(data_only) eq 0 then data_only = 0 if n_elements(no_pdu) eq 0 then no_pdu = 0 if n_elements(pdu) eq 0 then pdu = 0 if n_elements(xtension) eq 0 then xtension = '' ; ; Open file if file name is supplied ; fcbtype = size(file_or_fcb,/type) fcbsize = n_elements(file_or_fcb) if (fcbsize ne 1) or ((fcbtype ne 7) and (fcbtype ne 8)) then begin message = 'Invalid Filename or FCB supplied' goto,error_exit end if fcbtype eq 7 then begin fits_open,file_or_fcb,fcb,no_abort=no_abort,message=message if message NE '' then goto,error_exit end else fcb = file_or_fcb ; ; determine which extension to read ========================================== ; ; case 1: exten_no specified ; enum = exten_no if exten_no le -1 then begin ; ; case 2: extname, extver, or extlevel specified ; if (extname ne '') || (extlevel ne 0) || (extver ne 0) || $ (xtension ne '') then begin ; ; find extensions with supplied extname, extver, extlevel, and xtension ; good = replicate(1b,fcb.nextend+1) if extname ne '' then good = good and $ (strtrim(strupcase(extname)) eq strupcase(fcb.extname)) if xtension ne '' then good = good and $ (strtrim(strupcase(xtension)) eq strupcase(fcb.xtension)) if extver ne 0 then good = good and (extver eq fcb.extver) if extlevel ne 0 then good = good and (extlevel eq fcb.extlevel) good = where(good,ngood) ; ; select first one ; if ngood le 0 then begin message='No extension for given extname, extver, and/or' + $ ' extlevel found' goto,error_exit endif enum = good[0] end else begin ; ; case 3: read next extension ; enum = fcb.last_extension + 1 if (enum eq 0) && (fcb.naxis[0] eq 0) then enum = 1 end end ; ; check to see if it is a valid extension ; if enum gt fcb.nextend then begin message='EOF encountered' goto,error_exit end ; ; extract information from FCB for the extension ; bitpix = fcb.bitpix[enum] naxis = fcb.naxis[enum] if naxis gt 0 then axis = fcb.axis[0:naxis-1,enum] gcount = fcb.gcount[enum] pcount = fcb.pcount[enum] xtension = fcb.xtension[enum] fcompress = tag_exist(fcb,'fcompress') ? fcb.fcompress : 0 ; ; read header ================================================================ ; if data_only then goto,read_data h = bytarr(80,36,/nozero) nbytes_in_file = fcb.nbytes position = fcb.start_header[enum] if fcompress then mrd_skip,fcb.unit,position else $ point_lun,fcb.unit,position first_block = 1 ; first block in header flag repeat begin if position ge nbytes_in_file then begin message = 'EOF encountered while reading header' goto,error_exit endif readu,fcb.unit,h position += 2880 hdr = string(h>32b) endline = where(strcmp(hdr,'END ',8),nend) if nend gt 0 then hdr = hdr[0:endline[0]] if first_block then header = hdr else header = [header,hdr] first_block = 0 end until (nend gt 0) ; ; extract some header information ; bscale = sxpar(header,'bscale', Count = N_bscale) bzero = sxpar(header,'bzero', Count = N_bzero) if bscale eq 0.0 then bscale = 1.0 unsgn_int = (bitpix EQ 16) && (Bzero EQ 32768) && (bscale EQ 1) unsgn_lng = (bitpix EQ 32) && (Bzero EQ 2147483648) && (bscale EQ 1) if (unsgn_int || unsgn_lng) then $ if ~keyword_set(no_unsigned) then noscale = 1 if (N_bscale gt 0) &&(noscale eq 0) && (data_only eq 0) && $ (last eq 0) && (header_only eq 0) then sxaddpar,header,'bscale',1.0 if (N_bzero gt 0) && (noscale eq 0) && (data_only eq 0) && $ (last eq 0) && (header_only eq 0) then sxaddpar,header,'bzero',0.0 groups = sxpar(header,'groups') ; ; create header with form: ; ! Required Keywords ; ! BEGIN MAIN HEADER ------------------------------------------ ; ! Primary data unit header keywords ; ! BEGIN EXTENSION HEADER ------------------------------------- ; ! Extension header keywords ; ! END ; ; ; add Primary Data Unit header to it portion of the header to it, unless the ; NO_PDU keyword is set, or the INHERIT keyword is not found or set to false ; if no_pdu EQ 0 then no_pdu = 1 - (sxpar(header,'INHERIT') > 0) if pdu then no_pdu = 0 if (no_pdu eq 0) && (enum gt 0) then begin ; ; delete required keywords ; sxdelpar,header,['SIMPLE','BITPIX','NAXIS','NAXIS1', $ 'NAXIS2','NAXIS3','NAXIS4','NAXIS5', $ 'NAXIS6','NAXIS7','NAXIS8','EXTEND', $ 'PCOUNT','GCOUNT','GROUPS', $ 'XTENSION'] ; create required keywords ; hreq = strarr(20) hreq[0] = 'END ' if enum eq 0 then $ sxaddpar,hreq,'SIMPLE','T','image conforms to FITS standard' $ else sxaddpar,hreq,'XTENSION',xtension,'extension type' sxaddpar,hreq,'bitpix',bitpix,'bits per data value' sxaddpar,hreq,'naxis',naxis,'number of axes' if naxis gt 0 then for i=1,naxis do $ sxaddpar,hreq,'naxis'+strtrim(i,2),axis[i-1] if (enum eq 0) && (fcb.nextend GE 1) then $ sxaddpar,hreq,'EXTEND','T','file may contain extensions' if groups then sxaddpar,hreq,'GROUPS','T','Group format' if (enum gt 0) || (pcount gt 0) then $ sxaddpar,hreq,'PCOUNT',pcount,'Number of group parameters' if (enum gt 0) || (gcount gt 0) then $ sxaddpar,hreq,'GCOUNT',gcount,'Number of groups' n0 = where(strcmp(hreq,'END ',8)) & n0=n0[0] hpdu = fcb.hmain n1 = n_elements(hpdu) if n1 gt 1 then begin hreq = [hreq[0:n0-1], $ 'BEGIN MAIN HEADER ---------------------------------', $ hpdu[0:n1-2], $ 'BEGIN EXTENSION HEADER ----------------------------', $ 'END '] n0 = n0 + n1 + 1 end ; ; add extension header ; header = [hreq[0:n0-1],header] end if header_only then begin data = 0 goto,done endif ; ; Read Data =================================================================== ; read_data: if naxis eq 0 then begin ;null image? data = 0 ; ; check for implicit data specified by NPIX1, NPIX2, and PIXVALUE (provided ; the header was red, i.e. data_only was not specified) ; if data_only eq 0 then begin NPIX1 = sxpar(header,'NPIX1') NPIX2 = sxpar(header,'NPIX2') PIXVALUE = sxpar(header,'PIXVALUE') if (NPIX1*NPIX2) gt 0 then $ data = replicate(pixvalue,npix1,npix2) end goto,done endif case BITPIX of 8: IDL_type = 1 ; Byte 16: IDL_type = 2 ; Integer*2 32: IDL_type = 3 ; Integer*4 -32: IDL_type = 4 ; Real*4 -64: IDL_type = 5 ; Real*8 else: begin message = 'ERROR - Illegal value of BITPIX (= ' + $ strtrim(bitpix,2) + ') in FITS header' goto,error_exit end endcase ndata = product( axis, /integer ) bytes_per_word = (abs(bitpix)/8) nbytes_per_group = bytes_per_word * (pcount + ndata) nbytes = (gcount>1) * nbytes_per_group nwords = nbytes / bytes_per_word ; ; starting data position ; skip = fcb.start_data[enum] - position position = fcb.start_data[enum] ; ; find correct group ; if last eq 0 then begin if group ge (gcount>1) then begin message='INVALID group number specified' goto,error_exit end skip += long64(group) * nbytes_per_group position += skip end ; ; read group parameters ; if (enum eq 0) && (fcb.random_groups eq 1) && (pcount gt 0) && $ (last eq 0) then begin if N_params() gt 3 then begin group_par = make_array( dim = [pcount], type = idl_type, /nozero) if fcompress then mrd_skip,fcb.unit,skip else $ point_lun,fcb.unit,position readu,fcb.unit,group_par endif skip = long64(pcount) * bytes_per_word position += skip endif ; ; create data array ; if last gt 0 then begin ; ; user specified first and last ; if (first lt 0) || (last le 1) || (first gt last) || $ (last gt nwords-1) then begin message = 'INVALID value for parameters FIRST & LAST' goto,error_exit endif data = make_array(dim = [last-first+1], type=idl_type, /nozero) skip += long64(first) * bytes_per_word position += skip endif else begin ; ; full array ; if ndata eq 0 then begin data = 0 goto,done endif if naxis gt 8 then begin message = 'Maximum value of NAXIS allowed is 8' goto,error_exit endif data = make_array(dim = axis, type = idl_type, /nozero) endelse ; ; read array ; if fcompress then mrd_skip,fcb.unit,skip else $ point_lun,fcb.unit,position readu,fcb.unit,data if fcompress then swap_endian_inplace,data,/swap_if_little if ~keyword_set(No_Unsigned) && (~data_only) then begin if unsgn_int then begin data = uint(data) - uint(32768) endif else if unsgn_lng then begin data = ulong(data) - ulong(2147483648) endif endif ; ; scale data if header was read and first and last not used. Do a special ; check of an unsigned integer (BZERO = 2^15) or unsigned long (BZERO = 2^31) ; if (data_only eq 0) && (last eq 0) && (noscale eq 0) then begin if bitpix lt 32 then begin ;use real*4 for bitpix<32 bscale = float(bscale) bzero = float(bzero) endif if bscale ne 1.0 then data *= bscale if bzero ne 0.0 then data += bzero endif ; ; done ; done: if fcompress then begin free_lun,fcb.unit ff = strmid(fcb.filename,1,strlen(fcb.filename)-2) spawn,ff,unit=unit,/sh, stderr = stderr fcb.unit = unit endif else $ if fcbtype eq 7 then fits_close,fcb else file_or_fcb.last_extension=enum !err = 1 return ; ; error exit ; ioerror: message = !ERROR_STATE.MSG error_exit: if (fcbtype eq 7) && (N_elements(fcb) GT 0) then $ fits_close,fcb, no_abort=no_abort !err = -1 if keyword_set(no_abort) then return print,'FITS_READ ERROR: '+message retall end