forked from zietzm/Helmholtz_Test_Bench
Moved files. Bug fixes from clean room
This commit is contained in:
@@ -16,7 +16,7 @@ class ArduinoDevice(Arduino):
|
||||
ui_print("\nConnecting to Arduino...")
|
||||
# try to set up the arduino. Exceptions are handled by caller
|
||||
# search for connected arduino and connect by initializing arduino library class
|
||||
Arduino.__init__(self, timeout=0.2)
|
||||
Arduino.__init__(self, port="COM4", timeout=2)
|
||||
for pin in self.pins:
|
||||
self.pinMode(pin, "Output")
|
||||
self.digitalWrite(pin, "LOW")
|
||||
|
||||
+5
-2
@@ -165,8 +165,10 @@ class CoilConstantCalibration(Thread):
|
||||
time.sleep(self.SETTLE_TIME)
|
||||
|
||||
# Get coil constant
|
||||
field_diff_mag = np.linalg.norm(g.MAGNETOMETER.field - ambient_field)
|
||||
k_samples.append(field_diff_mag / c)
|
||||
field = g.MAGNETOMETER.field
|
||||
field_diff_mag = np.linalg.norm(field - ambient_field)
|
||||
sign = 1 if field[i] - ambient_field[i] >= 0 else -1
|
||||
k_samples.append((field_diff_mag * sign) / c)
|
||||
|
||||
# Save vector as principal coil direction if it is the last sample with the largest positive current
|
||||
if c_idx == currents.shape[0] - 1:
|
||||
@@ -177,6 +179,7 @@ class CoilConstantCalibration(Thread):
|
||||
k_deviations[i], _ = self.calculate_standard_deviation(k_samples)
|
||||
|
||||
# Put device into an off and ready state
|
||||
time.sleep(3)
|
||||
self.cage_dev.idle()
|
||||
|
||||
angles = {'xy': self.angle_between(axis_field_directions[0], axis_field_directions[1]) * 180 / pi,
|
||||
|
||||
+1
-1
@@ -48,4 +48,4 @@ default_psu_config = {
|
||||
|
||||
# Configuration for socket interface
|
||||
SOCKET_PORT = 6677
|
||||
SOCKET_MAX_CONNECTIONS = 5
|
||||
SOCKET_MAX_CONNECTIONS = 5
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import time
|
||||
import traceback
|
||||
from threading import RLock, Thread, Event
|
||||
from tkinter import messagebox
|
||||
@@ -206,25 +207,21 @@ class HelmholtzCageDevice:
|
||||
command_buffer = HelmholtzCageDevice._copy_command(self.command)
|
||||
self.command = None # Processed commands are removed from "buffer"
|
||||
if command_buffer:
|
||||
try:
|
||||
# Unpack command into "action" and argument
|
||||
command_string = command_buffer['command']
|
||||
command_arg = command_buffer['arg']
|
||||
# Check which command and delegate to responsible function
|
||||
if command_string == "set_signed_currents":
|
||||
self._set_signed_currents(command_arg)
|
||||
elif command_string == "set_field_raw":
|
||||
self._set_field_raw(command_arg)
|
||||
elif command_string == "set_field_compensated":
|
||||
self._set_field_compensated(command_arg)
|
||||
elif command_string == 'idle':
|
||||
self.idle()
|
||||
else:
|
||||
raise Exception("Command unknown!")
|
||||
except Exception as e:
|
||||
# No ui print, since this is really not something that should happen... (relevant for devs)
|
||||
print("An error occurred while processing command: {}".format(self.command))
|
||||
traceback.print_exc()
|
||||
# Unpack command into "action" and argument
|
||||
command_string = command_buffer['command']
|
||||
command_arg = command_buffer['arg']
|
||||
# Check which command and delegate to responsible function
|
||||
if command_string == "set_signed_currents":
|
||||
self._set_signed_currents(command_arg)
|
||||
elif command_string == "set_field_raw":
|
||||
self._set_field_raw(command_arg)
|
||||
elif command_string == "set_field_compensated":
|
||||
self._set_field_compensated(command_arg)
|
||||
elif command_string == 'idle':
|
||||
self.idle()
|
||||
else:
|
||||
raise Exception("Command unknown!")
|
||||
|
||||
|
||||
def _hw_poll_thread_method(self):
|
||||
"""This method forms the main thread for hardware command execution."""
|
||||
@@ -282,6 +279,7 @@ class HelmholtzCageDevice:
|
||||
axis.set_signed_current(current)
|
||||
except Exception as e:
|
||||
ui_print("Error {}: Unexpected error occured:\n{}".format(axis.name, e))
|
||||
traceback.print_exc()
|
||||
|
||||
def get_psu_for_axis(self, axis_index):
|
||||
"""Determine which channel of which psu is required"""
|
||||
@@ -443,7 +441,7 @@ class Axis:
|
||||
voltage_limit = min(max(1.3 * abs(safe_current) * self.resistance, 8), self.max_volts) # limit voltage
|
||||
|
||||
# Set voltages and currents. Outputs should already be active from initializer.
|
||||
self.psu.set_current(self.channel, safe_current)
|
||||
self.psu.set_current(self.channel, abs(safe_current))
|
||||
self.psu.set_voltage(self.channel, voltage_limit)
|
||||
|
||||
@property
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# [1] = "PS 2000B Programming Guide" from 2015-05-28
|
||||
# [2] = "PS 2000B object list"
|
||||
#
|
||||
import time
|
||||
|
||||
import serial
|
||||
import struct
|
||||
@@ -388,7 +389,9 @@ class PS2000B:
|
||||
|
||||
def set_current(self, value, channel):
|
||||
self.update_device_information(channel)
|
||||
time.sleep(0.05)
|
||||
self.enable_remote_control(channel)
|
||||
time.sleep(0.05)
|
||||
curr = int(round((value * 25600.0) / self.__device_information.nominal_current))
|
||||
self.__send_device_data(Objects.SET_VALUE_CURRENT, curr, channel)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# ABC is template used to create python abstract classes
|
||||
import time
|
||||
from abc import ABC, abstractmethod
|
||||
import serial
|
||||
|
||||
@@ -84,12 +85,15 @@ class PSUDevicePS2000B(PSUDevice):
|
||||
"""Provides HW support for the Elektro-Automatik PS2000B power supply.
|
||||
This class is an adapter for the already existing PS2000B library, to have a consistent interface"""
|
||||
|
||||
MIN_DELAY = 0.05
|
||||
|
||||
def __init__(self, com_port):
|
||||
super().__init__(com_port)
|
||||
"""Can fail; Check for serial.SerialException"""
|
||||
self.dev = PS2000B.PS2000B(com_port)
|
||||
# dev_info is a class which contains hw specific constants, such as nominal voltage and current
|
||||
self.dev_info = self.dev.get_device_information() # Cache this result
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
@staticmethod
|
||||
def valid_channels():
|
||||
@@ -98,25 +102,32 @@ class PSUDevicePS2000B(PSUDevice):
|
||||
|
||||
def enable_channel(self, channel_nr):
|
||||
self.dev.enable_output(channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
def disable_channel(self, channel_nr):
|
||||
self.dev.enable_output(channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
def set_current(self, channel_nr, current):
|
||||
self.dev.set_current(current, channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
def set_voltage(self, channel_nr, voltage):
|
||||
self.dev.set_voltage(voltage, channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
def poll_channel_state(self, channel_nr):
|
||||
self.dev.update_device_information(channel_nr) # update the information in the device object
|
||||
time.sleep(self.MIN_DELAY)
|
||||
dev_status = self.dev.get_device_status_information(channel_nr) # get object with new status info
|
||||
|
||||
# This is more efficient than the dev.get_voltage() call, since it does not require another update_device_info
|
||||
voltage = dev_status.actual_voltage_percent * self.dev_info.nominal_voltage # Extracted from PS2000B library
|
||||
voltage_setp = self.dev.get_voltage_setpoint(channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
current = dev_status.actual_current_percent * self.dev_info.nominal_current
|
||||
current_setp = self.dev.get_current_setpoint(channel_nr)
|
||||
time.sleep(self.MIN_DELAY)
|
||||
|
||||
# Format should match the provided template in abstract PSUDevice class.
|
||||
return {'active': dev_status.output_active, 'remote_active': dev_status.remote_control_active,
|
||||
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,35 @@
|
||||
import serial
|
||||
from datetime import datetime
|
||||
import socket
|
||||
|
||||
# Lookup table for cage axis to magnetometer transformation
|
||||
# First entry: Magnetometer y axis to cage x (reversed)
|
||||
# axis_mapping = [[1, -1 ...]
|
||||
axis_mapping = [[2, -1], [0, 1], [1, -1]]
|
||||
|
||||
# Helmholtz control software tcp port
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(("localhost", 6677))
|
||||
s.sendall("declare_api_version 2\n".encode())
|
||||
|
||||
# FGM3D software virtual serial port
|
||||
ser = serial.Serial("COM11")
|
||||
|
||||
line = b""
|
||||
ready = False
|
||||
while True:
|
||||
line += ser.read()
|
||||
if line[-2:] == b"\r\n":
|
||||
new_line = line[:-2].decode('ascii')
|
||||
delta = datetime.now().timestamp()*1000 - int(new_line.split(';')[0])+(2*60*60*1000)
|
||||
if delta < 500 and not ready:
|
||||
ready = True
|
||||
print("Program ready!")
|
||||
if ready:
|
||||
# Data is not valid otherwise
|
||||
# Only use the x y and z values
|
||||
tokens = [float(v) for v in new_line.split(";")[1:-1]]
|
||||
axis_fields = [tokens[m[0]]*m[1] for m in axis_mapping]
|
||||
axis_fields = ["{:.6e}".format(v) for v in axis_fields]
|
||||
s.sendall(("magnetometer_field {} {} {}\n".format(*axis_fields)).encode())
|
||||
line = b""
|
||||
Reference in New Issue
Block a user