## # @file Spin flipper control for Quokka # # Author: Ferdi Franceschini (ffr@ansto.gov.au) May 2010 # # The spin flipper can be installed with the following command, # ::scobj::rfgen::mkFlipper { # name "flipper" # address 1 # opCurr 68 # opFreq 241 # IP localhost # PORT 65123 # tuning 1 # currtol 1 # interval 2 # } # # NOTE: # If tuning=1 this will generate flipper/set_current and flipper/set_frequency # nodes for the instrument scientists. # The tuning parameter should be set to 0 for the users. # # The operation_manual_Platypus_polarization_system.doc:Sec 3.1 states the following # Attention # a) Do not switch on the RF output with non-zero current setting (the current # control becomes unstable)! If unsure, rotate the current setting # potentiometer 10 turns counter-clockwise. # b) In case of RF vacuum discharge (harmful for the system) # " the main symptom is that the RF power source turns into CV mode, the # voltage increases to 34 Vem and the current decreases; # " switch off the RF output; # " decrease current setting by rotating the potentiometer 10 turns counter-clockwise; # " verify the vacuum level in the tank and restart the flipper operation only if it is below 0.01 mbar. namespace eval ::scobj::rfgen { # Control states variable RAMPIDLE 0 variable RAMPSTOP 1 variable RAMPSTART 2 variable RAMPBUSY 3 variable RAMPTOZERO 4 variable FLIPOFF 5 variable MAXVOLTAGE 34 } ## # @brief Utility for trimming zero padding from current and frequency readings. # We do this to avoid misinterpreting numbers as octal proc ::scobj::rfgen::mkStatArr {stateArrName stateReport} { upvar $stateArrName stateArr array set stateArr $stateReport if {$stateArr(curr) != 0} { set stateArr(curr) [string trimleft $stateArr(curr) 0] } if {$stateArr(freq) != 0} { set stateArr(freq) [string trimleft $stateArr(freq) 0] } if {$stateArr(voltage) != 0} { set stateArr(voltage) [string trimleft $stateArr(voltage) 0] } } ## # @brief Switch the spin flipper on or off proc ::scobj::rfgen::set_flip_on {basePath} { variable RAMPSTART variable RAMPTOZERO set flipState [sct target] switch $flipState { "0" { hsetprop $basePath targetCurr 0 hsetprop $basePath OutputState 0 hsetprop $basePath ramping $RAMPSTART sct update 0 sct utime readtime } "1" { hsetprop $basePath targetCurr [hgetpropval $basePath opCurr] hsetprop $basePath targetFreq [hgetpropval $basePath opFreq] hsetprop $basePath OutputState 1 hsetprop $basePath ramping $RAMPSTART sct update 1 sct utime readtime } default { set ErrMsg "[sct] invalid input $flipState, Valid states for [sct] are 1 or 0" sct seterror "ERROR: $ErrMsg" return -code error $ErrMsg } } return idle } ## # @brief Get the target current and scale it for the RF generator. # Also updates the operating current for this session. # # @param basePath, The "spin-flipper" object path, this is where we keep our state variables. proc ::scobj::rfgen::set_current {basePath} { variable RAMPSTART set newCurr [sct target] set current [expr {round(10.0 * $newCurr)}] hsetprop $basePath targetCurr $current hsetprop $basePath opCurr $current hsetprop $basePath ramping $RAMPSTART hsetprop $basePath OutputState 1 return idle } ## # @brief Get the target frequency. Also updates the operating frequency for this session. # # @param basePath, The "spin-flipper" object path, this is where we keep our state variables. proc ::scobj::rfgen::set_frequency {basePath} { variable RAMPSTART set newFreq [sct target] hsetprop $basePath targetFreq $newFreq hsetprop $basePath opFreq $newFreq hsetprop $basePath ramping $RAMPSTART hsetprop $basePath OutputState 1 return idle } ## # @brief Request a state report from the RF generator proc ::scobj::rfgen::rqStatFunc {} { sct send "L:[sct address]" return rdState } ## # @brief Read and record the state report from the RF generator proc ::scobj::rfgen::rdStatFunc {} { variable RAMPBUSY variable RAMPSTART variable RAMPTOZERO variable RAMPIDLE variable FLIPOFF variable MAXVOLTAGE set basePath [sct] set currSuperState [sct ramping] set updateFlipper 0 set statStr [sct result] if {[string match "ASCERR:*" $statStr]} { sct geterror $statStr sct ramping $RAMPIDLE return stateChange } set statList [split $statStr "|="] mkStatArr stateArr $statList if {$statList != [sct oldStateRep]} { hset $basePath/flip_current [expr {$stateArr(curr) / 10.0}] hset $basePath/flip_frequency $stateArr(freq) hset $basePath/flip_voltage $stateArr(voltage) hset $basePath/flip_on $stateArr(O) hset $basePath/state_report $statList sct update $statList sct utime readtime sct oldStateRep $statList } if {$currSuperState != $FLIPOFF && $stateArr(curr) > [sct currTol] && $stateArr(O) && $stateArr(CV)} { broadcast "WARNING: spin flipper has switched to voltage control, voltage = $stateArr(voltage)" if {$stateArr(voltage) >= $MAXVOLTAGE} { sct ramping $FLIPOFF } } return stateChange } ## # @brief State transition function proc ::scobj::rfgen::stateFunc {} { variable RAMPIDLE variable RAMPSTOP variable RAMPSTART variable RAMPBUSY variable RAMPTOZERO variable FLIPOFF variable MAXVOLTAGE set basePath [sct] set currSuperState [sct ramping] mkStatArr stateArr [hval $basePath/state_report] set currControlStatus [sct status] switch $currSuperState [ subst -nocommands { $RAMPSTART { # broadcast RAMPSTART if [string match $currControlStatus "IDLE"] { statemon start flipper sct status "BUSY" sct ramping $RAMPBUSY return ramp } else { # Flipper is off, set current to zero before switching on sct origTargetCurr [sct targetCurr] sct targetCurr 0 sct OutputState 0 sct ramping $RAMPTOZERO return ramp } } $RAMPTOZERO { # broadcast RAMPTOZERO if {$stateArr(curr) <= [sct currTol]} { # We've reached a safe state so switch on and ramp to target current sct targetCurr [sct origTargetCurr] sct OutputState 1 sct ramping $RAMPBUSY } else { sct targetCurr 0 sct OutputState 0 } return ramp } $RAMPBUSY { # broadcast RAMPBUSY if { [expr {abs($stateArr(curr) - [sct targetCurr])}] <= [sct currTol] } { sct ramping $RAMPSTOP return idle } return ramp } $FLIPOFF { sct targetCurr 0 sct OutputState 0 if { $stateArr(curr) <= [sct currTol] } { sct ramping $RAMPSTOP broadcast "ERROR: Spin flipper switched off voltage exceeds $MAXVOLTAGE in voltage control state, check vacuum" return idle } else { return ramp } } $RAMPSTOP { # broadcast RAMPSTOP if [string match $currControlStatus "BUSY"] { statemon stop flipper sct status "IDLE" } sct ramping $RAMPIDLE return idle } $RAMPIDLE { # broadcast RAMPIDLE return idle } }] } ## # @brief Ramps the current up or down in steps of 0.5A and/or sets the frequency proc ::scobj::rfgen::rampFunc {} { set basePath [sct] set currSuperState [sct ramping] mkStatArr stateArr [hval $basePath/state_report] set targetCurr [sct targetCurr] set targetFreq [sct targetFreq] set output [sct OutputState] if { [expr {abs($stateArr(curr) - [sct targetCurr])}] <= [sct currTol] } { set curr $stateArr(curr) } elseif {$targetCurr < $stateArr(curr)} { set curr [expr $stateArr(curr)-5] if {$curr < $targetCurr} { set curr $targetCurr } } elseif {$targetCurr > $stateArr(curr)} { set curr [expr $stateArr(curr)+5] if {$curr > $targetCurr} { set curr $targetCurr } } set reply [sct_rfgen send "S:[sct address]:I=$curr:F=$targetFreq:K3=$stateArr(K3):K2=$stateArr(K2):K1=$stateArr(K1):O=$output"] return idle } ## # @brief Make a spin flipper control object # # @param argList, {name "flipper" address "1" opCurr 68 opFreq 241 IP localhost PORT 65123 tuning 0 interval 1} # # name: name of spin flipper object # address: address assigned to RF generator 1-9 # opCurr: the operating current, when you switch the spin flipper on it will ramp to this current # opFreq: the operating frequency, when you switch on the spin flipper it will set this frequency # IP: IP address of RF generator moxa box # PORT: Port number assigned to the generator on the moxa-box # tuning: boolean, set tuning=1 to allow instrument scientists to set the current and frequency # interval: polling and ramping interval in seconds. One sets the ramp rate to 0.5A/s proc ::scobj::rfgen::mkFlipper {argList} { variable RAMPIDLE # Generate parameter array from the argument list foreach {k v} $argList { set KEY [string toupper $k] set pa($KEY) $v } MakeSICSObj $pa(NAME) SCT_OBJECT sicslist setatt $pa(NAME) klass instrument sicslist setatt $pa(NAME) long_name $pa(NAME) # hfactory /sics/$pa(NAME)/status plain spy text hsetprop /sics/$pa(NAME) status "IDLE" hfactory /sics/$pa(NAME)/state_report plain internal text hfactory /sics/$pa(NAME)/flip_current plain internal float hfactory /sics/$pa(NAME)/flip_frequency plain internal int hfactory /sics/$pa(NAME)/flip_voltage plain internal int hfactory /sics/$pa(NAME)/flip_on plain internal int hsetprop /sics/$pa(NAME) read ::scobj::rfgen::rqStatFunc hsetprop /sics/$pa(NAME) rdState ::scobj::rfgen::rdStatFunc hsetprop /sics/$pa(NAME) stateChange ::scobj::rfgen::stateFunc hsetprop /sics/$pa(NAME) ramp ::scobj::rfgen::rampFunc hsetprop /sics/$pa(NAME) address $pa(ADDRESS) hsetprop /sics/$pa(NAME) tuning $pa(TUNING) hsetprop /sics/$pa(NAME) ramping $RAMPIDLE hsetprop /sics/$pa(NAME) opCurr $pa(OPCURR) hsetprop /sics/$pa(NAME) opFreq $pa(OPFREQ) hsetprop /sics/$pa(NAME) targetCurr 0 hsetprop /sics/$pa(NAME) origTargetCurr 0 hsetprop /sics/$pa(NAME) oldStateRep "" hsetprop /sics/$pa(NAME) currTol $pa(CURRTOL) hfactory /sics/$pa(NAME)/comp_current plain internal float hsetprop /sics/$pa(NAME)/comp_current units "A" hset /sics/$pa(NAME)/comp_current $pa(COMPCURR) hfactory /sics/$pa(NAME)/guide_current plain internal float hsetprop /sics/$pa(NAME)/guide_current units "A" hset /sics/$pa(NAME)/guide_current $pa(GUIDECURR) hfactory /sics/$pa(NAME)/thickness plain internal float hsetprop /sics/$pa(NAME)/thickness units "mm" hset /sics/$pa(NAME)/thickness $pa(THICKNESS) hfactory /sics/$pa(NAME)/set_flip_on plain user int hsetprop /sics/$pa(NAME)/set_flip_on write ::scobj::rfgen::set_flip_on /sics/$pa(NAME) # Only create the set current and frequency nodes when commissioning # Initialise properties required for generating the API for GumTree and to save data ::scobj::hinitprops $pa(NAME) flip_current flip_frequency flip_voltage flip_on comp_current guide_current thickness hsetprop /sics/$pa(NAME)/comp_current mutable false hsetprop /sics/$pa(NAME)/guide_current mutable false hsetprop /sics/$pa(NAME)/thickness mutable false if {[SplitReply [rfgen_simulation]] == "false"} { makesctcontroller sct_rfgen rfamp $pa(IP):$pa(PORT) mkStatArr stateArr [split [sct_rfgen transact "L:$pa(ADDRESS)"] "|="] hset /sics/$pa(NAME)/flip_current [expr {$stateArr(curr) / 10.0}] hset /sics/$pa(NAME)/flip_frequency $stateArr(freq) hset /sics/$pa(NAME)/flip_voltage $stateArr(voltage) hset /sics/$pa(NAME)/flip_on $stateArr(O) hsetprop /sics/$pa(NAME) targetFreq $stateArr(freq) hsetprop /sics/$pa(NAME) targetCurr [expr {$stateArr(curr) / 10.0}] sct_rfgen poll /sics/$pa(NAME) $pa(INTERVAL) sct_rfgen write /sics/$pa(NAME)/set_flip_on } if {$pa(TUNING)} { hfactory /sics/$pa(NAME)/set_current plain user float hfactory /sics/$pa(NAME)/set_frequency plain user int hsetprop /sics/$pa(NAME)/set_current write ::scobj::rfgen::set_current /sics/$pa(NAME) hsetprop /sics/$pa(NAME)/set_frequency write ::scobj::rfgen::set_frequency /sics/$pa(NAME) if {[SplitReply [rfgen_simulation]] == "false"} { sct_rfgen write /sics/$pa(NAME)/set_current sct_rfgen write /sics/$pa(NAME)/set_frequency } } }