begin procedure pipeline-structure
  !
  if (.not.exist(pipe)) then
     define structure pipe               /global
     ! Setup:
     define character*256 pipe%imbfits   /global  ! Where IMB-FITS files should be read
     define character*256 pipe%indexdir  /global  ! Where should go the .mrt files
     define character*256 pipe%classdir  /global  ! Where should go the .30m files
     define character*256 pipe%projfits  /global  ! Where per project IMB-FITS are stored
     define logical       pipe%docont    /global  ! Process continuum backends too?
     !
     ! Control variables for plots (png and html)
     !   plot%do:       enable/disable plot creation
     !   plot%pdir:     PNG hardcopies and html are created in this "project" directory
     ! Things are more complicated for the "online" directory:
     !   plot%odir:     "online" directory where plots are duplicated, and HTML are put there
     !   plot%osubdir:  subdirectory from plot%odir for PNG
     !   plot%osymlink: file name (no subdir) from plot%odir for symlink to last scan
     define structure     pipe%plot          /global
     define logical       pipe%plot%do       /global  ! Should produce OdP plots?
     define character*256 pipe%plot%pdir     /global  ! Project plot directory (png and html)
     define character*256 pipe%plot%odir     /global  ! Online plot main directory (png and html)
     define character*64  pipe%plot%osubdir  /global  ! PNG and HTML subdirectory
     define character*64  pipe%plot%osymlink /global  ! Symlink to last html plots
     define character*12  pipe%plot%error    /global  ! On error mode?
     !
     define character*256 pipe%vodir     /global  ! Where should go VO XML files
     define character*256 pipe%stopfile  /global  ! File triggering a clean exit
     define logical       pipe%simulator /global  ! Simulator mode?
     define character*256 pipe%simudate  /global  ! Date-time file in simulator mode
     define character*64  pipe%dynaproc  /global  ! Procedure name for dynamic variables
     define character*256 pipe%logfile   /global  ! Log file (not handled in the procedures, just for bookkeeping)
     define structure     pipe%info        /global
     define character*256 pipe%info%status /global  ! File describing current pipeline status
     define character*256 pipe%info%log    /global  ! File describing current pipeline logfile
     !
     ! Working variables:
     define character*12  pipe%queue     /global  ! QUEUE of current index in memory
     define character*256 pipe%classfile /global  ! .30m file currently opened
     define character*64  pipe%plotfile  /global  ! PNG file name
     define character*256 pipe%pplotfile /global  ! PNG file name in project directory
     define real          pipe%trextr[2] /global  ! Trec min-max in case of CALNOSKY
     !
     define integer       pipe%mpolars   /global  !
     let pipe%mpolars 2                           ! At most 2 polars
     define integer       pipe%mparts    /global  !
     let pipe%mparts 8                            ! At most 8 parts per backend @ 30m
     define integer pipe%parts[pipe%mparts] /global
     define integer       pipe%nparts    /global  ! Useful size of pipe%parts[]
     !
     ! Telescope setups in frequency
     define structure     pipe%tel                                 /global
     define integer       pipe%tel%n                               /global
     define integer       pipe%tel%npolar[pipe%mparts]             /global
     define character*12  pipe%tel%name[pipe%mparts,pipe%mparts]   /global
     define character*4   pipe%tel%color[pipe%mparts,pipe%mparts]  /global
     define character*24  pipe%tel%part[pipe%mparts]               /global
     define character*12  pipe%tel%line[pipe%mparts]               /global
     !
     ! List of PNG files
     ! define structure   pipe%png  /global  => (re)created when needed
  endif
  !
end procedure pipeline-structure
!
begin procedure pipeline-fileout
  !---------------------------------------------------------------------
  ! Open the Class file (if relevant).
  !  pro%arg[1]: backend name e.g. WILMA
  !  pro%arg[2]: date under format DD-MMM-YYYY e.g 16-NOV-2016
  !---------------------------------------------------------------------
  !
  if .not.set%mrtcal%output%spectra  return
  !
  define char*8 observed
  datetime /from pro%arg[2] DD-MMM-YYYY /to observed YYYYMMDD
  !
  let pipe%classfile 'pipe%classdir'"/"'pro%arg[1]'"Odp"'observed'".30m"
  !
  if file(pipe%classfile) then  ! No /OVERWRITE!
    file out 'pipe%classfile'
  else
    file out 'pipe%classfile' single
  endif
  !
end procedure pipeline-fileout
!
begin procedure pipeline-skip
  !---------------------------------------------------------------------
  ! Skip what is not yet calibrated in input index, for range of
  ! dates from pro%arg[1] to pro%arg[2]
  ! No INDEX UPDATE here, on purpose. This must be done by the caller.
  ! Ignore POINTING, FOCUS, DIY, and LISSAJOUS which we do not support
  ! yet.
  !---------------------------------------------------------------------
  !
  if pro%narg.ne.2 then
    message e pipeline-do  "2 arguments expected"
    return error
  endif
  !
  mfind  /completenes readable -
         /calibrated none -
         /date 'pro%arg[1]' to 'pro%arg[2]' -
         /obstype CALIBRATE TRACKED ONTHEFLYMAP
  !
  mset input data none
  for i 1 to mfound
    mrt\read next
    mrt\variable mhead write
    let mhead%key%calstatus 5  ! ZZZ 5 is code for SKIPPED
    mupdate  ! Replace calstatus NONE to SKIPPED
  next i
  !
  message i pipeline-skip "Calibration status set as SKIPPED for "'mfound'" files"
  !
end procedure pipeline-skip
!
begin procedure pipeline-process
  !---------------------------------------------------------------------
  ! Calibrate what is not yet calibrated in input index, for range of
  ! dates from pro%arg[1] to pro%arg[2]
  ! No INDEX UPDATE here, on purpose. This must be done by the caller.
  ! Ignore POINTING, FOCUS, DIY, and LISSAJOUS which we do not support
  ! yet.
  ! Ignore continuum backends for now, to avoid conflict with Mira
  !---------------------------------------------------------------------
  !
  if pro%narg.ne.2 then
    message e pipeline-do  "2 arguments expected"
    return error
  endif
  !
  if pipe%docont then  ! All backends
    mfind  /completenes readable -
          /calibrated none -
          /date 'pro%arg[1]' to 'pro%arg[2]' -
          /obstype CALIBRATE POINTING TRACKED ONTHEFLYMAP
  else  ! All but continuum backends
    mfind  /completenes readable -
          /calibrated none -
          /date 'pro%arg[1]' to 'pro%arg[2]' -
          /obstype CALIBRATE TRACKED ONTHEFLYMAP -
          /backend VESPA WILMA FTS
  endif
  if (mfound.eq.0)  return
  !
  ! Keep a list of project and dates that will be calibrated hereafter
  mlist /toc id observed /var mytoc1
  !
  ! Time statistics
  define integer tottodo totdone curtodo
  define real elapsed
  let elapsed 0.0
  let tottodo mfound
  let totdone 0
  sic cpu
  !
  ! Calibrate by project, date, and backend
  mlist /toc id observed backend /var mytoc
  define char*1024 mfindline
  for isetup 1 to mytoc%nsetup
    say ""
    say "Calibrating project" 'mytoc%setup[isetup,1]' 'mytoc%setup[isetup,2]' 'mytoc%setup[isetup,3]'
    let mfindline "mfind"
    let mfindline 'mfindline'" /completenes readable"
    let mfindline 'mfindline'" /calibrated none"
    let mfindline 'mfindline'" /date "'mytoc%setup[isetup,2]'
    let mfindline 'mfindline'" /projid "'mytoc%setup[isetup,1]'
    let mfindline 'mfindline'" /backend "'mytoc%setup[isetup,3]'
    let mfindline 'mfindline'" /obstype CALIBRATE TRACKED ONTHEFLYMAP"
    if pipe%docont then
      let mfindline 'mfindline'" POINTING"
    endif
    sic\execute 'mfindline'
    let curtodo mfound
    if (curtodo.ne.0) then  ! Should not happen...
      ! Set up environment depending on project id
      if pipe%dynaproc.ne." "  @ 'pipe%dynaproc'
      ! Switch to appropriate Class file
      @ pipeline-fileout 'mytoc%setup[isetup,3]' 'mytoc%setup[isetup,2]'
      ! Set up the info file
      @ pipeline-info-processing 'mytoc%setup[isetup,1]' 'mytoc%setup[isetup,2]' 'mytoc%setup[isetup,3]'
      ! Process
      las\set default  ! Revert Class to all defaults so that setups done all
                       ! around in the procedure are forgotten, as if PIPELINE
                       ! was invoked in a fresh new session. In particular, SET
                       ! MODE X or Y are back to AUTO (not FIXED), which disables
                       ! the Plot section in Class spectra written by command
                       ! PIPELINE.
      pipeline
      ! Success and error plots
      @ pipeline-plots 'pipe%queue' 'mytoc%setup[isetup,1]' 'mytoc%setup[isetup,2]' 'mytoc%setup[isetup,3]'
    endif
    !
    ! Some intermediate feedback because there may be a lot of projects
    sic cpu
    let elapsed elapsed+sic%cpu%raw%elapsed
    let totdone totdone+curtodo
    say "Done " 'nint(totdone/tottodo*100)' "% (" 'totdone' "/" 'tottodo' -
      ") in " 'nint(elapsed)' " sec" /format A I0 A I0 A I0 A I0 A
  next isetup
  !
  ! Main feedback. NB: this string is reused by pipeline-report.cal
  define char*32 string1 string2
  if elapsed.lt.60 then
    let string1 elapsed /format f0.1
    let string1 'string1'" sec"
  else if elapsed.lt.3600 then
    let string1 elapsed/60. /format f0.1
    let string1 'string1'" min"
  else
    let string1 elapsed/3600. /format f0.1
    let string1 'string1'" hr"
  endif
  define real rate
  let rate totdone/elapsed
  if rate.ge.1 then
    let string2 rate /format f0.1
    let string2 " ("'string2'" files/sec)"
  else if rate.ge.1/60. then
    let string2 rate*60. /format f0.1
    let string2 " ("'string2'" files/min)"
  else
    let string2 rate*3600. /format f0.1
    let string2 " ("'string2'" files/hr)"
  endif
  say ""
  say "Calibrated "'totdone'" files in "'string1''string2'
  !
  ! Duplicate index files (per project and per date). Only combinations
  ! processed above are updated.
  say ""
  define char*8 mydate
  for isetup 1 to mytoc1%nsetup
    !
    mfind /proj 'mytoc1%setup[isetup,1]' -
          /date 'mytoc1%setup[isetup,2]'
    !
    if mfound.gt.0 then
      !
      ! Set up environment depending on project and queue (there may be
      ! several projects in the current TOC!). This defines (among other
      ! things) the variable pipe%projfits. NB: there must be at least
      ! one IMB-FITS in index
      if pipe%dynaproc.ne." "  @ 'pipe%dynaproc'
      !
      if pipe%projfits.ne." " then
        datetime /from mytoc1%setup[isetup,2] DD-MMM-YYYY /TO mydate YYYYMMDD
        message i pipeline "Saving per project index files in "'pipe%projfits'"/"'mydate'
        mcopy /file 'pipe%projfits'"/"'mydate'
      endif
      !
    endif
    !
  next isetup
  !
end procedure pipeline-process
!
begin procedure pipeline-info-logfile
  !---------------------------------------------------------------------
  ! Save the current session log file name in a file which will be
  ! found by odp.sh
  !---------------------------------------------------------------------
  !
  if pipe%info%log.eq." "  return
  if pipe%logfile.eq." "   return
  !
  sic output 'pipe%info%log' new
  say 'pipe%logfile'
  sic output
  !
