Show message if ADC_Data is clipped (solves #16)
This commit is contained in:
@@ -19,6 +19,7 @@ import tables
|
|||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
|
class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
|
||||||
|
default_bit_depth = 14
|
||||||
"""
|
"""
|
||||||
Represents the result of an ADC, encapsulating data and metadata
|
Represents the result of an ADC, encapsulating data and metadata
|
||||||
for processing, visualization, and export.
|
for processing, visualization, and export.
|
||||||
@@ -50,6 +51,7 @@ class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.ylabel = "Samples [Digits]"
|
self.ylabel = "Samples [Digits]"
|
||||||
self.lock=threading.RLock()
|
self.lock=threading.RLock()
|
||||||
self.nChannels = 0
|
self.nChannels = 0
|
||||||
|
self.is_clipped = False
|
||||||
|
|
||||||
# using no argument for initialization
|
# using no argument for initialization
|
||||||
if (x is None) and (y is None) and (index is None) and (sampl_freq is None) and (desc is None) and (job_id is None) and (job_date is None):
|
if (x is None) and (y is None) and (index is None) and (sampl_freq is None) and (desc is None) and (job_id is None) and (job_date is None):
|
||||||
@@ -107,6 +109,44 @@ class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
|
|||||||
return self.cont_data
|
return self.cont_data
|
||||||
|
|
||||||
|
|
||||||
|
def check_clipping(self, bit_depth=None):
|
||||||
|
"""
|
||||||
|
Check if any data in y is clipped.
|
||||||
|
For a 14-bit ADC, clipping occurs at 2**13 (8192) or 2**13-1 (8191).
|
||||||
|
In general, for a bit_depth-bit signed ADC, limits are 2**(bit_depth-1) and -(2**(bit_depth-1)).
|
||||||
|
"""
|
||||||
|
if not self.contains_data():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if bit_depth is None:
|
||||||
|
# try to get bit_depth from description
|
||||||
|
if hasattr(self, "description") and self.description is not None:
|
||||||
|
if "adc_bit_depth" in self.description:
|
||||||
|
try:
|
||||||
|
bit_depth = int(self.description["adc_bit_depth"])
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
bit_depth = ADC_Result.default_bit_depth
|
||||||
|
else:
|
||||||
|
bit_depth = ADC_Result.default_bit_depth
|
||||||
|
else:
|
||||||
|
bit_depth = ADC_Result.default_bit_depth
|
||||||
|
|
||||||
|
limit_pos = 2**(bit_depth-1) - 1
|
||||||
|
limit_neg = -2**(bit_depth-1)
|
||||||
|
|
||||||
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
self.is_clipped = False
|
||||||
|
for ch_data in self.y:
|
||||||
|
if numpy.any(ch_data >= limit_pos) or numpy.any(ch_data <= limit_neg):
|
||||||
|
self.is_clipped = True
|
||||||
|
break
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
return self.is_clipped
|
||||||
|
|
||||||
|
|
||||||
def add_sample_space(self, samples):
|
def add_sample_space(self, samples):
|
||||||
"Adds space for n samples, where n can also be negative (deletes space). New space is filled up with \"0\""
|
"Adds space for n samples, where n can also be negative (deletes space). New space is filled up with \"0\""
|
||||||
|
|
||||||
@@ -575,4 +615,5 @@ def read_from_hdf(hdf_node):
|
|||||||
for ch in range(adc_data.shape[1]):
|
for ch in range(adc_data.shape[1]):
|
||||||
adc.y.append(adc_data[:,ch])
|
adc.y.append(adc_data[:,ch])
|
||||||
|
|
||||||
|
adc.check_clipping()
|
||||||
return adc
|
return adc
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.xlabel = "Time / s"
|
self.xlabel = "Time / s"
|
||||||
self.ylabel = "Avg. Samples [Digits]"
|
self.ylabel = "Avg. Samples [Digits]"
|
||||||
self.lock=threading.RLock()
|
self.lock=threading.RLock()
|
||||||
|
self.is_clipped = False
|
||||||
|
|
||||||
self.common_descriptions=None
|
self.common_descriptions=None
|
||||||
self.time_period=[]
|
self.time_period=[]
|
||||||
@@ -616,6 +617,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
|
||||||
else:
|
else:
|
||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
|
||||||
|
r.is_clipped = self.is_clipped
|
||||||
r.job_id = self.job_id
|
r.job_id = self.job_id
|
||||||
r.job_ids = self.job_ids.copy()
|
r.job_ids = self.job_ids.copy()
|
||||||
r.time_period = self.time_period[:] if self.time_period is not None else None
|
r.time_period = self.time_period[:] if self.time_period is not None else None
|
||||||
@@ -645,6 +647,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = 1, index = other.index, sampl_freq = other.sampling_rate, error = True)
|
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = 1, index = other.index, sampl_freq = other.sampling_rate, error = True)
|
||||||
else:
|
else:
|
||||||
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, index = other.index, sampl_freq = other.sampling_rate, n = 1, error = False)
|
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, index = other.index, sampl_freq = other.sampling_rate, n = 1, error = False)
|
||||||
|
if hasattr(other, "is_clipped"):
|
||||||
|
r.is_clipped = other.is_clipped
|
||||||
r.time_period=[other.job_date,other.job_date]
|
r.time_period=[other.job_date,other.job_date]
|
||||||
r.job_id = other.job_id
|
r.job_id = other.job_id
|
||||||
r.common_descriptions=other.description.copy()
|
r.common_descriptions=other.description.copy()
|
||||||
@@ -678,6 +682,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = True)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = True)
|
||||||
else:
|
else:
|
||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = False)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = False)
|
||||||
|
r.is_clipped = self.is_clipped or (hasattr(other, "is_clipped") and other.is_clipped)
|
||||||
r.time_period=[min(self.time_period[0],other.job_date),
|
r.time_period=[min(self.time_period[0],other.job_date),
|
||||||
max(self.time_period[1],other.job_date)]
|
max(self.time_period[1],other.job_date)]
|
||||||
r.job_id = other.job_id
|
r.job_id = other.job_id
|
||||||
@@ -744,13 +749,14 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
|
|
||||||
for i in range(self.get_number_of_channels()):
|
for i in range(self.get_number_of_channels()):
|
||||||
tmp_y.append(self.y[i] + other.y[i])
|
tmp_y.append(self.y[i] + other.y[i])
|
||||||
tmp_ysquare.append(self.y_square[i] + other.y_square[i])
|
if self.uses_statistics():
|
||||||
|
tmp_ysquare.append(self.y_square[i] + other.y_square[i])
|
||||||
|
|
||||||
if self.uses_statistics():
|
if self.uses_statistics():
|
||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = True)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = True)
|
||||||
else:
|
else:
|
||||||
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = False)
|
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = False)
|
||||||
|
r.is_clipped = self.is_clipped or (hasattr(other, "is_clipped") and other.is_clipped)
|
||||||
r.time_period=[min(self.time_period[0],other.time_period[0]),
|
r.time_period=[min(self.time_period[0],other.time_period[0]),
|
||||||
max(self.time_period[1],other.time_period[1])]
|
max(self.time_period[1],other.time_period[1])]
|
||||||
r.job_id = other.job_id
|
r.job_id = other.job_id
|
||||||
@@ -954,6 +960,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.set_title(self.__title_pattern % self.n)
|
self.set_title(self.__title_pattern % self.n)
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
|
if hasattr(other, "is_clipped"):
|
||||||
|
self.is_clipped = other.is_clipped
|
||||||
self.time_period=[other.job_date,other.job_date]
|
self.time_period=[other.job_date,other.job_date]
|
||||||
self.job_id = other.job_id # added by Oleg Petrov
|
self.job_id = other.job_id # added by Oleg Petrov
|
||||||
self.job_ids[other.job_id] = other.get_job_date()
|
self.job_ids[other.job_id] = other.get_job_date()
|
||||||
@@ -982,6 +990,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.y_square[i] += numpy.array(other.y[i], dtype="float32") ** 2
|
self.y_square[i] += numpy.array(other.y[i], dtype="float32") ** 2
|
||||||
|
|
||||||
self.n += 1
|
self.n += 1
|
||||||
|
if hasattr(other, "is_clipped"):
|
||||||
|
self.is_clipped = self.is_clipped or other.is_clipped
|
||||||
self.time_period=[min(self.time_period[0],other.job_date),
|
self.time_period=[min(self.time_period[0],other.job_date),
|
||||||
max(self.time_period[1],other.job_date)]
|
max(self.time_period[1],other.job_date)]
|
||||||
|
|
||||||
@@ -1020,6 +1030,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.y_square.append(self.y[i] ** 2)
|
self.y_square.append(self.y[i] ** 2)
|
||||||
|
|
||||||
self.set_title(self.__title_pattern % self.n)
|
self.set_title(self.__title_pattern % self.n)
|
||||||
|
if hasattr(other, "is_clipped"):
|
||||||
|
self.is_clipped = other.is_clipped
|
||||||
self.common_descriptions=other.common_desriptions.copy()
|
self.common_descriptions=other.common_desriptions.copy()
|
||||||
self.time_period=other.time_period[:]
|
self.time_period=other.time_period[:]
|
||||||
self.job_id = other.job_id # added by Oleg Petrov
|
self.job_id = other.job_id # added by Oleg Petrov
|
||||||
@@ -1049,6 +1061,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
|
|||||||
self.y_square[i] += other.y_square[i]
|
self.y_square[i] += other.y_square[i]
|
||||||
|
|
||||||
self.n += other.n
|
self.n += other.n
|
||||||
|
if hasattr(other, "is_clipped"):
|
||||||
|
self.is_clipped = self.is_clipped or other.is_clipped
|
||||||
self.time_period=[min(self.time_period[0],other.time_period[0]),
|
self.time_period=[min(self.time_period[0],other.time_period[0]),
|
||||||
max(self.time_period[1],other.time_period[1])]
|
max(self.time_period[1],other.time_period[1])]
|
||||||
self.job_id = other.job_id
|
self.job_id = other.job_id
|
||||||
|
|||||||
@@ -2058,7 +2058,8 @@ pygobject version %(pygobject)s
|
|||||||
"data_pool_write_interval": self.config_data_pool_write_interval_entry.get_text( ),
|
"data_pool_write_interval": self.config_data_pool_write_interval_entry.get_text( ),
|
||||||
"data_pool_complib": complib,
|
"data_pool_complib": complib,
|
||||||
"data_pool_comprate": self.config_data_pool_comprate.get_value_as_int( ),
|
"data_pool_comprate": self.config_data_pool_comprate.get_value_as_int( ),
|
||||||
"script_font": self.config_script_font_button.get_font_name( )
|
"script_font": self.config_script_font_button.get_font_name( ),
|
||||||
|
"adc_bit_depth": ADC_Result.default_bit_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
def set( self, config ):
|
def set( self, config ):
|
||||||
@@ -2085,6 +2086,11 @@ pygobject version %(pygobject)s
|
|||||||
if "script_font" in config:
|
if "script_font" in config:
|
||||||
self.config_script_font_button.set_font_name( config[ "script_font" ] )
|
self.config_script_font_button.set_font_name( config[ "script_font" ] )
|
||||||
self.set_script_font_handler( None )
|
self.set_script_font_handler( None )
|
||||||
|
if "adc_bit_depth" in config:
|
||||||
|
try:
|
||||||
|
ADC_Result.default_bit_depth = int(config["adc_bit_depth"])
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
if "data_pool_complib" in config:
|
if "data_pool_complib" in config:
|
||||||
# find combo-box entry and make it active...
|
# find combo-box entry and make it active...
|
||||||
model = self.config_data_pool_complib.get_model( )
|
model = self.config_data_pool_complib.get_model( )
|
||||||
@@ -2373,6 +2379,7 @@ class MonitorWidgets:
|
|||||||
self.update_counter = 0
|
self.update_counter = 0
|
||||||
self.update_counter_lock = threading.Lock( )
|
self.update_counter_lock = threading.Lock( )
|
||||||
self.description_text = None
|
self.description_text = None
|
||||||
|
self.clipping_marker = None
|
||||||
|
|
||||||
def source_list_reset( self ):
|
def source_list_reset( self ):
|
||||||
self.display_source_treestore.clear( )
|
self.display_source_treestore.clear( )
|
||||||
@@ -2955,6 +2962,12 @@ class MonitorWidgets:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.description_text = None
|
self.description_text = None
|
||||||
|
if self.clipping_marker is not None:
|
||||||
|
try:
|
||||||
|
self.clipping_marker.remove()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.clipping_marker = None
|
||||||
self.matplot_canvas.draw_idle( )
|
self.matplot_canvas.draw_idle( )
|
||||||
|
|
||||||
def update_display( self, subject=None ):
|
def update_display( self, subject=None ):
|
||||||
@@ -3142,6 +3155,20 @@ class MonitorWidgets:
|
|||||||
pass
|
pass
|
||||||
self.description_text = None
|
self.description_text = None
|
||||||
|
|
||||||
|
if getattr(in_result, "is_clipped", False):
|
||||||
|
if self.clipping_marker is None:
|
||||||
|
self.clipping_marker = self.matplot_axes.text(0.5, 0.95, "ADC OVERFLOW",
|
||||||
|
transform=self.matplot_axes.transAxes,
|
||||||
|
color="red", fontsize=20, fontweight="bold",
|
||||||
|
ha="center", va="top",
|
||||||
|
bbox=dict(facecolor='white', alpha=0.7, edgecolor='red'))
|
||||||
|
elif self.clipping_marker is not None:
|
||||||
|
try:
|
||||||
|
self.clipping_marker.remove()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.clipping_marker = None
|
||||||
|
|
||||||
# Draw it!
|
# Draw it!
|
||||||
self.matplot_canvas.draw_idle( )
|
self.matplot_canvas.draw_idle( )
|
||||||
del in_result
|
del in_result
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ class ResultReader:
|
|||||||
self.result.index = [(0,tmp_size-1)]
|
self.result.index = [(0,tmp_size-1)]
|
||||||
tmp_index += tmp_size
|
tmp_index += tmp_size
|
||||||
self.result.cont_data=True
|
self.result.cont_data=True
|
||||||
|
self.result.check_clipping()
|
||||||
tmp_part = None
|
tmp_part = None
|
||||||
|
|
||||||
def __parseFile_expat(self, in_file):
|
def __parseFile_expat(self, in_file):
|
||||||
@@ -310,6 +311,7 @@ class ResultReader:
|
|||||||
self.result.index.append((tmp_sample_counter,tmp_sample_counter+tmp_size-1))
|
self.result.index.append((tmp_sample_counter,tmp_sample_counter+tmp_size-1))
|
||||||
tmp_sample_counter+=tmp_size
|
tmp_sample_counter+=tmp_size
|
||||||
self.result.cont_data=True
|
self.result.cont_data=True
|
||||||
|
self.result.check_clipping()
|
||||||
|
|
||||||
# Callback when a xml start tag is found
|
# Callback when a xml start tag is found
|
||||||
def __xmlStartTagFound(self, in_name, in_attribute):
|
def __xmlStartTagFound(self, in_name, in_attribute):
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import tables
|
import tables
|
||||||
|
import numpy
|
||||||
|
|
||||||
r = MeasurementResult("test")
|
r = MeasurementResult("test")
|
||||||
|
|
||||||
@@ -9,6 +10,12 @@ def result():
|
|||||||
print("h5 already opened")
|
print("h5 already opened")
|
||||||
accu = Accumulation(error=False)
|
accu = Accumulation(error=False)
|
||||||
for num,ts in enumerate(results):
|
for num,ts in enumerate(results):
|
||||||
|
x = numpy.linspace(0, 1, 10)
|
||||||
|
y1 = [numpy.array([0, 4000, 8190, 8191, 8192, 0, -8191, -8192, -8193, 0], dtype="int16")]
|
||||||
|
|
||||||
|
# Test with default (14-bit)
|
||||||
|
adc1 = ADC_Result(x=x, y=y1, index=[(0,9)], sampl_freq=1000, desc={}, job_id=1, job_date=0)
|
||||||
|
accu.is_clipped = True
|
||||||
data["accu"] = accu
|
data["accu"] = accu
|
||||||
data["data/ts %i"%num] = ts+0
|
data["data/ts %i"%num] = ts+0
|
||||||
ts.write_to_hdf(h5, "/", f"adc_{num}", f"adc_{num}")
|
ts.write_to_hdf(h5, "/", f"adc_{num}", f"adc_{num}")
|
||||||
|
|||||||
Reference in New Issue
Block a user