!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_or_solve_parse(line,ientuser,icaluser,error)
  use mrtcal_messaging
  use gkernel_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calib_or_solve_parse
  use mrtcal_index_vars
  !---------------------------------------------------------------------
  ! @ private
  ! CALIBRATE | SOLVE ient
  ! 1. /WITH ical
  !---------------------------------------------------------------------
  character(len=*),           intent(in)    :: line
  integer(kind=entry_length), intent(out)   :: ientuser
  integer(kind=entry_length), intent(out)   :: icaluser
  logical,                    intent(inout) :: error
  !
  integer(kind=4), parameter :: icomm = 0
  integer(kind=4), parameter :: iwith = 1
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>PARSE'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_parse_numver(rname,line,icomm,1,ix,ientuser,error)
  if (error)  return
  if (sic_present(iwith,0)) then
     call mrtcal_parse_numver(rname,line,iwith,1,ix,icaluser,error)
     if (error)  return
  else
     ! Null value that will be interpreted as search for the best 
     ! associated calibration in the ix index
     icaluser = 0
  endif
end subroutine mrtcal_calib_or_solve_parse
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_or_solve_ix_entry(commcode,mrtset,ix,ientuser,icaluser,filebuf,  &
     calib,science,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_ix_entry
  use mrtcal_parameters
  use mrtcal_buffer_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer(kind=4),            intent(in)    :: commcode
  type(mrtcal_setup_t),       intent(in)    :: mrtset
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(inout) :: ientuser
  integer(kind=entry_length), intent(inout) :: icaluser
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(calib_t),              intent(inout) :: calib
  type(science_t),            intent(inout) :: science
  logical,                    intent(inout) :: error
  ! Local
  logical :: myerror
  integer(kind=4) :: scibackid,calbackid
  integer(kind=entry_length) :: isciprog,icalprog
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>IX>ENTRY'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  if (ix%obstype(ientuser).eq.obstype_calibrate) then
     ! Calibration scan: easy
     select case(commcode)
     case (code_command_calib,code_command_pipe)
        if (icaluser.ne.0) then
           call mrtcal_message(seve%e,rname,  &
                'Can not calibrate a calibration scan /WITH another scan')
           error = .true.
           return
        endif
        call mrtcal_calib_or_solve_ix_entry_calib(mrtset,ix,ientuser,filebuf,calib,error)
        if (error) return
     case (code_command_solve)
        call mrtcal_message(seve%e,rname,'Can not solve a calibration scan')
        error = .true.
        return
     case default
        call mrtcal_message(seve%e,rname,'Unknown command code')
        error = .true.
        return
     end select
  else
     ! Science scan: less easy
     if (icaluser.eq.0) then
        ! Search for the best associated calibration in the ix index
        myerror = error
        call mrtcal_calib_or_solve_autofind_done_cal(mrtset,ix,ientuser,icalprog,&
             filebuf,calib,myerror)
        if (myerror) then
           ! Previous matching calibration not found 
           ! => Need to set the calibration status of the current entry to
           !    FAILED
           call mrtcal_calib_or_solve_init_science(ix,ientuser,isciprog,error)
           if (error) return
           error = .true.
           return
        endif
        call mrtcal_calib_or_solve_feedback(ix,ientuser,icalprog,  &
             warnlim=mrtset%cal%winterval,error=error)
        if (error) return
     else
        ! Use user-defined calibration scan => Some additional sanity
        ! checks are required
        icalprog = icaluser
        call mrtcal_calib_or_solve_feedback(ix,ientuser,icalprog,error=error)
        if (error) return
     endif
     ! 1) Ensure 'icalprog' is the one currently buffered (reprocess if necessary)
     call mrtcal_calib_or_solve_reload(mrtset,ix,icalprog,filebuf,calib,error)
     if (error)  return
     ! 2) Initialize science scan (after calibration reprocess)
     call mrtcal_calib_or_solve_init_science(ix,ientuser,isciprog,error)
     if (error) return
     ! 3) Check calibration scan is sane for use
     call mrtcal_calib_or_solve_check_consistency(ix,ientuser,icalprog,error)
     if (error) return
     call mrtcal_calib_or_solve_check_obstype_is_cal(ix,icalprog,error)
     if (error) return
     call mrtcal_calib_or_solve_check_calstatus(ix,icalprog,error)
     if (error) return
     ! 4) Now calibrate
     call mrtcal_calib_or_solve_get_backid(ix,isciprog,scibackid,error)
     if (error) return
     call mrtcal_calib_or_solve_get_backid(ix,icalprog,calbackid,error)
     if (error) return
     call mrtcal_calib_or_solve_onebackend_science(commcode,&
          mrtset,calib%scan%val(calbackid)%last,&
          ix,isciprog,filebuf,&
          science%scan%val(scibackid),&
          error)
     if (error) return
     call mrtcal_calib_or_solve_exit_science(science%scan%val(scibackid),ix,isciprog,icalprog,error)
     if (error) return
  endif