end procedure pipeline-info-logfile
!
begin procedure pipeline-info-processing
  !---------------------------------------------------------------------
  ! Set up an information file of what is being done (processing)
  !  pro%arg[1]: Project id
  !  pro%arg[2]: Observation date
  !  pro%arg[3]: Backend name
  !---------------------------------------------------------------------
  !
  if pipe%info%status.eq." "  return
  !
  sic output 'pipe%info%status' new
  say "Status:"
  say " - PROCESSING project" 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[3]'
  @ pipeline-info-io
  sic output
  !
end procedure pipeline-info-processing
!
begin procedure pipeline-info-idle
  !---------------------------------------------------------------------
  ! Set up an information file of what is being done (idle)
  !---------------------------------------------------------------------
  !
  if pipe%info%status.eq." "  return
  !
  sic output 'pipe%info%status' new
  say "Status:"
  say " - WAITING for new data"
  @ pipeline-info-io
  sic output
  !
end procedure pipeline-info-idle
!
begin procedure pipeline-info-io
  !---------------------------------------------------------------------
  ! Display a summary of inputs and outputs
  !---------------------------------------------------------------------
  say "Inputs found in"
  say " - IMB-FITS      : "'pipe%imbfits'
  say "Outputs stored in"
  if pipe%projfits.eq." " then
    say " - Index         : "'pipe%indexdir'" and (not yet defined)"
  else
    say " - Index         : "'pipe%indexdir'" and "'pipe%projfits'
  endif
  if pipe%classdir.eq." " then
    say " - Class *.30m   : (not yet defined)"
  else
    say " - Class *.30m   : "'pipe%classdir'
  endif
  say " - TAPAS XML     : "'pipe%vodir'" (or "'pipe%vodir'"/YYYYMMDD/scans/NN/)"
  say " - Plots and html: "'pipe%plot%pdir'" and "'pipe%plot%odir'
  !
  if pipe%logfile.ne." " then
    say "Log file is      : "'pipe%logfile'
  endif
  !
end procedure pipeline-info-io
!
begin procedure pipeline-plots
  !---------------------------------------------------------------------
  ! Generate the success and error plots for all the scans in current
  ! index
  !   pro%arg[1]: Queue
  !   pro%arg[2]: Project id
  !   pro%arg[3]: Observation date (format DD-MMM-YYYY)
  !   pro%arg[4]: Backend name e.g. WILMA
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  if .not.pipe%plot%do  return
  !
  ! Success plots
  @ pipeline-plots-successes 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[4]'
  ! Errors plots
  @ pipeline-plots-errors 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[3]' 'pro%arg[4]'
  !
end procedure pipeline-plots
!
begin procedure pipeline-plots-errors
  !---------------------------------------------------------------------
  ! Generate fake OdP plots with error message for all the scans with
  ! status failed
  !  pro%arg[1]: Queue
  !  pro%arg[2]: Project id
  !  pro%arg[3]: Observation date (format DD-MMM-YYYY)
  !  pro%arg[4]: Backend name
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! ZZZ This should be factorized with the other MFIND
  ! ZZZ This detects again and again also the "old" failures
  mfind /completenes readable -
        /calibrated failed -
        /date 'pro%arg[3]' -
        /obstype CALIBRATE TRACKED ONTHEFLYMAP -
        /projid 'pro%arg[2]' -
        /backend 'pro%arg[4]'
  if (mfound.eq.0)  return
  !
  g\set box_loc 6 26 4 18
  for i 1 to mfound
    ! Already done? Compute pipe%pplotfile
    @ pipeline-plot-filename 'pro%arg[3]' 'mdx%scan[i]' 'pro%arg[4]' "0"
    !
    if .not.file(pipe%pplotfile) then
      !
      g\clear
      g\limits -1 1 -1 7
      g\box n n n
      g\draw text 0 5 "Error processing "'mdx%filename[i]' /user
      g\draw text 0 3 "Most frequent reasons are:"         /user
      g\draw text 0 2 "- Missing calibration in the previous "'set%mrtcal%calib%einterval'" minutes" /user
      g\draw text 0 1 "- Backend streams troubles (reboot needed?)" /user
      !
      @ pipeline-plot-hardcopy 'pro%arg[3]' 'mdx%scan[i]' 'pro%arg[4]' "0"
      @ pipeline-plot-html 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[3]' 'mdx%scan[i]' NONE
    endif
  next i
  !
end procedure pipeline-plots-errors
!
begin procedure pipeline-plots-successes
  !---------------------------------------------------------------------
  ! Generate the OdP plots for all the scans in current index
  !   pro%arg[1]: queue
  !   pro%arg[2]: project id
  !   pro%arg[3]: backend name e.g. WILMA
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*24 backend
  let backend 'pro%arg[3]'
  define char*4 abbrev
  if backend.eq."WILMA" then
    let abbrev "-W0"
  else if backend.eq."VESPA" then
    let abbrev "-V0"
  else if backend.eq."FTS" then
    let abbrev "-F0"
  else if backend.eq."BBC" then
    let abbrev "-B0"
  else if backend.eq."NBC" then
    let abbrev "-N0"
  else
    ! No plots for the other backends?
    return
  endif
  !
  mlist /toc observed scan obstype /variable scantoc
  !
  define char*11 iobse
  define integer iscan
  define char*16 itype
  for isetup 1 to scantoc%nsetup
    let iobse 'scantoc%setup[isetup,1]'
    let iscan 'scantoc%setup[isetup,2]'
    let itype 'scantoc%setup[isetup,3]'
    if itype.eq."CALIBRATE" then
       ! No tracking plot for calibrations (nothing to track)
       @ pipeline-plot-calibrate 'iobse' 'iscan' 'backend' 'abbrev'
    else if itype.eq."POINTING" then
       ! No tracking plot pointings... or do we want?
       @ pipeline-plot-pointing 'iobse' 'iscan' 'backend' 'abbrev'
    else if itype.eq."TRACKED" then
       @ pipeline-plot-tracking 'iobse' 'iscan' 'backend'
       @ pipeline-plot-tracked 'iobse' 'iscan' 'backend' 'abbrev'
    else if itype.eq."ONTHEFLYMAP" then
       @ pipeline-plot-tracking 'iobse' 'iscan' 'backend'
       @ pipeline-plot-otf 'iobse' 'iscan' 'backend' 'abbrev'
    endif
    !
    ! NB1: Note that this procedure will be (re)executed once per backend
    ! (because the current 'pipeline-plots' is also called once per
    ! backend). This way, 1st backend will show plots while others are
    ! not yet calibrated.
    ! NB2: note that the "TRACKED" html mode is always enabled for TRACKED
    ! observation, even if pipe%tel%npolars was found 1 (and not 2). I have
    ! at least 1 example where WILMA has the 2 polars to be overplotted,
    ! but something went wrong with FTS and only 1 polar is available. The
    ! slight counterpart is that the TRACKED plots are always suffixed
    ! with "a" or "b" even if only 1 polar is displayed per plot.
    @ pipeline-plot-html 'pro%arg[1]' 'pro%arg[2]' 'iobse' 'iscan' 'itype'
  next isetup
  !
end procedure pipeline-plots-successes
!
begin procedure pipeline-plot-tracking
  !---------------------------------------------------------------------
  ! OdP plot for tracking errors.
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*8 date
  datetime /from pro%arg[1] DD-MMM-YYYY /to date YYYYMMDD
  define char*256 filename
  let filename "iram30m-"'pro%arg[3]'"-"'date'"s"'pro%arg[2]'"-imb.fits" /lower  ! Lowercase the file name only
  let filename 'pipe%imbfits'"/"'filename'
  !
  if .not.file(filename)  return
  !
  ! Note: it is normally useless to recompute this plots for each backend,
  ! as they all duplicate the antenna streams (only backend streams differ).
  ! Might be an efficiency improvement to disable creating more than 1 plot.
  ! On the other hand, it be useful to have all of them for comparison in
  ! case something goes wrong. Note: the HTML web page shows only 1 plot.
  @ plot-tracking.cal 'filename'
  @ pipeline-plot-hardcopy 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[3]' "tracking"
  !
end procedure pipeline-plot-tracking
!
begin procedure pipeline-plot-calibrate
  !---------------------------------------------------------------------
  ! OdP plot for a CALIBRATE scan. Data taken from the currently opened
  ! Class file.
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  ! Note that the date is taken as input and not from Class data
  ! because of midnight issue (i.e. what if the scan overlaps
  ! midnight?)
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  !
  file in 'pipe%classfile'
  las\set type line
  find /scan iscan /telescope "*"'backabbrev'"*"
  if (found.eq.0)  return
  !
  @ pipeline-parse-parts
  !
  @ pipeline-plot-calibrate-boxsetup
  !
  define integer jpart
  define char*1 xlab ylab
  for ipart 1 to pipe%nparts
    let jpart pipe%parts[ipart]
    find /scan iscan /telescope "*"'backabbrev''jpart'"*"
    !
    clear
    @ window_xy 1 1 1
    l\set weight eq      ! If CALNOSKY, Tsys is -1000 in the header; rejected by STITCH
    stitch /nocheck off  ! Average header for command TITLE
    las\title
    @ pipeline-plot-calibrate-legend
    @ pipeline-plot-calibrate-modey
    !
    if toc%tele[1][10:11].eq."H0" then
      ! This is HERA receiver, e.g. 30M-V01-1H01. One box per pixel:
      for ipix 1 to 9
        find /scan iscan /telescope "*"'backabbrev''jpart'"-*H0"'ipix'
        !
        @ window_xy 3 3 'ipix'
        if ipix.ge.7 then
          let xlab P
        else
          let xlab N
        endif
        if mod(ipix,3).eq.1 then
          let ylab O
        else
          let ylab N
        endif
        if r%head%spe%nchan.eq.1 then  ! Continuum data
          @ pipeline-plot-calibrate-onebox-continuum 'idate' 'iscan' 'backname' 'jpart' 'ipix' 'xlab' 'ylab'
        else  ! Non continuum data
          @ pipeline-plot-calibrate-onebox-spectrum 'xlab' 'ylab'
        endif
        !
        ! Need to load one spectrum to know the telescope name
        get first
        g\draw text 0 -0.5 'r%head%gen%teles' 2 /char 8
      next ipix
      !
    else
      ! Non HERA receiver
      if r%head%spe%nchan.eq.1 then  ! Continuum data
        @ pipeline-plot-calibrate-onebox-continuum 'idate' 'iscan' 'backname' 'jpart' 1 P O
      else  ! Non continuum data
        @ pipeline-plot-calibrate-onebox-spectrum P O
      endif
    endif
    !
    @ pipeline-plot-hardcopy 'idate' 'iscan' 'backname' 'jpart'
    !
  next ipart
  !
  g\set char /def
  !
end procedure pipeline-plot-calibrate
!
begin procedure pipeline-plot-calibrate-boxsetup
  !---------------------------------------------------------------------
  ! Set up the box properties suited for a calibration scan
  !---------------------------------------------------------------------
  !
  @ window_tools
  let aspect_ratio no
  if toc%tele[1][10:11].eq."H0" then
    let position 2.0 28.0 1.5 18.0
    let inter 0.04  ! A bit of spacing between boxes for clarity
    las\set format long
    g\set char 0.45
  else
    let position 3.0 27.0 2.1 14.7
    let inter 0.0
    las\set format full
    g\set char 0.50
  endif
  let position position/21  ! Rescale in unit of Y height
  las\set unit f f
  !
