; $Id: calibratepsd.pro,v 1.4 2007/09/05 20:04:25 azuah Exp $
;###############################################################################
; NAME:
;   CalibratePSD
;
; PURPOSE:
;   PSD detector calibration module. It performs the following tasks
;   ** Load a calibration data file
;   ** Determine the final energy for each horizontal pixel for PSD
;   ** Determine the sensitivity or efficiency for each horizontal pixel
;   ** Save results in an output file
;
; CATEGORY:
;   Triple Axis Data Reduction
;
; AUTHOR:
;   Richard Tumanjong Azuah
;   NIST Center for Neutron Research
;   100 Bureau Drive, Gaithersburg, MD 20899
;   United States
;   azuah@nist.gov; (301) 9755604
;   Aug, 2007
;
; LICENSE:
;  The software in this file is written by an employee of 
;  National Institute of Standards and Technology 
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used of if the code in this file is
;  included in another product.
;
;###############################################################################
;==============================================================================
pro calibratePSD_cleanup, wTLB
compile_opt idl2

widget_control, wTLB, get_uvalue=sPtr
wDelete, (*sPtr).winPix

heap_free, sPtr

;print,'Cleaup called'

end

pro calibratePSD_help, event
compile_opt idl2

void = launch_help(!DAVE_AUXILIARY_DIR+'spinscalibpsd.pdf',tlb = event.top)
return

end


;==============================================================================
pro calibratePSD_loadData, sPtr, status=status
compile_opt idl2

status = 0
tlb = (*sPtr).wTLB
curDir = (*sPtr).dataDir
filename = dialog_pickfile(dialog_parent=tlb,title = 'Select calibration file to load',$
                           /must_exist,filter=['*.ng5','*.bt7'],path=curDir,get_path=newDir)
if (filename[0] eq '') then return

oData = obj_new('ng5CalibData',filename[0])

if (obj_valid(oData)) then begin
  if (obj_valid((*sPtr).oData)) then obj_destroy, (*sPtr).oData
  (*sPtr).oData = oData
  wID = widget_info(tlb,find_by_uname='VIEWPSD')
  if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
  wID = widget_info(tlb,find_by_uname='CALCEF')
  if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
  wID = widget_info(tlb,find_by_uname='CALCSEN')
  if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1

  (*sPtr).dataDir = newDir
  title = 'SPINS PSD Calibration -- '+file_basename(filename[0])
  widget_control, (*sPtr).wTLB, base_set_title=title
  status = 1 ; success
endif else $
  void = dialog_message('Unable to read contents of '+filename,/error,dialog_parent=tlb)
  
end

;==============================================================================
pro calibrate_dispPSDimage, sPtr, _Extra=etc
compile_opt idl2

; Get data to display
oData = (*sPtr).oData
if (~obj_valid(oData)) then return
oData->getProperty, data=data, etran=y
xs = (size(data,/dimensions))[1]
x = indgen(xs)+1
xTitle = 'Pixel'
yTitle = 'Energy Transfer'

Contour, transpose(data),x,y,xtitle=xTitle,ytitle=yTitle,/fill,nlevels=50, _Extra=etc $
       ,background=255,color=0,charsize=2.0,charthick=1.75
;spectrogram,data,x,y,xtitle=xTitle,ytitle=yTitle,/noskipgaps

end

;==============================================================================
pro calibrate_dispFits, sPtr, _Extra=etc
compile_opt idl2

; Get data to display
oData = (*sPtr).oData
if (~obj_valid(oData)) then return
oData->getProperty, data=data
xs = (size(data,/dimensions))[1]
x = indgen(xs)+1

;get the current slider value
wID = widget_info((*sPtr).wTLB,find_by_uname='DGSLIDER')
if (widget_info(wID,/valid)) then widget_control, wID, get_value=index
if (n_elements(index) eq 0) then index=1
y = data[index-1,*]

xTitle = 'Pixel'
yTitle = 'Counts'

