def MEC_ISIO(): """ interfaces with the shared memory buffer of remote DM Here we create the interface between the shm and this code to apply new offsets for the remote DM. The interfacing is handled by pyMILK found https://github.com/milk-org/pyMilk. We also create the probe by calling beambar, and send that offset map to the shm. We read in the time the probe pattern was applied. Functinality exists to save the probe pattern and timestamp together, but it is unused for beammapping on MEC as of 6/4/20 so it is currently unused. :return: nothing explicitly returned but probe is applied (will persist on DM until it is externally cleared, eg by the RTC computer on SCExAO). Saving capability not currently implemented. """ # Create shared memory (shm) interface sp = ShmParams() MECshm = SHM( sp.shm_name) # create c-type interface using pyMilk's ISIO wrapper data = MECshm.get_data( ) # used to determine size of struct (removes chance of creating wrong probe size) # Create Probe bp = BarParams(data.shape) # import settings probe = beambar(bp, line_dir=bp.dir, center=bp.probe_center, debug=bp.debug) # create probe pattern MECshm.set_data(probe) # Apply Probe t_sent = MECshm.IMAGE.md.lastaccesstime # Read Time # Saving Probe and timestamp together ap = AppliedProbe(bp, probe, t_sent) return MECshm
def example_shm(shm_name="MECshm"): """ example from the isio.shm README found on github :param shm_name: name of the shared memory buffer (default is MECshm) :return: the struct that contains the shared memory buffer """ MECshm = SHM(shm_name) data = MECshm.get_data(check=True) # Wait for semaphore udate, then read # Writing to existing shm stream MECshm.set_data(np.random.rand(*MECshm.shape).astype(MECshm.nptype)) # Reading a RT stream ocam = SHM('ocam2d') output = ocam.multi_recv_data( 10000, outputFormat=1, # Puts everything in a 3D np.array monitorCount= True # Prints a synthesis of counters, ie likely missed frames ) print(output.shape) # 10000 x 120 x 120 print(output.dtype) # np.uint16 # Creating a brand new stream (e.g. 30x30 int16 images) shm_wr = SHM( 'shm_name', data, # 30x30 int16 np.array location=-1, # CPU shared=True, # Shared ) # Or with shape and type shm_wr = SHM('shm_name', ((30, 30), np.int16), ...) return MECshm
def __init__(self, publisher): super().__init__() # Define the attributes self._INITIALDIR = os.getcwd() self._connected = False self._pos = 0 * np.ones((core.NSEGMENTS, 3)) self._off = np.c_[np.ones((core.NSEGMENTS, 2)) * core.TIPTILTMIN, 0 * np.ones((core.NSEGMENTS, 1))] self._on = 0 * np.ones((core.NSEGMENTS, 3)) self.pub = publisher self.running = True self.milk_solution = True if self.milk_solution: # Prepare the maps to be ploted self._init_maps() self.flag_test = True # Prepare the shared memory self.data_plot = SHM( 'irisaoim', ((self.map_width, self.map_height), np.float32), location=-1, shared=True) # Start the mems self.start() else: self.connect() self.flat()
def send_flat(channel): """ sends a DM flat (array of 0's) to the DM channel specified SCExAO DM channel names have the format "dm00dispXX" where XX is the channel number. Generally CDI probes or any active nulling takes place on channel 06 or 07. I think 00 is the 'observed' DM flat (may not be an array of 0s due to voltage anomolies on the DM itself). :return: nothing explicitly returned but probe is applied (will persist on DM until it is externally cleared, eg by the RTC computer on SCExAO). """ # Create shared memory (shm) interface sp = ShmParams() # channel = sp.shm_name MECshm = SHM( channel) # create c-type interface using pyMilk's ISIO wrapper data = MECshm.get_data( ) # used to determine size of struct (removes chance of creating wrong probe size) # Create Flat flat = np.zeros((data.shape[0], data.shape[1]), dtype=np.float32) MECshm.set_data(flat) # Apply flat probe to get start time
def __init__(self, camera, publisher): Thread.__init__(self) self.pub = publisher self.cam = camera self.running = True self.data_ready = False self.live_pause = False self.camera_link = CAMERA_LINK self.width = WIDTH_IMAGE self.height = HEIGHT_IMAGE self.vbin = VERTICAL_BINNING self.hbin = HORIZONTAL_BINNING self.ReadMode = READ_MODE self.AcqMode = ACQUISITION_MODE self.ft_mode = FRAME_TRANSFER_MODE self.Lower_left_X = LOWER_LEFT_X self.Lower_left_Y = LOWER_LEFT_Y self.data = np.zeros( [np.int(self.width / self.hbin), np.int(self.height / self.vbin)]) self.dark = np.zeros( [np.int(self.width / self.hbin), np.int(self.height / self.vbin)]) self.rawdark = [] self.c_min = None self.c_max = None self.graphpoints = 200 self.PLTxdata = np.array([]) self.PLTydata = np.array([]) self.CROPsize = 3 self.CROPpos = [0, self.CROPsize * 2, 0, self.CROPsize * 2] self.CROPdata = np.zeros([self.CROPsize * 2, self.CROPsize * 2]) self.lastPhotArrayTRY = [] self.lastPhotArraySent = [] self.FLUX_V2PM = None self.FLUX_P2VM = None self.exposure_time = DEFAULT_EXP_TIME self.gain = DEFAULT_GAIN self.rawdata = np.zeros( [np.int((self.width / self.hbin) * (self.height / self.vbin))], dtype=np.float64) self.ixionim = SHM('ixionim', ((np.int(self.width / self.hbin), np.int(self.height / self.vbin)), np.float64), location=-1, shared=1) self.ixiondark = SHM('ixiondark', ((np.int(self.width / self.hbin), np.int(self.height / self.vbin)), np.float64), location=-1, shared=1)
class AndorCtrl(Thread): def __init__(self, camera, publisher): Thread.__init__(self) self.pub = publisher self.cam = camera self.running = True self.data_ready = False self.live_pause = False self.camera_link = CAMERA_LINK self.width = WIDTH_IMAGE self.height = HEIGHT_IMAGE self.vbin = VERTICAL_BINNING self.hbin = HORIZONTAL_BINNING self.ReadMode = READ_MODE self.AcqMode = ACQUISITION_MODE self.ft_mode = FRAME_TRANSFER_MODE self.Lower_left_X = LOWER_LEFT_X self.Lower_left_Y = LOWER_LEFT_Y self.data = np.zeros( [np.int(self.width / self.hbin), np.int(self.height / self.vbin)]) self.dark = np.zeros( [np.int(self.width / self.hbin), np.int(self.height / self.vbin)]) self.rawdark = [] self.c_min = None self.c_max = None self.graphpoints = 200 self.PLTxdata = np.array([]) self.PLTydata = np.array([]) self.CROPsize = 3 self.CROPpos = [0, self.CROPsize * 2, 0, self.CROPsize * 2] self.CROPdata = np.zeros([self.CROPsize * 2, self.CROPsize * 2]) self.lastPhotArrayTRY = [] self.lastPhotArraySent = [] self.FLUX_V2PM = None self.FLUX_P2VM = None self.exposure_time = DEFAULT_EXP_TIME self.gain = DEFAULT_GAIN self.rawdata = np.zeros( [np.int((self.width / self.hbin) * (self.height / self.vbin))], dtype=np.float64) self.ixionim = SHM('ixionim', ((np.int(self.width / self.hbin), np.int(self.height / self.vbin)), np.float64), location=-1, shared=1) self.ixiondark = SHM('ixiondark', ((np.int(self.width / self.hbin), np.int(self.height / self.vbin)), np.float64), location=-1, shared=1) def run(self): while self.running: if not self.live_pause: while True: self.cam.GetMostRecentImage(self.rawdata) getimerr = self.cam.GetMostRecentImage_error if getimerr == 20002 or self.live_pause: break time.sleep(self.cam.accu_cycle_time + self.exposure_time) # Write the image in the shared memory self.ixionim.set_data( np.reshape(self.cam.imageArray, (np.int(self.height / self.vbin), np.int(self.width / self.hbin) ))) ## Somehow width and height are inverted self.data_ready = True def update(self): if self.data_ready: #self.data_ready = False return self.data def set_shutter_CLOSED(self): ''' Sets the internal camera shutter to CLOSED position ''' self.cam.AbortAcquisition() time.sleep(0.5) self.cam.SetShutter(0, 2, 300, 100) time.sleep(0.5) self.cam.StartAcquisition() def set_shutter_OPEN(self): ''' Sets the internal camera shutter to OPEN position ''' self.cam.AbortAcquisition() time.sleep(0.5) self.cam.SetShutter(0, 1, 300, 100) time.sleep(0.5) self.cam.StartAcquisition() def wait_for_idle(self, maxwaittime=10): t0 = time.time() while ((time.time() - t0) <= maxwaittime): time.sleep(0.1) self.cam.GetStatus() if self.cam.status == "DRV_IDLE": # 20073: not acquiring return self.cam.status return self.cam.status def set_camera(self, camera): ''' This command lets the live viewer know which camera to talk to. This function probably shouldn't be changed, but can be modified if we whish to use the same code for different camera. It also passess all the dll camera commands into the viewer so the camera can be controlled from this thread. ''' self.cam = camera def get_hardware_info(self): get_hardware_info = self.cam.GetHardwareVersion() self.pub.pprint(get_hardware_info) self.pub.pprint('Firmware Version :' + str(self.cam.CameraFirmwareVersion)) self.pub.pprint('Firmware Built :' + str(self.cam.CameraFirmwareBuild)) def get_software_info(self): get_software_info = self.cam.GetSoftwareVersion() self.pub.pprint(get_software_info) self.pub.pprint('Driver Version :' + str(self.cam.vxdVer)) self.pub.pprint('DLL Version :' + str(self.cam.dllVer)) #------------------------------------------------------------------------- # Start / Stop live camera #------------------------------------------------------------------------- def play(self): # self.live_pause = False def pause(self): # self.live_pause = True def start(self): ''' Starts the video display. ''' if self.cam is None: raise Exception("Camera not connected!") self.get_hardware_info() self.get_software_info() self.cam.SetReadMode(self.ReadMode) self.cam.SetAcquisitionMode(self.AcqMode) self.cam.SetFrameTransferMode(self.ft_mode) self.cam.SetKineticCycleTime(0) # Does nothing: returns DRV_NOT_AVAILABLE - feature is not supported - firmware/driver update maybe ? # self.cam.SetIsolatedCropMode(1, self.height, self.width, self.vbin, self.hbin) # self.set_camera_link_mode(0) self.cam.SetImage(self.hbin, self.vbin, self.Lower_left_X, self.Lower_left_X + np.int(self.width) - 1, self.Lower_left_Y, self.Lower_left_Y + np.int(self.height) - 1) #self.cam.SetImage( ??bin , SPECTRAL_bin, ??start, OPD_DIM, ??, SPECTRAL_DIM) self.cam.SetShutter(0, 1, 50, 50) self.cam.SetExposureTime(self.exposure_time) self.cam.GetEMGainRange() #self.cam.GetEMCCDGain() if self.gain > self.cam.gainRange[1]: self.pub.pprint( "WARNING : Gain of " + str(self.gain) + " is too high. You need to lower your ambition ;-) ") self.pub.pprint("Highest available gain: " + str(self.cam.gainRange[1])) self.pub.pprint("Gain value changed to 0,") self.gain = 0 self.cam.SetEMCCDGain(self.gain) self.set_camera_link_mode(self.camera_link) self.cam.GetAcquisitionTimings() self.cam.GetEMCCDGain() self.pub.pprint("Actual exposure time: " + str(self.cam.exp_time)) #self.pub.pprint("Actual accu cycle time: "+str(self.cam.accu_cycle_time)) self.pub.pprint("Actual kine cycle time: " + str(self.cam.kinetic_cycle_time)) self.pub.pprint("Camera gain: " + str(self.cam.gain)) self.pub.pprint("\n") #self.pub.pprint("Camera Gain: ") #self.pub.pprint(str(self.gain)) self.cam.GetNumberPreAmpGains() self.cam.GetPreAmpGain() self.cam.GetStatus() self.cam.StartAcquisition() super().start() os.system('shmImshow.py ixionim &') self.pub.pprint("Andor iXon Initialised\n") def stop(self): self.running = False self.cam.AbortAcquisition() self.cam.SetShutter(0, 2, 300, 100) self.cam.ShutDown() os.system("pkill -f 'shmImshow.py ixionim'") self.join() #------------------------------------------------------------------------- # Temperature control #------------------------------------------------------------------------- def GetCurrentTemperature(self): self.cam.GetTemperature() self.pub.pprint("Current temperature is: " + str(self.cam.temperature) + "°C\n") return 0 def SetDetTemperature(self, temperature): self.cam.GetTemperatureRange() if temperature < self.cam.min_temp or temperature > self.cam.max_temp: self.pub.pprint("!!!Wrong temperature set!!!") self.pub.pprint("Set it again.") self.pub.pprint("Valid temperature is in the range [%d, %d].\n" % (self.cam.min_temp, self.cam.max_temp)) return 0 self.cam.SetTemperature(temperature) #------------------------------------------------------------------------- # Acquisition Mode #------------------------------------------------------------------------- def set_single_scan(self): ''' Set the acquisition mode to "Fixed" (takes one image). ''' #self.cam.SetSingleScan() self.cam.SetReadMode(4) self.cam.SetAcquisitionMode(1) self.cam.SetImage(1, 1, 1, self.width, 1, self.height) def set_video_scan(self): ''' Set the acquisition mode to "Continuous" (takes one image). ''' self.cam.SetReadMode(4) self.cam.SetAcquisitionMode(5) self.cam.SetKineticCycleTime(0) self.cam.SetImage(1, 1, 1, self.height, 1, self.width) #self.cam.SetVideoScan() def set_frame_series(self): self.cam.SetReadMode(4) self.cam.SetAcquisitionMode(3) self.cam.SetImage(1, 1, 1, self.width, 1, self.height) def set_series_scan_param(self, N_acc, Nframes, acc_cycle_time, KinCyclTime): """ Sets the Parameters needed for taking a datacube. Number entered needs to be the number of frames. Can also change the kinectic cycle time by writing 'KinCyclTime=##' in (s). """ self.cam.SetReadMode(4) self.cam.SetAcquisitionMode(3) self.cam.SetNumberAccumulations(N_acc) self.cam.SetAccumulationCycleTime(acc_cycle_time) self.cam.SetNumberKinetics(Nframes) self.numberframes = N_acc * Nframes self.cam.SetKineticCycleTime(KinCyclTime) def get_AcquisitionTimings(self): self.cam.GetAcquisitionTimings() self.pub.pprint("Actual exposure time is: " + str(self.cam.exp_time)) self.pub.pprint("Actual accumulation cycle time is: " + str(self.cam.accu_cycle_time)) self.pub.pprint("Actual kinetic cycle time time is: " + str(self.cam.kinetic_cycle_time)) ## ADD MULTI-TRACK and RANDOM-TRACK FUNCTIONS HERE ## in progress... def set_multi_track(self, number, height, offset, bottom, gap): self.cam.SetReadMode(1) self.cam.SetMultiTrack(number, height, offset, bottom, gap) def set_camera_link_mode(self, mode): #if mode == 0: # self.pub.pprint("Camera link is set off") #if mode == 1: # self.pub.pprint("Camera link is set on") Set = self.cam.SetCameraLinkMode(mode) if self.cam.set_camera_link_mode_error == 20002: if mode == 0: self.pub.pprint("Camera link is set off") if mode == 1: self.pub.pprint("Camera link is set on") else: self.pub.pprint( "Camera link mode : Something went wrong. Error code =" + str(self.cam.set_camera_link_mode_error)) def get_bits(self): self.cam.GetBitDepth() nbits = self.cam.bits self.pub.pprint("Number of bits is: " + str(nbits)) #------------------------------------------------------------------------- # Exposure time // Gain #------------------------------------------------------------------------- def set_exptime(self, exptime): ''' Sets the camera exposure time in SECONDS. Example: a.set_exptime(0.01) the default exposure time at initialisation is set by a global variable: DEFAULT_EXP_TIME and is set to 0.001 - 1ms (1kHz) Executing this command will pause the video link while it updates then reactivate it. *WARNING* - Careful when subtracting darks as they will no longer match. ''' self.exposure_time = exptime self.cam.GetEMCCDGain() gain = self.cam.gain # Stop the acquisition self.cam.AbortAcquisition() # Set the Read mode and the acquisition mode self.cam.SetReadMode(self.ReadMode) self.cam.SetAcquisitionMode(self.AcqMode) self.cam.SetKineticCycleTime(0) # Set the Image parameters self.cam.SetIsolatedCropMode(1, self.height, self.width, self.vbin, self.hbin) self.cam.SetImage(self.hbin, self.vbin, self.Lower_left_X, self.Lower_left_X + np.int(self.width) - 1, self.Lower_left_Y, self.Lower_left_Y + np.int(self.height) - 1) self.cam.SetExposureTime(exptime) self.cam.SetShutter(0, 1, 50, 50) self.cam.GetEMGainRange() self.cam.GetNumberPreAmpGains() self.cam.GetPreAmpGain() self.cam.SetEMCCDGain(self.gain) self.cam.GetStatus() self.cam.GetAcquisitionTimings() self.cam.GetEMCCDGain() self.pub.pprint("Actual exposure time: " + str(self.cam.exp_time)) #self.pub.pprint("Actual accu cycle time: "+str(self.cam.accu_cycle_time)) self.pub.pprint("Actual kine cycle time: " + str(self.cam.kinetic_cycle_time)) self.pub.pprint("Gain: " + str(self.cam.gain)) self.cam.StartAcquisition() def get_exptime(self): #exp_time = self.cam.GetExpTime() self.cam.GetAcquisitionTimings() self.pub.pprint("Exposure time is %f s." % self.cam.exp_time) self.exposure_time = self.cam.exp_time return self.cam.exp_time def set_gain(self, gain): ''' TO DO ''' self.cam.GetEMGainRange() if gain > self.cam.gainRange[1]: self.pub.pprint( "WARNING : Gain of " + str(self.gain) + " is too high. You need to lower your ambition ;-) ") self.pub.pprint("Highest available gain: " + str(self.cam.gainRange[1])) else: self.gain = gain self.cam.GetAcquisitionTimings() exptime = self.cam.exp_time # Stop the acquisition self.cam.AbortAcquisition() # Set the Read mode and the acquisition mode self.cam.SetReadMode(self.ReadMode) self.cam.SetAcquisitionMode(self.AcqMode) self.cam.SetKineticCycleTime(0) # Set the Image parameters self.cam.SetIsolatedCropMode(1, self.height, self.width, self.vbin, self.hbin) self.cam.SetImage(self.hbin, self.vbin, self.Lower_left_X, self.Lower_left_X + np.int(self.width) - 1, self.Lower_left_Y, self.Lower_left_Y + np.int(self.height) - 1) self.cam.SetExposureTime(exptime) self.cam.SetShutter(0, 1, 50, 50) self.cam.GetEMGainRange() self.cam.GetNumberPreAmpGains() self.cam.GetPreAmpGain() self.cam.SetEMCCDGain(self.gain) self.cam.GetStatus() self.cam.GetAcquisitionTimings() self.cam.GetEMCCDGain() self.pub.pprint("Actual exposure time: " + str(self.cam.exp_time)) #self.pub.pprint("Actual accu cycle time: "+str(self.cam.accu_cycle_time)) self.pub.pprint("Actual kine cycle time: " + str(self.cam.kinetic_cycle_time)) self.pub.pprint("Gain: " + str(self.cam.gain)) self.cam.StartAcquisition() def get_gain(self): #exp_time = self.cam.GetExpTime() self.cam.GetEMCCDGain() self.pub.pprint("Camera gain is %f ." % self.cam.gain) self.gain = self.cam.gain return self.cam.gain #------------------------------------------------------------------------- # Horizontal / Vertical speed #------------------------------------------------------------------------- def get_number_vs_speeds(self): self.cam.GetNumberVSSpeeds() self.number_vs_speeds = self.cam.number_vs_speeds self.pub.pprint("Number of vs speeds is %d.\n" % self.number_vs_speeds) def get_number_hs_speeds(self): self.cam.GetNumberHSSpeeds() self.number_hs_speeds = self.cam.number_hs_speeds self.pub.pprint("Number of hs speeds is %d.\n" % self.number_hs_speeds) def get_vs_speed(self): self.cam.GetVSSpeed() self.vs_speed = self.cam.vs_speed self.pub.pprint("VS speed is %d.\n" % self.vs_speed) def get_hs_speed(self): self.cam.GetHSSpeed() self.hs_speed = self.cam.hs_speed self.pub.pprint("HS speed is %d.\n" % self.hs_speed) def set_vs_speed(self, index): # self.cam.SetVSSpeed(index) def set_hs_speed(self, index): # self.cam.SetHSSpeed(index) def get_number_vertical_speeds(self): self.cam.GetNumberVerticalSpeeds() self.number_vertical_speeds = self.cam.number_vertical_speeds self.pub.pprint("Number of vertical speeds is %d.\n" % self.number_vertical_speeds) def get_number_horizontal_speeds(self): self.cam.GetNumberHorizontalSpeeds() self.number_horizontal_speeds = self.cam.number_horizontal_speeds self.pub.pprint("Number of horizontal speeds is %d.\n" % self.number_horizontal_speeds) def get_vertical_speed(self): self.cam.GetVerticalSpeed() self.index_vertical_speed = self.cam.index_vertical_speed self.vertical_speed = self.cam.vertical_speed self.pub.pprint("Vertical speed index is %d.\n" % self.index_vertical_speed) self.pub.pprint("Vertical speed is %d.\n" % self.vertical_speed) def get_horizontal_speed(self): self.cam.GetHorizontalSpeed() self.index_horizontal_speed = self.cam.index_horizontal_speed self.horizontal_speed = self.cam.horizontal_speed self.pub.pprint("Horizontal speed index is %d.\n" % self.index_horizontal_speed) self.pub.pprint("Horizontal speed is %d.\n" % (self.horizontal_speed)) def set_vertical_speed(self, index): # self.cam.SetVerticalSpeed(index) def set_horizontal_speed(self, index): # self.cam.SetHorizontalSpeed(index) #------------------------------------------------------------------------- # Display options #------------------------------------------------------------------------- def set_clims(self, c_min, c_max): ''' Function to set the colour limits of the andor video display. Format: set_clims(intiger1, intiger2) - 2>1 always otherwise will give error. Example: a.set_clims(0,10000) - locks between 0 and 10k counts To make auto scaling colourmap, set both variables to 'None' a.set_clims(None,None) The video display should update in realtime your changes. ''' if c_min > c_max: raise ValueError( "c_min > c_max, c_max has to be higher than c_min.") self.c_min = c_min self.c_max = c_max #------------------------------------------------------------------------- # Data / Darks acquisition #------------------------------------------------------------------------- def acq_dark(self): self.cam.SetShutter(0, 2, 50, 50) time.sleep(0.2) self.cam.GetMostRecentImage(self.rawdata) self.ixiondark.set_data( np.reshape( self.cam.imageArray, (np.int(self.height / self.vbin), np.int( self.width / self.hbin)))) ## Somehow width and height are inverted self.cam.SetShutter(0, 1, 50, 50) def acq_cube(self, N_frames, filename=None): exptime = self.get_exptime() if filename is None: final_filename = str(int(1000 * time.time())) + "_datacube" else: final_filename = filename os.system( 'python /home/first/Documents/lib/firstctrl/FIRST_Ixion/ixionSaveCube.py ' + str(N_frames) + ' ' + str(exptime) + ' ' + str(self.cam.gain) + ' ' + str(self.cam.temperature) + ' ' + str(final_filename) + ' ' + str(SAVEFILEPATH)) def acq_cube_multi(self, N_cubes, N_frames, filename=None): for i in range(N_cubes): print('Acquisition of cube ' + str(i + 1)) if filename is None: final_filename = str(int( 1000 * time.time())) + "_datacube_" + str(i) else: final_filename = filename + '_' + str(i) self.acq_cube(N_frames, filename=final_filename) def acq_wavelength_cal(self, wavelength): if wavelength == 'Darks': self.pub.pprint("Starting darks for wavelength calibration") date = datetime.datetime.today().strftime('%Y%m%d') self.cam.SetShutter(0, 2, 50, 50) time.sleep(0.2) self.acq_cube(50, filename=date + '_WaveCals_Darks_0') self.cam.SetShutter(0, 1, 50, 50) else: self.pub.pprint("Starting calibration for " + str(wavelength) + " nm") date = datetime.datetime.today().strftime('%Y%m%d') self.acq_cube(50, filename=date + '_WaveCals_' + str(wavelength) + 'nm_0') def acq_dark_old(self): ''' Acquires a new dark frame for automatic subtraction on the video feed. The Dark is taken of whatever is on the detector at the time so make sure you turn the source off. Example: a.acq_new_dark() This should be re-taken whenever you change exposure time. It will pause the video feed while it does this and keep it internally in a variable called 'a.dark' ''' self.cam.AbortAcquisition() time.sleep(0.5) self.rawdark = [] self.set_single_scan() self.cam.StartAcquisition() time.sleep(0.2) self.cam.GetMostRecentImage(self.rawdark) self.dark = np.reshape(self.rawdark, (self.height, self.width)) self.cam.AbortAcquisition() self.ixiondark.set_data(self.dark.astype(np.float32)) self.set_video_scan() time.sleep(0.2) self.cam.StartAcquisition() time.sleep(0.2) def acq_cube_old(self, N_frames, exptime, filename=None): self.set_exptime(exptime) ## WARNIING : WIDTH AND HEIGHT HAVE BEEN SWAPPED BECAUSE I TAKE TRANSPOSE OF EACH IMAGE #imCube = np.zeros((N_frames, self.width, self.height)) #count = 0 #while count < N_frames: imCube = [] for i in tqdm(range(N_frames)): img = np.zeros([self.width * self.height]) self.cam.GetMostRecentImage(img) #img=[] #self.cam.GetMostRecentImage(img) getimerr = self.cam.GetMostRecentImage_error if getimerr == 20002: #imCube[count,:,:] = np.transpose(np.reshape(self.cam.imageArray, (self.height, self.width))) imCube.append( np.transpose( np.reshape(self.cam.imageArray, (self.height, self.width)))) #count = count+1 time.sleep(0.1 + exptime) imCube = np.array(imCube) if filename is None: final_filename = str(int(1000 * time.time())) + "_datacube" else: final_filename = filename self.pub.pprint('Acquisition over...') header = fits.Header() header['ExpTime'] = np.round(exptime, decimals=3) header['Gain'] = self.cam.gain header['Temp'] = self.cam.temperature fits.writeto(SAVEFILEPATH + final_filename + '.fits', imCube, header, overwrite=True) self.pub.pprint("Image saved in '" + SAVEFILEPATH + final_filename + ".fits'\n") os.system("ds9 " + SAVEFILEPATH + final_filename + ".fits &")
from pyMilk.interfacing.isio_shmlib import SHM import numpy as np # 50x50 input try: dmvolt = SHM('dmvolt') except: dmvolt = SHM('dmvolt', np.zeros((50,50), dtype=np.uint16), shared=1, location=-1) # 4096 linear output dmout = SHM('dmvolt1', symcode=0) # Row/Col mappings dm_start_row = [18,15,13,11,10, 8, 7, 6, 5, 5, 4, 3, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8,10,11,13,15,18] dm_end_row = [31,34,36,38,39,41,42,43,44,44,45,46,46,47,47,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,47,47,46,46,45,44,44,43,42,41,39,38,36,34,31] pupil = np.zeros((50,50), dtype=np.bool) for r in range(50): for c in range(50): if r >= dm_start_row[c] and r <= dm_end_row[c]: pupil[r,c] = True # To flatten dmvolt_map = dmvolt.get_data() dmvolt_map_flat = dmvolt_map.flatten() dm_optical_lut = np.array([
N_frames = np.int(sys.argv[1]) exptime = np.float32(sys.argv[2]) gain = np.int(sys.argv[3]) temperature = np.int(sys.argv[4]) file_name = sys.argv[5] SAVEFILEPATH = sys.argv[6] date = datetime.datetime.today().strftime('%Y%m%d') save_dir = SAVEFILEPATH + date + '/' if not os.path.exists(save_dir): os.makedirs(save_dir) print(str(N_frames) + ' Frames ' + str(exptime) + ' ms each') # Defines the shared mem where the images are + create image cube im = SHM('ixionim') imCube = im.multi_recv_data(N_frames, outputFormat=1) #imCube = np.array(imCube) imCube = imCube.transpose(0, 2, 1) print('Cube acquisition over...') # Save and display the cube header = fits.Header() header['ExpTime'] = np.round(exptime, decimals=3) header['Gain'] = gain header['Temp'] = temperature fits.writeto(save_dir + file_name + '.fits', imCube, header, overwrite=True) print("Image saved in '" + save_dir + file_name + ".fits'\n") #os.system("ds9 " + save_dir + file_name + ".fits &")
def get_mask(): # use maps calculated from aol0_dmmap dmmask = SHM("aol0_dmmask").get_data(check=False) dmslaved = SHM("aol0_dmslaved").get_data(check=False) mask = dmmask.astype(bool) ^ dmslaved.astype(bool) # XOR return mask
def loop_offset(N, plot=True, maxiter=np.inf, **kwargs): # stream = SHM("aol0_modeval_ol") stream = SHM("dm00disp10") basis = SHM("aol0_DMmodes").get_data(check=False) mask = get_mask() offsets = [] if plot: fig, axs = plt.subplots(2, 2) axs[0, 0].set_xlabel(r"$\Delta x$") axs[0, 1].set_xlabel(r"$\Delta y$") axs[1, 0].set_xlabel(r"$\Delta r$") axs[1, 1].set_xlabel(r"$\theta$") plt.tight_layout() i = 0 # previous_modes = stream.get_data(check=True) # make sure to get data so semID exists # previous_frame = basis @ previous_modes logger.info("starting loop") while i < maxiter: # previous_frame = stream.get_data(check=True) # # wait for N frames (TODO async) # stream.IMAGE.semflush(stream.semID) # stream.IMAGE.semwait(stream.semID) # cnt = stream.IMAGE.md.cnt0 # print(cnt) # for _ in range(N - 1 - (cnt % N)): # stream.IMAGE.semwait(stream.semID) # current_modes = stream.get_data(check=True) # # calculate open-loop reconstruction # current_frame = basis @ current_modes # current_frame = stream.get_data(check=True) cube = stream.multi_recv_data(N + 1, outputFormat=1) previous_frame = cube[0] current_frame = cube[-1] # show frames plt.figure(fig_frames.number) frames[0].imshow(previous_frame * mask, origin="lower") frames[1].imshow(current_frame * mask, origin="lower") plt.pause(1e-5) # make offset measurement x, y, r, theta = measure_shift(previous_frame, current_frame, mask=mask) x /= N y /= N r /= N # convert to average shift per frame logger.info( f"{i:03d}: x={x} m/s, y={y} m/s, r={r} m/s, theta={theta} deg") offsets.append(dict(x=x, y=y, r=r, theta=theta)) # plotting if plot: axs[0, 0].scatter(i, x, c="C0") axs[0, 1].scatter(i, y, c="C1") axs[1, 0].scatter(i, r, c="C2") axs[1, 1].scatter(i, theta, c="C3") # fig.canvas.copy_from_bbox(ax.bbox) # blitting plt.figure(fig.number) plt.pause(1e-5) # loop updates i += 1 previous_frame = current_frame return offsets
class Mems(Thread): def __init__(self, publisher): super().__init__() # Define the attributes self._INITIALDIR = os.getcwd() self._connected = False self._pos = 0 * np.ones((core.NSEGMENTS, 3)) self._off = np.c_[np.ones((core.NSEGMENTS, 2)) * core.TIPTILTMIN, 0 * np.ones((core.NSEGMENTS, 1))] self._on = 0 * np.ones((core.NSEGMENTS, 3)) self.pub = publisher self.running = True self.milk_solution = True if self.milk_solution: # Prepare the maps to be ploted self._init_maps() self.flag_test = True # Prepare the shared memory self.data_plot = SHM( 'irisaoim', ((self.map_width, self.map_height), np.float32), location=-1, shared=True) # Start the mems self.start() else: self.connect() self.flat() def __enter__(self): return self def __del__(self): self.flat() self.disconnect() __exit__ = __del__ def _pprint(self, message): ''' Print message with a color define in the class PColors. ''' self.pub.pprint(PColors.MEMS + str(message) + PColors.ENDC) def start(self): # Start the mems self.connect() self.flat() # Start the py-milk live feed os.system("shmImshow.py irisaoim &") # Push the initial opd map self._init_figure() # Start the Thread super().start() def stop(self): self._pprint(" Closing mems...\n") time.sleep(1) self.running = False time.sleep(1) self.join() def connect(self): """ Connects to the Mems """ # already connected if self._connected: return 0 # hack to CD to the folder with the cal files os.chdir(core.PATHCALMEMS) disableHardware = False self._mirror = IrisAO_API.MirrorConnect(core.MIRRORNUM, core.DRIVERNUM, disableHardware) # CD to previous folder os.chdir(self._INITIALDIR) self._connected = True def disconnect(self): """ Disconnects the Mems """ if not self._connected: self._pprint("ERROR: Not connected to Mems") return 0 self.flat() IrisAO_API.MirrorRelease(self._mirror) self._connected = False def exit(self): """ Disconnects the Mems """ self.disconnect() @property def connected(self): """ If there is an active link to the Mems """ return self._connected @connected.setter def connected(self, value): self._pprint("Read-only") @property def first_nseg(self): return len(core.FIRSTSEGS) @first_nseg.setter def first_nseg(self, value): self._pprint('Read-only') @property def first_seg(self): """ The favorite segments of First """ return core.FIRSTSEGS @first_seg.setter def first_seg(self, value): self._pprint('Read-only') def flat(self): """ Sets all tip, tilt, piston to nil """ if not self._connected: self._pprint("ERROR: Not connected to Mems") return 0 IrisAO_API.MirrorCommand(self._mirror, IrisAO_API.MirrorInitSettings) self._pos = np.zeros((core.NSEGMENTS, 3)) def _moveit(self, arr, elm): elm, sz = self._clean_segment(elm) if elm is None: self._pprint( "Wrong input, should be int, list of int, 'first', or 'all'") return 0 piston, tip, tilt = arr[:, :][core.mask_elm(elm)].T self.set_pos(elm=elm, piston=piston, tip=tip, tilt=tilt) def off(self, elm='all'): """ Sets all piston to nil; sets tip & tilt to min range """ self._moveit(self._off, elm) def on(self, elm='first'): """ Sets all piston to nil; sets tip & tilt to min range """ self._moveit(self._on, elm) def _clean_segment(self, elm): if isinstance(elm, int): return [elm], 1 elif isinstance(elm, str): if elm.lower() == 'first': elm = core.FIRSTSEGS elif elm.lower() == 'all': elm = [i for i in range(1, core.NSEGMENTS + 1)] elif hasattr(elm, '__iter__'): elm = [ item for item in core.clean_list(elm) if 0 < item <= core.NSEGMENTS ] else: return None, None return elm, len(elm) def get_pos(self, elm): """ Gets the positions of the mems segments ex: tip, tilt, piston = m.get_pos('first') Input argument elm can be: * int -> 1 segment * list of int -> n segment * 'first' -> the first segments * 'all' -> all segments """ if not self._connected: self._pprint("ERROR: Not connected to Mems") return 0 elm, sz = self._clean_segment(elm) if elm is None: self._pprint( "Wrong input, should be int, list of int, 'first', or 'all'") return 0 # (tip, tilt, piston), locked, reachable return np.asarray(IrisAO_API.\ GetMirrorPosition(self._mirror, elm)[0]).T def set_pos(self, elm, piston=None, tip=None, tilt=None): """ Sets the positions of the mems segments Input argument elm can be: * int -> 1 segment * list of int -> n segment * 'first' -> the first segments * 'all' -> all segments tip, tilt, piston can be left to None to remain unchanged if not None, tip, tilt and piston should be a list of floats with same size as elm """ if not self._connected: self._pprint("ERROR: Not connected to Mems") return elm, sz = self._clean_segment(elm) # check input if piston is None: piston = self._pos[:, 0][core.mask_elm(elm)] elif np.size(piston) != sz: self._pprint('Wrong size, should be same as elm: {}'.format(sz)) return piston = core.clean_pos(piston, ax='piston') if tip is None: tip = self._pos[:, 1][core.mask_elm(elm)] elif np.size(tip) != sz: self._pprint('Wrong size, should be same as elm: {}'.format(sz)) return tip = core.clean_pos(tip, ax='tiptilt') if tilt is None: tilt = self._pos[:, 2][core.mask_elm(elm)] elif np.size(tilt) != sz: self._pprint('Wrong size, should be same as elm: {}'.format(sz)) return tilt = core.clean_pos(tilt, ax='tiptilt') new_val = np.vstack((piston, tip, tilt)).T self._pos[core.mask_elm(elm), :] = new_val new_val = [tuple(item) for item in new_val] # print(new_val) # replace in local values IrisAO_API.SetMirrorPosition(self._mirror, elm, new_val) IrisAO_API.MirrorCommand(self._mirror, IrisAO_API.MirrorSendSettings) def _shape_save(self, name, arr, override): if not self._connected: self._pprint("ERROR: Not connected to Mems") return if os.path.isfile(name) and not bool(override): self._pprint("File '{}' already exists".format(name)) return np.savetxt(name, arr, header="PISTON, TIP, TILT") self._pprint("Saved in '{}'".format(name)) def shape_save(self, name, override=False): """ Saves the current shape into a file Args: * name (str): the name of the file * override (bool): whether to override a file if already existing """ name = core.make_filepath(name, core.SHAPEFILENAME) self._pos = self.get_pos('all').T self._shape_save(name, self._pos, override) def shape_on_save(self, name, override=False): """ Saves the current shape ON into a file Args: * name (str): the name of the file * override (bool): whether to override a file if already existing """ name = core.make_filepath(name, core.SHAPEONFILENAME) self._shape_save(name, self._pos, override) self._on = self._pos.copy() def shape_off_save(self, name, override=False): """ Saves the current shape OFF into a file Args: * name (str): the name of the file * override (bool): whether to override a file if already existing """ name = core.make_filepath(name, core.SHAPEOFFFILENAME) self._shape_save(name, self._pos, override) self._off = self._pos.copy() def shape_list(self): """ Shows all available shape files saved """ self._pprint("\n".join(core.list_filepath(core.SHAPEFILENAME))) def shape_on_list(self): """ Shows all available shape ON files saved """ self._pprint("\n".join(core.list_filepath(core.SHAPEONFILENAME))) def shape_off_list(self): """ Shows all available shape OFF files saved """ self._pprint("\n".join(core.list_filepath(core.SHAPEOFFFILENAME))) def _shape_delete(self, name): if os.path.isfile(name): os.remove(name) self._pprint("Removed: '{}'".format(name)) else: self._pprint("File '{}' not found".format(name)) def shape_delete(self, name): """ Deletes a shape file saved Args: * name (str): the name of the file """ self._shape_delete(core.make_filepath_nostamp(name, core.SHAPEFILENAME)) def shape_on_delete(self, name): """ Deletes a shape ON file saved Args: * name (str): the name of the file """ self._shape_delete( core.make_filepath_nostamp(name, core.SHAPEONFILENAME)) def shape_off_delete(self, name): """ Deletes a shape OFF file saved Args: * name (str): the name of the file """ self._shape_delete( core.make_filepath_nostamp(name, core.SHAPEOFFFILENAME)) def _shape_load(self, name): if not self._connected: self._pprint("ERROR: Not connected to Mems") return None if os.path.isfile(name): l = np.loadtxt(name) self._pprint("Loaded '{}'".format(name)) return l else: self._pprint("File '{}' not found".format(name)) return None def shape_load(self, name): """ Loads a shape file previously saved Args: * name (str): the name of the file """ name = core.make_filepath_nostamp(name, core.SHAPEFILENAME) res = self._shape_load(name) if res is not None: self._moveit(np.loadtxt(name), 'all') def shape_on_load(self, name): """ Loads a shape ON file previously saved Args: * name (str): the name of the file """ name = core.make_filepath_nostamp(name, core.SHAPEONFILENAME) res = self._shape_load(name) if res is not None: self._on = np.loadtxt(name) def shape_off_load(self, name): """ Loads a shape OFF file previously saved Args: * name (str): the name of the file """ name = core.make_filepath_nostamp(name, core.SHAPEOFFFILENAME) res = self._shape_load(name) if res is not None: self._off = np.loadtxt(name) ##### Seb & Nick's additions - Might be broke ######### def piston_scan(self, elm, piston_begin, piston_end, step, wait_time): """ Piston Scan function. Format is piston_scan(Seg#, Where to begin, Where to end, stepsize,wait time). It will then scan through once. """ nb_steps = int((abs(piston_end) + abs(piston_begin)) / step) # print(nb_steps) # print('Initiating ramp from '+str(piston_begin)+' to '+str(piston_end)+' with '+str(nb_steps)+' steps') self._pprint("MEMS Scanning....") for i in range(0, nb_steps + 1): self.set_pos(elm, piston_begin + i * step, 0, 0) # print(piston_begin+i*step) # print('Piston step nb '+str(i+1)) time.sleep(wait_time) # Gui methods def run(self): if self.milk_solution: while self.running: if self.connected: # Get pos of mems piston, tip, tilt = self.get_pos('all') if self.flag_test: self.flag_test = False np.savetxt( FCTRLV2_PATH + "pisttiptilt_test.txt", np.concatenate((np.array(piston), np.array(tip), np.array(tilt))).reshape((3, 37))) # Compute the new opd map self._update_map(piston, tip, tilt) # Push data to the sahred memory self.data_plot.set_data(self.map_opd.astype(np.float32)) else: self._init_figure() def _compute_radii(self): for pix_x in range(self.map_height): for pix_y in range(self.map_width): seg_ind = self.map_index[pix_x, pix_y] if seg_ind != 0: radius_x = pix_x - self.map_centers[0, seg_ind - 1] self.map_radius_x[pix_x, pix_y] = radius_x radius_y = pix_y - self.map_centers[1, seg_ind - 1] self.map_radius_y[pix_x, pix_y] = radius_y def _init_maps(self): self.map_index, self.map_index_h = fits.getdata(FCTRLV2_PATH + MEMS_INDEX_NAME, header=True) self.map_height, self.map_width = np.shape(self.map_index) self.map_opd = np.ones((self.map_height, self.map_width)) self.map_opd[self.map_index == 0] = 0 self.map_centers = np.loadtxt(FCTRLV2_PATH + MEMS_CENTERS_NAME, dtype=np.int) self.map_radius_x = np.ones((self.map_height, self.map_width)) self.map_radius_y = np.ones((self.map_height, self.map_width)) self._compute_radii() def _init_figure(self): self.data_plot.set_data(self.map_opd.astype(np.float32)) def _update_map(self, piston_arr, tip_arr, tilt_ar): """ Compute piston, tip and tilt in opd unit. """ for seg_ind in range(37): tip_value = self.map_radius_x[self.map_index == seg_ind + 1] * np.sin( tip_arr[seg_ind] * 10**(-3)) tilt_value = self.map_radius_y[self.map_index == seg_ind + 1] * np.sin( tilt_ar[seg_ind] * 10**(-3)) self.map_opd[self.map_index == seg_ind + 1] = piston_arr[seg_ind] + tip_value + tilt_value
def MEC_CDI(): """ interfaces with the shared memory buffer of remote DM Here we create the interface between the shm and this code to apply new offsets for the remote DM. The interfacing is handled by pyMILK found https://github.com/milk-org/pyMilk. We also create the probe by calling config_probe, and send that offset map to the shm in a loop based on the CDI settings in CDI_params. We read in the time the probe pattern was applied, and save it and other cdi params to a structure called out. You can set both cdi.end_probes_after_ncycles and cdi.end_probes_after_time, and the loop will break at the shorter of the two, but if it hits the time limit it finishes the last full cycle before breaking (eg will wait the full length to complete the probe cycle and null time if the cdi.end_probes_after_time hits in the middle of a cycle). :return: nothing explicitly returned but probe is applied (will persist on DM until it is externally cleared, eg by the RTC computer on SCExAO). DM flat is sent as last command after cycle is terminated. """ # Create shared memory (shm) interface sp = ShmParams() MECshm = SHM( sp.shm_name) # create c-type interface using pyMilk's ISIO wrapper data = MECshm.get_data( ) # used to determine size of struct (removes chance of creating wrong probe size) # Initialize cdi = CDI_params() cdi.gen_phaseseries() out = cdi.init_out(data.shape[0]) # Create Flat flat = np.zeros((data.shape[0], data.shape[1]), dtype=np.float32) MECshm.set_data(flat) # Apply flat probe to get start time pt_start = MECshm.IMAGE.md.lastaccesstime # probe time start # Run Probe Cycles n_cycles = 0 n_cmd = 0 while n_cycles < cdi.end_probes_after_ncycles: olt = datetime.datetime.now() # outer loop time if (olt - pt_start).total_seconds() > cdi.time_limit: # print(f'elasped={(olt-pt_start).total_seconds()}, total test time={cdi.time_limit},\n' # f'ncycles={n_cycles}, n_cmds={n_cmd}') print('Max time Elapsed. Ending probe cycles') break for ip, theta in enumerate(cdi.phase_cycle): ilt = datetime.datetime.now() # inner loop time probe = config_probe(cdi, theta, data.shape[0]) MECshm.set_data(probe) # Apply Probe pt_sent = MECshm.IMAGE.md.lastaccesstime # probe time sent cdi.save_tseries(out, n_cmd, pt_sent) if n_cmd <= cdi.n_probes: cdi.save_probe(out, n_cmd, probe) n_cmd += 1 while True: if (datetime.datetime.now() - ilt).total_seconds() > cdi.phase_integration_time: break # Send flat and wait until next cycle begins MECshm.set_data(flat) # effectively clearing SHM pt_sent = MECshm.IMAGE.md.lastaccesstime # probe time sent cdi.save_tseries(out, n_cmd, pt_sent) cdi.save_probe(out, n_cmd, flat) n_cmd += 1 while True: if (datetime.datetime.now() - pt_sent).total_seconds() > cdi.null_time: # print(f"n_cmd = {n_cmd}\n " # f'cycle time = {(datetime.datetime.now() - olt).total_seconds()}s vs expected ' # f'probe+null time= {cdi.time_probe_plus_null:.2f}s') n_cycles += 1 break # Wrapping up print( f'\ntotal time elapsed = {(datetime.datetime.now()-pt_start).total_seconds():.2f} sec' ) out.ts.start = pt_start out.ts.n_cycles = n_cycles out.ts.n_cmds = n_cmd out.ts.cmd_tstamps = out.ts.cmd_tstamps[0:n_cmd] out.ts.elapsed_time = (datetime.datetime.now() - pt_start).total_seconds() # Make sure shm is clear MECshm.set_data(flat) # Saving Probe and timestamp together if cdi.save_to_disk: cdi.save_out_to_disk(out) # Fig if cdi.plot: plot_probe_cycle(out) # plot_probe_response_cycle(out) # plot_probe_response(out, 0) plot_quick_coord_check(out, 2) plt.show() return MECshm, out