end procedure pipeline-plot-calibrate-boxsetup
!
begin procedure pipeline-plot-calibrate-modey
  !---------------------------------------------------------------------
  ! Set the best limits for all calibration spectra in current index.
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define real mini[3,found] maxi[3,found] minim maxim
  define real tmini[found] tmaxi[found] tminim tmaxim ! Trec in case of CALNOSKY
  get zero
  for i 1 to found
    get next
    compute mini[1,i] min ry                       /blank r%head%spe%bad  ! calsky
    compute mini[2,i] min r%assoc%calcold%data     /blank r%head%spe%bad  ! calcold
    compute mini[3,i] min r%assoc%calambient%data  /blank r%head%spe%bad  ! calambient
    compute maxi[1,i] max ry                       /blank r%head%spe%bad  ! calsky
    compute maxi[2,i] max r%assoc%calcold%data     /blank r%head%spe%bad  ! calcold
    compute maxi[3,i] max r%assoc%calambient%data  /blank r%head%spe%bad  ! calambient
    !
    if r%head%pos%sourc.eq."CALNOSKY" then
      compute tmini[i] min r%assoc%trec%data  /blank r%head%spe%bad
      compute tmaxi[i] max r%assoc%trec%data  /blank r%head%spe%bad
    endif
  next i
  !
  compute minim min mini /blank r%head%spe%bad
  compute maxim max maxi /blank r%head%spe%bad
  las\set mode y minim-(maxim-minim)*0.05 maxim+(maxim-minim)*0.05  ! 5% margins
  !
  if r%head%pos%sourc.eq."CALNOSKY" then
    compute tminim min tmini /blank r%head%spe%bad
    compute tmaxim max tmaxi /blank r%head%spe%bad
    if tminim.eq.tmaxim then
      let pipe%trextr[1] tminim*0.95
      let pipe%trextr[2] tmaxim*1.05
    else
      let pipe%trextr[1] tminim-(tmaxim-tminim)*0.05
      let pipe%trextr[2] tmaxim+(tmaxim-tminim)*0.05
    endif
  endif
  !
end procedure pipeline-plot-calibrate-modey
!
begin procedure pipeline-plot-calibrate-onebox-spectrum
  !---------------------------------------------------------------------
  ! Plot one box and its Cal* spectra from the current index, without
  ! clearing nor changing the box location.
  ! pro%arg[1]: lower axis rule for labelling P/O/N
  ! pro%arg[2]: left axis rule for labelling P/O/N
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  stitch
  !
  ! Box(es)
  if r%head%pos%sourc.eq."CALNOSKY" then
    ! Need 2 sub-boxes (one for the loads, one for Trec). Prepare the
    ! 1st for loads
    define real obox[5]  ! Old box
    let obox box_xmin box_xmax box_ymin 0.0 box_ymax
    let obox[4]  obox[3]+(obox[5]-obox[3])/3  ! 1/3 vs 2/3
    g\set box_loc obox[1] obox[2] obox[4] obox[5]
    las\box N 'pro%arg[2]'
  else
    las\box 'pro%arg[1]' 'pro%arg[2]'
  endif
  if pro%arg[2].ne."N"  g\label "[counts]" /Y
  !
  ! Loads
  spectrum
  pen /col lime_green
  spectrum calcold
  pen /col red
  spectrum calambient
  pen /col foreground
  !
  ! Trec
  if r%head%pos%sourc.eq."CALNOSKY" then
    ! There was no CalSky plotted, plot Trec in a 2nd sub box
    ! Y range
    define real oymin oymax
    let oymin user_ymin
    let oymax user_ymax
    las\set mode y pipe%trextr[1] pipe%trextr[2]
    ! Box
    g\set box_loc obox[1] obox[2] obox[3] obox[4]
    las\box 'pro%arg[1]' 'pro%arg[2]'
    if pro%arg[2].ne."N"  g\label "[K]" /Y
    ! Spectrum
    pen /col blue
    spectrum trec
    ! Revert to normal
    las\set mode y oymin oymax
    pen /col foreground
    g\set box_loc obox[1] obox[2] obox[3] obox[5]
  endif
  !
end procedure pipeline-plot-calibrate-onebox-spectrum
!
begin procedure pipeline-plot-calibrate-onebox-continuum
  !---------------------------------------------------------------------
  ! Plot one box and its Cal* measurements, for continuum data only.
  ! The data is read from the IMB-FITS (instead of the Class file),
  ! because we want to plot the loads w/t time, while time was lost
  ! in the Class file because loads were time-averaged.
  ! pro%arg[1]: date [DD-MMM-YYYY]
  ! pro%arg[2]: scan number
  ! pro%arg[3]: backend name
  ! pro%arg[4]: part id
  ! pro%arg[5]: pixel number
  ! pro%arg[6]: lower axis rule for labelling P/O/N
  ! pro%arg[7]: left axis rule for labelling P/O/N
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*8 date
  datetime /from pro%arg[1] DD-MMM-YYYY /to date YYYYMMDD
  define integer ipart ipix
  let ipart 'pro%arg[4]'
  let ipix 'pro%arg[5]'
  define char*256 filename
  let filename "iram30m-"'pro%arg[3]'"-"'date'"s"'pro%arg[2]'"-imb.fits" /lower  ! Lowercase the file name only
  let filename 'pipe%imbfits'"/"'filename'
  !
  if .not.file(filename) then
    ! IMB FITS file not found, draw single channel spectrum as fallback
    @ pipeline-plot-calibrate-onebox-spectrum 'pro%arg[6]' 'pro%arg[7]'
    g\pen /col red
    g\draw text 0 0.2 "File "'filename'" not found" /box 8
    g\pen /col foreground
    return
  endif
  !
  mset input data all      ! We plot all the dumps (easier because this avoids aligning the MJD ontrack)
  mset input mjdinter yes  ! Same as mset calib mjdinter yes used by reduction pipeline
  mset input tochunk no
  read 'filename' /file
  variable
  !
  ! Find the position of the part we are considering. Remember this is
  ! for continuum (1 channel per part)
  define integer ichan
  for i 1 to imbf%back%head%desc%naxis2
    if imbf%back%table%part[i].eq.ipart.and.imbf%back%table%pixel[i].eq.ipix then
      let ichan imbf%back%table%refchan[i]
    endif
  next i
  !
  ! MJD range?
  define double limmjd[2]
  let limmjd[1] subs%backdata%table%mjd[1]
  read 'filename' /file /subscan 'imbf%prim%n_obsp'
  variable
  let limmjd[2] subs%backdata%table%mjd[subs%backdata%head%desc%naxis2]
  g\limits limmjd[1] limmjd[2] = = /margin 5  ! Let Y limits (set by SET MODE Y) unmodified
  !
  for isub 1 to IMBF%PRIM%N_OBSP
    !
    read 'filename' /file /subscan isub
    variable
    !
    if SUBS%ANTSLOW%HEAD%SUBSTYPE.eq."calSky" then
      pen /col foreground /dash 1
    else if SUBS%ANTSLOW%HEAD%SUBSTYPE.eq."calAmbient" then
      pen /col red /dash 1
    else if SUBS%ANTSLOW%HEAD%SUBSTYPE.eq."calCold" then
      pen /col lime_green /dash 1
    endif
    g\histo SUBS%BACKDATA%TABLE%MJD IMBDATA%IMBF%VAL[ichan,1,]
    !
    pen /colo web_grey /dash 3
    g\draw relocate subs%antslow%head%mjd_beg user_ymin /user
    g\draw line     subs%antslow%head%mjd_beg user_ymax /user
    g\draw relocate subs%antslow%head%mjd_end user_ymin /user
    g\draw line     subs%antslow%head%mjd_end user_ymax /user
    !
  next isub
  !
  pen /def
  !
  ! Plot the box with hrs unit (instead of MJD)
  define real limhr[2]
  datetime /from limmjd MJD /to limhr RADIAN
  let limhr limhr*12/pi
  if limhr[2].lt.limhr[1]  let limhr[2] limhr[2]+24  ! Patch for midnight
  g\limits limhr[1] limhr[2] = = /margin 5
  g\box 'pro%arg[6]' 'pro%arg[7]'
  if pro%arg[6].ne."N"  g\label "UTC [hrs]" /X
  if pro%arg[7].ne."N"  g\label "[counts]" /Y
  !
end procedure pipeline-plot-calibrate-onebox-continuum
!
begin procedure pipeline-plot-calibrate-legend
  !---------------------------------------------------------------------
  ! Draw the legend for the 3 loads
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define real x y ll
  let x 0.0
  let y 0.0
  let ll 1.0  ! Line length
  !
  if r%head%pos%sourc.eq."CALNOSKY" then
    let y y+0.5
    g\draw relo x        y              /box 7
    g\pen /col blue
    g\draw line x+ll     y              /box 7
    g\pen /col 0
    g\draw text x+ll+0.5 y "Trec"     6 /box 7
  endif
  !
  let y y+0.5
  g\draw relo x        y                /box 7
  g\pen /col lime_green
  g\draw line x+ll     y                /box 7
  g\pen /col 0
  g\draw text x+ll+0.5 y "CalCold"    6 /box 7
  !
  if r%head%pos%sourc.ne."CALNOSKY" then
    let y y+0.5
    g\draw relo x        y              /box 7
    g\pen /col 0
    g\draw line x+ll     y              /box 7
    g\draw text x+ll+0.5 y "CalSky"   6 /box 7
  endif
  !
  let y y+0.5
  g\draw relo x        y                /box 7
  g\pen /col red
  g\draw line x+ll     y                /box 7
  g\pen /col 0
  g\draw text x+ll+0.5 y "CalAmbient" 6 /box 7
  !
end procedure pipeline-plot-calibrate-legend
!
begin procedure pipeline-plot-pointing
  !---------------------------------------------------------------------
  ! OdP plot for a POINTING scan. Data taken from the currently opened
  ! Class file.
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  ! Note that the date is taken as input and not from Class data
  ! because of midnight issue (i.e. what if the scan overlaps
  ! midnight?)
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  !
  file in 'pipe%classfile'
  las\set type cont
  find /scan iscan /telescope "*"'backabbrev'"*"
  if (found.eq.0)  return
  !
  ! First main plot combining all backends: regenerated as new backends arrive
  ! Note: this includes the hardcopy
  @ plot-pointing-results-1.cal 'pro%arg[1]' 'pro%arg[2]'
  !
  ! Second main plot combining all backends (same remark)
  ! Note: this includes the hardcopy
  @ plot-pointing-results-2.cal 'pro%arg[1]' 'pro%arg[2]'
  !
  g\set char /def
  !
end procedure pipeline-plot-pointing
!
begin procedure pipeline-plot-tracked
  !---------------------------------------------------------------------
  ! OdP plot for a TRACKED scan. Data taken from the currently opened
  ! Class file.
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  ! Note that the date is taken as input and not from Class data
  ! because of midnight issue (i.e. what if the scan overlaps
  ! midnight?)
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  !
  file in 'pipe%classfile'
  las\set type line
  find /scan iscan /telescope "*"'backabbrev'"*"
  if (found.eq.0)  return
  !
  @ pipeline-parse-setups
  !
  @ pipeline-plot-tracked-boxsetup
  l\set weight eq  ! Avoid STITCH troubles if Tsys is -1000
  !
  @ pipeline-plot-tracked-npolars 'idate' 'iscan' 'backname' yes
  if any(pipe%tel%npolar.ge.2) then
    ! Same plot is repeated a second time, but with swapped polarizations
    @ pipeline-plot-tracked-npolars 'idate' 'iscan' 'backname' no
  endif
  !
  g\set char /def
  !
end procedure pipeline-plot-tracked
!
begin procedure pipeline-plot-tracked-boxsetup
  !---------------------------------------------------------------------
  ! Set up the box properties suited for a tracked scan
  !---------------------------------------------------------------------
  !
  @ window_tools
  let aspect_ratio no
  if toc%tele[1][10:11].eq."H0" then
    let position 1.5 29.0 1.5 17.0
    let inter 0.04  ! A bit of spacing between boxes for clarity
    las\set format long
    g\set char 0.45
  else
    let position 3.0 27.0 2.1 14.7
    let inter 0.0
    las\set format full
    g\set char 0.50
  endif
  let position position/21  ! Rescale in unit of Y height
  las\set unit f f
  !
