namespace eval ccu { variable bits variable pcnt 0 # bits to be kept / bits to be set array set bits { pulsOn {0xfe 1} pulsOff {0xfe 0} n2On {0xfc 1} n2Off {0xfc 2} heOn {0xfa 1} heOff {0xfa 4} nvOpen {0xf6 9} nvClose {0xf6 1} nvOff {0x0e 0xc0} weak {0xcf 0x20} normal {0xcf 0x10} strong {0xcf 0x30} micro {0x3f 0xc0} short {0x3f 0x80} long {0x3f 0x00} } } # general ccu initialization proc stdConfig::ccuInit {} { variable node variable ctrl variable name if {[controller std "0x0d" 2]} { controllerDesc "CCU cryostat control unit" prop resolution 26.214 prop dout 0x06 prop readA0 0 prop readA1 0 prop readA2 0 prop readA3 0 prop readA4 0 prop readA5 0 prop readA6 0 prop readA7 0 prop starttime 0 prop n2off ccu::valveoff n2 prop n2on ccu::valveon n2 prop heoff ccu::valveoff he prop heon ccu::valveon he prop beforefixed 0 set node /sics/$ctrl/tasks poll 1 prop start ccu::start prop read ccu::handler } set node /sics/$ctrl } # automatic n2 fill with two pt sensors proc stdConfig::n2_ccu {{title ""} {type n2cryo}} { variable name ccuInit # A0: upper sensor, A1: lower sensor prop readA0 1 prop readA1 1 prop n2path /$name if {$type eq "trap"} { prop warn_trap_empty 1 set vis false } else { prop warn_no_auto 1 set vis true } obj n2ccu out -int default -1 prop write ccu::write prop enum watching=0,filling=1,inactive=4 prop visible $vis prop label "filling state" if {$title eq ""} { set title "LN2 fill settings" } kids $title { node threshold par 90 prop help {[K] a sensor below this value is immersed} prop visible $vis node limit par 240 prop label "warm limit" prop help {[K] above this value the cryo is considered as warm} prop visible $vis node tmo1 par 300 prop label "max. tube cooling time" prop help {[sec]} prop visible $vis node tmo2 par 1800 prop label "max. fill time" prop help {[sec]} prop visible $vis node upper upd node lower upd } return "LN2 fill with CCU" } # automatic fill with level reading from external source proc stdConfig::ccuExtInit {type {readlevel 0}} { variable node variable ctrl ccuInit obj fillccu out -int default -1 prop write ccu::writeExt $type prop enum watching=0,filling=1,inactive=2 prop label "filling state" if {$type eq "n2"} { set text LN2 } else { set text LHe } kids "$text fill settings" { node readlevel -text par $readlevel prop visible false node lowlevel par 10 node highlevel par 100 node smooth upd prop fmt %.7g default -1 node minfillminutes par 3.0 node maxfillminutes par 30.0 node minholdhours par 12.0 node maxholdhours par 120.0 node tolerance par 20.0 node badreadingminutes par 30.0 node tubecoolingminutes par 3.0 if {$type eq "he"} { node vessellimit par 10.0 node vext upd } } set node /sics/$ctrl } proc stdConfig::n2_ext_ccu {} { variable name ccuExtInit n2 "lev n2" prop n2extpath /$name prop n2fill "ccu::fillExt n2" return "LN2 fill with CCU and external level meter" } proc stdConfig::he_ext_ccu {{readlevel lev}} { variable name ccuExtInit he $readlevel prop heextpath /$name prop hefill "ccu::fillExt he" return "LHe fill with CCU and external level meter" } proc stdConfig::he_ccu {} { variable name ccuInit # A3: he sensor prop readA3 1 prop hepath /$name # voltage divider 56.2 kOhm + 200 kOhm, max. 10 V # full is ~ 2.2 V prop full [expr 56.2 / 256.2 * 10] prop empty 0 obj heccu upd return "He reading from AMI135/136 with CCU" } proc stdConfig::he9_ccu {} { variable name ccuInit # A6: he sensor (potentiometer channel) prop readA6 2 prop hepath /$name # on this level meter, full is 0, empty is 0.5 V prop full 0 prop empty 0.5 obj heccu upd return "He reading from old MA09 with CCU" } proc stdConfig::n2cool_ccu {} { variable name variable node variable ctrl ccuInit obj fillccu out -int default -1 prop write ccu::writePrecool prop enum watching=0,filling=1,inactive=2 prop label "precool filling state" return "LN2 precool with CCU" } proc stdConfig::nv_ccu {{vtype VTI}} { variable name ccuInit # A2: pressure sensor prop readA2 1 prop nvpath /$name obj nvccu out -int default 0 prop control ccu::control prop write ccu::writeNv if {$vtype != "VTI"} { # lambda point refrigerator prop enum "off=5,fixed=0,controlling=1,close=3,open=4" prop vtype $vtype } else { prop enum "automatic=2,fixed=0,controlling=1,close=3,open=4" prop vtype $vtype } poll 0.5 progress ticker prop ticker ccu::ticker kids "$vtype needle valve settings" { node set out default 2 prop write ccu::writeSet prop label "flow set" node flowmax par 20 prop label "flow maximum" node prop par 200 node int par 10 node minpulse par 100 prop label "minimum pulse length:" prop enum "micro=1,short=10,long=100" prop help "micro: 1 ms, short: 10 ms, long: 100 ms" node tolerance par 0.05 node speed upd node flow upd node pos upd default 20 kids "position estimation parameters" { node norm par 20 node max par 60 } node autoflow -none kids "autoflow control parameters" { flow::make } } return "$vtype needle valve control with CCU" } proc stdConfig::nvread_ccu {} { variable name ccuInit # A2: pressure sensor prop readA2 1 prop nvpath /$name prop readonly 1 obj nvccu rd -int prop read ccu::flowtask prop enum "readonly=0" kids "pressure reading" { node flow upd } } proc ccu::flowtask {} { sct update 0 namespace eval :: { flowtask } return idle } proc ccu::start {} { # set all digital ports to output push-pull and all outputs inactive # remark: bit 1 and 2 are inactive High (hex 06) sct send ":10000000030600FF00FF0006.." return ccu::setADCrate } proc ccu::setADCrate {} { # set adc rate to 8 sct send ":10000D0001020008.." return ccu::getidn } proc ccu::getidn {} { sct send ":0300030001.." return stdSct::completeStart } proc ccu::handler {} { variable last_nv 0 variable last_n2 0 set now [clock seconds] if {$now/5 != $last_n2/5} { set last_n2 $now # read all used channels set chanlist {0 1 2 3 4 5 6 7} } elseif {[sct readA2]} { set last_nv $now # read only used channels for needle valve control set chanlist {2 6 7} } set min 8 set max -1 foreach chan $chanlist { if {[sct readA$chan]} { if {$chan > $max} { set max $chan } if {$chan < $min} { set min $chan } } } if {[silent 0 sct hefill] ne 0} { [sct controllerName] queue [sct heextpath] read hefill } if {[silent 0 sct n2fill] ne 0} { [sct controllerName] queue [sct n2extpath] read n2fill } if {$min > $max} { return idle } set n [expr $max - $min + 1] sct firstChan $min sct lastChan $max sct nChan $n sct send [format ":04%4.4x%4.4x.." $min $n] return ccu::update } proc ccu::smooth {var band old} { upvar $var val #clientput "$val $band $old" set weight [expr 0.05 + pow(($val - $old) / $band, 2)] #clientput "w $weight" if {$weight < 1.0} { set val [expr $old + ($val - $old) * $weight] } #clientput "$weight $val" } proc ccu::convertPt2T {sensor raw} { set millivolt [expr $raw / [sct resolution]] if {$millivolt < 30} { hupdate [sct n2path]/status "LN2 sensor unplugged" hsetprop [sct n2path]/$sensor geterror "LN2 sensor unplugged" return 0 } if {$millivolt < 150 || $millivolt > 1500} { hupdate [sct n2path]/status "illegal LN2 sensor reading: $millivolt mV" hsetprop [sct n2path]/$sensor geterror "illegal LN2 sensor reading: $millivolt mV" return 0 } set t [expr 68 * (5000.0 / $millivolt - 1.0) * 0.25 + 26.8] catch { ccu::smooth t 5.0 [hvali [sct n2path]/$sensor] } hupdate [sct n2path]/$sensor $t hdelprop [sct n2path]/$sensor geterror return $t } proc ccu::nvstatus {} { set status "" set vtype [hgetpropval [sct nvpath] vtype] if {[silent -63 hvali [sct nvpath]/flow] < -50} { append status "$vtype needle valve pressure sensor not connected\n" } elseif {[silent -1 hvali [sct nvpath]] == -1} { append status "$vtype needle valve fixed\n" } set pos [silent 20 hvali [sct nvpath]/pos] if {$pos >= [hvali [sct nvpath]/pos/max]} { append status "$vtype needle valve probably open\n" } if {$pos <= 0} { append status "$vtype needle valve probably closed\n" } hupdate [sct nvpath]/status $status } proc ccu::update {} { set fmt [format ":04%2.2X%s" [expr 2 * [sct nChan]] [string repeat %4x [sct nChan]]] set vars [lrange {a0 a1 a2 a3 a4 a5 a6 a7} [sct firstChan] [sct lastChan]] if {[eval scan [sct result] $fmt $vars] != [sct nChan]} { error "bad response to '[sct send]': '[sct result]'" } if {[sct readA2]} { # reading 0..65535, voltage 0..2500 mV with a 100 Ohm resistor: 0..25 mA # 4..20 mA is 0..250 mbar set flow [expr ($a2 / 65535.0 * 25 - 4) / 16.0 * 250] catch { ccu::smooth flow 0.5 [silent 0 hvali [sct nvpath]/flow] } hupdate [sct nvpath]/flow $flow if {$flow < -50} { hsetprop [sct nvpath]/flow geterror "pressure sensor not connected" switch [silent 0 hvali [sct nvpath]] { 1 - 2 { hset [sct nvpath] 0 hsetprop [sct nvpath]/flow geterror "pressure sensor not connected" } } } else { hdelprop [sct nvpath]/flow geterror } if {[silent 0 sct readonly] == 0} { ccu::nvstatus [sct controllerName] queue [sct nvpath] read control } } if {[sct readA6] == 2} { # He reading on chan 6 (for MA09) # reading 65535 is 2.5 V, output is in percent set val [expr 100.*(2.5*$a6/65535.-[sct empty])/([sct full]-[sct empty])] hupdate [sct hepath] $val } elseif {[sct readA3]} { # He reading on chan 3 (for AMI 135/136 level meter) # reading 65535 is 2.5 V, output is in percent set val [expr 100.*(2.5*$a3/65535.-[sct empty])/([sct full]-[sct empty])] if {$val < -1.0} { set val -1.0 } elseif {$val > 101.0} { set val 101.0 } hupdate [sct hepath] $val } if {[sct firstChan] == 0} { # automatic n2 fill with 2 point sensor set upper [convertPt2T upper $a0] set lower [convertPt2T lower $a1] set lim [hvali [sct n2path]/limit] set thr [hvali [sct n2path]/threshold] set state [hvali [sct n2path]] if {$upper > $lim || $lower > $lim} { if {$state < 3} { hupdate [sct n2path]/status "sensor warm" hupdate [sct n2path] 4 hsetprop [sct n2path] enum watching=0,fill=3,inactive (sensor warm)=4 return n2off } elseif {$state == 4} { hupdate [sct n2path]/status "sensor warm" hupdate [sct n2path] $state return idle } } elseif {[string equal "sensor warm" [result hvali [sct n2path]/status]]} { hupdate [sct n2path]/status "" } if {$upper < $thr && $lower > $thr} { if {$state < 4} { hupdate [sct n2path]/status "sensor upside down" hupdate [sct n2path] 4 hsetprop [sct n2path] enum watching=0,fill=3,inactive (sensor upside down)=4 return n2off } else { hupdate [sct n2path] $state return idle } } elseif {[string equal "sensor upside down" [result hvali [sct n2path]/status]]} { hupdate [sct n2path]/status "" } if {[silent 0 sct warn_trap_empty]} { set now [clock seconds] if {$lower > $thr} { if {[silent 0 sct empty_time] == 0} { sct empty_time $now } if {$now > [sct empty_time] + 600} { set hr [expr 20 - ($now - [sct empty_time]) / 3600] if {$hr <= 0} { set txt "the trap gets empty soon - please fill immediately" } else { set txt "the trap gets empty soon - please fill within $hr h" } } else { set txt "" } } else { set txt "" sct empty_time 0 } if {$txt ne [silent 0 hvali [sct n2path]/status]} { hupdate [sct n2path]/status $txt } } switch -- $state { -1 { # initializing hupdate [sct n2path] 4 hsetprop [sct n2path] enum watching=0,fill=1,inactive=4 if {[silent 0 sct warn_no_auto]} { hupdate [sct n2path]/status "automatic LN2 filling off" } return n2off } 0 { # watching if {$lower > $thr} { clientput "start N2 fill" hupdate [sct n2path] 1 hsetprop [sct n2path] enum watching=0,fill (starting)=1,inactive=4 sct starttime [clock seconds] return n2on } } 1 { # filling until lower or upper is cold if {[clock seconds] > [sct starttime] + [hvali [sct n2path]/tmo1]} { if {$lower < $thr} { hupdate [sct n2path] 2 hsetprop [sct n2path] enum watching=0,fill=2,inactive=4 return n2on } clientput "stop N2 fill (lower sensor not cold quick enough)" hupdate [sct n2path]/status "lower sensor not cold quick enough" hupdate [sct n2path] 4 hsetprop [sct n2path] enum watching=0,fill=3,inactive (lower sensor not cold quick enough)=4 return n2off } else { if {$upper < $thr} { clientput "finished N2 fill" hupdate [sct n2path] 0 hsetprop [sct n2path] enum watching=0,fill=1,inactive=4 return n2off } } sct update $state return n2on } 2 - 3 { # filling until upper is cold if {[clock seconds] > [sct starttime] + [hvali [sct n2path]/tmo2]} { clientput "stop N2 fill (fill timeout)" hupdate [sct n2path]/status "fill timeout" hupdate [sct n2path] 4 hsetprop [sct n2path] enum watching=0,filling=2,inactive (fill timeout)=4 return n2off } elseif {$upper < $thr} { clientput "finished N2 fill" hupdate [sct n2path] 0 hsetprop [sct n2path] enum watching=0,fill=1,inactive=4 return n2off } hupdate [sct n2path] $state return n2on } } hupdate [sct n2path] $state } return idle } proc ccu::output args { variable bits variable pcnt set dout [sct dout] foreach a $args { set b $bits($a) set dout [expr $dout & [lindex $b 0] | [lindex $b 1]] } sct dout $dout sct send [format ":10000200010200%2.2x.." $dout] if {[silent 0 result pulsedebug]} { if {[string equal pulsOn $args]} { incr pcnt } elseif {[string equal pulsOff $args]} { } else { clientput "$args ($pcnt)" set pcnt 0 } clientput "[format "%2.2x" $dout]" } } proc ccu::valveoff {type} { ccu::output ${type}Off return stdSct::complete } proc ccu::valveon {type} { ccu::output ${type}On return ccu::pulse } proc ccu::pulse {} { ccu::output pulsOff return stdSct::complete } proc ccu::write {} { hupdate [sct n2path]/status "" switch -- [sct target] { # watching 0 { sct update [sct target] sct enum watching=0,fill=3,inactive=4 return n2off } # inactive 4 { sct update 4 sct enum watching=0,fill=3,inactive=4 hupdate [sct]/status "automatic LN2 filling off" return n2off } 1 - 2 - 3 { sct starttime [clock seconds] if {[hvali [sct]] < 3} { sct update 1 sct enum watching=0,fill=1,inactive=4 } else { sct update 3 sct enum watching=0,fill=3,inactive=4 } return idle } } return idle } # --- automatic fill with level reading from external source --- proc calcsmooth {level minspeed maxspeed} { if {![string is double $level]} { return 999.9 } set now [clock seconds] set delta [expr $now - [silent $now sct lasttime]] sct lasttime $now # use property for better resolution set smooth [hvali [sct]/smooth] if {$smooth < 0} { set smooth $level } set gs [expr $smooth + $delta * $maxspeed] if {$level > $gs} { set smooth $gs } else { set gs [expr $smooth + $delta * $minspeed] if {$level < $gs} { set smooth $gs } else { set smooth $level } } set badtime [silent 0 sct badtime] if {abs($smooth - $level) > [hvali [sct]/tolerance]} { if {$badtime < [hvali [sct]/badreadingminutes] * 60 * 2} { sct badtime [expr $badtime + $delta] } } else { if {$badtime > $delta} { sct badtime [expr $badtime - $delta] } else { sct badtime 0 } } hupdate [sct]/smooth $smooth return $smooth } proc ccu::fillExt {type} { set level [silent invalid result [hvali [sct]/readlevel]] if {! [string is double $level]} { hupdate [sct ${type}extpath]/status "illegal $type level reading" return idle } set state [hvali [sct]] set now [clock seconds] set lowlevel [hvali [sct]/lowlevel] if {$lowlevel < 0.0} { hupdate [sct]/lowlevel 0.0 } if {$lowlevel > 90.0} { hupdate [sct]/lowlevel 90.0 } set highlevel [hvali [sct]/highlevel] if {$highlevel > 100.0} { hupdate [sct]/highlevel 100.0 } if {$highlevel < $lowlevel + 10.0} { hupdate [sct]/highlevel [expr $lowlevel + 10.0] } switch -- $state { -1 { # initializing hupdate [sct]/status "automatic $type filling off" hset [sct] 2 sct lasttime 0 return ${type}off } 0 { # watching set minspeed [expr - 100.0 / [hvali [sct]/minholdhours] / 3600.] set maxspeed [expr - 100.0 / [hvali [sct]/maxholdhours] / 3600.] set s [calcsmooth $level $minspeed $maxspeed] if {$s < $lowlevel} { if {[sct badtime] > [hvali [sct]/badreadingminutes] * 60} { hupdate [sct]/status "automatic $type filling off - continuos bad reading" hset [sct] 2 return ${type}off } # start fill hset [sct] 1 clientput "$type level low - start fill" } return ${type}off } 1 { set vcmd [silent 0 sct vessel_cmd] set now [clock seconds] set s [hvali [sct]/smooth] if {$s < [sct minlevel]} { sct minlevel $s } set vessel 999 set maxspeed [expr 100.0 / [hvali [sct]/minfillminutes] / 60.] if {[sct tubecooling] && $now < [sct startfill] + [hvali [sct]/tubecoolingminutes] * 60 && $s < [sct minlevel] + 5} { # allow fill tube to get cold set minspeed [expr - 100.0 / [hvali [sct]/maxfillminutes] / 60.] } else { if {[sct tubecooling]} { sct tubecooling 0 if {$vcmd ne "0"} { sct vstart_level [expr [result $vcmd]] # start time - 2 minutes sct vstart_time [expr $now - 120] sct vlast_time $now hupdate [sct]/vext [sct vstart_level] } } set minspeed [expr + 100.0 / [hvali [sct]/maxfillminutes] / 60.] if {$vcmd ne "0"} { set dt [expr $now - [sct vlast_time]] if {$dt > 0} { set slope [expr ([hvali [sct]/vext] - [sct vstart_level])/([sct vlast_time] - [sct vstart_time])] } else { set slope 0 } set vessel [result $vcmd] set vext [expr [hvali [sct]/vext] + $slope * $dt] if {$vext < $vessel} { if {$vext < $vessel - 2.0} { set errcnt [silent 0 sct errcnt] incr errcnt if {$errcnt > 3} { hupdate [sct]/status "not enough progress on $type vessel - automatic ${type} filling off ($vext < $vessel - 2)" hset [sct] 2 return ${type}off } sct errcnt $errcnt } else { set vessel $vext } } hupdate [sct]/vext $vessel sct vlast_time $now } } if {$vessel < [silent 0 hvali [sct]/vessellimit]} { hupdate [sct]/status "$type vessel empty - automatic ${type} filling off" hset [sct] 2 return ${type}off } if {[calcsmooth $level $minspeed $maxspeed] <= $highlevel} { # continue filling return ${type}on } clientput "$type level high - stop fill" hset [sct] 0 return ${type}off } 2 { set minspeed [expr - 100.0 / [hvali [sct]/minholdhours] / 3600.] set maxspeed [expr + 100.0 / [hvali [sct]/minfillminutes] / 60.] calcsmooth $level $minspeed $maxspeed } } return idle } proc ccu::writeExt {type} { sct update [sct target] hupdate [sct]/smooth [silent 0 result [hvali [sct]/readlevel]] switch -- [sct target] { # watching 0 { catch {logsetup [sct]/vext clear} hupdate [sct]/status "" eval [silent "expr 0" sct slow_cmd] return ${type}off } # inactive 2 { catch {logsetup [sct]/vext clear} eval [silent "expr 0" sct slow_cmd] if {[hvali [sct]/status] eq ""} { hupdate [sct]/status "automatic ${type} filling off" } clientput "ERROR: [hvali [sct]/status]" return ${type}off } # fill 1 { hupdate [sct]/status "" set vcmd [silent 0 sct vessel_cmd] if {$vcmd ne "0"} { set vlev [result $vcmd] if {$vlev == 60} { hupdate [sct]/status "vessel level meter not connected - automatic ${type} filling off" clientput [hvali [sct]/status] sct update 2 return ${type}off } if {$vlev < [hvali [sct]/vessellimit] + 3.0} { hupdate [sct]/status "vessel not full enough - automatic ${type} filling off" clientput [hvali [sct]/status] sct update 2 return ${type}off } } sct minlevel 100 sct errcnt 0 sct startfill [clock seconds] sct tubecooling 1 eval [silent "expr 0" sct fast_cmd] return ${type}on } } return idle } proc ccu::writePrecool {} { hupdate [sct]/status "" sct update [sct target] switch -- [sct target] { # watching 0 { return n2off } # inactive 2 { hupdate [sct]/status "LN2 precooling off" return n2off } 1 { return n2on } } return idle } proc ccu::writeNv {} { if {[sct target] != 5} { eval [silent expr sct switchgraph] 1 } hupdate [sct] [sct target] switch -- [sct target] { 5 { # off eval [silent expr sct switchgraph] 0 hupdate [sct]/speed 0 ccu::output nvOff return stdSct::complete } 0 { # fixed hupdate [sct]/speed 0 ccu::output nvOff return stdSct::complete } 1 - 2 { # controlled or automatic: sct beforefixed [sct target] ccu::output nvOff return stdSct::complete } 3 { # close if {[hvali [sct]/pos] < [hvali [sct]/pos/norm]} { hupdate [sct]/pos [hvali [sct]/pos/norm] } } 4 { # open if {[hvali [sct]/pos] > [hvali [sct]/pos/norm]} { hupdate [sct]/pos [hvali [sct]/pos/norm] } } } return idle } proc ccu::writeSet {} { switch -- [hvali [sct objectPath]] { 0 - 3 - 4 - 5 { hset [sct objectPath] [sct beforefixed] } } sct update [sct target] return idle } proc ccu::adjustpos {delta} { set pos [hvali [sct]/pos] set normpos [hvali [sct]/pos/norm] if {$pos < $normpos} { if {$delta < 0} { if {$pos <= 0} { set pos 0 } else { set pos [expr $pos + $delta * 0.2] } } else { set pos [expr $pos + $delta] } } else { if {$delta > 0} { set pos [expr $pos + $delta * 0.5] } else { set pos [expr $pos + $delta] } } if {$pos > [hvali [sct]/pos/max]} { set pos [hvali [sct]/pos/max] } hupdate [sct]/pos $pos } proc ccu::trans args { # transform all variables listed in the args foreach var $args { upvar $var val if {$val > 4.0} { set val [expr 3.3 + ($val - 4.0) * 0.1] } elseif {$val > 3.5} { set val [expr 3.2 + ($val - 3.5) * 0.2] } elseif {$val > 3.0} { set val [expr 3 + ($val - 3) * 0.4] } } } # polling script for nv control proc ccu::control {} { switch -- [hvali [sct]] { 0 - -1 - 5 { # fixed or off if {[sct beforefixed] == 0} { sct beforefixed 2 } logsetup [sct]/set clear set speed 0 return idle } 3 { # closing set soll -1 set speed -1000 } 4 { # opening set soll -1 set speed 1000 } 1 { # controlled set soll [hvali [sct]/set] # the following line is for graphic (using flowtarget as nv_set curve) catch {hupdate [sct]/autoflow/flowtarget $soll} } 2 { # automatic flow::task /nv/autoflow [hvali [sct]/set] [hvali [sct]/flowmax] set soll [hvali [sct]/autoflow/flowset] } default { return idle } } if {$soll >= 0} { # case 1 and 2: pid control set ist [hvali [sct]/flow] set tol [hvali [sct]/tolerance] if {$soll > $ist + $tol} { set soll [expr $soll - $tol] } elseif {$soll < $ist - $tol} { set soll [expr $soll + $tol] } else { set soll $ist } # transfer function taking into account heavy nonlinearities of needle valve ccu::trans ist soll set prop [hvali [sct]/prop] set int [hvali [sct]/int] set diff [expr $soll - $ist] set lastdiff [silent $diff sct lastdiff] sct lastdiff $diff # carry is used for finetuning when speed is below 1 set carry [silent 0 sct carry] set speed [expr $prop * ($diff - $lastdiff) + $int * $diff + $carry] } else { logsetup [sct]/set clear } set long 100 set short 10 set micro 1 set minpulse [hvali [sct]/minpulse] if {$speed > 700} { set speed 700 } elseif {$speed < -700} { set speed -700 } # clientput $speed set normpos [hvali [sct]/pos/norm] set maxpos [hvali [sct]/pos/max] set pos [hvali [sct]/pos] if {$speed < 0} { set dir nvClose set plen [expr -$speed] set force normal } else { set dir nvOpen set plen $speed if {$pos < $normpos} { set force strong } else { set force normal } } sct pulser ccu::pulser if {$plen >= $long * 0.9} { set length long sct utime endtime set p [expr int($plen / $long)] if {$p < 1} { set p 1 } sct pulsecnt $p sct endtime [expr [sct endtime] + $plen * 0.001] sct plen $plen if {$plen == 700} { # a pulse of more than 700 ms is extended to a 1000 ms hardware pulse set plen 1000 sct pulser ccu::pulsend } else { sct pulser ccu::timer } } elseif {$plen >= $short * 0.9 && $minpulse <= 10} { set length short set p [expr int($plen / $short)] if {$p > 5} { set p 5 } elseif {$p < 1} { set p 1 } sct pulsecnt $p set plen [expr $p * $short] } elseif {$plen >= $micro * 0.9 && $minpulse <= 1} { set length micro set force strong set p [expr int($plen / $micro)] if {$p > 5} { set p 5 } elseif {$p < 1} { set p 1 } sct pulsecnt $p set plen [expr $p * $micro] } else { sct carry $speed hupdate [sct]/speed 0 return idle } sct carry 0 if {$speed < 0} { set speed -$plen } else { set speed $plen } hupdate [sct]/speed $speed ccu::output $dir $force $length ccu::adjustpos [expr $speed * 0.001] return pulser } proc ccu::timer {} { sct utime nowtime if {[sct nowtime] > [sct endtime]} { ccu::output nvOff return stdSct::complete } ccu::output pulsOff return ccu::timer } proc ccu::pulser {} { set p [sct pulsecnt] incr p -1 sct pulsecnt $p if {$p <= 0} { ccu::output nvOff return stdSct::complete } ccu::output pulsOff return ccu::pulser2 } proc ccu::pulser2 {} { ccu::output pulsOn return ccu::pulser } proc ccu::ticker {} { if {([sct dout] & 0x30) == 0} { # do not tick when motor is off return idle } ccu::output pulsOn return ccu::pulsend } proc ccu::pulsend {} { ccu::output pulsOff return stdSct::complete } proc ccu::unpoll {} { return unpoll }