-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcalibration.py
208 lines (176 loc) · 7.96 KB
/
calibration.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
from numpy.core.arrayprint import format_float_positional
from old.model import ChannelInterfaceData
from re import S
import numpy as np
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import (
QComboBox,
QGridLayout,
QLabel,
QPushButton,
QVBoxLayout,
QWidget,
QMainWindow
)
from config import CALIBRATION_VOLTAGE, CHANNEL_NAMES_IN, CHANNEL_NAMES_OUT, DEBUG_MODE
"""
Make new window
(assumes DAQ output is correct)
Step 1:
Hook up DAQ output into DAQ input directly
Step 2:
Run calibration
- go through each channel, see what it reads when output is known (0V, 1V, 3V)
- reset voltages on writer to saved params
- save the offsets
Step 3:
Hook up back onto coils
-Save and finish
"""
# TODO: if previous session has a sin wave, calibration will end up using that sin wave instead of
# the calibration voltage
class CalibrationWindow(QMainWindow):
"""
Calibrates the DAQ reader to ensure the correct offset for all channels
"""
# data with offsets applied
corrected_data = QtCore.pyqtSignal(object)
offsets_received = QtCore.pyqtSignal(object)
def __init__(
self, parent, writer, reader, write_channels, read_channels, saved_offsets
):
super().__init__(parent)
self.writer = writer
self.reader = reader
self.write_channels = write_channels
self.read_channels = read_channels
self.calibration_state = True
self.calibration_voltage = CALIBRATION_VOLTAGE
self.handler_counter = 0
self.offsets = saved_offsets
# value represents the index of the output channel to take voltage readings from
self.assigned_output = [int for x in CHANNEL_NAMES_IN]
self.init_ui()
self.calibration_btn.clicked.connect(self.on_calibration_btn_clicked)
def init_ui(self):
"""
Initialize the calibration UI with the widgets and their values
"""
self.setWindowTitle("Calibration")
self.mainbox = QWidget(self)
self.setCentralWidget(self.mainbox)
layout = QVBoxLayout(self)
layout.setSpacing(5)
self.mainbox.setLayout(layout)
grid_layout = QGridLayout(self)
self.sel_output_ch_combo = [QComboBox(self) for i in CHANNEL_NAMES_IN]
grid_layout.addWidget(QLabel("Input Ch."), 0, 0)
grid_layout.addWidget(QLabel("Output Ch."), 0, 1)
grid_layout.addWidget(QLabel("Offsets"), 0, 2)
for i, ch_in in enumerate(CHANNEL_NAMES_IN):
for j, ch_out in enumerate(CHANNEL_NAMES_OUT):
item = ch_out + " (ao" + str(self.write_channels[j]) + ")"
self.sel_output_ch_combo[i].addItem(item, userData=i)
# set -1 to trigger event handler for all combo boxes on creation
self.sel_output_ch_combo[i].setCurrentIndex(-1)
# first argument of handler when connected is index of selection (indicated by _).
# ignore so the selected combobox qt object can be passed instead
handler = lambda _, combo=self.sel_output_ch_combo[i]: self.on_output_channel_selected(
combo)
self.sel_output_ch_combo[i].currentIndexChanged.connect(handler)
self.sel_output_ch_combo[i].setCurrentIndex(0)
self.offsets_label = [
QLabel(str(self.offsets[i]) + "V")
for i, ch in enumerate(CHANNEL_NAMES_IN)
]
grid_layout.addWidget(
QLabel(
ch_in + " (ai" + str(self.read_channels[i]) + ")"), i + 1, 0
)
grid_layout.addWidget(self.sel_output_ch_combo[i], i + 1, 1)
grid_layout.addWidget(self.offsets_label[i], i + 1, 2)
# make associations between daq input channel and the daq out channel it is receiving voltage from
self.calibration_voltage_label = QLabel(
"Calibration Voltage: {}V\n".format(self.calibration_voltage)
)
self.calibration_btn = QPushButton("Start Calibration")
instructions = "\nEnsure the input DAQ channels are connected to the corresponding"
instructions += "\noutput DAQ channels before starting calibration\n"
instructions += "\n(Exit to skip calibration)\n"
layout.addWidget(self.calibration_voltage_label)
layout.addLayout(grid_layout)
layout.addWidget(QLabel(instructions))
layout.addWidget(self.calibration_btn)
# combo = contains the info for the selected output DAQ we are reading from
# combo.currentData() = input channel that we are assigning to
def on_output_channel_selected(self, combo):
self.assigned_output[combo.currentData()] = combo.currentIndex()
print(self.assigned_output)
def on_calibration_btn_clicked(self):
"""
If calibration has not started, start calibration
Else calibration has finished and close this window
"""
if self.calibration_state:
self.run_calibration()
else:
print("Exited Calibration")
self.close()
def on_data_collected(self, data):
"""
handler that is called when reader takes in a buffer
When the DAQ reader is on, it will repeated call this function
We will read the second/third buffer to ensure that any previous values are flushed
This new value should be whatever is connected to the DAQ Input
The calibration offset = True voltage (self.calibration_voltage) - Measured voltage (current reading)
"""
self.handler_counter += 1
# allow the DAQ to clear its buffer before taking in voltage
if self.handler_counter < 2:
return
self.handler_counter = 0
print("Calibration data received")
# collect mean of data in buffer, and apply offset to plotter
for i, ch in enumerate(CHANNEL_NAMES_IN):
index = self.assigned_output[i]
self.offsets[i] = self.calibration_voltage - np.mean(data[index])
self.offsets_label[i].setText(str(self.offsets[i]) + "V")
print("Offset:", self.offsets)
self.offsets_received.emit(self.offsets)
self.calibration_btn.setText("Finish Calibration")
self.calibration_state = False
# writer.pause will make one more call to this handler once paused
# disconnect before writer can make another call to this handler
self.reader.incoming_data.disconnect(self.on_data_collected)
self.writer.pause() # will end up emitting on_data_collected again
self.writer.output_states = self.saved_writer_states
self.writer.voltages = self.saved_writer_voltages
self.writer.frequencies = self.saved_writer_frequencies
def run_calibration(self):
"""
When calibration is running, all new data will also pass through the on_data_collected function
This will allow the program to process the true and measured voltage and calibrate accordingly
"""
print("Calibration Started")
# connect to reader to get input
self.reader.incoming_data.connect(self.on_data_collected)
# save previous output states
self.saved_writer_states = self.writer.output_states
self.saved_writer_frequencies = self.writer.frequencies
self.saved_writer_voltages = self.writer.voltages
# set calibration voltages
for i, ch in enumerate(CHANNEL_NAMES_OUT):
self.writer.output_states[i] = True
self.writer.voltages[i] = self.calibration_voltage
self.writer.frequencies[i] = 0
self.writer.resume()
# handler takes input from reader and then emits the calibrated data
# maybe put in another object
def apply_calibration(self, data):
"""
This function is called as a bridge between the received signal and the used signal
Applies calibration offset all incoming data
"""
corrected = [chan_data + self.offsets[i]
for i, chan_data in enumerate(data)]
self.corrected_data.emit(corrected)