end procedure pipeline-plot-tracked-boxsetup
!
begin procedure pipeline-plot-tracked-npolars
  !---------------------------------------------------------------------
  ! Draw one polar over the other one. Order is ruled by argument
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] direct order or indirect?
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname
  let backname 'pro%arg[3]'
  define logical direct
  let direct 'pro%arg[4]'
  !
  define integer firstpol lastpol incrpol
  define char*1 suffix
  !
  define char*1 xlab ylab
  !
  for itel 1 to pipe%tel%n
    !
    if direct then
      let firstpol 1
      let lastpol pipe%tel%npolar[itel]
      let incrpol +1
      let suffix "a"
    else
      let firstpol pipe%tel%npolar[itel]
      let lastpol  1
      let incrpol  -1
      let suffix "b"
    endif
    !
    find        /scan iscan /telescope 'pipe%tel%name[1,itel]'
    for ipol 2 to pipe%tel%npolar[itel]
      find append /scan iscan /telescope 'pipe%tel%name[ipol,itel]'
    next ipol
    !
    clear
    @ window_xy 1 1 1
    @ pipeline-plot-tracked-modey 'iscan' 'itel'
    @ pipeline-plot-tracked-legend 'iscan' 'itel'
    !
    if toc%tele[1][10:11].eq."H0" then
      ! This is HERA receiver, e.g. 30M-V01-1H01. One box per pixel:
      for ipix 1 to 9
        !
        @ window_xy 3 3 'ipix'
        if ipix.ge.7 then
          let xlab P
        else
          let xlab N
        endif
        if mod(ipix,3).eq.1 then
          let ylab O
        else
          let ylab N
        endif
        for ipol firstpol to lastpol by incrpol
          find /scan iscan /telescope 'pipe%tel%name[ipol,itel][1:11]''ipix'
          @ pipeline-plot-tracked-onebox 'xlab' 'ylab' 'pipe%tel%color[ipol,itel]'
        next ipol
        !
        ! Need to average the 2 polarizations to know the "average" telescope
        ! name for this pixel
        find        /scan iscan /telescope 'pipe%tel%name[1,itel][1:11]''ipix'
        for ipol 2 to pipe%tel%npolar[itel]
          find append /scan iscan /telescope 'pipe%tel%name[ipol,itel][1:11]''ipix'
        next ipol
        stitch
        g\draw text 0 -0.5 'r%head%gen%teles' 2 /char 8
      next ipix
      !
      ! Need an average header with the last polarization drawn (all pixels)
      find /scan iscan /telescope 'pipe%tel%name[lastpol,itel]'
      stitch /nocheck offset
      !
    else
      ! Non HERA receiver e.g. 30ME0VUI-V01
      !
      for ipol firstpol to lastpol by incrpol
        find /scan iscan /telescope 'pipe%tel%name[ipol,itel]'
        ! ZZZ ca dessine la boite 2 fois...
        @ pipeline-plot-tracked-onebox P O 'pipe%tel%color[ipol,itel]'
      next ipol
      !
    endif
    !
    las\title  ! Put a title, but for the last polarization only
    !
    @ pipeline-plot-calwith 'idate' 'iscan' 'backname'
    @ pipeline-plot-hardcopy 'idate' 'iscan' 'backname' 'pipe%tel%part[itel]''suffix'
    !
  next itel
  !
end procedure pipeline-plot-tracked-npolars
!
begin procedure pipeline-plot-tracked-modey
  !---------------------------------------------------------------------
  ! Set the best limits for all spectra in current index.
  !   pro%arg[1] scan number
  !   pro%arg[2] telescope setup number
  ! This procedure is a bit tricky because we want limits which suit
  ! well for all polarizations at the same time (as we overplot them
  ! all in one plot).
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define integer iscan itel
  let iscan 'pro%arg[1]'
  let itel  'pro%arg[2]'
  !
  define integer npol
  let npol pipe%tel%npolar[itel]
  !
  define real minim maxim
  if toc%tele[1][10:11].eq."H0" then
    define real mini[npol,9] maxi[npol,9]
    for ipix 1 to 9
      for ipol 1 to npol
        find /scan iscan /telescope 'pipe%tel%name[ipol,itel][1:11]''ipix'
        stitch /noc offset
        compute mini[ipol,ipix] min ry /blank r%head%spe%bad
        compute maxi[ipol,ipix] max ry /blank r%head%spe%bad
      next ipol
    next ipix
  else
    define real mini[npol] maxi[npol]
    for ipol 1 to npol
      find /scan iscan /telescope 'pipe%tel%name[ipol,itel]'
      stitch
      compute mini[ipol] min ry /blank r%head%spe%bad
      compute maxi[ipol] max ry /blank r%head%spe%bad
    next ipol
  endif
  !
  compute minim min mini /blank r%head%spe%bad
  compute maxim max maxi /blank r%head%spe%bad
  !
  define real yrange
  if minim.eq.r%head%spe%bad then
    ! No valid data
    let minim 0
    let maxim 0
    let yrange 1
  else if minim.lt.maxim then
    ! Ymin < Ymax (valid values)
    let yrange maxim-minim
  else if minim.ne.0.0 then
    ! Ymin == Ymax != 0
    let yrange abs(minim)
  else
    ! Ymin == Ymax == 0
    let yrange 1
  endif
  las\set mode y minim-yrange*0.05 maxim+yrange*0.05  ! 5% margins
  !
end procedure pipeline-plot-tracked-modey
!
begin procedure pipeline-plot-tracked-onebox
  !---------------------------------------------------------------------
  ! Plot one box and its tracked spectrum from the current index,
  ! without clearing nor changing the box location.
  ! pro%arg[1]: lower axis rule for labelling P/O/N
  ! pro%arg[2]: left axis rule for labelling P/O/N
  ! pro%arg[3]: spectrum color name
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  if found.le.0  return
  !
  stitch /noc offset  ! No check for offsets as this procedure is also
                      ! used for some OTF plots
  las\box 'pro%arg[1]' 'pro%arg[2]'
  pen /col 'pro%arg[3]'
  spectrum
  pen /col foreground
  !
end procedure pipeline-plot-tracked-onebox
!
begin procedure pipeline-plot-tracked-legend
  !---------------------------------------------------------------------
  ! Draw the legend for the polarizations. This is suited only for non
  ! HERA receiver (because of specific telescope name)
  ! pro%arg[1] scan number
  ! pro%arg[2] telescope setup number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define integer iscan itel
  let iscan 'pro%arg[1]'
  let itel  'pro%arg[2]'
  !
  define real x y ll
  let x 0.0
  let y 0.0
  let ll 1.0  ! Line length
  !
  for ipol 1 to pipe%tel%npolar[itel]
    find /scan iscan /telescope 'pipe%tel%name[ipol,itel]'
    !
    if found.gt.0 then
      let y y+0.5
      g\draw relo x        y                              /box 7
      g\pen /col 'pipe%tel%color[ipol,itel]'
      g\draw line x+ll     y                              /box 7
      g\pen /col foreground
      g\draw text x+ll+0.5 y 'pipe%tel%name[ipol,itel]' 6 /box 7
    endif
    !
  next ipol
  !
  if pipe%tel%npolar[itel].gt.pipe%mpolars then
    let y y+0.5
    g\pen /col orange
    g\draw text x y 'pipe%tel%npolar[itel]'" PARTS IN THIS BAND!" 6 /box 7
    g\pen /col foreground
  endif
  !
end procedure pipeline-plot-tracked-legend
!
begin procedure pipeline-plot-otf
  !---------------------------------------------------------------------
  ! OdP plot for an OTF scan. Data taken from the currently opened
  ! Class file.
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  ! Note that the date is taken as input and not from Class data
  ! because of midnight issue (i.e. what if the scan overlaps
  ! midnight?)
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  !
  file in 'pipe%classfile'
  las\set type line
  find /scan iscan /telescope "*"'backabbrev'"*"
  if (found.eq.0)  return
  !
  @ pipeline-parse-parts
  !
  lut rainbow3
  las\set mode all total
  las\set sort number
  !
  define integer jpart
  for ipart 1 to pipe%nparts
    let jpart pipe%parts[ipart]
    !
    ! 2 plots: one with the 2D load, one with the average spectrum
    @ pipeline-plot-otf-load 'idate' 'iscan' 'backname' 'backabbrev' 'jpart'
    @ pipeline-plot-otf-average 'idate' 'iscan' 'backname' 'backabbrev' 'jpart'
    !
  next ipart
  !
  g\set char /def
  !
end procedure pipeline-plot-otf
!
begin procedure pipeline-plot-otf-load
  !---------------------------------------------------------------------
  ! LOAD and draw the current index
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  !   pro%arg[5] part number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  define integer jpart
  let jpart 'pro%arg[5]'
  !
  @ pipeline-plot-otf-load-boxsetup
  !
  clear
  @ window_xy 1 1 1
  !
  find /scan iscan /telescope "*"'backabbrev''jpart'"*"
  load  ! Average header for command TITLE /INDEX
  las\title /index
  @ pipeline-plot-otf-load-modey
  !
  define char*1 xlab ylab
  !
  if toc%tele[1][10:11].eq."H0" then
    ! This is HERA receiver, e.g. 30M-V01-1H01. One box per pixel:
    for ipix 1 to 9
      find /scan iscan /telescope "*"'backabbrev''jpart'"-*H0"'ipix'
      !
      @ window_xy 3 3 'ipix'
      if ipix.ge.7 then
        let xlab P
      else
        let xlab N
      endif
      if mod(ipix,3).eq.1 then
        let ylab O
      else
        let ylab N
      endif
      @ pipeline-plot-otf-load-onebox 'xlab' 'ylab'
      !
      ! Need to load one spectrum to know the telescope name (up to now
      ! we worked in index mode, no R buffer in memory)
      get first  ! ZZZ utiliser idx
      g\draw text 0 -0.5 'r%head%gen%teles' 2 /char 8
    next ipix
    ! Remove the individual wedges and make a unique one
    g\clear segment rwedge:*
    g\clear segment wbox:*
    @ window_xy 1 1 1
    wedge
    !
  else
    ! Non HERA receiver
    @ pipeline-plot-otf-load-onebox P O
  endif
  !
  @ pipeline-plot-calwith 'idate' 'iscan' 'backname'
  @ pipeline-plot-hardcopy 'idate' 'iscan' 'backname' 'jpart'"a"
  !
end procedure pipeline-plot-otf-load
!
begin procedure pipeline-plot-otf-load-boxsetup
  !---------------------------------------------------------------------
  ! Set up the box properties suited for an OTF scan (LOAD version)
  !---------------------------------------------------------------------
  !
  @ window_tools
  let aspect_ratio no
  if toc%tele[1][10:11].eq."H0" then
    let position 2.0 28.0 1.5 16.5
    let inter 0.04  ! A bit of spacing between boxes for clarity
    las\set format full
    g\set char 0.45
  else
    let position 2.0 28.0 1.5 16.0
    let inter 0.0
    las\set format full
    g\set char 0.50
  endif
  let position position/21  ! Rescale in unit of Y height
  las\set unit f f
  !
end procedure pipeline-plot-otf-load-boxsetup
!
begin procedure pipeline-plot-otf-load-modey
  !---------------------------------------------------------------------
  ! Set the best limits for all spectra in current index.
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Need the blank value! Same as command LOAD: take it from 1st
  ! spectrum in index
  l\get first
  !
  define real ymin ymax
  compute ymin min p%y /blank r%head%spe%bad
  compute ymax max p%y /blank r%head%spe%bad
  set mode y ymin ymax
  !