plot,x,y,psym=4,symsize=2,xtitle=xtitle,ytitle=ytitle, _Extra=etc $
    ,background=255,color=0,charsize=2.0,charthick=1.75 $
    ,ticklen=1.0,xgridstyle=1,ygridstyle=1
    
; errors
errplot,x,y-sqrt(y),y+sqrt(y),color=0

; fit
yFit = (*(*sPtr).pFit)[index-1,*]
oplot,x,yfit,color=0,thick=2.0

end

;==============================================================================
pro calibrate_dispEf, sPtr, _Extra=etc
compile_opt idl2

; Get data to display
oData = (*sPtr).oData
if (~obj_valid(oData)) then return
oData->getProperty, nPix=nPix, Efp=Efp
x = indgen(nPix)+1

;get Ef
pixFits = (*(*sPtr).pPixFits)
efFits =  (*(*sPtr).pEfFits)
xTitle = 'Pixel'
yTitle = 'Effective Final Energy'

plot,x,Efp,xtitle=xtitle,ytitle=ytitle, _Extra=etc $
    ,background=255,color=0,charsize=2.0,charthick=1.75,ystyle=16 $
    ,ticklen=1.0,xgridstyle=1,ygridstyle=1

; actual fitted pix positions from gaussian peaks
oplot,pixFits,efFits,color=0,psym=4,symsize=2

end

;==============================================================================
pro calibrate_dispSen, sPtr, _Extra=etc
compile_opt idl2

; Get data to display
oData = (*sPtr).oData
if (~obj_valid(oData)) then return
oData->getProperty, nPix=nPix, sen=sen
x = indgen(nPix)+1

xTitle = 'Pixel'
yTitle = 'Sensitivity'

plot,x,sen,xtitle=xtitle,ytitle=ytitle,psym=4,symsize=2, _Extra=etc $
    ,background=255,color=0,charsize=2.0,charthick=1.75,ystyle=16 $
    ,ticklen=1.0,xgridstyle=1,ygridstyle=1

end

;==============================================================================
pro calibratePSD_refresh, sPtr
compile_opt idl2
; save current device settings
device,get_decomposed = dc
tvlct,r,g,b,/get

; load a default color table
device,decomposed = 0 ; don't want color decomposition
;colors = hfbs_GetColor(/Load, Start=1) ; custom (16) color table
loadct,39,/silent

; Generate the plot
wset,(*sPtr).winPix
plotType = (*sPtr).plotType
;if (n_elements(plottype) eq 0) then plottype=0

; determine xrange and yrange
zp2=(*sPtr).zp2
etc = {dummy:0}
if (zp2[0] gt 0 || zp2[1] gt 0) then begin
    zp1 = (*sPtr).zp1
    x = min([zp1[0], zp2[0]])
    y = min([zp1[1], zp2[1]])
    w = abs(zp1[0] - zp2[0])
    h = abs(zp1[1] - zp2[1])
    x2 = x+w
    y2 = y+h
    lc = convert_coord(x, y, /device, /to_data)
    uc = convert_coord(x2, y2, /device, /to_data)
    etc = {xrange:[lc[0],uc[0]],yrange:[lc[1],uc[1]]}
endif

case plotType of
  0: calibrate_dispPSDimage, sPtr, _Extra=etc
  1: calibrate_dispFits, sPtr, _Extra=etc
  2: calibrate_dispEf, sPtr, _Extra=etc
  3: calibrate_dispSen, sPtr, _Extra=etc
endcase
wset,(*sPtr).winVis
device,copy = [0,0,(*sPtr).winXs,(*sPtr).winYs,0,0,(*sPtr).winPix]

; revert to original device settings
device,decomposed = dc
tvlct,r,g,b
end

;==============================================================================
; Fit Gaussians to data. The fitted peak position at each data point is the pixel
; which corresponds to the final energy for that data point.
function calibratePSD_doFits, sPtr
compile_opt idl2

oData = (*sPtr).oData
oData->getProperty,data=data,nDat=nDat,nPix=nPix,etran=E,Efix=EfixAna $
                  ,meanInt=meanInt,sigmaInt=sigmaInt
