; NAME:
;   pro darkcurrent_reduce.pro (v2.16)
;
; 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.
;       Ernie Morse, IDTL, September 29, 2004 (v2.14)
;           Modified error handler so that math errors are distinguished from
;           non-math errors.
;       Ernie Morse, IDTL, October 8, 2004 (v2.15)
;           Made changes to prevent blank lines being written to imgoutfile for
;           input images that are railed in every read.  Moved opening of
;           imgoutfile to end of procedure, conditional on there being at least
;           one non-railed image and trimmed imginfo array to only include
;           entries with non-null file names.
;       Ernie Morse, IDTL, September 2, 2005 (v2.16)
;           Now uses djsiterstat to determine median and standard
;           deviation, which is used to calculate min and max
;           for histogram procedure.  Number of bins has been altered so
;           that bin size = 1.0 ADU.
;
Pro darkcurrent_reduce2, 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

    ; set directory path delimiter
    pathdelim=path_sep()

    ; 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
        endofline = string(10b)
    endif else begin
        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 testdir = testdir + pathdelim

    ; set outdir to testdir + 'SlopePlots' if not set by user
    If (keyword_set(outdir) eq 0) then outdir = testdir + 'SlopePlots' + pathdelim

    ; 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 outdir = outdir + pathdelim

    ; set defaults for keyword values not provided by the calling context
    If (not keyword_set(infile)) then infile = 'dark.lst'
    If (not keyword_set(valoutfile)) then valoutfile = 'slopevals.txt'
    If (not keyword_set(imgoutfile)) then imgoutfile = 'slopeimgs.txt'
    If (not keyword_set(groupfactor)) then groupfactor = 1

    ; 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)'

    ; 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 slope values
    file_mkdir,Outdir
    openw, Lun_Val, Outdir + Valoutfile, /get_lun

    ; 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 stregex(SCAName,'HRG-10-007', /boolean) or SCAName eq ' ' then $
	SCAType = 'H4RG'
    ; 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
            'H4RG' : begin
                Region = [4, 4091, 4, 4091]
		SCAName = 'H4RG-10-007'
            end
            '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
    Mask10Sig = intarr(Xsize, Ysize)
    CosmicRays = fltarr(Xsize, Ysize)

    ; read mask array if present, otherwise construct mask of all 1s.
    if (keyword_set(GDMask)) then begin
        Fits_Read, GDMask, GDMask_Data, GDMask_Header, /data_only
    endif else begin
        GDMask_Data = make_array(Xsize, Ysize, /integer, value=1)
    endelse

    ; Form a region mask where the reference pixels are not located
    Region_Mask = GDMask_Data[Region[0]:Region[1]:Colinc, $
            Region[2]:Region[3]:Rowinc]

    ; 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
        Sxy = dblarr(xsize, ysize)
        Sx = dblarr(xsize, ysize)
        Sy = dblarr(xsize, ysize)
        Sxx = dblarr(xsize, ysize)

        ; read the 1st frame
        Fits_Read, Testdir + ImgFiles[Imgcount], 0, ImgHdr, /header_only

        ; extract information from header
        If SCAType ne 'H4RG' then begin
	        Exposuretime = double(sxpar(imghdr, 'exp_time'))
	        Numreads = fix(sxpar(imghdr, 'numutr'))
	        Samples_Per_Read = fix(sxpar(imghdr, 'numsamrd'))
	        Dettemp = float(sxpar(imghdr, 'dettemp'))
	        ; Evidently, the last few reads in the dark current experiment
	        ; for H2RG-003-SIPIN are corrupted. This can be seen by 
	        ; displaying the corresponding images. In order to address 
		; this problem, I (Don) added the following lines of code 
		; that throw away the last 10 reads.
	        if (SCAname eq 'H2RG-003-SIPIN' and NumReads gt 12) then $
			numreads = numreads - 10
	Endif Else Begin
	        Numreads = Fix(sxpar(ImgHdr, 'NGROUPS'))
	        Samples_Per_Read = Fix(sxpar(ImgHdr, 'NREADS'))
	        Ndrops = Fix(sxpar(ImgHdr, 'NDROPS'))
	        DetTemp = float(sxpar(ImgHdr, 'MOLYTABL'))
	        ExposureTime = 4096.*4096./32.*10.e-6*$
			       (Ndrops*NumReads+Samples_Per_Read*Numreads)
	Endelse

        ; 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) / $
			   (NumGroups) * 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 ReadTimes[OverTimes] = !values.f_nan

        ; 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
	    print, GroupNum
        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 RawSig[0:FirstGroupNum - 1] = -32768

        ; set up array used to hold previous read for cosmic ray detection
        Previousgroup = fltarr(Xsize,Ysize)

	;Zero the Cosmic Ray Arrays
        Mask10Sig[*] = 0
	CosmicRays[*] = 0

        ; loop through the reads of an individual input file
        for Groupcount = FirstGroupNum + 1, Numgroups - 1 do begin
			print,'Reading group number ',GroupCount
            ; 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,/plot
                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 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))

	    Fits_Write, OutDir+'TestDiff.fits', DiffFrame
	    stop             
	    if (Outliers[0] ne -1) then begin
		Mask10Sig[Outliers] = Mask10sig[Outliers] + 1
	 	CosmicRays[Outliers] = DiffFrame[Outliers]
	    endif
            PreviousGroup = SCAGroup

            ; do the incremental slope calculation operations
            Sxy = Sxy + GroupTimes[GroupCount] * SCAGroup
            Sx = Sx + GroupTimes[GroupCount]
            Sy = Sy + SCAGroup
            Sxx = Sxx + GroupTimes[GroupCount] ^ 2

            ; extract the region of interest from the frame and apply the mask
            ; (which has already been cut down to the size of the region)
            SCA_Roi = SCAGroup[Region[0]:Region[1]:Colinc, $
			       Region[2]:Region[3]:Rowinc]
            SCA_Mask = SCA_Roi[where(Region_Mask eq 1)]

            ; begin the process of finding signal values for individual reads
            ; begin by getting the median and stddev using iterative sigma
            ; clipping algorithm.
            DJS_Iterstat, SCA_Mask, sigrej=4.0, sigma=DJSig, median=DJMed
            TheMedian[GroupCount] = DJMed

            ; 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
            _Min = floor(DJMed - (DJSig * 4)) + 0.5
            _Max = floor(DJMed + (DJSig * 4)) - 0.5
            _nbins = _max - _min

            ; set up error handler to catch and deal with any errors
            ; caused by gaussfit procedure.
            math_err_stat = check_math(mask = 208)
            math_errcount = 0
            catch, errval
            if (errval ne 0) then begin
                ; if there is a math error which has been thrown manually
                ; by a call to the message procedure, reduce the number of bins
                ; and retry
                if (strpos(!error_state.msg, 'math error') ne -1) then begin
                    _nbins = _nbins / 2
                    message, /reset
                    math_errcount = math_errcount + 1
                endif else begin
                    ; if this is an unexpected non-math error, cancel the
                    ; error handler and retry, allowing the error to be
                    ; handled by the default IDL handler (which should exit the
                    ; procedure).
                    catch, /cancel
                endelse
            endif

            ; make a histogram from the masked data in the region of interest.
            theHist = histogram(sca_mask, nbins=_nbins, min=_min, max=_max, /nan, locations = theVals)

            ; 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)
            if (math_err_stat ne 0 and math_errcount lt 5) then message, 'math error'

            ; 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,
        ; 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
        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
            crpix = where(mask10sig ne 0)
            if (crpix[0] ne -1) then outarray[crpix] = !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'
	CosName = strjoin(NameParts[0:n_elements(NameParts)-2], '.') + '_cosmics.fits'

        print,'Writing file ',OutDir+OutName
        Fits_Write, Outdir + Outname, OutArray, ImgHdr
	print,'Writing file ',Outdir+CosName
	Fits_Write, Outdir + Cosname, CosmicRays, 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)')
    endfor

    ; get rid of null file names (representing images that are railed in every read)
    validnames = where(imginfo.name ne '', numvalid)
    if (numvalid gt 0) then begin
        imginfo = imginfo[validnames]

        ; open output file for names of slope images
        openw, lun_img, outdir + imgoutfile, /get_lun

        ; 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 output files
    free_lun, lun_val

End