end procedure pipeline-plot-otf-load-modey
!
begin procedure pipeline-plot-otf-load-onebox
  !---------------------------------------------------------------------
  ! Plot one box and its index plot from the current index, without
  ! clearing nor changing the box location.
  ! pro%arg[1]: lower axis rule for labelling P/O/N
  ! pro%arg[2]: left axis rule for labelling P/O/N
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define real osize
  let osize character_size
  !
  ! Main plot
  las\load  ! Load data
  exp\variable general /index  ! Load headers
  !
  define integer dims[8]
  compute dims dimof p%x
  if dims[1].eq.1 then
    ! Continuum backend
    define real hrs /like idx%head%gen%ut
    let hrs idx%head%gen%ut*12/pi
    g\limits /var hrs p%y
    g\box
    g\connect hrs p%y  ! Avoid g\histo which makes a weird connector between subscans
    g\label "UTC [hrs]" /X
    !
    ! Subscan separators
    define integer isub
    let isub 0
    g\set char 0.40
    g\pen /colo dim_grey /dash 2
    for i 1 to found
      if idx%head%gen%subscan[i].ne.isub then
        ! Another subscan begins. Draw the subscan separator
        g\draw relo hrs[i] user_ymin /user
        g\draw line hrs[i] user_ymax /user
        g\draw text hrs[i] user_ymin " "'idx%head%gen%subscan[i]' 3 90 /user
        let isub idx%head%gen%subscan[i]
      endif
    next i
    g\pen /colo foreground /dash 1
    !
  else
    ! Non continuum backend
    ! Plot load before wedge else the wedge would have incorrect limits!
    las\spectrum /index
    las\box 'pro%arg[1]' 'pro%arg[2]' /index
    !
    ! Subscan separators
    define integer isub
    let isub 0
    g\set char 0.35
    g\pen /colo dim_grey
    for i 1 to found
      if idx%head%gen%subscan[i].ne.isub then
        ! Another subscan begins
        if idx%head%gen%subscan[i].eq.isub+1 then
          g\pen /dash 2
        else
          g\pen /dash 1
        endif
        if isub.ne.0 then
          ! Draw the subscan separator
          g\draw relo user_xmin i /user
          g\draw line user_xmax i /user
        endif
        g\draw text user_xmin i " "'idx%head%gen%subscan[i]' 9 /user
        let isub idx%head%gen%subscan[i]
      endif
    next i
    g\pen /colo foreground /dash 1
  endif
  !
  g\set char osize
  !
end procedure pipeline-plot-otf-load-onebox
!
begin procedure pipeline-plot-otf-average
  !---------------------------------------------------------------------
  ! Average and draw the current index
  !   pro%arg[1] date [DD-MMM-YYYY]
  !   pro%arg[2] scan number
  !   pro%arg[3] backend name e.g. WILMA
  !   pro%arg[4] backend abbreviation e.g. -W0
  !   pro%arg[5] part number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*11 idate
  let idate 'pro%arg[1]'
  define integer iscan
  let iscan 'pro%arg[2]'
  define char*12 backname backabbrev
  let backname 'pro%arg[3]'
  let backabbrev 'pro%arg[4]'
  define integer jpart
  let jpart 'pro%arg[5]'
  !
  @ pipeline-plot-otf-average-boxsetup
  !
  clear
  @ window_xy 1 1 1
  !
  find /scan iscan /telescope "*"'backabbrev''jpart'"*"
  load  ! Average header for command TITLE /INDEX
  las\title /index  ! Put a title => reuse an index title, this is nicer
                    ! as we overlay the average with the load
  @ pipeline-plot-otf-average-modey 'iscan' 'backabbrev' 'jpart'
  !
  define char*1 xlab ylab
  !
  if toc%tele[1][10:11].eq."H0" then
    ! This is HERA receiver, e.g. 30M-V01-1H01. One box per pixel:
    for ipix 1 to 9
      find /scan iscan /telescope "*"'backabbrev''jpart'"-*H0"'ipix'
      !
      @ window_xy 3 3 'ipix'
      if ipix.ge.7 then
        let xlab P
      else
        let xlab N
      endif
      if mod(ipix,3).eq.1 then
        let ylab O
      else
        let ylab N
      endif
      @ pipeline-plot-otf-average-onebox 'xlab' 'ylab'
      g\draw text 0 -0.5 'r%head%gen%teles' 2 /char 8
    next ipix
    !
  else
    ! Non HERA receiver
    @ pipeline-plot-otf-average-onebox P O
  endif
  !
  @ pipeline-plot-calwith 'idate' 'iscan' 'backname'
  @ pipeline-plot-hardcopy 'idate' 'iscan' 'backname' 'jpart'"b"
  !
end procedure pipeline-plot-otf-average
!
begin procedure pipeline-plot-otf-average-boxsetup
  !---------------------------------------------------------------------
  ! Set up the box properties suited for an OTF scan (AVERAGE version)
  !---------------------------------------------------------------------
  !
  ! Reuse this one so that the alternate plots (OTF vs average) use the
  ! same box
  @ pipeline-plot-otf-load-boxsetup
  !
  ! But the format should be not be "full" (not enough room)
  las\set format long
  !
end procedure pipeline-plot-otf-average-boxsetup
!
begin procedure pipeline-plot-otf-average-modey
  !---------------------------------------------------------------------
  ! Set the best limits for all spectra in current index.
  !   pro%arg[1] scan number
  !   pro%arg[2] backend abbreviation e.g. -W0
  !   pro%arg[3] part number
  ! This procedure is a bit tricky because we want limits which suit
  ! well for all polarizations at the same time (as we overplot them
  ! all in one plot).
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define integer iscan jpart
  define char*12 backabbrev
  let iscan 'pro%arg[1]'
  let backabbrev 'pro%arg[2]'
  let jpart 'pro%arg[3]'
  !
  define real minim maxim
  if toc%tele[1][10:11].eq."H0" then
    define real mini[9] maxi[9]
    for ipix 1 to 9
      find /scan iscan /telescope "*"'backabbrev''jpart'"-*H0"'ipix'
      stitch /noc offset
      compute mini[ipix] min ry /blank r%head%spe%bad
      compute maxi[ipix] max ry /blank r%head%spe%bad
    next ipix
    compute minim min mini /blank r%head%spe%bad
    compute maxim max maxi /blank r%head%spe%bad
  else
    find /scan iscan /telescope "*"'backabbrev''jpart'"*"
    stitch /nocheck off
    compute minim min ry /blank r%head%spe%bad
    compute maxim max ry /blank r%head%spe%bad
  endif
  !
  define real yrange
  if minim.eq.r%head%spe%bad then
    ! No valid data
    let minim 0
    let maxim 0
    let yrange 1
  else if minim.lt.maxim then
    ! Ymin < Ymax (valid values)
    let yrange maxim-minim
  else if minim.ne.0.0 then
    ! Ymin == Ymax != 0
    let yrange abs(minim)
  else
    ! Ymin == Ymax == 0
    let yrange 1
  endif
  las\set mode y minim-yrange*0.05 maxim+yrange*0.05  ! 5% margins
  !
end procedure pipeline-plot-otf-average-modey
!
begin procedure pipeline-plot-otf-average-onebox
  !---------------------------------------------------------------------
  ! Plot one box and its index plot from the current index, without
  ! clearing nor changing the box location.
  ! pro%arg[1]: lower axis rule for labelling P/O/N
  ! pro%arg[2]: left axis rule for labelling P/O/N
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Reuse this one which is fine:
  @ pipeline-plot-tracked-onebox 'pro%arg[1]' 'pro%arg[2]' "foreground"
  !
end procedure pipeline-plot-otf-average-onebox
!
begin procedure pipeline-plot-calwith
  !---------------------------------------------------------------------
  ! Add a legend line
  !   "Calibrated with scan XXX from YY min ago (limit is ZZZ)"
  ! pro%arg[1] date [DD-MMM-YYYY]
  ! pro%arg[2] scan number
  ! pro%arg[3] backend name e.g. WILMA
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  @ window_xy 1 1 1
  !
  define char*11 cdate sdate
  define integer cscan sscan
  define char*12 cback sback
  define double  cmjd  smjd
  !
  ! Science scan
  let sdate 'pro%arg[1]'
  let sscan 'pro%arg[2]'
  let sback 'pro%arg[3]'
  mfind /date 'sdate' /scan 'sscan' /back 'sback'
  datetime /from mdx%dobs[1] GAG_DATE mdx%ut[1] RADIAN /to smjd MJD
  mset input data none
  m\read 'mdx%num[1]'
  m\variable
  !
  ! Calibration scan
  datetime /from mhead%sci%caldobs GAG_DATE /to cdate DD-MMM-YYYY
  let cscan 'mhead%sci%calscan'
  let cback 'sback'
  mfind /date 'cdate' /scan 'cscan' /back 'cback'
  !
  define char*128 message
  if (mfound.eq.1) then
    datetime /from mdx%dobs[1] GAG_DATE mdx%ut[1] RADIAN /to cmjd MJD
    !
    define char*128 mess1 mess2
    define double diff
    let diff (smjd-cmjd)*24*60  ! Day to minutes
    if diff.lt.1. then
      let mess1 diff*60 /format f0.1
      let mess1 'mess1'" sec."
    else if diff.gt.60. then
      let mess1 diff/60 /format f0.1
      let mess1 'mess1'" hrs."
    else
      let mess1 diff /format f0.1
      let mess1 'mess1'" min."
    endif
    let mess1 "Scan "'sscan'" calibrated with scan "'cscan'" observed "'mess1'" before"
    !
    let mess2 set%mrtcal%calib%einterval /format f0.0
    let mess2 "(limit "'mess2'" min)"
    !
    if diff.gt.set%mrtcal%calib%winterval
      let message 'mess1'" "'mess2' /upper
      pen /col dark_orange
    else
      let message 'mess1'" "'mess2'
      pen /col web_green
    endif
    !
  else
    ! 0 (or more than 1) calibration scan
    let message "Scan "'sscan'" calibrated with unknown scan"
    pen /col dark_orange
  endif
  !
  g\draw text 0 0.5 'message' 4 /box 9
  pen /col foreground
  !
end procedure pipeline-plot-calwith
!
begin procedure pipeline-plot-filename
  !---------------------------------------------------------------------
  ! Generate the PNG plot file name
  ! pro%arg[1] date [DD-MMM-YYYY]
  ! pro%arg[2] scan number
  ! pro%arg[3] backend name
  ! pro%arg[4] part id
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Clear previous values in case of error
  let pipe%plotfile " "
  let pipe%pplotfile " "
  !
  define char*8 obsdate
  datetime /from pro%arg[1] DD-MMM-YYYY /to obsdate YYYYMMDD
  !
  if pro%arg[3].eq." " then
    let pipe%plotfile "iram30m-plot-"'obsdate'"s"'pro%arg[2]'"-"'pro%arg[4]'".png"
  else
    let pipe%plotfile "iram30m-plot-"'obsdate'"s"'pro%arg[2]'"-"'pro%arg[3]'"-"'pro%arg[4]'".png"
  endif
  !
  ! Full path to project directory
  let pipe%pplotfile 'pipe%plot%pdir'"/"'pipe%plotfile'
  !