if (ptr_valid((*sPtr).pFit)) then $
  (*(*sPtr).pFit) = data*0.0 $
else $
  (*sPtr).pFit = ptr_new(data*0.0)
x = findgen(nPix)+1.0 

;p = [0.0,0.0,1.0,0.0,1.0] ; bc,bs,area,center,sigma
p = [1.0,0.0,1.0,0.0,0.0] ; area,center,sigma,bc,bs

wID = widget_info((*sPtr).wTlb,find_by_uname='THRESH')
if (widget_info(wID,/valid)) then $
  widget_control, wID, get_value=threshold $
else $
  threshold = 2.0
threshold = float(threshold[0]) ; widget_control above returns a string array of 1
thresh_min = meanInt - threshold*sigmaInt
thresh_max = meanInt + threshold*sigmaInt
  
;parinfo = replicate({value:0.D,fixed:0,limited:[0,0],limits:[0.D,0]}, 5)
for i=0,nDat-1 do begin
  y = reform(data[i,*])
  yerror = sqrt(y)
  ind = where(yerror le 0.0,cnt)
  if (cnt gt 0) then yerror[ind] = 1.0

  peak_loc = get_peak_pos(x,y,1,fwhm=fwhm,indices=indices)
  p[0] = y[indices]
  p[1] = peak_loc
  p[2] = fwhm/2.354D
  
  ; if peak intensity is outside threshold then don't fit peak
  if (p[0] lt thresh_min or p[0] gt thresh_max) then continue

;  parinfo[*].value = p
;  parinfo[2].limited[0] = 1 ;  limit lower bound of sigma
;  parinfo[2].limits[0] = 1.0D ; make sigma >= 1; resolution width is > 1 so this is okay!
;  parinfo[0].limited[0] = 1 ;  limit lower bound of area
;  parinfo[0].limits[0] = 0.0 ; make area >= 0
;  yfit = mpcurvefit(x,y,1./yerror,p,function_name='single_gaussian',status=status $
;                    ,parinfo=parinfo,/autoderivative,chisq=chisq)  
  yfit = gaussfit(x,y,fp,chisq=chisq,estimates=p,nterms=5,sigma=sg,measure_errors=yerror)

  (*(*sPtr).pFit)[i,*] = yfit
  pixDpt = (n_elements(pixDpt) gt 0)? [pixDpt,fp[1]] : fp[1] ; fitted peak pos
  pixError = (n_elements(pixError) gt 0)? [pixError,sg[1]] : sg[1] ; fitted peak pos error
  efDpt = (n_elements(efDpt) gt 0)? [efDpt,E[i] + EfixAna] : E[i] + EfixAna ; final ene = ene transfer + fixed analyzer energy
endfor

; Now know the final energy at the pixel locations that coincide with the 
; center of the Gaussian peaks. Now need to extrapolate the results using
; a linear interpolation/fit to obtain final energy for any pixel.
; pixDpt = pixel location of peak for each data point
; efDpt = effective final energy for each data point
; efp = effective final energy for each pixel


;NB - pixError is actually the error in the pixel location and NOT Ef as it is used below!
lparam = poly_fit(pixDpt,efDpt,1,measure_errors=pixError,status=status) ; fit to poly of degree 1
if (status eq 0) then begin
  efp = lparam[0] + lparam[1]*x
endif else begin
  ; fit unsuccessful so use interpolation
  efp = interpol(efDpt,pixDpt,x,/lsquadratic)
endelse
oData->setProperty, Efp=efp

if (ptr_valid((*sPtr).pPixFits)) then $
  (*(*sPtr).pPixFits) = pixDpt $
else $
  (*sPtr).pPixFits = ptr_new(pixDpt)
if (ptr_valid((*sPtr).pEfFits)) then $
  (*(*sPtr).pEfFits) = EfDpt $
else $
  (*sPtr).pEfFits = ptr_new(EfDpt)

return, 1
end


