;+
; NAME: xrtobs__define
;
;
;
; PURPOSE: Definition of the xrtobs (XRT observation) class, a subclass of
;          hinobs, which again is a subclass of anyobs. The class definition
;          contains methods that differs from those of hinobs and anyobs.
;
;
;
;                                           (sdc_util)
;                                              |
;                                            anyobs
;                                          /       \
;                                    hinobs         irisobs
;                                  /   |   \ 
;                            xrtobs  sotobs eisobs
;                                     /  \
;                              sp1dobs    spl2obs
;
;
;
; METHODS: See anyobs__define and hinobs__define
;
;
;
; COMMON BLOCKS: none
;
;
;
; WRITTEN: Terje Fredvik, UiO, December 2007
;
;
;
;
; MODIFICATION HISTORY: $Log: xrtobs__define.pro,v $
; MODIFICATION HISTORY: Revision 1.24  2010/05/19 12:35:31  tfredvik
; MODIFICATION HISTORY: no message
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.22  2008/12/19 11:21:15  tfredvik
; MODIFICATION HISTORY: # ::_image_final_touch: use only pixels within a radius of rmax from disc centre as input to sigrange. For whitelight images however we instead use a sigrange fraction of 0.9995 (instead of the default value of 0.99)
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.18  2008/02/28 14:54:45  tfredvik
; MODIFICATION HISTORY: # ::_image_final_touch: added /nan keyword to bytscl.
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.17  2008/02/27 13:55:33  tfredvik
; MODIFICATION HISTORY: # :_correct_offset: fixed typo (self instead of o)
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.16  2008/02/07 14:47:50  tfredvik
; MODIFICATION HISTORY: # Removed keywords from init that are accepted by hinobs::init; pass keywords to hinobs::init by reference.
; MODIFICATION HISTORY: # _image_final_touch: added call to bytscl
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.15  2008/01/24 13:16:00  tfredvik
; MODIFICATION HISTORY: # Added method _correct_offset. Code moved from hinobs__define.
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.14  2008/01/02 09:50:26  tfredvik
; MODIFICATION HISTORY: # ::_image_final_touch: perform sigrange on image before alog10 in order to prevent "Floating divide by 0" errors
; MODIFICATION HISTORY: # ::cleanup: changed output string: "Cleaning up" -> "Cleaning up (hinobs)"
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.13  2007/12/19 09:48:38  tfredvik
; MODIFICATION HISTORY: # init: added keywords maxplotsz and noprep.
; MODIFICATION HISTORY: # read_data: run xrt_prep if keyword noprep is not set when creating the object.
; MODIFICATION HISTORY: # _ensure_title: use gt_fkw instead of fxpar to prevent crash if xrt_prep has been run (*header will be a structure after prepping, not a strarr)
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.1  2007/12/17 13:27:44  tfredvik
; MODIFICATION HISTORY: # Next generation handles XXX_prep
; MODIFICATION HISTORY:
; MODIFICATION HISTORY: Revision 1.12  2007/12/10 13:59:00  tfredvik
; MODIFICATION HISTORY: # Added method _image_final_touch
; MODIFICATION HISTORY: # Added doc header
; MODIFICATION HISTORY:
;;          31. jan 2011 Terje Fredvik  # Do not set version (SVN revision is
;                                      set in hinobs__define)
;            
;           14. Dec 2011 Terje Fredvik Added method _mk_jpg2000_specific -
;                        a Helioviewer compatible jpg2000 image
;           17. Apr 2012 Terje Fredvik Changed XCEN/YCEN correction values for
;             quicklook files. Separate offset values for G-band observations.
;           25. Jul 2012 Terje Fredvik New methods for fitting the limb and 
;               estimating an offset to X/YCEN: ::_get_and_set_xycencorrections, 
;               ::_gt_limbfit_fulldisk_candidate, ::_do_limb_fit and 
;               ::_find_xycencorretion_closest_pointing_nos
;           14. Sep 2012: Terje Fredvik
;                         ::_find_xycen_delta_closest_pointing_no: pick only
;                         one file per pointing_no. Added check if no files
;                         within the pointing_no range were found.
;           20. Sep 2012: Removed
;                         ::_find_xycen_delta_closest_pointing_no. Renamed
;                         ::_gt_limbfit_fulldisk_candidate to
;                         ::_gt_limbfit_best_fulldisk. Changed logic in
;                         ::_get_and_set_xycen_delta_and_rsun_arcsec so that
;                         we only search for suitable full disk images within
;                         a specified pointing_no range, do not interpolate 
;                         pointing correction values from the archive. 
;         11. Apr 2013 Terje Fredvik: added info about new superclass anyobs
;         05. Feb 2015 Terje Fredvik - # Try to run the pointing adjustments of 
;                                      xrt_prep/xrt_rad_coaldb. If it fails
;                                      (due to missing auxilliary files)
;                                      run our limb detection routines
;                                      instead. xycen_delta will be non-zero
;                                      only when the latter method has been
;                                      performed (in order to make the archive
;                                      software able to locate which XRT files
;                                      that needs to be reprocessed when the
;                                      files needed by the xrt_prep adjustment
;                                      routines are available).
;                                      # Replaced ::_get_xycen_delta_and_rsun_arcsec 
;                                      with ::_get_xycen_delta (rsun_arces is
;                                      now set in anyobs__define). 
;           05. May 2015 Terje Fredvik: ::_gt_limbfit_best_fulldisk: Do not
;                                       order search results in order to prevent
;                                       code from crashing when run on SDC
;                                       backup server:
;                                       osdc->order,''. ::_do_limb_fit
;                                       now calls sdcobj instead of obsolete
;                                       hobj when creating a full disk XRT
;                                       object
;           24. Aug 2015 Terje Fredvik  ::_mk_jpg2000_specific: create file 
;                                       "Pointing_may_be_inaccurate" in
;                                       jpg2000 out directory if
;                                       xrt_read_coaldb failed. 
;           26. Oct 2021 Terje Fredvik  ::_get_xycen_delta: Ensure that limb
;                                       fitting is performed if xrt_prep coalignment failed 
;
;   
;         
;-
;