end procedure pipeline-plot-filename
!
begin procedure pipeline-plot-hardcopy
  !---------------------------------------------------------------------
  ! Generate the hardcopies
  ! pro%arg[1] date [DD-MMM-YYYY]
  ! pro%arg[2] scan number
  ! pro%arg[3] backend name
  ! pro%arg[4] part id
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Compute pipe%plotfile and pipe%pplotfile:
  @ pipeline-plot-filename 'pro%arg[1]' 'pro%arg[2]' 'pro%arg[3]' 'pro%arg[4]'
  !
  hardcopy 'pipe%pplotfile' /dev png /overwrite
  !
  if pipe%plot%odir.ne." " then
    sic copy 'pipe%pplotfile' -
             'pipe%plot%odir'"/"'pipe%plot%osubdir'"/"'pipe%plotfile'
  endif
  !
  say ""
  !
end procedure pipeline-plot-hardcopy
!
begin procedure pipeline-plot-html
  !---------------------------------------------------------------------
  ! Build the html file showing all the plots for given date and scan
  !  pro%arg[1]  Queue
  !  pro%arg[2]  Project id
  !  pro%arg[3]  date [DD-MMM-YYYY]
  !  pro%arg[4]  scan number
  !  pro%arg[5]  observing type in CALIBRATE, POINTING, TRACKED,
  !              ONTHEFLYMAP, NONE
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*12 obstype
  let obstype 'pro%arg[5]'
  define char*8 yyyymmdd
  datetime /from pro%arg[3] DD-MMM-YYYY /to yyyymmdd YYYYMMDD
  define integer iscan
  let iscan 'pro%arg[4]'
  define char*32 prefix
  let prefix "iram30m-plot-"'yyyymmdd'"s"'iscan'  ! e.g. iram30m-plot-20161116s230
  define integer lprefix
  let lprefix len_trim(prefix)
  !
  define logical suffixed
  let suffixed obstype.eq."TRACKED".or.obstype.eq."ONTHEFLYMAP"
  !
  ! Compute the list of PNG for this scan in structure pipe%png%
  @ pipeline-plot-html-pnglist 'prefix' 'suffixed'
  @ pipeline-plot-html-tracklist 'prefix'
  @ pipeline-plot-html-pointing-list 'prefix'
  if pipe%png%nfile.le.0.and.pipe%png%track%file.eq." ".and.pipe%png%pointing%nfile.eq.0  return  ! Nothing to do
  !
  ! Create the JS file with list to other scans
  @ pipeline-plot-html-scanlist 'pro%arg[2]' 'pro%arg[3]' 'pipe%plot%pdir' ".html" NO
  !
  ! Now create the html
  define char*128 htmlfile
  let htmlfile "iram30m-plot-"'yyyymmdd'"s"'iscan'  ! No ".html" suffix
  sic out 'pipe%plot%pdir'"/"'htmlfile'".html"
  !
  @ pipeline-plot-html-topmenu 'pro%arg[2]' 'yyyymmdd' 'iscan'
  @ pipeline-plot-html-queue 'pro%arg[1]' 'pro%arg[2]'
  @ pipeline-plot-html-tracking
  @ pipeline-plot-html-restable 'yyyymmdd' 'iscan' 'obstype'
  @ pipeline-plot-html-pointing
  @ pipeline-plot-html-backends 'obstype' 'suffixed'
  !
  ! Add a few extra lines so that the last anchored image appears at
  ! correct place when clicking on its anchor.
  say "<BR><BR><BR><BR><BR><BR>"
  sic out
  !
  ! Duplicate the html file if needed
  if pipe%plot%odir.ne." " then
    ! Copy, but suffixed with -mrtcal.html to avoid ambiguity with Mira ones!
    sic copy 'pipe%plot%pdir'"/"'htmlfile'".html" -
             'pipe%plot%odir'"/"'htmlfile'"-mrtcal.html"
    ! Need another version of the scanlist js file
    @ pipeline-plot-html-scanlist 'pro%arg[2]' 'pro%arg[3]' 'pipe%plot%odir' -
                                  "-mrtcal.html" 'pipe%plot%osymlink.ne." "'
    !
    if pipe%plot%osymlink.ne." " then
      define char*256 symlink
      let symlink 'pipe%plot%odir'"/"'pipe%plot%osymlink'
      sic delete 'symlink'
      system "ln -s "'htmlfile'"-mrtcal.html "'symlink'
    endif
  endif
  !
end procedure pipeline-plot-html
!
begin procedure pipeline-plot-html-pnglist
  !---------------------------------------------------------------------
  ! Create the list of PNG files for this scan, and store it in the
  ! global structure pipe%png%
  !   pro%arg[1]  PNG file name prefix
  !   pro%arg[2]  YES or NO: is the file name suffixed with "a" or "b"
  !
  ! The PNG file names discovered by this procedure follow a strict
  ! convention (see SIC FIND below). The capital letter in the backend
  ! name allow to distinguish from other PNGs not to be discovered here.
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*32 prefix
  let prefix 'pro%arg[1]'  ! e.g. iram30m-plot-20161116s230
  define integer lprefix
  let lprefix len_trim(prefix)
  define logical suffixed
  let suffixed 'pro%arg[2]'
  !
  if suffixed then
    ! Files are suffixed with "a.png" or "b.png"
    sic find 'prefix'"-[A-Z]*[0-9]a.png" 'pipe%plot%pdir' ! e.g. iram30m-plot-20161116s230-*[1-9]a.png
  else
    sic find 'prefix'"-[A-Z]*[0-9].png" 'pipe%plot%pdir' ! e.g. iram30m-plot-20161116s230-*[1-9].png
  endif
  !
  ! Save in pipe%png% because sub-procedures may do their own SIC FIND
  if exist(pipe%png)  delete /variable pipe%png
  define structure pipe%png /like dir /global
  let pipe%png% dir%
  if pipe%png%nfile.le.0  return
  !
  ! Sort file names
  ! sic\sort pipe%png%file  ! Alphabetically => not what we want...
  define integer wei[pipe%png%nfile] il ir ns
  define char*1 b  ! Backend letter
  if suffixed then
    let ns 5  ! Suffix to ignore has 5 characters: "a.png"
  else
    let ns 4  ! Suffix to ignore has 4 characters: ".png"
  endif
  for ifile 1 to pipe%png%nfile
    ! Weight by backend part
    let ir len_trim(pipe%png%file[ifile])-ns
    let wei[ifile] 'pipe%png%file[ifile][ir:ir]'
    ! And weight by backend
    let il lprefix+2
    let b 'pipe%png%file[ifile][il:il]'
    if b.eq."F" then      ! FTS
      let wei[ifile] wei[ifile]+10
    else if b.eq."W" then  ! WILMA
      let wei[ifile] wei[ifile]+20
    else if b.eq."V" then  ! VESPA
      let wei[ifile] wei[ifile]+30
    else if b.eq."B" then  ! BBC
      let wei[ifile] wei[ifile]+40
    else if b.eq."N" then  ! NBC
      let wei[ifile] wei[ifile]+50
    else  ! Unknown => will appear on top of the list
      let wei[ifile] wei[ifile]+0
    endif
  next ifile
  sic\sort wei pipe%png%file
  delete /variable wei il ir
  if pipe%png%nfile.le.0  return  ! Nothing to do
  !
  ! Also compute backend parts from file names
  define char*16 pipe%png%lparts[pipe%png%nfile] /global  ! long, e.g. VESPA-1 or VESPA-1+2
  define char*8  pipe%png%sparts[pipe%png%nfile] /global  ! short, e.g. V1 or V1+2
  define integer il im ir
  for ifile 1 to pipe%png%nfile
    ! Long name: just extract the piece of file name
    let il lprefix+2
    let ir len_trim(pipe%png%file[ifile])-ns
    let pipe%png%lparts[ifile]  'pipe%png%file[ifile][il:ir]'  ! e.g. VESPA-1
    ! Short name: extract first backend letter + number(s)
    let ir len_trim(pipe%png%lparts[ifile])
    for iletter 2 to ir
      if pipe%png%lparts[ifile][iletter:iletter].eq."-" then
        let im iletter+1
        break
      endif
    next iletter
    let pipe%png%sparts[ifile]  -
      'pipe%png%lparts[ifile][1:1]''pipe%png%lparts[ifile][im:ir]'  ! e.g. "V" + "1+2"
  next ifile
  !
end procedure pipeline-plot-html-pnglist
!
begin procedure pipeline-plot-html-tracklist
  !---------------------------------------------------------------------
  ! Fetch one antenna tracking plot in the dedicated structure
  !   pro%arg[1]  PNG file name prefix
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*32 prefix
  let prefix 'pro%arg[1]'  ! e.g. iram30m-plot-20161116s230
  define integer lprefix
  let lprefix len_trim(prefix)
  !
  sic find 'prefix'"-*-tracking.png" 'pipe%plot%pdir' ! e.g. iram30m-plot-20161116s230-*-tracking.png
  !
  define structure pipe%png%track /global
  define char*512  pipe%png%track%file /global
  define char*12   pipe%png%track%backend /global
  if dir%nfile.le.0  return
  !
  ! Sort file names by backends
  define integer wei[dir%nfile] il
  define char*1 b  ! Backend letter
  for ifile 1 to dir%nfile
    ! Weight by backend
    let il lprefix+2
    let b 'dir%file[ifile][il:il]'
    if b.eq."F" then      ! FTS
      let wei[ifile] wei[ifile]+10
    else if b.eq."W" then  ! WILMA
      let wei[ifile] wei[ifile]+20
    else if b.eq."V" then  ! VESPA
      let wei[ifile] wei[ifile]+30
    else if b.eq."B" then  ! BBC
      let wei[ifile] wei[ifile]+40
    else if b.eq."N" then  ! NBC
      let wei[ifile] wei[ifile]+50
    else  ! Unknown => will appear on top of the list
      let wei[ifile] wei[ifile]+0
    endif
  next ifile
  sic\sort wei dir%file
  delete /variable wei il
  let pipe%png%track%file 'dir%file[1]'
  !
  ! Backend name
  define integer if il
  let if lprefix+2
  let il len_trim(pipe%png%track%file)-13  ! "-tracking.png" = 13 chars
  let pipe%png%track%backend 'pipe%png%track%file[if:il]'
  !
end procedure pipeline-plot-html-tracklist
!
begin procedure pipeline-plot-html-pointing-list
  !---------------------------------------------------------------------
  ! Fetch the pointing result plots
  !   pro%arg[1]  PNG file name prefix
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*32 prefix
  let prefix 'pro%arg[1]'  ! e.g. iram30m-plot-20161116s230
  define integer lprefix
  let lprefix len_trim(prefix)
  !
  define structure pipe%png%pointing         /global
  define integer   pipe%png%pointing%nfile   /global
  !
  ! Count PNG
  sic find 'prefix'"-pointing-*.png" 'pipe%plot%pdir' ! e.g. iram30m-plot-20220817s29-pointing-E0.png
  let pipe%png%pointing%nfile dir%nfile
  !
  if pipe%png%pointing%nfile.eq.0  return
  !
  ! Now collect names
  define char*512 pipe%png%pointing%file[pipe%png%pointing%nfile] /global
  define integer jfile
  !
  sic find 'prefix'"-pointing-*.png" 'pipe%plot%pdir' ! e.g. iram30m-plot-20220817s29-pointing-E0.png
  ! First basic sorting which ensures in particular that e.g. E0 comes before E2
  sic\sort dir%file
  !
  ! Second sorting
  define integer wei[pipe%png%pointing%nfile] il ir ns
  for ifile 1 to pipe%png%pointing%nfile
    let wei[ifile] 1
    let ns len_trim(dir%file[ifile])
    let ir ns-4  ! Strip off ".png"
    let il ir-3
    if dir%file[ifile][il:ir].eq."-all" then
      let wei[ifile] 0  ! Put first
    endif
  next ifile
  sic\sort wei dir%file
  del /var wei
  for ifile 1 to dir%nfile
    let jfile jfile+1
    let pipe%png%pointing%file[jfile] 'dir%file[ifile]'
  next ifile
  !
  ! Also compute anchor names from file names
  define char*3 pipe%png%pointing%anchor[pipe%png%pointing%nfile] /global  ! e.g. "all" or "E0"
  let ns 4   ! Suffix to be ignored i.e. ".png"
  for ifile 1 to pipe%png%pointing%nfile
    ! Just extract the piece of file name
    let ir len_trim(pipe%png%pointing%file[ifile])-ns
    for ipos ir to 1 by -1
      if pipe%png%pointing%file[ifile][ipos:ipos].eq."-" then
        let il ipos+1
        break
      endif
    next ipos
    let pipe%png%pointing%anchor[ifile] 'pipe%png%pointing%file[ifile][il:ir]'
  next ifile
  !
