

PRO eis_mask_spectrum, l1name, mask, swspec=swspec, lwspec=lwspec, $
                       refill=refill, offset=offset, shift=shift, $
                       iexp=iexp, sum=sum, subtract_l1name=subtract_l1name, $
                       fix=fix, linear=linear, calib=calib

;+
; NAME
;
;     EIS_MASK_SPECTRUM
;
; PROJECT
;
;     HINODE/EIS
;
; EXPLANATION
;
;     Using a pixel mask created with EIS_PIXEL_MASK, this routine
;     averages the EIS spectra over the specified spatial pixels to
;     create an averaged spectrum. The error bars on the intensities
;     are correctly calculated.
;
;     If the pixel mask covers a large spatial area, then the slit
;     tilt and orbit variation (which modify the line centroid
;     positions on the detector) result in an enhanced broadening in
;     the spectral lines. Setting the /SHIFT keyword shifts the
;     individual pixel spectra onto a common wavelength scale (using
;     the routine EIS_SHIFT_SPEC), thus preventing the enhanced
;     broadening. 
;
;     The output spectrum can be fit with Gaussians using the routine
;     SPEC_GAUSS_EIS.PRO.
;
;     In some circumstances it may be preferable to sum the specta at
;     the different spatial locations instead of averaging them. In
;     this case, the /SUM keyword should be set. Applications include
;     the case where a small bright point is not resolved by the
;     instrument. The true intensity of the bright point is then the
;     sum of spectra that have been dispersed by the instrument.
;
; INPUTS
;
;     L1NAME  The full path specification of an EIS level-1 file to
;             which the specified pixel mask belongs.
;
;     MASK    A pixel mask generated by EIS_PIXEL_MASK.
;
; KEYWORDS
;
;     REFILL  If set, then the /refill option will be passed to
;             eis_getwindata and so missing pixels will get
;             interpolated.
;
;     SHIFT   If set, then the routine EIS_SHIFT_SPEC will be applied
;             to interpolate the individual pixel spectra onto a
;             common wavelength scale. This helps prevent 'smearing'
;             of the spectra when the averaging is performed over
;             large spatial areas.
;
;     SUM    If set, then the spectra will be summed instead of
;            averaged.
;
;     FIX    If set, then the windata structures are processed with
;            EIS_FIX_WINDATA in order to identify bright data columns
;            and set them to missing.
;
;     LINEAR This requests eis_shift_spec to perform linear
;            interpolation instead of cubic interpolation. Due to the
;            way cubic interpolation is performed, missing pixels can
;            mess up the interpolated spectrum. With linear
;            interpolation this is not the case.
;
; OPTIONAL INPUTS
;
;     OFFSET  This keyword is obsolete. Setting it will simply trigger
;             the /SHIFT keyword to be set. The OFFSET keyword has
;             been made obsolete because, since Sept. 2010, the WINDATA
;             structures now include the wavelength offset tag
;             (WAVE_CORR) which can be used by EIS_SHIFT_SPEC to
;             perform the spectrum shifts that previously required
;             OFFSET. 
;
;     IEXP   If the data-set has multiple exposures per raster position,
;            then setting IEXP allows a particular exposure to be
;            selected.  Note that IEXP is ignored if there is only one
;            exposure per raster position.
;
;     SUBTRACT_L1NAME  This specifies the name of an EIS level-1 file
;                      which is subtracted from the L1NAME file. For
;                      this keyword to work, the two files must be
;                      from the same raster. For each windata
;                      structure, the subtract_l1name windata
;                      intensity array is subtracted and the error
;                      array combines the two. This filename is useful
;                      if a sudden brightening appears in
;                      L1NAME and it's necessary to subtract
;                      off the previous raster. Note that /SHIFT is
;                      switched on when SUBTRACT_L1NAME is given.
;
;     CALIB   An integer specifying which version of the EIS
;             calibration you would like to use. The options are:
;               1 - Del Zanna (2013)
;               2 - Del Zanna (2013), with /undecay
;               3 - Warren et al. (2014)
;               4 - Warren et al. (2014), with /undecay
;              Check the EIS wiki for more details of these
;              options. It is highly recommended that you run eis_prep
;              as recommended on the EIS Wiki Data Analysis Guide, and
;              then specify calib=1 or 3 depending on which
;              calibration you prefer (not setting calib will give the
;              original laboratory EIS calibration). Options 2 and 4
;              are for if you used the /correct_sensitivity keyword
;              for eis_prep.
;
; OPTIONAL OUTPUTS
;
;     SWSPEC  These are structures with the following tags:
;     LWSPEC  .wvl  A 2048 element array containing wavelengths
;                   (angstroms) for the complete SW or LW bands.
;             .int  A 2048 element array containing intensities
;                   (erg/cm2/s/sr/Ang) for the complete SW or LW bands.
;             .err  A 2048 element array containing intensity errors
;                   (erg/cm2/s/sr/Ang) for the complete SW or LW bands.
;             .qual A 2048 element array containing a quality number for
;                   each pixel in the band. The quality is the
;                   number of spatial pixels used to generate the
;                   average intensity value at that pixel.
;             .qual_max An integer specifying the number of spatial
;                       pixels in the pixel mask.
;             .yip  An integer containing the Y initial position of
;                   the raster.
;             .date_obs  A string containing the raster start time. 
;             .slit_ind  Integer containing EIS slit index.
;             .refill  Integer indicating if /refill was set.
;             .shift  Integer indicating if /shift was set.
;             .sum  Integer indicating if /sum was set.
;             .calib Integer indicating which calibration was used.
;             .missing  The missing data value.
; 
; CALLS
;
;     EIS_ADD_WD_TO_SPEC, EIS_GET_HDR_STRUC, EIS_PIXEL_TO_WAVELENGTH,
;     CENTROID, EIS_SHIFT_SPEC
;
; HISTORY
;
;     Ver.1, 27-Oct-2008, Peter Young
;     Ver.2, 11-Nov-2008, Peter Young
;       Set wd=0 at end of for loop to improve memory usage.
;     Ver.3, 4-Dec-2008, Peter Young
;       Added TIME= keyword in call to eis_pixel_to_wavelength.
;     ver.4, 7-Aug-2009, Peter Young
;       Added /refill keyword.
;     Ver.5, 15-Dec-2009, Peter Young
;       Added offset= keyword and call to eis_shift_spec.
;     Ver.6, 16-Apr-2010, Peter Young
;       Program was running out of memory for data-sets with large
;       arrays. Memory usage has been improved now.
;     Ver.7, 28-Jun-2011, Peter Young
;       Introduced the /SHIFT keyword, which replaces OFFSET=. The
;       latter has now been made obsolete.
;     Ver.8, 2-Apr-2012, Peter Young
;       Added IEXP= optional input (for use with data-sets with
;       nexp_prp>1).
;     Ver.9, 13-Apr-2012, Peter Young
;       Corrected bug with IEXP= implementation (crashed if
;       nexp_prp=1).
;     Ver.10, 30-May-2012, Peter Young
;       Added /SUM keyword.
;     Ver.11, 6-Sep-2012, Peter Young
;       Added YIP, DATE_OBS, REFILL, SHIFT and SUM to the output
;       structures.
;     Ver.12, 1-Nov-2012, Peter Young
;       The /SHIFT keyword wasn't actually implemented so this
;       has been added; also added SUBTRACT_L1NAME= optional input. 
;     Ver.13, 5-Nov-2012, Peter Young
;       Added /FIX keyword.
;     Ver.14, 5-Nov-2013, Peter Young
;       Added /LINEAR keyword.
;     Ver.15, 21-Apr-2017, Peter Young
;       Added calib and missing tags to the output structures.
;     Ver.16, 12-Aug-2025, Peter Young
;       Added calstat and units tags to output structures.
;-

