@@ -21,6 +21,7 @@ def __init__(self, uuid, cfg, signal, *args, **kwargs):
21
21
# Instrument Specific Attributes
22
22
self ._parser : Optional [HyperBBParser ] = None
23
23
self .signal_reconstructed = None
24
+ self .invalid_packet_alarm_triggered = False
24
25
# Default serial communication parameters
25
26
self .default_serial_baudrate = 9600
26
27
self .default_serial_timeout = 1
@@ -49,7 +50,9 @@ def setup(self, cfg):
49
50
raise ValueError ('Missing calibration plaque file (*.mat)' )
50
51
if 'temperature_file' not in cfg .keys ():
51
52
raise ValueError ('Missing calibration temperature file (*.mat)' )
52
- self ._parser = HyperBBParser (cfg ['plaque_file' ], cfg ['temperature_file' ])
53
+ if 'firmware_version' not in cfg .keys ():
54
+ cfg ['firmware_version' ] = 1
55
+ self ._parser = HyperBBParser (cfg ['plaque_file' ], cfg ['temperature_file' ], cfg ['firmware_version' ])
53
56
self .signal_reconstructed = np .empty (len (self ._parser .wavelength )) * np .nan
54
57
# Overload cfg with received data
55
58
prod_var_names = ['beta_u' , 'bb' ]
@@ -65,13 +68,25 @@ def setup(self, cfg):
65
68
self .spectrum_plot_x_values = [self ._parser .wavelength ]
66
69
# Update Active Timeseries Variables
67
70
self .widget_active_timeseries_variables_names = ['beta(%d)' % x for x in self ._parser .wavelength ]
71
+ self .widget_active_timeseries_variables_selected = []
68
72
self .active_timeseries_wavelength = np .zeros (len (self ._parser .wavelength ), dtype = bool )
69
73
for wl in np .arange (450 , 700 , 50 ):
70
74
channel_name = 'beta(%d)' % self ._parser .wavelength [np .argmin (np .abs (self ._parser .wavelength - wl ))]
71
75
self .update_active_timeseries_variables (channel_name , True )
76
+ # Reset Alarm
77
+ self .invalid_packet_alarm_triggered = False
72
78
73
79
def parse (self , packet ):
74
- return self ._parser .parse (packet )
80
+ if len (packet ) == 0 : # Empty lines on firmware v2 at end of wavelength scan
81
+ return []
82
+ data = self ._parser .parse (packet )
83
+ if len (data ) == 0 :
84
+ self .signal .packet_corrupted .emit ()
85
+ if self .invalid_packet_alarm_triggered is False :
86
+ self .invalid_packet_alarm_triggered = True
87
+ self .logger .warning ('Unable to parse frame. Check firmware version.' )
88
+ self .signal .alarm_custom .emit ('Unable to parse frame.' , 'Check HyperBB firmware version in "Setup".' )
89
+ return data
75
90
76
91
def handle_data (self , raw , timestamp ):
77
92
beta_u , bb , wl , gain , net_ref_zero_flag = self ._parser .calibrate (np .array ([raw ], dtype = float ))
@@ -122,27 +137,44 @@ def update_active_timeseries_variables(self, name, state):
122
137
['beta(%d)' % wl for wl in self ._parser .wavelength [self .active_timeseries_wavelength ]]
123
138
124
139
125
- class MetaHyperBBParser (type ):
126
- def __init__ (cls , name , bases , dct ):
127
- cls .FRAME_VARIABLES = ['ScanIdx' , 'DataIdx' , 'Date' , 'Time' , 'StepPos' , 'wl' , 'LedPwr' , 'PmtGain' , 'NetSig1' ,
128
- 'SigOn1' , 'SigOn1Std' , 'RefOn' , 'RefOnStd' , 'SigOff1' , 'SigOff1Std' , 'RefOff' ,
129
- 'RefOffStd' , 'SigOn2' , 'SigOn2Std' , 'SigOn3' , 'SigOn3Std' , 'SigOff2' , 'SigOff2Std' ,
130
- 'SigOff3' , 'SigOff3Std' , 'LedTemp' , 'WaterTemp' , 'Depth' , 'Debug1' , 'zDistance' ]
131
- cls .FRAME_TYPES = [int , int , str , str , int , int , int , int , int ,
132
- float , float , float , float , float , float , float ,
133
- float , float , float , float , float , float , float ,
134
- float , float , float , float , float , int , int ]
135
- # FRAME_PRECISIONS = ['%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d',
136
- # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
137
- # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
138
- # '%.1f', '%.1f', '%.2f', '%.2f', '%.2f', '%d', '%d']
139
- cls .FRAME_PRECISIONS = ['%s' ] * len (cls .FRAME_VARIABLES )
140
- for x in cls .FRAME_VARIABLES :
141
- setattr (cls , f'idx_{ x } ' , cls .FRAME_VARIABLES .index (x ))
142
-
140
+ class HyperBBParser ():
141
+ def __init__ (self , plaque_cal_file , temperature_cal_file , firmware_version = 1 ):
142
+ # Frame Parser
143
+ self .firmware_version = firmware_version
144
+ if firmware_version == 1 :
145
+ self .FRAME_VARIABLES = ['ScanIdx' , 'DataIdx' , 'Date' , 'Time' , 'StepPos' , 'wl' , 'LedPwr' , 'PmtGain' , 'NetSig1' ,
146
+ 'SigOn1' , 'SigOn1Std' , 'RefOn' , 'RefOnStd' , 'SigOff1' , 'SigOff1Std' , 'RefOff' ,
147
+ 'RefOffStd' , 'SigOn2' , 'SigOn2Std' , 'SigOn3' , 'SigOn3Std' , 'SigOff2' , 'SigOff2Std' ,
148
+ 'SigOff3' , 'SigOff3Std' , 'LedTemp' , 'WaterTemp' , 'Depth' , 'Debug1' , 'zDistance' ]
149
+ self .FRAME_TYPES = [int , int , str , str , int , int , int , int , int ,
150
+ float , float , float , float , float , float , float ,
151
+ float , float , float , float , float , float , float ,
152
+ float , float , float , float , float , int , int ]
153
+ # FRAME_PRECISIONS = ['%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d',
154
+ # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
155
+ # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
156
+ # '%.1f', '%.1f', '%.2f', '%.2f', '%.2f', '%d', '%d']
157
+ self .FRAME_PRECISIONS = ['%s' ] * len (self .FRAME_VARIABLES )
158
+ for x in self .FRAME_VARIABLES :
159
+ setattr (self , f'idx_{ x } ' , self .FRAME_VARIABLES .index (x ))
160
+ elif firmware_version == 2 :
161
+ self .FRAME_VARIABLES = ['ScanIdx' , 'Date' , 'Time' , 'wl' , 'PmtGain' ,
162
+ 'NetRef' , 'NetSig1' , 'NetSig2' , 'NetSig3' ,
163
+ 'LedTemp' , 'WaterTemp' , 'Depth' , 'SupplyVolt' , 'ChSaturated' ]
164
+ self .FRAME_TYPES = [int , str , str , int , int ,
165
+ float , float , float , float , float ,
166
+ float , float , float , float , float ]
167
+ # FRAME_PRECISIONS = ['%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d',
168
+ # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
169
+ # '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f', '%.1f',
170
+ # '%.1f', '%.1f', '%.2f', '%.2f', '%.2f', '%d', '%d']
171
+ self .FRAME_PRECISIONS = ['%s' ] * len (self .FRAME_VARIABLES )
172
+ for x in self .FRAME_VARIABLES :
173
+ setattr (self , f'idx_{ x } ' , self .FRAME_VARIABLES .index (x ))
174
+ else :
175
+ raise ValueError ('Firmware version not supported.' )
143
176
144
- class HyperBBParser (metaclass = MetaHyperBBParser ):
145
- def __init__ (self , plaque_cal_file , temperature_cal_file ):
177
+ # Instrument Specific Attributes
146
178
self ._theta = float ('nan' )
147
179
self .Xp = float ('nan' )
148
180
@@ -229,22 +261,29 @@ def calibrate(self, raw):
229
261
raw = np .delete (raw , sel , axis = 0 )
230
262
# Shortcuts
231
263
wl = raw [:, self .idx_wl ]
232
- # Remove saturated reading
233
- raw [raw [:, self .idx_SigOn1 ] > self .saturation_level , self .idx_SigOn1 ] = np .nan
234
- raw [raw [:, self .idx_SigOn2 ] > self .saturation_level , self .idx_SigOn2 ] = np .nan
235
- raw [raw [:, self .idx_SigOn3 ] > self .saturation_level , self .idx_SigOn3 ] = np .nan
236
- raw [raw [:, self .idx_SigOff1 ] > self .saturation_level , self .idx_SigOff1 ] = np .nan
237
- raw [raw [:, self .idx_SigOff2 ] > self .saturation_level , self .idx_SigOff2 ] = np .nan
238
- raw [raw [:, self .idx_SigOff3 ] > self .saturation_level , self .idx_SigOff3 ] = np .nan
239
- # Calculate net signal for ref, low gain (2), high gain (3)
240
- net_ref = raw [:, self .idx_RefOn ] - raw [:, self .idx_RefOff ]
241
- net_sig2 = raw [:, self .idx_SigOn2 ] - raw [:, self .idx_SigOff2 ]
242
- net_sig3 = raw [:, self .idx_SigOn3 ] - raw [:, self .idx_SigOff3 ]
243
- net_ref_zero_flag = np .any (net_ref == 0 )
244
- net_ref [net_ref == 0 ] = np .nan
245
- scat1 = raw [:, self .idx_NetSig1 ] / net_ref
246
- scat2 = net_sig2 / net_ref
247
- scat3 = net_sig3 / net_ref
264
+ if self .firmware_version == 1 :
265
+ # Remove saturated reading
266
+ raw [raw [:, self .idx_SigOn1 ] > self .saturation_level , self .idx_SigOn1 ] = np .nan
267
+ raw [raw [:, self .idx_SigOn2 ] > self .saturation_level , self .idx_SigOn2 ] = np .nan
268
+ raw [raw [:, self .idx_SigOn3 ] > self .saturation_level , self .idx_SigOn3 ] = np .nan
269
+ raw [raw [:, self .idx_SigOff1 ] > self .saturation_level , self .idx_SigOff1 ] = np .nan
270
+ raw [raw [:, self .idx_SigOff2 ] > self .saturation_level , self .idx_SigOff2 ] = np .nan
271
+ raw [raw [:, self .idx_SigOff3 ] > self .saturation_level , self .idx_SigOff3 ] = np .nan
272
+ # Calculate net signal for ref, low gain (2), high gain (3)
273
+ net_ref = raw [:, self .idx_RefOn ] - raw [:, self .idx_RefOff ]
274
+ net_sig2 = raw [:, self .idx_SigOn2 ] - raw [:, self .idx_SigOff2 ]
275
+ net_sig3 = raw [:, self .idx_SigOn3 ] - raw [:, self .idx_SigOff3 ]
276
+ net_ref_zero_flag = np .any (net_ref == 0 )
277
+ net_ref [net_ref == 0 ] = np .nan
278
+ scat1 = raw [:, self .idx_NetSig1 ] / net_ref
279
+ scat2 = net_sig2 / net_ref
280
+ scat3 = net_sig3 / net_ref
281
+ else : # Assume firmware 2
282
+ net_ref_zero_flag = np .any (raw [:, self .idx_NetRef ] == 0 )
283
+ # TODO Check if need to flag saturated (ChSaturated)
284
+ scat1 = raw [:, self .idx_NetSig1 ] / raw [:, self .idx_NetRef ]
285
+ scat2 = raw [:, self .idx_NetSig2 ] / raw [:, self .idx_NetRef ]
286
+ scat3 = raw [:, self .idx_NetSig3 ] / raw [:, self .idx_NetRef ]
248
287
# Subtract dark offset
249
288
scat1_dark_removed = scat1 - self .f_dark_cal_scat_1 (raw [:, self .idx_PmtGain ], wl )
250
289
scat2_dark_removed = scat2 - self .f_dark_cal_scat_2 (raw [:, self .idx_PmtGain ], wl )
@@ -264,9 +303,15 @@ def calibrate(self, raw):
264
303
scatx_corrected [np .isnan (scatx_corrected )] = scat2_t_corrected [np .isnan (scatx_corrected )] # otherwise low gain
265
304
scatx_corrected [np .isnan (scatx_corrected )] = scat1_t_corrected [np .isnan (scatx_corrected )] # otherwise raw pmt
266
305
# Keep gain setting
267
- gain = np .ones ((len (raw ), 1 )) * 3
268
- gain [np .isnan (raw [:, self .idx_SigOn3 ])] = 2
269
- gain [np .isnan (raw [:, self .idx_SigOn2 ])] = 1
306
+ if self .firmware_version == 1 :
307
+ gain = np .ones ((len (raw ), 1 )) * 3
308
+ gain [np .isnan (raw [:, self .idx_SigOn3 ])] = 2
309
+ gain [np .isnan (raw [:, self .idx_SigOn2 ])] = 1
310
+ else :
311
+ # TODO Check if method is correct based on documentation
312
+ gain = np .ones ((len (raw ), 1 )) * 3
313
+ gain [raw [:, self .idx_ChSaturated ] == 3 ] = 2
314
+ gain [raw [:, self .idx_ChSaturated ] == 2 ] = 1
270
315
# Calculate beta
271
316
uwl = np .unique (wl )
272
317
# mu = pchip_interpolate(self.wavelength, self.mu, uwl) # Optimized as no need of interpolation as same wavelength as calibration
0 commit comments