FUNCTION xrtobs::init, file, _ref_extra=extra
  IF self->hinobs::init(file, _extra=extra) EQ 0 $
  THEN return,0
  
  self.thumbsmoothsz = 7
  
  not_set = -1
  self.gband = not_set
  
  return,1
END


PRO xrtobs::cleanup
  self->hinobs::cleanup
 
  print,"Cleaning up (xrtobs)"
END


PRO xrtobs::_instrument_specific_read_method, data, header
  o = self
  o->private
  IF o->get(/prep) THEN xrt_prep, self.file, 0, header, data,/despike,/quiet,dark_type=2 $
                                  ELSE data = readfits(o.file, header,/silent)
END


PRO xrtobs::_ensure_instrument_specific_properties
  ;;  Quicklook files have incorrect XCEN AND YCEN; XCEN is offset by
  ;;  approximately -30", YCEN by -20". Let's fix it! UPDATE 17 Apr 2012 - The
  ;;  quickook files now seem to be offset by a completely different amount,
  ;;  now the offset is apporximately +15/20" in X/YCEN, and 0"/+25" for white
  ;;  light images.
  o = self
  o->private
  header = *(o->get(/header))
  
  o->_ensure_gband
  
  ;; Is the file a Quicklook file? If so, adjust XCEN and YCEN
  IF stregex(gt_fkw(header,'ORIG_RF0'),'Quick',/boolean) THEN BEGIN 
     IF self->get(/gband) THEN BEGIN ;gt_fkw(header,'ec_fw2_') EQ 'Gband' THEN BEGIN 
        o->_instrument_specific_properties_modify_xycen, header, 'XCEN',0 ;-30.
        o->_instrument_specific_properties_modify_xycen, header, 'YCEN',25 
     ENDIF ELSE BEGIN 
        o->_instrument_specific_properties_modify_xycen, header, 'XCEN',15 ;-30.
        o->_instrument_specific_properties_modify_xycen, header, 'YCEN',20 ;-20.
     ENDELSE
     
     ;; Replace old header with new modified header
     IF ptr_valid(o.header) THEN ptr_free,o.header
     o.header = ptr_new(header,/no_copy) 
  ENDIF