IF n_params() LT 2 THEN BEGIN
  print,'Use:  IDL> eis_mask_spectrum, l1name, mask, swspec=swspec, lwspec=lwspec'
  print,'                         /refill, /shift, iexp=, /sum, subtract_l1name='
  print,'                         /fix, /linear, calib='
  print,''
  print,'  /shift - process data with eis_shift_spec (use /linear for linear interpolation)'
  print,'  /refill - fills in missing data'
  print,'  /fix   - use eis_fix_windata'
  print,'  subtract_l1name - name of a level-1 file to subtract from input file'
  print,'  calib  - specifies calibration to apply.'
  return
ENDIF


IF n_tags(mask) EQ 0 THEN BEGIN
  print,'% EIS_MASK_SPECTRUM: please define a pixel mask - see eis_pixel_mask.pro.'
  return
ENDIF

IF n_elements(offset) NE 0 THEN BEGIN
  print,'%EIS_MASK_SPECTRUM: setting OFFSET= is now equivalent to setting the /SHIFT keyword. In'
  print,'                    future please simply use /SHIFT in the call to EIS_MASK_SPECTRUM.'
  shift=1
ENDIF 

IF keyword_set(shift) AND NOT keyword_set(refill) THEN BEGIN
  print,'% EIS_MASK_SPECTRUM: when setting the /SHIFT keyword it is recommended to'
  print,'                     also use the /REFILL keyword to reduce the effect of '
  print,'                     missing pixels.'
  print,''