;==============================================================================
; Evaluate the sensitivity or efficiency for each pixel in the horizontal direction 
; (vertical integration has been performed for the data).
function calibratePSD_calcSen, sPtr, minPix, maxPix 
compile_opt idl2

oData = (*sPtr).oData
oData->getProperty,data=data,nPix=nPix,Efp=Efp


sumP = total(data,1) ; Sum counts for all data points as a function of pixel
dEfdPix = abs(deriv(Efp)) ; dEf/dp - gradient of Ef wrt pixels
normFactor = nPix/total(sumP/dEfdPix)
sens = (sumP/dEfdPix) * normFactor
sensError = sqrt(sumP)/dEfdPix*normFactor

oData->SetProperty, sen=sens

return, 1

end


;==============================================================================
pro calibratePSD_drawEvent, sPtr, event
compile_opt idl2

case event.type of
   0: begin                ; button press
        (*sPtr).mouseType = event.press
        if ((*sPtr).mouseType eq 4) then begin ; right-hand mb so reset plot to full range
            calibratePSD_refresh, sPtr
            (*sPtr).mouseType = 0 ; set mouse type to undefined value
            return
        endif
        if ((*sPtr).mouseType eq 1) then begin ; left-hand mb so record mouse location
            (*sPtr).zp1 = [event.x,event.y]
        endif
    end
    1: begin                ; button release
        if ((*sPtr).mouseType eq 1) then begin ; mouse press was a left-hand mb
            calibratePSD_refresh, sPtr
            (*sPtr).zp2 = [0,0] ; reset to zero
        endif
        (*sPtr).mouseType = 0
    end
    2: begin                ; mouse motion
        if ((*sPtr).mouseType eq 1) then begin ; mouse press was a left-hand mb
            (*sPtr).zp2 = [event.x, event.y]
            zp1 = (*sPtr).zp1
            zp2 = (*sPtr).zp2
            xc = [zp1[0], event.x, event.x, zp1[0], zp1[0]]
            yc = [zp1[1], zp1[1], event.y, event.y, zp1[1]]
            device, copy=[0,0,(*sPtr).winXs,(*sPtr).winYs,0,0,(*sPtr).winPix] ; copy contents of winPix to winVis
;            device,get_decomposed = dc
;            device,decomposed = 0
            plots, xc, yc, /device, color=(*sPtr).colors.red
;            device,decomposed = dc
        endif
    end
    else:
endcase    



end


;==============================================================================
pro calibratePSD_writeImage, sPtr, jpeg=jpeg, png=png
compile_opt idl2

writejpeg = (keyword_set(jpeg))? 1 : 0
writepng = (keyword_set(png))? 1 : 0
if (~writejpeg && ~writepng) then return

fname = (writejpeg)? 'psd_calibration.jpeg' : 'psd_calibration.png'
ext =  (writejpeg)? 'jpeg' : 'png'
flt =  (writejpeg)? '*.jpeg' : '*.png'

fname = dialog_pickfile(default_extension=ext,dialog_parent=(*sPtr).wTLB,filter=flt,file=fname $
                        ,/overwrite_prompt,path=(*sPtr).workDir,/write)
if (fname eq '') then return

wset, (*sPtr).winVis
image = tvrd()

if (writejpeg) then write_jpeg, fname,image else write_png, fname, image

end


;==============================================================================
pro calibratePSD_save, sPtr
compile_opt idl2

fname = 'psd_calibration.txt'
ext =  'txt'
flt = '*.txt'

fname = dialog_pickfile(default_extension=ext,dialog_parent=(*sPtr).wTLB,filter=flt,file=fname $
                        ,/overwrite_prompt,path=(*sPtr).workDir,/write)
if (fname eq '') then return

oData = (*sPtr).oData
oData->getProperty,NPIX=nPix,EFP=efp,SEN=sen

openw,lun,fname,/get_lun

printf,lun,nPix
printf,lun,efp,format='(8G10.4)'
printf,lun,sen,format='(8G10.4)'

free_lun, lun,/force
end


;==============================================================================
pro calibratePSD_event, event
compile_opt idl2

