!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_otf_psw(mrtset,backcal,backsci,filebuf,error)
  use gbl_message
  use gkernel_interfaces
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this => mrtcal_calib_otf_psw
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),    intent(in)    :: mrtset
  type(calib_backend_t),   intent(in)    :: backcal
  type(science_backend_t), intent(inout) :: backsci
  type(imbfits_buffer_t),  intent(inout) :: filebuf
  logical,                 intent(inout) :: error
  ! Local
  integer(kind=4) :: ion,non
  character(len=*), parameter :: rname='CALIB>OTF>PSW'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_init_onloop(backsci,non,error)
  if (error) return
  do ion=1,non
     call mrtcal_get_nexton(backsci,error)
     if (error) return
     call mrtcal_otf_psw_prepare_nextoff(mrtset,filebuf,backsci,error)
     if (error) return
     call mrtcal_init_dumpcycle_loop('onTheFly',mrtset,filebuf,backsci,error)
     if (error) return
     do while (backsci%switch%book%idump.lt.backsci%switch%book%ndump)
        call mrtcal_get_next_otfpsw_cycle(mrtset,filebuf,backsci,error)
        if (error) return
        if (backsci%switch%book%found) then
           call mrtcal_on_minus_off(isotfmap,notfsw,backsci,error)
           if (error) return
           call mrtcal_tscale_computation(backcal,backsci,error)
           if (error) return
           call mrtcal_tscale_application(backsci%tscale,backsci%diff,error)
           if (error) return
           call mrtcal_write_toclass(backsci%diff,mrtset%out,backsci%nspec,error)
           if (error) return
        endif
     enddo ! switch%book%idump
     call mrtcal_switch_book_list(backsci%switch%book,error)
     if (error) return
     ! User can interrupt the pipeline
     if (sic_ctrlc_status()) then
        error = .true.
        exit
     endif
  enddo !ion
  !
end subroutine mrtcal_calib_otf_psw
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_get_next_otfpsw_cycle(mrtset,filebuf,backsci,error)
  use gbl_message
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this=>mrtcal_get_next_otfpsw_cycle
  !---------------------------------------------------------------------
  ! @ private
  ! OTF PSW is an hybrid case between tracked PSW that only deals with
  ! subscan cycles and (WSW and FSW) that only deal with dump cycle. In
  ! OTF PSW, the ON is delivered as a dump, but the OFF is typically
  ! interpolated between two OFF subscans.
  !
  ! So here, we first read the current ON dump and put it in the
  ! cycle%data(1) structure, we then compute the OFF "phase" from one or
  ! two OFF subscans and put it in the cycle%data(2) structure. We can then
  ! fill the switching sections based on the cycle%desc structure.
  ! ---------------------------------------------------------------------
  type(mrtcal_setup_t),    intent(in)    :: mrtset
  type(imbfits_buffer_t),  intent(inout) :: filebuf
  type(science_backend_t), intent(inout) :: backsci
  logical,                 intent(inout) :: error
  !
  character(len=*), parameter :: rname='GET>NEXT>OTFPSW>CYCLE'
  !
  call mrtcal_find_next_dumpcycle(&
       filebuf%subscanbuf%subscan%backdata,&
       backsci%switch%book,error)
  if (error) return
  call mrtcal_read_next_dumpcycle(mrtset,&
       filebuf%imbf,&
       filebuf%subscanbuf%subscan,&
       filebuf%subscanbuf%databuf,&
       backsci%switch,&
       error)
  if (error) return
  if (backsci%switch%book%found) then
     call reassociate_chunkset_2d(backsci%switch%cycle%data(backsci%switch%cycle%ion),backsci%on%curr,error)
     if (error) return
     call mrtcal_otf_psw_select_off(mrtset,backsci,error)
     if (error) return
     backsci%off%curr => backsci%switch%cycle%data(backsci%switch%cycle%ioff)
     call mrtcal_fill_switch_section(backsci%switch%cycle,error)
     if (error) return
  endif
  !
