def __init__(self, **kwargs): self.__channel_1 = IT6432Connection(1) self.__channel_2 = IT6432Connection(2) self.__channel_3 = IT6432Connection(3) self.power_supplies = (self.__channel_1, self.__channel_2, self.__channel_3) self.__set_currents = [0, 0, 0] if 'num_steps' in kwargs.keys(): self.__ramp_num_steps = kwargs['num_steps'] else: self.__ramp_num_steps = 5
def closeConnection(channel_1: IT6432Connection, channel_2=None, channel_3=None): """Close the connection with the current sources.""" channel_1._write("system:local") channel_1.close() if channel_2 is not None: channel_2._write("system:local") channel_2.close() if channel_3 is not None: channel_3._write("system:local") channel_3.close()
def openConnection(channel_1: IT6432Connection, channel_2=None, channel_3=None): """ Open a connection to a IT6432 current source. Returns: IT6432Connection: Instances of connection objects representing each channel. """ channel_1.connect() channel_1._write("system:remote") if channel_2 is not None: channel_2.connect() channel_2._write("system:remote") if channel_3 is not None: channel_3.connect() channel_3._write("system:remote")
def rampVoltageSimple(connection: IT6432Connection, set_voltage, new_voltage, step_size=0.01): """ Helper function to take care of setting the voltage. Args: connection (IT6432Connection): set_voltage (float): Voltage that is set right now. new_voltage (float): Target voltage. step_size (float, optional): Defaults to 0.01. threshold (float, optional): Defaults to 0.02. """ if connection.channel != 2: postfix_v = "V" else: postfix_v = "" threshold = 2 * step_size connection._write(f"voltage {set_voltage}" + postfix_v) diff_v = new_voltage - set_voltage sign = np.sign(diff_v) while abs(diff_v) >= threshold: set_voltage = set_voltage + sign * step_size connection._write(f"voltage {set_voltage}" + postfix_v) diff_v = new_voltage - set_voltage sign = np.sign(diff_v) connection._write(f"voltage {new_voltage}" + postfix_v)
def rampVoltageSimple(connection: IT6432Connection, set_voltage: float = 0, new_voltage: float = 0.3, steps: int = 5): """ Helper function to take care of ramping the voltage. Args: connection (IT6432Connection): set_voltage (float): Voltage that is set right now. new_voltage (float): Target voltage. steps (int, optional): Defaults to 5. """ if connection.channel != 2: postfix_v = "V" else: postfix_v = "" connection._write(f"voltage {set_voltage}" + postfix_v) diff_v = new_voltage - set_voltage step_size = diff_v / steps for _ in range(steps): set_voltage = set_voltage + step_size connection._write(f"voltage {set_voltage}" + postfix_v) diff_v = new_voltage - set_voltage # threshold = 0.05 # while abs(diff_v) >= threshold: # set_voltage = set_voltage + step_size # connection._write(f"voltage {set_voltage}" + postfix_v) # diff_v = new_voltage - set_voltage # step_size = 0.05 * diff_v connection._write(f"voltage {new_voltage}" + postfix_v)
def demagnetizeCoils( channel_1: IT6432Connection, channel_2: IT6432Connection, channel_3: IT6432Connection, current_config=[1, 1, 1], # factor=0.5 ): """ Try to eliminate any hysteresis effects by applying a slowly oscillating and decaying voltage to the coils. Args: factor (float): A factor 0<factor<1 to reduce the applied field by. """ # if factor >= 1: # factor = 0.99 steps = np.array([0, 1, 2, 3, 4]) bounds = 0.475 * np.outer(current_config, np.exp(-steps)) channel_1._write('current 5.01A') channel_2._write('current 5.01') channel_3._write('current 5.01A') thread_pool = [None, None, None] sign = -1 for i in range(bounds.shape[1]): voltages = getMeasurement(channel_1, channel_2, channel_3, meas_quantity='voltage') thread_pool[0] = threading.Thread( target=rampVoltageSimple, name='currentController_1', args=[channel_1, voltages[0], sign * bounds[0, i]], kwargs={'step_size': 0.06}) thread_pool[1] = threading.Thread( target=rampVoltageSimple, name='currentController_2', args=[channel_2, voltages[1], sign * bounds[1, i]], kwargs={'step_size': 0.06}) thread_pool[2] = threading.Thread( target=rampVoltageSimple, name='currentController_3', args=[channel_3, voltages[2], sign * bounds[2, i]], kwargs={'step_size': 0.06}) for thread in thread_pool: thread.start() for thread in thread_pool: thread.join() sign *= -1 sleep(0.1) disableCurrents(channel_1, channel_2, channel_3)
def getMeasurement(channel_1: IT6432Connection, channel_2=None, channel_3=None, meas_type=[""], meas_quantity=["current"]): """ Get DC current/power/voltage values from each channel Returns: a list of all the currents (or an error code) """ command = "measure:" quantities = ["current", "voltage", "power"] types = ["", "acdc", "max", "min"] if meas_quantity not in quantities: meas_quantity = "current" if meas_type not in types: meas_type = "" command += meas_quantity if meas_type != "": command += ":" + meas_type[0] command += "?" measured = [] res = channel_1.query(command) if isinstance(res, list): res = res[0] measured.append(float(res)) if channel_2 is not None: res = channel_2.query(command) if isinstance(res, list): res = res[0] measured.append(float(res)) if channel_3 is not None: res = channel_3.query(command) if isinstance(res, list): res = res[0] measured.append(float(res)) return measured
def rampVoltage(connection: IT6432Connection, new_voltage, new_current, step_size=0.01): """ Ramp voltage to a new specified value. The current should not be directly set, due to the load inductance instead it is a limit for the voltage increase. Like this, it is possible to ensure that the current takes the exact desired value without causing the voltage protection to trip. Args: connection (IT6432Connection): new_voltage (float): Target voltage new_current (float): Target current step_size (float, optional): Voltage increment. Defaults to 0.01. """ logging.basicConfig(filename="voltage_ramp.log", level=logging.DEBUG, force=True) logging.info("now ramping current in channel %s", connection.channel) if connection.channel != 2: postfix_v = "V" postfix_i = "A" else: postfix_v = "" postfix_i = "" connection.clrOutputProt() if connection.query("output?") == "0": connection._write("voltage 0" + postfix_v) connection._write("output 1") if new_current > connection.current_lim: new_current = connection.current_lim if new_current < 0.002 or abs(new_voltage) < 0.001: new_current = 0.002 new_voltage = 0 if abs(new_voltage) > connection.voltage_lim: new_voltage = connection.voltage_lim meas_voltage = getMeasurement(connection, meas_quantity="voltage")[0] meas_current = getMeasurement(connection, meas_quantity="current")[0] logging.debug( f"actual voltage: {meas_voltage}V, actual current: {meas_current}A") logging.debug( f"target voltage: {new_voltage}V, desired current: {new_current}A") if new_current - abs(meas_current) < 0: intermediate_step = 0.4 * new_current if new_current > 0.01 else 0 rampVoltageSimple(connection, meas_voltage, intermediate_step, step_size) repeat_count = 0 meas_current_queue = [meas_current, 0] while not (abs(meas_current) < new_current or repeat_count >= 5): meas_current_queue.insert( 0, getMeasurement(connection, meas_quantity="current")[0]) meas_current_queue.pop(2) repeat = abs(meas_current_queue[0] - meas_current_queue[1]) < 0.002 if repeat: repeat_count += 1 else: repeat_count = 0 connection._write(f"current {new_current}" + postfix_i) if new_current < 0.002 or abs(new_voltage) < 0.001: connection._write("output 0") else: meas_voltage = getMeasurement(connection, meas_quantity="voltage")[0] rampVoltageSimple(connection, meas_voltage, new_voltage, step_size) messages = connection.getStatus() if "QER0" in messages.keys(): logging.info(messages["QER0"] + ", channel: %s", connection.channel) if "QER4" in messages.keys(): logging.info(messages["QER4"] + ", channel: %s", connection.channel) if "OSR1" in messages.keys(): logging.info(messages["OSR1"] + ", channel: %s", connection.channel)
args=[channel_3, voltages[2], sign * bounds[2, i]], kwargs={'step_size': 0.06}) for thread in thread_pool: thread.start() for thread in thread_pool: thread.join() sign *= -1 sleep(0.1) disableCurrents(channel_1, channel_2, channel_3) ########## test stuff out ########## if __name__ == "__main__": channel_1 = IT6432Connection(1) channel_2 = IT6432Connection(2) channel_3 = IT6432Connection(3) openConnection(channel_1, channel_2, channel_3) # setCurrents(channel_1, channel_2, channel_3, np.array([1, 1, 1])) # sleep(15) # channel_2._write('voltage:prot 9.05') # channel_2._write('curr 1') # rampVoltage(channel_2, 1.7, 3.4, 0.05) # print(channel_1.outputInfo()) # channel_2.saveSetup(0) # channel_3.saveSetup(0) # print(channel_3.outputInfo())
def rampVoltage(connection: IT6432Connection, new_voltage: float, new_current: float, steps: int): """ Ramp voltage to a new specified value. The current should not be directly set due to the load inductance, instead it is a limiter for the voltage increase. Like this, it is possible to ensure that the current takes the exact desired value without causing the voltage protection to trip. Args: channel (1,2 or 3): channel on which to change the voltage. new_voltage (float): Target voltage new_current (float): Target current steps (int): Voltage increment. """ connection.clrOutputProt() if connection.channel != 2: postfix_v = "V" postfix_i = "A" else: postfix_v = "" postfix_i = "" if connection.query("output?") == "0": connection._write("voltage 0" + postfix_v + ";:output 1") if new_current > connection.current_lim: new_current = connection.current_lim if new_current < 0.002 or abs(new_voltage) < 0.001: new_current = 0.002 new_voltage = 0 if abs(new_voltage) > connection.voltage_lim: new_voltage = connection.voltage_lim meas_voltage = connection.getMeasurement(meas_quantity="voltage") meas_current = connection.getMeasurement(meas_quantity="current") if new_current - abs(meas_current) < 0: intermediate_step = 0.4 * new_current if new_current > 0.02 else 0 rampVoltageSimple(connection, meas_voltage, intermediate_step, steps) repeat_count = 0 meas_current_queue = [meas_current, 0] while not (abs(meas_current) < new_current or repeat_count >= 5): meas_current_queue.insert( 0, connection.getMeasurement(meas_quantity="current")) meas_current_queue.pop(2) repeat = abs(meas_current_queue[0] - meas_current_queue[1]) < 0.002 if repeat: repeat_count += 1 else: repeat_count = 0 connection._write(f"current {new_current}" + postfix_i) if new_current < 0.002 or abs(new_voltage) < 0.001: connection._write("output 0") else: meas_voltage = connection.getMeasurement(meas_quantity="voltage") rampVoltageSimple(connection, meas_voltage, new_voltage, steps)
def runCurrents(config_list, t=[], subdir='default_location', demagnetize=False, temp_meas=False): """ Set arbitrary currents on each channel. Includes timed mode, magnetic field and temperature measurements and setting new currents. Args: config_list (list of (np.array() size 3)): list of current configs in [A]. Make sure not to give more than 3! t (int): timer duration list. multiple timers -> different currents will be set for different amounts of time. If zero, user can decide whether to change the current or deactivate it. Defaults to []. subdir (str, optional): Default location where measurements are stored. Defaults to 'default_location'. demagnetize (bool, optional): if true, demagnetization will run every time the field is deactivated. temp_meas (bool, optional): if true, temperature will be measured. """ global desCurrents channel_1 = IT6432Connection(1) channel_2 = IT6432Connection(2) channel_3 = IT6432Connection(3) openConnection(channel_1, channel_2, channel_3) # on until interrupted by user if len(t) == 0 or t[0] == 0: channels = config_list[0] for i in range(len(channels)): desCurrents[i] = round(channels[i], 3) setCurrents(channel_1, channel_2, channel_3, desCurrents) # wait until user presses enter c1 = '0' while c1 != 'q': c1 = input( '[q] to disable currents\n[c]: get currents\n[r]: Set new currents\n[s]: monitor magnetic field\n') if c1 == 'c': currentsList = getMeasurement(channel_1, channel_2, channel_3) print( f'current 1: {currentsList[0]:.3f}, current 2: {currentsList[1]:.3f}, current 3: {currentsList[2]:.3f}') elif c1 == 'r': channels[0] = input('Channel 1 current: ') channels[1] = input('Channel 2 current: ') channels[2] = input('Channel 3 current: ') # handle new inputs for i in range(len(channels)): try: desCurrents[i] = round(channels[i], 3) except BaseException: print( "non-integer value entered, setting channel {} to 0".format(i + 1)) desCurrents[i] = 0 setCurrents(channel_1, channel_2, channel_3, desCurrents) ########################### ONLY WITH METROLAB SENSOR ########################### ########################################################################## elif c1 == 's': with MetrolabTHM1176Node(period=0.05, range='0.3 T', average=20) as node: test_thread = p.inputThread(1) test_thread.start() sleep(0.1) while p.flags[0]: newBMeasurement = sensor_to_magnet_coordinates( np.array(node.measureFieldmT())) # newBMeasurement = np.random.randn((3)) * 10 B_magnitude = np.linalg.norm(newBMeasurement) theta = np.degrees( np.arccos(newBMeasurement[2] / B_magnitude)) phi = np.degrees(np.arctan2( newBMeasurement[1], newBMeasurement[0])) if p.flags[0]: print( f'\rMeasured B field: ({newBMeasurement[0]:.2f}, {newBMeasurement[1]:.2f}, ' f'{newBMeasurement[2]:.2f}) / In polar coordinates: ({B_magnitude:.2f}, ' f'{theta:.2f}°, {phi:.2f}°) ', sep='', end='', flush=True) sleep(0.5) p.threadLock.acquire() p.flags.insert(0, 1) p.threadLock.release() ########################################################################## else: # initialize temperature sensor and measurement routine and start measuring if temp_meas: arduino = ArduinoUno('COM7') measure_temp = threading.Thread( target=arduino.getTemperatureMeasurements, kwargs={ 'print_meas': False}) measure_temp.start() # use only with Metrolab sensor try: duration = int(input('Duration of measurement (default is 10s): ')) except BaseException: duration = 10 try: period = float(input('Measurement trigger period (default is 0.5s, 0.01-2.2s): ')) except BaseException: period = 0.5 if period < 0.1: block_size = 10 elif period < 0.05: block_size = 30 elif period >= 0.5: block_size = 1 # print(duration, period) params = { 'name': 'BFieldMeasurement', 'block_size': block_size, 'period': period, 'duration': duration, 'averaging': 3} faden = p.myMeasThread(10, **params) gotoPosition() savedir = input('Name of directory where this measurement will be saved: ') faden.start() for index, timer in enumerate(t): channels = config_list[index] for i in range(len(channels)): desCurrents[i] = round(channels[i], 3) # print(desCurrents) setCurrents(channel_1, channel_2, channel_3, desCurrents) if timer < 500: countdown = p.timerThread(11, timer) countdown.start() sleep(timer) countdown.join() else: countdown = p.timerThread(11, timer) countdown.start() starttime = time() while time() - starttime < timer: pause = min(500, timer - (time() - starttime)) sleep(pause) getMeasurement(channel_1, channel_2, channel_3) countdown.join() faden.join() if temp_meas: arduino.stop = True measure_temp.join() saveTempData( arduino.data_stack, directory=r'C:\Users\Magnebotix\Desktop\Qzabre_Vector_Magnet\1_Version_2_Vector_Magnet\1_data_analysis_interpolation\Data_Analysis_For_VM\temperature_measurements', filename_suffix='temp_meas_repeatability') saveLoc = rf'C:\Users\Magnebotix\Desktop\Qzabre_Vector_Magnet\1_Version_2_Vector_Magnet\1_data_analysis_interpolation\Data_Analysis_For_VM\data_sets\{savedir}' strm(p.return_dict(), saveLoc, now=True) if demagnetize: demagnetizeCoils(channel_1, channel_2, channel_3, config_list[-1]) disableCurrents(channel_1, channel_2, channel_3) closeConnection(channel_1, channel_2, channel_3)