END

PRO xrtobs::_instrument_specific_properties_modify_xycen, header, keyword, add
  ;; If only read_header has been called, the header will be a strarr. If
  ;; read_data has been called, the header is a structure. We handle the two
  ;; cases separately:
  self->private
  keyword = strupcase(keyword)
  IF size(header,/tname) EQ 'STRUCT' THEN BEGIN
     ;; Header is a structure. Simply modify the value of the correct structure tag
     val = gt_fkw(header,keyword)
     header.(where(tag_names(header) EQ keyword)) = val + add
  ENDIF ELSE BEGIN
     ;; Header is a string array.
     val = fxpar(header, keyword, start=ix) ; Value of keyword we will change
     entry = header[ix] ; String containing keyword, "=', keyword value and spaces
     pos = strsplit(entry, ' ', length=length) ; Find where and length of non-blanks
     val += add ; calculate new value
     strput,entry,strtrim(val,2),pos[2] ; replace old value by new value
     header[ix] = entry ; replace original full header string by modified string
  ENDELSE
END


PRO xrtobs::_ensure_title
  o = self
  o->private
  o.title = ptr_new(strarr(o->get(/nicons)))
  header = *(o->get(/header))
   
  (*o.title)[*] = 'FW1: '+gt_fkw(header,'ec_fw1_') + $
                  ', FW2: '+gt_fkw(header,'ec_fw2_') 
END



FUNCTION xrtobs::_image_final_touch, im, _extra=extra
  self->private
  
  ;; If FOV reaches beyond rmax there might be pixels that are weaker than the
  ;; weakest pixel on disk, and this may mess up the color table (we don't
  ;; want to waste valuable color table entries on weak off limb
  ;; pixels). Therefore first check that the full FOV is within r = rmax
  
  rmax = 1050. ;; Radius in ", 1050 will include a small distance beyond limb
  
  header = *(self->get(/header))
  xcen = gt_fkw(header,'xcen')
  ycen = gt_fkw(header,'ycen')
  fovx = gt_fkw(header,'fovx')
  fovy = gt_fkw(header,'fovy')
  
  count = 0 ; will be the # of pixels inside rmax if FOV is partially off limb
  
  gband = self->get(/gband)
  
  IF gband THEN fraction=0.9995 ELSE BEGIN  
     ;; For white light images the following method doesn't work very
     ;; well (there is hardly any counts outside the limb)
     
     ;; The distance from sun centre to the farthest corner of the FOV 
     farthestcorner = sqrt((abs(xcen)+fovx/2.)^2 + (abs(ycen)+fovy/2.)^2.)
     
     IF farthestcorner GT rmax THEN BEGIN 
        ;; Parts of the FOV are outside rmax.  Extract a circular sub image
        ;; with a radius of rmax arcsecs from disc centre. Do a sigrange of
        ;; this sub image, apply the range of the sigranged sub image to the
        ;; full image. Then do an alog and bytescale
        
        xscale = gt_fkw(header,'xscale')
        yscale = gt_fkw(header,'yscale') 
        naxis1 = gt_fkw(header,'naxis1')
        naxis2 = gt_fkw(header,'naxis2')
        
        dist_circle, r, [naxis1,naxis2], $
                     (naxis1-1)/2. - xcen/xscale, (naxis2-1)/2. - ycen/yscale
        
        r *= xscale ;; assumes xscale eq yscale...
        ondisc = where(r LT rmax, count)
     ENDIF
  ENDELSE
  imsub = (count GT 0) ? im[where(r LT rmax)] : im
  dummy = sigrange(imsub, range=range, fraction=fraction)
  im = im > (range[0] >  0) < range[1]

  IF gband THEN im = bytscl(im,/nan) ELSE im =  bytscl(alog(im),/nan)
  
  return, im 
END