end subroutine mrtcal_get_next_otfpsw_cycle
!
subroutine mrtcal_otf_psw_read_surrounding_offs(mrtset,imbf,subscanbuf,backsci,error)
  use gbl_message
  use gkernel_types
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this=>mrtcal_otf_psw_read_surrounding_offs
  !---------------------------------------------------------------------
  ! @ private
  ! Get in the current list of subscans the number of the previous and 
  ! the next OFF subscans, read them, average them in time, and store 
  ! them into backsci%off
  !
  ! That's a stack. The code must start the previous OFF subscan, because 
  ! it probably is the current next OFF subscan, as the subscans are 
  ! ordered by increasing time. This saves data reading. Setting the 
  ! subscan number to zero means that we did not find either the previous 
  ! or the next off
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),            intent(in)    :: mrtset
  type(imbfits_t),                 intent(in)    :: imbf
  type(subscan_buffer_t),          intent(inout) :: subscanbuf
  type(science_backend_t), target, intent(inout) :: backsci
  logical,                         intent(inout) :: error
  ! Local
  logical, parameter :: iscal = .false.
  logical :: found
  integer(kind=4) :: ioff,iprev,inext
  type(chunkset_2d_t), pointer :: altoff
  character(len=*), parameter :: rname='OTF>PSW>READ>SURROUNDING>OFFS'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Stack previous off
  ioff = backsci%on%curr%isub
  call eclass_2dble_getprev(backsci%list%onoff,backsci%list%off,ioff,found,error)
  if (error) return
  if (found) then
     iprev = backsci%list%isub(ioff)
     if (backsci%off%prev%isub.eq.iprev) then
        ! Nothing to be done...
     else if (backsci%off%next%isub.eq.iprev) then
        altoff => backsci%off%prev
        backsci%off%prev => backsci%off%next
        backsci%off%next => altoff
     else
        call mrtcal_average_times(mrtset,iscal,backsci%list%isub(ioff),'track',imbf,subscanbuf,backsci%off%prev,error)
        if (failed_calibrate(rname,backsci%list%isub(ioff),error))  return
     endif
  else
     backsci%off%prev%isub = 0 ! Nevertheless, leave the type allocated
  endif
  ! Stack next off
  ioff = backsci%on%curr%isub
  call eclass_2dble_getnext(backsci%list%onoff,backsci%list%off,ioff,found,error)
  if (error) return
  if (found) then
     inext = backsci%list%isub(ioff)
     if (backsci%off%prev%isub.eq.inext) then
        altoff => backsci%off%next
        backsci%off%next => backsci%off%prev
        backsci%off%prev => altoff
     else if (backsci%off%next%isub.eq.inext) then
        ! Nothing to be done...
     else
        call mrtcal_average_times(mrtset,iscal,backsci%list%isub(ioff),'track',imbf,subscanbuf,backsci%off%next,error)
        if (error)  return
     endif
  else
     backsci%off%next%isub = 0 ! Nevertheless, leave the type allocated
  endif
  !
end subroutine mrtcal_otf_psw_read_surrounding_offs
!
subroutine mrtcal_otf_psw_interpolate_off_init(off,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this=>mrtcal_otf_psw_interpolate_off_init
  !---------------------------------------------------------------------
  ! @ private
  ! *** JP In output, interp%isub = prev%isub or interp%isub = next%isub
  ! *** JP It would be better to have the averaged value stored as a real
  ! *** JP If this subscan number is used at read time, some additional
  ! *** JP protection would be needed.
  !---------------------------------------------------------------------
  type(off_stack_t), target, intent(inout) :: off
  logical,                   intent(inout) :: error
  ! Local
  type(chunkset_2d_t), pointer :: prev,next,offset,slope,interp
  character(len=*), parameter :: rname='OTF>PSW>INTERPOLATE>OFF>INIT'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  prev => off%prev
  next => off%next
  slope  => off%slope
  offset => off%offset
  interp => off%interp
  !
  if ((prev%isub.gt.0).and.(next%isub.gt.0)) then
     ! Both are there => Interpolation possible
     call mrtcal_chunkset_2d_interpolate_init(prev,next,slope,offset,interp,error)
     if (error) return
  else if ((prev%isub.le.0).and.(next%isub.le.0)) then
     ! Both are missing => Something wrong
     call mrtcal_message(seve%e,rname,'No subscans associated to previous and next OFF')
     error = .true.
     return
  else
     ! Only one is missing => Use the other one
     if (error) return
     if (prev%isub.le.0) then
        ! Previous is missing, just use the next one
        call mrtcal_chunkset_2d_interpolate_init_as_ref(next,slope,offset,interp,error)
        if (error) return
        call mrtcal_chunkset_2d_copy_data(next,offset,error)
        if (error) return
     endif
     if (next%isub.le.0) then
        ! Next is missing, just use the previous one
        call mrtcal_chunkset_2d_interpolate_init_as_ref(prev,slope,offset,interp,error)
        if (error) return
        call mrtcal_chunkset_2d_copy_data(prev,offset,error)
        if (error) return
     endif
     ! Set slope to zero (This ones must come after the header copy)
     call mrtcal_chunkset_2d_init_data(slope,0.,0.,0.,error)
  endif
  off%curr => off%interp
  !
end subroutine mrtcal_otf_psw_interpolate_off_init
!
subroutine mrtcal_otf_psw_prepare_nextoff(mrtset,filebuf,backsci,error)
  use gbl_message
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this=>mrtcal_otf_psw_prepare_nextoff
  !---------------------------------------------------------------------
  ! @ private
  ! Read the OFF subscans surrounding the current ON subscan and prepare
  ! the linear interpolation in time, i.e., compute the segment slope and
  ! constant
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),    intent(in)    :: mrtset
  type(imbfits_buffer_t),  intent(inout) :: filebuf
  type(science_backend_t), intent(inout) :: backsci
  logical,                 intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='OTF>PSW>PREPARE>NEXTOFF'
  !
  call mrtcal_otf_psw_read_surrounding_offs(mrtset,&
       filebuf%imbf,&
       filebuf%subscanbuf,&
       backsci,error)
  if (error) return
  call mrtcal_otf_psw_interpolate_off_init(backsci%off,error)
  if (error) return
  !