end procedure pipeline-plot-html-pointing-list
!
begin procedure pipeline-plot-html-scanlist
  !---------------------------------------------------------------------
  ! Insert the scan list (links between scan pages)
  ! This is done through an inclusion, so that updating the include
  ! file will also update all the old html files
  ! So this procedure does 2 things:
  !  - insert the inclusion command line
  !  - update the include file
  ! The procedure assumes the HTML is currently opened with SIC OUTPUT
  !
  ! pro%arg[1]  Project id
  ! pro%arg[2]  Date [DD-MMM-YYYY]
  ! pro%arg[3]  Working directory
  ! pro%arg[4]  Suffix for html files e.g. ".html"
  ! pro%arg[5]  YES|NO also add a link to 'osymlink'
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*256 wdir
  let wdir 'pro%arg[3]'
  define char*16 suffix
  let suffix 'pro%arg[4]'
  define logical dolink
  let dolink 'pro%arg[5]'
  !
  ! Create/update the include file
  sic output 'wdir'"/scanlist-"'pro%arg[1]'".js" new
  say "document.write('\"
  !
  define integer icurrent iprevious
  datetime /from pro%arg[2] DD-MMM-YYY /to icurrent GAG_DATE
  let iprevious icurrent-1
  define char*8 previous current
  datetime /from iprevious GAG_DATE /to previous YYYYMMDD
  datetime /from icurrent  GAG_DATE /to current  YYYYMMDD
  !
  ! Previous and current days
  define char*64 htmlfile
  define char*256 dhtmlfile
  define integer iscan
  !
  mfind /proj 'pro%arg[1]' /date 'previous' to 'current'
  mlist /toc observed scan
  define integer first
  let first max(1,mtoc%nsetup-9)  ! Only the last 10 setups
  !
  say "<i>links: \"
  let previous " "
  for iset first to mtoc%nsetup
    !
    datetime /from mtoc%setup[iset,1] DD-MMM-YYYY /to current YYYYMMDD
    if current.ne.previous then
      say 'current'" \"
      let previous 'current'
    endif
    !
    let iscan 'mtoc%setup[iset,2]'  ! Ugly trick because string has leading blanks
    let htmlfile "iram30m-plot-"'current'"s"'iscan''suffix'
    let dhtmlfile 'wdir'"/"'htmlfile'  ! With its path
    if file(dhtmlfile) then
      say "<a href="""'htmlfile'""">s"'iscan'"</a> \"
    else
      say "s"'iscan'" \"
    endif
    !
  next iset
  !
  if dolink then
    say "<a href="""'pipe%plot%osymlink'""">last</a> \"
  endif
  !
  say "</i>\"
  say "');"
  !
  sic output
  !
end procedure pipeline-plot-html-scanlist
!
begin procedure pipeline-plot-html-topmenu
  !---------------------------------------------------------------------
  ! Create the top-menu header with fixed position on the page. The html
  ! file must be already opened with SIC OUTPUT
  !   pro%arg[1]  Project id
  !   pro%arg[2]  date [YYYYMMDD]
  !   pro%arg[3]  scan number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Create the backend links list string
  define char*1024 linklist
  let linklist "<i>"
  if pipe%png%nfile.gt.0 then
    let linklist 'linklist'"Backends:"
    for ifile 1 to pipe%png%nfile
      let linklist 'linklist'" <a href='#plot"'ifile'"'>"'pipe%png%sparts[ifile]'"</a>"
    next ifile
  endif
  if pipe%png%pointing%nfile.gt.0 then
    let linklist "Receivers:"
    for ifile 1 to pipe%png%pointing%nfile
      let linklist 'linklist'" <a href='#plot"'ifile'"'>"'pipe%png%pointing%anchor[ifile]'"</a>"
    next ifile
  endif
  let linklist 'linklist'"</i><br>"
  !
  say "<head>"
  say "<style>"
  ! Top menu
  say "#TopMenu {"
  say "  display: block;"
  say "  position: fixed;"
  say "  top: 0px;"
  say "  left: 0px;"
  say "  width: 100%;"
  say "  height: 62px;"
  say "  text-align: left;"
  say "  padding-top: 10px;"
  say "  padding-left: 10px;"
  say "  background-color: #FFFFFF;"
  say "}"
  ! Anchors are anchored above the actual data, so that the data is not hidden
  ! by the top menu when clicking on the anchor.
  say "a.anchor {"
  say "    display: block;"
  say "    position: relative;"
  say "    top: -60px;"
  say "    visibility: hidden;"
  say "}"
  ! say ".anchor { padding-top: 90px; }"
  say "</style>"
  say "</head>"
  !
  say ""
  !
  say "<div id=""TopMenu"">"
  say "Mrtcal plots showing "'pro%arg[2]'" s"'pro%arg[3]'
  say "<script src=""scanlist-"'pro%arg[1]'".js""></script>"  ! Scan list
  say "<br>"
  say 'linklist'
  say "<small>(Comments/Questions/Bug reports: email to gildas@iram.fr)</small>"
  say "</div>"
  !
  say ""
  !
  ! Add a few extra lines on top so that the first image is not hidden
  ! by the top menu.
  say "<BR><BR><BR>"
  !
end procedure pipeline-plot-html-topmenu
!
begin procedure pipeline-plot-html-queue
  !---------------------------------------------------------------------
  ! Warn if queue is not equal to project
  !  pro%arg[1]  Queue
  !  pro%arg[2]  Project id
  !---------------------------------------------------------------------
  !
  if pro%arg[1].ne.pro%arg[2] then
    say "<P>"
    say "<table border=2 bordercolor=""red""><tr><td>"
    say "<font color=""red"">"
    say "Queue: "'pro%arg[1]'
    say "<BR>Project: "'pro%arg[2]'
    if pro%arg[1][1:4].ne."pool" then
      say "<BR>=> Non standard setup"
    endif
    say "</font>"
    say "</td></tr></table>"
  endif
  !
end procedure pipeline-plot-html-queue
!
begin procedure pipeline-plot-html-tracking
  !---------------------------------------------------------------------
  ! Show the tracking error plots for each backend
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! The same image is given with 2 paths. The one which is not found will not
  ! be displayed.
  say "<br>"
  ! 1) Image is located in the same directory, or
  say "<img src='"'pipe%png%track%file'"' alt=""""" -
      " onerror=""this.style.display='none'""/>"
  ! 2) Image is located in a subdirectory from symlink
  say "<img src='"'pipe%plot%osubdir'"/"'pipe%png%track%file'"' alt=""""" -
      " onerror=""this.style.display='none'""/>"
  say ""
  !
end procedure pipeline-plot-html-tracking
!
begin procedure pipeline-plot-html-restable
  !---------------------------------------------------------------------
  ! pro%arg[1]  date [YYYYMMDD]
  ! pro%arg[2]  scan number
  ! pro%arg[3]  observing type in CALIBRATE, POINTING, TRACKED,
  !             ONTHEFLYMAP, NONE
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*12 obstype
  let obstype 'pro%arg[3]'
  !
  if obstype.eq."CALIBRATE" then
    @ pipeline-plot-html-caltable 'pro%arg[1]' 'pro%arg[2]'
  else if obstype.eq."POINTING" then
    @ pipeline-plot-html-poitable 'pro%arg[1]' 'pro%arg[2]'
  endif
end procedure pipeline-plot-html-restable
!
begin procedure pipeline-plot-html-caltable
  !---------------------------------------------------------------------
  ! pro%arg[1]  date [YYYYMMDD]
  ! pro%arg[2]  scan number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*64 filter
  let filter "iram30m-calibration-*-"'pro%arg[1]'"s"'pro%arg[2]'".dat"
  !
  ! ZZZ Calibration tables are created in current directory: move to
  ! the project directory, together with the same tables for the other
  ! backend:
  sic find 'filter'
  for ifile 1 to dir%nfile
    sic rename 'dir%file[ifile]' 'pipe%plot%pdir'
  next ifile
  !
  ! Find all the calibration tables for this scan:
  sic find 'pipe%plot%pdir'"/"'filter'
  if dir%nfile.le.0  return
  !
  say "<pre>"
  !
  ! Header (duplicate of Fortran code)
  say "--------------------------------------------------------------------------------"
  say "idFe  freq  THotLoad TColdLoad idBe  Pix  recTemp sysTemp calTemp tauZenith pwv"
  say "      [GHz] ---- [Kelvin] ----            ------ [Kelvin] -------  [Neper]  [mm]"
  say "--------------------------------------------------------------------------------"
  !
  ! ZZZ This appends the table in a random way:
  define integer nline
  for ifile 1 to dir%nfile
    compute nline line 'dir%file[ifile]'
    if nline.gt.0 then
      define character*100 l[nline]
      accept l /column 'dir%file[ifile]' /format a
      for iline 1 to nline
        say 'l[iline]'
      next iline
      say "--------------------------------------------------------------------------------"
      delete /var l
    endif
  next ifile
  !
  say "</pre>"
  !
end procedure pipeline-plot-html-caltable
!
begin procedure pipeline-plot-html-poitable
  !---------------------------------------------------------------------
  ! pro%arg[1]  date [YYYYMMDD]
  ! pro%arg[2]  scan number
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  define char*64 filter
  let filter "iram30m-pointing-*-"'pro%arg[1]'"s"'pro%arg[2]'".dat"
  !
  ! ZZZ Pointing tables are created in current directory: move to
  ! the project directory, together with the same tables for the other
  ! backend:
  sic find 'filter'
  for ifile 1 to dir%nfile
    sic rename 'dir%file[ifile]' 'pipe%plot%pdir'
  next ifile
  !
  ! Find all the calibration tables for this scan:
  sic find 'pipe%plot%pdir'"/"'filter'
  if dir%nfile.le.0  return
  !
  say "<pre>"
  !
  ! Header (duplicate of Fortran code)
  say "---------------------------------------------------------------------------------------------------"
  say "idFe  freq  idBe  Angle    Position        FWHM             Area             Offset       Slope"
  say "      [GHz]       [deg]  --[arcsec]--  -[fraction]-  -[SNR]-[K.arcsec]-  ----[mK]----  [ mK/arcsec]"
  say "---------------------------------------------------------------------------------------------------"
  !
  ! ZZZ This appends the table in a random way:
  define integer nline
  for ifile 1 to dir%nfile
    compute nline line 'dir%file[ifile]'
    if nline.gt.0 then
      define character*128 l[nline]  ! Do not forget room for <font> tags
      accept l /column 'dir%file[ifile]' /format a
      for iline 1 to nline
        say 'l[iline]'
      next iline
      say "---------------------------------------------------------------------------------------------------"
      delete /var l
    endif
  next ifile
  !
  say "</pre>"
  !
end procedure pipeline-plot-html-poitable
!
begin procedure pipeline-plot-html-pointing
  !---------------------------------------------------------------------
  ! Show the pointing result plots
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  for ifile 1 to pipe%png%pointing%nfile
    ! The same image is given with 2 paths. The one which is not found will not
    ! be displayed.
    say "<p><a class=""anchor"" name='plot"'ifile'"'>plot"'ifile'"</a>"
    say "Showing: "'pipe%png%pointing%anchor[ifile]'
    say "<br>"
    ! 1) Image is located in the same directory, or
    say "<img src='"'pipe%png%pointing%file[ifile]'"' alt=""""" -
        " onerror=""this.style.display='none'""/>"
    ! 2) Image is located in a subdirectory from symlink
    say "<img src='"'pipe%plot%osubdir'"/"'pipe%png%pointing%file[ifile]'"' alt=""""" -
        " onerror=""this.style.display='none'""/>"
    say ""
  next ifile
  !
end procedure pipeline-plot-html-pointing
!
begin procedure pipeline-plot-html-backends
  !---------------------------------------------------------------------
  ! Create the HTML section displaying the calibrated data plots
  !  pro%arg[1] Obstype
  !  pro%arg[2] Suffixed (YES|NO)
  !---------------------------------------------------------------------
  !
  define char*12 obstype
  let obstype 'pro%arg[1]'
  define logical suffixed
  let suffixed 'pro%arg[2]'
  !
  ! Backend list
  define integer ns
  if suffixed then
    let ns 5  ! Suffix to ignore has 5 characters: "a.png"
  else
    let ns 4  ! Suffix to ignore has 4 characters: ".png"
  endif
  define char*256 file1 file2 dfile2
  define integer letter
  define logical hover
  for ifile 1 to pipe%png%nfile
    !
    say "<p><a class=""anchor"" name='plot"'ifile'"'>plot"'ifile'"</a>"
    say "Showing: "'pipe%png%lparts[ifile]'
    let file1 'pipe%png%file[ifile]'
    if suffixed then
      let letter len_trim(file1)-ns+1
      let file2 'file1'
      let file2[letter:letter] "b"  ! Switch "a" letter to "b"
      let dfile2 'pipe%plot%pdir'"/"'file2'
      let hover file(dfile2)
    else
      let hover no
    endif
    if hover then
      ! The same image is given with 2 paths. The one which is not found will not
      ! be displayed.
      if obstype.eq."TRACKED" then
        say "<small>(fly the mouse over the plot to swap polarizations)</small><BR>"
      else if obstype.eq."ONTHEFLYMAP" then
        say "<small>(fly the mouse over the plot to display the average spectrum)</small><BR>"
      endif
      say "<BR>"
      ! 1) Image is located in the same directory, or
      say "<img src='"'file1'"' alt=""""" -
          " onmouseover=""this.src='"'file2'"'""" -
          " onmouseout=""this.src='"'file1'"'""" -
          " onerror=""this.style.display='none'""/>"
      ! 2) Image is located in a subdirectory from symlink
      say "<img src='"'pipe%plot%osubdir'"/"'file1'"' alt=""""" -
          " onmouseover=""this.src='"'pipe%plot%osubdir'"/"'file2'"'""" -
          " onmouseout=""this.src='"'pipe%plot%osubdir'"/"'file1'"'""" -
          " onerror=""this.style.display='none'""/>"
    else
      ! The same image is given with 2 paths. The one which is not found will not
      ! be displayed.
      say "<br>"
      ! 1) Image is located in the same directory, or
      say "<img src='"'file1'"' alt=""""" -
          " onerror=""this.style.display='none'""/>"
      ! 2) Image is located in a subdirectory from symlink
      say "<img src='"'pipe%plot%osubdir'"/"'file1'"' alt=""""" -
          " onerror=""this.style.display='none'""/>"
    endif
    say ""
  next ifile
  !
