; NAME: ; pro darkcurrent_reduce.pro (v2.11) ; ; PURPOSE: ; This program takes a images listed in filename and produces estimates of ; dark current from them. A per-pixel dark current frame is produced, and ; individual dark current slope plots can optionally be created. ; ; CALLING SEQUENCE: ; darkcurrent_reduce, testdir, [outdir [, infile [, valoutfile $ ; [, noref [, ma_refcor [, region [,imgoutfile] [, groupfactor]]]]]] ; ; INPUTS: ; testdir string containing directory name wherein input images and a ; list of the image file names can be found ; ; KEYWORD PARAMETERS: ; outdir directory where output will be written; defaults to ; testdir + '\SlopePlots' ; infile input file name. File must be in directory specified by ; testdir. Default name is 'dark.lst' ; valoutfile name for output file containing read-by-read mode values. ; Will be written to outdir. Default is 'slopevals.txt'. ; imgoutfile name for output file containing names of slope images. ; Will be written to outdir. Default is 'slopeimgs.txt'. ; region rectangular region of SCA to consider for analysis. Should ; be entered as 4-element 1-d integer array, i.e. ; [x1,x2,y1,y2] to consider pixels in rows x1 to x2 and ; columns y1 to y2. Default is specified as all non-reference ; pixels ; ma_refcor set for maximal averaging reference pixel correction ; noref set to disable reference pixel correction ; gdmask set equal to the name of a good pixel mask. 1=good & 0=bad ; groupfactor sets number of reads placed together in a group for ; processing; defaults to 1 (which means mode of each read ; is determined). ; ; ; NOTE: Default behavior is to do spatial average reference pixel correction. ; If both ma_refcor and noref are set, the noref keyword over-rides the ; ma_refcor keyword and no reference pixel correction will be done. ; ; EXAMPLE ; ; Reduce data from specified directory, using default file names, ; generating slope plots and using maximal averaging reference pixel ; correction, using default region. ; ; IDL> dkdir = '\\rabbit\rockwell3\H2RG-006-5.0mu\cold1\dktest.3Apr03' ; IDL> darkcurrent_reduce, dkdir, /ma_refcor ; ; REFERENCE: ; ; ; MODIFICATION HISTORY: ; Written by: ; Don Figer, IDTL, May 3, 2003 ; ; Modified by: ; Don Figer, IDTL, May 5, 2002 ; Added output switches ; ; Bernie Rauscher, IDTL, August 5, 2002 ; Added reference pixel ; correction, glow correction, and spiffy line-fit plots. ; ; Ernie Morse, IDTL, October 21, 2002 (v1.1) ; Modified code so that choosing bjrmods option does ONLY the bjrmods ; section, and not the rest of the procedure ; Added code to create output file for use by darkjumbo2.pro ; Added readtime and month2days functions for use in creating ; darkjumbo output file ; Increased min and max histogram bin limits--they were too small for ; higher-signal images ; ; Ernie Morse, IDTL, October 31, 2002 (v1.2) ; Modified bjrmods section so that PostScript output files are put in ; outdir, not the current directory. ; ; Ernie Morse, IDTL, Nov. 7, 2002 (v1.2.1) ; Now gets SCA type from header, instead of assuming H1RG ; Calls refsub with SCA type instead of refsub_h1rg directly ; ; Ernie Morse, IDTL, January 7, 2003 (v1.2.2) ; Changed BJRMODS histogram setup so that histogram bins are ; centered around median signal in data instead of around 0. ; In the previous method, the actual distribution curve tended to ; move off the edge of the histogram range as signal increased and ; there were a lot of empty bins with negative values. ; ; Ernie Morse, IDTL, February 10-12, 2003 (v1.3) ; Changed reading of FITS files to frame-at-a-time to allow for ; reading arbitrarily large files to prevent limitations imposed by ; the amount of RAM available. ; Added region keyword to allow specification of custom region. ; Removed hard-coded assumptions that array size is 1024 x 1024 ; (array size now taken from header keywords). ; Removed hard-coded assumptions of analysis region. ; Changed plot parameters (increased line & char size & thickness, ; only print det. temp. to 1 mK accuracy). ; ; Ernie Morse, IDTL, March 5, 2003 (v1.3) ; Added keywords to allow specification of type of reference pixel ; correction ; Added information about analysis region and reference pixel ; correction to plots. ; ; Ernie Morse, IDTL, March 13, 2003 (v1.3) ; Added name of file to bottom margin area of slopefit plots in ; bjrmods section. ; ; Ernie Morse, IDTL, April 4, 2003 (v2.0) ; Started all over from scratch. ; ; Ernie Morse, IDTL, April 8, 2003 (v2.1) ; Cast values read in from headers to int and float data types to ; insure that the slope array ; written out at the end will be a float array in order to decrease ; size of the resulting FITS file. ; ; B.J. Rauscher, IDTL, April 10, 2003 (v2.2) ; Added ability to use good pixel masks ; ; Ernie Morse, IDTL, April 11, 2003 (v2.3) ; Changed calls to refsub--must now pass SCA name instead of SCA ; type ; ; Ernie Morse, IDTL, April 14, 2003 (v2.4) ; Removed the bin representing 0 from the gaussfit to protect against ; spurious zeros caused by railed pixels. ; ; Ernie Morse, IDTL, April 16, 2003 (v2.5) ; Corrected bug found in implementation of zero-bin rejection. ; ; Ernie Morse, IDTL, April 30, 2003 (v2.6) ; Added the rd_time keyword and code to allow the processing of the ; Fowler glow images. ; ; Ernie Morse, IDTL, May 9, 2003 (v2.7) ; Eliminated sa_refcor keyword--spatial average reference pixel ; correction is now default ; Added noref keyword--can be set to explicitly disable reference ; pixel correction ; Made changes in code to reflect new default case (spatial averaging). ; Changed default output directory to testdir+'SlopePlots' ; ; Ernie Morse, IDTL, May 15, 2003 (v2.8) ; Added write of reference pixel correction version number and type ; to header of slope images ; ; Ernie Morse, IDTL, October 10, 2003 (v2.9) ; Added cosmic ray detection to creation of per-pixel images ; ; Ernie Morse, IDTL, October 17, 2003 (v2.10) ; Added groupfactor keyword and modifications to allow grouping ; of reads for processing. Split outfile keyword into valoutfile and ; imgoutfile. Made some changes to format of valoutfile (it now ; must include read times as they can not be calculated in the ; analysis script anymore due to the addition of grouping). ; ; Ernie Morse, IDTL, December 2, 2003 (v2.11) ; Corrected bugs associated with operation when groupfactor = 1. ; Added graceful exit for case when all pixels in input image are ; railed. ; ; Ernie Morse, IDTL, July 15, 2004(v2.12) ; Changed from using binsize = 1.0 to starting out with 128 bins with ; size determined by data distribution. ; Added code to catch math errors (from curvefit) ; Corrected problems that arose when first reads of input image are ; at bottom rail. ; Ernie Morse, IDTL, July 22, 2004 (v2.13) ; Added rowinc and colinc keywords. ; ;1234567890123456789012345678901234567890123456789012345678901234567890123456789 Pro darkcurrent_reduce, testdir, outdir=outdir, infile=infile, $ valoutfile=valoutfile, imgoutfile=imgoutfile, ma_refcor=ma_refcor, $ noref=noref, region=region, gdmask = gdmask, groupfactor=groupfactor, $ rowinc = rowinc, colinc = colinc, noimgout = noimgout ; set directory path delimiter & end-of-line character based on ; operating system in use (non-unix is assumed to be Windows). if (!version.os_family eq 'unix') then begin pathdelim = '/' endofline = string(10b) endif else begin pathdelim = '\' endofline = string(13b) + string(10b) endelse ; check to see if input directory name ends in path delimiter--if not, ; add it to the end if (strmid(testdir, strlen(testdir)-1, 1) ne pathdelim) then begin testdir = testdir + pathdelim endif ; set outdir to testdir + 'SlopePlots' if not set by user if (keyword_set(outdir) eq 0) then begin outdir = testdir + 'SlopePlots' + pathdelim endif ; check to see if outdirectory name ends in path delimiter--if not, ; add it to the end if (strmid(outdir, strlen(outdir)-1, 1) ne pathdelim) then begin outdir = outdir + pathdelim endif ; set defaults for keyword values not provided by the calling context if (not keyword_set(infile)) then begin infile = 'dark.lst' endif if (not keyword_set(valoutfile)) then begin valoutfile = 'slopevals.txt' endif if (not keyword_set(imgoutfile)) then begin imgoutfile = 'slopeimgs.txt' endif if (not keyword_set(groupfactor)) then begin groupfactor = 1 endif ; If reference pixel correction is to be done, query the refsub function for ; the version number, otherwise, just set a flag indicating it was not done if (not keyword_set(noref)) then begin ref_ver = ' ' refsub, 0, sca=' ', version = ref_ver if (not keyword_set(ma_refcor)) then begin ref_type = 'Spatial averaging' endif else begin ref_type = 'Maximal averaging' endelse endif else begin ref_ver = 'Not used' ref_type = 'N/A' endelse if (not keyword_set(rowinc)) then begin rowinc = 1 endif if (not keyword_set(colinc)) then begin colinc = 1 endif ; open input file and read all filenames into an array readcol, testdir + infile, imgfiles, format='(A)' ; open output file for slope values openw, lun_val, outdir + valoutfile, /get_lun if (not keyword_set(noimgout)) then begin ; make an array of structs to hold filename and detector temperature ; for output slope images imginfo = replicate({name:'', dettemp:''}, n_elements(imgfiles)) ; open output file for names of slope images, unless noimgout keyword ; is set openw, lun_img, outdir + imgoutfile, /get_lun endif ; start by reading just the header of the first image in the list to get ; some required information fits_read, testdir + imgfiles[0], 0, firsthdr, /header_only xsize = fix(sxpar(firsthdr, 'naxis1')) ysize = fix(sxpar(firsthdr, 'naxis2')) scaName = sxpar(firsthdr, 'detname') ; define the number of pixels to read in for one group of sample reads numpixrd = long(xsize) * ysize ; extract SCA type from SCA name scaType = strmid(scaName, 0, strpos(scaName, '-')) ; if the user didn't set the region, define default regions based on the ; SCA type if (not keyword_set(region)) then begin case scaType OF 'H1RG' : begin region = [4, 1019, 4, 1019] end 'H2RG' : begin region = [4, 2043, 4, 2043] end 'SB304': begin region = [4, 2051, 0, 2047] end ELSE : begin print, 'Detector type ' + scaType + $ ' not supported. Exiting procedure.' return end endcase endif ; construct a string representation of the region array region_str = strtrim(string(region), 2) region_out = '[' + region_str[0] + ':' + region_str[1] + ':' $ + strtrim(string(colinc), 2) + ', ' + region_str[2] + ':' $ + region_str[3] + ':' + strtrim(string(rowinc), 2) + ']' ; write values common to all the images to the header of the output file printf, lun_val, ref_ver printf, lun_val, ref_type printf, lun_val, region_out ; construct arrays for cosmic ray detection if (not keyword_set(noimgout)) then begin mask10sig = intarr(xsize, ysize) endif ; loop through images listed in input file and process them one frame at ; a time for imgcount = 0, n_elements(imgfiles) - 1 do begin ; To keep it simple, the standard formula from Numerical ; Recipes will be used to calculate slope per pixel in an incremental ; fashion. ; ; slope = N * S[x_i * y_i] - S[x_i] * S[y_i] / ( N * S[x_i^2] - ; S[x_i]^2 ) ; First initialize all sums that we will need. To keep it ; simple, I'll keep some redundant sums (e.g., the ; information already is available to the program) ; declare these running-sum arrays for each file so they'll be zeroed ; at the start each time if (not keyword_set(noimgout)) then begin Sxy = dblarr(xsize, ysize) Sx = dblarr(xsize, ysize) Sy = dblarr(xsize, ysize) Sxx = dblarr(xsize, ysize) ; set up array used to hold previous read for cosmic ray detection previousgroup = fltarr(xsize,ysize) mask10sig[*] = 0 endif ; read the 1st frame fits_read, testdir + imgfiles[imgcount], 0, imghdr, /header_only ; extract information from header exposuretime = double(sxpar(imghdr, 'exp_time')) numreads = fix(sxpar(imghdr, 'numutr')) samples_per_read = fix(sxpar(imghdr, 'numsamrd')) dettemp = float(sxpar(imghdr, 'dettemp')) ; find out how many frame groups there are numframes = numreads * samples_per_read numgroups = ceil(float(numframes) / groupfactor) groupends = indgen(numgroups) * groupfactor + (groupfactor - 1) groupends[numgroups - 1] = groupends[numgroups - 1] < (numframes - 1) groupstart = 0 ; create an array containing the time for each group of reads readtimes = dindgen(groupfactor, numgroups) / (numreads - 1) * $ exposuretime ; if groupfactor > 1 and the last group isn't completely filled ; (i.e., the number of reads in the exposure is not evenly ; divisible by the groupfactor), the readtimes array will contain ; exposure times for reads that aren't present because it assumes that ; all groups contain the same number of reads. Mark these invalid ; exposure times as NaN so they can be ignored in processing. overtimes = where(readtimes gt exposuretime) if (overtimes[0] ne -1) then begin readtimes[overtimes] = !values.f_nan endif ; make an array to hold the exposure times for each group. Each ; element is the mean value of the exposure times of the reads ; contained in the group. grouptimes = fltarr(numgroups) for groupnum = 0, numgroups - 1 do begin grouptimes[groupnum] = mean(readtimes[*, groupnum], /nan) endfor ; read the frame groups in until the 1st non-railed group is encountered groupnum = -1 repeat begin groupnum = groupnum + 1 fits_read, testdir + imgfiles[imgcount], firstgroup, $ first = (groupends[groupnum]) * numpixrd, $ last = (groupends[groupnum + 1]) * numpixrd - 1 ; transform the first group of reads into an average framecount = groupends[groupnum + 1] - groupends[groupnum] firstgroup = reform(float(firstgroup), xsize, ysize, framecount) firstgroup = total(firstgroup, 3) / framecount regionpix = firstgroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc] lowrailpix = where(regionpix lt -32760) hirailpix = where(regionpix gt 32760) lowfraction = n_elements(lowrailpix) / float(n_elements(regionpix)) hifraction = n_elements(hirailpix) / float(n_elements(regionpix)) hi_or_low = hifraction ge lowfraction ? 32767.0 : -32768.0 endrep until ((lowfraction le 0.5 and hifraction le 0.5) $ OR (groupnum eq (numgroups - 2))) ; skip to next image if all reads are railed low if (groupnum eq numgroups - 2) then begin ; make an array to hold the output combination of group time and ; corresponding mode value and raw signal outcombo = fltarr(3, numgroups) outcombo[0, *] = grouptimes outcombo[2, *] = hi_or_low ; write out time and signal values for each read to the file, ; darkcurrent_analyze can be called later to make slope plots. printf, lun_val, testdir+imgfiles[imgcount] + ' ' + $ strtrim(string(numgroups), 2) + ' ' + $ strtrim(string(groupfactor), 2) printf, lun_val, outcombo, format='(F14.3, F14.3, F14.3)' printf, lun_val ; print blank space to separate data for ; different images continue endif ; the first group is the zero-level for the dark current, so adjust ; the grouptimes to be relative to the time of the first group firstgroupnum = groupnum starttime = grouptimes[firstgroupnum] grouptimes = grouptimes - starttime ; find out where the railed pixels are railedpix = where(firstgroup gt 32760 OR firstgroup lt -32760) ; exit the procedure if every pixel in the read is railed if (n_elements(railedpix) eq n_elements(firstgroup)) then begin stop, 'Processing cannot continue because every pixel in ' + $ 'input image is railed at the top or bottom of the A-D range.' endif ; declare array to hold median and mode values theMode = (theMedian = fltarr(numgroups)) ; declare array to hold raw median signal for each read group rawsig = fltarr(numgroups) rawsig[firstgroupnum] = median(firstgroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc]) if (firstgroupnum ne 0) then begin rawsig[0:firstgroupnum - 1] = -32768 endif ; loop through the reads of an individual input file for groupcount = firstgroupnum + 1, numgroups - 1 do begin ; read all the frames composing one sample group fits_read, testdir + imgfiles[imgcount], scagroup, $ first = (groupends[groupcount - 1] + 1) * numpixrd, $ last = (groupends[groupcount] + 1) * numpixrd - 1 ; transform the group of reads into an average framecount = groupends[groupcount] - groupends[groupcount - 1] scagroup = reform(float(scagroup), xsize, ysize, framecount) scagroup = total(scagroup, 3) / framecount ; record the median signal rawsig[groupcount] = median(scagroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc]) ; subtract off the first group as bias correction scagroup = scagroup - firstgroup ; if it was requested, perform reference pixel correction on all ; reads if (not keyword_set(noref)) then begin print, 'Applying reference pixel correction to ' + $ imgfiles[imgcount] + ', read # ' + $ strtrim(string(groupcount),2) if (not keyword_set(ma_refcor)) then begin refsub, scagroup, scaName=scaName endif else begin refsub, scagroup, scaName=scaName, /simple endelse endif else begin print, 'No ref. pixel correction for ' + imgfiles[imgcount] + $ ', read #' + strtrim(string(groupcount),2) endelse ; do the incremental slope calculation operations if (not keyword_set(noimgout)) then begin ; do cosmic ray detection diffFrame = scagroup - previousgroup crsdev = stddev(diffFrame[4:xsize-5, 4:ysize-5]) crmed = median(diffFrame[4:xsize-5, 4:ysize-5]) outliers = where(diffFrame gt (crmed + crsdev*10)) if (outliers[0] ne -1) then begin mask10sig[outliers] = mask10sig[outliers] + 1 endif previousgroup = scagroup Sxy = Sxy + grouptimes[groupcount] * scagroup Sx = Sx + grouptimes[groupcount] Sy = Sy + scagroup Sxx = Sxx + grouptimes[groupcount] ^ 2 endif ; begin the process of finding signal values for individual reads theMedian[groupcount] = $ median(scagroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc]) sdev = stddev(scagroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc]) ; We want to find the mode. The easiest way to do this is to fit a ; gaussian to a histogram. ; set min and max histogram bins, centered around the median of the ; data ;_binsize = 1.0 ;_min = long((theMedian[groupcount] - (sdev * 4))) + _binsize / 2.0 ;_max = long((theMedian[groupcount] + (sdev * 4))) - _binsize / 2.0 _min = long((theMedian[groupcount] - (sdev * 4))) _max = long((theMedian[groupcount] + (sdev * 4))) _nbins = 128 ; Only the non-railed pixels should be considered, so set the ; railed ones to NaN if (railedpix[0] ne -1) then begin scagroup[railedpix] = !values.f_nan endif ; Make a histogram using either all of the data, or only data where ; gdmask says to. math_err_stat = check_math(mask = 208) math_errcount = 0 catch, errval if (errval ne 0) then begin ;_binsize = _binsize * 2 _nbins = _nbins / 2 message, /reset math_errcount = math_errcount + 1 endif if (not keyword_set(gdmask)) then begin ; If not using the mask, look at all pixels ;theHist = $ ;histogram(scagroup[region[0]:region[1], $ ;region[2]:region[3]], binsize=_binsize, min=_min, $ ;max=_max, /nan) theHist = histogram(scagroup[region[0]:region[1]:colinc, $ region[2]:region[3]:rowinc], nbins = _nbins, $ min = _min, max = _max, /nan, locations = theVals) endif else begin ; If using the mask, look only at masked pixels in ; photo-sensitive region fits_read, gdmask, gdmask_data, gdmask_header, /data_only ;theHist = histogram(scagroup[where(gdmask_data eq 1)], $ ; binsize=_binsize, min=_min, max=_max, /nan) theHist = histogram(scagroup[where(gdmask_data eq 1)], $ nbins = _nbins, min = _min, max = _max, /nan, $ locations = theVals) endelse ; determine the corresponding values for the histogram bins ;theVals = findgen(n_elements(theHist)) * _binsize + _min + $ ; _binsize / 2.0 ; adjust values to center of bins theVals = theVals + ((_max - _min) / float(_nbins - 1)) / 2.0 ; fit the histogram counts and values to a gaussian theResult = gaussfit(theVals, theHist, A) math_err_stat = check_math(mask = 208) wait, 2 if (math_err_stat ne 0 and math_errcount lt 5) then begin message, !error_state.msg endif ; we want the mode, which is element 1 of the A array returned from ; gaussfit print, math_errcount, a[1] theMode[groupcount] = A[1] endfor ; make an array to hold the output combination of group time and ; corresponding mode value and raw signal outcombo = fltarr(3, n_elements(theMode)) outcombo[0, *] = grouptimes + starttime outcombo[1, *] = theMode outcombo[2, *] = rawsig ; write out time and signal values for each read to the file, ; darkcurrent_analyze can be called later to make slope plots. printf, lun_val, testdir+imgfiles[imgcount] + ' ' + $ strtrim(string(numgroups), 2) + ' ' + $ strtrim(string(groupfactor), 2) printf, lun_val, outcombo, format='(F14.3, F14.3, F14.3)' printf, lun_val ; print blank space to separate data for ; different images ; calculate the final per-pixel slope values if (not keyword_set(noimgout)) then begin outarray = (numgroups * Sxy - Sx * Sy) / $ (numgroups * Sxx - Sx * Sx) ; change output array to float to make output FITS file smaller outarray = float(outarray) ; set the detected cosmic rays/hot pixels to NaN if (mask10sig[0] ne -1) then begin outarray[where(mask10sig ne 0)] = !values.f_nan endif ; correct values in the original header to indicate 1 frame of float ; values sxaddpar, imghdr, 'naxis3', 1 sxaddpar, imghdr, 'bitpix', -32 sxaddpar, imghdr, 'refver', ref_ver sxaddpar, imghdr, 'reftype', ref_type ; write out slope array with filename equal to original filename ; with '_slope' inserted prior to '.fits' nameparts = strsplit(imgfiles[imgcount], '.', /extract) outname = strjoin(nameparts[0:n_elements(nameparts)-2], '.') + $ '_slope.fits' fits_write, outdir + outname, outarray, imghdr ; enter the output name and detector temperature (rounded to ; nearest 0.1 K) into imginfo array imginfo[imgcount].name = outname imginfo[imgcount].dettemp = string(dettemp, format='(F5.1)') endif endfor if (not keyword_set(noimgout)) then begin ; sort the imginfo struct array by the detector temperature field imginfo = imginfo[sort(imginfo.dettemp)] ; find the lengths of all the filenames and set the length of the field ; for all names to the maximum value + 5 characters (for output ; formatting) namelen = strlen(imginfo.name) fieldlen = max(namelen) + 5 for i=0, n_elements(imginfo) - 1 do begin imginfo[i].name = imginfo[i].name + string(make_array(fieldlen - $ strlen(imginfo[i].name), /byte, val=32b)) endfor ; add EOL character(s) to output dettemp so that output will not all be ; on the same line imginfo.dettemp = imginfo.dettemp + endofline ; write the imginfo struct array out to the image list file writeu, lun_img, imginfo.name + endofline free_lun, lun_img endif ; close the slope values output files free_lun, lun_val End