;begin error handler------------------------------------------------------------
defsysv,'!debug',exist=debugExist
if (debugExist && (!debug eq 0)) then begin
    catch, catchError
    if (catchError ne 0) then begin
        ;;print, 'Error handled!'
        eTitle = 'Error encountered in calibratePSD_Event'
        eMsg = 'An error or unusual condition was encountered!'
        eMsg = [eMsg,'Please, report the following to the DAVE team:']
        eMsg = [eMsg,!error_state.msg]
        void = dialog_message(/error,eMsg,title=eTitle)
        catch, /cancel
        return
    endif
endif
;end error handler
widget_control, event.top, get_uvalue=sPtr
tlb = (*sPtr).wTLB

uname = widget_info(event.id,/uname)

case uname of
  'EXIT': widget_control, event.top, /destroy
  
  'HELP': calibratePSD_help, event
  
  'DRAW': calibratePSD_DrawEvent, sPtr, event
  
  'JPEG': calibratePSD_writeImage, sPtr, /jpeg

  'PNG': calibratePSD_writeImage, sPtr, /png
  
  'LOAD': begin
    calibratePSD_loadData, sPtr, status=status    
    if (~status) then return
    
    ; Reset widget states

    wNames = ['VIEWFITS','DGSLIDER','VIEWEF','VIEWSEN'] ; de-sensitize these
    for i=0,n_elements(wNames)-1 do begin
        wID = widget_info((*sPtr).wTLB,find_by_uname=wNames[i])
        if (widget_info(wID,/valid)) then widget_control, wID, sensitive=0
    endfor
    ; update slider widget range
    oData = (*sPtr).oData
    oData->getProperty, nDat=nDat
    wID = widget_info((*sPtr).wTLB,find_by_uname='DGSLIDER')
    if (widget_info(wID,/valid)) then widget_control, wID, set_slider_max=nDat, set_slider_min=1
    
    ; refresh plot window
    wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWPSD')
    if (widget_info(wID,/valid)) then $
        widget_control, wID, /set_button,send_event={id:wID,top:tlb,handler:tlb,select:1}
    
