python3-damaris from Joachim Beerwerth

This commit is contained in:
Markus Rosenstihl
2025-04-25 14:57:52 +02:00
commit 13431cab41
60 changed files with 16338 additions and 0 deletions
+524
View File
@@ -0,0 +1,524 @@
# -*- coding: iso-8859-1 -*-
from .Resultable import Resultable
from .Drawable import Drawable
from .Signalpath import Signalpath
from .DamarisFFT import DamarisFFT
import threading
import numpy
import sys
import types
import datetime
import tables
#############################################################################
# #
# Name: Class ADC_Result #
# #
# Purpose: Specialised class of Resultable and Drawable #
# Contains recorded ADC Data #
# #
#############################################################################
class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
def __init__(self, x = None, y = None, index = None, sampl_freq = None, desc = None, job_id = None, job_date = None):
Resultable.__init__(self)
Drawable.__init__(self)
# Title of this accumulation: set Values: Job-ID and Description (plotted in GUI -> look Drawable)
# Is set in ResultReader.py (or in copy-construktor)
self.__title_pattern = "ADC-Result: job_id = %s, desc = %s"
# Axis-Labels (inherited from Drawable)
self.xlabel = "Time (s)"
self.ylabel = "Samples [Digits]"
self.lock=threading.RLock()
self.nChannels = 0
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):
self.cont_data = False
self.sampling_rate = 0
self.index = []
self.x = []
self.y = []
elif (x is not None) and (y is not None) and (index is not None) and (sampl_freq is not None) and (desc is not None) and (job_id is not None) and (job_date is not None):
self.x = x
self.y = y
self.index = index
self.sampling_rate = sampl_freq
self.cont_data = True
self.description = desc
self.job_id = job_id
self.job_date = job_date
title="ADC-Result: job-id=%d"%int(self.job_id)
if len(self.description)>0:
for k,v in self.description.items():
# string keys can be made invisible by adding two underscores in front of them
if not (type(k) in (str,) and k[0] == '_' and k[1] == '_'):
title+=", %s=%s"%(k,v)
self.set_title(title)
else:
raise ValueError("Wrong usage of __init__!")
def create_data_space(self, channels, samples):
"Initialises the internal data-structures"
if self.contains_data():
print("Warning ADC-Result: Tried to run \"create_data_space()\" more than once.")
return
if channels <= 0: raise ValueError("ValueError: You cant create an ADC-Result with less than 1 channel!")
if samples <= 0: raise ValueError("ValueError: You cant create an ADC-Result with less than 1 sample!")
for i in range(channels):
self.y.append(numpy.zeros((samples,), dtype="Int16"))
self.x = numpy.zeros((samples,), dtype="Float64")
self.index.append((0, samples-1))
self.cont_data = True
def contains_data(self):
"Returns true if ADC_Result contains data. (-> create_data_space() was called)"
return self.cont_data
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\""
self.lock.acquire()
if not self.cont_data:
print("Warning ADC-Result: Tried to resize empty array!")
return
length = len(self.y[0])
self.x = numpy.resize(self.x, (length+samples))
for i in range(self.get_number_of_channels()):
self.y[i] = numpy.resize(self.y[i], (length+samples))
self.index.append((length, len(self.y[0])-1))
self.lock.release()
def get_result_by_index(self, index):
self.lock.acquire()
try:
start = self.index[index][0]
end = self.index[index][1]
except:
self.lock.release()
raise
tmp_x = self.x[start:end+1].copy()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i][start:end+1].copy())
r = ADC_Result(x = tmp_x, y = tmp_y, index = [(0,len(tmp_y[0])-1)], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
def get_sampling_rate(self):
"Returns the samplingfrequency"
return self.sampling_rate + 0
def set_sampling_rate(self, hz):
"Sets the samplingfrequency in hz"
self.sampling_rate = float(hz)
def get_nChannels(self):
"Gets the number of channels"
return self.nChannels + 0
def set_nChannels(self, channels):
"Sets the number of channels"
self.nChannels = int(channels)
def get_index_bounds(self, index):
"Returns a tuple with (start, end) of the wanted result"
return self.index[index]
def uses_statistics(self):
return False
def write_to_csv(self, destination=sys.stdout, delimiter=" "):
"""
writes the data to a file or to sys.stdout
destination can be a file or a filename
suitable for further processing
"""
# write sorted
the_destination=destination
if type(destination) in (str,):
the_destination=open(destination, "w")
the_destination.write("# adc_result\n")
the_destination.write("# t y0 y1 ...\n")
self.lock.acquire()
try:
xdata=self.get_xdata()
ch_no=self.get_number_of_channels()
ydata=list(map(self.get_ydata, range(ch_no)))
#yerr=map(self.get_yerr, xrange(ch_no))
for i in range(len(xdata)):
the_destination.write("%e"%xdata[i])
for j in range(ch_no):
the_destination.write("%s%e"%(delimiter, ydata[j][i]))
the_destination.write("\n")
the_destination=None
xdata=ydata=None
finally:
self.lock.release()
def write_to_simpson(self, destination=sys.stdout, delimiter=" "):
"""
writes the data to a text file or sys.stdout in Simpson format,
for further processing with the NMRnotebook software;
destination can be a file or a filename
"""
# write sorted
the_destination=destination
if type(destination) in (str,):
the_destination=open(destination, "w")
self.lock.acquire()
try:
xdata=self.get_xdata()
the_destination.write("SIMP\n")
the_destination.write("%s%i%s"%("NP=", len(xdata), "\n"))
the_destination.write("%s%i%s"%("SW=", self.get_sampling_rate(), "\n"))
the_destination.write("TYPE=FID\n")
the_destination.write("DATA\n")
ch_no=self.get_number_of_channels()
ydata=list(map(self.get_ydata, range(ch_no)))
for i in range(len(xdata)):
for j in range(ch_no):
the_destination.write("%g%s"%(ydata[j][i], delimiter))
the_destination.write("\n")
the_destination.write("END\n")
the_destination=None
xdata=ydata=None
finally:
self.lock.release()
def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None):
accu_group=hdffile.create_group(where=where,name=name,title=title)
accu_group._v_attrs.damaris_type="ADC_Result"
if self.contains_data():
self.lock.acquire()
try:
# save time stamps
if "job_date" in dir(self) and self.job_date is not None:
accu_group._v_attrs.time="%04d%02d%02d %02d:%02d:%02d.%03d"%(self.job_date.year,
self.job_date.month,
self.job_date.day,
self.job_date.hour,
self.job_date.minute,
self.job_date.second,
self.job_date.microsecond/1000)
if self.description is not None:
for (key,value) in self.description.items():
accu_group._v_attrs.__setattr__("description_"+key,str(value))
accu_group._v_attrs.__setattr__("sampling_rate",self.sampling_rate)
# save interval information
filter=None
if complib is not None:
if complevel is None:
complevel=9
filter=tables.Filters(complevel=complevel,complib=complib,shuffle=1)
index_table=hdffile.create_table(where=accu_group,
name="indices",
description={"start": tables.UInt64Col(),
"length": tables.UInt64Col(),
"start_time": tables.Float64Col(),
"dwelltime": tables.Float64Col()},
title="indices of adc data intervals",
filters=filter,
expectedrows=len(self.index))
index_table.flavor="numpy"
# save channel data
new_row=index_table.row
for i in range(len(self.index)):
new_row["start"]=self.index[i][0]
new_row["dwelltime"]=1.0/self.sampling_rate
new_row["start_time"]=1.0/self.sampling_rate*self.index[i][0]
new_row["length"]=self.index[i][1]-self.index[i][0]+1
new_row.append()
index_table.flush()
new_row=None
index_table=None
# prepare saving data
channel_no=len(self.y)
timedata=numpy.empty((len(self.y[0]),channel_no),
dtype = "Int32")
for ch in range(channel_no):
timedata[:,ch]=self.get_ydata(ch)
# save data
time_slice_data=None
if filter is not None:
chunkshape = numpy.shape(timedata)
if len(chunkshape) <= 1:
chunkshape = (min(chunkshape[0],1024*8),)
else:
chunkshape = (min(chunkshape[0],1024*8), chunkshape[1])
if tables.__version__[0]=="1":
time_slice_data=hdffile.create_carray(accu_group,
name="adc_data",
shape=timedata.shape,
atom=tables.Int32Atom(shape=chunkshape,
flavor="numpy"),
filters=filter,
title="adc data")
else:
time_slice_data=hdffile.create_carray(accu_group,
name="adc_data",
shape=timedata.shape,
chunkshape=chunkshape,
atom=tables.Int32Atom(),
filters=filter,
title="adc data")
time_slice_data[:]=timedata
else:
time_slice_data=hdffile.create_array(accu_group,
name="adc_data",
obj=timedata,
title="adc data")
finally:
timedata=None
time_slice_data=None
accu_group=None
self.lock.release()
# Ueberladen von Operatoren und Built-Ins -------------------------------------------------------
def __len__(self):
"Redefining len(ADC_Result obj), returns the number of samples in one channel and 0 without data"
if len(self.y)>0:
return len(self.y[0])
return 0
def __repr__(self):
"""
writes job meta data and data to string returned
"""
tmp_string = "Job ID: " + str(self.job_id) + "\n"
tmp_string += "Job Date: " + str(self.job_date) + "\n"
tmp_string += "Description: " + str(self.description) + "\n"
if len(self.y)>0:
tmp_string += "Indexes: " + str(self.index) + "\n"
tmp_string += "Samples per Channel: " + str(len(self.y[0])) + "\n"
tmp_string += "Samplingfrequency: " + str(self.sampling_rate) + "\n"
tmp_string += "X: " + repr(self.x) + "\n"
for i in range(self.get_number_of_channels()):
tmp_string += ("Y(%d): " % i) + repr(self.y[i]) + "\n"
return tmp_string
def __add__(self, other):
"Redefining self + other (scalar)"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(self.y[i], dtype="Float64") + other)
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
else:
raise ValueError("ValueError: Cannot add \"%s\" to ADC-Result!" % str(other.__class__))
def __radd__(self, other):
"Redefining other (scalar) + self"
return self.__add__(other)
def __sub__(self, other):
"Redefining self - other (scalar)"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(self.y[i], dtype="Float64") - other)
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
else:
raise ValueError("ValueError: Cannot subtract \"%s\" to ADC-Result!") % str(other.__class__)
def __rsub__(self, other):
"Redefining other (scalar) - self"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(other - numpy.array(self.y[i], dtype="Float64"))
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
else:
raise ValueError("ValueError: Cannot subtract \"%s\" to ADC-Result!") % str(other.__class__)
def __mul__(self, other):
"Redefining self * other (scalar)"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(self.y[i], dtype="Float64") * other)
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
else:
raise ValueError("ValueError: Cannot multiply \"%s\" to ADC-Result!") % str(other.__class__)
def __rmul__(self, other):
"Redefining other (scalar) * self"
return self.__mul__(other)
def __pow__(self, other):
"Redefining self ** other (scalar)"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(self.y[i], dtype="Float64") ** other)
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
else:
raise ValueError("ValueError: Cannot multiply \"%s\" to ADC-Result!") % str(other.__class__)
def __div__(self, other):
"Redefining self / other (scalar)"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(self.y[i], dtype="Float64") / other)
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
else:
raise ValueError("ValueError: Cannot multiply \"%s\" to ADC-Result!") % str(other.__class__)
def __rdiv__(self, other):
"Redefining other (scalar) / self"
if isinstance(other, int) or isinstance(other, float):
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(other / numpy.array(self.y[i], dtype="Float64"))
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
else:
raise ValueError("ValueError: Cannot multiply \"%s\" to ADC-Result!") % str(other.__class__)
def __neg__(self):
"Redefining -self"
self.lock.acquire()
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(-self.y[i]))
r = ADC_Result(x = self.x[:], y = tmp_y, index = self.index[:], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
def read_from_hdf(hdf_node):
"""
read accumulation data from HDF node and return it.
"""
# formal checks first
if not isinstance(hdf_node, tables.Group):
return None
if hdf_node._v_attrs.damaris_type!="ADC_Result":
return None
if not (hdf_node.__contains__("indices") and hdf_node.__contains__("adc_data")):
return None
# job id and x,y titles are missing
adc=ADC_Result()
# populate description dictionary
adc.description={}
for attrname in hdf_node._v_attrs._v_attrnamesuser:
if attrname.startswith("description_"):
adc.description[attrname[12:]]=hdf_node._v_attrs.__getattr__(attrname)
if "time" in dir(hdf_node._v_attrs):
timestring=hdf_node._v_attrs.__getattr__("time")
adc.job_date=datetime.datetime(int(timestring[:4]), # year
int(timestring[4:6]), # month
int(timestring[6:8]), # day
int(timestring[9:11]), # hour
int(timestring[12:14]), # minute
int(timestring[15:17]), # second
int(timestring[18:21])*1000 # microsecond
)
# start with indices
for r in hdf_node.indices.iterrows():
adc.index.append((r["start"],r["start"]+r["length"]-1))
adc.sampling_rate=1.0/r["dwelltime"]
# now really belief there are no data
if len(adc.index)==0:
adc.cont_data=False
return adc
adc.cont_data=True
# now do the real data
adc_data=hdf_node.adc_data.read()
adc.x=numpy.arange(adc_data.shape[0], dtype="Float64")/adc.sampling_rate
for ch in range(adc_data.shape[1]):
adc.y.append(adc_data[:,ch])
return adc
+970
View File
@@ -0,0 +1,970 @@
# -*- coding: iso-8859-1 -*-
#############################################################################
# #
# Name: Class Accumulation #
# #
# Purpose: Specialised class of Errorable and Drawable #
# Contains accumulated ADC-Data #
# #
#############################################################################
from .Errorable import Errorable
from .Drawable import Drawable
from .DamarisFFT import DamarisFFT
from .Signalpath import Signalpath
#from DataPool import DataPool
import sys
import threading
import types
import tables
import numpy
import datetime # added by Oleg Petrov
import ctypes # added by Oleg Petrov
import struct # added by Oleg Petrov
import os # added by Oleg Petrov
class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
def __init__(self, x = None, y = None, y_2 = None, n = None, index = None, sampl_freq = None, error = False):
Errorable.__init__(self)
Drawable.__init__(self)
# Title of this accumulation (plotted in GUI -> look Drawable)
self.__title_pattern = "Accumulation: n = %d"
# Axis-Labels (inherited from Drawable)
self.xlabel = "Time (s)"
self.ylabel = "Avg. Samples [Digits]"
self.lock=threading.RLock()
self.common_descriptions=None
self.time_period=[]
self.job_id = None # added by Oleg Petrov
self.use_error = error
if self.uses_statistics():
if (y_2 is not None):
self.y_square = y_2
elif (y_2 is None) :
self.y_square = []
else:
raise ValueError("Wrong usage of __init__!")
if (x is None) and (y is None) and (index is None) and (sampl_freq is None) and (n is None):
self.sampling_rate = 0
self.n = 0
self.set_title(self.__title_pattern % self.n)
self.cont_data = False
self.index = []
self.x = []
self.y = []
elif (x is not None) and (y is not None) and (index is not None) and (sampl_freq is not None) and (n is not None):
self.x = x
self.y = y
self.sampling_rate = sampl_freq
self.n = n
self.set_title(self.__title_pattern % self.n)
self.index = index
self.cont_data = True
else:
raise ValueError("Wrong usage of __init__!")
def get_accu_by_index(self, index):
self.lock.acquire()
try:
start = self.index[index][0]
end = self.index[index][1]
except:
self.lock.release()
raise
tmp_x = self.x[start:end+1]
tmp_y = []
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i][start:end+1])
r = Accumulation(x = tmp_x, y = tmp_y, n = self.n, index = [(0,len(tmp_y[0])-1)], sampl_freq = self.sampling_rate, error = self.use_error)
self.lock.release()
return r
def get_ysquare(self, channel):
if self.uses_statistics():
try:
return self.y_square[channel]
except:
raise
else: return None
def contains_data(self):
return self.cont_data
def get_sampling_rate(self):
"Returns the samplingfrequency"
return self.sampling_rate + 0
def get_index_bounds(self, index):
"Returns a tuple with (start, end) of the wanted result"
return self.index[index]
def uses_statistics(self):
return self.use_error
# Schnittstellen nach Außen --------------------------------------------------------------------
def get_yerr(self, channel):
"""
return error (std.dev/sqrt(n)) of mean
"""
if not self.uses_statistics(): return numpy.zeros((len(self.y[0]),),dtype="Float64")
if not self.contains_data(): return []
self.lock.acquire()
if self.n < 2:
retval=numpy.zeros((len(self.y[0]),),dtype="Float64")
self.lock.release()
return retval
try:
variance_over_n = (self.y_square[channel] - (self.y[channel]**2 / float(self.n)))/float((self.n-1)*self.n)
except IndexError:
print("Warning Accumulation.get_ydata(channel): Channel index does not exist.")
variance_over_n = numpy.zeros((len(self.y[0]),), dtype="Float64")
self.lock.release()
# sample standard deviation / sqrt(n)
return numpy.nan_to_num(numpy.sqrt(variance_over_n))
def get_ydata(self, channel):
"""
return mean data
"""
if not self.contains_data(): return []
self.lock.acquire()
try:
tmp_y = self.y[channel] / self.n
except IndexError:
print("Warning Accumulation.get_ydata(channel): Channel index does not exist.")
tmp_y = numpy.zeros((len(self.y[0]),), dtype="Float64")
self.lock.release()
return tmp_y
def get_ymin(self):
if not self.contains_data(): return 0
tmp_min = []
self.lock.acquire()
for i in range(self.get_number_of_channels()):
tmp_min.append(self.get_ydata(i).min())
if self.uses_statistics() and self.ready_for_drawing_error():
for i in range(self.get_number_of_channels()):
tmp_min.append((self.get_ydata(i) - self.get_yerr(i)).min())
self.lock.release()
return min(tmp_min)
def get_ymax(self):
if not self.contains_data(): return 0
tmp_max = []
self.lock.acquire()
for i in range(self.get_number_of_channels()):
tmp_max.append(self.get_ydata(i).max())
if self.uses_statistics() and self.ready_for_drawing_error():
for i in range(self.get_number_of_channels()):
tmp_max.append((self.get_ydata(i) + self.get_yerr(i)).max())
self.lock.release()
return max(tmp_max)
def get_job_id(self):
# return None
return self.job_id # modified by Oleg Petrov
def write_to_csv(self, destination=sys.stdout, delimiter=" "):
"""
writes the data to a file or to sys.stdout
destination can be a file or a filename
suitable for further processing
"""
the_destination=destination
if type(destination) in (str,):
the_destination=open(destination, "w")
the_destination.write("# accumulation %d\n"%self.n)
self.lock.acquire()
try:
if self.common_descriptions is not None:
for (key,value) in self.common_descriptions.items():
the_destination.write("# %s : %s\n"%(key, str(value)))
the_destination.write("# t")
ch_no=self.get_number_of_channels()
if self.use_error:
for i in range(ch_no): the_destination.write(" ch%d_mean ch%d_err"%(i,i))
else:
for i in range(ch_no): the_destination.write(" ch%d_mean"%i)
the_destination.write("\n")
xdata=self.get_xdata()
ydata=list(map(self.get_ydata, range(ch_no)))
yerr=None
if self.use_error:
yerr=list(map(self.get_yerr, range(ch_no)))
for i in range(len(xdata)):
the_destination.write("%e"%xdata[i])
for j in range(ch_no):
if self.use_error:
the_destination.write("%s%e%s%e"%(delimiter, ydata[j][i], delimiter, yerr[j][i]))
else:
the_destination.write("%s%e"%(delimiter,ydata[j][i]))
the_destination.write("\n")
the_destination=None
xdata=yerr=ydata=None
finally:
self.lock.release()
# ------------- added by Oleg Petrov, 14 Feb 2012 ----------------------
def write_to_simpson(self, destination=sys.stdout, delimiter=" ", frequency=100e6):
"""
writes the data to a text file or sys.stdout in Simpson format,
for further processing with the NMRnotebook software;
destination can be a file or a filename
"""
# write sorted
the_destination=destination
if type(destination) in (str,):
the_destination=open(destination, "w")
self.lock.acquire()
try:
xdata=self.get_xdata()
the_destination.write("SIMP\n")
the_destination.write("%s%i%s"%("NP=", len(xdata), "\n"))
the_destination.write("%s%i%s"%("SW=", self.get_sampling_rate(), "\n"))
the_destination.write("%s%i%s"%("REF=", frequency, "\n"))
the_destination.write("TYPE=FID\n")
the_destination.write("DATA\n")
ch_no=self.get_number_of_channels()
ydata=list(map(self.get_ydata, range(ch_no)))
for i in range(len(xdata)):
for j in range(ch_no):
the_destination.write("%g%s"%(ydata[j][i], delimiter))
the_destination.write("\n")
the_destination.write("END\n")
the_destination=None
xdata=ydata=None
finally:
self.lock.release()
# ------------- added by Oleg Petrov, 10 Sep 2013 -----------------------
def write_to_tecmag(self, destination=sys.stdout, nrecords=1,\
frequency=100.,\
last_delay = 1.,\
receiver_phase=0.,\
nucleus='1H'):
"""
writes the data to a binary file in TecMag format;
destination can be a file object or a filename;
nrecords determines an indirect dimension in 2D experiments;
"""
#TODO: Function is most likely broken in Python 3 because Strings are now unicode and binary files cannot write unicode
if self.job_id == None or self.n == 0:
raise ValueError("write_to_tecmag: cannot get a record number")
else:
record = (self.job_id/self.n)%nrecords + 1
the_destination=destination
if type(destination) in (str,):
if record == 1 and os.path.exists(destination):
os.rename(destination, os.path.dirname(destination)+'/~'+os.path.basename(destination))
self.lock.acquire()
try:
npts = [len(self), nrecords, 1, 1]
data_offset = 2*4*npts[0]*npts[1]*npts[2]*npts[3] # length of data section
dwell = 1./self.get_sampling_rate()
sw = 0.5/dwell
base_freq = [frequency, frequency, 0., 0.]
offset_freq = [0., 0., 0., 0.]
ob_freq = [sum(x) for x in zip(base_freq, offset_freq)]
date = self.time_period[0].strftime("%Y/%m/%d %H:%M:%S")
# data handling:
ch_no=self.get_number_of_channels()
ydata = list(map(self.get_ydata, range(ch_no)))
if ch_no == 1:
ydata = [ydata, numpy.zeros(len(ydata))]
# data is arranged in RIRIRIRI blocks in linear order:
data = numpy.append([ydata[0]], [ydata[1]], axis=0)
data = data.T
data = data.flatten()
if record == 1:
the_destination=open(destination, "wb")
# allocate space for all records in advance:
buff = ctypes.create_string_buffer(1056+data_offset+2068)
struct.pack_into('8s', buff, 0, 'TNT1.005') # 'TNT1.000' version ID
struct.pack_into('4s', buff, 8, 'TMAG') # 'TMAG' tag
struct.pack_into('?', buff, 12, True) # BOOLean value
struct.pack_into('i', buff, 16, 1024) # length of Tecmag struct
#Initialize TECMAG structure:
struct.pack_into('4i', buff, 20, *npts) # npts[4]
struct.pack_into('4i', buff, 36, *npts) # actual_npts[4]
struct.pack_into('i', buff, 52, npts[0]) # acq_points
struct.pack_into('4i', buff, 56, 1, 1, 1, 1) # npts_start[4]
struct.pack_into('i', buff, 72, self.n) # scans
struct.pack_into('i', buff, 76, self.n) # actual_scans
struct.pack_into('i', buff, 88, 1) # sadimension
struct.pack_into('4d', buff, 104, *ob_freq) # ob_freq[4]
struct.pack_into('4d', buff, 136, *base_freq) # base_freq[4]
struct.pack_into('4d', buff, 168, *offset_freq) # offset_freq[4]
struct.pack_into('d', buff, 200, 0.0) # ref_freq
struct.pack_into('h', buff, 216, 1) # obs_channel
struct.pack_into('42s', buff, 218, 42*'2') # space2[42]
struct.pack_into('4d', buff, 260, sw, sw, 0., 0.) # sw[4], sw = 0.5/dwell
struct.pack_into('4d', buff, 292, dwell, dwell, 0., 0.) # dwell[4]
struct.pack_into('d', buff, 324, sw) # filter, = 0.5/dwell
struct.pack_into('d', buff, 340, (npts[0]*dwell)) # acq_time
struct.pack_into('d', buff, 348, 1.) # last_delay (5*T1 minus sequence length)
struct.pack_into('h', buff, 356, 1) # spectrum_direction
struct.pack_into('16s', buff, 372, 16*'2') # space3[16]
struct.pack_into('d', buff, 396, receiver_phase) # receiver_phase
struct.pack_into('4s', buff, 404, 4*'2') # space4[4]
struct.pack_into('16s', buff, 444, 16*'2') # space5[16]
struct.pack_into('264s', buff, 608, 264*'2') # space6[264]
struct.pack_into('32s', buff, 884, date) # date[32]
struct.pack_into('16s', buff, 916, nucleus) # nucleus[16]
# TECMAG Structure total => 1024
struct.pack_into('4s', buff, 1044, 'DATA') # 'DATA' tag
struct.pack_into('?', buff, 1048, True) # BOOLean
struct.pack_into('i', buff, 1052, data_offset) # length of data
struct.pack_into('%sf' % (2*npts[0]), buff, 1056, *data) # actual data (one record)
struct.pack_into('4s', buff, 1056+data_offset, 'TMG2') # 'TMG2' tag
struct.pack_into('?', buff, 1056+data_offset+4, True) # BOOLean
struct.pack_into('i', buff, 1056+data_offset+8, 2048) # length of Tecmag2 struct
# Leave TECMAG2 structure empty:
struct.pack_into('52s', buff, 1056+data_offset+372, 52*'2') # space[52]
struct.pack_into('866s', buff, 1056+data_offset+1194, 866*'2') # space[610]+names+strings
# TECMAG2 Structure total => 2048
struct.pack_into('4s', buff, 1056+data_offset+2060, 'PSEQ') # 'PSEQ' tag 658476
struct.pack_into('?', buff, 1056+data_offset+2064, False) # BOOLean 658480
the_destination.write(buff)
else:
the_destination=open(destination, "rb+")
buff = ctypes.create_string_buffer(4*2*npts[0])
struct.pack_into('%sf' % (2*npts[0]), buff, 0, *data)
the_destination.seek(1056+4*2*npts[0]*(record-1))
the_destination.write(buff)
the_destination = None
ydata=None
finally:
self.lock.release()
# -----------------------------------------------------------------------
def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None):
accu_group=hdffile.create_group(where=where,name=name,title=title)
accu_group._v_attrs.damaris_type="Accumulation"
if self.contains_data():
self.lock.acquire()
try:
# save time stamps
if self.time_period is not None and len(self.time_period)>0:
accu_group._v_attrs.earliest_time="%04d%02d%02d %02d:%02d:%02d.%03d"%(self.time_period[0].year,
self.time_period[0].month,
self.time_period[0].day,
self.time_period[0].hour,
self.time_period[0].minute,
self.time_period[0].second,
self.time_period[0].microsecond/1000)
accu_group._v_attrs.oldest_time="%04d%02d%02d %02d:%02d:%02d.%03d"%(self.time_period[1].year,
self.time_period[1].month,
self.time_period[1].day,
self.time_period[1].hour,
self.time_period[1].minute,
self.time_period[1].second,
self.time_period[1].microsecond/1000)
if self.common_descriptions is not None:
for (key,value) in self.common_descriptions.items():
accu_group._v_attrs.__setattr__("description_"+key,str(value))
accu_group._v_attrs.__setattr__("sampling_rate",self.sampling_rate)
# save interval information
filter=None
if complib is not None:
if complevel is None:
complevel=9
filter=tables.Filters(complevel=complevel,complib=complib,shuffle=1)
# tried compression filter, but no effect...
index_table=hdffile.create_table(where=accu_group,
name="indices",
description={"start": tables.UInt64Col(),
"length": tables.UInt64Col(),
"start_time": tables.Float64Col(),
"dwelltime": tables.Float64Col(),
"number": tables.UInt64Col()},
title="indices of adc data intervals",
filters=filter,
expectedrows=len(self.index))
index_table.flavor="numpy"
# save interval data
new_row=index_table.row
for i in range(len(self.index)):
new_row["start"]=self.index[i][0]
new_row["dwelltime"]=1.0/self.sampling_rate
new_row["start_time"]=1.0/self.sampling_rate*self.index[i][0]
new_row["length"]=self.index[i][1]-self.index[i][0]+1
new_row["number"]=self.n
new_row.append()
index_table.flush()
new_row=None
index_table=None
# prepare saving data
channel_no=len(self.y)
timedata=numpy.empty((len(self.y[0]),channel_no*2), dtype = "Float64")
for ch in range(channel_no):
timedata[:,ch*2]=self.get_ydata(ch)
if self.uses_statistics():
timedata[:,ch*2+1]=self.get_yerr(ch)
else:
timedata[:,ch*2+1]=numpy.zeros((len(self.y[0]),),dtype = "Float64")
# save data
time_slice_data=None
if filter is not None:
chunkshape=timedata.shape
if len(chunkshape) <= 1:
chunkshape = (min(chunkshape[0],1024*8),)
else:
chunkshape = (min(chunkshape[0],1024*8), chunkshape[1])
if tables.__version__[0]=="1":
time_slice_data=hdffile.create_carray(accu_group,
name="accu_data",
shape=timedata.shape,
atom=tables.Float64Atom(shape=chunkshape,
flavor="numpy"),
filters=filter,
title="accu data")
else:
time_slice_data=hdffile.create_carray(accu_group,
name="accu_data",
shape=timedata.shape,
chunkshape=chunkshape,
atom=tables.Float64Atom(),
filters=filter,
title="accu data")
time_slice_data[:]=timedata
else:
time_slice_data=hdffile.create_array(accu_group,
name="accu_data",
obj=timedata,
title="accu data")
finally:
time_slice_data=None
accu_group=None
self.lock.release()
# / Schnittstellen nach Außen ------------------------------------------------------------------
# Überladen von Operatoren ---------------------------------------------------------------------
def __len__(self):
"""
return number of samples per channel, 0 if empty
"""
if len(self.y)>0:
return len(self.y[0])
return 0
def __repr__(self):
"Redefining repr(Accumulation)"
if not self.contains_data(): return "Empty"
tmp_string = "X: " + repr(self.x) + "\n"
for i in range(self.get_number_of_channels()):
tmp_string += ("Y(%d): " % i) + repr(self.y[i]) + "\n"
if self.uses_statistics(): tmp_string += "y_square(%d): " % i + str(self.y_square[i]) + "\n"
tmp_string += "Indexes: " + str(self.index) + "\n"
tmp_string += "Samples per Channel: " + str(len(self.y[0])) + "\n"
tmp_string += "Samplingfrequency: " + str(self.sampling_rate) + "\n"
tmp_string += "n: " + str(self.n)
return tmp_string
def __add__(self, other):
"Redefining self + other"
# Float or int
if isinstance(other, int) or isinstance(other, float):
if not self.contains_data(): raise ValueError("Accumulation: You cant add integers/floats to an empty accumulation")
else:
tmp_y = []
tmp_ysquare = []
self.lock.acquire()
for i in range(self.get_number_of_channels()):
# Dont change errors and mean value
if self.uses_statistics(): tmp_ysquare.append(self.y_square[i] + ( (2*self.y[i]*other) + ((other**2)*self.n) ))
tmp_y.append(self.y[i] + (other*self.n))
if self.uses_statistics():
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, y_2 = tmp_ysquare, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
else:
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
r.job_id = self.job_id # added by Oleg Petrov
self.lock.release()
return r
# ADC_Result
elif str(other.__class__) == "damaris.data.ADC_Result.ADC_Result":
# Other empty (return)
# todo: this is seems to be bugy!!!! (Achim)
if not other.contains_data(): return
# Self empty (copy)
if not self.contains_data():
tmp_y = []
tmp_ysquare = []
self.lock.acquire()
for i in range(other.get_number_of_channels()):
tmp_y.append(numpy.array(other.y[i], dtype="Float64"))
if self.uses_statistics(): tmp_ysquare.append(tmp_y[i] ** 2)
if self.uses_statistics():
r = Accumulation(x = numpy.array(other.x, dtype="Float64"), y = tmp_y, y_2 = tmp_ysquare, n = 1, index = other.index, sampl_freq = other.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(other.x, dtype="Float64"), y = tmp_y, index = other.index, sampl_freq = other.sampling_rate, n = 1, error = False)
r.time_period=[other.job_date,other.job_date]
r.job_id = other.job_id # added by Oleg Petrov
r.common_descriptions=other.description.copy()
self.lock.release()
return r
# Other and self not empty (self + other)
else:
self.lock.acquire()
if self.sampling_rate != other.get_sampling_rate(): raise ValueError("Accumulation: You cant add ADC-Results with diffrent sampling-rates")
if len(self.y[0]) != len(other): raise ValueError("Accumulation: You cant add ADC-Results with diffrent number of samples")
if len(self.y) != other.get_number_of_channels(): raise ValueError("Accumulation: You cant add ADC-Results with diffrent number of channels")
for i in range(len(self.index)):
if self.index[i] != other.get_index_bounds(i): raise ValueError("Accumulation: You cant add ADC-Results with diffrent indexing")
tmp_y = []
tmp_ysquare = []
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i] + other.y[i])
if self.uses_statistics(): tmp_ysquare.append(self.y_square[i] + (numpy.array(other.y[i], dtype="Float64") ** 2))
if self.uses_statistics():
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, y_2 = tmp_ysquare, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = False)
r.time_period=[min(self.time_period[0],other.job_date),
max(self.time_period[1],other.job_date)]
r.job_id = other.job_id # added by Oleg Petrov
if self.common_descriptions is not None:
r.common_descriptions={}
for key in list(self.common_descriptions.keys()):
if (key in other.description and self.common_descriptions[key]==other.description[key]):
r.common_descriptions[key]=value
self.lock.release()
return r
# Accumulation
elif str(other.__class__) == "damaris.data.Accumulation.Accumulation":
# Other empty (return)
if not other.contains_data(): return
# Self empty (copy)
if not self.contains_data():
tmp_y = []
tmp_ysquare = []
self.lock.acquire()
if self.uses_statistics():
r = Accumulation(x = numpy.array(other.x, dtype="Float64"), y = tmp_y, y_2 = tmp_ysquare, n = other.n, index = other.index, sampl_freq = other.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(other.x, dtype="Float64"), y = tmp_y, n = other.n, index = other.index, sampl_freq = other.sampling_rate, error = False)
for i in range(other.get_number_of_channels()):
tmp_y.append(other.y[i])
tmp_ysquare.append(other.y_square[i])
r.time_period=other.time_period[:]
r.job_id = other.job_id # added by Oleg Petrov
if other.common_descriptions is not None:
r.common_descriptions=othter.common_descriptions.copy()
else:
r.common_descriptions=None
self.lock.release()
return r
# Other and self not empty (self + other)
else:
self.lock.acquire()
if self.sampling_rate != other.get_sampling_rate(): raise ValueError("Accumulation: You cant add accumulations with diffrent sampling-rates")
if len(self.y[0]) != len(other): raise ValueError("Accumulation: You cant add accumulations with diffrent number of samples")
if len(self.y) != other.get_number_of_channels(): raise ValueError("Accumulation: You cant add accumulations with diffrent number of channels")
for i in range(len(self.index)):
if self.index[i] != other.get_index_bounds(i): raise ValueError("Accumulation: You cant add accumulations with diffrent indexing")
if self.uses_statistics() and not other.uses_statistics(): raise ValueError("Accumulation: You cant add non-error accumulations to accumulations with error")
tmp_y = []
tmp_ysquare = []
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i] + other.y[i])
tmp_ysquare.append(self.y_square[i] + other.y_square[i])
if self.uses_statistics():
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, y_2 = tmp_ysquare, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = False)
r.time_period=[min(self.time_period[0],other.time_period[0]),
max(self.time_period[1],other.time_period[1])]
r.job_id = other.job_id # added by Oleg Petrov
r.common_descriptions={}
if self.common_descriptions is not None and other.common_descriptions is not None:
for key in list(self.common_descriptions.keys()):
if (key in other.common_descriptions and
self.common_descriptions[key]==other.common_descriptions[key]):
r.common_descriptions[key]=value
self.lock.release()
return r
def __radd__(self, other):
"Redefining other + self"
return self.__add__(other)
def __sub__(self, other):
"Redefining self - other"
return self.__add__(-other)
def __rsub__(self, other):
"Redefining other - self"
return self.__neg__(self.__add__(-other))
def __iadd__(self, other):
"Redefining self += other"
# Float or int
if isinstance(other, int) or isinstance(other, float):
if not self.contains_data(): raise ValueError("Accumulation: You cant add integers/floats to an empty accumulation")
else:
self.lock.acquire()
for i in range(self.get_number_of_channels()):
#Dont change errors and mean value
if self.uses_statistics(): self.y_square[i] += (2*self.y[i]*other) + ((other**2)*self.n)
self.y[i] += other*self.n
self.lock.release()
return self
# ADC_Result
elif type(other).__module__+"."+type(other).__name__ == "damaris.data.ADC_Result.ADC_Result":
# Other empty (return)
if not other.contains_data(): return self
# Self empty (copy)
if not self.contains_data():
self.lock.acquire()
self.n += 1
self.index = other.index[0:]
self.sampling_rate = other.sampling_rate
self.x = numpy.array(other.x, dtype="Float64")
self.cont_data = True
for i in range(other.get_number_of_channels()):
self.y.append(numpy.array(other.y[i], dtype="Float64"))
if self.uses_statistics(): self.y_square.append(self.y[i] ** 2)
self.set_title(self.__title_pattern % self.n)
self.lock.release()
self.time_period=[other.job_date,other.job_date]
self.job_id = other.job_id # added by Oleg Petrov
self.common_descriptions=other.description.copy()
return self
# Other and self not empty (self + other)
else:
self.lock.acquire()
if self.sampling_rate != other.get_sampling_rate(): raise ValueError("Accumulation: You cant add ADC-Results with diffrent sampling-rates")
if len(self.y[0]) != len(other): raise ValueError("Accumulation: You cant add ADC-Results with diffrent number of samples")
if len(self.y) != other.get_number_of_channels(): raise ValueError("Accumulation: You cant add ADC-Results with diffrent number of channels")
for i in range(len(self.index)):
if self.index[i] != other.get_index_bounds(i): raise ValueError("Accumulation: You cant add ADC-Results with diffrent indexing")
for i in range(self.get_number_of_channels()):
self.y[i] += other.y[i]
if self.uses_statistics(): self.y_square[i] += numpy.array(other.y[i], dtype="Float64") ** 2
self.n += 1
self.time_period=[min(self.time_period[0],other.job_date),
max(self.time_period[1],other.job_date)]
self.job_id = other.job_id # added by Oleg Petrov
if self.common_descriptions is not None:
for key in list(self.common_descriptions.keys()):
if not (key in other.description and self.common_descriptions[key]==other.description[key]):
del self.common_descriptions[key]
self.set_title(self.__title_pattern % self.n)
self.lock.release()
return self
# Accumulation
elif type(other).__module__+"."+type(other).__name__ == "damaris.data.Accumulation.Accumulation":
# Other empty (return)
if not other.contains_data(): return
# Self empty (copy)
if not self.contains_data():
if self.uses_statistics() and not other.uses_statistics(): raise ValueError("Accumulation: You cant add non-error accumulations to accumulations with error")
self.lock.acquire()
self.n += other.n
self.index = other.index[0:]
self.sampling_rate = other.sampling_rate
self.x = numpy.array(other.x, dtype="Float64")
self.cont_data = True
for i in range(other.get_number_of_channels()):
self.y.append(numpy.array(other.y[i], dtype="Float64"))
if self.uses_statistics(): self.y_square.append(self.y[i] ** 2)
self.set_title(self.__title_pattern % self.n)
self.common_descriptions=other.common_desriptions.copy()
self.time_period=other.time_period[:]
self.job_id = other.job_id # added by Oleg Petrov
self.lock.release()
return self
# Other and self not empty (self + other)
else:
self.lock.acquire()
if self.sampling_rate != other.get_sampling_rate(): raise ValueError("Accumulation: You cant add accumulations with diffrent sampling-rates")
if len(self.y[0]) != len(other): raise ValueError("Accumulation: You cant add accumulations with diffrent number of samples")
if len(self.y) != other.get_number_of_channels(): raise ValueError("Accumulation: You cant add accumulations with diffrent number of channels")
for i in range(len(self.index)):
if self.index[i] != other.get_index_bounds(i): raise ValueError("Accumulation: You cant add accumulations with diffrent indexing")
if self.uses_statistics() and not other.uses_statistics(): raise ValueError("Accumulation: You cant add non-error accumulations to accumulations with error")
for i in range(self.get_number_of_channels()):
self.y[i] += other.y[i]
if self.uses_statistics(): self.y_square[i] += other.y_square[i]
self.n += other.n
self.time_period=[min(self.time_period[0],other.time_period[0]),
max(self.time_period[1],other.time_period[1])]
self.job_id = other.job_id # added by Oleg Petrov
if self.common_descriptions is not None and other.common_descriptions is not None:
for key in list(self.common_descriptions.keys()):
if not (key in other.description and
self.common_descriptions[key]==other.common_descriptions[key]):
del self.common_descriptions[key]
self.set_title(self.__title_pattern % self.n)
self.lock.release()
return self
elif other is None:
# Convenience: ignore add of None
return self
else:
raise ValueError("can not add "+repr(type(other))+" to Accumulation")
def __isub__(self, other):
"Redefining self -= other"
return self.__iadd__(-other)
def __neg__(self):
"Redefining -self"
if not self.contains_data(): return
tmp_y = []
self.lock.acquire()
for i in range(self.get_number_of_channels()):
tmp_y.append(numpy.array(-self.y[i], dtype="Float64"))
if self.uses_statistics():
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, y_2 = numpy.array(self.y_square), n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(self.x, dtype="Float64"), y = tmp_y, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = False)
self.lock.release()
return r
def read_from_hdf(hdf_node):
"""
read accumulation data from HDF node and return it.
"""
# formal checks first
if not isinstance(hdf_node, tables.Group):
return None
if hdf_node._v_attrs.damaris_type!="Accumulation":
return None
if not (hdf_node.__contains__("indices") and hdf_node.__contains__("accu_data")):
print("no accu data")
return None
accu=Accumulation()
# populate description dictionary
accu.common_descriptions={}
for attrname in hdf_node._v_attrs._v_attrnamesuser:
if attrname.startswith("description_"):
accu.common_descriptions[attrname[12:]]=hdf_node._v_attrs.__getattr__(attrname)
eariliest_time=None
if "earliest_time" in dir(hdf_node._v_attrs):
timestring=hdf_node._v_attrs.__getattr__("earliest_time")
earliest_time=datetime.datetime(int(timestring[:4]), # year
int(timestring[4:6]), # month
int(timestring[6:8]), # day
int(timestring[9:11]), # hour
int(timestring[12:14]), # minute
int(timestring[15:17]), # second
int(timestring[18:21])*1000 # microsecond
)
oldest_time=None
if "oldest_time" in dir(hdf_node._v_attrs):
timestring=hdf_node._v_attrs.__getattr__("oldest_time")
oldest_time=datetime.datetime(int(timestring[:4]), # year
int(timestring[4:6]), # month
int(timestring[6:8]), # day
int(timestring[9:11]), # hour
int(timestring[12:14]), # minute
int(timestring[15:17]), # second
int(timestring[18:21])*1000 # microsecond
)
if oldest_time is None or earliest_time is None:
accu.time_period=None
if len(accu.common_descriptions)==0:
# no accus inside, so no common description expected
accu.common_descriptions=None
accu.cont_data=False
else:
accu.time_period=[oldest_time, earliest_time]
accu.cont_data=True
# start with indices
for r in hdf_node.indices.iterrows():
accu.index.append((r["start"],r["start"]+r["length"]-1))
accu.n=r["number"]
accu.sampling_rate=1.0/r["dwelltime"]
# now really belief there are no data
if len(accu.index)==0 or accu.n==0:
accu.cont_data=False
return accu
# now do the real data
accu_data=hdf_node.accu_data.read()
accu.x=numpy.arange(accu_data.shape[0], dtype="Float64")/accu.sampling_rate
# assume error information, todo: save this information explicitly
accu.y_square=[]
accu.use_error=False
for ch in range(accu_data.shape[1]/2):
accu.y.append(accu_data[:,ch*2]*accu.n)
if accu.n<2 or numpy.all(accu_data[:,ch*2+1]==0.0):
accu.y_square.append(numpy.zeros((accu_data.shape[0]) ,dtype="Float64"))
else:
accu.use_error=True
accu.y_square.append((accu_data[:,ch*2+1]**2)*float((accu.n-1.0)*accu.n)+(accu_data[:,ch*2]**2)*accu.n)
if not accu.use_error:
del accu.y_square
return accu
+54
View File
@@ -0,0 +1,54 @@
# -*- coding: iso-8859-1 -*-
from .Resultable import Resultable
#############################################################################
# #
# Name: Class Error_Result #
# #
# Purpose: Specialised class of Resultable #
# Contains occured error-messages from the core #
# #
#############################################################################
class Config_Result(Resultable):
def __init__(self, config = None, desc = None, job_id = None, job_date = None):
Resultable.__init__(self)
if config is None: self.config = { }
if desc is None: self.description = { }
self.job_id = job_id
self.job_date = job_date
def get_config_dictionary(self):
return self.config
def set_config_dictionary(self, config):
self.config = config
def get_config(self, key):
if key in self.config: return self.config[key]
else: return None
def set_config(self, key, value):
if key in self.config:
print("Warning Config_Result: Key \"%s\" will be overwritten with \"%s\"" % (key, value))
self.config[key] = value
# Überladen von Operatoren und Built-Ins -------------------------------------------------------
def __repr__(self):
return str(self.config)
def __str__(self):
return str(self.config)
+239
View File
@@ -0,0 +1,239 @@
import numpy
import sys
from . import autophase
from functools import reduce
class DamarisFFT:
"""
Class for Fourier transforming data.
Provides several helper and apodization functions
"""
def clip( self, start=None, stop=None ):
"""
Method for clipping data, returns only the data between start and stop
start and stop can be either time or frequency.
The unit is automatically determined (Hz or s).
:param float start: beginning of clipping in s
:param float stop: end of clipping in s
"""
# check if start/stop order is properly
if start > stop:
start, stop = stop, start
# do nothing if one uses clip as a "placeholder"
if start == None and stop == None:
return self
if start == None:
start = self.x[ 0 ]
if stop == None:
stop = self.x[ -1 ]
# check if data is fft which changes the start/stop units
if self.xlabel == "Frequency / Hz":
isfft = True
start = self.x.size * (0.5 + start / self.sampling_rate)
stop = self.x.size * (0.5 + stop / self.sampling_rate)
else:
isfft = False
# get the corresponding indices
start *= self.sampling_rate
stop *= self.sampling_rate
# check if boundaries make sense, raise exception otherwise
if numpy.abs( int( start ) - int( stop ) ) <= 0:
raise ValueError( "start stop too close: There are no samples in the given boundaries!" )
# clip the data for each channel
for ch in range( len( self.y ) ):
self.y[ ch ] = self.y[ ch ][ int( start ):int( stop ) ]
self.x = self.x[ int( start ):int( stop ) ]
return self
def baseline( self, last_part=0.1 ):
"""
Correct the baseline of your data by subtracting the mean of the
last_part fraction of your data.
:param float last_part: last section of your timesignal used to calculate baseline
last_part defaults to 0.1, i.e. last 10% of your data
"""
# TODO baseline correction for spectra after:
# Heuer, A; Haeberlen, U.: J. Mag. Res.(1989) 85, Is 1, 79-94
n = int( self.x.size * last_part )
for ch in range( len( self.y ) ):
self.y[ ch ] -= self.y[ ch ][ -n: ].mean( )
return self
def exp_window( self, line_broadening=10 ):
"""
Exponential window function
:param float line_broadening: default 10, line broadening factor in Hz
.. math::
\\exp\\left(-\\pi\\cdot \\textsf{line_broadening} \\cdot t\\right)
"""
apod = numpy.exp( -self.x * numpy.pi * line_broadening )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def gauss_window( self, line_broadening=10 ):
"""
Gaussian window function
:param float line_broadening: default 10, line broadening factor in Hz
.. math:: \\exp\\left(- (\\textsf{line_broadening} \\cdot t)^2\\right)
"""
apod = numpy.exp( -(self.x * line_broadening) ** 2 )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def dexp_window( self, line_broadening=-10, gaussian_multiplicator=0.3 ):
apod = numpy.exp( -(self.x * line_broadening - gaussian_multiplicator * self.x.max( )) ** 2 )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def traf_window( self, line_broadening=10 ):
apod = (numpy.exp( -self.x * line_broadening )) ** 2 / ( (numpy.exp( -self.x * line_broadening )) ** 3
+ (
numpy.exp( -self.x.max( ) * line_broadening )) ** 3 )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def hanning_window( self ):
"""
Symmetric centered window (hanning)
"""
apod = numpy.hanning( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def hamming_window( self ):
"""
Symmetric centered window (hamming)
"""
apod = numpy.hamming( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def blackman_window( self ):
"""
Symmetric centered window (blackmann)
"""
apod = numpy.blackman( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def bartlett_window( self ):
"""
Symmetric centered window (bartlett)
"""
apod = numpy.bartlett( self.x.size )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def kaiser_window( self, beta=4, use_scipy=None ):
"""
Symmetric centered window (kaiser)
"""
if use_scipy == None:
# modified Bessel function of zero kind order from somewhere
def I_0( x ):
i0 = 0
fac = lambda n: reduce( lambda a, b: a * (b + 1), list(range( n)), 1 )
for n in range( 20 ):
i0 += ((x / 2.0) ** n / (fac( n ))) ** 2
return i0
t = numpy.arange( self.x.size, type=numpy.Float ) - self.x.size / 2.0
T = self.x.size
# this is the window function array
apod = I_0( beta * numpy.sqrt( 1 - (2 * t / T) ** 2 ) ) / I_0( beta )
else:
# alternative method using scipy
import scipy
apod = scipy.kaiser( self.x.size, beta )
for i in range( 2 ):
self.y[ i ] = self.y[ i ] * apod
return self
def autophase( self ):
"""
Automatically phases the data to maximize real part.
Works nice with a SNR above 20 dB, i.e.
10 V signal to 0.1 V noise amplitude.
"""
autophase.get_phase( self )
return self
def fft( self, samples=None ):
"""
Calculate the Fourier transform of the data inplace.
For zero filling set **samples** to a value higher than your data length,
smaller values will truncate your data.
:param int samples: default=None, if given, number of samples returned
"""
fft_of_signal = numpy.fft.fft( self.y[ 0 ] + 1j * self.y[ 1 ], n=samples )
fft_of_signal = numpy.fft.fftshift( fft_of_signal )
dwell = 1.0 / self.sampling_rate
n = fft_of_signal.size
fft_frequencies = numpy.fft.fftfreq( n, dwell )
self.x = numpy.fft.fftshift( fft_frequencies )
self.y[ 0 ] = fft_of_signal.real
self.y[ 1 ] = fft_of_signal.imag
self.set_xlabel( "Frequency / Hz" )
return self
def magnitude( self ):
"""
Return absolute signal, i.e.:
.. math::
y[0] &= \\sqrt{y[0]^2 + y[1]^2} \\\\
y[1] &= 0
"""
# this should calculate the absolute value, and set the imag channel to zero
self.y[ 0 ] = numpy.sqrt( self.y[ 0 ] ** 2 + self.y[ 1 ] ** 2 )
self.y[ 1 ] *= 0 # self.y[0].copy()
return self
def ppm(self, f_ref):
"""
Return result scaled to PPM compared to f_ref
:param f_ref: larmor frequency in MHz
:return:
"""
self.x /= f_ref
self.set_xlabel( "PPM" )
return self
+191
View File
@@ -0,0 +1,191 @@
# data pool collects data from data handling script
# provides data to experiment script and display
import sys
import types
import tables
import collections
import threading
import traceback
import io
from . import ADC_Result
from . import Accumulation
from . import MeasurementResult
class DataPool(collections.MutableMapping):
"""
dictionary with sending change events
"""
# supports tranlation from dictionary keys to pytables hdf node names
# taken from: Python Ref Manual Section 2.3: Identifiers and keywords
# things are always prefixed by "dir_" or "dict_"
translation_table=""
for i in range(256):
c=chr(i)
if (c>="a" and c<="z") or \
(c>="A" and c<="Z") or \
(c>="0" and c<="9"):
translation_table+=c
else:
translation_table+="_"
class Event:
access=0
updated_value=1
new_key=2
deleted_key=3
destroy=4
def __init__(self, what, subject="", origin=None):
self.what=what
self.subject=subject
self.origin=origin
def __repr__(self):
return "<DataPool.Event origin=%s what=%d subject='%s'>"%(self.origin, self.what,self.subject)
def copy(self):
return DataPool.Event(self.what+0, self.subject+"", self.origin)
def __init__(self):
self.__mydict={}
self.__dictlock=threading.Lock()
self.__registered_listeners=[]
def __getitem__(self, name):
try:
self.__dictlock.acquire()
return self.__mydict[name]
finally:
self.__dictlock.release()
def __setitem__(self, name, value):
try:
self.__dictlock.acquire()
if name in self.__mydict:
e=DataPool.Event(DataPool.Event.updated_value,name,self)
else:
e=DataPool.Event(DataPool.Event.new_key, name,self)
self.__mydict[name]=value
finally:
self.__dictlock.release()
self.__send_event(e)
def __delitem__(self, name):
try:
self.__dictlock.acquire()
del self.__mydict[name]
finally:
self.__dictlock.release()
self.__send_event(DataPool.Event(DataPool.Event.deleted_key,name,self))
def __iter__(self):
try:
self.__dictlock.acquire()
return iter(self.__mydict)
finally:
self.__dictlock.release()
def __len__(self):
try:
self.__dictlock.acquire()
return len(self.__mydict)
finally:
self.__dictlock.release()
def keys(self):
try:
self.__dictlock.acquire()
return list(self.__mydict.keys())
finally:
self.__dictlock.release()
def __send_event(self, _event):
for l in self.__registered_listeners:
l(_event.copy())
def __del__(self):
self.__send_event(DataPool.Event(DataPool.Event.destroy))
self.__registered_listeners=None
def write_hdf5(self,hdffile,where="/",name="data_pool", complib=None, complevel=None):
if type(hdffile) is bytes:
dump_file=tables.open_file(hdffile, mode="a")
elif isinstance(hdffile,tables.File):
dump_file=hdffile
else:
raise Exception("expecting hdffile or string")
dump_group=dump_file.create_group(where, name, "DAMARIS data pool")
self.__dictlock.acquire()
dict_keys=list(self.__mydict.keys())
self.__dictlock.release()
try:
for key in dict_keys:
if key[:2]=="__": continue
dump_dir=dump_group
# walk along the given path and create groups if necessary
namelist = key.split("/")
for part in namelist[:-1]:
dir_part="dir_"+str(part).translate(DataPool.translation_table)
if not dir_part in dump_dir:
dump_dir=dump_file.create_group(dump_dir,name=dir_part,title=part)
else:
if dump_dir._v_children[dir_part]._v_title==part:
dump_dir=dump_dir._v_children[dir_part]
else:
extension_count=0
while dir_part+"_%03d"%extension_count in dump_dir:
extension_count+=1
dump_dir=dump_file.create_group(dump_dir,
name=dir_part+"_%03d"%extension_count,
title=part)
# convert last part of key to a valid name
group_keyname="dict_"+str(namelist[-1]).translate(DataPool.translation_table)
# avoid double names by adding number extension
if group_keyname in dump_dir:
extension_count=0
while group_keyname+"_%03d"%extension_count in dump_dir:
extension_count+=1
group_keyname+="_%03d"%extension_count
self.__dictlock.acquire()
if key not in self.__mydict:
# outdated ...
self.__dictlock.release()
continue
value=self.__mydict[key]
self.__dictlock.release()
# now write data, assuming, the object is constant during write operation
if "write_to_hdf" in dir(value):
try:
value.write_to_hdf(hdffile=dump_file,
where=dump_dir,
name=group_keyname,
title=key,
complib=complib,
complevel=complevel)
except Exception as e:
print("failed to write data_pool[\"%s\"]: %s"%(key,str(e)))
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
print("detailed traceback: %s\n"%str(e)+traceback_file.getvalue())
traceback_file=None
else:
print("don't know how to store data_pool[\"%s\"]"%key)
value=None
finally:
dump_group=None
if type(hdffile) is bytes:
dump_file.close()
dump_file=None
def register_listener(self, listening_function):
self.__registered_listeners.append(listening_function)
def unregister_listener(self, listening_function):
if listening_function in self.__registered_listeners:
self.__registered_listeners.remove(listening_function)
+180
View File
@@ -0,0 +1,180 @@
# -*- coding: iso-8859-1 -*-
import threading
#############################################################################
# #
# Name: Class Drawable #
# #
# Purpose: Base class of everything plottable #
# #
#############################################################################
class Drawable:
def __init__(self):
# Will be set correctly in one of the subclasses
self.x = []
self.y = []
self.styles = { }
self.xlabel = None
self.ylabel = None
self.title = None
self.legend = { }
self.text = {}
self.xmin = 0
self.xmax = 0
self.ymin = 0
self.ymax = 0
def get_xdata(self):
"Returns a reference to the x-Plotdata (array)"
return self.x
def set_xdata(self, pos, value):
"Sets a point in x"
try:
self.x[pos] = value
except:
raise
def get_ydata(self, channel):
"Returns the y-Plotdata of channel n (array)"
try:
return self.y[channel]
except:
raise
def set_ydata(self, channel, pos, value):
"Sets a point in y"
try:
self.y[channel][pos] = value
except:
raise
def get_number_of_channels(self):
"Returns the number of channels in y"
return len(self.y)
def get_style(self):
"Returns a reference to plot-styles (dictionary)"
return self.styles
def set_style(self, channel, value):
"Sets a channel to a certain plot-style"
if channel in self.styles:
print("Drawable Warning: Style key \"%s\" will be overwritten with \"%s\"" % (str(channel), str(value)))
self.styles[channel] = str(value)
def get_xlabel(self):
"Returns the label for the x-axis"
return self.xlabel
def set_xlabel(self, label):
"Sets the label for the x-axis"
self.xlabel = str(label)
def get_ylabel(self):
"Gets the label for the y-axis"
return self.ylabel
def set_ylabel(self, label):
"Sets the label for the y-axis"
self.ylabel = str(label)
def get_text(self, index):
"Returns labels to be plotted (List)"
if index in self.text:
return self.text[index]
else: return None
def set_text(self, index, text):
"Sets labels to be plotted "
self.text[index] = str(text)
def get_title(self):
"Returns the title of the plot"
return self.title
def set_title(self, title):
"Sets the title of the plot"
self.title = str(title)
def get_legend(self):
"Returns the legend of the plot (Dictionary)"
return self.legend
def set_legend(self, channel, value):
"Sets the legend of the plot"
if key in self.legend:
print("Drawable Warning: Legend key \"%s\" will be overwritten with \"%s\"" % (str(channel), str(value)))
self.legend[channel] = str(value)
def get_xmin(self):
"Returns minimun of x"
return self.x.min()
def set_xmin(self, xmin):
"Sets minimum of x"
self.xmin = xmin
def get_xmax(self):
"Returns maximum of x"
return self.x.max()
def set_xmax(self, xmax):
"Sets maximum of x"
self.xmax = xmax
def get_ymin(self):
"Returns minimum of y"
if type(self.y)==type([]):
return min([l.min() for l in self.y])
else:
return self.y.min()
def set_ymin(self, ymin):
"Sets minimum of y"
self.ymin = ymin
def get_ymax(self):
"Returns maximimum of y"
if type(self.y)==type([]):
return max([l.max() for l in self.y])
else:
return self.y.max()
def set_ymax(self, ymax):
"Sets maximum of y"
self.ymax = ymax
+71
View File
@@ -0,0 +1,71 @@
# -*- coding: iso-8859-1 -*-
from .Resultable import Resultable
from .Drawable import Drawable
#############################################################################
# #
# Name: Class Error_Result #
# #
# Purpose: Specialised class of Resultable #
# Contains occured error-messages from the core #
# #
#############################################################################
class Error_Result(Resultable, Drawable):
def __init__(self, error_msg = None, desc = {}, job_id = None, job_date = None):
Resultable.__init__(self)
Drawable.__init__(self)
if error_msg is not None:
self.error_message = error_msg
self.set_title("Error-Result: %s" % error_msg)
else:
self.error_message = error_msg
self.description = desc
self.job_id = job_id
self.job_date = job_date
def get_error_message(self):
return self.error_message
def set_error_message(self, error_msg):
self.set_title("Error-Result: %s" % error_msg)
self.error_message = error_msg
# No statistics
def uses_statistics(self):
return False
# Nothing to plot
def get_ydata(self):
return [0.0]
# Nothing to plot
def get_xdata(self):
return [0.0]
# Überladen von Operatoren und Built-Ins -------------------------------------------------------
def __repr__(self):
tmp_string = "Core error-message: %s" % self.error_message
return tmp_string
def __len__(self):
return len(self.error_message)
def __str__(self):
return self.error_message
# Preventing an error when adding something to an error-result (needed for plotting error-results)
def __add__(self, other):
return self
+76
View File
@@ -0,0 +1,76 @@
# -*- coding: iso-8859-1 -*-
#############################################################################
# #
# Name: Class Errorable #
# #
# Purpose: Base class of everything what could contain a statistic error #
# #
#############################################################################
class Errorable:
def __init__(self):
# Will be determined in one of the subclasses
self.xerr = []
self.yerr = []
self.error_color = ""
self.bars_above = False
self.n = 0
def get_xerr(self):
"Returns a reference to x-Error (array)"
return self.xerr
def set_xerr(self, pos, value):
"Sets a point in x-Error"
try:
self.xerr[pos] = value
except:
raise
def get_yerr(self, channel):
"Returns a list of y-Errors (list of arrays, corresponding channels)"
try:
return self.yerr[channel]
except:
raise
def set_yerr(self, channel, pos, value):
"Sets a point in y-Error"
try:
self.yerr[channel][pos] = value
except:
raise
def get_error_color(self):
"Returns the error-bar color"
return self.error_color
def set_error_color(self, color):
"Sets the error-bar color"
self.error_color = color
def get_bars_above(self):
"Gets bars-above property of errorplot"
return self.bars_above
def set_bars_above(self, bars_above):
"Sets bars-above property of errorplot"
self.bars_above = bool(bars_above)
def ready_for_drawing_error(self):
"Returns true if more than one result have been accumulated"
if self.n >= 2: return True
else: return False
+288
View File
@@ -0,0 +1,288 @@
import threading
import math
import types
import sys
import tables
import numpy
import collections
from . import Drawable
## provide gaussian statistics for a series of measured data points
#
# AccumulatedValue provides mean and error of mean after being fed with measured data
# internaly it keeps the sum, the sum of squares and the number of data points
class AccumulatedValue:
def __init__(self, mean=None, mean_err=None, n=None):
"""
one value with std. deviation
can be initialized by:
No argument: no entries
one argument: first entry
two arguments: mean and its error, n is set 2
three arguments: already existing statistics defined by mean, mean's error, n
"""
if mean is None:
self.y=0.0
self.y2=0.0
self.n=0
elif mean_err is None and n is None:
self.y=float(mean)
self.y2=self.y**2
self.n=1
elif mean_err is None:
self.n=max(1, int(n))
self.y=float(mean)*self.n
self.y2=(float(mean)**2)*self.n
elif n is None:
self.n=2
self.y=float(mean)*2
self.y2=(float(mean_err)**2+float(mean)**2)*2
else:
self.n=int(n)
self.y=float(mean)*self.n
self.y2=float(mean_err)**2*n*(n-1.0)+float(mean)**2*n
def __add__(self,y):
new_one=AccumulatedValue()
if (type(y) is types.InstanceType and isinstance(y, AccumulatedValue)):
new_one.y=self.y+y.y
new_one.y2=self.y2+y.y2
new_one.n=self.n+y.n
else:
new_one.y=self.y+float(y)
new_one.y2=self.y2+float(y)**2
new_one.n=self.n+1
return new_one
def __iadd__(self,y):
if (type(y) is types.InstanceType and isinstance(y, AccumulatedValue)):
self.y+=y.y
self.y2+=y.y2
self.n+=y.n
else:
self.y+=float(y)
self.y2+=float(y)**2
self.n+=1
return self
def copy(self):
a=AccumulatedValue()
a.y=self.y
a.y2=self.y2
a.n=self.n
return a
def mean(self):
"""
returns the mean of all added/accumulated values
"""
if self.n is None or self.n==0:
return None
else:
return self.y/self.n
def sigma(self):
"""
returns the standard deviation added/accumulated values
"""
if self.n>1:
variance=(self.y2-(self.y**2)/float(self.n))/(self.n-1.0)
if variance<0:
if variance<-1e-20:
print("variance=%g<0! assuming 0"%variance)
return 0.0
return math.sqrt(variance)
elif self.n==1:
return 0.0
else:
return None
def mean_error(self):
"""
returns the mean's error (=std.dev/sqrt(n)) of all added/accumulated values
"""
if self.n>1:
variance=(self.y2-(self.y**2)/float(self.n))/(self.n-1.0)
if variance<0:
if variance<-1e-20:
print("variance=%g<0! assuming 0"%variance)
return 0.0
return math.sqrt(variance/self.n)
elif self.n==1:
return 0.0
else:
return None
def __str__(self):
if self.n==0:
return "no value"
elif self.n==1:
return str(self.y)
else:
return "%g +/- %g (%d accumulations)"%(self.mean(),self.mean_error(),self.n)
def __repr__(self):
return str(self)
class MeasurementResult(Drawable.Drawable, collections.UserDict):
def __init__(self, quantity_name):
"""
convenient accumulation and interface to plot functions
dictionary must not contain anything but AccumulatedValue instances
"""
Drawable.Drawable.__init__(self)
collections.UserDict.__init__(self)
self.quantity_name=quantity_name
self.lock=threading.RLock()
# get the selected item, if it does not exist, create an empty one
def __getitem__(self, key):
if key not in self:
a=AccumulatedValue()
self.data[float(key)]=a
return a
else:
return self.data[float(key)]
def __setitem__(self,key,value):
if not isinstance(value, AccumulatedValue):
value=AccumulatedValue(float(value))
return collections.UserDict.__setitem__(self,
float(key),
value)
def __add__(self, right_value):
if right_value==0:
return self.copy()
else:
raise Exception("not implemented")
def get_title(self):
return self.quantity_name
def get_xdata(self):
"""
sorted array of all dictionary entries without Accumulated Value objects with n==0
"""
keys=numpy.array([k for k in list(self.data.keys()) if not (isinstance(self.data[k], AccumulatedValue) and self.data[k].n==0)],
dtype="Float64")
keys.sort()
return keys
def get_ydata(self):
return self.get_xydata()[1]
def get_xydata(self):
k=self.get_xdata()
v=numpy.array([self.data[key].mean() for key in k], dtype="Float64")
return [k,v]
def get_errorplotdata(self):
k=self.get_xdata()
v=numpy.array([self.data[key].mean() for key in k], dtype="Float64")
e=numpy.array([self.data[key].mean_error() for key in k], dtype="Float64")
return [k,v,e]
def get_lineplotdata(self):
k=self.get_xdata()
v=numpy.array(self.y, dtype="Float64")
return [k, v]
def uses_statistics(self):
"""
drawable interface method, returns True
"""
return True
def write_to_csv(self,destination=sys.stdout, delimiter=" "):
"""
writes the data to a file or to sys.stdout
destination can be a file or a filename
suitable for further processing
"""
# write sorted
the_destination=destination
if type(destination) in (str,):
the_destination=open(destination, "w")
the_destination.write("# quantity:"+str(self.quantity_name)+"\n")
the_destination.write("# x y ysigma n\n")
for x in self.get_xdata():
y=self.data[x]
if type(y) in [float, int, int]:
the_destination.write("%e%s%e%s0%s1\n"%(x, delimiter, y, delimiter, delimiter))
else:
the_destination.write("%e%s%e%s%e%s%d\n"%(x,
delimiter,
y.mean(),
delimiter,
y.mean_error(),
delimiter,
y.n))
the_destination=None
def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None):
h5_table_format= {
"x" : tables.Float64Col(),
"y" : tables.Float64Col(),
"y_err" : tables.Float64Col(),
"n" : tables.Int64Col()
}
filter=None
if complib is not None:
if complevel is None:
complevel=9
filter=tables.Filters(complevel=complevel,complib=complib,shuffle=1)
mr_table=hdffile.create_table(where=where,name=name,
description=h5_table_format,
title=title,
filters=filter,
expectedrows=len(self))
mr_table.flavor="numpy"
mr_table.attrs.damaris_type="MeasurementResult"
self.lock.acquire()
try:
mr_table.attrs.quantity_name=self.quantity_name
row=mr_table.row
xdata=self.get_xdata()
if xdata.shape[0]!=0:
for x in self.get_xdata():
y=self.data[x]
row["x"]=x
if type(y) in [float, int, int]:
row["y"]=y
row["y_err"]=0.0
row["n"]=1
else:
row["y"]=y.mean()
row["y_err"]=y.mean_error()
row["n"]=y.n
row.append()
finally:
mr_table.flush()
self.lock.release()
def read_from_hdf(hdf_node):
"""
reads a MeasurementResult object from the hdf_node
or None if the node is not suitable
"""
if not isinstance(hdf_node, tables.Table):
return None
if hdf_node._v_attrs.damaris_type!="MeasurementResult":
return None
mr=MeasurementResult(hdf_node._v_attrs.quantity_name)
for r in hdf_node.iterrows():
mr[r["x"]]=AccumulatedValue(r["y"],r["y_err"],r["n"])
return mr
+28
View File
@@ -0,0 +1,28 @@
class Persistance :
def __init__(self, shots):
self.shots = shots
self.accu = 0
self.counter = 0
self.result_list = []
def fade(self, res):
self.counter += 1
if self.accu == 0:
self.accu=res+0
self.result_list.append(res)
if self.counter < 1:
for i,ch in enumerate(self.accu.y):
ch += res.y[i]
elif len(self.result_list) == self.shots:
self.counter = len(self.result_list)
old_result = self.result_list.pop(0)
for i,ch in enumerate(self.accu.y):
ch *= self.shots
ch -= old_result.y[i]
ch += res.y[i]
else:
for i,ch in enumerate(self.accu.y):
ch *= self.counter-1
ch += res.y[i]
self.accu /= self.counter
return self.accu
+65
View File
@@ -0,0 +1,65 @@
# -*- coding: iso-8859-1 -*-
#############################################################################
# #
# Name: Class Resultable #
# #
# Purpose: Base class of everything what could be a core-result #
# #
#############################################################################
class Resultable:
def __init__(self):
self.job_id = None
self.job_date = None
self.description = { }
def get_job_id(self):
"Returns the job-id of this result"
return self.job_id
def set_job_id(self, _id):
"Sets the job-id of this result"
self.job_id = _id
def get_job_date(self):
"Gets the date of this result"
return self.job_date
def set_job_date(self, date):
"Sets the date of this result"
self.job_date = date
def get_description_dictionary(self):
"Returns a reference to the description (Dictionary)"
return self.description
def set_description_dictionary(self, dictionary):
"Sets the entire description"
self.description = dictionary
def get_description(self, key):
"Returns the description value for a given key"
if key in self.description:
return self.description[key]
else:
print("Warning Resultable: No value for key \"%s\". Returned None" % str(key))
return None
def set_description(self, key, value):
"Adds a attribute to the description"
if key in self.description:
print("Warning: Result key \"%s\" will be overwritten with \"%s\"." % (str(key), str(value)))
self.description[key] = value
+37
View File
@@ -0,0 +1,37 @@
import numpy as N
class Signalpath:
def phase(self, degrees):
"""
rotate signal by **degrees** for phase cycling, etc.
:param degrees: rotate signal by this value
:return:
"""
if self.get_number_of_channels() != 2:
raise Exception("rotation defined only for 2 channels")
# simple case 0, 90, 180, 270 degree
reduced_angle = divmod(degrees, 90)
if abs(reduced_angle[1]) < 1e-6:
reduced_angle = reduced_angle[0] % 4
if reduced_angle == 0:
return
elif reduced_angle == 1:
self.y[1] *= -1
self.y = [self.y[1], self.y[0]]
elif reduced_angle == 2:
self.y[0] *= -1
self.y[1] *= -1
elif reduced_angle == 3:
self.y[0] *= -1
self.y = [self.y[1], self.y[0]]
else:
sin_angle = N.sin(degrees / 180.0 * N.pi)
cos_angle = N.cos(degrees / 180.0 * N.pi)
self.y = [cos_angle * self.y[0] - sin_angle * self.y[1],
sin_angle * self.y[0] + cos_angle * self.y[1]]
return self
+34
View File
@@ -0,0 +1,34 @@
# -*- coding: iso-8859-1 -*-
from .Resultable import Resultable
from .Drawable import Drawable
from types import *
#############################################################################
# #
# Name: Class Temp_Result #
# #
# Purpose: Specialised class of Resultable and Drawable #
# Contains recorded temperature data #
# #
#############################################################################
class Temp_Result(Resultable, Drawable):
def __init__(self, x = None, y = None, desc = None, job_id = None, job_date = None):
Resultable.__init__(self)
Drawable.__init__(self)
if (x is None) and (y is None) and (desc is None) and (job_id is None) and (job_date is None):
pass
elif (x is not None) and (y is not None) and (desc is not None) and (job_id is not None) and (job_date is not None):
pass
else:
raise ValueError("Wrong usage of __init__!")
# Überladen von Operatoren und Built-Ins -------------------------------------------------------
# / Überladen von Operatoren und Built-Ins -----------------------------------------------------
+9
View File
@@ -0,0 +1,9 @@
from damaris.data.ADC_Result import ADC_Result
from damaris.data.Accumulation import Accumulation
from damaris.data.MeasurementResult import MeasurementResult, AccumulatedValue
from damaris.data.DataPool import DataPool
from damaris.data.Error_Result import Error_Result
from damaris.data.Config_Result import Config_Result
__all__=["ADC_Result", "Accumulation", "MeasurementResult", "AccumulatedValue", "DataPool", "FFT", "Error_Result", "Config_Result" ]
+63
View File
@@ -0,0 +1,63 @@
from scipy.optimize import fmin_powell, bisect, ridder, brentq
import numpy as N
def calculate_entropy(phi, real, imag, gamma, dwell):
"""
Calculates the entropy of the spectrum (real part).
p = phase
gamma should be adjusted such that the penalty and entropy are in the same magnitude
"""
# This is first order phasecorrection
# corr_phase = phi[0]+phi[1]*arange(0,len(signal),1.0)/len(signal) # For 0th and 1st correction
# Zero order phase correction
real_part = real*N.cos(phi)-imag*N.sin(phi)
# Either this for calculating derivatives:
# Zwei-Punkt-Formel
# real_diff = (Re[1:]-Re[:-1])/dwell
# Better this:
# Drei-Punkte-Mittelpunkt-Formel (Ränder werden nicht beachtet)
# real_diff = abs((Re[2:]-Re[:-2])/(dwell*2))
# Even better:
# Fünf-Punkte-Mittelpunkt-Formel (ohne Ränder)
real_diff = N.abs((real_part[:-4]-8*real_part[1:-3]
+8*real_part[3:-1]-2*real_part[4:])/(12*dwell))
# TODO Ränder, sind wahrscheinlich nicht kritisch
# Calculate the entropy
h = real_diff/real_diff.sum()
# Set all h with 0 to 1 (log would complain)
h[h==0]=1
entropy = N.sum(-h*N.log(h))
# My version, according the paper
#penalty = gamma*sum([val**2 for val in Re if val < 0])
# calculate penalty value: a real spectrum should have positive values
if real_part.sum() < 0:
tmp = real_part[real_part<0]
penalty = N.dot(tmp,tmp)
if gamma == 0:
gamma = entropy/penalty
penalty = N.dot(tmp,tmp)*gamma
else:
penalty = 0
#print "Entropy:",entrop,"Penalty:",penalty # Debugging
shannon = entropy+penalty
return shannon
def get_phase(result_object):
global gamma
gamma=0
real = result_object.y[0].copy()
imag = result_object.y[1].copy()
dwell = 1.0/result_object.sampling_rate
# fmin also possible
xopt = fmin_powell( func=calculate_entropy,
x0=N.array([0.0]),
args=(real, imag, gamma, dwell),
disp=0)
result_object.y[0] = real*N.cos(xopt) - imag*N.sin(xopt)
result_object.y[1] = real*N.sin(xopt) + imag*N.cos(xopt)
return result_object