PRO xrtobs::_get_xycen_delta, fit_limb=fit_limb
  ;;
  ;; If fit_limb isn't set we want the xrt_prep pointing adjustment to be
  ;; run. In this case the xycen_delta structure tag is set to a pointer to
  ;; [0,0]. If the built in pointing fix fails we want the SDC limb fitting 
  ;; code to be run instead. xycen_delta will then be a pointer to the
  ;; [xcen_delta, ycen_delta] array created by the SDC limb fitting code.
  
  o = self
  o->private
  
  
  ;; Check if xrt_rad_coaldb (called by xrt_prep) failed (e.g. due to missing
  ;; .geny files or missing entries in the files). If so we set fit_limb to 2 
  ;; in order to force our limb fitting code to be executed.
  IF fit_limb EQ 0 THEN BEGIN 
     h = o->hget(/history)
     FOR i =0, n_elements(h)-1 DO IF strpos(h[i],'XRT_READ_COALDB') NE -1 THEN BEGIN
        coal = strmid(h[i],1,/reverse_offset)
        CASE coal OF 
           '1': coaltxt = 'Cross Correlation results (AIA and XRT)' 
           '2': coaltxt = 'Limb fitting results (G-band)' 
           '3': coaltxt = 'UFSS data' 
           '4': coaltxt = 'Limb fitting results (X-ray)' 
           '6': coaltxt = 'Other methods' 
           ELSE: coaltxt ='Unknown correction method (check xrt_read_coaldb for updates)'
        ENDCASE 
        IF coal EQ '-1' OR strmid(coaltxt,0,7) EQ 'Unknown' THEN BEGIN 
           fit_limb = 2
           box_message,'xrt_read_coaldb failed to update pointing. Running SDC limb fitting'
        ENDIF ELSE box_message,'xrt_read_coaldb calibration type: '+coaltxt
     ENDIF 
  ENDIF
  
  
  IF fit_limb EQ 1 THEN BEGIN    
     ;; Is the x/ycen correction present in the SDC archive for this file?
     ;;   YES: restore x/ycen correction
     ;;   NO:  try to fit limb for present pointing_no and FW combination
     ;;               
     ;; Get the x/ycen correction for the present pointing_no and FW
     ;; combination from the archive. If no correction exist for the present
     ;; pointing_no/FW combo, check if it's possible to to a limb fit.  for
     ;; the present pointing_no or the correction for the closest pointing
     ;; number. First check if the x/y correction is already present in the
     ;; archive
     
     ;; Find the the corrections to xcen and ycen from the archive
     print,'...finding xycen_delta from the archive'

     osdc=obj_new('osdc')
     osdc->show,'FILE, XCEN_DELTA, YCEN_DELTA'
     osdc->condition,'anon: y'
     osdc->condition,'FILE: '+prune_filename(self->get(/file))
     osdc->order,''
     osdc->search, out
     obj_destroy,osdc
     
     IF out.xcen_delta EQ 0 THEN $ ;; Assuming that all calculated deltas are NE 0
        fit_limb =  2 $
     ELSE xycen_delta =  [out.xcen_delta, out.ycen_delta]
  ENDIF
  
  
  IF fit_limb EQ 2 THEN BEGIN 
     ;; Get all potential fulldisk XRT images for o.pointing_no. 
     path = o->_gt_limbfit_best_fulldisk()
     
     ;; If some fulldisk images have been found, do limb fitting. If no
     ;; suitable fulldisk images have been found within the specified
     ;; pointing_no range, set xycen_delta to [0,0]
     IF path NE '' THEN $
        xycen_delta = o->_do_limb_fit(path) ELSE xycen_delta = [0,0]
  ENDIF
  
  
  IF fit_limb GE 1 THEN BEGIN 
     IF ptr_valid(o.xycen_delta) THEN ptr_free,o.xycen_delta
  ENDIF ELSE xycen_delta = [0,0]
  
  o->set, xycen_delta=ptr_new(xycen_delta)
  
END



