def get_continuous_signal(channel_name, sample_time, sample_points): """Acquire a continuous signal. Parameters ----------- channel_name : str The continuous channel to view, e.g. I_t, Z_t, Df_t, Aux1_t sample_time : float The time to acquire in seconds sample_points : int The number of points to acquire Returns ------- x_data : Numpy array y_data : Numpy array Examples -------- Acquire 60 points of I(t) data over 0.1 seconds >>> from nOmicron.microscope import IO >>> from nOmicron.utils.plotting import plot_linear_signal >>> IO.connect() >>> t, I = get_continuous_signal("I(t)", 1e-1, 60) >>> plot_linear_signal(v, I, "I(V)") >>> IO.disconnect() Acquire 100 points of Z(t) data over 5 seconds >>> from nOmicron.microscope import IO >>> from nOmicron.utils.plotting import plot_linear_signal >>> IO.connect() >>> t, I = get_continuous_signal("Z(t)", 5, 100) >>> plot_linear_signal(v, I, "I(V)") >>> IO.disconnect() """ global view_count, x_data, y_data x_data = y_data = None view_count = 0 def view_continuous_callback(): global view_count, x_data, y_data view_count += 1 pbar.update(1) data_size = mo.view.Data_Size() period = mo.clock.Period() x_data = np.linspace(0, (data_size - 1) * period, data_size) y_data = np.array(mo.sample_data(data_size)) IO.enable_channel(channel_name) IO.set_clock(sample_time, sample_points) mo.view.Data(view_continuous_callback) mo.allocate_sample_memory(sample_points) pbar = tqdm(total=1) while view_count < 1 and mo.mate.rc == mo.mate.rcs['RMT_SUCCESS']: mo.wait_for_event() mo.clock.Enable(False) mo.view.Data() IO.disable_channel() return x_data, y_data
def get_xy_scan(channel_name, x_direction, y_direction, num_lines='all', mode='new', return_filename=False): """ Perform and get an xy scan Parameters ---------- channel_name : str The channel to acquire from, e.g. Z, I, Aux1, Aux2 x_direction : str Must be one of 'Forward', 'Backward', or 'Forward-Backward' y_direction : str Must be one of 'Up' or 'Up-Down' num_lines : int or str Number of lines to get. If an int, must be less than the number of lines in the scanner window. Default is 'all' mode : str, optional Must be one of ['new', 'pause', 'continue']. Default is 'new' return_filename : bool, optional If the full file name of the scan should be returned along with the data. Default is False Returns ------- xydata : ndarray Max 4 dimensions (y_up/y_down, x_up/x_down, x, y). First two dimensions will only appear if y_direction is "Up-Down" and x_direction is "Forward-Backward" - will be missing as appropriate Examples -------- >>> from nOmicron.microscope import IO >>> from nOmicron.utils.plotting import plot_xy >>> IO.connect() >>> xydata = get_xy_scan("Z", x_direction="Forward", y_direction="Up-Down") >>> plot_xy(xydata, pixel_scale=mo.xy_scanner.Width() * 1e9 / mo.xy_scanner.Points()) >>> IO.disconnect() Warnings -------- Quite often (but unreliably!), running this function will get Matrix into a state in which it will return one/two lines (unless play/pause is clicked manually) when operating manually. To restore this functionality, run utils.utils.restore_z_functionality() and restart your scan. """ global scan_dir_x, scan_dir_y, line_count_y, view_count, tot_packets, xydata # Setup and parsing parameters allowed = ["new", "pause", "continue"] if mode not in allowed: raise ValueError(f"Mode must be one of {allowed}") if num_lines == 'all': num_lines = mo.xy_scanner.Lines() x_dir_dict = {"Forward": "Fw", "Backward": "Bw"} x_direction_strings = [ x_dir_dict[x_dir] for x_dir in x_direction.split("-") ] y_direction_strings = y_direction.split("-") # Set triggers if x_direction == "Forward-Backward": mo.xy_scanner.X_Retrace(True) else: mo.xy_scanner.X_Retrace(False) if y_direction == "Up-Down": if num_lines != 'all' and num_lines != mo.xy_scanner.Lines( ): # Force set lines instead? raise ValueError( "If scanning in both directions, num_lines cannot be set") mo.xy_scanner.Y_Retrace(True) else: mo.xy_scanner.Y_Retrace(False) # while not mo.xy_scanner.Enable_Scan(): # Enforce that we won't take data during relocation and confuse the CU # print("waiting") # sleep(0.1) # Set counters and pre-allocate view_count = [None, None] scan_dir_y = 0 scan_dir_x = 1 line_count_y = 0 tot_packets = 0 xydata = np.zeros((2, 2, num_lines, mo.xy_scanner.Points())) xydata[:] = np.nan def view_xy_callback(): global scan_dir_x, scan_dir_y, line_count_y, view_count, tot_packets, xydata tot_packets += 1 mo.tot_packets = tot_packets line_count_y += 1 if x_direction == "Forward-Backward": scan_dir_x = int(not (bool(scan_dir_x))) line_count_y = line_count_y - scan_dir_x scan_dir_y = (line_count_y - 1) // num_lines data_size = mo.view.Data_Size() data_pts = np.array(mo.sample_data(data_size)) xydata[scan_dir_y, int(not (bool(scan_dir_x))), (line_count_y - (num_lines * scan_dir_y)) - 1, :] = data_pts view_count = [mo.view.Run_Count(), mo.view.Cycle_Count()] pbar.update(1) # pbar.set_postfix({"Scanline": mo.view.Packet_Count()}) # if tot_packets % 2 == 1: # pbar.set_postfix({"Scanline": mo.view.Packet_Count()+1}) # Enable channels for x_direction_string in x_direction_strings: IO.enable_channel(f"{channel_name}_{x_direction_string}") mo.view.Data(view_xy_callback) mo.allocate_sample_memory(mo.xy_scanner.Points()) if mode == 'new': mo.experiment.start() elif mode == 'pause': mo.experiment.resume() else: pass # Get the data pbar = tqdm(total=num_lines * len(x_direction_strings) * len(y_direction_strings)) while tot_packets < num_lines * len(x_direction_strings) * len(y_direction_strings) \ and mo.mate.rc == mo.mate.rcs['RMT_SUCCESS']: mo.wait_for_event() if mode == 'new': mo.experiment.stop() elif mode == 'pause': mo.experiment.pause() else: pass # Pretty the output to make physical sense xydata = np.flip(xydata, axis=2) if len(x_direction_strings) != 2: xydata = xydata[:, 0, :, :] if len(y_direction_strings) != 2: xydata = xydata[0, :, :, :] np.squeeze(xydata) # Return nicely if return_filename: filename = f"{mo.experiment.Result_File_Path()}\\{mo.experiment.Result_File_Name()}--{mo.view.Run_Count()}_{mo.view.Cycle_Count()}.Z_mtrx" return xydata, filename else: return xydata
def get_point_spectra(channel_name, target_position, start_end, sample_time, sample_points, repeats=1, forward_back=True, return_filename=False): """ Go to a position and perform fixed point spectroscopy. Parameters ---------- channel_name : str The channel to acquire from, e.g. I_V, Z_V, Aux2_V target_position : list [x, y] in the range -1,1. Can be converted from real nm units with utils.convert... start_end : tuple Start and end I/Z/Aux2 sample_time : float The time to acquire in seconds sample_points : int The number of points to acquire repeats : int The number of repeat spectra to take for each point forward_back : bool Scan in both directions, or just one. return_filename : bool, optional If the full file name of the scan should be returned along with the data. Default is False Returns ------- x_data : Numpy array y_data : If performing repeat spectra and: Scanning in both directions: list of list of Numpy arrays, where inner list is [0] forwards, [1] backwards Scanning in one direction: list of Numpy arrays If no repeat spectra and: Scanning in both directions: list of Numpy arrays, where list is [0] forwards, [1] backwards Scanning in one direction: single Numpy array Examples -------- Acquire 60 points of I(V) data over 10 milliseconds, with tip placed in middle of scan window. >>> from nOmicron.microscope import IO >>> from nOmicron.utils.plotting import plot_linear_signal >>> IO.connect() >>> v, I = get_point_spectra("I(V)", start_end=[0, 1], target_position=[0, 0], ... >>> repeats=3, sample_points=50, sample_time=10e-3, forward_back=True) >>> plot_linear_signal(v, I, "I(V)") >>> IO.disconnect() """ global view_count, view_name, x_data, y_data modes = { "V": 0, "Z": 1, "Varied Z": 2 } # Varied Z not fully supported yet! max_count = (repeats * (forward_back + 1)) view_count = 0 x_data = None y_data = [] [y_data.append([None] * (bool(forward_back) + 1)) for i in range(repeats)] # Can't use [] ** repeats def view_spectroscopy_callback(): global view_count, view_name, x_data, y_data pbar.update(1) view_count += 1 view_name = [mo.view.Run_Count(), mo.view.Cycle_Count()] cycle_count = mo.view.Cycle_Count() - 1 packet_count = mo.view.Packet_Count() - 1 data_size = mo.view.Data_Size() x_data = np.linspace(start_end[0], start_end[1], data_size) y_data[cycle_count][packet_count] = np.array( mo.sample_data(data_size)) * 1e-9 if packet_count == 1: y_data[cycle_count][packet_count] = np.flip( y_data[cycle_count][packet_count]) # Set all the parameters IO.enable_channel(channel_name) mo.spectroscopy.Spectroscopy_Mode(modes[channel_name[-2]]) getattr(mo.spectroscopy, f"Device_{modes[channel_name[-2]] + 1}_Points")(sample_points) getattr(mo.spectroscopy, f"Raster_Time_{modes[channel_name[-2]] + 1}")(sample_time) getattr(mo.spectroscopy, f"Device_{modes[channel_name[-2]] + 1}_Start")(start_end[0]) getattr(mo.spectroscopy, f"Device_{modes[channel_name[-2]] + 1}_End")(start_end[1]) getattr(mo.spectroscopy, f"Device_{modes[channel_name[-2]] + 1}_Repetitions")(repeats) getattr(mo.spectroscopy, f"Enable_Device_{modes[channel_name[-2]] + 1}_Ramp_Reversal")( forward_back) # Set up spec mo.xy_scanner.Store_Current_Position(True) mo.xy_scanner.Target_Position(target_position) mo.xy_scanner.Trigger_Execute_At_Target_Position(True) # Do it mo.xy_scanner.move() mo.allocate_sample_memory(sample_points) mo.view.Data(view_spectroscopy_callback) pbar = tqdm(total=max_count) while view_count < max_count and mo.mate.rc == mo.mate.rcs['RMT_SUCCESS']: mo.wait_for_event() mo.view.Data() # Return to normal mo.xy_scanner.Trigger_Execute_At_Target_Position(False) mo.xy_scanner.Return_To_Stored_Position(True) mo.xy_scanner.Store_Current_Position(False) IO.disable_channel() if not forward_back: y_data = [item[0] for item in y_data] if repeats == 1: y_data = y_data[0] if return_filename: filename = f"{mo.experiment.Result_File_Path()}\\{mo.experiment.Result_File_Name()}--{view_count[0]}_{view_count[1]}.{channel_name}_mtrx" return x_data, y_data, filename else: return x_data, y_data