API
 
Loading...
Searching...
No Matches
adcCtrl.py
Go to the documentation of this file.
1import sys
2import logging
3from enum import Enum
4import time
5import numpy as np
6
7import xconf
8
9from magaox.indi.device import XDevice, BaseConfig
10from magaox.camera import XCam
11from magaox.constants import StateCodes
12
13from purepyindi2 import device, properties, constants
14from purepyindi2.messages import DefNumber, DefSwitch, DefLight, DefText
15
16import hcipy as hp
17from scipy.optimize import minimize
18
20 def __init__(self,wavelength=656E-9,bandwidth=100E-9,grating_angle=-28,grating_freq=47):
21 self.wavelength = wavelength
22 self.bandwidth = bandwidth
23 self.grating_angle = grating_angle
24 self.grating_freq = grating_freq
25 self.normalized_wavelength = wavelength / 656E-9 #normalizing the wavelengths to the ha values
26 self.normalized_bandwidth = bandwidth / 656E-9
27 self.maxiter = 6
28 self.control_mtx = np.matrix([[0,0],])
29 self.current_speckle = None
30
31 def set_measurement(self, data):
32 self.data = data
33
34 def make_gaussian(self, mu_x, mu_y, sigma_x,sigma_y, orientation):
35 def func(grid):
36 new_grid = grid.shifted([-mu_x, -mu_y]).rotated(orientation)
37 x = new_grid.x / sigma_x
38 y = new_grid.y / sigma_y
39 r2 = x**2 + y**2
40 return hp.Field(np.exp(-0.5 * r2), grid)
41 return func
42
43 def satellite_spot(self, amplitude, mu_x, mu_y, sigma_x, sigma_y, orientation, background):
44 def func(grid):
45 return hp.Field(amplitude * self.make_gaussian(mu_x, mu_y, sigma_x, sigma_y, orientation)(grid) + background, grid)
46 return func
47
48 def cost(self, theta):
49 fit = self.satellite_spot(*theta)(self.data.grid)
50 j = np.sum( (self.data - fit)**2)
51
52 aspect_ratio = np.abs(theta[3] / theta[4])
53
54 ##boundary condition for the aspect ratio
55 if self.current_speckle == 0 or self.current_speckle == 2:
56 if aspect_ratio >= 1:
57 j+= 1E3 * aspect_ratio **2
58 elif self.current_speckle ==1 or self.current_speckle == 3:
59 if aspect_ratio <= 1:
60 j+= 1E3 * 1/aspect_ratio**2
61
62 return j
63
64 def fit(self,theta_est):
65 fitting = minimize(self.cost,theta_est,options={'maxiter':self.maxiter})
66 return fitting
67
69 M00 = np.sum(self.data)
70 M10 = np.sum(self.data * self.data.grid.x)
71 M01 = np.sum(self.data * self.data.grid.y)
72
73 centroid = [M10/M00,M01/M00]
74 return centroid
75
76 def estimate_angle(self):
77 M00 = np.sum(self.data)
78 M10 = np.sum(self.data * self.data.grid.x)
79 M01 = np.sum(self.data * self.data.grid.y)
80
81 M20 = np.sum(self.data * self.data.grid.x**2)
82 M02 = np.sum(self.data * self.data.grid.y**2)
83 M11 = np.sum(self.data * self.data.grid.y * self.data.grid.x)
84
85 mu10 = M10 / M00
86 mu01 = M01 / M00
87 mu20 = M20 / M00 - mu10**2
88 mu02 = M02 / M00 - mu01**2
89 mu11 = M11 / M00 - mu10 * mu01
90 angle = (1/2 * np.arctan2(2 * mu11, mu20 - mu02))
91
92 return angle
93
94 def find_speckle(self, image,speckle_number):
95 '''speckles are indexed from the top right going counter clockwise'''
96 grating_freq = self.grating_freq
97 grating_angle = self.grating_angle
98 corners = np.array([[0, grating_freq * self.normalized_wavelength],[grating_freq * self.normalized_wavelength,0],[0, -grating_freq * self.normalized_wavelength],[-grating_freq * self.normalized_wavelength,0]])
99 sizes = np.array([[8,20],[20,8],[8,20],[20,8]])
100 #sizes = np.array([[16,25],[25,16],[16,25],[25,16]])
101
102 rect = hp.make_rotated_aperture(hp.make_rectangular_aperture(size=sizes[speckle_number], center=corners[speckle_number]), np.deg2rad(-grating_angle))(image.grid)
103 speckle_img = rect * image
104 return speckle_img
105
106 def set_psf(self,psf):
107 self.psf = psf
108
110
111 speckle_angles = np.zeros(4)
112 sig_x = [0.8,3.5,0.8,3.5]
113 sig_y = [3.5,0.8,3.5,0.8]
114
115 for i in range(4):
116 img = self.find_speckle(self.psf,i)
117 self.current_speckle = i
118 self.set_measurement(img)
119
120 sigma_x = sig_x[i]
121 sigma_y = sig_y[i]
122 #orientation = np.radians(28)
123
124 orientation = self.estimate_angle()
125 if self.current_speckle == 0 or self.current_speckle ==2:
126 orientation = np.pi/2 - orientation
127 elif self.current_speckle == 1 or self.current_speckle == 3:
128 orientation = -orientation
129
130 amplitude = self.data.max()
131 centroid = self.estimate_centroid()
132 mu_x = centroid[0]
133 mu_y = centroid[1]
134 background = 0
135
136 theta_est = np.array([amplitude, mu_x, mu_y, sigma_x, sigma_y, orientation, background])
137 fit = self.fit(theta_est)
138 speckle_angles[i] = np.degrees(fit.x[5])
139
140 self.current_speckle = None
141 speckle_angles = np.array(speckle_angles).T
142
143 return speckle_angles
144
145 def speckle_pairs(self, speckle_angles):
146 pair02 = speckle_angles[0] - speckle_angles[2]
147 pair13 = speckle_angles[1] - speckle_angles[3]
148 return np.array([pair02,pair13])
149
150 def calculate_command(self,speckle_angles):
151 predicted_disp = self.control_mtx * np.matrix(speckle_angles).T
152 predicted_disp = np.array(predicted_disp)
153 return -predicted_disp
154
155 def clear(self):
156 self.psf = None
157 self.data = None
158
159 def set_control_mtx(self,matrix):
160 self.control_mtx = matrix
161
162 '''this is stuff specifically for working with the real calibration datacubes'''
163 def window_field(self,data, center, width, height):
164 indx = data.grid.closest_to(center)
165 y_ind, x_ind = np.unravel_index(indx, data.shaped.shape)
166 cutout = data.shaped[(y_ind-height//2):(y_ind + height//2), (x_ind-width//2):(x_ind+width//2)]
167 sub_grid = hp.make_pupil_grid([width, height], [width * data.grid.delta[0], height * data.grid.delta[1]])
168 return hp.Field(cutout.ravel(), sub_grid)
169
170 def crop_image(self, image,extent=400,mask_diam=60):
171 #cutout a centered PSF
172 img = image/image.max()
173
174 img_subtracted = img >0.1
175 center_of_intensity = np.array([sum(img_subtracted*img_subtracted.grid.x)/sum(img_subtracted),sum(img_subtracted*img_subtracted.grid.y)/sum(img_subtracted)])
176 mask_ap = hp.make_circular_aperture(mask_diam,center_of_intensity)
177 mask = mask_ap(img_subtracted.grid)
178 mask = abs(mask - 1)
179 masked_img = mask * img
180
181 img = masked_img
182 img = self.window_field(img,[center_of_intensity[0],center_of_intensity[1]],extent,extent)
183 img /= img.max()
184
185 bk = np.median(img)
186 img -= bk
187 img = hp.Field([x if x>0 else 0 for x in img],img.grid)
188
189 return img
190
191 def filter_image(self,img,low_freq = 0.01,high_freq=1):
192
193 ff = hp.FourierFilter(img.grid, hp.make_circular_aperture(2 * np.pi * low_freq))
194 filtered_img= np.real(ff.forward(img + 0j))
195 img = img - filtered_img
196
197 ff2 = hp.FourierFilter(img.grid, hp.make_circular_aperture(2 * np.pi * high_freq))
198 filtered_img = np.real(ff2.forward(img + 0j))
199 img = filtered_img
200 filtered_subtracted = img
201
202 return filtered_subtracted
203
204
205@xconf.config
207 """
208 """
209 shmim : str = xconf.field(help="Name of the camera device (specifically, the associated shmim, if different)")
210 dark_shmim : str = xconf.field(help="Name of the dark frame shmim associated with this camera device")
211
212@xconf.config
213class AdcCtrlConfig(BaseConfig):
214 """ Active ADC control
215 """
216 camera : CameraConfig = xconf.field(help="Camera to use")
217 sleep_interval_sec : float = xconf.field(default=0.25, help="Sleep interval between loop() calls")
218
219class States(Enum):
220 IDLE = 0
221 CLOSED_LOOP = 1
222 ONESHOT = 2
223 CALIB = 3
224
225class adcCtrl(XDevice):
226 config: AdcCtrlConfig
227
228 def setup(self):
229 self.log.debug(f"I was configured! See? {self.config=}")
230
231 fsm = properties.TextVector(name='fsm')
232 fsm.add_element(DefText(name='state', _value=StateCodes.INITIALIZED.name))
233 self.add_property(fsm)
234
235 sv = properties.SwitchVector(
236 name='state',
237 rule=constants.SwitchRule.ONE_OF_MANY,
238 perm=constants.PropertyPerm.READ_WRITE,
239 )
240 sv.add_element(DefSwitch(name="idle", _value=constants.SwitchState.ON))
241 sv.add_element(DefSwitch(name="adcLoop", _value=constants.SwitchState.OFF))
242 sv.add_element(DefSwitch(name="oneshot", _value=constants.SwitchState.OFF))
243 sv.add_element(DefSwitch(name="calibrate", _value=constants.SwitchState.OFF))
244 self.add_property(sv, callback=self.handle_state)
245
246 nv = properties.NumberVector(name='n_avg')
247 nv.add_element(DefNumber(
248 name='current', label='Number of frames', format='%i',
249 min=1, max=150, step=1, _value=1
250 ))
251 nv.add_element(DefNumber(
252 name='target', label='Number of frames', format='%i',
253 min=1, max=150, step=1, _value=1
254 ))
255 self.add_property(nv, callback=self.handle_n_avg)
256
257 nv = properties.NumberVector(name='no_measurements')
258 nv.add_element(DefNumber(
259 name='number', label='number', format='%i',
260 min=1, max=100.00, step=1, _value=1
261 ))
262 self.add_property(nv, callback=self.handle_no_measurements)
263
264 nv = properties.NumberVector(name='gain')
265 nv.add_element(DefNumber(
266 name='current', label='ADC Loop Gain', format='%.2f',
267 min=0.00, max=1.00, step=0.01, _value=0.10
268 ))
269 nv.add_element(DefNumber(
270 name='target', label='ADC Loop Gain', format='%.2f',
271 min=0.00, max=1.00, step=0.01, _value=0.10
272 ))
273 self.add_property(nv, callback=self.handle_gain)
274
275 nv = properties.NumberVector(name='offset')
276 nv.add_element(DefNumber(
277 name='current', label='offset', format='%.2f',
278 min=-45, max=45, step=0.01, _value=0.0
279 ))
280 nv.add_element(DefNumber(
281 name='target', label='offset', format='%.2f',
282 min=-45, max=45, step=0.01, _value=0.0
283 ))
284 self.add_property(nv, callback=self.handle_offset)
285
286 nv = properties.NumberVector(name='ctrl_mtx')
287 nv.add_element(DefNumber( #first element
288 name='m00', label='m00', format='%.4f',
289 min=-10.00, max=10.00, step=0.0001, _value=0.21178766
290 ))
291 nv.add_element(DefNumber(
292 name='m01', label='m01', format='%.4f',
293 min=-10.00, max=10.00, step=0.0001, _value=0.19275196
294 ))
295 self.add_property(nv, callback=self.handle_ctrl_mtx)
296
297 sv = properties.SwitchVector(
298 name='labmode',
299 rule=constants.SwitchRule.ONE_OF_MANY,
300 perm=constants.PropertyPerm.READ_WRITE,
301 )
302 sv.add_element(DefSwitch(name="toggle", _value=constants.SwitchState.OFF))
303 self.add_property(sv, callback=self.handle_labmode)
304
305 sv = properties.SwitchVector(
306 name='knife_edge',
307 rule=constants.SwitchRule.ONE_OF_MANY,
308 perm=constants.PropertyPerm.READ_WRITE,
309 )
310 sv.add_element(DefSwitch(name="toggle", _value=constants.SwitchState.OFF))
311 self.add_property(sv, callback=self.handle_knife_edge)
312
313 sv = properties.SwitchVector(
314 name='knife_edge_findzero',
315 rule=constants.SwitchRule.ONE_OF_MANY,
316 perm=constants.PropertyPerm.READ_WRITE,
317 )
318 sv.add_element(DefSwitch(name="request", _value=constants.SwitchState.OFF))
319 self.add_property(sv, callback=self.handle_knife_edge_findzero)
320
321 sv = properties.SwitchVector(
322 name='reset_deltaADCs',
323 rule=constants.SwitchRule.ONE_OF_MANY,
324 perm=constants.PropertyPerm.READ_WRITE,
325 )
326 sv.add_element(DefSwitch(name="request", _value=constants.SwitchState.OFF))
327 self.add_property(sv, callback=self.handle_reset)
328
329 self.client.get_properties('adctrack')
330 self.client.get_properties('fwsci1')
331
332 self.log.info("Found camera: {:s}".format(self.config.camera.shmim))
333 self.camera = XCam(
334 self.config.camera.shmim,
335 pixel_size=6.0/21.0,
336 use_hcipy=True,
337 indi_client=self.client
338 )
339
340 self._state = States.IDLE
341
342 #self._loop_counter = 0
343 self._n_avg = 1
344 self._gain = 0.5
345 self._command = 0
346 self._control_mtx = np.array([0.21178766, 0.19275196])
347 self._extent = 400
348 self.delta_1 = 0
349 self.delta_2 = 0
350 self._offset = 0
351 self._mask_diam = 50
352 self._lab = False
353 self._knife_edge = False
354 self._knife_edge_zero1 = 26.78175714
355 self._knife_edge_zero2 = 26.544759645
357
358 if self.client['adctrack.deltaADC1.current'] != 0:
359 self.set_command(0,0)
360 self.send_command()
361
362 if self.client['fwsci1.filterName.i'] == constants.SwitchState.ON:
363 self._center_wavelength = 762E-9
364 self._extent = 400
365 elif self.client['fwsci1.filterName.z'] == constants.SwitchState.ON:
366 self._center_wavelength = 908E-9
367 self._extent = 480
368 else:
369 self._center_wavelength = 656E-9
370 self._extent = 400
371
372 self.ADC = AdcFitter(wavelength=self._center_wavelength)
373 self.log.debug(f'initial normalized wavelength value: {self.ADC.normalized_wavelength}')
374 #self.update_wavelength()
375 self.ADC.set_control_mtx(self._control_mtx)
376
377 self.properties['fsm']['state'] = StateCodes.READY.name
378 self.update_property(self.properties['fsm'])
379
380 def handle_state(self, existing_property, new_message):
381 target_list = ['idle', 'adcLoop', 'oneshot','calibrate']
382 for key in target_list:
383 if existing_property[key] == constants.SwitchState.ON:
384 current_state = key
385
386 if current_state not in new_message:
387
388 for key in target_list:
389 existing_property[key] = constants.SwitchState.OFF
390 if key in new_message:
391 existing_property[key] = new_message[key]
392
393 if key == 'idle':
394 self._state = States.IDLE
395 self.properties['fsm']['state'] = StateCodes.READY.name
396 #self._command = 0
397 self.log.debug('State changed to idle')
398 elif key == 'adcLoop':
399 self._state = States.CLOSED_LOOP
400 #self.update_wavelength()
401 self.properties['fsm']['state'] = StateCodes.OPERATING.name
402 self.log.debug('State changed to closed-loop')
403 elif key == 'oneshot':
404 self._state = States.ONESHOT
405 #self.update_wavelength()
406 self.properties['fsm']['state'] = StateCodes.OPERATING.name
407 self.log.debug('State changed to oneshot')
408 elif key == 'calibrate':
409 self._state = States.CALIB
410 #self.update_wavelength()
411 self.properties['fsm']['state'] = StateCodes.OPERATING.name
412 self.log.debug('State changed to calibration')
413
414 self.update_property(existing_property)
415 self.update_property(self.properties['fsm'])
416
417 def handle_labmode(self,existing_property, new_message):
418 if 'toggle' in new_message and new_message['toggle'] is constants.SwitchState.ON:
419 self.log.debug('changing to lab mode')
420 existing_property['toggle'] = constants.SwitchState.ON
421 self._lab = True
422 else:
423 self.log.debug('changing to onsky mode')
424 existing_property['toggle'] = constants.SwitchState.OFF
425 self._lab = False
426
427 self.update_property(existing_property)
428
429 def handle_knife_edge(self,existing_property, new_message):
430 if 'toggle' in new_message and new_message['toggle'] is constants.SwitchState.ON:
431 self.log.debug('changing into knife edge mode')
432 existing_property['toggle'] = constants.SwitchState.ON
433 self._knife_edge = True
434 else:
435 self.log.debug('exiting knife edge mode')
436 existing_property['toggle'] = constants.SwitchState.OFF
437 self._knife_edge = False
438
439 self.update_property(existing_property)
440
441 def handle_knife_edge_findzero(self,existing_property, new_message):
442 if 'request' in new_message and new_message['request'] is constants.SwitchState.ON:
443 self.log.debug('finding convergence point for use with knife edge ')
444 existing_property['request'] = constants.SwitchState.OFF
445
446 img = self.camera.grab_stack(self._n_avg)
447 transpose = img.shaped.T
448 img = transpose.ravel()
449
450 if self._lab == False:
451 img = self.ADC.filter_image(img)
452
453 self.ADC.set_psf(img)
454 angles = self.ADC.find_speckle_angles2()
455 self._knife_edge_zero1 = angles[1]
456 self._knife_edge_zero2 = angles[2]
457
458 self.log.debug(f'zero points for bottom speckles changed to {self._knife_edge_zero1} and {self._knife_edge_zero2}')
459 self.update_property(existing_property)
460
461 def handle_reset(self,existing_property, new_message):
462 if 'request' in new_message and new_message['request'] is constants.SwitchState.ON:
463 self.log.debug('resetting deltaADC properties')
464 existing_property['request'] = constants.SwitchState.OFF
465 self.set_command(0,0)
466 self.send_command()
467 self._command = 0
468
469 self.update_property(existing_property)
470
471 def handle_n_avg(self, existing_property, new_message):
472 if 'target' in new_message and new_message['target'] != existing_property['current']:
473 existing_property['current'] = new_message['target']
474 existing_property['target'] = new_message['target']
475 self._n_avg = int(new_message['target'])
476 self.log.debug(f'now averaging over {self._n_avg} frames')
477 self.update_property(existing_property)
478
479 def handle_no_measurements(self, existing_property, new_message):
480 if 'number' in new_message and new_message['number'] != existing_property['number']:
481 existing_property['number'] = new_message['number']
482 self._no_measurements = int(new_message['number'])
483 self.log.debug(f'now averaging {self._no_measurements} measurements before sending command')
484 self.update_property(existing_property)
485
486 def handle_gain(self, existing_property, new_message):
487 if 'target' in new_message and new_message['target'] != existing_property['current']:
488 existing_property['current'] = new_message['target']
489 existing_property['target'] = new_message['target']
490 self._gain = float(new_message['target'])
491 self.log.debug(f'loop gain changed to {self._gain}')
492 self.update_property(existing_property)
493
494 def handle_offset(self, existing_property, new_message):
495 if 'target' in new_message and new_message['target'] != existing_property['current']:
496 existing_property['current'] = new_message['target']
497 existing_property['target'] = new_message['target']
498 self._offset = float(new_message['target'])
499 self.log.debug(f'offset changed to {self._offset}')
500 self.send_command()
501 self.update_property(existing_property)
502
503 def handle_ctrl_mtx(self, existing_property, new_message):
504 old_matrix = self._control_mtx
505 if 'm00' in new_message and new_message['m00'] != existing_property['m00']:
506 existing_property['m00'] = new_message['m00']
507 self._control_mtx[0] = float(new_message['m00'])
508
509 if 'm01' in new_message and new_message['m01'] != existing_property['m01']:
510 existing_property['m01'] = float(new_message['m01'])
511 self._control_mtx[1] = new_message['m01']
512
513 self.log.debug(f'control matrix changed to {self._control_mtx}')
514 self.update_property(existing_property)
515
517 if self.client['fwsci1.filterName.i'] == constants.SwitchState.ON:
518 self._center_wavelength = 762E-9
519 self._extent = 400
520 elif self.client['fwsci1.filterName.z'] == constants.SwitchState.ON:
521 self._center_wavelength = 908E-9
522 self._extent = 480
523 self.log.debug('filter in zprime')
524 else:
525 self._center_wavelength = 656E-9
526 self._extent = 400
527
528 self.ADC.wavelength = self._center_wavelength
529 self.ADC.normalized_wavelength = self.ADC.wavelength / 6565E-9
530 self.log.debug(f'using center wavelength {self._center_wavelength*1E9} nm, ADC instance sees {self.ADC.wavelength} & {self.ADC.normalized_wavelength} normalized')
531
533 #self._command = 0
534 self.properties['state']['oneshot'] = constants.SwitchState.OFF
535 self.properties['state']['adcLoop'] = constants.SwitchState.OFF
536 self.properties['state']['calibrate'] = constants.SwitchState.OFF
537 self.properties['state']['idle'] = constants.SwitchState.ON
538 self.update_property(self.properties['state'])
539 self._state = States.IDLE
540
541 def set_command(self, d1, d2):
542 self.delta_1 = d1
543 self.delta_2 = d2
544
545 def add_command(self, d1,d2):
546 self.delta_1 += d1
547 self.delta_2 += d2
548
549 def send_command(self):
550 self.client['adctrack.deltaADC1.target'] = self.delta_1 + self.delta_2 + self._offset
551 self.client['adctrack.deltaADC2.target'] = self.delta_1 - self.delta_2 + self._offset
552
553 do_check = True
554 tolerance = 0.05
555 while do_check:
556
557 current_1 = self.client['adctrack.deltaADC1.current']
558 current_2 = self.client['adctrack.deltaADC2.current']
559
560 if abs(current_1 - self.delta_1 - self.delta_2 - self._offset) < tolerance and abs(current_2 - self.delta_1 + self.delta_2 - self._offset) < tolerance:
561 do_check = False
562
563 time.sleep(0.05)
564
565 def loop(self):
566 if self._state == States.CLOSED_LOOP:
567 measurements = []
568 error = 0
569
570 for i in range(self._no_measurements):
571 img = self.camera.grab_stack(self._n_avg)
572 transpose = img.shaped.T
573 img = transpose.ravel()
574
575 if self._lab == False:
576 img = self.ADC.filter_image(img)
577
578 img = self.ADC.crop_image(img,extent=self._extent,mask_diam=self._mask_diam)
579 self.ADC.set_psf(img)
580
581 if self._knife_edge:
582 angles = self.ADC.find_speckle_angles2()
583 bottom_speckle_angles = np.array([angles[1] - self._knife_edge_zero1 ,angles[2] - self._knife_edge_zero2])
584 self.log.debug(f'angle offsets: {angles}')
585 error = self.ADC.calculate_command(bottom_speckle_angles)
586 else:
587 angles = self.ADC.find_speckle_angles2()
588 pair_angles = self.ADC.speckle_pairs(angles)
589 self.log.debug(f'angle offsets: {angles}')
590 error = self.ADC.calculate_command(pair_angles)
591
592 self.log.debug(f'measured error: {error}')
593 measurements.append(error)
594 #self._command = np.squeeze(self._command + -self._gain * error)
595
596 error = np.mean(measurements)
597 self.log.debug(f'mean error: {error}')
598
599 if np.abs(error) < 2: #setting a threshold so the prisms don't do anything crazy
600 self.add_command(error * self._gain,0)
601 self.send_command()
602 self.log.debug(f'ADC command sent: {error * self._gain}')
603 else: self.log.info(f'ADC command {self._command} exceeds acceptable threshold and was not sent')
604
605 elif self._state == States.ONESHOT:
606 measurements = []
607 for i in range(self._no_measurements):
608 img = self.camera.grab_stack(self._n_avg)
609 transpose = img.shaped.T
610 img = transpose.ravel()
611
612 if self._lab == False:
613 img = self.ADC.filter_image(img)
614
615 img = self.ADC.crop_image(img,extent=self._extent,mask_diam=self._mask_diam)
616 self.ADC.set_psf(img)
617 #center_of_intensity = np.array([sum(img*img.grid.x)/sum(img),sum(img*img.grid.y)/sum(img)])
618 #self.log.info(f'center of intensity: {center_of_intensity}')
619
620 if self._knife_edge:
621 angles = self.ADC.find_speckle_angles2()
622 bottom_speckle_angles = np.array([angles[1],angles[2]])
623 self.log.debug(f'angle offsets: {angles}')
624 self._command = np.squeeze(self.ADC.calculate_command(bottom_speckle_angles))
625 else:
626 angles = self.ADC.find_speckle_angles2()
627 pair_angles = self.ADC.speckle_pairs(angles)
628 self.log.debug(f'angle offsets: {angles}')
629 self._command = np.squeeze(self.ADC.calculate_command(pair_angles))
630
631 measurements.append(self._command)
632 self.log.debug(f'single measurement command: {self._command}')
633
634 error = np.mean(measurements)
635 self.log.debug(f'average command: {error} (just calculated, not sent)')
636 #### deleting the send command part so you can use it without interfering with anyone else's stuff
637 # if np.abs(self._command) < 5: #setting a threshold so the prisms don't do anything crazy
638 # self.add_command(self._command,0)
639 # self.send_command()
640 # self.log.debug(f'ADC command sent: {self._command}')
641 # else:
642 # self.log.info(f'ADC command {self._command} exceeds acceptable threshold and was not sent')
643
644 self.log.info('transitioning to idle')
645 self.transition_to_idle()
646 self._command = 0
647 self.log.info('successfully transitioned to idle')
648
649 elif self._state == States.CALIB:
650 sweep_angles = np.linspace(-3,3,26)
651 diff_pointing_pairs = np.zeros((len(sweep_angles),2))
652
653 if self._knife_edge == False:
654 self.log.debug(f'calibrating in regular mode')
655 for i, orientation in enumerate(sweep_angles):
656 self.log.debug(f'Step {i:d}')
657 self.set_command(orientation, 0)
658 self.send_command()
659
660 img = self.camera.grab_stack(self._n_avg)
661 transpose = img.shaped.T
662 img = transpose.ravel()
663
664 if self._lab == False:
665 img = self.ADC.filter_image(img)
666
667 img = self.ADC.crop_image(img,extent=self._extent,mask_diam=self._mask_diam)
668 self.ADC.set_psf(img)
669
670 angles = self.ADC.find_speckle_angles2()
671 pointing_pair = self.ADC.speckle_pairs(angles)
672 diff_pointing_pairs[i,] = pointing_pair
673
674 # self.set_command(0,0)
675 # self.send_command()
676
677 # a1 = np.zeros(2)
678 # b1 = np.zeros(2)
679
680 # for j in range(2):
681 # b1[j] , a1[j] = np.polyfit(sweep_angles,diff_pointing_pairs[:,j],deg=1)
682
683 # response = np.matrix([b1])
684 # self.log.debug(f'response matrix: {response}')
685
686 # if np.isnan(np.sum(response)):
687 # self.log.info(f'calibration failed, measured response is NaN')
688 # self.transition_to_idle()
689 # else:
690 # new_control_mtx = np.linalg.pinv(response)
691
692 # self._control_mtx = new_control_mtx.T
693
694 else:
695 self.log.debug(f'calibrating in knife-edge mode')
696 for i, orientation in enumerate(sweep_angles):
697 self.log.debug(f'Step {i:d}')
698 self.set_command(orientation, 0)
699 self.send_command()
700
701 img = self.camera.grab_stack(self._n_avg)
702 transpose = img.shaped.T
703 img = transpose.ravel()
704
705 if self._lab == False:
706 img = self.ADC.filter_image(img)
707
708 img = self.ADC.crop_image(img,extent=self._extent,mask_diam=self._mask_diam)
709 self.ADC.set_psf(img)
710
711 angles = self.ADC.find_speckle_angles2()
712 bottom_speckle_angles = np.array([angles[1],angles[2]])
713 diff_pointing_pairs[i,] = bottom_speckle_angles
714
715 self.set_command(0,0)
716 self.send_command()
717
718 a1 = np.zeros(2)
719 b1 = np.zeros(2)
720
721 for j in range(2):
722 b1[j] , a1[j] = np.polyfit(sweep_angles,diff_pointing_pairs[:,j],deg=1)
723
724 response = np.matrix([b1])
725 self.log.debug(f'response matrix: {response}')
726
727 if np.isnan(np.sum(response)):
728 self.log.info(f'calibration failed, measured response is NaN')
729 self.transition_to_idle()
730 else:
731 new_control_mtx = np.linalg.pinv(response)
732
733 self._control_mtx = new_control_mtx.T
734 self.ADC.set_control_mtx(self._control_mtx)
735 self.log.info(f'calibration updated control matrix to: {self._control_mtx}')
736
737 self.properties['ctrl_mtx']['m00'] = self._control_mtx[0,0]
738 self.properties['ctrl_mtx']['m01'] = self._control_mtx[0,1]
739 self.update_property(self.properties['ctrl_mtx'])
740
741 self.transition_to_idle()
742
743
744
745
746
747
make_gaussian(self, mu_x, mu_y, sigma_x, sigma_y, orientation)
Definition adcCtrl.py:34
filter_image(self, img, low_freq=0.01, high_freq=1)
Definition adcCtrl.py:191
find_speckle_angles2(self)
Definition adcCtrl.py:109
__init__(self, wavelength=656E-9, bandwidth=100E-9, grating_angle=-28, grating_freq=47)
Definition adcCtrl.py:20
estimate_angle(self)
Definition adcCtrl.py:76
set_control_mtx(self, matrix)
Definition adcCtrl.py:159
current_speckle
boundary condition for the aspect ratio
Definition adcCtrl.py:29
cost(self, theta)
Definition adcCtrl.py:48
speckle_pairs(self, speckle_angles)
Definition adcCtrl.py:145
window_field(self, data, center, width, height)
Definition adcCtrl.py:163
set_measurement(self, data)
Definition adcCtrl.py:31
fit(self, theta_est)
Definition adcCtrl.py:64
crop_image(self, image, extent=400, mask_diam=60)
Definition adcCtrl.py:170
estimate_centroid(self)
Definition adcCtrl.py:68
set_psf(self, psf)
Definition adcCtrl.py:106
satellite_spot(self, amplitude, mu_x, mu_y, sigma_x, sigma_y, orientation, background)
Definition adcCtrl.py:43
find_speckle(self, image, speckle_number)
Definition adcCtrl.py:94
calculate_command(self, speckle_angles)
Definition adcCtrl.py:150
handle_gain(self, existing_property, new_message)
Definition adcCtrl.py:486
transition_to_idle(self)
Definition adcCtrl.py:532
send_command(self)
Definition adcCtrl.py:549
handle_ctrl_mtx(self, existing_property, new_message)
Definition adcCtrl.py:503
add_command(self, d1, d2)
Definition adcCtrl.py:545
handle_knife_edge_findzero(self, existing_property, new_message)
Definition adcCtrl.py:441
handle_no_measurements(self, existing_property, new_message)
Definition adcCtrl.py:479
handle_state(self, existing_property, new_message)
Definition adcCtrl.py:380
handle_knife_edge(self, existing_property, new_message)
Definition adcCtrl.py:429
update_wavelength(self)
Definition adcCtrl.py:516
AdcCtrlConfig config
Definition adcCtrl.py:226
handle_reset(self, existing_property, new_message)
Definition adcCtrl.py:461
handle_n_avg(self, existing_property, new_message)
Definition adcCtrl.py:471
handle_labmode(self, existing_property, new_message)
Definition adcCtrl.py:417
set_command(self, d1, d2)
Definition adcCtrl.py:541
handle_offset(self, existing_property, new_message)
Definition adcCtrl.py:494
_command
deleting the send command part so you can use it without interfering with anyone else's stuff if np....
Definition adcCtrl.py:345