FUNCTION xrtobs::_gt_limbfit_best_fulldisk
  ;; Find good fulldisk candidates. We're only interested in high S/N
  ;; ratio images, or else the fit is often not that good. It's much better to
  ;; interpolate the pointing correction from the values obtained for
  ;; pointing_no's that is quite close in time than using values calculated
  ;; based on a low S/N image. In the regular SDC pipeline we ingest one file
  ;; at a time and therefore 
  
  self->private
    
  ;; Find the pointing_no of self
  pointing_no = self->get(/pointing_no)
  

  
  header = self->extract_header()  
  
  ;; Check if the current image is a Gband image or not. This will determine the
  ;; minimum required exposure time and FW2 postion for full disk image
  gband = self->get(/gband);(gt_fkw(header,'EC_FW2_') EQ 'Gband') ? 1 : 0 ; Gband observation?
  minexptime = (gband) ? 0.007 : 0.2                     ; 
  
  ;; The range of pointing_no's we're interested in. We want to use a range
  ;; that's so large than we find a good fulldisk image within this range, but
  ;; we don't want the range to be so large that the search takes a long time
  ;; or, and most importantly, that the time difference between the image
  ;; we're looking at and the closest good fulldisk image is too large,
  ;; i.e. delta T should be less than ~10 days or so. With n=150 all delta Ts
  ;; are less than 11 days for non-Gband, median difference is .5 days. For
  ;; G-band however we need to decrease the n to ~40 to get a max time
  ;; difference of ~10 days. But that will give 16 images where no full disk
  ;; can be found, so let's increase to 130, that will give a max time
  ;; difference of ~13 days (and only 5 images without corresponding fulldisk)
  n = (gband) ? 130 : 150 ;; 
  lowlim = trim(pointing_no - n)
  highlim = trim(pointing_no + n)
  
  ;; Note that there are three (actually more, but I don't need to consider
  ;; more than three) tables involved when determining the filter wheel
  ;; positions; X__EC_FWn_ is the text (e.g. Al_mesh). Also X__EC_FWn and
  ;; X__EC__FWn__ID_, and we use the latter for quering the database, but the
  ;; former is the one that is showed on the SDC web page. For FW2: 
  ;;   GBand:   *FW2 = 3  *FW2__ID_= 1.  
  ;;   Al_Mesh: *FW2 = 2, *FW2__ID_= 4.  
  ;;   Ti_poly: *FW2 = 1, *FW2__ID_= 6.
  fw2_id = (gband) ? '1' : '4,6'   
  
  print,'...finding fulldisk candidates'
  osdc=obj_new('osdc')
  osdc->show,'FILE,INSTRUME,DATE_OBS,EXPTIME,XCEN,YCEN,FOVX,FOVY,X__EC_FW1_,X__EC_FW2_,X__EC_IMTY_,POINTING_NO,THUMBS,NAXIS1,NAXIS2,X__PIXCNT, X__CHIP_SUM, STATUS, X__PLATESCL, VER_RF0'
  osdc->limit,0
  osdc->condition,'INSTRUME: ,XRT'
  ;osdc->condition,'anon: y'
  osdc->condition,'FOVX: >2000'
  osdc->condition,'FOVY: >2000'
  osdc->condition,'CEN_RADIUS: <200'
  osdc->condition,'EXPTIME: >'+trim(minexptime)
  osdc->condition,'SS__L1LEAD: y'
  osdc->condition,'X__EC_FW1__ID_: ,1' ; Open
  osdc->condition,'X__EC_FW2__ID_: ,'+fw2_id
  osdc->condition,'X__EC_IMTY__ID_: ,1' ; Normal only (no dark)
  osdc->condition,'POINTING_NO: > ' + lowlim + ' < ' + highlim
  osdc->condition,'VER_RF0: '+ self->get(/ver_rf0)
  osdc->order,''
  IF gband THEN osdc->condition,'X__EC_VL__ID_: ,3' ; Visible light shutter open
  osdc->search,out
  
  path = ''
  
  IF size(out,/tname) EQ 'UNDEFINED' THEN good_files_found = 0 ELSE BEGIN
     ;; Fitting fails if many pixels are missing. Only consider images with 
     ;; less than 100-min_fractionpixnt percent pixels missing
     min_fractionpixcnt =  0.98
     goodix = where(out.x__pixcnt GT min_fractionpixcnt*out.naxis1*out.naxis2)
     
     IF goodix[0] EQ -1 THEN good_files_found = 0 ELSE BEGIN 
        good_files_found = 1
        out = out[goodix]       
        same_pointing_no_ix = where(out.pointing_no eq pointing_no)
        IF same_pointing_no_ix[0] NE -1 THEN BEGIN 
           ;; Pick all fulldisk candidates with the same pointing_no
           out = out[same_pointing_no_ix] 
        ENDIF ELSE BEGIN 
           ;; Pick all fulldisk candidates with closest pointing_no(s)
           closest_ix = gt_closest_index(pointing_no, out.pointing_no,/all)
           out = out[closest_ix]
        ENDELSE 
     ENDELSE 
 
    
     ;; Code taken from Stein Vidar's mk_xrt_synops2.pro
     IF good_files_found THEN BEGIN 
        paths =  osdc->paths(out,/fetch) 
        lastgood = -10
        FOR i=0,n_elements(paths)-1 DO BEGIN 
           print,"Checking "+out[i].file
           fxread,paths[i],data,hdr,/compress
           h = histogram(data,omin=omin)
           filling = 1
           IF omin EQ 0 THEN BEGIN
              filling = 0.1
              IF float(h[0])/n_elements(data) GT 0.01 THEN filling = 0
           END
           e = out[i]
           sig = sigma(data)
           n = n_elements(data)
           bottom = data[(sort(data[lindgen(n/20)]))[n/2000]]
           data = (data-bottom) > 1
           goodness = sig/(bottom > 1)*filling
           
           data = data/e.exptime/e.x__chip_sum
           det = e.x__ec_fw1_+e.x__ec_fw2_
           
           quicklook = out[i].status EQ 'Quicklook'
           
           ;; Open - Al_mesh observations are peppered with spots which increase
           ;; the sig value of the image. We therefore subtract 0.85 (found by trial
           ;; and error) from the goodness value:   
           goodness = goodness - 0.85*(det EQ 'OpenAl_mesh')
           
           goodness = goodness - quicklook
           
           IF goodness LT lastgood THEN BEGIN
              print,"CONTINUE b/c not good enough",goodness
              CONTINUE
           END
           
           print,"Good enough: ",goodness," Quicklook: ",quicklook
           
           goodix = i
           
           lastgood = goodness
           obj_destroy, osdc
           
        ENDFOR 
        path = paths[goodix]
        box_message,'Using correction for PN '+$
                    trim(out[goodix].pointing_no)+', dPN='+$
                    trim(pointing_no-out[goodix].pointing_no)+', '+$
                    prune_filename(path)+'. PLATESCL: '+$
                    trim(self->hget(/platescl))+', '+$
                         trim(out[goodix].x__platescl)+' (fd)'
     ENDIF

  ENDELSE 

  
  return, path
  