end subroutine mrtcal_calib_or_solve_ix_entry
!
subroutine mrtcal_calib_or_solve_ix_entry_calib(mrtset,ix,icaluser,filebuf,  &
     calib,error)
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_ix_entry_calib
  use mrtcal_buffer_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  ! Calibrate the 'icaluser' entry (calibration scan) in IX
  ! Return the new (calibrated) entry number in 'icaluser'.
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),       intent(in)    :: mrtset
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(inout) :: icaluser
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(calib_t),              intent(inout) :: calib
  logical,                    intent(inout) :: error
  ! Local
  integer(kind=entry_length) :: icalprog
  integer(kind=4) :: calbackid
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>IX>ENTRY>CALIB'
  !
  call mrtcal_calib_or_solve_feedback(ix,icaluser,error=error)
  if (error) return
  call mrtcal_calib_or_solve_init_calib(ix,icaluser,icalprog,error)
  if (error) return
  call mrtcal_calib_or_solve_get_backid(ix,icalprog,calbackid,error)
  if (error) return
  call mrtcal_calib_or_solve_onebackend_calibration(&
       mrtset,ix,icalprog,filebuf,&
       calib%scan%val(calbackid)%work,error)
  if (error)  return
  call mrtcal_calib_or_solve_exit_calib(calib%scan%val(calbackid),ix,icalprog,error)
  if (error) return
  ! Success: update icaluser to store the calibrated entry number.
  icaluser = icalprog