ENDIF

;
; If SUBTRACT_L1NAME is used, then need to set /SHIFT.
;
IF n_elements(subtract_l1name) NE 0 THEN shift=1


;
; Check the calib keyword
;
IF n_elements(calib) EQ 0 THEN calib=0


missing=-100

;
; Get current time and convert to UTC for use as time_stamp.
;
jd=systime(/julian,/utc)
mjd=jd-2400000.5d
mjd_str={ mjd: floor(mjd), time: (mjd-floor(mjd))*8.64d7 }
time_stamp=anytim2utc(/ccsds,mjd_str)

swspec={ wvl: fltarr(2048), $
         int: fltarr(2048)+missing, $
         err: fltarr(2048)+missing, $
         qual: intarr(2048), $
         qual_max: 0, $
         yip: 0, $
         date_obs: '', $
         refill: keyword_set(refill), $
         shift: keyword_set(shift), $
         sum: keyword_set(sum), $
         subtract_l1name: '', $
         slit_ind: -1, $
         missing: missing, $
         calstat: '', $
         units: '', $
         calib: 0, $
         exposure_time: -1., $
         time_stamp: time_stamp}

lwspec={ wvl: fltarr(2048), $
         int: fltarr(2048)+missing, $
         err: fltarr(2048)+missing, $
         qual: intarr(2048), $
         qual_max: 0, $
         yip: 0, $
         date_obs: '', $
         refill: keyword_set(refill), $
         shift: keyword_set(shift), $
         sum: keyword_set(sum), $
         subtract_l1name: '', $
         missing: missing, $
         slit_ind: -1, $
         calstat: '', $
         units: '', $
         calib: 0, $
         exposure_time: -1., $
         time_stamp: time_stamp}

IF n_elements(subtract_l1name) NE 0 THEN BEGIN
  swspec.subtract_l1name=subtract_l1name
  lwspec.subtract_l1name=subtract_l1name
ENDIF 