END


FUNCTION xrtobs::_do_limb_fit, path, plot=plot
  self->private
  
  default, fraction, 5.5  ;5.5
  default, width, 3
  default, plot, 0
  
  fulldisk = sdcobj(path)
  
  data = fulldisk->extract_data()
  
  header = fulldisk->extract_header()
  
  scale = gt_fkw(header,'platescl') 
  rsun = fulldisk->extract_rsun_arcsec()/scale   
  
  ;; We trust our estimation of the rsun rather well, but still to be even
  ;; more sure that we cover the entire limb we increase the width 3 pixels
  ;; (we can do this since the G-band sobel image has a very well defined
  ;; edge).
  IF self->get(/gband) THEN width += 3 
  ;IF self->get(/gband) THEN rsun -= 4
  
  nx = (size(data))[1]
  ny = (size(data))[2]
  
  x = rebin(reform(findgen(nx),nx,1),nx,ny)-nx/2
  y = rebin(reform(findgen(ny),1,ny),nx,ny)-ny/2
  r = sqrt(x^2+y^2)
  r = shift(r,nx/2,ny/2) 
  
                                ;  scaled_data = data < max(data) * fraction
  scaled_data = data < median(data[where(data GT 0)]) * fraction
  img = sobel(scaled_data)
  
  conv = abs(fft(fft(exp(-(r-rsun)^2/width^2),-1)*fft(img,-1),1)) 
  
  ix = where(conv eq max(conv))
  sspix = array_indices(conv, ix) ;; pixel coordinates of fitted suncentre
  
  xycen_fit = ([nx/2., ny/2.]-sspix)*scale ;; fitted coordinates of image centre 
  
  fulldisk->extract_pointinginfo,xcen,ycen
  xycen_delta = xycen_fit - [xcen,ycen] ;; amount of x/ycen adjustment needed
  
  IF keyword_set(plot) THEN BEGIN 
     plot_image,shift(sigrange(img),nx/2,ny/2)
     cont = exp(-(shift(r,sspix[0]-nx/2,sspix[1]-ny/2)-rsun)^2/$
                width^2)
     contour,cont,/overplot,nlevels=1,color=255,c_line=0
     contour,cont,/overplot,nlevels=1,color=0,c_line=2
     print,'x/ycen must be adjusted by '+trim(xycen_delta[0])+'", '+$
           trim(xycen_delta[1])+'"'
     
     plot_image,conv
     plots,[sspix[0]-rsun,sspix[0]+rsun],[sspix[1],sspix[1]]
     plots,[sspix[0],sspix[0]],[sspix[1]-rsun, sspix[1]+rsun]
     plot_image,scaled_data
     
     plots,[sspix[0]-rsun,sspix[0]+rsun],[sspix[1],sspix[1]]
     plots,[sspix[0],sspix[0]],[sspix[1]-rsun, sspix[1]+rsun]
     
     tvcircle,rsun,sspix[0],sspix[1]
     tvcircle,rsun,sspix[0],sspix[1],color=0,line=2
  ENDIF
  
  obj_destroy, fulldisk
  
  return, xycen_delta
  
