itkdb-gtk 0.0.3__py3-none-any.whl → 0.20.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- itkdb_gtk/{sendShipments.py → CreateShipments.py} +74 -78
- itkdb_gtk/{getShipments.py → GetShipments.py} +99 -106
- itkdb_gtk/GlueWeight.py +45 -66
- itkdb_gtk/ITkDB.desktop +8 -0
- itkdb_gtk/ITkDB.svg +380 -0
- itkdb_gtk/ITkDBlogin.py +10 -6
- itkdb_gtk/ITkDButils.py +295 -57
- itkdb_gtk/PanelVisualInspection.py +590 -0
- itkdb_gtk/QRScanner.py +120 -0
- itkdb_gtk/SensorUtils.py +492 -0
- itkdb_gtk/ShowAttachments.py +267 -0
- itkdb_gtk/ShowComments.py +94 -0
- itkdb_gtk/ShowDefects.py +103 -0
- itkdb_gtk/UploadModuleIV.py +566 -0
- itkdb_gtk/UploadMultipleTests.py +746 -0
- itkdb_gtk/UploadTest.py +509 -0
- itkdb_gtk/VisualInspection.py +297 -0
- itkdb_gtk/WireBondGui.py +1304 -0
- itkdb_gtk/__init__.py +38 -12
- itkdb_gtk/dashBoard.py +292 -33
- itkdb_gtk/dbGtkUtils.py +356 -75
- itkdb_gtk/findComponent.py +242 -0
- itkdb_gtk/findVTRx.py +36 -0
- itkdb_gtk/readGoogleSheet.py +1 -2
- itkdb_gtk/untrash_component.py +35 -0
- {itkdb_gtk-0.0.3.dist-info → itkdb_gtk-0.20.1.dist-info}/METADATA +21 -12
- itkdb_gtk-0.20.1.dist-info/RECORD +30 -0
- {itkdb_gtk-0.0.3.dist-info → itkdb_gtk-0.20.1.dist-info}/WHEEL +1 -1
- itkdb_gtk-0.20.1.dist-info/entry_points.txt +12 -0
- itkdb_gtk/checkComponent.py +0 -131
- itkdb_gtk/groundingTest.py +0 -225
- itkdb_gtk/readAVSdata.py +0 -565
- itkdb_gtk/uploadPetalInformation.py +0 -604
- itkdb_gtk/uploadTest.py +0 -384
- itkdb_gtk-0.0.3.dist-info/RECORD +0 -19
- itkdb_gtk-0.0.3.dist-info/entry_points.txt +0 -7
- {itkdb_gtk-0.0.3.dist-info → itkdb_gtk-0.20.1.dist-info}/top_level.txt +0 -0
itkdb_gtk/SensorUtils.py
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""A collection of utilities for sensor data."""
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import warnings
|
|
5
|
+
import numpy as np
|
|
6
|
+
from itkdb_gtk import ITkDButils
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# The following is taken from
|
|
10
|
+
# https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
|
|
11
|
+
#
|
|
12
|
+
mm = 1e-3
|
|
13
|
+
cm = 1e-2
|
|
14
|
+
UpperV = 500 # For sensor to pass, I must be < Imax up until this voltage and no breakdown must be detected before then.
|
|
15
|
+
StabilityV = 700 # Voltage with multiple current readings to check stability
|
|
16
|
+
|
|
17
|
+
AreaDict = {
|
|
18
|
+
"Unknown": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
19
|
+
#
|
|
20
|
+
"ATLAS12": 95.7 * 95.64 * mm * mm,
|
|
21
|
+
"ATLAS17LS": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
22
|
+
#
|
|
23
|
+
"ATLAS18R0": 89.9031 * cm * cm,
|
|
24
|
+
"ATLAS18R1": 89.0575 * cm * cm,
|
|
25
|
+
"ATLAS18R2": 74.1855 * cm * cm,
|
|
26
|
+
"ATLAS18R3": 80.1679 * cm * cm,
|
|
27
|
+
"ATLAS18R4": 87.4507 * cm * cm,
|
|
28
|
+
"ATLAS18R5": 91.1268 * cm * cm,
|
|
29
|
+
#
|
|
30
|
+
"ATLAS18SS": 93.6269 * cm * cm,
|
|
31
|
+
"ATLAS18LS": 93.6269 * cm * cm,
|
|
32
|
+
#
|
|
33
|
+
"ATLASDUMMY18": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
NO_BD_CONST = 9.99e99
|
|
38
|
+
|
|
39
|
+
def LocateMicroDischarge(
|
|
40
|
+
I,
|
|
41
|
+
V,
|
|
42
|
+
sm_window=2,
|
|
43
|
+
bd_limit=5.5,
|
|
44
|
+
allow_running_bd=True,
|
|
45
|
+
use_additional_cond=False,
|
|
46
|
+
tolerence=0.05,
|
|
47
|
+
voltage_span=4,
|
|
48
|
+
fit_window=5,
|
|
49
|
+
):
|
|
50
|
+
"""
|
|
51
|
+
Function for BDV estimation - if questions please contact Vera Latonova (vera.latonova@cern.ch).
|
|
52
|
+
I,V must have same shape and voltages must be in ascending order,
|
|
53
|
+
same indexes of I&V arrays must correspond each other,
|
|
54
|
+
only invalid data or holdstep should be stripped before
|
|
55
|
+
but it is not necessary. Measurments U=0&I=0 are removed.
|
|
56
|
+
If there is same or higher amount of same voltages in row than
|
|
57
|
+
sm_window, from this sequence we cannot estimete dI/dV and
|
|
58
|
+
we have to remove this averaged point.
|
|
59
|
+
|
|
60
|
+
It is assumed that only parameter use_additional_cond would be
|
|
61
|
+
changed by caller. Changing of other parameters may affect
|
|
62
|
+
BDV unexpectedly.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@param[in] I - array of currents without any cut
|
|
66
|
+
@param[in] V - array of voltages, ascending order, without any cut
|
|
67
|
+
@param[in] sm_window - size of smoothing window
|
|
68
|
+
@param[in] bd_limit - BD limit for |U| < 500V
|
|
69
|
+
@param[in] allow_running_bd - allow increase bd_limit for |U| > 500
|
|
70
|
+
@param[in] use_additional_cond - use additional BD contition
|
|
71
|
+
@param[in] tolerence - configuration of additional condition
|
|
72
|
+
@param[in] voltage_span - max width of hump on spectra which may be neglected
|
|
73
|
+
in voltage steps in additional contition
|
|
74
|
+
@param[in] fit_window - number of points used for linear fit before BD voltage
|
|
75
|
+
|
|
76
|
+
@return BD voltage (always positive) or NO_BD_CONST = 9.99e99 if not found.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# add nan to the end of array
|
|
80
|
+
V = np.abs(V)
|
|
81
|
+
I = np.abs(I)
|
|
82
|
+
|
|
83
|
+
# skip zeros
|
|
84
|
+
ind = np.where(np.logical_or(I != 0, V != 0))
|
|
85
|
+
V = V[ind]
|
|
86
|
+
I = I[ind]
|
|
87
|
+
|
|
88
|
+
V_ = np.append(V, np.nan * np.ones(sm_window - 1))
|
|
89
|
+
I_ = np.append(I, np.nan * np.ones(sm_window - 1))
|
|
90
|
+
|
|
91
|
+
# make 2D array of I's, V's each row_ind shifted by row_ind index
|
|
92
|
+
# i.e from array [1,3,5] we make (for sm_window=2) 2D array
|
|
93
|
+
# [ 1,3,5,nan]
|
|
94
|
+
# [nan,5,1,3]
|
|
95
|
+
# than get average from each column -> I_avg, V_avg
|
|
96
|
+
r = np.arange(sm_window)
|
|
97
|
+
|
|
98
|
+
V2 = np.outer(np.ones(sm_window), V_)
|
|
99
|
+
row_ind, col_ind = np.ogrid[: V2.shape[0], : V2.shape[1]]
|
|
100
|
+
col_ind = col_ind - r[:, np.newaxis]
|
|
101
|
+
V2 = V2[row_ind, col_ind]
|
|
102
|
+
# strip fields with nans
|
|
103
|
+
V2 = np.transpose(V2[:, (sm_window - 1) : -(sm_window - 1)])
|
|
104
|
+
|
|
105
|
+
I2 = np.outer(np.ones(sm_window), I_)
|
|
106
|
+
row_ind, col_ind = np.ogrid[: I2.shape[0], : I2.shape[1]]
|
|
107
|
+
col_ind = col_ind - r[:, np.newaxis]
|
|
108
|
+
I2 = I2[row_ind, col_ind]
|
|
109
|
+
I2 = np.transpose(I2[:, (sm_window - 1) : -(sm_window - 1)])
|
|
110
|
+
|
|
111
|
+
# get V & I averages
|
|
112
|
+
try:
|
|
113
|
+
V_avg = np.average(V2, axis=1)
|
|
114
|
+
I_avg = np.average(I2, axis=1)
|
|
115
|
+
except ZeroDivisionError:
|
|
116
|
+
# not enough data
|
|
117
|
+
return NO_BD_CONST
|
|
118
|
+
|
|
119
|
+
# find dI / dV array
|
|
120
|
+
# I'm not able to write this without cycle
|
|
121
|
+
dIdV = np.array([])
|
|
122
|
+
for i in range(V2.shape[0]):
|
|
123
|
+
with warnings.catch_warnings():
|
|
124
|
+
warnings.filterwarnings("error")
|
|
125
|
+
try:
|
|
126
|
+
dIdV = np.append(dIdV, np.polyfit(V2[i, :], I2[i, :], 1)[0])
|
|
127
|
+
except (np.RankWarning, TypeError):
|
|
128
|
+
dIdV = np.append(dIdV, np.nan)
|
|
129
|
+
|
|
130
|
+
# stripping U[n] == U[n+1] (i.e. hodlsetp) => fit cannot be sucessful =>
|
|
131
|
+
# dIdV is nan @holdstep
|
|
132
|
+
ind = np.where(np.isfinite(dIdV))
|
|
133
|
+
I_avg = I_avg[ind]
|
|
134
|
+
V_avg = V_avg[ind]
|
|
135
|
+
dIdV = dIdV[ind]
|
|
136
|
+
|
|
137
|
+
# get running BDV limit & compare
|
|
138
|
+
bd_limit_running = bd_limit + np.where(
|
|
139
|
+
allow_running_bd and V_avg > 500, (V_avg - 500.0) / 100.0, 0
|
|
140
|
+
)
|
|
141
|
+
V_avg_BD_ind = dIdV / (I_avg / V_avg) > bd_limit_running
|
|
142
|
+
V_avg_BD = V_avg[V_avg_BD_ind]
|
|
143
|
+
|
|
144
|
+
# Estimate BDV
|
|
145
|
+
BDV = np.array([])
|
|
146
|
+
|
|
147
|
+
# no break-down
|
|
148
|
+
if V_avg_BD.shape == (0,):
|
|
149
|
+
return NO_BD_CONST
|
|
150
|
+
|
|
151
|
+
# if V_avg_BD_ind[0] == True ... BDV <- V[0]
|
|
152
|
+
# for others V_avg_BD_ind[n] == True BDV <- (V_avg[n] + V_avg[n-1])/2
|
|
153
|
+
if V_avg_BD_ind[0]:
|
|
154
|
+
BDV = np.append(BDV, V[0])
|
|
155
|
+
V_avg_BD_ind[0] = False
|
|
156
|
+
|
|
157
|
+
BDV = np.append(
|
|
158
|
+
BDV,
|
|
159
|
+
(V_avg[np.where(V_avg_BD_ind)] + V_avg[np.where(V_avg_BD_ind)[0] - 1]) / 2.0,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
###########################################################################
|
|
163
|
+
## Application of additional condition ####################################
|
|
164
|
+
###########################################################################
|
|
165
|
+
if not use_additional_cond:
|
|
166
|
+
return BDV[0]
|
|
167
|
+
|
|
168
|
+
# get index if V <= BDV
|
|
169
|
+
B = np.where(np.less.outer(BDV, V))
|
|
170
|
+
col_ind = np.mgrid[: BDV.shape[0], : V.shape[0]][1]
|
|
171
|
+
col_ind[B[0], B[1]] = 0
|
|
172
|
+
V_BDV_ind = np.max(col_ind, axis=1)
|
|
173
|
+
|
|
174
|
+
back_ok_v_ind = 0
|
|
175
|
+
while True:
|
|
176
|
+
with warnings.catch_warnings():
|
|
177
|
+
warnings.filterwarnings("error")
|
|
178
|
+
try:
|
|
179
|
+
a, b = np.polyfit(
|
|
180
|
+
V[
|
|
181
|
+
max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
|
|
182
|
+
back_ok_v_ind, V_BDV_ind[0]
|
|
183
|
+
)
|
|
184
|
+
],
|
|
185
|
+
I[
|
|
186
|
+
max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
|
|
187
|
+
back_ok_v_ind, V_BDV_ind[0]
|
|
188
|
+
)
|
|
189
|
+
],
|
|
190
|
+
1,
|
|
191
|
+
)
|
|
192
|
+
except (np.RankWarning, TypeError):
|
|
193
|
+
return BDV[0]
|
|
194
|
+
|
|
195
|
+
ind = np.where(1 - (a * V + b) / I <= tolerence)[0]
|
|
196
|
+
try:
|
|
197
|
+
back_ok_v_ind = np.min(ind[ind > V_BDV_ind[0] + 1])
|
|
198
|
+
except ValueError:
|
|
199
|
+
# sensor is not going back
|
|
200
|
+
return BDV[0]
|
|
201
|
+
# hump is too long -- it cannot be skipped
|
|
202
|
+
if back_ok_v_ind - V_BDV_ind[0] > voltage_span:
|
|
203
|
+
return BDV[0]
|
|
204
|
+
|
|
205
|
+
# skip BDVs inside hump
|
|
206
|
+
ind = BDV >= V[back_ok_v_ind]
|
|
207
|
+
BDV = BDV[ind]
|
|
208
|
+
V_BDV_ind = V_BDV_ind[ind]
|
|
209
|
+
if V_avg_BD.shape == (0,):
|
|
210
|
+
return NO_BD_CONST
|
|
211
|
+
|
|
212
|
+
return NO_BD_CONST
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def scale_iv(I, T1, T2):
|
|
216
|
+
"""Normalize corrent to given temperature (T2)
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
I (array): Current
|
|
220
|
+
T1 (float): Original temperature
|
|
221
|
+
T2 (float): New temperature.
|
|
222
|
+
|
|
223
|
+
Return:
|
|
224
|
+
Array with scaled currents.
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
factor = (T2 / T1) ** 2 * np.exp((-1.21 / 8.62) * (1 / T2 - 1 / T1))
|
|
228
|
+
return factor * I
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def sensor_data_to_json(session, mdata, mod_type, logger=None):
|
|
232
|
+
"""Read a Sensor data file and return the corresponding JSon for hte PDB.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
mdata: Data as returned by :function:`read_sensor_file`.
|
|
236
|
+
logger: an object that writes messages via a write_message method
|
|
237
|
+
"""
|
|
238
|
+
# Check if we are dealing with sensors or modules
|
|
239
|
+
is_module = "Module_SN" in mdata
|
|
240
|
+
|
|
241
|
+
if is_module:
|
|
242
|
+
test = ITkDButils.get_test_skeleton(session, "MODULE", mdata["TestType"])
|
|
243
|
+
tp = "ATLAS18{}".format(mod_type[0:2])
|
|
244
|
+
area = AreaDict[tp] / cm**2
|
|
245
|
+
SN = mdata["Module_SN"]
|
|
246
|
+
|
|
247
|
+
else:
|
|
248
|
+
test = ITkDButils.get_test_skeleton(session, "SENSOR", mdata["TestType"])
|
|
249
|
+
area = AreaDict[mod_type] / cm**2
|
|
250
|
+
SN = mdata["Component"]
|
|
251
|
+
|
|
252
|
+
if logger:
|
|
253
|
+
logger.write_message("Analyzing {}\n".format(SN))
|
|
254
|
+
|
|
255
|
+
# The data arrays
|
|
256
|
+
V = np.abs(mdata["curve"]["V"])
|
|
257
|
+
I = np.abs(mdata["curve"]["I"])
|
|
258
|
+
passed = True
|
|
259
|
+
|
|
260
|
+
# Find Current @ 500V
|
|
261
|
+
try:
|
|
262
|
+
indx = np.where(V == 500)[0]
|
|
263
|
+
i_500 = I[indx][0] / area
|
|
264
|
+
except IndexError:
|
|
265
|
+
i_500 = 999
|
|
266
|
+
|
|
267
|
+
if logger:
|
|
268
|
+
logger.write_message("I @ 500V = {:.2f} nA/cm2\n".format(i_500))
|
|
269
|
+
|
|
270
|
+
# Compute current stability
|
|
271
|
+
IStability = abs(I[abs(V) == StabilityV])
|
|
272
|
+
IVariation = -1
|
|
273
|
+
if np.size(IStability) > 1: # Maybe make == 4?
|
|
274
|
+
IVariation = abs(np.std(IStability) / np.mean(IStability))
|
|
275
|
+
|
|
276
|
+
if logger:
|
|
277
|
+
logger.write_message("I stability = {:.6f} nA\n".format(IVariation))
|
|
278
|
+
|
|
279
|
+
# Search for Micro discharges
|
|
280
|
+
# Check for micro-discharge in non-normalized current,
|
|
281
|
+
# removing duplicate Voltage entries (e.g. for stability measurements)
|
|
282
|
+
comments = []
|
|
283
|
+
defects = []
|
|
284
|
+
UniqueVs, UniqueIndices = np.unique(V, return_index=True)
|
|
285
|
+
MicroDischargeV = LocateMicroDischarge(I[UniqueIndices], UniqueVs)
|
|
286
|
+
if MicroDischargeV < np.max(V):
|
|
287
|
+
comments.append("Found micro discharge: {:.1f} V\n".format(MicroDischargeV))
|
|
288
|
+
if logger:
|
|
289
|
+
logger.write_message(comments[-1])
|
|
290
|
+
|
|
291
|
+
if MicroDischargeV < UpperV:
|
|
292
|
+
txt = "microdischarge happening before {:.1f}V.".format(UpperV)
|
|
293
|
+
defects.append({
|
|
294
|
+
"name": "MicroDischarge",
|
|
295
|
+
"description": txt,
|
|
296
|
+
"properties": {}
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
if logger:
|
|
300
|
+
logger.write_message("...{}. FAILED\n".format(txt))
|
|
301
|
+
|
|
302
|
+
passed = False
|
|
303
|
+
else:
|
|
304
|
+
if MicroDischargeV == NO_BD_CONST:
|
|
305
|
+
MicroDischargeV = 700.0
|
|
306
|
+
|
|
307
|
+
test["component"] = SN
|
|
308
|
+
test["institution"] = mdata["Institute"]
|
|
309
|
+
test["runNumber"] = mdata["RunNumber"]
|
|
310
|
+
test["date"] = ITkDButils.get_db_date(
|
|
311
|
+
"{} {}".format(mdata["Date"], mdata["Time"])
|
|
312
|
+
)
|
|
313
|
+
test["passed"] = passed
|
|
314
|
+
test["problems"] = False
|
|
315
|
+
test["properties"]["VBIAS_SMU"] = mdata["Vbias_SMU"]
|
|
316
|
+
test["properties"]["RSERIES"] = mdata["Rseries"]
|
|
317
|
+
test["properties"]["TEST_DMM"] = mdata["Test_DMM"]
|
|
318
|
+
test["properties"]["RSHUNT"] = mdata["Rshunt"]
|
|
319
|
+
test["properties"]["RUNNUMBER"] = mdata["RunNumber"]
|
|
320
|
+
test["properties"]["COMMENTS"] = mdata["Comments"]
|
|
321
|
+
test["properties"]["ALGORITHM_VERSION"] = "0.0.0"
|
|
322
|
+
if is_module:
|
|
323
|
+
test["properties"]["SOFTWARE_TYPE_VERSION"] = "pyProbe"
|
|
324
|
+
test["properties"]["MODULE_STAGE"] = mdata["Module_Stage"]
|
|
325
|
+
|
|
326
|
+
test["results"]["TEMPERATURE"] = mdata["Temperature"]
|
|
327
|
+
test["results"]["HUMIDITY"] = mdata["Humidity"]
|
|
328
|
+
test["results"]["VBD"] = MicroDischargeV
|
|
329
|
+
test["results"]["I_500V"] = i_500
|
|
330
|
+
test["results"]["VOLTAGE"] = -np.abs(V)
|
|
331
|
+
test["results"]["CURRENT"] = -np.abs(I)
|
|
332
|
+
test["results"]["RMS_STABILITY"] = IVariation
|
|
333
|
+
test["results"]["SHUNT_VOLTAGE"] = np.zeros(V.shape)
|
|
334
|
+
test["defects"] = defects
|
|
335
|
+
test["comments"] = comments
|
|
336
|
+
|
|
337
|
+
return test
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def read_sensor_file(fnam):
|
|
341
|
+
"""Read a data file. Return dictionary with all teh data."""
|
|
342
|
+
labels = []
|
|
343
|
+
metadata = {}
|
|
344
|
+
with open(fnam, "r", encoding="utf-8") as ifile:
|
|
345
|
+
first = True
|
|
346
|
+
for line in ifile:
|
|
347
|
+
if first:
|
|
348
|
+
first = False
|
|
349
|
+
ipos = line.rfind(".")
|
|
350
|
+
metadata["fname"] = line[:ipos]
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
if line.find("Voltage [V]") >= 0 or line.find("Voltage[V]") >= 0:
|
|
354
|
+
labels = line.split("\t")
|
|
355
|
+
break
|
|
356
|
+
|
|
357
|
+
rc = line.find(":")
|
|
358
|
+
if rc >= 0:
|
|
359
|
+
key = line[:rc].strip()
|
|
360
|
+
val = line[rc + 1 :].strip()
|
|
361
|
+
if key in ["Temperature", "Humidity"]:
|
|
362
|
+
metadata[key] = float(val)
|
|
363
|
+
else:
|
|
364
|
+
metadata[key] = val
|
|
365
|
+
|
|
366
|
+
V = []
|
|
367
|
+
I = []
|
|
368
|
+
S = []
|
|
369
|
+
for line in ifile:
|
|
370
|
+
data = [float(s) for s in line.split()]
|
|
371
|
+
V.append(data[0])
|
|
372
|
+
I.append(data[1])
|
|
373
|
+
try:
|
|
374
|
+
S.append(data[2])
|
|
375
|
+
except IndexError:
|
|
376
|
+
S.append(0.0)
|
|
377
|
+
|
|
378
|
+
metadata["curve"] = {
|
|
379
|
+
"V": np.abs(np.array(V)),
|
|
380
|
+
"I": np.abs(np.array(I)),
|
|
381
|
+
"S": np.abs(np.array(S)),
|
|
382
|
+
"labels": labels,
|
|
383
|
+
}
|
|
384
|
+
return metadata
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def save_sensor_data(fnam, mdata, name=None):
|
|
388
|
+
"""Save sensor dat in file with the proper format.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
fnam: file name or file object.
|
|
392
|
+
mdata (dict): data as returned by :function:`read_sensor_file`
|
|
393
|
+
|
|
394
|
+
"""
|
|
395
|
+
if hasattr(fnam, "write") and callable(fnam.write):
|
|
396
|
+
data_out = fnam
|
|
397
|
+
if name:
|
|
398
|
+
fnam = name
|
|
399
|
+
else:
|
|
400
|
+
fnam = build_file_name(mdata)
|
|
401
|
+
|
|
402
|
+
else:
|
|
403
|
+
data_out = open(fnam, 'w', encoding="utf-8")
|
|
404
|
+
fnam = Path(fnam).name
|
|
405
|
+
|
|
406
|
+
is_module = "Module_SN" in mdata
|
|
407
|
+
if is_module:
|
|
408
|
+
SN = mdata["Module_SN"]
|
|
409
|
+
else:
|
|
410
|
+
SN = mdata["Component"]
|
|
411
|
+
|
|
412
|
+
if is_module:
|
|
413
|
+
items = [
|
|
414
|
+
"Type",
|
|
415
|
+
"Wafer",
|
|
416
|
+
"Module_SN",
|
|
417
|
+
"Module_Stage",
|
|
418
|
+
"Date",
|
|
419
|
+
"Time",
|
|
420
|
+
"Institute",
|
|
421
|
+
"TestType",
|
|
422
|
+
"Vbias_SMU",
|
|
423
|
+
"Rseries",
|
|
424
|
+
"Test_DMM",
|
|
425
|
+
"Rshunt",
|
|
426
|
+
"Software type and version, fw version",
|
|
427
|
+
"RunNumber",
|
|
428
|
+
"Temperature",
|
|
429
|
+
"Humidity",
|
|
430
|
+
"Comments",
|
|
431
|
+
]
|
|
432
|
+
else:
|
|
433
|
+
items = [
|
|
434
|
+
"Type",
|
|
435
|
+
"Batch",
|
|
436
|
+
"Wafer",
|
|
437
|
+
"Component",
|
|
438
|
+
"Date",
|
|
439
|
+
"Time",
|
|
440
|
+
"Institute",
|
|
441
|
+
"TestType",
|
|
442
|
+
"Vbias_SMU",
|
|
443
|
+
"Rseries",
|
|
444
|
+
"Test_DMM",
|
|
445
|
+
"Rshunt",
|
|
446
|
+
"RunNumber",
|
|
447
|
+
"Temperature",
|
|
448
|
+
"Humidity",
|
|
449
|
+
"Comments",
|
|
450
|
+
]
|
|
451
|
+
|
|
452
|
+
data_out.write("{}\n".format(fnam))
|
|
453
|
+
for key in items:
|
|
454
|
+
if key == "Module_SN" or key == "Component":
|
|
455
|
+
data_out.write("{}: {}\n".format(key, SN))
|
|
456
|
+
else:
|
|
457
|
+
data_out.write("{}: {}\n".format(key, mdata[key]))
|
|
458
|
+
|
|
459
|
+
labels = [lbl for lbl in mdata["curve"]["labels"]]
|
|
460
|
+
if len(labels) < 3:
|
|
461
|
+
labels.append("Shunt_voltage [mV]")
|
|
462
|
+
|
|
463
|
+
for il, label in enumerate(labels):
|
|
464
|
+
if il:
|
|
465
|
+
data_out.write("\t")
|
|
466
|
+
data_out.write(label)
|
|
467
|
+
data_out.write("\n")
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
# The data arrays
|
|
471
|
+
V = np.abs(mdata["curve"]["V"])
|
|
472
|
+
I = np.abs(mdata["curve"]["I"])
|
|
473
|
+
S = np.abs(mdata["curve"]["S"])
|
|
474
|
+
|
|
475
|
+
ndata = len(V)
|
|
476
|
+
for i in range(ndata):
|
|
477
|
+
data_out.write("{:10.2f}\t{:10.2f}\t{:10.2f}\n".format(V[i], I[i], S[i]))
|
|
478
|
+
|
|
479
|
+
print(data_out.name)
|
|
480
|
+
data_out.close()
|
|
481
|
+
|
|
482
|
+
def build_file_name(mdata):
|
|
483
|
+
"""Create a file name from the data."""
|
|
484
|
+
is_module = "Module_SN" in mdata
|
|
485
|
+
if is_module:
|
|
486
|
+
SN = mdata["Module_SN"]
|
|
487
|
+
fnam = "{}_{}_IV_{}".format(SN, mdata["Module_Stage"], mdata["RunNumber"])
|
|
488
|
+
else:
|
|
489
|
+
SN = mdata["Component"]
|
|
490
|
+
fnam = "{}-W{}_IV_{}".format(mdata["Batch"], mdata["Wafer"], mdata["RunNumber"])
|
|
491
|
+
|
|
492
|
+
return fnam
|