end procedure pipeline-plot-html-backends
!
begin procedure pipeline-parse-parts
  !---------------------------------------------------------------------
  ! Build the list of unique backend parts from current index. In
  ! return, pipe%parts[] and pipe%nparts are filled.
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Get all values
  list /toc telescope
  !
  ! Fill the list of parts
  let pipe%nparts 0
  let pipe%parts 0
  define integer ipart
  for iset 1 to toc%nsetup
    if toc%tele[iset][10:11].eq."H0" then  ! HERA e.g. 30M-V01-1H01
      let ipart 'toc%tele[iset][7:7]'
    else                                   ! Non HERA e.g. 30ME0VUI-V01
      let ipart 'toc%tele[iset][12:12]'
    endif
    let pipe%parts[ipart] ipart
  next iset
  !
  ! Compress the list
  for i 1 to pipe%mparts
    if pipe%parts[i].gt.0 then
      let pipe%nparts pipe%nparts+1
      let pipe%parts[pipe%nparts] pipe%parts[i]
    endif
  next i
  !
  ! exa pipe%nparts pipe%parts
  !
end procedure pipeline-parse-parts
!
begin procedure pipeline-parse-setups
  !---------------------------------------------------------------------
  ! Build the list of telescope (frequency) setups, so that we are able
  ! later on to plot independently the 2 polarizations covering the same
  ! frequency setup. Quite tricky however. For example:
  !
  !  30ME1HLI-F01  =>  group A
  !  30ME1HUI-F02  =>  group B
  !  30ME1VLI-F03  =>  group A
  !  30ME1VUI-F04  =>  group B
  !  30ME1VUO-F05  =>  group C
  !  30ME1VLO-F06  =>  group D
  !  30ME1HUO-F07  =>  group C
  !  30ME1HLO-F08  =>  group D
  ! Quite obvious here, we just group the UI, UO, LI, LO. But in this
  ! case:
  !  30ME0VUI-V01  =>  group A
  !  30ME0HUI-V01  =>  group A
  !  30ME0HUI-V02  =>  group B
  !  30ME0VUI-V02  =>  group B
  !  30ME0VUI-V03  =>  group C
  !  30ME0HUI-V03  =>  group C
  !  30ME0VUI-V04  =>  group D
  !  30ME0HUI-V04  =>  group D
  ! The frequency setup are the same for 30ME0VUI-V01 and 30ME0HUI-V01,
  ! but NOT for 30ME0VUI-V01 and 30ME0VUI-V02! This is a VESPA feature.
  !
  ! The heuristic is constructed in 2 tries below:
  !  - Try #1: just group by bands. If this succeeds to make groups with
  !            more than one telescope per group, we stop (VESPA example
  !            above).
  ! - Try #2: groups by bands and backend parts otherwise (first example
  !           above)
  !---------------------------------------------------------------------
  !
  on error 'pipe%plot%error'
  !
  ! Get all TELESCOPE values. LINE values will help decide if we observe
  ! the same frequency range or not
  list /toc telescope line
  !
  define integer jtel jpol p1 p2 polpos partpos
  define logical ishera
  define char*1 polletter
  define integer d[8]
  !
  for itry 1 to 2
    let pipe%tel%n 0
    let pipe%tel%npolar 0
    let pipe%tel%name " "
    let pipe%tel%line " "
    let pipe%tel%color " "
    for iset 1 to toc%nsetup
      let ishera toc%setup[iset,1][10:11].eq."H0"
      if ishera then  ! HERA e.g. 30M-V01-1H01
        ! Search if telescope is already registered
        let jtel 0
        for itel 1 to pipe%tel%n
          if toc%setup[iset,2].eq.pipe%tel%line[itel] then
            ! Same LINE name (i.e. probably same frequency coverage)
            !
            ! Check on receiver? No, always "H0"
            ! Check on band? No, not available in telescope name
            ! Check on backend part? No, this messes up check on polarizations
            !
            ! => This one is a new telescope name for this frontend-backend-line combination
            let jtel itel
            break
          endif
        next itel
        !
        let polletter "1"  ! What is the H polarization letter in telescope name
        let polpos    9  ! Where is the polarization letter in telescope name
        let partpos   7  ! Where is the part letter in telescope name
        !
      else                                   ! Non HERA e.g. 30ME0VUI-V01
        ! Search if telescope is already registered
        let jtel 0
        for itel 1 to pipe%tel%n
          if toc%setup[iset,2].eq.pipe%tel%line[itel] then
            ! Same LINE name (i.e. probably same frequency coverage)
            if toc%setup[iset,1][4:5].eq.pipe%tel%name[1,itel][4:5] then
              ! Same receiver
              if toc%setup[iset,1][7:8].eq.pipe%tel%name[1,itel][7:8] then
                ! Same band e.g. UI
                if itry.eq.2.or.toc%setup[iset,1][12:12].eq.pipe%tel%name[1,itel][12:12] then
                  ! Same backend part (try #2 only)
                  ! => This one is a new telescope name for this combination
                  let jtel itel
                  break
                endif
              endif
            endif
          endif
        next itel
        !
        let polletter "H" ! What is the H polarization letter in telescope name
        let polpos    6   ! Where is the polarization letter in telescope name
        let partpos   12  ! Where is the part letter in telescope name
      endif  ! HERA / not HERA
      !
      ! Register
      if jtel.eq.0 then  ! New combination
        let pipe%tel%n pipe%tel%n+1
        let jtel pipe%tel%n
        let pipe%tel%npolar[jtel] 1
        let jpol 1
      else  ! New occurence of an old combination
        let jpol 0
        for ipol 1 to pipe%tel%npolar[jtel]
          if ishera.and.pipe%tel%name[ipol,jtel][polpos:polpos].eq.toc%setup[iset,1][polpos:polpos] then
            ! We already registered this polar as number 'ipol' (this happens when
            ! iterating HERA pixels)
            let jpol ipol
            ! pipe%tel%npolar[jtel] unchanged
          endif
        next ipol
        if jpol.eq.0 then
          ! This is another polar!
          let pipe%tel%npolar[jtel] pipe%tel%npolar[jtel]+1
          let jpol pipe%tel%npolar[jtel]
        endif
      endif
      !
      ! Save telescope and line names
      if ishera then
        let pipe%tel%name[jpol,jtel]  'toc%setup[iset,1][1:11]'"*"  ! Drop the pixel number
      else
        let pipe%tel%name[jpol,jtel]  'toc%setup[iset,1]'  ! Full name
      endif
      let pipe%tel%line[jtel] 'toc%setup[iset,2]'
      !
      ! Which color for which polarization?
      if pipe%tel%name[jpol,jtel][polpos:polpos].eq.polletter then
        let pipe%tel%color[jpol,jtel]  "blue"
      else
        let pipe%tel%color[jpol,jtel]  "red"
      endif
      !
      ! Build the 'part' name (typically "1" or "1+2"... or more if backend is
      ! misconfigured!)
      let p1 'pipe%tel%name[1,jtel][partpos:partpos]'
      if jpol.eq.1 then
        ! Only one part in use
        let pipe%tel%part[jtel] 'p1'
      else
        ! Two (or more...) parts in use
        define integer p[jpol]
        for ipol 1 to jpol
          let p[ipol] 'pipe%tel%name[ipol,jtel][partpos:partpos]'
        next ipol
        compute q gather p  ! Remove duplicates
        sic\sort q
        let pipe%tel%part[jtel] 'q[1]'
        compute d dimof q
        for ipol 2 to d[1]
          let pipe%tel%part[jtel] 'pipe%tel%part[jtel]'"+"'q[ipol]'
        next ipol
        delete /var p q
      endif
      !
    next iset
    !
    if pipe%tel%n.lt.toc%nsetup  break
    !
  next itry
  !
!   say "N: " 'pipe%tel%n'
!   for itel 1 to pipe%tel%n
!     say "Npolar: " 'pipe%tel%npolar[itel]' 'pipe%tel%part[itel]'
!     for ipol 1 to pipe%tel%npolar[itel]
!       say 'pipe%tel%name[ipol,itel]' 'pipe%tel%color[ipol,itel]'
!     next ipol
!   next itel
!   pause
  !
end procedure pipeline-parse-setups