END

PRO xrtobs::_mk_jpg2000_specific, im, header, dir, fitsname, outdir=outdir
  self->private
  hv_hin_xrt2jp2_specific, im, header, dir, fitsname, outdir=outdir, err=err
  IF err NE '' THEN message, err,/info ELSE BEGIN 
     h = self->hget(/history)
     datestring = strmid(fitsname,strpos(fitsname,'level0')+7,11)
     FWstring = 'FW1_'+self->hget(/EC_FW1_)+'_FW2_'+self->hget(/EC_FW2_)
     pointingmaybeinaccurate = outdir + 'write/v0.8/jp2/XRT/' + datestring + FWstring + $
                               '/Pointing_may_be_inaccurate'
     FOR i =0, n_elements(h)-1 DO IF strpos(h[i],'XRT_READ_COALDB') NE -1 THEN BEGIN
        coal = strmid(h[i],1,/reverse_offset)
        IF coal EQ '-1' THEN BEGIN 
           print, 'dir: '+dir
           print,'outdir: '+outdir
           print,'xrt_read_coaldb failed: creating file '+pointingmaybeinaccurate
           openw, lun, pointingmaybeinaccurate, /GET_LUN 
           free_lun, lun
        ENDIF ELSE BEGIN
           file_delete, pointingmaybeinaccurate,/allow_nonexistent
           print,'xrt_read_caoldb run successfully: deleting file '+pointingmaybeinaccurate
        ENDELSE 
        
     ENDIF
     
  ENDELSE 
  
  
END

PRO xrtobs::_ensure_gband
  self->private
  IF ~self->is_set(/gband) THEN BEGIN 
     header = self->extract_header()
     self.gband = (gt_fkw(header,'ec_fw2_') EQ 'Gband' && $
     gt_fkw(header,'ec_fw1_') EQ 'Open') ? 1 : 0
  END
END

FUNCTION xrtobs::get_gband
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.gband
END

PRO xrtobs__define
  INT = 0
  d = {xrtobs, $
       inherits hinobs, $
       gband:INT}
  
END