;    (*sPtr).plotType = 0
;    calibratePSD_refresh, sPtr;, type=0
  end
  
  'VIEWPSD': begin
    widget_control, event.id, set_uvalue=event.select
    if (event.select eq 1) then begin
      (*sPtr).plotType = 0
      calibratePSD_refresh, sPtr;, type=0
      ;uncheck view Fits
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWFITS')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0 
      widget_control, wID, set_uvalue=0
      ;uncheck view Sensitivity
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWSEN')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Ef
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWEF')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0
      widget_control, wID, set_uvalue=0
    endif else begin
    
    endelse
  end
  
  'VIEWEF': begin
    widget_control, event.id, set_uvalue=event.select
    if (event.select eq 1) then begin
      ;uncheck view PSD
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWPSD')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Fits
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWFITS')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Sensitivity
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWSEN')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0
      widget_control, wID, set_uvalue=0
      
      ; Update plot with Ef plot
      (*sPtr).plotType = 2
      calibratePSD_refresh, sPtr;, type=2
    endif 
  end

  'VIEWSEN': begin
    widget_control, event.id, set_uvalue=event.select
    if (event.select eq 1) then begin
      ;uncheck view PSD
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWPSD')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Fits
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWFITS')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Ef
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWEF')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0
      widget_control, wID, set_uvalue=0
      
      ; Update display with sen plot
      (*sPtr).plotType = 3
      calibratePSD_refresh, sPtr;, type=3
    endif 
  end


  'VIEWFITS': begin
    widget_control, event.id, set_uvalue=event.select
    if (event.select eq 1) then begin
      ; sensitize slider
      wID = widget_info((*sPtr).wTLB,find_by_uname='DGSLIDER')
      if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
      ;uncheck view PSD
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWPSD')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0    
      widget_control, wID, set_uvalue=0
      ;uncheck view Sensitivity
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWSEN')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0
      widget_control, wID, set_uvalue=0
      ;uncheck view Ef
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWEF')
      if (widget_info(wID,/valid)) then widget_control, wID, set_button=0
      widget_control, wID, set_uvalue=0
      
      ; Update plot with fits
      (*sPtr).plotType = 1
      calibratePSD_refresh, sPtr;, type=1
    endif else begin
      wID = widget_info((*sPtr).wTLB,find_by_uname='DGSLIDER')
      if (widget_info(wID,/valid)) then widget_control, wID, sensitive=0    
    endelse
  end
  
  'DGSLIDER': begin
    buttonIsSet = 0
    wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWFITS')
    if (widget_info(wID,/valid)) then widget_control, wID, get_uvalue=buttonIsSet
    if (buttonIsSet) then begin
        (*sPtr).plotType = 1
        calibratePSD_refresh, sPtr;, type=1
    endif
  end
  
  'CALCEF': begin
    ok = calibratePSD_doFits(sPtr)
    if (ok) then begin ; update some widget states
      ; sensitize view fits and refresh plot
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWFITS')
      if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
      widget_control, wID, get_uvalue=buttonIsSet
      if (buttonIsSet) then calibratePSD_refresh, sPtr;, type=1

      ; sensitize view ef and refresh plot
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWEF')
      if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
      widget_control, wID, get_uvalue=buttonIsSet
      if (buttonIsSet) then calibratePSD_refresh, sPtr;, type=2
    endif
  end
  
  'CALCSEN': begin
    ok = calibratePSD_calcSen(sPtr)

    if (ok) then begin ; update some widget states
      ; sensitize view sen and refresh plot
      wID = widget_info((*sPtr).wTLB,find_by_uname='VIEWSEN')
      if (widget_info(wID,/valid)) then widget_control, wID, sensitive=1
      widget_control, wID, get_uvalue=buttonIsSet
      if (buttonIsSet) then calibratePSD_refresh, sPtr;, type=3
    endif    
  end
  
  'SAVE': begin
    calibratePSD_save, sPtr
  end
  
  else: print,'Event generated by ',uname
endcase



end 

;==============================================================================
pro calibratePSD, group_leader=gpl, workDir=workDir, dataDir=dataDir, DAVETool=DAVETool
compile_opt idl2

registeredName = 'calibratePSD'
if xregistered(registeredName) then return

if (n_elements(workDir) eq 0) then workDir = ''
if (n_elements(dataDir) eq 0) then dataDir = ''
if (n_elements(DaveTool) eq 0) then DaveTool = obj_new()

; Create a custom color table for later use
tvlct,r,g,b,/get
device,decomposed = 0
colors = hfbs_GetColor(/Load, Start=1) ; custom (16) color table
device,decomposed = dc
tvlct,r,g,b

; define top-level base widget; make it resizeable
title = 'SPINS PSD Calibration'
wTLB = widget_base(group_leader=gpl,/row,title=title,mbar=mbar,/tlb_size_events)

; menus
fileMenu = widget_button(mbar, value='File',/menu)
; fill the 'File' menu
void = widget_button(fileMenu,value='Load calibration file',uname='LOAD')
;void = widget_button(fileMenu,value='Export plot to PS',uname='PS',/separator)
void = widget_button(fileMenu,value='Export plot to jpeg',uname='JPEG',/separator)
void = widget_button(fileMenu,value='Export plot to png',uname='PNG')
void = widget_button(fileMenu,value='Save calibration results',/separator,uname='SAVE')
void = widget_button(fileMenu,value='Exit',uname='EXIT',/separator)

helpMenu = widget_button(mbar, value='Help',/menu)
void = widget_button(helpMenu,value='Mini Manual', uname='HELP')

wCB1 = widget_base(wTLB,/col) ; controls base
wCB2 = widget_base(wTLB,/row) ; graphics base

