# version for cryotel at boa (it is to be verified if FOCUS cryotel is different!)

namespace eval cryotelboa {} {
}

proc stdConfig::cryotelboa {} {  
#  controler uses std with send terminator "\r"
#  5sec comm.interval, receive terminator "\n"
#  and multi line replies will be separated by ","
  controller std "\r" 5 "\n" ","
# the {2} in addition to the command defines the number of expected return values
  prop startcmd "SERIAL{2}"

# driver for Sunpower stirling cooler CryoTel  
# serial interface is set to:
# Char Size/Stop Bits:         8/1    Input Speed:                4800
# Flow Ctrl:                  None    Output Speed:               4800
# Parity:                     None    Modem Control:               None


  obj cryo rd 0.5
  prop label Temperature
  prop update cryotelboa::updatemean
  prop readcmd "TC{2}"  
  prop readfmt "TC ,%f"

  kids cryotel {

    node cool wr
    prop label Cool
    prop enum 1
    prop read cryotelboa::readcool
    prop write cryotelboa::writecool

    node control wr
    default 1
    prop check cryotelboa::checkcontrol
    prop enum auto_power=1,manual_power=0,controlled_T=2
    prop help "recommended mode: auto_power, use coolpower or holdpower depending on T"
    prop update cryotelboa::updatecontrol
    prop writecmd "SET PID=%.2f"
    prop readcmd "SET PID{2}"
    prop readfmt "SET PID ,%f"

    node set wr
    prop label Setpoint
    prop check cryotelboa::checkset
    prop writecmd "SET TTARGET=%.2f"
    prop readcmd "SET TTARGET{2}"
    prop readfmt "SET TTARGET ,%f"

    node setpower wr
    prop check cryotelboa::checksetpower
    prop writecmd "SET PWOUT=%.2f"
    prop readcmd "SET PWOUT{2}"
    prop readfmt "SET PWOUT ,%f"

    node coolpower par 240

    node holdpower par 120   

    node cool_threshold par 100
    prop help "switch to coolpower above this value"

    node hold_threshold par 95
    prop help "switch to holdpower below this value"

    node power rd
    prop label Power
    prop readcmd "P{2}"
    prop readfmt "P ,%f"

    node filter -none
    
    kids filter {
      # on jittery system:
      # period was 180, amplitute 3, precision 0.1
      node period par 10
      prop help "oszillation period / sec"

      node amplitude par 0.1
      prop help "oszillation amplitude / K (+/-)"

      node precision par 0.01
      
      node raw upd
    
      node intdif upd
    }
  }
}

proc cryotelboa::updatemean {} {
  set value [stdSct::scanresult]
  set cnt [silent -1 sct cnt]
  if {$cnt < 0} {
    sct update $value
    updateval [sct]/filter/raw $value
    sct cnt 0
    return idle
  }
  incr cnt
  sct sum [expr [silent 0 sct sum] + $value]
  if {$cnt < 10} {
    sct cnt $cnt
  } else {
    set raw [expr [sct sum] / $cnt]
    set filtered [hvali [sct]]
    sct cnt 0
    sct sum 0
    updateval [sct]/filter/raw $raw

    # intdif is accumulating the difference raw - filtered
    # filtered is corrected by +precision or -precision if abs(intdif) is over lim
    set per [hval [sct]/filter/period]
    set lim [expr 0.5 * [hval [sct]/filter/amplitude]]
    set prec [hval [sct]/filter/precision]
    # 20 = 4 * pollperiod * cnt
    set damp [expr exp(-20 * $prec / $lim / $per)]
    # 10 = cnt * pollperiod * 2
    set intdif [expr ([hvali [sct]/filter/intdif] + ($raw - $filtered) * 10 / $per) * $damp]
    if {$intdif > $lim} {
      set filtered [expr (round(($filtered + $intdif - $lim)/ $prec + 1)) * $prec]
      set intdif [expr $lim - $prec * 2]
    } elseif {$intdif < -$lim} {
      set filtered [expr (round(($filtered + $intdif + $lim)/ $prec - 1)) * $prec]
      set intdif [expr -$lim + $prec * 2]
    }
    updateval [sct]/filter/intdif $intdif
    sct update [format %.5g $filtered]
    if {[hval [sct]/control] == 1} {
      if {$filtered > [hval [sct]/cool_threshold] - 0.01} {
        hset [sct]/setpower [hval [sct]/coolpower]
      } elseif {$filtered < [hval [sct]/hold_threshold] + 0.01} {
        hset [sct]/setpower [hval [sct]/holdpower]
      }
    }
  }
  return idle
}

proc cryotelboa::checkset {} {
  if {[hvali [sct parent]/control] == 0} {
    sct print "setpoint is effective only in controlled_T mode"
  }
}

proc cryotelboa::checksetpower {} {
  if {[hvali [sct parent]/control] == 2} {
    sct print "setpower is effective only in manual_power mode"
  }
  if {[sct target] == [hvali [sct]]} {
    sct write stdSct::complete
  } else {
    sct write stdSct::write
  }
}

proc cryotelboa::visibility {mode} {
  set showlist {
    0 setpower
    1 {coolpower holdpower cool_threshold hold_threshold}
    2 set
  }
  foreach {m vars} $showlist {
    if {$m == $mode} {
      set visible true
    } else {
      set visible false
    }
    foreach var $vars {
      hsetprop [sct parent]/$var visible $visible
    }
  }
}

proc cryotelboa::updatecontrol {} {
  set value [stdSct::scanresult]
  if {$value == 0 && [sctval [sct]] == 1} {
    set value 1
  }
  sct update $value
  visibility $value
  return idle
}

proc cryotelboa::checkcontrol {} {
  visibility [sct target]
  if {[sct target] == 1} {
    sct write stdSct::completeUpdate
  } else {
    sct write stdSct::write
  }
}

proc cryotelboa::writecool {} {
  set flag [expr ! [sct target]]
  sct send "SET SSTOP=$flag"
  sct update [sct target]
  return stdSct::complete
}

proc cryotelboa::readcool {} {
  sct send "SET SSTOP{2}"
  return cryotelboa::updatecool
}

proc cryotelboa::updatecool {} {
  lassign [split [sct result] ,] _ value
  sct update [expr $value == 0]
  return idle
}