end subroutine mrtcal_otf_psw_prepare_nextoff
!
subroutine mrtcal_otf_psw_select_off(mrtset,backsci,error)
  use mrtcal_interfaces, except_this=>mrtcal_otf_psw_select_off
  use mrtcal_messaging
  use mrtcal_calib_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  ! Select the OFF subscan to be used. It can be either a linear
  ! interpolation of the previous and next OFF or the nearest neighbour
  ! in time. That's MRTSET%CAL%INTERPOFF that switches the behavior.
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),            intent(in)    :: mrtset
  type(science_backend_t), target, intent(inout) :: backsci
  logical,                         intent(inout) :: error
  ! Local
  integer(kind=4) :: idata
  real(kind=8) :: dist_onprev,dist_onnext
  real(kind=8), parameter :: mjd_tol = 1d0/(24d0*3600d0) ! 1 sec in mjd
  character(len=*), parameter :: rname='OTF>PSW>SELECT>OFF'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  idata = backsci%switch%cycle%ndata+1
  if (idata.gt.backsci%switch%cycle%npha) then
     call mrtcal_message(seve%e,rname,'Trying to fill more data than allocated memory in the OTFPSW cycle')
     error = .true.
     return
  endif
  ! *** JP This is checked for each dump. It would be more efficiency
  ! *** JP to have the address of the right subroutine decided earlier
  ! *** JP and passed here. Is it needed?
  select case(mrtset%cal%interpoff)
  case (interp_linear)
     ! Interpolate
     call mrtcal_chunkset_2d_interpolate_do(&
          backsci%on%curr%mjd%med,&
          backsci%off%slope,&
          backsci%off%offset,&
          backsci%off%interp,&
          error)
     call reassociate_chunkset_2d(backsci%off%interp,backsci%switch%cycle%data(idata),error)
     if (error) return
  case (interp_nearest)
     ! Use off nearest in time
     if ((backsci%off%prev%isub.gt.0).and.(backsci%off%next%isub.gt.0)) then
        dist_onprev = backsci%on%curr%mjd%beg-backsci%off%prev%mjd%end
        dist_onnext = backsci%off%next%mjd%beg-backsci%on%curr%mjd%end
        ! *** JP The MJD tolerance was fixed when this routine was also
        ! *** JP used for PSW tracked. Should it be set to 0 sec for OTF?
        if (dist_onprev-dist_onnext.le.mjd_tol) then
           call mrtcal_message(mseve%calib%book,rname,'Using previous OFF')
           call reassociate_chunkset_2d(backsci%off%prev,backsci%switch%cycle%data(idata),error)
           if (error) return
        else
           call mrtcal_message(mseve%calib%book,rname,'Using next OFF')
           call reassociate_chunkset_2d(backsci%off%next,backsci%switch%cycle%data(idata),error)
           if (error) return
        endif
     else if (backsci%off%prev%isub.gt.0) then
        call mrtcal_message(mseve%calib%book,rname,'Only previous OFF available')
        call reassociate_chunkset_2d(backsci%off%prev,backsci%switch%cycle%data(idata),error)
        if (error) return
     else if (backsci%off%next%isub.gt.0) then
        call mrtcal_message(mseve%calib%book,rname,'Only next OFF available')
        call reassociate_chunkset_2d(backsci%off%next,backsci%switch%cycle%data(idata),error)
        if (error) return
     else
        call mrtcal_message(seve%e,rname,'No subscans associated to previous nor to next OFF')
        error = .true.
        return
     endif
  case default
     call mrtcal_message(seve%e,rname,'Interpolation mode not implemented')
     error = .true.
     return
  end select
  ! Success => Increase the backsci%switch%cycle%ndata counter accordingly
  backsci%switch%cycle%ndata = backsci%switch%cycle%ndata+1
  !
end subroutine mrtcal_otf_psw_select_off
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