wCB10 = widget_base(wCB1,/col,/frame)
void = widget_label(wCB10,value='Raw Calibration File',/align_center)
openBmp = filepath('open.bmp',subdirectory=['resource','bitmaps'])
tt = 'Load a PSD raw calibration dataset'
void = widget_button(wCB10,value=openBmp,/bitmap,/align_center,uname='LOAD',tooltip=tt)
;wCB10a = widget_base(wCB10,/row)
;void = widget_label(wCB10a,value='Loaded:')
;wLoadFile = widget_label(wCB10a,value='',/dynamic_resize,uname='FNAME')
wCB10a = widget_base(wCB10,/nonexclusive)
tt = 'View the raw PSD image data in the graphics window'
void = widget_button(wCB10a,value='View PSD Data',sensitive=0,uname='VIEWPSD',tooltip=tt)
widget_control, void, /set_button, set_uvalue=1

wCB11 = widget_base(wCB1,/col,/frame)
void = widget_label(WCB11,value='Energy Calibration',/align_center)
void = cw_field(wCB11,title='Intensity Threshold:',value=2.0,/col,/return_events $
                ,uname='THRESH',/float)
tt = 'Perform Gaussian fits of the peaks for each data point'
void = widget_button(wCB11,value='Fit Gaussians',/align_center,uname='CALCEF',tooltip=tt,sensitive=0)
wCB11a = widget_base(wCB11,/nonexclusive)
tt = 'View the Gaussian fits in the graphics window'
void = widget_button(wCB11a,value='View Fits',sensitive=0,uname='VIEWFITS',tooltip=tt)
widget_control, void, set_button=0, set_uvalue=0
void = widget_slider(wCB11,title='Data point to view',sensitive=0,uname='DGSLIDER')
wCB11b = widget_base(wCB11,/nonexclusive)
tt = 'View the evaluated final energy for each pixel in the graphics window'
void = widget_button(wCB11b,value='View Calc. Ef(pixel)',sensitive=0,uname='VIEWEF',tooltip=tt)
widget_control, void, set_button=0, set_uvalue=0


wCB12 = widget_base(wCB1,/col,/frame)
void = widget_label(wCB12,value='Sensitivity Calibration')
tt = 'Evaluate the detector efficiency at each pixel'
void = widget_button(wCB12,value='Calibrate Sen.',uname='CALCSEN',tooltip=tt,sensitive=0)
wCB12a = widget_base(wCB12,/nonexclusive)
tt = 'View the evaluated sensitivity for each pixel in the graphics window'
void = widget_button(wCB12a,value='View Sen(pixel)',sensitive=0,uname='VIEWSEN',tooltip=tt)
widget_control, void, set_button=0, set_uvalue=0

; draw widget
xs = 650 & ys = 500
void = widget_draw(wCB2,xsize=xs,ysize=ys,uname='DRAW',/button_events,/motion_events,retain=2)


window,/free,/pixmap,xsize=xs,ysize=ys
winPix = !d.window

widget_control, wTLB,/realize
widget_control, void, get_value=winVis

sPtr = ptr_new({wTLB:wTLB $
                ,wCB1:wCB1 $
                ,wCB2:wCB2 $
                ,winVis:winVis $
                ,winPix:winPix $
                ,winXs:xs $
                ,winYs:ys $
                ,mouseType:0 $
                ,plotType:0 $
                ,zp1:[0,0] $
                ,zp2:[0,0] $
                ,colors:colors $
                ,oData:obj_new() $
                ,pFit:ptr_new() $
                ,pPixFits:ptr_new() $
                ,pEfFits:ptr_new() $
                ,workDir:workDir $
                ,dataDir:dataDir $
                ,daveTool:DaveTool $
               })
widget_control, wTLB, set_uvalue=sPtr

xmanager, registeredName, wTLB, cleanup='calibratePSD_cleanup', /no_block

end


;==============================================================================
; Function to evalute a single Gaussian + sloping bkgd
pro single_gaussian, x,p,yfit
bkgd = p[0] + p[1]*x
ggaus = p[2]*exp(-0.5*((x-p[3])/p[4])^2)
;ggaus = p[2]/(sqrt(2.0*!dpi)*p[4]) * exp(-0.5*((x-p[3])/p[4])^2) ; normalized
yfit = bkgd + ggaus

end
