;+
; Project     :	SOHO - CDS
;
; Name        :	CPT_DETAILS
;
; Purpose     :	Prepares the CDS command tables from the details science plan.
;
; Explanation :	This routine interrogates the databases describing the science plan
;               and produces the tables needed to implement the plan on the CDS.
;
;               Studies in the sci_details database may either be time tagged or follow the 
;               previous study sequentially.
;               The latter involves putting commands into the previous study which depend on 
;               the following study and thus restrict the re-usability of sequential studies. 
;
;               The initial state for a time tagged study is reached by command from the 
;               deferred command store so that the current state does not affect the series table.
;               The initial state for a sequential study is reached from the series tables
;               associated with the previous study.
;               Thus at the start of a study the state parameters are set up for the first raster
;               in the study.
;               
;               CPT loops through the science plan in reverse so that information on the next study is 
;               known at the time of processing each study. 
;
;               In the series_id database each study is labelled 
;               by its initial pointing and deferred pointing.  
;               The initial pointing is the pointing of the first raster in the study 
;               whether deferred or fixed. The deferred pointing is taken from the point_details
;               entry if present else is zero.
;               Only studies with the same initial and deferred pointings are re-usable in the sense
;               of not having to load the series tables onto the CDS spacecraft again.
;
;               Studies are also labelled by the sequential study ID which is the CDHS ID of the next study
;               if it follows sequentially. 
;               If the next study is time tagged then this is field is set to -1 
;               (=#FFFF which is not a valid CDHS ID).
;
;               Studies may be repeated using the repeat n_pointings and repeat n_repeat_s options.
;               This is implemented by daisy-chaining series together and so uses n_pointings*n_repeat_s
;               series ( though some may be re-used).
;
;               The emergency study is present and will be loaded if the EMERGENCY keyword is set.
;
;               For engineering studies :
;                  the pointing and slit number must be unchanged;
;                  the GSETID parameters must be unchanged.
;               No longer need :   
;                  the following study must be time tagged to allow a call to a warm setup series
;                  within the engineering series table;
;                  the warm start up table is called within the engineering table at the creators
;                  discretion.
;
;               Engineering studies may still have deferred pointing and may be time tagged or
;               follow on sequentially from the previous study. They may also now be repeated indefinitely.
;
;               If raster, dexwin or vdswin tables have previously been encountered they will not be 
;               processed again. The times associated with the commprep database entries will be the
;               first encountered i.e the latest.    
;                                         
; Use         :	<cpt_details, ltb_unit, details_list, flag_studies, initial_state, range, series_list, cut_off_time> 
;
; Inputs      :	ltb_unit       : logical unit for LTB file
;               details_list   : structure array of studies from sci_details database.
;               flag_studies   : structure containing info about corresponding flag studies.
;               initial_gsetid : integer giving initial gset_id of CDS instrument.
;               range          : integer giving acceptable range of mirror and slit movements.
;               cut_off_time   : DOUBLE TAI format time before which tables may be overwritten
;
; Opt. Inputs : None.
;
; Outputs     : Produces the various CVT files needed for the science plan.
;               series_list  : list of series tables for studies.
;
; Opt. Outputs:	None.
;
; Keywords    : EMERGENCY : indicates presence and whether to load the emergency study.
;               ADJUST_ALLOWED : allow adjustment of mirror and slit positions to avoid 
;                                OPS movement if possible. If ADJUST_ALLOWED=2 then 
;                                explicit movement of the slit is allowed for NIS rasters.
;		MAX_SLIT_POS   : Use the number passed through this keyword as the maximum
;				 allowed slit position.  This value is used to offset GIS rasters
;				 in the N/S direction in such a way that they do not exceed 
;				 this value.
;
;
;
; Calls       :	get_detail, get_study, get_raster, get_datawin,
;               chk_fund_series, get_series_info, get_raster_info,
;               get_dexwin_info, get_vdswin_info,
;               wr_vdswin_file, wr_dexwin_file, wr_raster_file, wr_series_file,
;               wr_ltb, anytim2cal, set_exposure, update_dex, nxt_series_id, fix_series_id.
;                
; Common      :	None.
;
; Restrictions:	None.
;
; Side effects:	None.
;
; Category    :	Command preparation.
;
; Prev. Hist. :	None.
;
; Written     :	Version 0.0, Martin Carter, RAL, 29/9/95
;                            Split off from the CPT main routine.
;                            Used named structures for table lists.
;
; Modified    :	Version 0.1, 11/10/95, MKC
;                            Corrected bug dealing with GIS exposure time.
;                       0.2, 17/10/95, CDP
;                            Corrected addition of VDS_OFFSETS.
;                       0.3, 18/10/95, MKC
;                            Added extra mod. to VDS_OFFSETS and vds_centre tag logic.
;                       0.4, 31/10/95, MKC
;                            Added keyword /emergency.
;                       0.5, 1/11/95, MKC
;                            Added check that full VDS window not required before adding in VDS offsets.
;                       0.6, 2/11/95, MKC
;                            Changed emergency study logic slightly.
;                       0.7, 7/11/95
;                            Added gset_id to state structure and series_id database.
;                            Used fstate to carry the current_state to wr_series_file.
;                            Removed .dur tag from series.
;                            Updated .time tag to incorporate timings within repeats.
;                            Changed to hard fail on bad .repn
;                       0.8, 21/11/95
;                            Modified so that VDS_STATE is part of vdswin structure and vdswin_id
;                            database.
;                            Added so that IEF scheme only used for right detector.
;                       0.9, 23/11/95
;                            Fixed bug in raster span calculation.
;                            Fixed bug in initializing current_state.
;                            Removed state from argument list.
;                       1.0, 5/12/95
;                            Changed to mirror and slit spans.
;                            Added engineering studies.
;                            Added dsolx and dsoly tags to series structure.
;                            Rearranged repeat studies and deferred pointings.  
;                            Added gset_id parameters.  
;                            Added range and initial state to argument list.            
;                       1.1, 14/12/95
;                            Added reverse study processing.
;                       1.2, 2/1/96
;                            Checked which GIS detectors in use.
;                            Corrected treatment of deferred pointing.
;                            Added flag study tag to series structure.
;                       1.3, 15/1/96
;                            Fixed problem with initial gsetid.
;                       1.4, MKC, 29/1/96
;                            Added adjust_allowed keyword.
;                       1.5, MKC, 19/2/96
;                            Replaced vds_offset calculation with update_dex .
;                            Removed vds_state from argument list.
;                            Modified vdswin_id database.      
;                       1.6, MKC, 11/3/96
;                            Modified argument list to make explicit that only the initial gset id is 
;                            needed from the initial state.    
;                       1.7, MKC, 15/3/96
;                            Modified use of update_dex to include the gsetid and to refer to GIS windows also.
;                       1.8, MKC, 8/7/96
;                            Fixed bug to do with gset_id = -1 rather than 0 for non GSET studies.
;                       1.9, MKC, 5/9/96
;                            Modified adjust_allowed comment.
;                       2.0, MKC, 3/10/96
;                            Brought raster loop inside non-engineering study condition.
;                            Added cut off time to allow previous tables to be overwritten.
;                            Added solx, soly to raster structure. 
;                            Changed processing of repeat studies and repeat pointings to load into
;                            single set of series tables.
;                            Added printout of no. of tables used.    
;                            Fixed so that always gets new chds id for series table and updates series_id database
;                            in single go at the end.                              
;                            Put back a tag giving following study cdhs id and used instead of fseries where appropriate.
;                       2.1, MKC, 23/10/96
;                            Fixed raster pointing when raster previously used.                                    
;                       2.2, DMZ (GSFC), 11-Dec-96, changed to use GET_CDS_STUDY
;                            and GET_CDS_RASTER instead of GET_STUDY and
;                            GET_RASTER. New versions use IDL pointers and are
;                            much much faster.
;                       2.3, MKC, 18/2/97
;                            Corrected bug with re-using dexwin_details.
;                            Added gis tag to dexwin structure.
;                       2.4, MKC, 24/2/97
;                            Changed so that engineering studies must not change pointing or slit number.
;                            Study following an engineering study need no longer be time tagged.
;                       2.5, MKC, 21/4/97
;                            Removed deferred engineering study .type tag.
;                            Added n_rasters_def = 0 check for engineering study.
;                            Removed cdhs mode set to 'B'.
;                            Special studies may not change state of instrument.
;			2.6, REY, 08/05/03
;                            Added code to adjust the OPS position in order to compensate for  GIS 
;			     rasters along the Y axis (using the slit) that are not centered in the 
;			     middle of the slit range.  Added the MAX_SLIT_POSITION keyword to support
;			     this mode of operation.
;                       2.7, Zarro (L-3Com/GSFC), 11/10/03 
;                            Added check for undefined rasters
;			2.8, REY, 02/12/04
;			     Added a sign adjustment to the slit position offset to compensate for the
;			     case when the spacecraft is rolled 180 degrees.
;
; Version     :	Version 2.8. 02/12/04
;-
;**********************************************************

PRO cpt_details, ltb_unit, details_list, flag_studies, initial_gsetid, range, series_list, cut_off_time, $
                 EMERGENCY=emergency, ADJUST_ALLOWED=adjust_allowed, MAX_SLIT_POS = max_slit_pos

  ; set no. of studies 
  ; NB This includes the emergency study

  ndetails = N_ELEMENTS(details_list)

  ; get GSET ID for the end of each study
  ; for non GIS studies this will be the last used GSET ID
  ; NB sw uses gset_id=0 as null gset_id for VDS 
  ;    planning sw uses gset_id=-1 but this only encountered here
  
  gsetids    = INTARR(ndetails)

  IF details_list(0).gset_id LT 0 THEN gsetids(0) = initial_gsetid ELSE gsetids(0) = details_list(0).gset_id

  FOR stdn = 1, ndetails-1 DO $
    IF details_list(stdn).gset_id LT 0 THEN gsetids(stdn) = gsetids(stdn-1) $
                                       ELSE gsetids(stdn) = details_list(stdn).gset_id

  ; set up initial dummy values for following series

  fseries = {SERIES_STRUCTURE}
  fseries.cdhsid = -1          ; set invalid CDHS ID
  fseries.tt     = 1           ; set next study time tagged

  ; get next SERIES CDHS ID

  series_cdhsid = nxt_series_id()

  ; loop through studies within next CDS period in reverse order
  ; NB loop may include extra study added for emergency operation

  FOR stdn = 0, ndetails-1 DO BEGIN

    ; reverse processing order

    stdyno = ndetails - 1 - stdn 

    ; check if this is emergency study

    IF KEYWORD_SET(emergency) AND stdyno EQ ndetails-1 THEN BEGIN

      PRINT, ''
      PRINT, 'EMERGENCY STUDY'
      PRINT, ''

      ; copy emergency study details to science plan
      ; NB will not have the pointing structure appended
      ;    but this wont be needed for the emergency study
      ;    because the dummy details_list entry was created 
      ;    with n_pointings=0 and n_repeat_s=1
      ;    Emergency study must have fixed pointing

      sciplan_details = details_list(stdyno)

    ENDIF ELSE BEGIN

      PRINT, ''
      PRINT, 'DETAILED STUDY ', stdyno
      PRINT, ''

      ; extract science plan details
      ; NB sciplan details is the same structure as an element of details_list except
      ;    it has the pointings structure appended.

      get_detail, details_list(stdyno).date_obs, sciplan_details

    ENDELSE

    ; print some study details

    PRINT, 'STUDY OBJECTIVE     : ', sciplan_details.sci_obj
    PRINT, 'TIME OF OBSERVATION : ', ANYTIM2CAL ( sciplan_details.date_obs )
    PRINT, ''
    
    ; perform any checks on sciplan_details

    IF sciplan_details.n_repeat_s LE 0 THEN $
      MESSAGE, 'INVALID STUDY REPEAT NUMBER : ' + STRTRIM(sciplan_details.n_repeat_s,1)

    IF stdyno EQ 0 AND NOT sciplan_details.time_tagged THEN $
      MESSAGE, 'FIRST STUDY NOT TIME-TAGGED'

    ; extract study parameters (changed to use GET_CDS_STUDY by DMZ)

;    get_study, sciplan_details.study_id, sciplan_details.studyvar, study_details

    study_details=get_cds_study(sciplan_details.study_id,sciplan_details.studyvar)

    ; perform any checks on study_details

    IF ( study_details.usable NE 'Y' ) THEN MESSAGE, 'Study not usable'

    ; perform checks if engineering study

    IF study_details.study_id EQ 0 THEN BEGIN

      ; engineering study

      ; check no. of repeat studies

      IF sciplan_details.n_repeat_s NE 1 THEN $
         MESSAGE, 'INVALID ENGINEERING STUDY REPEAT NO. = ' + STRTRIM( sciplan_details.n_repeat_s,2 )

      ; check no. of repeat pointings
      ; ignore any deferred pointings at present

;      IF sciplan_details.n_pointings NE 0 THEN $
;         MESSAGE, 'NO DEFERRED POINTINGS ALLOWED FOR ENGINEERING STUDY = ' + STRTRIM( sciplan_details.n_pointings,2 )

      ; check default no. of rasters

      IF study_details.n_raster_def NE 0 THEN $
         MESSAGE, 'TOO MANY RASTERS FOR ENGINEERING STUDY = ' + STRTRIM( study_details.n_raster_def,2 )

    ENDIF

    ; if flag master then extract flag master scheme from database 
    ; if not flag master set value to 0

    IF sciplan_details.flag_master NE 0 THEN BEGIN

      ; get IEF info from IEF databases

      get_ief, ABS(sciplan_details.flag_master), flag_master_info

      ; if flag master scheme then modify IEF windows using calibration
 
      flag_master_info = update_dex ( flag_master_info, GSET_ID=details_list(stdyno).gset_id )
 
    ENDIF ELSE flag_master_info = 0

    ; set up no. of non-repeated rasters
    ; check first if special study 

    IF study_details.n_raster_def EQ 0 THEN BEGIN
      
      ; avoid raster loop

      nrasters = 0 

      rasters  = 0 

    ENDIF ELSE BEGIN

      ; loop through rasters

      nrasters = N_ELEMENTS ( study_details.rasters )

      rasters  = REPLICATE ( {RASTER_STRUCTURE}, nrasters )

      FOR rstrno = 0, nrasters-1 DO BEGIN  

        ; set up no. of times to execute raster

        repn = study_details.rasters(rstrno).n_repeat_r

        ; check sensible no. of rasters

        IF repn LE 0 THEN $
	   MESSAGE, 'Invalid raster repeat number =' + STRTRIM ( repn, 1)

        ; if last raster set repn to n_rasters1

        IF (rstrno EQ nrasters-1) THEN $
	  repn = sciplan_details.n_rasters1

        ; check if raster already in raster list
        ; check raster id, var, repn

        raster_index = -1

        IF N_ELEMENTS(raster_list) GT 0 AND sciplan_details.flag_master EQ 0 AND flag_studies(stdyno).cdhsid EQ 0 THEN BEGIN

          idlist = WHERE ( raster_list.id EQ study_details.rasters(rstrno).ras_id, count )

          IF count GT 0 THEN BEGIN

            varlist = WHERE ( raster_list(idlist).var EQ study_details.rasters(rstrno).ras_var, count )

            IF count GT 0 THEN BEGIN

              repnlist = WHERE ( raster_list(idlist(varlist)).repn EQ repn, count )

              IF count GT 0 THEN raster_index = idlist(varlist(repnlist(0)))
    
            ENDIF

          ENDIF

        ENDIF 

        ; check if first time raster encountered

        IF raster_index LT 0 THEN BEGIN

          ; get raster details (changed to use GET_CDS_RASTER by DMZ)

;          get_raster, study_details.rasters(rstrno).ras_id, $
;                      study_details.rasters(rstrno).ras_var, raster_details

           raster_details=get_cds_raster(study_details.rasters(rstrno).ras_id, $
                      study_details.rasters(rstrno).ras_var)

          ; set up known raster table parameters

          raster = {RASTER_STRUCTURE}

          raster.id   = study_details.rasters(rstrno).ras_id
          raster.var  = study_details.rasters(rstrno).ras_var
          raster.time = sciplan_details.date_obs

          ; save detector  

          raster.detector = raster_details.detector

          ; set up VDS and GIS exposure time and units

          set_exposure, raster_details.exptime, raster

          ; save slit number

          raster.slitn = raster_details.slit_num

          ; check slit number valid

          IF (raster.slitn LT 1) OR (raster.slitn GT 6) THEN $
            MESSAGE, 'Invalid slit number used : ' + STRTRIM ( raster.slitn, 1)

          ; write mirror and slit spans to raster structure
          ; NB x/y steps from planning database are in arcsecs rather than steps
          ;    spans are translated into steps

          raster.mirrorspan = ((raster_details.nx-1)*raster_details.xstep)/2

          raster.slitspan   = (raster_details.ny-1)*raster_details.ystep

          ; set up no. of times to execute raster

          raster.repn = repn

          ; set up IEF flag bits but only if raster is for right detector

          IF sciplan_details.flag_master NE 0 THEN BEGIN

            IF raster.detector EQ flag_master_info.detector THEN BEGIN

              PRINT, 'Raster is IEF flag master'
              PRINT, ''

              ; set up master bit = 13 

              raster.repn = raster.repn OR '2000'X

            ENDIF

          ENDIF
  
          IF flag_studies(stdyno).cdhsid NE 0 THEN BEGIN

            ; set up flag receiver bit = 12

	    raster.repn = raster.repn OR '1000'X

            ; set up flag repointing bit = 14

            IF flag_studies(stdyno).repoint THEN raster.repn = raster.repn OR '4000'X

          ENDIF

          ; check if raster table already loaded for this study and get table info

          get_raster_info, raster, cut_off_time

          ; check if dexwin already in dexwin list
          ; check dexwin id

          dexwin_index = -1

          IF N_ELEMENTS(dexwin_list) GT 0 THEN BEGIN

            idlist = WHERE ( dexwin_list.id EQ raster_details.dw_id, count )

            IF count GT 0 THEN dexwin_index = idlist(0)

          ENDIF 

          ; check if first time dexwin encountered

          IF dexwin_index LT 0 THEN BEGIN

            ; get data extraction window list details

            get_datawin, raster_details.dw_id, dexwin_details
 
            ; set up known dexwin table parameters

            dexwin = {DEXWIN_STRUCTURE}

            dexwin.id   = raster_details.dw_id
            dexwin.time = sciplan_details.date_obs

            ; modify dexwin values to reflect calibration
            ; get calibration version number
         
            dexwin_details = update_dex ( dexwin_details, GSET_ID = details_list(stdyno).gset_id, VERSION = calibration_version ) 

            dexwin.version = calibration_version

            ; check if GIS detector

            IF raster.detector NE 'N' THEN dexwin.gis(dexwin_details.wins.win_def(0)/2048) = 1

            ; check if dexwin table already loaded for this study and get table info

            get_dexwin_info, dexwin, cut_off_time
   
            ; write out dexwin CVT file

            wr_dexwin_file, dexwin, dexwin_details

          ENDIF ELSE BEGIN

            ; dexwin previously encountered

            dexwin = dexwin_list(dexwin_index)

          ENDELSE

          ; set up dexwin list and increment current position

          IF N_ELEMENTS(dexwin_list) EQ 0 THEN dexwin_list = [ dexwin ] $
                                          ELSE dexwin_list = [ dexwin_list, dexwin ]

          ; check if NIS detector

          IF raster.detector EQ 'N' THEN BEGIN

            ; check if vdswin already in vdswin list
            ; check vdswin id and flag master not set up

            vdswin_index = -1

            IF N_ELEMENTS(vdswin_list) GT 0 AND sciplan_details.flag_master EQ 0 THEN BEGIN

              idlist = WHERE ( vdswin_list.id EQ dexwin.id, count )

              IF count GT 0 THEN vdswin_index = idlist(0)

            ENDIF 
        
            ; check if first time vdswin encountered

            IF vdswin_index LT 0 THEN BEGIN

              ; check if vds windows loaded

              ; set up known vdswin table parameters

              vdswin = {VDSWIN_STRUCTURE}

              vdswin.id      = dexwin.id
              vdswin.time    = sciplan_details.date_obs
              vdswin.version = dexwin.version
              vdswin.iefid   = ABS ( sciplan_details.flag_master )

              ; check if vdswin table already loaded for this study and 
              ; get table info

              get_vdswin_info, vdswin, cut_off_time

              ; write out vdswin CVT file

              wr_vdswin_file, vdswin, dexwin_details, flag_master_info

            ENDIF ELSE BEGIN

              ; vdswin previously encountered

              vdswin = vdswin_list(vdswin_index)

            ENDELSE

            ; set up vdswin list and increment current position

            IF N_ELEMENTS(vdswin_list) EQ 0 THEN vdswin_list = [ vdswin ] $
                                            ELSE vdswin_list = [ vdswin_list, vdswin ]

            ; set up more raster parameters

            raster.vdswid = vdswin.cdhsid

          ENDIF ELSE BEGIN

            ; GIS detector

            ; check which GIS detectors in use

            raster.gis = dexwin.gis 

          ENDELSE

          ; set up more raster parameters

          raster.dexwid = dexwin.cdhsid

          ; write out raster CVT file

          wr_raster_file, raster, raster_details

          ; add raster to rasters array

          rasters(rstrno) = raster

        ENDIF ELSE BEGIN

          ; raster previously encountered
          ; add raster to rasters array

          rasters(rstrno) = raster_list(raster_index)

        ENDELSE

        ; add raster to raster list

        IF N_ELEMENTS(raster_list) EQ 0 THEN raster_list = [ rasters(rstrno) ] $
                                        ELSE raster_list = [ raster_list, rasters(rstrno) ]
      ENDFOR

      ; save raster pointings (not used to define raster)
      ; NB must be done outside of raster loop since raster may already have been used 
      ;    but may have different pointing

      rasters.solx     = study_details.rasters.ins_x
      rasters.soly     = study_details.rasters.ins_y

    ENDELSE

    ; set up known series table parameters

    series = {SERIES_STRUCTURE}

    series.id   = sciplan_details.study_id
    series.var  = sciplan_details.studyvar   
    series.time = sciplan_details.date_obs 

    ; set normal study tag

    series.flagst = 'N'
    
    ; set up range 

    series.range = range

    ; check if emergency study
    ; if emergency study then set infinite repeat tag

    IF KEYWORD_SET(emergency) AND stdyno EQ ndetails-1 THEN series.rpt = 'Y' ELSE series.rpt = 'N'

    ; set no. of times to repeat last raster and IEF flags if science study

    IF study_details.n_raster_def NE 0 THEN series.repn = raster.repn
 
    ; check if solar feature tracking required

    IF sciplan_details.tracking THEN series.sft  = 'Y' ELSE series.sft  = 'N'

    ; set flag indicating whether first call to study is time tagged

    series.tt = sciplan_details.time_tagged

    ; set up current gset ID 

    series.fstate.gsetid = gsetids(stdyno)

    ; set up deferred and relative raster pointings

    ; set up repeat pointings and repeat studies

    IF sciplan_details.n_pointings EQ 0 THEN npointings = 1 ELSE npointings = sciplan_details.n_pointings 

    ; combine study repeats and repeat pointings into single series
    ; NB the set of pointings are repeated for each study repeat
    ; duplicate rasters for study for each repeat

    temp_rasters = 0

    ; loop over study repeats 
    ; NB n_repeat_s should be > 0, npointings is > 0

    FOR s = 0, sciplan_details.n_repeat_s-1 DO BEGIN

      ; loop over different pointings 

      FOR p = 0, npointings-1 DO BEGIN

        ; Set delta_solary to 0 to indicate that no adjustment will be necessary.
        ; If we determine that we actually do need to make an adjustement, then we
        ; will reset delta_solary to the correct value.
        delta_solary = 0

        ;-- added safety check for rasters not being a structure (Zarro, 11/10/03)
        IF IS_STRUCT (rasters) THEN BEGIN

           ; Check if the KEYWORD max_slit_pos is defined.  If it is then we will adjust 
           ; the raster pointing to compensate for the adjusted slit range.
           IF KEYWORD_SET (max_slit_pos)       AND		$ 
              (rasters(0).detector EQ 'G')     AND		$
              (rasters(0).slitspan GT 0)       THEN BEGIN
       
              ; Calculate a new slit range and slit center based on the new maximum slit position.
              new_slit_center = max_slit_pos - rasters(0).slitspan/2.0

              ; Adjust the Y axis pointing for the offset in the slit center caused by the adjusted slit range.
              ;  If the enviroment variable SOHO_ORIENT is 180, then invert the correction.
              delta_solary = - new_slit_center
              if GETENV ('SOHO_ORIENT') EQ 180 THEN delta_solary = -delta_solary

           ENDIF

        ENDIF

        ; check if study has deferred pointing

        IF sciplan_details.n_pointings NE 0 THEN BEGIN

          ; get deferred pointing

          series.dsolx = sciplan_details.pointings(p).ins_x
          series.dsoly = sciplan_details.pointings(p).ins_y

          ; Adjust the pointing to compensate for the offset required by GIS slit movements (if needed).

          series.dsoly = series.dsoly + delta_solary

        ENDIF ELSE BEGIN

          ; Adjust the pointing to compensate for the offset required by GIS slit movements (if needed).
          ; But only do this if this is a regular science study, not an engineering study.

          IF study_details.n_raster_def NE 0 THEN rasters.soly = rasters.soly + delta_solary

        ENDELSE 

        ; check for deferred raster pointings

        list = WHERE ( study_details.rasters.pointing EQ 1, count )

        IF count GT 0 THEN BEGIN

          ; deferred pointing defined at details level

          rasters(list).solx = series.dsolx
          rasters(list).soly = series.dsoly

        ENDIF 

        ; check for relative raster pointings

        list = WHERE ( study_details.rasters.pointing EQ -1, count )

        IF count GT 0 THEN BEGIN

          ; relative pointing defined within the raster
          ; NB pointing now defined relative to the first raster in the study
          ; which has fixed or deferred pointing.
          
          rasters(list).solx = rasters(0).solx + rasters(list).solx
          rasters(list).soly = rasters(0).soly + rasters(list).soly

        ENDIF 

        ; extend rasters array

        IF s EQ 0 AND p EQ 0 THEN temp_rasters = rasters ELSE temp_rasters = [temp_rasters, rasters]

      ENDFOR

    ENDFOR

    rasters = temp_rasters

    ; check if special study (with n_raster_def=0)

    IF study_details.n_raster_def NE 0 THEN BEGIN

      ; study is normal study

      ; set slit number 

      series.istate.slitn  = rasters(0).slitn

      ; set up GSETID for study
      ; istate.gsetid and fstate.gsetid will be the same if GIS raster involved

      series.istate.gsetid = details_list(stdyno).gset_id

      ; set pointing

      series.istate.solarx = rasters(0).solx
      series.istate.solary = rasters(0).soly

    ENDIF ELSE BEGIN

      ; study is special study
      ; GSETID will be unchanged
      ; slit number will be unchanged
      ; pointing will be unchanged

      series.istate  = fseries.istate

    ENDELSE

    ; check if series reusable
    ; this forces creation of a new series table ID if :
    ;       1)  The study is a flag master.
    ;           In this case the series contains the IEF table. 
    ;       2)  The study is a flag receiver. 
    ;           In this case the series contains commands which depend on the 
    ;           flag study.
    ;       3)  The study has more than one deferred pointing associated with it.
    ; NB even if the next study follows on sequentially so that the current study
    ;    contains values dependent on the following study the current study may still
    ;    be re-used.

    IF (sciplan_details.flag_master NE 0 ) OR (flag_studies(stdyno).cdhsid NE 0) OR (npointings GT 1) $
       THEN series.reus = 'N' ELSE series.reus = 'Y'

    ; bypass check for series table loaded 

    series.cdhsid = series_cdhsid

    ; set up following study id

    series.nextst = fseries.cdhsid

    ; check if series table already loaded for this study and 
    ; get table info 
    ; NB may return array of series structures.

    get_series_info, series, cut_off_time

    ; write out series CVT file 
    ; NB may return array of series structures

    wr_series_file, series, fseries, sciplan_details, study_details, $
                    rasters, cut_off_time, flag_studies(stdyno), flag_master_info, $
                    ADJUST_ALLOWED=adjust_allowed, MAX_SLIT_POS = max_slit_pos

    ; increment series cdhs id

    series_cdhsid = series_cdhsid + N_ELEMENTS(series)

    ; printout no. of series uses

    PRINT, 'NO. OF SERIES TABLES USED = ', STRTRIM(N_ELEMENTS(series),1)
    PRINT, ''

    ; add to series list 

    IF N_ELEMENTS(series_list) EQ 0 THEN series_list = [ series ] $
                                    ELSE series_list = [ series_list, series ]

    ; set up following study info

    fseries = series(0)

  ENDFOR ; end of loop over details list of studies

  ; update series_id database in one go

  fix_series_id, series_list

  ; write out LTB file
  ; overwrites any existing file 

  PRINTF, ltb_unit, '# List of table indeces for sci_details studies
  PRINTF, ltb_unit

  ; write out lists of tables used and to be loaded

  IF N_ELEMENTS ( series_list ) GT 0 THEN wr_ltb, ltb_unit, series_list, 'SERIES', 'NEWSERIES'
  IF N_ELEMENTS ( raster_list ) GT 0 THEN wr_ltb, ltb_unit, raster_list, 'RASTER', 'NEWRASTER'
  IF N_ELEMENTS ( dexwin_list ) GT 0 THEN wr_ltb, ltb_unit, dexwin_list, 'DEXWIN', 'NEWDEXWIN'
  IF N_ELEMENTS ( vdswin_list ) GT 0 THEN wr_ltb, ltb_unit, vdswin_list, 'VDSWIN', 'NEWVDSWIN'

END





