Package dics :: Module detmech
[hide private]
[frames] | no frames]

Source Code for Module dics.detmech

  1  import ConfigParserRT 
  2  import linear 
  3  import logging 
  4  import math 
  5  from rotator import angle 
  6   
7 -class detmech:
8 """ 9 Object representing the DAZLE/CIRPASS camera's detector focus/tilt 10 mechanism which consists of a three point kinematic mount. 11 """
12 - def __init__(self, det1, det2, det3, offset1, offset2, offset3, name):
13 """ 14 Arguments: 15 16 det1: A linear.linear object corresponding to detector motor 1. 17 det2: A linear.linear object corresponding to detector motor 2. 18 det3: A linear.linear object corresponding to detector motor 3. 19 offset1: Offset from the nominal zero position to zero the tilt. 20 offset2: Offset from the nominal zero position to zero the tilt. 21 offset3: Offset from the nominal zero position to zero the tilt. 22 name: An identifier used for logging purposes. 23 """ 24 self.logger = logging.getLogger(name) 25 if isinstance(det1, linear.linear): 26 self.det1 = det1 27 else: 28 self.logger.critical('det1 is not a linear.linear object!') 29 if isinstance(det2, linear.linear): 30 self.det2 = det2 31 else: 32 self.logger.critical('det2 is not a linear.linear object!') 33 if isinstance(det3, linear.linear): 34 self.det3 = det3 35 else: 36 self.logger.critical('det3 is not a linear.linear object!') 37 38 self.offset1 = int(offset1) 39 self.offset2 = int(offset2) 40 self.offset3 = int(offset3)
41
42 - def updateConfig(self, config):
43 """ 44 Should be called by the parent instrument object's destructor 45 to enable status information to be saved to file on exit. 46 47 Takes a ConfigParserRT.SafeConfigParser object as argument. 48 """ 49 self.logger.debug("updateConfig() called") 50 if isinstance(config, ConfigParserRT.SafeConfigParser): 51 config.set(self.name, 'offset1', str(self.offset1)) 52 config.set(self.name, 'offset2', str(self.offset2)) 53 config.set(self.name, 'offset3', str(self.offset3)) 54 else: 55 self.logger.critical("Invalid argument to updateConfig(%s)" \ 56 % repr(config)) 57 self.logger.critical("Unable to save status data")
58
59 - def setupDetmech(self):
60 """ 61 Calls the setupLinear() methods of each of the three mechanism motors 62 to establish the centre of the range of movement, limit switch 63 positions and software limits. 64 65 Due to slight variations in limit switch positions this will not 66 properly zero the tilts of the detector, in order to do this a target 67 screen should be used together with manual tilt adjustments, and then 68 zeroTilts() called to set the required offsets for each motor. 69 70 Care should be taken in using this routine, and it should not be run 71 unless necessary. As each of the motors is exercised over its full 72 range in turn large tilts will occur and there are some combinations 73 of motor positions where this can result in the detector box contacting 74 the LN2 can. Attempting to ensure that the motors, and in particular 75 motor 3, are near the middle of their range before beginning this 76 process will minimise risk. 77 """ 78 det1.setupLinear() 79 det3.setupLinear() 80 det2.setupLinear()
81
82 - def getPosition(self):
83 """ 84 Returns the current focus position (in microns) and the tilts along 85 the two diagonals of the detector (as rotator.angle objects). 86 """ 87 pos1 = det1.getPosition() - self.offset1 88 pos2 = det2.getPosition() - self.offset2 89 pos3 = det3.getPosition() - self.offset3 90 focus = (pos1 + pos3) / 2.0 91 tilt1 = angle(math.arctan2(pos1 - pos3, 152000)) 92 tilt2 = angle(math.arctan2(pos2 - focus, 76000)) 93 return focus, tilt1, tilt2
94
95 - def printPosition(self):
96 """ 97 Similar to getPosition(), but instead prints a human readable 98 string decribing the focus position (microns) and the tiles (degrees). 99 """ 100 focus, tilt1, tilt2 = self.getPosition() 101 print 'Focus: %i, tilt1: %f, tilt2: %f' % \ 102 (focus, tilt1.getAngle('d'), tilt2.getAngle('d'))
103
104 - def zeroTilts(self):
105 """ 106 Call this function after manually zeroing the tilt of the detector 107 by imaging a test target screen. This adjusts the 3 offsets used in 108 calculating the detector position so that afterwards the tilt 109 calculations will be correct. 110 """ 111 # Calculate focus position from old offsets. This will be preserved. 112 pos1 = det1.getPosition() - self.offset1 113 pos3 = det3.getPosition() - self.offset3 114 focus = (pos1 + pos3) / 2.0 115 # Can now calculate new offsets on basis that tilt1 == tilt2 == 0. 116 self.offset1 = det1.getPosition() - focus 117 self.offset2 = det2.getPosition() - focus 118 self.offset3 = det3.getPosition() - focus
119
120 - def moveTo(self, focus, tilt1=0, tilt2=0):
121 """ 122 Arguments: 123 124 focus: new focus position, in microns. 125 tilt1: new tilt along the 1-3 diagonal, in degrees, default 0 126 tilt2: new tilt along the 3 diagonal, in degrees, default 0. 127 """ 128 focus = float(focus) 129 # Convert to radians 130 tilt1 = math.radians(float(tilt1)) 131 tilt2 = math.radians(float(tilt2)) 132 # Calculate new required motor positions 133 new1 = focus + 76000 * math.tan(tilt1) + offset1 134 new3 = focus - 76000 * math.tan(tilt1) + offset3 135 new2 = focus + 76000 * math.tan(tilt2) + offset2 136 # Check that the move will not be out of range for any motor. 137 low1, up1 = self.det1.getLimits() 138 low2, up2 = self.det2.getLimits() 139 low3, up3 = self.det3.getLimits() 140 if new1 < low1: 141 self.logger.critical(\ 142 'Move exceeds lower limit of motor 1, ignored!') 143 return 144 if new1 > up1: 145 self.logger.critical(\ 146 'Move exceeds upper limit of motor 1, ignored!') 147 return 148 if new2 < low2: 149 self.logger.critical(\ 150 'Move exceeds lower limit of motor 2, ignored!') 151 return 152 if new2 > up2: 153 self.logger.critical(\ 154 'Move exceeds upper limit of motor 2, ignored!') 155 return 156 if new3 < low3: 157 self.logger.critical(\ 158 'Move exceeds lower limit of motor 3, ignored!') 159 return 160 if new3 > up3: 161 self.logger.critical(\ 162 'Move exceeds upper limit of motor 3, ignored!') 163 return 164 # Now execute the required moves, in millimetre increments 165 # to avoid extreme tilts. 166 cur1 = self.det1.getPosition() 167 cur2 = self.det2.getPosition() 168 cur3 = self.det3.getPosition() 169 while cur1 != new1 or cur2 != new2 or cur3 != new3: 170 if abs(new1-cur1) > 1000: 171 self.det1.moveBy(1000 * (new1-cur1) / abs(new1-cur1)) 172 elif new1 != cur1: 173 self.det1.moveTo(new1) 174 175 if abs(new2-cur2) > 1000: 176 self.det2.moveBy(1000 * (new2-cur2) / abs(new2-cur2)) 177 elif new2 != cur2: 178 self.det2.moveTo(new2) 179 180 if abs(new3-cur3) > 1000: 181 self.det3.moveBy(1000 * (new3-cur3) / abs(new3-cur3)) 182 elif new3 != cur3: 183 self.det3.moveTo(new3) 184 185 cur1 = self.det1.getPosition() 186 cur2 = self.det2.getPosition() 187 cur3 = self.det3.getPosition()
188
189 - def moveBy(dfocus, dtilt1=0, dtilt2=0):
190 """ 191 Arguments: 192 193 dfocus: The amount that the focus position should change by, in 194 microns. 195 dtilt1: The amount that tilt about the first diagonal should change by 196 in degrees 197 dtilt2: The amount that tilt about the second diagonal should change by 198 in degrees. 199 """ 200 curr_focus, curr_tilt1, curr_tilt2 = self.getPosition() 201 new_focus = curr_focus + float(dfocus) 202 new_tilt1 = curr_tilt1.getAngle('d') + float(dtilt1) 203 new_tilt2 = curr_tilt2.getAngle('d') + flaot(dtilt2) 204 self.moveTo(new_focus, new_tilt1, new_tilt2)
205
206 - def getLimits(self):
207 """ 208 Returns a tuple containing the upper and lower focus position limits 209 for zero tilts. If tilts are non-zero this range will be reduced, and 210 likewise the focus position will determine the range of available 211 tilts. This function doesn't try to work those effects out. 212 """ 213 lowers, uppers = zip(self.det1.getLimits(), self.det2.getLimits(), \ 214 self.det3.getLimits()) 215 lowers = lowers - (self.offset1, self.offset2, self.offset3) 216 uppers = uppers - (self.offset1, self.offset2, self.offset3) 217 return min(lowers), max(uppers)
218