chck=0
yip=-1
FOR i=0,24 DO BEGIN
 ;
  wd=eis_getwindata(l1name,i,refill=refill,/quiet)
  IF yip EQ -1 THEN BEGIN
    yip=wd.hdr.yws
    swspec.yip=yip
    lwspec.yip=yip
  ENDIF 
  IF swspec.date_obs EQ '' THEN BEGIN
    swspec.date_obs=wd.hdr.date_obs
    lwspec.date_obs=wd.hdr.date_obs
  ENDIF 
  IF swspec.slit_ind EQ -1 THEN BEGIN
    swspec.slit_ind=wd.hdr.slit_ind
    lwspec.slit_ind=wd.hdr.slit_ind
  ENDIF
  IF swspec.calstat EQ '' THEN BEGIN
    swspec.calstat=wd.hdr.calstat
    lwspec.calstat=wd.hdr.calstat
    swspec.units=wd.units
    lwspec.units=wd.units
  ENDIF 
 ;
  IF n_tags(wd) NE 0 THEN BEGIN
    IF keyword_set(fix) THEN wd=eis_fix_windata(temporary(wd))
   ;
    nexp_prp=wd.hdr.nexp_prp
    IF nexp_prp GT 1 AND n_elements(iexp) EQ 0 AND chck EQ 0 THEN BEGIN
      print,''
      print,'INFORMATION: '
      print,'This data-set has '+trim(nexp_prp)+' exposures per raster position. The exposure times are:'
      FOR k=0,nexp_prp-1 DO BEGIN
        print,format='(i5,f6.1,"s")',k,wd.exposure_time[0,k]
      ENDFOR 
      print,'Only the largest exposure times will be averaged to produce the final spectrum. To select a'
      print,'particular exposure please specify the exposure number with the input IEXP=.'
      print,''
      chck=1
    ENDIF
   ;
    IF n_elements(iexp) GT 0 THEN BEGIN
      IF nexp_prp GT 1 AND iexp GE nexp_prp THEN BEGIN
        print,'%EIS_MASK_SPECTRUM: IEXP must take a value between 0 and '+trim(nexp_prp-1)+'. Returning...'
        return
      ENDIF 
    ENDIF 
   ;
    nwin=wd.hdr.nwin
    print,'Processing window '+trim(i+1)+' of '+trim(nwin)+'...'
   ;
   ; Use the first windata to determine the wavelength scales for the SW
   ; and LW channels 
   ;
    IF i EQ 0 THEN BEGIN
      swspec.wvl=eis_pixel_to_wavelength(findgen(2048),1,wd.hdr.slit_ind,time=wd.hdr.date_obs)
      lwspec.wvl=eis_pixel_to_wavelength(findgen(2048),0,wd.hdr.slit_ind,time=wd.hdr.date_obs)
    ENDIF
   ;
    IF keyword_set(shift) OR n_elements(offset) NE 0 THEN BEGIN
      wd=eis_shift_spec(temporary(wd),linear=linear)
    ENDIF
   ;
   ; The following takes the windata structure from the file
   ; SUBTRACT_L1NAME and subtracts it from WD, correcting the error too. 
   ;
    IF n_elements(subtract_l1name) NE 0 THEN BEGIN
      wd0=eis_getwindata(subtract_l1name,i,refill=refill,/quiet)
      IF keyword_set(fix) THEN wd0=eis_fix_windata(temporary(wd0))
      wd0=eis_shift_spec(temporary(wd0),linear=linear)
     ;
      wd.int=wd.int-wd0.int
      kk=where(wd.err EQ wd.missing OR wd0.err EQ wd0.missing,nk)
      newerr=sqrt(wd.err^2 + wd0.err^2)
      newerr[kk]=wd.missing
      wd.err=temporary(newerr)
      wd0=0
    ENDIF 
 ;
    tdetx=wd.hdr.tdetx
    tdetxw=wd.hdr.tdetxw
    IF tdetx LT 2148 THEN BEGIN    ;; - SW BAND
      wpix=tdetx+indgen(tdetxw)-50
      eis_add_wd_to_spec,wd,mask,wpix,swspec,iexp=iexp,sum=sum
    ENDIF ELSE BEGIN                  ;; - LW BAND
      wpix=tdetx+indgen(tdetxw)-50-2148
      eis_add_wd_to_spec,wd,mask,wpix,lwspec,iexp=iexp,sum=sum
    ENDELSE
    wd=0
  ENDIF 
ENDFOR

IF n_elements(calib) EQ 0 THEN calib=0
IF calib GT 0 THEN BEGIN
  swspec=eis_recal_mask_spec(swspec,calib)
  lwspec=eis_recal_mask_spec(lwspec,calib)
ENDIF 

END