end subroutine mrtcal_calib_or_solve_ix_entry_calib
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_or_solve_get_backid(idx,ient,backid,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_get_backid
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Return the backend identifier for the i-th entry in the input index
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  integer(kind=entry_length), intent(in)    :: ient
  integer(kind=4),            intent(out)   :: backid
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>GET>BACKEND'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  backid = idx%backend(ient)
  if (backid.eq.backend_unknown) then
     call mrtcal_message(seve%e,rname,'Unknown backend')
     error = .true.
     return
  endif
end subroutine mrtcal_calib_or_solve_get_backid
!
subroutine mrtcal_calib_or_solve_autofind_matching_cal(idx,backward,mtole,ientuser,  &
  istart,jcalprog,error)
  use phys_const
  use mrtcal_dependencies_interfaces
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_autofind_matching_cal
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !   Automatic search of the previous or next matching calibration in
  ! the input index, starting from the istart entry.
  !   The returned scan will be:
  !    - a calibration (obstype)
  !    - with calibration status none or done (reject failed or empty)
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  logical,                    intent(in)    :: backward  ! Backward or forward search?
  real(kind=4),               intent(in)    :: mtole     ! [min] Tolerance
  integer(kind=entry_length), intent(in)    :: ientuser
  integer(kind=entry_length), intent(inout) :: istart
  integer(kind=entry_length), intent(out)   :: jcalprog
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>AUTOFIND>MATCHING>CAL'
  logical :: duplicated
  integer(kind=4), parameter :: newest = 0
  integer(kind=entry_length) :: ient,jent,first,last,step
  character(len=message_length) :: mess
  real(kind=8) :: sdobsut,cdobsut,dtole
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_separator(mseve%calib%book,rname,2,error)
  if (error) return
  call mrtindex_list_one_default(idx,ientuser,mess,error)
  call mrtcal_message(mseve%calib%book,rname,'Matching '//mess)
  !
  if (backward) then
    first = istart-1
    last = 1
    step = -1
  else
    first = istart+1
    last = idx%next-1
    step = +1
  endif
  sdobsut = idx%dobs(ientuser)+idx%ut(ientuser)/2.d0/pi
  dtole = mtole/60.d0/24.d0  ! Convert minutes to days
  jcalprog = 0
  do ient=first,last,step
     ! Finding the most recent version of the previous entry in the ix index
     ! *** JP I am not sure but there should be a shortcut as we already 
     ! *** JP known ient, i.e. why searching it from idx%num(ient) again?
     call mrtindex_numver2ent(rname,idx,idx%num(ient),newest,jent,duplicated,error)
     if (error) return
     call mrtindex_list_one_default(idx,jent,mess,error)
     call mrtcal_message(mseve%calib%book,rname,'Trying   '//mess)
     ! Search for a successful calibration of the same project and same backend
     if (idx%obstype(jent).ne.obstype_calibrate) cycle
     if (idx%calstatus(jent).eq.calstatus_failed) cycle
     if (idx%calstatus(jent).eq.calstatus_empty) cycle
     if (idx%calstatus(jent).eq.calstatus_skipped) cycle
     if (idx%projid(jent).ne.idx%projid(ientuser)) cycle
     if (idx%backend(jent).ne.idx%backend(ientuser)) cycle
     cdobsut = idx%dobs(jent)+idx%ut(jent)/2.d0/pi
     if (abs(cdobsut-sdobsut).gt.dtole)  cycle  ! Or exit?
     !
     ! OK, found a matching candidate
     ! *** JP That's not enough. Tuned frequency and channel width should also match...
     jcalprog = jent
     istart = ient  ! Also reset starting point so that we start from here
                    ! if we need to resume the loop
     exit
  enddo ! ient
end subroutine mrtcal_calib_or_solve_autofind_matching_cal
!
subroutine mrtcal_calib_or_solve_autofind_done_cal(mrtset,ix,ientuser,jcalprog,filebuf,calib,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_autofind_done_cal
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Automatic search of the nearest matching calibration in the input index.
  ! Calibrate it when needed
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),       intent(in)    :: mrtset
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(in)    :: ientuser
  integer(kind=entry_length), intent(out)   :: jcalprog
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(calib_t),              intent(inout) :: calib
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>AUTOFIND>DONE>CAL'
  logical :: myerror,way(2)
  integer(kind=4) :: iway
  integer(kind=entry_length) :: istart
  character(len=message_length) :: mess
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  way(1) = .true.   ! Backward search first
  way(2) = .false.  ! Then forward search
  !
  do iway=1,2
    istart = ientuser
    do while (.true.)
      call mrtcal_calib_or_solve_autofind_matching_cal(ix,way(iway),mrtset%cal%einterval,  &
        ientuser,istart,jcalprog,error)
      if (error) return
      if (jcalprog.eq.0)  exit  ! Nothing found, try next way
      if (ix%calstatus(jcalprog).eq.calstatus_done)  return  ! Found a calibrated one => done!
      !
      ! Found one, but it is not calibrated. Try to calibrate it:
      myerror = .false.
      call mrtcal_calib_or_solve_ix_entry_calib(mrtset,ix,jcalprog,filebuf,calib,myerror)
      if (myerror) then
        ! Calibration failure: its calibration status is marked failed in the
        ! index but it is not an error for the current subroutine. Continue
        ! to search for a valid calibration.
      else
        ! Calibration success: jcalprog is now the calibrated entry.
        return
      endif
    enddo
  enddo  ! iway
  !
  ! At this point we tried both ways but we have not return'ed from the loop
  ! => no matching calibration found
  !
  ! Give also some feedback, so that the error does not come alone and we see
  ! clearly which scan fails. The line "Calibrating ..." is also needed by the
  ! pipeline-report script.
  call mrtcal_calib_or_solve_feedback(ix,ientuser,error=error)
  if (error)  continue
  !
  ! This is an error. If you plan to relax this, think twice because having a
  ! reasonable upper limit avoids Mrtcal to pair a science scan with a
  ! calibration scan observed 2 days ago before a storm...
  write(mess,'(A,F0.1,A)')   &
    'No matching calibration in the surrounding ',mrtset%cal%einterval,' minutes'
  call mrtcal_message(seve%e,rname,mess)
  error = .true.
  return
end subroutine mrtcal_calib_or_solve_autofind_done_cal
!
subroutine mrtcal_calib_or_solve_check_consistency(idx,ient,ical,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_check_consistency
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  integer(kind=entry_length), intent(in)    :: ient
  integer(kind=entry_length), intent(inout) :: ical
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>CHECK>CONSISTENCY'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  if (idx%backend(ient).ne.idx%backend(ical)) then
     call mrtcal_message(seve%e,rname,'Calibration and science backends do not match')
     error = .true.
     return
  endif
end subroutine mrtcal_calib_or_solve_check_consistency
!
subroutine mrtcal_calib_or_solve_check_obstype_is_cal(idx,ical,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_check_obstype_is_cal
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  integer(kind=entry_length), intent(inout) :: ical
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>CHECK>OBSTYPE>IS>CAL'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  if (idx%obstype(ical).ne.obstype_calibrate) then
     call mrtcal_message(seve%e,rname,'Entry ### is not a calibration in input index')
     error = .true.
     return
  endif
end subroutine mrtcal_calib_or_solve_check_obstype_is_cal
!
subroutine mrtcal_calib_or_solve_check_calstatus(idx,ical,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_check_calstatus
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  integer(kind=entry_length), intent(in)    :: ical
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>CHECK>CALSTATUS'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  select case(idx%calstatus(ical))
  case (calstatus_done)
    return
  case (calstatus_none)
    call mrtcal_message(seve%e,rname,'Cannot use a calibration entry with an UNCALIBRATED status')
    error = .true.
  case (calstatus_failed)
    call mrtcal_message(seve%e,rname,'Cannot use a calibration entry with a FAILED status')
    error = .true.
  case (calstatus_empty)
    call mrtcal_message(seve%e,rname,'Cannot use a calibration entry with an EMPTY status')
    error = .true.
  case (calstatus_skipped)
    call mrtcal_message(seve%e,rname,'Cannot use a calibration entry with an SKIPPED status')
    error = .true.
  case default
    call mrtcal_message(seve%e,rname,'Cannot use a calibration entry with an UNKNOWN status')
    error = .true.
  end select
end subroutine mrtcal_calib_or_solve_check_calstatus
!
subroutine mrtcal_calib_or_solve_check_current(idx,ical,backcal,warn,same,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_check_current
  use mrtcal_calib_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Check if the calibration scan given as argument is the ical-th
  ! entry in the index. Issue warning messages if needed.
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: idx
  integer(kind=entry_length), intent(in)    :: ical
  type(calib_backend_t),      intent(in)    :: backcal
  logical,                    intent(in)    :: warn
  logical,                    intent(out)   :: same
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>CHECK>CURRENT'
  character(len=message_length) :: mess
  character(len=11) :: date1,date2
  !
  same = .true.
  !
  if (idx%dobs(ical).ne.backcal%head%key%dobs) then
    same = .false.
    if (warn) then
      call gag_todate(idx%dobs(ical),date1,error)
      if (error)  return
      call gag_todate(backcal%head%key%dobs,date2,error)
      if (error)  return
      write(mess,'(3(A,1X))') 'Inconsistent observing dates:',date1,date2
      call mrtcal_message(seve%w,rname,mess)
    endif
  endif
  !
  if (idx%scan(ical).ne.backcal%head%key%scan) then
    same = .false.
    if (warn) then
      write(mess,'(A,1X,I0,1X,I0)') 'Inconsistent scan numbers:',idx%scan(ical),backcal%head%key%scan
      call mrtcal_message(seve%w,rname,mess)
    endif
  endif
  !
  if (idx%backend(ical).ne.backcal%head%key%backend) then
    same = .false.
    if (warn) then
      write(mess,'(3(A,1X))') 'Inconsistent backends:',  &
        mrtindex_backend(idx%backend(ical)),mrtindex_backend(backcal%head%key%backend)
      call mrtcal_message(seve%w,rname,mess)
    endif
  endif
end subroutine mrtcal_calib_or_solve_check_current
!
subroutine mrtcal_calib_or_solve_reload(mrtset,idx,ient,filebuf,calib,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_reload
  use mrtcal_buffer_types
  use mrtcal_calib_types
  use mrtindex_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),       intent(in)    :: mrtset
  type(mrtindex_optimize_t),  intent(inout) :: idx
  integer(kind=entry_length), intent(inout) :: ient
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(calib_t),              intent(inout) :: calib
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>RELOAD'
  logical :: same
  integer(kind=4) :: backid
  !
  call mrtcal_calib_or_solve_get_backid(idx,ient,backid,error)
  if (error) return
  call mrtcal_calib_or_solve_check_current(idx,ient,calib%scan%val(backid)%last,.true.,same,error)
  if (error)  return
  !
  if (same) then
    call mrtcal_message(seve%d,rname,'Calibration in memory is the desired one: nothing to do')
  else
    ! Recalibrate the calibration scan and load it in memory
    call mrtcal_message(seve%w,rname,  &
      'The calibration scan currently in memory is not the desired one: reprocess')
    call mrtcal_calib_or_solve_ix_entry_calib(mrtset,idx,ient,filebuf,calib,error)
    if (error)  return
    call mrtcal_message(seve%w,rname,'Calibration scan reprocessing done, resuming science scan')
    call mrtcal_separator(seve%r,rname,2,error)
    if (error) return
  endif
end subroutine mrtcal_calib_or_solve_reload
!
subroutine mrtcal_calib_or_solve_feedback(idx,ient,ical,warnlim,error)
  use phys_const
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calib_or_solve_feedback
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)           :: idx
  integer(kind=entry_length), intent(in)           :: ient
  integer(kind=entry_length), intent(in), optional :: ical
  real(kind=4),               intent(in), optional :: warnlim
  logical,                    intent(inout)        :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>FEEDBACK'
  character(len=message_length) :: mess
  real(kind=4) :: diff,fact
  integer(kind=4) :: sev
  character(len=8) :: befaft,tunit
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  call mrtindex_list_one_default(idx,ient,mess,error)
  if (error) return
  call mrtcal_message(seve%r,rname,'Calibrating '//mess)
  if (.not.present(ical))  return
  !
  call mrtindex_list_one_default(idx,ical,mess,error)
  if (error) return
  call mrtcal_message(seve%r,rname,'With        '//mess)
  if (.not.present(warnlim))  return
  !
  diff = (idx%dobs(ient)+idx%ut(ient)/2.d0/pi) -  &
         (idx%dobs(ical)+idx%ut(ical)/2.d0/pi)
  diff = diff*24.d0*60.d0  ! Convert day to min
  if (diff.gt.0.) then
    befaft = ' before'
  else
    befaft = ' after'
    diff = -diff
  endif
  if (diff.lt.1.) then
    fact = 60.
    tunit = ' seconds'
  else
    fact = 1.
    tunit = ' minutes'
  endif
  write(mess,'(A,F0.1,2A)')  'Calibration observed ',diff*fact,tunit,befaft
  if (abs(diff).gt.warnlim) then
    call sic_upper(mess)
    sev = seve%w
  else
    sev = seve%i
  endif
  call mrtcal_message(sev,rname,mess)
end subroutine mrtcal_calib_or_solve_feedback
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_or_solve_init_calib(ix,ientuser,ientprog,error)
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_init_calib
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Prepare a new entry for a calibration scan
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(in)    :: ientuser
  integer(kind=entry_length), intent(out)   :: ientprog
  logical,                    intent(inout) :: error
  !
  call mrtcal_calib_or_solve_init(ix,.true.,ientuser,ientprog,error)
  if (error)  return
end subroutine mrtcal_calib_or_solve_init_calib
!
subroutine mrtcal_calib_or_solve_init_science(ix,ientuser,ientprog,error)
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_init_science
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Prepare a new entry for a science scan
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(inout) :: ix
  integer(kind=entry_length), intent(in)    :: ientuser
  integer(kind=entry_length), intent(out)   :: ientprog
  logical,                    intent(inout) :: error
  !
  call mrtcal_calib_or_solve_init(ix,.false.,ientuser,ientprog,error)
  if (error)  return
end subroutine mrtcal_calib_or_solve_init_science
!
subroutine mrtcal_calib_or_solve_init(ix,iscalib,ientuser,ientprog,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_init
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Do not call directly! Call mrtcal_calib_or_solve_init_calib or
  ! mrtcal_calib_or_solve_init_science instead.
  ! ---
  ! Copy the ient entry of the input index into a new entry. Then set 
  ! in place its calstatus to calstatus_failed. This status will
  ! be replaced in place by calstatus_done in mrtcal_calib_or_solve_exit_*
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(inout) :: ix
  logical,                    intent(in)    :: iscalib
  integer(kind=entry_length), intent(in)    :: ientuser
  integer(kind=entry_length), intent(out)   :: ientprog
  logical,                    intent(inout) :: error
  ! Local
  type(mrtindex_entry_t) :: entry
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>INIT'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! *** JP We should first try to see whether the data was already
  ! *** JP already calibrated. If yes, we should return without error,
  ! *** JP but send a warning. Of course, the user could force the
  ! *** JP calibration through /FORCE option. In this case, we should
  ! *** JP upgrade the version number to last number + 1...
  !
  ! Set the new entry. Everything identical to 'ientuser'...
  call mrtindex_optimize_to_entry(ix,ientuser,entry,error)
  if (error) return
  ! ... except those:
  entry%indx%calstatus = calstatus_failed  ! Until calibration is not successful
  ! entry%indx%version = set by mrtindex_append_entry
  !
  ! --- Sections depending on obstype ----------------------------------
  ! The section pre-allocations are needed because it may happen that
  ! mrtcal_calib_or_solve_reload adds another entry after the current
  ! one, so we can not extend it anymore.
  !
  ! --- Calibration section ---
  if (iscalib) then
    ! This is calibration scan. Forget possible previous calibration results
    ! (we will overwrite it with possible new tunings)
    entry%head%presec(sec_calib_id) = .false.
  endif
  !
  ! --- Science section ---
  if (iscalib) then
    ! This is calibration scan. No Science section
    entry%head%presec(sec_science_id) = .false.
  else
    ! This is a "science" scan (in large sense, i.e. everything which
    ! is calibrated thanks to a calibration scan). Prepare room for the
    ! Science section, it will be updated (with the actual values) by
    ! mrtcal_calib_or_solve_exit_science.
    call mrtindex_entry_zscience(entry%head%sci,error)
    if (error)  return
    entry%head%presec(sec_science_id) = .true.
  endif
  !
  ! --- Pointing section ---
  ! No solution and section disabled as long as no result to be written in index.mrt
  entry%head%presec(sec_pointing_id) = .false.  ! As long as no result
  entry%indx%solstatus = solstatus_none
  if (entry%indx%obstype.eq.obstype_pointing) then
    call entry%head%poi%zero(error)
    if (error)  return
  endif
  !
  ! --- End of sections ------------------------------------------------
  !
  call mrtindex_append_entry(entry,ientuser,ientprog,ix,error)
  if (error) return
  !
  call mrtindex_entry_free(entry,error)
  if (error)  return
end subroutine mrtcal_calib_or_solve_init
!
subroutine mrtcal_calib_or_solve_exit_calib(cal,ix,ientprog,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_exit_calib
  use mrtindex_types
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_work_t), intent(inout) :: cal
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(in)    :: ientprog
  logical,                    intent(inout) :: error
  ! Local
  type(mrtindex_entry_t) :: entry
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>EXIT>CALIB'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtindex_optimize_to_entry(ix,ientprog,entry,error)
  if (error) return
  !
  if (cal%work%nspec.eq.0) then
    ! 0-sized calibration: done, but not useable later on
    ! No purpose saving an empty calibration section.
    entry%indx%calstatus = calstatus_empty
  else
    ! This is a successful calibration: the 'work' calibration
    ! becomes the 'last' useable
    call cal%swap(error)
    if (error)  return
    !
    ! *** JP: For the moment use this one. But in fact, the work on presec
    !         should also be done inside this one
    !
    ! Fill and enable the calibration section
    call mrtcal_entry_chopperset2calsec(cal%last,entry%head%cal,error)
    if (error) return
    entry%head%presec(sec_calib_id) = .true.
    !
    entry%indx%calstatus = calstatus_done
  endif
  !
  call mrtindex_extend_entry(entry,ientprog,ix,error)
  if (error) return
  call mrtindex_entry_free(entry,error)
  if (error)  return
end subroutine mrtcal_calib_or_solve_exit_calib
!
subroutine mrtcal_calib_or_solve_exit_science(sci,ix,isciprog,icalprog,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_exit_science
  use mrtcal_calib_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(science_backend_t),    intent(in)    :: sci
  type(mrtindex_optimize_t),  intent(inout) :: ix
  integer(kind=entry_length), intent(in)    :: isciprog
  integer(kind=entry_length), intent(in)    :: icalprog
  logical,                    intent(inout) :: error
  ! Local
  type(mrtindex_entry_t) :: entry
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>EXIT>SCIENCE'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtindex_optimize_to_entry(ix,isciprog,entry,error)
  if (error) return
  !
  if (sci%nobs.eq.0) then
    entry%indx%calstatus = calstatus_empty
  else
    entry%indx%calstatus = calstatus_done
    !
    ! Fill and enable the science section
    entry%head%sci%caldobs = ix%dobs(icalprog)
    entry%head%sci%calscan = ix%scan(icalprog)
    entry%head%sci%calback = ix%backend(icalprog)
    entry%head%sci%calvers = ix%version(icalprog)
    entry%head%presec(sec_science_id) = .true.
    !
    ! Enable the pointing section if relevant
    if (entry%indx%obstype.eq.obstype_pointing) then
      call mrtcal_entry_sdrifts2poisec(sci,entry%head%poi,error)
      if (error) return
      entry%head%presec(sec_pointing_id) = .true.
      entry%indx%solstatus = solstatus_done
    endif
    !
  endif
  !
  call mrtindex_extend_entry(entry,isciprog,ix,error)
  if (error) return
  call mrtindex_entry_free(entry,error)
  if (error)  return
end subroutine mrtcal_calib_or_solve_exit_science
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_or_solve_onebackend_calibration(mrtset,scanidx,ient,filebuf,&
     backcal,error)
  use mrtcal_dependencies_interfaces
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_onebackend_calibration
  use mrtcal_buffer_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),       intent(in)    :: mrtset
  type(mrtindex_optimize_t),  intent(in)    :: scanidx
  integer(kind=entry_length), intent(in)    :: ient
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(calib_backend_t),      intent(inout) :: backcal
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>ONEBACKEND>CALIBRATION'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_calib_or_solve_read_backend_header(scanidx,ient,filebuf,backcal%head,error)
  if (error) return
  !
  ! Keep memory of the chunks description
  call imbfits_copy_back_chunks(filebuf%imbf%back%chunks,backcal%chunksetlist,error)
  if (error)  return
  !
  call mrtcal_calib_calibration(mrtset,filebuf,backcal,error)
  if (error) return
end subroutine mrtcal_calib_or_solve_onebackend_calibration
!
subroutine mrtcal_calib_or_solve_onebackend_science(commcode,doset,backcal,&
     scanidx,ient,filebuf, backsci,error)
  use mrtcal_dependencies_interfaces
  use mrtcal_messaging
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_onebackend_science
  use mrtcal_parameters
  use mrtcal_buffer_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer(kind=4),            intent(in)    :: commcode
  type(mrtcal_setup_t),       intent(in)    :: doset    ! Setup (user choices)
  type(calib_backend_t),      intent(in)    :: backcal
  type(mrtindex_optimize_t),  intent(in)    :: scanidx
  integer(kind=entry_length), intent(in)    :: ient
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(science_backend_t),    intent(inout) :: backsci
  logical,                    intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>ONEBACKEND>SCIENCE'
  type(mrtcal_setup_t) :: doneset  ! Setup (resolved choices)
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_calib_or_solve_read_backend_header(scanidx,ient,filebuf,backsci%head,error)
  if (error) return
  !
  ! Keep memory of the chunks description
  call imbfits_copy_back_chunks(filebuf%imbf%back%chunks,backsci%chunksetlist,error)
  if (error)  return
  !
  ! Translate user choices for this scan
  call mrtcal_setup_do2done(backsci%head%key%obstype,  &
    backsci%head%key%switchmode,doset,doneset,error)
  if (error)  return
  !
  ! Build and print subscan list
  call mrtcal_init_scan_cal(backsci,filebuf%imbf,error)
  if (error) return
  !
  if (all(backsci%list%empty)) then
    call mrtcal_message(seve%w,rname,  &
      'All subscan data tables are zero-sized: scan marked as EMPTY')
    ! backsci%nspec was initialized and left to 0. mrtcal_calib_or_solve_exit_science
    ! will then mark the scan as empty.
    return
  endif
  !
  select case(backsci%head%key%obstype)
  case (obstype_tracked)
     select case(commcode)
     case (code_command_calib,code_command_pipe)
        select case(backsci%head%key%switchmode)
        case (switchmode_pos)
           call mrtcal_calib_tracked_psw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case (switchmode_wob)
           call mrtcal_calib_tracked_wsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case (switchmode_fre)
           call mrtcal_calib_tracked_fsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select ! backsci%head%key%switchmode
     case (code_command_solve)
        call mrtcal_message(seve%e,rname,'Unsupported observing type when solving')
        error = .true.
        return
     case default
        call mrtcal_message(seve%e,rname,'Unknown command code')
        error = .true.
        return
     end select ! commcode
  case (obstype_otf)
     select case(commcode)
     case (code_command_calib,code_command_pipe)
        select case(backsci%head%key%switchmode)
        case (switchmode_pos)
           call mrtcal_calib_otf_psw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case (switchmode_fre)
           call mrtcal_calib_otf_fsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select ! backsci%head%key%switchmode
     case (code_command_solve)
        call mrtcal_message(seve%e,rname,'Unsupported observing type when solving')
        error = .true.
        return
     case default
        call mrtcal_message(seve%e,rname,'Unknown command code')
        error = .true.
        return
     end select ! commcode
  case (obstype_pointing)   
     select case(commcode)
     case (code_command_calib)
        select case(backsci%head%key%switchmode)
!!$        case (switchmode_pos)
!!$           call mrtcal_calib_pointing_psw(doneset,backcal,backsci,filebuf,error)
!!$           if (error) return
        case (switchmode_bea)
           call mrtcal_calib_pointing_bsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select  ! backsci%head%key%switchmode
     case (code_command_solve,code_command_pipe)
        select case(backsci%head%key%switchmode)
!!$        case (switchmode_pos)
!!$           call mrtcal_solve_pointing_psw(doneset,backcal,backsci,filebuf,error)
!!$           if (error) return
        case (switchmode_bea)
           call mrtcal_solve_pointing_bsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select ! backsci%head%key%switchmode
     case default
        call mrtcal_message(seve%e,rname,'Unknown command code')
        error = .true.
        return
     end select ! commcode
  case (obstype_focus)
     select case(commcode)
     case (code_command_calib)
        select case(backsci%head%key%switchmode)
        case (switchmode_bea)
           call mrtcal_calib_focus_bsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select ! backsci%head%key%switchmode
     case (code_command_solve,code_command_pipe)
        select case(backsci%head%key%switchmode)
        case (switchmode_bea)
           call mrtcal_solve_focus_bsw(doneset,backcal,backsci,filebuf,error)
           if (error) return
        case default
           call mrtcal_message(seve%e,rname,'Unsupported switched mode '// &
                mrtindex_swmode(backsci%head%key%switchmode))
           error = .true.
           return
        end select ! backsci%head%key%switchmode
     case default
        call mrtcal_message(seve%e,rname,'Unknown command code')
        error = .true.
        return
     end select ! commcode
  case default
     call mrtcal_message(seve%e,rname,'Unsupported observing type '// &
          mrtindex_obstype(backsci%head%key%obstype))
     error = .true.
     return
  end select ! backsci%head%key%obstype
end subroutine mrtcal_calib_or_solve_onebackend_science
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! *** JP Not sure where the following code should go: in calib-utils.f90?
!
subroutine mrtcal_calib_or_solve_read_backend_header(scanidx,ient,filebuf,head,error)
  use mrtcal_messaging
  use classic_api
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calib_or_solve_read_backend_header
  use mrtcal_buffer_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),  intent(in)    :: scanidx
  integer(kind=entry_length), intent(in)    :: ient
  type(imbfits_buffer_t),     intent(inout) :: filebuf
  type(mrtindex_header_t),    intent(inout) :: head
  logical,                    intent(inout) :: error
  ! Local
  character(len=filename_length) :: filename
  type(classic_entrydesc_t) :: edesc
  character(len=*), parameter :: rname='CALIB>OR>SOLVE>READ>BACKEND>HEADER'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtindex_entry_rheader(scanidx,ient,edesc,head,error)
  if (error) return
  call mrtindex_optimize_to_filename(scanidx,ient,filename,error)
  if (error) return
  call imbfits_read_header(filename,filebuf%imbf,error)
  if (error) return
end subroutine mrtcal_calib_or_solve_read_backend_header
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
