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
+9
View File
@@ -0,0 +1,9 @@
{
"linters": {
"sample": {
"type": "pep8",
"include": "(\\.py$)",
"exclude": "(^third-party/)"
}
}
}
+7
View File
@@ -0,0 +1,7 @@
include src/gui/DAMARIS.png
include src/gui/DAMARIS.ico
include src/gui/damaris.glade
include src/gui/damaris.gladep
include doc/index.html
recursive-include doc/reference-html *.html *.gif *.png *.css
recursive-include doc/tutorial-html *.html *.gif *.png *.css *.tar.gz *.sh
+39
View File
@@ -0,0 +1,39 @@
Installing into private directory
=================================
For whatever reason, it might be useful to install the
python frontend into a private directory.
Here are some examples on how to do it:
python setup.py install --home=/opt/damaris-private
or
python setup.py install --home=$HOME/devel/damaris-installed
To start Damaris use for example:
/opt/damaris-private/bin/DAMARIS
Updating debian/changelog
=========================
Subversion
==========
Use the following command to get all the commit messages:
Mac OS X:
svn2cl -i -a --stdout |gsed 's/^\t/ /'|gsed -e '/^[0-9]/d'
Linux:
svn2cl -i -a --stdout |sed 's/^\t/ /'|sed -e '/^[0-9]/d'
Git
===
git log --oneline .
Then pick the stuff the users need to know.
dch -M -v x.xx
View File
View File
+148
View File
@@ -0,0 +1,148 @@
python3-damaris (0.18.2) UNRELEASED; urgency=medium
* Experiment queuing implemented
* Joachim Beerwerths (JB) fixes merged
* Experiment class tracks experiment time (JB)
* implemented rudimentary PPM display (swapped x axis)
-- Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de> Wed, 12 Jun 2019 09:23:26 +0200
python3-damaris (0.18.1) unstable; urgency=medium
* switch to git
* pytables v2 to v3 conversion done
* new syntax highlighter, abortable sleep function
* goniometer control for Marco Braun NMR probe head (AXYs) added
* added cmd line script to read out and set flow control (red-y series by Voegtlin)
* modifications to write_to_tecmag, allow 2D experiments (by Oleg Petrov)
* multiple DAMARIS can run at the same time, allowing queuing of experiemnts
(rudimentary)
-- Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de> Mon, 26 Mar 2018 16:40:24 +0200
python3-damaris (0.16) UNRELEASED; urgency=medium
* version bump
-- Markus Rosenstihl <markusro@pfg.fkp.physik.tu-darmstadt.de> Wed, 03 Jun 2015 10:34:11 +0100
python3-damaris (0.15-0) experimental; urgency=low
* [r762] added proper homepage and documentation links
* [r760] src/data/DaFFT.py[DEL]: Removed deprecated DaFFT module.
This module is replaced by DamarisFFT,
which extends the ADC_Result and Accumulation class.
* [r759] src/experiments/Experiment.py: * Updated more
documentation
- Fixes #4 (set_pfg)
* [r758] src/experiments/Experiment.py: Added sphinx documentation
strings
* [r757] src/data/DamarisFFT.py: fixed taps/space, update
documentation
* [r756] src/gui/DamarisGUI.py: reformatted DamarisGUI.py to make
it better readable
* [r755] src/data/DamarisFFT.py, src/gui/DamarisGUI.py: fixed evil
tab/spaces
* [r754] src/data/DamarisFFT.py: fix indentaion in DamarisFFT
* [r753] src/data/DaFFT.py, src/data/DamarisFFT.py,
src/data/DataPool.py, src/data/MeasurementResult.py,
src/data/Persistance.py, src/data/autophase.py,
src/gui/BackendDriver.py, src/gui/ExperimentHandling.py,
src/gui/ExperimentWriter.py, src/gui/ResultHandling.py: Heroic
squashing of spurious furious tab bugs
* [r741] migrate to standard svn repo layout
* [r740] - exceptions now derive from a common DamarisException
class, which in turn derives from std::runtime_error
(previously, all exceptions derived from std::string, which is
not a good idea)
- I also added a RecoverableException class for ADC_result and
the like to derive from. This class should be
used whenever code is caught inside the backend, e.g. in the
experiment loop, and passed on to the frontend
without aborting
- backends were modified accordingly
- started adding a new test suite for more thorough unit tests
and to understand program flow better
- the working directory is now reset after a core is destroyed,
also cleaned up the getcwd code
* [r737] - frontend fix for multi-channel detection
-- Markus Rosenstihl <Markus.Rosenstihl@physik.tu-darmstadt.de> Mon, 15 Dec 2014 21:22:45 +0000
python3-damaris (0.14-svn) experimental; urgency=low
* MeasurementResults (MR) can be lineplots (Oleg Petrov)
* Default symbol for MR changed to "*"
* Several more improvements to GUI code
* Fixed some errors in FFT module (oelg Petrov)
* Config now according to XDG standard
-- Markus Rosenstihl <Markus.Rosenstihl@physik.tu-darmstadt.de> Wed, 07 Nov 2012 12:36:40 +0000
python3-damaris (0.13-0) experimental; urgency=low
* using cElementTree if available, giving the ResultReader almost 20x speed up
-- Markus Rosenstihl <Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de> Wed, 03 Nov 2010 18:49:46 +0000
python3-damaris (0.12-0.0) experimental; urgency=low
* new development version
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Sat, 24 May 2008 17:49:25 +0200
python3-damaris (0.11-0.1) experimental; urgency=low
* updated debian python policy
* job descriptions: types are saved and recovered
* support for system wide defaults file thanks to Christian
* new interleaved range by Markus Rosenstihl
* revised display source change event handling, found some severe errors
* cursor movement and selection problems solved
* log window information are saved in hdf files
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Thu, 17 Mar 2008 17:28:13 +0100
python3-damaris (0.11-0.0) experimental; urgency=low
* rename to python3-damaris
* x axis log plot
* %()s functions for hdf file naming
* line number widgets for scripts
* Persistance class added (scope like fade away of signal changes)
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Thu, 07 Feb 2008 02:21:45 +0100
greendamaris (0.10-0.2) experimental; urgency=medium
* increased compatibility with pytables version 1 and 2
* sending signals to backend, when it fails to quit properly
* correcting quit event return value
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Fri, 12 Oct 2007 13:34:33 +0200
greendamaris (0.10-0.1) experimental; urgency=low
* added docs and browser button for docs
* limited number of points to display for GTKCairo backend
* numarray replaced by numpy
* adapted width of window to fit even gnome icon size
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Tue, 02 Oct 2007 19:11:33 +0200
greendamaris (0.10-0.0) experimental; urgency=medium
* started separate source package of pyhton-damaris
* pycentral used to support both python 2.4 and 2.5
-- Achim Gaedke <Achim.Gaedke@physik.tu-darmstadt.de> Sat, 08 Sep 2007 12:56:06 +0200
+1
View File
@@ -0,0 +1 @@
9
+16
View File
@@ -0,0 +1,16 @@
Source: python3-damaris
Section: science
Priority: optional
Maintainer: Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de>
Build-Depends: debhelper, python3-dev, dh-python, dpkg-dev
Standards-Version: 3.7.3
X-Python3-Version: >= 3.5
Package: python3-damaris
Architecture: all
XB-Python-Version: ${python3:Versions}
Provides: ${python3:Provides}, damaris-frontend-py3
Depends: ${python3:Depends}, python3-numpy, python3-scipy, python3-gi, python3-matplotlib, python3-tables, lzop, python3-xdg, libgtksourceview-3.0-1
Recommends: damaris-backends, python3-numpy-ext
Suggests: python3-doc, python3-tables-doc, python3-numpy-doc
Description: python frontend for DAMARIS project
+10
View File
@@ -0,0 +1,10 @@
[Desktop Entry]
Name=DAMARIS3
Comment=DArmstadt MAgnetic Resonance Instrument Software
Exec=/usr/bin/DAMARIS3 %F
X-MultipleArgs=true
Terminal=false
MimeType=text/x-python;text/plain;
Type=Application
Categories=Science;Education;Physics;Chemistry;
Icon=DAMARIS3
+1
View File
@@ -0,0 +1 @@
?package(python-damaris): needs="X11" section="Apps/Science" title="DAMARIS3" longtitle="DArmstadt Magnetic Resonance Instrument Software" command="DAMARIS3" icon="/usr/share/python-damaris/images/DAMARIS3.png"
Vendored Executable
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/make -f
# Achim Gaedke
# May 2007
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
PYVERS=$(shell py3versions -vr)
PACKAGE_NAME=python3-damaris
MODULE_NAME=damaris3
PYBASE=$(CURDIR)/debian/${PACKAGE_NAME}
DH_ALWAYS_EXCLUDE=CVS:.svn
clean:
dh_testdir
dh_testroot
dh_installdirs
# Add here commands to clean up after the build process.
rm -f *-stamp
rm -rf dist build $(PYVERS:%=build-ext-%)
find . -name *\.py[co] -exec rm {} \;
dh_clean
build: build-stamp
build-stamp: $(PYVERS:%=build-ext-%)
touch $@
build-ext-%:
dh_testdir
python$* setup.py build
touch $@
install: install-stamp
# install menu related things
dh_install debian/damaris3.desktop /usr/share/applications/
install-stamp: build-stamp $(PYVERS:%=install-ext-%)
install-ext-%:
python$* setup.py install --root $(PYBASE) --install-layout=deb
echo "DAMARIS3 script: removing path to local installation"
-sed 's/^sys.path.insert(0,.*).*/# damaris package is found on default path/' <$(PYBASE)/usr/bin/DAMARIS3 >$(PYBASE)/usr/bin/DAMARIS3.new
-mv $(PYBASE)/usr/bin/DAMARIS3.new $(PYBASE)/usr/bin/DAMARIS3
binary-indep: build install
dh_link usr/share/python3-damaris/doc usr/share/doc/python3-damaris/html
dh_link usr/share/python3-damaris/images usr/share/doc/python3-damaris/images
dh_link usr/share/python3-damaris/images/DAMARIS3.png usr/share/icons/DAMARIS3.png
dh_testdir -i
dh_testroot -i
dh_installchangelogs -i
dh_installdocs -i
dh_python3
dh_installmenu -i
dh_compress -i -X.py
dh_fixperms -i
dh_installdeb -i
dh_gencontrol -i
dh_md5sums -i
dh_builddeb -i
binary-arch: build install
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure
+242
View File
@@ -0,0 +1,242 @@
# Doxyfile 1.5.3
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "python DAMARIS"
PROJECT_NUMBER = 0.13-svn
OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class " \
"The $name widget " \
"The $name file " \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text "
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = damaris
INPUT_ENCODING = ISO-8859-15
FILE_PATTERNS = *.py \
*.PY
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = YES
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = NO
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = reference-html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
HTML_DYNAMIC_SECTIONS = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = NO
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
DOT_PATH = /usr/bin/
DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 1000
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
+24
View File
@@ -0,0 +1,24 @@
by now the documentation creation is not automatized...
# html reference
# requires dot and doxygen
cd doc
ln -s ../src damaris
doxygen Doxyfile
rm damaris
# todo: copy damaris logo
# html wiki export
# requires moinmoin and damaris/data as wikidata
cd doc
# underlay must be writable, so we have to copy it...
cp -r /usr/share/moin/underlay wikiunderlay
python dump_wiki.py
cp -r /usr/share/moin/htdocs/modern tutorial-html
rm -r wikiunderlay wikiconfig.py
# get useful numpy doc
wget http://www.scipy.org/Numpy_Example_List_With_Doc?action=print
+177
View File
@@ -0,0 +1,177 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Dump a MoinMoin wiki to static pages
based on "moin.py export dump" command
"""
import sys, os, time, StringIO, codecs, shutil, re, errno
from MoinMoin import config, wikiutil, Page
from MoinMoin.request import RequestCLI
from MoinMoin.action import AttachFile
HTML_SUFFIX = ".html"
page_template = u'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=%(charset)s">
<title>%(pagename)s</title>
<link rel="stylesheet" type="text/css" media="all" charset="utf-8" href="%(theme)s/css/common.css">
<link rel="stylesheet" type="text/css" media="screen" charset="utf-8" href="%(theme)s/css/screen.css">
<link rel="stylesheet" type="text/css" media="print" charset="utf-8" href="%(theme)s/css/print.css">
</head>
<body>
<div id="page">
<h1 id="title">%(pagename)s</h1>
%(pagehtml)s
</div>
<hr>
%(timestamp)s
</body>
</html>
'''
def _attachment(request, pagename, filename, outputdir):
filename = filename.encode(config.charset)
source_dir = AttachFile.getAttachDir(request, pagename)
source_file = os.path.join(source_dir, filename)
dest_dir = os.path.join(outputdir, "attachments", wikiutil.quoteWikinameFS(pagename))
dest_file = os.path.join(dest_dir, filename)
dest_url = "attachments/%s/%s" % (wikiutil.quoteWikinameFS(pagename), wikiutil.url_quote(filename))
if os.access(source_file, os.R_OK):
if not os.access(dest_dir, os.F_OK):
try:
os.makedirs(dest_dir)
except:
print ("Cannot create attachment directory '%s'" % dest_dir)
raise
elif not os.path.isdir(dest_dir):
print ("'%s' is not a directory" % dest_dir)
shutil.copyfile(source_file, dest_file)
print ('Writing "%s"...' % dest_url)
return dest_url
else:
return ""
class PluginScript: #(MoinScript):
""" Dump script class """
def __init__(self):
pass
def mainloop(self):
""" moin-dump's main code. """
# Prepare output directory
outputdir=os.path.join(os.curdir,"tutorial-html")
try:
os.mkdir(outputdir)
print "Created output directory '%s'!" % outputdir
except OSError, err:
if err.errno != errno.EEXIST:
print "Cannot create output directory '%s'!" % outputdir
raise
sys.path.insert(0, os.path.abspath(os.curdir))
wikiconfig_template="""
from MoinMoin.multiconfig import DefaultConfig
class Config(DefaultConfig):
sitename = u'DAMARIS Homepage and Usergroup'
logo_string = u'<img src="/damaris/wiki/damaris/DAMARIS.png" alt="DAMARIS Logo">'
page_front_page = u"Welcome"
interwikiname = 'damaris'
data_dir = '%(pwd)s/wikidata/'
data_underlay_dir = '%(pwd)s/wikiunderlay'
url_prefix = '/damaris/wiki'
theme_default = 'modern'
"""%{"pwd": os.curdir, "underlay": "/home/achim/underlay" }
config_file = open("wikiconfig.py","w")
print >>config_file, wikiconfig_template
config_file.close()
# start with wiki entry page
request = RequestCLI(pagename="Welcome")
# fix url_prefix so we get relative paths in output html
url_prefix = "."
request.cfg.url_prefix = url_prefix
pages = request.rootpage.getPageList(user='') # get list of all pages in wiki
pages.sort()
# extract a list of pages to be extracted
# trial session to fat!!!
try:
pages_match = re.compile("^(Tutorial|auxiliary tools|overview|installation|code snippets)")
pages = [page for page in pages if pages_match.match(page)]
except:
print "did not find suitable pages"
raise
wikiutil.quoteWikinameURL = lambda pagename, qfn=wikiutil.quoteWikinameFS: (qfn(pagename) + HTML_SUFFIX)
AttachFile.getAttachUrl = lambda pagename, filename, request, addts=0, escaped=0: (_attachment(request, pagename, filename, outputdir))
errfile = os.path.join(outputdir, 'error.log')
errlog = open(errfile, 'w')
errcnt = 0
page_front_page = wikiutil.getSysPage(request, request.cfg.page_front_page).page_name
page_title_index = wikiutil.getSysPage(request, 'TitleIndex').page_name
page_word_index = wikiutil.getSysPage(request, 'WordIndex').page_name
navibar_html = ''
for p in [page_front_page, page_title_index, page_word_index]:
navibar_html += '&nbsp;[<a href="%s">%s</a>]' % (wikiutil.quoteWikinameURL(p), wikiutil.escape(p))
urlbase = request.url # save wiki base url
for pagename in pages:
# we have the same name in URL and FS
file = wikiutil.quoteWikinameURL(pagename)
print ('Writing "%s"...' % file)
try:
pagehtml = ''
request.url = urlbase + pagename # add current pagename to url base
page = Page.Page(request, pagename)
request.page = page
try:
request.reset()
pagehtml = request.redirectedOutput(page.send_page, request, count_hit=0, content_only=1)
except:
errcnt = errcnt + 1
print >>sys.stderr, "*** Caught exception while writing page!"
print >>errlog, "~" * 78
print >>errlog, file # page filename
import traceback
traceback.print_exc(None, errlog)
finally:
timestamp = time.strftime("%Y-%m-%d %H:%M")
filepath = os.path.join(outputdir, file)
fileout = codecs.open(filepath, 'w', config.charset)
logo_html = '<img src="logo.png">'
fileout.write(page_template % {
'charset': config.charset,
'pagename': pagename,
'pagehtml': pagehtml,
'logo_html': logo_html,
'navibar_html': navibar_html,
'timestamp': timestamp,
'theme': request.cfg.theme_default,
})
fileout.close()
# ToDo: insert style sheets and logo
errlog.close()
if errcnt:
print >>sys.stderr, "*** %d error(s) occurred, see '%s'!" % (errcnt, errfile)
if __name__=="__main__":
PluginScript().mainloop()
+84
View File
@@ -0,0 +1,84 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>python-damaris documentation index</title>
</head>
<body>
<h1><img alt="DAMARIS" src="../images/DAMARIS.png" align="middle">DAMARIS
Documentation Index<br>
</h1>
<h2>Local Resources:</h2>
<br>
Documentation from DAMARIS wiki:<br>
<ul>
<li><a href="tutorial-html/Tutorial.html">Tutorial</a></li>
<li><a href="tutorial-html/Tutorial%282f%29List_of_Commands.html">Command
Reference</a></li>
</ul>
python-damaris source code reference (generated by <a
href="http://www.doxygen.org/">doxygen</a>)<br>
<ul>
<li><a href="reference-html/index.html">index</a></li>
<li><a href="reference-html/namespacedamaris_1_1data.html">result
reference</a><br>
</li>
<li><a
href="reference-html/namespacedamaris_1_1experiments_1_1Experiment.html">experiment
reference</a></li>
</ul>
<h2>Internet Resources:</h2>
<ul>
<li><a href="http://damaris.berlios.de/">DAMARIS
Homepage</a></li>
<li>Software Related</li>
<ul>
<li><a href="http://www.python.org/">Python Homepage</a></li>
<li><a href="http://www.numpy.org/">Numeric Python (numpy)</a></li>
<li><a href="http://www.pytables.org/">HDF Support (pytables)</a><br>
</li>
<li><a href="http://www.scipy.org/">Scientific Python (scipy)</a></li>
<li><a href="http://www.pygtk.org/">PyGTK Homepage (Graphics)<br>
</a></li>
<li><a href="matplotlib.sourceforge.net">2D Graph drawing
(Matplotlib)</a></li>
<li><a href="http://ipython.scipy.org/">Interactive Python
Interpreter (iPython)</a><br>
</li>
</ul>
<li>More Documentation:</li>
<ul>
<li>Official Python <a href="http://www.python.org/doc/">Documentation</a>
/ <a href="http://docs.python.org/tut/tut.html">Tutorial</a></li>
<li>More Python <a
href="http://www.awaretek.com/tutorials.html#begin">Tutorials</a><br>
</li>
</ul>
<ul>
<li><a href="http://www.scipy.org/Numpy_Example_List_With_Doc">Numpy
Example List</a></li>
<li><a href="http://www.tramy.us/numpybook.pdf">Numpy Book (pdf)</a>
von Travis E. Oliphant</li>
<li>A <a href="http://www.scipy.org/Cookbook">Cookbook</a> for
many advanced data processing tasks<br>
</li>
</ul>
<li>NMR Links</li>
<ul>
<li><a href="http://www.nmrwiki.org/">NMR wiki</a><br>
</li>
<li><a href="http://www.spincore.com/nmrinfo/">NMR Info</a><br>
</li>
<li><a href="http://www.diffusion-fundamentals.org/">Diffusion
Fundamentals</a><br>
</li>
</ul>
<ul>
<li><a href="http://www.fkp.tu-darmstadt.de/groups/ag_fujara/fup/index.de.jsp">AG Fujara</a></li>
<li><a href="http://www.fkp.tu-darmstadt.de/groups/ag_vogel/index.de.jsp">AG Vogel</a><br>
</li>
</ul>
</ul>
</body>
</html>
+1864
View File
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/python3
# setup script will insert local DAMARIS installation path behind import sys statement
# this must happen before any damaris stuff is called!
import sys
import os, argparse
import sqlite3
# for numpy-1.1 and later: check the environment for LANG and LC_NUMERIC
# see: http://projects.scipy.org/scipy/numpy/ticket/902
if os.environ.get("LANG", "").startswith("de") or os.environ.get("LC_NUMERIC", "").startswith("de"):
os.environ["LC_NUMERIC"] = "C"
parser = argparse.ArgumentParser(description='DArmstadt MAgnetic Resonance Instrumentation Software')
parser.add_argument("--run", action="store_true", help="run DAMARIS immediately with given scripts")
parser.add_argument("--clean", action="store_true", help="cleanup DAMARIS run files")
parser.add_argument("--debug", action="store_true", help="run DAMARIS with DEBUG flag set")
parser.add_argument("--mpl", help="run DAMARIS with matplotlib backend",
choices=["GTK3Agg","GTK3Cairo"], default="GTK3Agg")
parser.add_argument("exp_script", help="experiment script", nargs="?", metavar="EXP.py")
parser.add_argument("res_script", help="result script", nargs="?", metavar="RES.py")
#parser.add_argument("exp_res_script", help="(NOT IMPLEMENTED) further experiment and result script pairs", nargs=argparse.REMAINDER, metavar="EXPn.py RESn.py")
args = parser.parse_args()
import matplotlib
if args.mpl:
matplotlib.use(args.mpl)
import damaris.gui.DamarisGUI
lockfile = os.path.expanduser('~/.damaris.lockdb')
if args.clean:
if os.path.exists(lockfile):
print("Removing lockfile: %s"%lockfile)
os.remove(lockfile)
else:
print("Lockfile does not exists: %s"%lockfile)
lockdb = sqlite3.connect(lockfile)
c = lockdb.cursor()
c.execute("CREATE TABLE IF NOT EXISTS damaris (uuid text, status text)")
lockdb.commit()
if args.debug:
damaris.gui.DamarisGUI.debug = True
print("debug flag set")
try:
import resource
resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
except ImportError:
pass
matplotlib.rcParams["verbose.level"] = "debug"
print(args)
d = damaris.gui.DamarisGUI.DamarisGUI(args.exp_script, args.res_script, start_immediately=args.run)
d.run()
#for exp_script, res_script in args.exp_res_script:
# print "here"
# d = damaris.gui.DamarisGUI.DamarisGUI(exp_script, res_script, start_immediatly=args.run)
# d.run()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
#lockdb.close()
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env python
import shutil
import os
import os.path
import sys
from distutils.core import setup
from distutils.command.build_scripts import build_scripts as _build_scripts
from distutils import log
from distutils.util import convert_path
from distutils.dep_util import newer
#if sys.version_info < (2, 5, 3):
# log.error("**** ERROR: Install manually: python setup.py install ****")
# raise ValueError
class build_damaris_scripts(_build_scripts):
#user_options=_build_scripts.user_options[:]
#user_options.append(('install-dir=', 'd', "directory to install scripts to"))
def initialize_options (self):
_build_scripts.initialize_options(self)
self.damaris_dir = None
def finalize_options (self):
_build_scripts.finalize_options(self)
self.set_undefined_options('install',
('install_lib', 'damaris_dir'))
def run (self):
"change PYTHON_PATH for DAMARIS executable"
_build_scripts.run(self)
script="scripts/DAMARIS3"
script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script))
self.damaris_dir=os.path.normpath(self.damaris_dir)
if self.damaris_dir in sys.path:
log.debug("not changing %s (this path is on standard path)", script)
# nothing to do for us
return
# now change PATH in DAMARIS script
# copy backup
log.info("adapting DAMARIS script to use local installation")
shutil.copyfile(outfile, outfile+".bak")
# the file should keep all its attributes (executable...)
inf=open(outfile+".bak","r")
outf=open(outfile,"w")
l=inf.readline()
while not l.startswith("import sys") and l!="":
outf.write(l)
l=inf.readline()
if l!="":
outf.write(l)
l=inf.readline()
while l.endswith("# inserted by setup.py\n"):
l=inf.readline()
outf.write("sys.path.insert(0,\"%s\") # inserted by setup.py\n"%self.damaris_dir)
outf.write(l)
outf.writelines(inf.readlines())
inf.close()
outf.close()
os.remove(outfile+".bak")
# create doc data file information
distribution_doc_prefix=os.path.join("share","python3-damaris","doc")
distribution_data_files = [[ "share", []],
[os.path.join("share", "python3-damaris", "images"),
["src/gui/DAMARIS3.png", "src/gui/DAMARIS3.ico"]],
[os.path.join("share", "python3-damaris"), []],
[distribution_doc_prefix, ['doc/index.html']]]
if os.path.isdir(os.path.join("doc","reference-html")):
# no subdirs, work can be done in simple way
distribution_data_files.append([os.path.join(distribution_doc_prefix, 'reference-html'),
[os.path.join('doc', 'reference-html', f)
for f in os.listdir(os.path.join('doc', 'reference-html'))]])
if os.path.isdir(os.path.join("doc","tutorial-html")):
# here, modern style file and attachment directories should be handled
for d in os.walk(os.path.join("doc","tutorial-html")):
distribution_data_files.append([os.path.join(os.path.dirname(distribution_doc_prefix),d[0]),
[os.path.join(d[0], f) for f in d[2]]])
LONG_DESCRIPTION="""
DArmstadt MAgnetic Resonance Instrument Software
"""
GPL_LICENCE = "feed licence here"
setup (
name = 'python3-damaris',
version = "0.18.2",
description = 'python frontend for DAMARIS (DArmstadt MAgnetic Resonance Instrument Software)',
long_description = LONG_DESCRIPTION,
author = 'Achim Gaedke',
author_email = 'Achim.Gaedke@physik.tu-darmstadt.de',
maintainer = 'Achim Gaedke',
maintainer_email = 'Achim.Gaedke@physik.tu-darmstadt.de',
url = 'http://www.fkp.physik.tu-darmstadt.de/damaris/',
license = GPL_LICENCE,
platforms = ['Any',],
keywords = ['NMR', 'data-processing'],
packages = [ 'damaris',
'damaris.data',
'damaris.experiments',
'damaris.gui',
'damaris.tools' ],
package_dir = { 'damaris': 'src',
'damaris.data': 'src/data',
'damaris.experiments': 'src/experiments',
'damaris.gui': 'src/gui',
'damaris.tools': 'src/tools' },
package_data = { 'damaris.gui': ['DAMARIS3.png', 'DAMARIS3.ico', 'damaris.xml', 'python.xml']},
scripts = ['scripts/DAMARIS3'],
cmdclass={"build_scripts": build_damaris_scripts},
data_files = distribution_data_files
)
+18
View File
@@ -0,0 +1,18 @@
##\mainpage DArmstadt MAgnetic Resonance Instrument Software
#
#Python Frontend based on
# - Python/GTK
# - Matplotlib
# - Numpy
# - PyTables
#
#Written by
# - Achim Gaedke
# - Christopher Schmitt
# - Markus Rosenstihl
# - Holger Stork
# - Christian Tacke
## module contents
#
__all__=["experiments", "data", "gui"]
+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
+609
View File
@@ -0,0 +1,609 @@
# -*- coding: utf-8 -*-
import types
import numpy
class StateBase(object):
def __init__(self):
pass
def to_xml(self, indent = ""):
return indent + "<!-- " + repr(self) + " -->"
class StateSimple(StateBase):
def __init__(self, time, content=None):
super(StateSimple, self).__init__()
if time < 0:
raise AssertionError("time for state is negative!")
self.time = time
self.content = content
def to_xml(self, indent = ""):
s = indent + '<state time="%s"' % repr(self.time)
if self.content is None:
return s + '/>\n'
s += '>\n'
s += indent + ' ' + str(self.content) + '\n'
s += indent + '</state>\n'
return s
def __repr__(self):
return 'StateSimple(%s, %s)' % (self.time, repr(self.content))
class StateList(StateBase):
def __init__(self):
super(StateList, self).__init__()
self.list = []
def to_xml(self, indent = " "):
s = ""
for k in self.list:
if hasattr(k, "to_xml"):
s += k.to_xml(indent)
else:
s += indent + str(k)
return s
def append(self, val):
self.list.append(val)
class StateLoop(StateList):
"""Represents a loop in the state tree"""
def __init__(self, repeat):
super(StateLoop, self).__init__()
self.repeat = repeat
def to_xml(self, indent = ""):
s = indent + ('<sequent repeat="%d">\n' % self.repeat)
s += super(StateLoop, self).to_xml(indent + " ")
s += indent + '</sequent>\n'
return s
def __repr__(self):
return 'StateLoop(repeat=%d, %s)' \
% (self.repeat, repr(self.list))
#############################################################
# #
# Class: Experiment #
# #
# Purpose: Represents one full experiment (one program on #
# the pulse-card; one file) #
# #
#############################################################
from . import dac
class Experiment:
"""
Class holding the complete state tree for a single experiment. This state tree represents one
program on the pulse card. It is written in a single file and picked up by the backend.
"""
## Experiment class holding the state tree
job_id = 0
def __init__(self, gating=None, rf_sources=[], rf_gates=[]):
self.job_id = Experiment.job_id
Experiment.job_id += 1
self.state_list = StateList()
self.list_stack = []
self.description = { }
self.gating = gating
assert type(rf_sources) == type(list()), "rf_sources needs to be a list with the channels"
assert type(rf_gates) == type(list()), "rf_gates needs to be a list with the channels"
assert len(rf_gates) == len(rf_sources), "rf_sources and rf_gates must have equal number of entries"
self.rf_sources = rf_sources
self.rf_gates = rf_gates
#for tracking the experiment length:
#because loops are possible we need to track the length for each loop level
self.total_time=[]
self.total_time.append(0.0)
#and we need to know the number of iterations of the loops to multiply the state.
self.loop_iterations=[]
self.loop_iterations.append(1)
# Commands -------------------------------------------------------------------------------------
## Creates a state with ttl signals of duration *length*.
#
# **Example:**
# ttl_pulse(length=1e-6,value=3)
# will create a ttl pulse on channels 0 and 1 (2**0 + 2**1) of duration 1us
# @param length time length if this state
# @param channel select a single channel (1...24)
# @param value select the channels via decimal representation (2**0 + 2**1 ...)
def ttl_pulse(self, length, channel = None, value = None):
"""
Creates a state with length **length** and switches
requested bits of the pulse programmer to HIGH:
:param float length: pulse length in seconds
:param int channel: selects a single channel (No. 1 - 24)
:param int value: lines to set (integer) for example value=3 selects channels 1 and 2 (2**1 + 2**2)
"""
the_value=0
if value is not None:
the_value=int(value)
elif channel is not None:
the_value=1<<channel
self.state_list.append(StateSimple(length, \
'<ttlout value="0x%06x"/>' % the_value))
self.total_time[-1] += length
## Same as ttl_pulse, but no *channel* keyword
def ttls(self, length = None, value = None):
"""
Same as ttl_pulse, but no *channel* keyword
:param float length:
:param int value: lines to set (integer)
:return:
"""
the_value=int(value)
s_content = '<ttlout value="0x%06x"/>' % the_value
if length is not None:
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
else:
self.state_list.append(s_content)
def rf_pulse(self, length=None, phase=0, source=0):
"""
make an rf pulse, including gating and phase switching
:param float length: pulse length
:param float phase: pulse phase
:param int source: source id
:return:
"""
if not self.gating:
raise SyntaxError("Can not use rf_pulse without configuration: Experiment(gating=None, rf_sources=[], rf_gates=[]")
self.set_phase(phase, ttls=self.rf_gates[source])
self.ttls(length=self.gating-0.5e-6,value=self.rf_gates[source])
self.ttls(length=length, value=self.rf_gates[source]+self.rf_sources[source])
## Beginning of a new state
def state_start(self, time):
"""
Starts a state in the pulse programs with duration *time*.
This must be closed with state_end()
"""
self.state_list.append('<state time="%s">\n' % repr(time))
self.total_time[-1] += time
## End of *state_start*
def state_end(self):
"""
Closes a previous start_state()
"""
self.state_list.append('</state>\n')
## An empty state doing nothing
# @param time Duration of this state
# @param ttls Additinional ttl channels
def wait(self, time, ttls=None, gating=False):
"""
Wait specified **time** doing nothing.
:param float time: seconds to wait
:param int ttls: lines to set (integer)
:param bool gating: reduce time by gating, i.e. wait in front of rf_pulse
:return:
"""
if gating:
time -= self.gating
if ttls is not None:
s_content = '<ttlout value="0x%06x"/>' % ttls
self.state_list.append(StateSimple(time,s_content))
else:
self.state_list.append(StateSimple(time))
self.total_time[-1] += time
## Records data with given number of samples, sampling-frequency frequency and sensitivity
# @param samples Number of samples to record
# @param frequency Sampling frequency
# @param timelength Length of this state, per default calculated automatically
# @param sensitivity Sensitivity in Umax/V
# @param ttls Additional ttl channels
def record(self, samples, frequency, timelength=None, sensitivity = None, ttls=None, channels = 3, offset = None, impedance = None):
"""
Records data with given number of samples, sampling frequency and sensitivity.
Optionally, the time length of this state can be specified. If not specified **timelength** is
deduced from **samples**/**frequency**:
:param int samples: Number of samples to record
:param float frequency: Sampling frequency
:param float timelength: Length of this state, per default calculated automatically
:param float sensitivity: Sensitivity in Umax/V
:param int ttls: lines to set (integer)
:param int channels: default=3, Number of channels counting from 0
"""
attributes='s="%d" f="%d"'%(samples,frequency)#%g
if channels != 1 and channels != 3 and channels != 5 and channels != 15:
raise ValueError("Channel definition is illegal")
attributes += ' channels="%i"'%(channels)
nchannels = 0
if channels == 1:
nchannels = 1
elif channels == 3 or channels == 5:
nchannels = 2
elif channels == 15:
nchannels = 4
if sensitivity is not None:
# float values are allowed and applied to all channels
if isinstance(sensitivity, float) or isinstance(sensitivity, int):
for i in range(nchannels):
attributes +=' sensitivity%i="%f"'%(i, float(sensitivity))
else:
for i in range(nchannels):
attributes +=' sensitivity%i="%f"'%(i, sensitivity[i])
if offset is not None:
# int values are allowed and applied to all channels
if isinstance(offset, int):
for i in range(nchannels):
attributes +=' offset%i="%f"'%(i, offset)
else:
for i in range(nchannels):
attributes +=' offset%i="%f"'%(i, offset[i])
if impedance is not None:
# float values are allowed and applied to all channels
if isinstance(impedance, float):
for i in range(nchannels):
attributes += ' impedance%i="%i"'%(i, impedance)
else:
for i in range(nchannels):
attributes += ' impedance%i="%i"'%(i, impedance[i])
s_content = '<analogin %s/>' % attributes
if ttls is not None:
s_content+='<ttlout value="0x%06x"/>' % ttls
if timelength is None:
timelength = samples / float(frequency)#*1.01
self.state_list.append(StateSimple(timelength, s_content))
self.total_time[-1] += timelength
## Create a loop on the pulse programmer. Loop contents can not change inside the loop.
# @params iterations Number of loop iterations
def loop_start(self, iterations):
"""
creates a loop of given number of iterations and has to be closed by loop_end().
Commands inside the loop can not change, i.e. the parameters are the same for each loop run.
This loop is created on the pulse programmer, thus saving commands.
One must close the loop with loop_end (see below)!
:param int iterations: iterations to loop
"""
l = StateLoop(iterations)
self.state_list.append(l)
# (These two lines could probably be guarded by a mutex)
self.list_stack.append(self.state_list)
self.state_list = l
self.total_time.append(0.0)
self.loop_iterations.append(iterations)
## End loop state
def loop_end(self):
"""
Closes a loop_start() statement.
"""
# (This line could probably be guarded by a mutex)
self.state_list = self.list_stack.pop(-1)
looptime=self.total_time.pop(-1)
loopiterations=self.loop_iterations.pop(-1)
self.total_time[-1] += looptime*loopiterations
## Set the frequency and phase of the frequency source.
## This state needs 2us.
# @param frequency New frequency in Hz
# @param phase New phase in degrees
# @param ttls Additional ttl channels
def set_frequency(self, frequency, phase, ttls=0):
"""
Sets the frequency and phase of the frequency source and optionally further channels.
The state length is 2 µs. Stabilisation time depends on RF source.
:param float frequency: frequency to set in Hz
:param float phase: phase to set
:param int ttls: default=0, lines to set (integer)
"""
s_content = '<analogout id="0" f="%f" phase="%f"/>' % (frequency, phase)
if ttls != 0:
s_content += '<ttlout value="0x%06x"/>' % ttls
self.state_list.append(StateSimple(2e-6, s_content))
self.total_time[-1] += 2e-6
## Creates a, possibly shaped, pulsed gradient.
# @param dac_value DAC value to set
# @param length Duration of the state, minimum length is 42*90ns=3.78us (default)
# @param shape Tuple of (shape, resolution/seconds), shape can be one of: rec (default), sin2, sin
# @param is_seq If set to *True*, do NOT set DAC to zero after this state
# @param trigger Additional ttl channels
def set_pfg(self, dac_value=None, length=None, shape=('rec', 0), trigger=4, is_seq=False):
"""
This sets the value for the PFG, it also sets it back automatically.
If you don't whish to do so (i.e. line shapes) set is_seq=1
If you want to set a trigger, set trigger (default=4, i.e. channel 2)
If you want shaped gradients: shape=(ashape, resolution), ashape can be rec, sin2, sin
The default DAC ID is hardcoded (id=1)!
:param int dac_value: DAC value to set
:param float length: Duration of the state, minimum length is 42*90ns=3.78us (default)
:param tuple shape: Tuple of (shape, resolution/seconds), shape can be one of: rec (default), sin2, sin
:param bool is_seq: If set to *True*, do *NOT* set DAC to zero at the end of this state
:param int trigger: default=4, lines to set (integer)
"""
try:
form, resolution = shape
except:
raise SyntaxError("shape argument needs to be a tuple, i.e. ('shape',resolution), shape can be sin, sin2, rec")
if length == None:
# mimimum length
length=42*9e-8
if resolution >= length:
raise ValueError("Resolution %.3e of shaped gradients can not be longer than total length %.3e"%(resolution, length))
if resolution < 42*9e-8:
raise ValueError("Resulution %.3e can not be smaller than %.3e"%(resolution, 42*9e-8))
t_steps = numpy.arange(0,length,resolution)
if form == 'rec': # shape==None --> rectangular gradients
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="%i"/>' % (trigger, dac_value)
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
if not is_seq:
s_content = '<analogout id="1" dac_value="0"/>'
self.state_list.append(StateSimple(42*9e-8, s_content))
self.total_time[-1] += 42*9e-8
elif form == 'sin2':
# sin**2 shape
for t in t_steps:
dac = int (dac_value*numpy.sin(numpy.pi/length*t)**2)
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="%i"/>' % (trigger, dac)
self.state_list.append(StateSimple(resolution, s_content))
# set it back to zero
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="0"/>' % (trigger)
self.state_list.append(StateSimple(resolution, s_content))
self.total_time[-1] += resolution*(len(t_steps)+1)
elif form == 'sin':
# sin shape
for t in t_steps:
dac = int (dac_value*numpy.sin(numpy.pi/length*t))
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="%i"/>' % (trigger, dac)
self.state_list.append(StateSimple(resolution, s_content))
# set it back to zero
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="0"/>' % (trigger)
self.state_list.append(StateSimple(resolution, s_content))
self.total_time[-1] += resolution*(len(t_steps)+1)
else: # don't know what to do
raise SyntaxError("form is unknown: %s"%form)
## sets the value of a DAC
# @param dac_value DAC value to set
# @param dac_id ID of the dac in case of multiple DAC(default=1)
# @param length Duration of the state
# @param is_seq If set to *True*, do NOT set DAC to zero after this state
# @param ttls Additional ttl channels
def set_dac(self, dac_value, dac_id=1, length=None, is_seq=False, ttls=0):
"""
This sets the value for the DAC and if given the TTLs.
It also sets it back to zero automatically.
If you don't wish to set the value back to zero (i.e. line shapes) set is_seq=True
The state length is at least 3.78 µs (is_seq=1) or 7.28µs (is_seq=0).
:param int dac_value: dac value, between -2**19-1 and +2**19
:param int dac_id: default=1, which DAC to control
:param float length: default=None, length of this state in seconds. If *None* length=42*90ns=3.78µs
:param bool is_seq: default=False, do not reset DAC to 0 (zero) if True
:param int ttls: default=0, lines to set (integer)
"""
if length==None:
length=42*9e-8
s_content = '<analogout id="%d" dac_value="%i"/><ttlout value="0x%06x"/>' \
% (dac_id, dac_value, ttls)
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
if not is_seq:
s_content = '<analogout id="%d" dac_value="0"/><ttlout value="0x%06x"/>' \
% (dac_id, ttls)
self.state_list.append(StateSimple(42*9e-8, s_content))
self.total_time[-1] += 42*9e-8
## sets the phase of the frequency source.
## This state needs 0.5us, though the phase switching time is dependent on the frequency source
# @param phase New phase to set
# @param ttls Additional ttl channels
def set_phase(self, phase, ttls=0):
"""
Sets the phase of the RF source to this value.
Note: This is relative to the phase at the beginnig of the experiment.
The state length is 0.5 µs, but the stabilisation time is RF source dependent. Typical
values for PTS310 is 2 µs.
:param float phase: phase to set
:param int ttls: default=0, lines to set (integer)
"""
s_content = '<analogout phase="%f" />' % (phase)
if ttls!=0:
s_content += '<ttlout value="%d"/>' % ttls
self.state_list.append(StateSimple(0.5e-6, s_content))
self.total_time[-1] += 0.5e-6
## sets a description which is carried via the back end result
## file to the result script in the front end. In the result script
## you can extract the description with get_description(key)
# @param key Name of description
# @param value Value of description
def set_description(self, key, value):
"""Sets a description which is carried via the back end result
file to the result script in the front end. In the result script
you can extract the description with get_description
:param str key: the key
:param value: the value, its type is saved.
"""
if key in list(self.description.keys()):
print('Warning: Overwriting existing description "%s" = "%s" with "%s"' % (key, self.description[key], value))
self.description[key] = value
## set the PTS310/PTS500 frequency source to local mode
def set_pts_local(self):
"""
Sets the PTS310/PTS500 frequency source to local mode.
State length is 2 µs.
"""
self.state_list.append(StateSimple(1e-6, '<ttlout value="0xf000"/>'))
self.state_list.append(StateSimple(1e-6, '<ttlout value="0x8000"/>'))
self.total_time[-1] += 2e-6
# / Commands -----------------------------------------------------------------------------------
# Public Methods -------------------------------------------------------------------------------
def get_job_id(self):
"""
Returns the job-id of the current experiment
:returns: job_id
"""
return self.job_id
def write_xml_string(self):
"""
Returns the current program as a string
:returns: XML string
"""
# Standart XML-Kopf
xml_string = '<?xml version="1.0" encoding="ISO-8859-1"?>\n'
# Experiment-Start-Tag einfügen
xml_string += '<experiment no="%d">\n' % self.job_id
# Descriptions einfügen
if len(self.description)==0:
xml_string += ' <description/>\n'
else:
xml_string += ' <description>\n'
for key,value in self.description.items():
type_string="repr"
if value is None:
type_string="None"
value=""
elif type(value) is float or isinstance(value, numpy.floating):
type_string="Float"
value=repr(value)
elif type(value) is int or isinstance(value, numpy.integer):
type_string="Int"
value=repr(value)
elif type(value) is int:
type_string="Long"
value=repr(value)
elif type(value) is complex or isinstance(value, numpy.complexfloating):
type_string="Complex"
value=repr(value)
elif type(value) is bool or isinstance(value, numpy.bool_):
type_string="Boolean"
value=repr(value)
elif type(value) in (str,):
type_string="String"
else:
value=repr(value)
xml_string += ' <item key="%s" type="%s">%s</item>\n'%(key, type_string ,value)
xml_string += " </description>\n"
# Experiment-Inhalt einfügen
xml_string += self.state_list.to_xml(indent = " ")
# Experiment-End-Tag
xml_string += '</experiment>\n'
return xml_string
def write_quit_job(self):
"""
Returns a xml quit-job
:returns: XML quit-job
"""
return '<?xml version="1.0" encoding="ISO-8859-1"?>\n<quit/>'
def get_length(self):
timelength = 0.0
#calculate the correct timelength also for unclosed loops, if there is no unclosed loop
#the timelength of this experiment is self.total_time[-1].
for i in range(len(self.total_time)):
timelength += self.total_time[i]
timelength *= self.loop_iterations[i]
return timelength
class Quit(Experiment):
def write_xml_string(self):
return '<?xml version="1.0" encoding="ISO-8859-1"?>\n<quit no="%d"/>'%self.job_id
# /Public Methods ------------------------------------------------------------------------------
def self_test():
e = Experiment()
e.set_description("key", "value")
e.set_frequency(85e6, 90, ttls=16)
e.wait(1e-6)
#e.rf_pulse(1, 1e-6/3) # val = 1
e.ttl_pulse(1e-6/3, 1) # val = 2
e.ttl_pulse(1e-6/3, None, 7) # val = 7
if True:
e.loop_start(30)
e.set_pfg(dac_value=1024, length=5e-6, is_seq = True, shape=('rec', 42*9e-8))
e.loop_start(400)
e.set_phase(270, ttls = 32)
e.loop_end()
e.ttl_pulse(5e-6, channel = 6)
e.loop_end()
else:
l = StateLoop(3)
l.append(StateSimple(5e-6, '<ttlout value="1"/>'))
e.state_list.append(l)
e.set_dac(12345, dac_id=2, is_seq = True, ttls=16)
e.record(1024, 20e6)
try:
e.wait(-1)
except AssertionError:
pass
else:
raise AssertionError("An exception should happen")
e.set_pts_local()
print(e.write_xml_string())
print(e.get_length())
if __name__ == '__main__':
self_test()
+3
View File
@@ -0,0 +1,3 @@
from .Experiment import Experiment
from damaris.tools.ranges import *
#__all__=["Experiment"]
+12
View File
@@ -0,0 +1,12 @@
#import math
"""
This module holds everything connected with the DAC and PFG
"""
def conv(I_out=0):
"""
converts the demanded Output current in Integer
"""
V_dac=I_out/50.0
dac_value=-(V_dac-0.00983)/1.81413e-5
return int(dac_value)
+296
View File
@@ -0,0 +1,296 @@
import os
import os.path
import subprocess
import sys
import time
import re
import glob
from . import ExperimentWriter
from . import ResultReader
import threading
import types
import signal
if sys.platform=="win32":
import winreg
__doc__ = """
This class handles the backend driver
"""
class BackendDriver(threading.Thread):
def __init__(self, executable, spool, clear_jobs=False, clear_results=False):
threading.Thread.__init__(self, name="Backend Driver")
self.core_pid = None
self.core_input = None
self.core_output = None
self.statefilename = None
self.executable=str(executable)
self.spool_dir=spool
self.experiment_pattern="job.%09d"
self.result_pattern=self.experiment_pattern+".result"
if not os.path.isfile(self.executable):
raise AssertionError("could not find backend %s "%self.executable)
if not os.access(self.executable,os.X_OK):
raise AssertionError("insufficient rights for backend %s execution"%self.executable)
if not os.path.isdir(self.spool_dir):
try:
os.makedirs(os.path.abspath(self.spool_dir))
except OSError as e:
print(e)
raise AssertionError("could not create backend's spool directory %s "%self.spool_dir)
# remove stale state filenames
if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
old_state_files=glob.glob(os.path.join(self.spool_dir,"*.state"))
statelinepattern=re.compile("<state name=\"([^\"]+)\" pid=\"([^\"]+)\" starttime=\"([^\"]+)\">")
for statefilename in old_state_files:
statefile=open(statefilename,"r")
statelines=statefile.readlines()
statefile.close
del statefile
core_pid=None
for l in statelines:
matched=statelinepattern.match(l)
if matched:
core_pid=int(matched.group(2))
break
if core_pid is not None:
if os.path.isdir("/proc/%d"%core_pid):
raise AssertionError("found backend with pid %d (state file %s) in same spool dir"%(core_pid,statefilename))
else:
print("removing stale backend state file", statefilename)
os.remove(statefilename)
else:
print("todo: take care of existing backend state files")
self.result_reader = ResultReader.BlockingResultReader(self.spool_dir,
no=0,
result_pattern=self.result_pattern,
clear_jobs=clear_jobs,
clear_results=clear_results)
self.experiment_writer = ExperimentWriter.ExperimentWriterWithCleanup(self.spool_dir,
no=0,
job_pattern=self.experiment_pattern,
inform_last_job=self.result_reader)
self.quit_flag=threading.Event()
self.raised_exception=None
def run(self):
# take care of older logfiles
self.core_output_filename=os.path.join(self.spool_dir,"logdata")
if os.path.isfile(self.core_output_filename):
i=0
max_logs=100
while os.path.isfile(self.core_output_filename+".%02d"%i):
i+=1
while (i>=max_logs):
i-=1
os.remove(self.core_output_filename+".%02d"%i)
for j in range(i):
os.rename(self.core_output_filename+".%02d"%(i-j-1),self.core_output_filename+".%02d"%(i-j))
os.rename(self.core_output_filename, self.core_output_filename+".%02d"%0)
# create logfile
self.core_output=open(self.core_output_filename,"w")
# again look out for existing state files
state_files=glob.glob(os.path.join(self.spool_dir,"*.state"))
if state_files:
self.raised_exception="found other state file(s) in spool directory: "+",".join(state_files)
self.quit_flag.set()
return
# start backend
if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
self.core_input=subprocess.Popen([self.executable, "--spool", self.spool_dir],
stdout=self.core_output,
stderr=self.core_output)
if sys.platform=="win32":
cygwin_root_key=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/")
cygwin_path=winreg.QueryValueEx(cygwin_root_key,"native")[0]
os.environ["PATH"]+=";"+os.path.join(cygwin_path,"bin")+";"+os.path.join(cygwin_path,"lib")
self.core_input=subprocess.Popen("\"" + self.executable + "\"" + " --spool "+self.spool_dir,
stdout=self.core_output,
stderr=self.core_output)
# wait till state file shows up
timeout=10
# to do: how should I know core's state name????!!!!!
self.statefilename=None
state_files=glob.glob(os.path.join(self.spool_dir,"*.state"))
while len(state_files)==0:
if timeout<0 or self.core_input is None or self.core_input.poll() is not None or self.quit_flag.isSet():
# look into core log file and include contents
print(timeout, self.core_input, self.quit_flag.isSet())
if self.core_input is not None:
print(self.core_input.poll())
log_message=''
self.core_input=None
if os.path.isfile(self.core_output_filename):
# to do include log data
log_message='\n'+''.join(open(self.core_output_filename,"r", errors='replace').readlines()[:10])
if not log_message:
log_message=" no error message from core"
self.core_output.close()
self.raised_exception="no state file appeared or backend died away:"+log_message
print(self.raised_exception)
self.quit_flag.set()
return
time.sleep(0.05)
timeout-=0.05
state_files=glob.glob(os.path.join(self.spool_dir,"*.state"))
# save the one
if len(state_files)>1:
print("did find more than one state file, taking first one!")
self.statefilename=state_files[0]
# read state file
statefile=open(self.statefilename,"r")
statelines=statefile.readlines()
statefile=None
statelinepattern=re.compile("<state name=\"([^\"]+)\" pid=\"([^\"]+)\" starttime=\"([^\"]+)\">")
self.core_pid=-1
for l in statelines:
matched=statelinepattern.match(l)
if matched:
self.core_pid=int(matched.group(2))
break
# wait on flag and look after backend
while not self.quit_flag.isSet() and self.is_busy():
self.quit_flag.wait(0.1)
if self.quit_flag.isSet():
self.stop_queue()
while self.is_busy():
time.sleep(0.1)
if not self.is_busy():
if self.core_input is not None:
backend_result=self.core_input.poll()
wait_loop_counter=0
while backend_result is None:
# waiting in tenth of a second
time.sleep(0.1)
wait_loop_counter+=1
backend_result=self.core_input.poll()
if backend_result is not None: break
if wait_loop_counter==10:
print("sending termination signal to backend process")
self.send_signal("SIGTERM")
elif wait_loop_counter==20:
print("sending kill signal to backend process")
self.send_signal("SIGKILL")
elif wait_loop_counter>30:
print("no longer waiting for backend shutdown")
break
if backend_result is None:
print("backend dit not end properly, please stop it manually")
elif backend_result>0:
print("backend returned ", backend_result)
elif backend_result<0:
sig_name=[x for x in dir(signal) if x.startswith("SIG") and \
x[3]!="_" and \
(type(signal.__dict__[x])is int) and \
signal.__dict__[x]==-backend_result]
if sig_name:
print("backend was terminated by signal ",sig_name[0])
else:
print("backend was terminated by signal no",-backend_result)
self.core_input = None
self.core_pid = None
# the experiment handler should stop
if self.experiment_writer is not None:
# self.experiment_writer.
self.experiment_writer=None
# tell result reader, game is over...
#self.result_reader.stop_no=self.experiment_writer.no
if self.result_reader is not None:
self.result_reader.poll_time=-1
self.result_reader=None
def clear_job(self,no):
jobfilename=os.path.join(self.spool_dir,"job.%09d")
resultfilename=os.path.join(self.spool_dir,"job.%09d.result")
if os.path.isfile(jobfilename):
os.remove(jobfilename)
if os.path.isfile(resultfilename):
os.remove(resultfilename)
def get_messages(self):
# return pending messages
if self.core_output.tell()==os.path.getsize(self.core_output_filename):
return None
return self.core_output.read()
def restart_queue(self):
self.send_signal("SIGUSR1")
def stop_queue(self):
self.send_signal("SIGQUIT")
# assumes success
#self.core_pid=None
#self.core_input=None
def abort(self):
# abort execution
self.send_signal("SIGTERM")
# assumes success
#self.core_pid=None
#self.core_input=None
def send_signal(self, sig):
if self.core_pid is None:
print("BackendDriver.send_signal is called with core_pid=None")
return
try:
if sys.platform[:5]=="linux":
os.kill(self.core_pid,signal.__dict__[sig])
if sys.platform[:7]=="win32":
# reg_handle=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
cygwin_root_key=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/")
cygwin_path=winreg.QueryValueEx(cygwin_root_key,"native")[0]
kill_command=os.path.join(cygwin_path,"bin","kill.exe")
os.popen("%s -%s %d"%(kill_command,sig,self.core_pid))
except OSError as e:
print("could not send signal %s to core: %s"%(sig, str(e)))
def is_busy(self):
"Checks for state file"
return self.statefilename is not None and os.path.isfile(self.statefilename) and \
self.core_input is not None and self.core_input.poll() is None
#file_list = glob.glob(os.path.join(self.spool_dir, self.core_state_file))
#if len(file_list) != 0:
# return True
#else:
# return False
def get_exp_writer(self):
return self.experiment_writer
def get_res_reader(self):
return self.result_reader
def __del__(self):
# stop core and wait for it
if self.core_pid is not None:
try:
self.abort()
except OSError:
pass
self.core_input=None
if self.core_output:
self.core_output.close()
self.core_output=None
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
import threading
import io
import traceback
import sys
import time
from damaris.experiments.Experiment import Quit
from damaris.experiments import Experiment
class StopExperiment(Exception):
pass
class ExperimentHandling(threading.Thread):
"""
runs the experiment script in sandbox
"""
def __init__(self, script, exp_writer, data):
threading.Thread.__init__(self, name="experiment handler")
self.script=script
self.writer=exp_writer
self.data=data
self.quit_flag = threading.Event()
if self.data is not None:
self.data["__recentexperiment"]=-1
def synchronize(self, before=0, waitsteps=0.1):
while (self.data["__recentexperiment"]>self.data["__recentresult"]+before) and not self.quit_flag.isSet():
self.quit_flag.wait(waitsteps)
if self.quit_flag.isSet():
raise StopExperiment
def sleep(self, seconds):
self.quit_flag.wait(seconds)
if self.quit_flag.isSet():
raise StopExperiment
def run(self):
dataspace={}
exp_classes = __import__('damaris.experiments', dataspace, dataspace, ['Experiment'])
for name in dir(exp_classes):
if name[:2]=="__" and name[-2:]=="__": continue
dataspace[name]=exp_classes.__dict__[name]
del exp_classes
dataspace["data"]=self.data
dataspace["synchronize"]=self.synchronize
dataspace["sleep"]=self.sleep
self.raised_exception = None
self.location = None
exp_iterator=None
try:
exec(self.script, dataspace)
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
self.traceback=traceback_file.getvalue()
traceback_file=None
return
if "experiment" in dataspace:
try:
exp_iterator=dataspace["experiment"]()
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
self.traceback=traceback_file.getvalue()
traceback_file=None
return
while exp_iterator is not None and not self.quit_flag.isSet():
# get next experiment from script
try:
job=next(exp_iterator)
except StopExperiment as se:
break
except StopIteration as si:
break
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
self.traceback=traceback_file.getvalue()
traceback_file=None
break
# send it
self.writer.send_next(job)
# write a note
if isinstance(job, Experiment):
if self.data is not None:
self.data["__recentexperiment"]=job.job_id+0
# track time when experiment is started. This is placed after executing the script
# and getting the iterator so that time for initializing devices etc. is not included
if "__experimentstarted" not in self.data:
self.data["__experimentstarted"] = time.time()
# track time of experiments in queue, the total time and for each experiment:
if "__totalexperimentlength" not in self.data:
self.data["__totalexperimentlength"] = 0.0
if "__experimentlengths" not in self.data:
self.data["__experimentlengths"] = {}
experimentlength = job.get_length()
self.data["__experimentlengths"][job.job_id+0] = experimentlength
self.data["__totalexperimentlength"] += experimentlength
# relax for a short time
if "__resultsinadvance" in self.data and self.data["__resultsinadvance"]+100<job.job_id:
self.quit_flag.wait(0.05)
if self.quit_flag.isSet():
dataspace=None
exp_iterator=None
break
self.writer.send_next(Quit(), quit=True)
# do not count quit job (is this a good idea?)
dataspace=None
self.exp_iterator=None
self.writer=None
+66
View File
@@ -0,0 +1,66 @@
import os
import os.path
import shutil
from damaris.experiments import Experiment
class ExperimentWriter:
"""
writes experiments in propper way to spool directory
"""
def __init__(self, spool, no=0, job_pattern="job.%09d", inform_last_job=None):
self.spool=spool
self.job_pattern=job_pattern
self.no=no
self.inform_last_job=inform_last_job
# test if spool exists
if not os.path.isdir(spool):
os.mkdir(spool)
def send_next(self, job, quit=False):
"""
"""
if quit and self.inform_last_job is not None:
self.inform_last_job.stop_no=self.no
self.inform_last_job=None
job.job_id=self.no
job_filename=os.path.join(self.spool,self.job_pattern%self.no)
f=open(job_filename+".tmp","w")
f.write(job.write_xml_string())
f.flush()
f.close() # explicit close under windows necessary (don't know why)
del f
# this implementation tries to satisfiy msvc filehandle caching
os.rename(job_filename+".tmp", job_filename)
#shutil.copyfile(job_filename+".tmp", job_filename)
#try:
# os.unlink(job_filename+".tmp")
#except OSError:
# print "could not delete temporary file %s.tmp"%job_filename
self.no+=1
def __del__(self):
if self.inform_last_job is not None:
self.inform_last_job.stop_no=self.no-1
self.inform_last_job=None
class ExperimentWriterWithCleanup(ExperimentWriter):
"""
writes experiments and cleans up in front of queue
"""
def __init__(self, spool, no=0, job_pattern="job.%09d", inform_last_job=None):
ExperimentWriter.__init__(self, spool, no, job_pattern, inform_last_job=inform_last_job)
self.delete_no_files(self.no)
def send_next(self, job, quit=False):
self.delete_no_files(self.no+1)
ExperimentWriter.send_next(self,job,quit)
def delete_no_files(self,no):
"""
delete everything with this job number
"""
filename=os.path.join(self.spool,(self.job_pattern%no))
if os.path.isfile(filename): os.unlink(filename)
if os.path.isfile(filename+".tmp"): os.unlink(filename+".tmp")
if os.path.isfile(filename+".result"): os.unlink(filename+".result")
+78
View File
@@ -0,0 +1,78 @@
import threading
import io
import sys
import os
import os.path
import traceback
from damaris.data import Resultable
class ResultHandling(threading.Thread):
"""
runs the result script in sandbox
"""
def __init__(self, script_data, result_iterator, data_pool):
threading.Thread.__init__(self,name="result handler")
self.script=script_data
self.results=result_iterator
self.data_space=data_pool
self.quit_flag=self.results.quit_flag
if self.data_space is not None:
self.data_space["__recentresult"]=-1
def run(self):
# execute it
dataspace={}
data_classes = __import__('damaris.data', dataspace, dataspace, ['*'])
for name in dir(data_classes):
if name[:2]=="__" and name[-2:]=="__": continue
dataspace[name]=data_classes.__dict__[name]
del data_classes
dataspace["results"]=self
dataspace["data"]=self.data_space
self.raised_exception=None
self.location = None
try:
exec(self.script, dataspace)
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
self.traceback=traceback_file.getvalue()
traceback_file=None
return
if not "result" in dataspace:
dataspace=None
return
try:
dataspace["result"]()
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
traceback_file=io.StringIO()
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
self.traceback=traceback_file.getvalue()
traceback_file=None
dataspace=None
def __iter__(self):
if self.quit_flag.isSet():
self.results=None
return
for i in self.results:
if hasattr(self.results, "in_advance"):
self.data_space["__resultsinadvance"]=self.results.in_advance
if self.quit_flag.isSet():
self.results=None
return
if isinstance(i, Resultable.Resultable):
if self.data_space is not None:
self.data_space["__recentresult"]=i.job_id+0
yield i
if self.quit_flag.isSet():
self.results=None
return
def stop(self):
self.quit_flag.set()
+576
View File
@@ -0,0 +1,576 @@
# -*- coding: iso-8859-1 -*-
#############################################################################
# #
# Name: Class ResultReader #
# #
#############################################################################
import os
import os.path
import glob
import time
import sys
import base64
import numpy
try:
import xml.etree.cElementTree
ELEMENT_TREE = True
except:
import xml.parsers.expat
ELEMENT_TREE = False
import threading
from datetime import datetime
from damaris.data import ADC_Result
from damaris.data import Error_Result
from damaris.data import Temp_Result
from damaris.data import Config_Result
class ResultReader:
"""
starts at some point and returns result objects until none are there
"""
CONFIG_TYPE = 3
TEMP_TYPE = 2
ADCDATA_TYPE = 1
ERROR_TYPE = 0
def __init__(self, spool_dir=".", no=0, result_pattern="job.%09d.result", clear_jobs=False, clear_results=False):
self.spool_dir = spool_dir
self.start_no = no
self.no = self.start_no
self.result_pattern = result_pattern
self.clear_jobs=clear_jobs
self.clear_results=clear_results
self.quit_flag=threading.Event() # asychronous quit flag
def __iter__(self):
"""
get next job with iterator
"""
expected_filename=os.path.join(self.spool_dir,self.result_pattern%(self.no))
while os.access(expected_filename,os.R_OK):
yield self.get_result_object(expected_filename)
# purge result file
if self.clear_results:
if os.path.isfile(expected_filename): os.remove(expected_filename)
if self.clear_jobs:
if os.path.isfile(expected_filename[:-7]): os.remove(expected_filename[:-7])
self.no+=1
expected_filename=os.path.join(self.spool_dir,self.result_pattern%(self.no))
return
def get_result_object(self, in_filename):
"""
get result object
"""
# class-intern result-object currently being processed
retries=0
result_file=None
while result_file is None:
try:
result_file = open(in_filename, "r")
except IOError as e:
if retries>10:
raise e
print(e, "retry", retries)
time.sleep(0.05)
retries+=1
# get date of last modification
self.result_job_date = datetime.fromtimestamp(os.stat(in_filename)[8])
if ELEMENT_TREE:
self.__parseFile = self.__parseFile_cETree
else:
self.__parseFile = self.__parseFile_expat
self.__parseFile (result_file)
result_file.close()
result_file = None
r=self.result
self.result = None
return r
def __parseFile_cETree(self, in_file):
self.result = None
self.in_description_section=False
self.result_description = { }
self.result_job_number = None
# Job Date is set in __read_file()
self.__filetype = None
for elem in xml.etree.cElementTree.ElementTree(file=in_file).getiterator():
if elem.tag == 'result':
self.result_job_number = int(elem.get("job"))
pass
elif elem.tag == 'description':
if elem.text!=None:
self.result_description = {}
self.in_description_section=True
self.in_description_data=()
for an_item in elem.getchildren():
self.in_description_data = (an_item.get("key"), an_item.get("type"), an_item.text)
# make item contents to dictionary item:
k,t,v=self.in_description_data
self.in_description_data=()
if t == "None":
self.result_description[k]=None
if t == "Float":
self.result_description[k]=float(v)
elif t == "Int":
self.result_description[k]=int(v)
elif t == "Long":
self.result_description[k]=int(v)
elif t == "Complex":
self.result_description[k]=complex(v)
elif t == "Boolean":
self.result_description[k]=bool(v)
elif t == "String":
self.result_description[k]=v
else:
# Anything else will be handled as a string
# Probably "repr".
self.result_description[k]=v
elif elem.tag == 'adcdata':
self.__filetype = ResultReader.ADCDATA_TYPE
self.adc_result_trailing_chars = ""
if self.result is None:
self.result = ADC_Result()
# None: new guess for adc data encoding
# "a": ascii
# "b": base64
self.adc_data_encoding = None
self.result.set_sampling_rate(float(elem.get("rate")))
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
self.result.set_nChannels(int(elem.get("channels")))
self.result.set_description_dictionary(self.result_description.copy())
title = "ADC-Result: job-id=%d"%int(self.result_job_number)
if len(self.result_description) > 0:
for k,v in self.result_description.items():
title += ", %s=%s"%(k,v)
self.result.set_title(title)
self.result_description = None
self.adc_result_sample_counter = 0
self.adc_result_parts = [] # will contain arrays of sampled intervals, assumes same sample rate
else:
if float(elem.get("rate")) != self.result.get_sampling_rate():
print("sample rate different in ADC_Result, found %f, former value %f"%\
(float(in_attribute["rate"]),self.result.get_sampling_rate()))
new_samples = int(elem.get("samples"))
self.adc_result_sample_counter += new_samples
self.adc_result_trailing_chars = "".join(elem.text.splitlines())
tmp_string = base64.standard_b64decode(self.adc_result_trailing_chars)
self.adc_result_trailing_chars = None
tmp = numpy.fromstring(tmp_string,dtype='Int16')
tmp_string = None
self.adc_result_parts.append(tmp)
tmp = None
# we do not need this adcdata anymore, delete it
elem.clear()
elif elem.tag == 'error':
self.__filetype = ResultReader.ERROR_TYPE
self.result = Error_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
self.result.set_description_dictionary(self.result_description.copy())
self.result.set_error_message(elem.text)
elif elem.tag == 'temp':
self.__filetype = ResultReader.TEMP_TYPE
self.result = Error_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
elif elem.tag == 'conf':
self.__filetype = ResultReader.CONF_TYPE
self.result = Error_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
# xml file was traversed now prepare the data in one go
# prepare result data
if self.result is not None and \
self.__filetype == ResultReader.ADCDATA_TYPE and \
self.adc_result_sample_counter>0:
# fill the ADC_Result with collected data
# x data
self.result.x=numpy.arange(self.adc_result_sample_counter, dtype="Float64")/\
self.result.get_sampling_rate()
self.result.y = []
nChannels = self.result.get_nChannels()
# initialise the y arrays
for i in range(nChannels):
self.result.y.append(numpy.empty(self.adc_result_sample_counter, dtype='Int16'))
# remove from result stack
tmp_index = 0
while self.adc_result_parts:
tmp_part=self.adc_result_parts.pop(0)
tmp_size = int(tmp_part.size/nChannels)
for i in range(nChannels):
# split interleaved data
self.result.y[i][tmp_index:tmp_index+tmp_size] = tmp_part[i::nChannels]
self.result.y[i][tmp_index:tmp_index+tmp_size] = tmp_part[i::nChannels]
if self.result.index != []:
self.result.index.append((tmp_index, tmp_index+tmp_size-1))
else:
self.result.index = [(0,tmp_size-1)]
tmp_index += tmp_size
self.result.cont_data=True
tmp_part = None
def __parseFile_expat(self, in_file):
"Parses the given file, adding it to the result-queue"
self.result = None
self.in_description_section=False
self.result_description = { }
self.result_job_number = None
# Job Date is set in __read_file()
self.__filetype = None
# Expat XML-Parser & Binding handlers
self.xml_parser = xml.parsers.expat.ParserCreate()
self.xml_parser.StartElementHandler = self.__xmlStartTagFound
self.xml_parser.CharacterDataHandler = self.__xmlCharacterDataFound
self.xml_parser.EndElementHandler = self.__xmlEndTagFound
self.element_stack=[]
try:
# short version, but pyexpat buffers are awfully small
# self.xml_parser.ParseFile(in_file)
# read all, at least try
databuffer=in_file.read(-1)
# test wether really everything was read...
databuffer2=in_file.read(self.xml_parser.buffer_size)
if databuffer2=="":
# parse everything at once
self.xml_parser.Parse(databuffer,True)
else:
# do the first part ...
self.xml_parser.Parse(databuffer,False)
databuffer=databuffer2
# ... and again and again
while databuffer!="":
self.xml_parser.Parse(databuffer,False)
databuffer=in_file.read(-1)
self.xml_parser.Parse("",True)
except xml.parsers.expat.ExpatError as e:
print("result file %d: xml parser '%s' error at line %d, offset %d"%(self.no,
xml.parsers.expat.ErrorString(e.code),
e.lineno,
e.offset))
self.result = None
del databuffer
self.xml_parser.StartElementHandler=None
self.xml_parser.EndElementHandler=None
self.xml_parser.CharacterDataHandler=None
del self.xml_parser
# prepare result data
if self.result is not None and \
self.__filetype == ResultReader.ADCDATA_TYPE and \
self.adc_result_sample_counter>0:
# fill the ADC_Result with collected data
self.result.x=numpy.arange(self.adc_result_sample_counter, dtype="Float64")/\
self.result.get_sampling_rate()
self.result.y=[]
self.result.index=[]
for i in range(2):
self.result.y.append(numpy.empty((self.adc_result_sample_counter,), dtype="Int16"))
tmp_sample_counter=0
while self.adc_result_parts:
tmp_part=self.adc_result_parts.pop(0)
tmp_size=tmp_part.size/2
self.result.y[0][tmp_sample_counter:tmp_sample_counter+tmp_size]=tmp_part[::2]
self.result.y[1][tmp_sample_counter:tmp_sample_counter+tmp_size]=tmp_part[1::2]
self.result.index.append((tmp_sample_counter,tmp_sample_counter+tmp_size-1))
tmp_sample_counter+=tmp_size
self.result.cont_data=True
# Callback when a xml start tag is found
def __xmlStartTagFound(self, in_name, in_attribute):
# General Result-Tag
if in_name == "result":
self.result_job_number = int(in_attribute["job"])
# Job-Date is set in __read_file()
# Description
elif in_name == "description":
# old style description:
if len(in_attribute)!=0:
self.result_description = in_attribute.copy()
self.in_description_section=True
self.in_description_data=()
elif self.in_description_section and in_name == "item":
self.in_description_data=[in_attribute["key"], in_attribute["type"], ""]
# ADC_Results
elif in_name == "adcdata":
self.__filetype = ResultReader.ADCDATA_TYPE
self.adc_result_trailing_chars = ""
if self.result is None:
self.result = ADC_Result()
# None: new guess for adc data encoding
# "a": ascii
# "b": base64
self.adc_data_encoding = None
self.result.set_sampling_rate(float(in_attribute["rate"]))
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
self.result.set_description_dictionary(self.result_description.copy())
title="ADC-Result: job-id=%d"%int(self.result_job_number)
if len(self.result_description)>0:
for k,v in self.result_description.items():
title+=", %s=%s"%(k,v)
self.result.set_title(title)
self.result_description=None
self.adc_result_sample_counter = 0
self.adc_result_parts=[] # will contain arrays of sampled intervals, assumes same sample rate
else:
if float(in_attribute["rate"])!=self.result.get_sampling_rate():
print("sample rate different in ADC_Result, found %f, former value %f"%\
(float(in_attribute["rate"]),self.result.get_sampling_rate()))
new_samples=int(in_attribute["samples"])
self.adc_result_sample_counter += new_samples
# version depends on the inclusion of http://bugs.python.org/issue1137
if sys.hexversion>=0x020501f0:
# extend buffer to expected base64 size (2 channels, 2 byte)
required_buffer=int(new_samples*4/45+1)*62
if self.xml_parser.buffer_size < required_buffer:
try:
self.xml_parser.buffer_size=required_buffer
except AttributeError:
pass
# pass all chardata as one block
self.xml_parser.buffer_text = True
# do not change the contents
self.xml_parser.returns_unicode=False
# Error_Results
elif in_name == "error":
self.__filetype = ResultReader.ERROR_TYPE
self.result = Error_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
self.result.set_description_dictionary(self.result_description.copy())
# Temp_Results
elif in_name == "temp":
self.__filetype = ResultReader.TEMP_TYPE
self.result = Temp_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
# Config_Results
elif in_name == "conf":
self.__filetype = ResultReader.CONFIG_TYPE
self.result = Config_Result()
self.result.set_job_id(self.result_job_number)
self.result.set_job_date(self.result_job_date)
# maintain the stack
self.element_stack.append(in_name)
def __xmlCharacterDataFound(self, in_cdata):
if self.in_description_section and len(self.in_description_data):
self.in_description_data[2]+=in_cdata
# ADC_Result
elif self.__filetype == ResultReader.ADCDATA_TYPE and self.element_stack[-1]=="adcdata":
self.adc_result_trailing_chars+=in_cdata
# Error_Result
elif self.__filetype == ResultReader.ERROR_TYPE:
tmp_string = self.result.get_error_message()
if tmp_string is None: tmp_string = ""
tmp_string += in_cdata
self.result.set_error_message(tmp_string)
# Temp_Results
elif self.__filetype == ResultReader.TEMP_TYPE:
pass
# Config_Results
elif self.__filetype == ResultReader.CONFIG_TYPE:
pass
def __xmlEndTagFound(self, in_name):
# maintain the stack
self.element_stack.pop()
if in_name == "adcdata":
# ADC_Result
if self.__filetype == ResultReader.ADCDATA_TYPE:
# detect type of data encoding from first line
if self.adc_data_encoding is None:
self.adc_result_trailing_chars=self.adc_result_trailing_chars.strip()
first_line_end=self.adc_result_trailing_chars.find("\n")
first_line=""
if first_line_end!=-1:
first_line=self.adc_result_trailing_chars[:first_line_end]
else:
first_line=self.adc_result_trailing_chars
if len(first_line.lstrip("-0123456789 \t\n\r"))==0:
try:
list(map(int,list(filter(len,first_line.split()))))
except ValueError as e:
pass
else:
self.adc_data_encoding="a"
if self.adc_data_encoding is None and len(first_line)%4==0:
try:
base64.standard_b64decode(first_line)
except TypeError:
pass
else:
self.adc_data_encoding="b"
if self.adc_data_encoding is None:
print("unknown ADC data format \"%s\""%first_line)
tmp=None
if self.adc_data_encoding=="a":
values=list(map(int,self.adc_result_trailing_chars.split()))
tmp=numpy.array(values, dtype="Int16")
elif self.adc_data_encoding=="b":
tmp_string=base64.standard_b64decode(self.adc_result_trailing_chars)
tmp=numpy.fromstring(tmp_string, dtype="Int16")
del tmp_string
else:
print("unknown ADC data format")
self.adc_result_trailing_chars=""
self.adc_result_parts.append(tmp)
del tmp
return
elif in_name == "description":
self.in_description_section=False
elif self.in_description_section and in_name == "item":
# make item contents to dictionary item:
k,t,v=self.in_description_data
self.in_description_data=()
if t == "None":
self.result_description[k]=None
if t == "Float":
self.result_description[k]=float(v)
elif t == "Int":
self.result_description[k]=int(v)
elif t == "Long":
self.result_description[k]=int(v)
elif t == "Complex":
self.result_description[k]=complex(v)
elif t == "Boolean":
self.result_description[k]=bool(v)
elif t == "String":
self.result_description[k]=v
else:
# Anything else will be handled as a string
# Probably "repr".
self.result_description[k]=v
elif in_name == "result":
pass
# Error_Result
elif self.__filetype == ResultReader.ERROR_TYPE:
pass
# Temp_Result
elif self.__filetype == ResultReader.TEMP_TYPE:
pass
# Config_Result
elif self.__filetype == ResultReader.CONFIG_TYPE:
pass
class BlockingResultReader(ResultReader):
"""
to follow an active result stream
"""
def __init__(self, spool_dir=".", no=0, result_pattern="job.%09d.result", clear_jobs=False, clear_results=False):
ResultReader.__init__(self, spool_dir, no, result_pattern, clear_jobs=clear_jobs, clear_results=clear_results)
self.stop_no=None # end of job queue
self.poll_time=0.1 # sleep interval for polling results, <0 means no polling and stop
self.in_advance=0
def __iter__(self):
"""
get next job with iterator
block until result is available
"""
expected_filename=os.path.join(self.spool_dir,self.result_pattern%(self.no))
while (not self.quit_flag.isSet()) and (self.stop_no is None or self.stop_no>self.no):
if not os.access(expected_filename,os.R_OK):
# stop polling, if required
if self.poll_time<0: break
self.quit_flag.wait(self.poll_time)
continue
# find pending results
self.in_advance=max(self.no,self.in_advance)
in_advance_filename=os.path.join(self.spool_dir,self.result_pattern%(self.in_advance+1))
while os.access(in_advance_filename, os.R_OK) and (self.stop_no is None or self.stop_no>self.in_advance+1):
# do not more than 100 results in advance at one glance
if self.in_advance>self.no+100: break
self.in_advance+=1
in_advance_filename=os.path.join(self.spool_dir,self.result_pattern%(self.in_advance+1))
if self.quit_flag.isSet(): break
r=self.get_result_object(expected_filename)
if self.quit_flag.isSet(): break
if self.quit_flag.isSet(): break
yield r
if self.clear_results:
if os.path.isfile(expected_filename): os.remove(expected_filename)
if self.clear_jobs:
if os.path.isfile(expected_filename[:-7]): os.remove(expected_filename[:-7])
self.no+=1
expected_filename=os.path.join(self.spool_dir,self.result_pattern%(self.no))
return
def quit(self):
self.quit_flag.set()
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
<glade-project>
<name>damaris-gui</name>
<program_name>damaris</program_name>
<gnome_support>FALSE</gnome_support>
</glade-project>
+2186
View File
File diff suppressed because it is too large Load Diff
+210
View File
@@ -0,0 +1,210 @@
<?xml version="1.0"?>
<!--
This syntax-file was generated by sourceview2codebuffer.xsl from
GtkSourceView's Python-syntax-file!
This transformation is not perfect so it may need some hand-word to fix
minor issues in this file.
You can get sourceview2codebuffer.xsl from http://pygtkcodebuffer.googlecode.com/.
-->
<syntax>
<string style="string" escape="\"><starts>([uUrR]|[uU][rR]|[rR][uU])?"""</starts><ends>"""</ends></string>
<string style="string" escape="\"><starts>([uUrR]|[uU][rR]|[rR][uU])?'''</starts><ends>'''</ends></string>
<string style="string" escape="\"><starts>([uUrR]|[uU][rR]|[rR][uU])?"</starts><ends>"</ends></string>
<string style="string" escape="\"><starts>([uUrR]|[uU][rR]|[rR][uU])?'</starts><ends>'</ends></string>
<keywordlist style="preprocessor">
<keyword>import</keyword>
<keyword>from</keyword>
<keyword>as</keyword>
<keyword>False</keyword>
<keyword>None</keyword>
<keyword>True</keyword>
<keyword>__name__</keyword>
<keyword>__debug__</keyword>
</keywordlist>
<keywordlist style="keyword">
<keyword>def</keyword>
<keyword>class</keyword>
<keyword>return</keyword>
</keywordlist>
<keywordlist style="keyword">
<keyword>and</keyword>
<keyword>assert</keyword>
<keyword>break</keyword>
<keyword>continue</keyword>
<keyword>del</keyword>
<keyword>elif</keyword>
<keyword>else</keyword>
<keyword>except</keyword>
<keyword>exec</keyword>
<keyword>finally</keyword>
<keyword>for</keyword>
<keyword>global</keyword>
<keyword>if</keyword>
<keyword>in</keyword>
<keyword>is</keyword>
<keyword>lambda</keyword>
<keyword>not</keyword>
<keyword>or</keyword>
<keyword>pass</keyword>
<keyword>print</keyword>
<keyword>raise</keyword>
<keyword>try</keyword>
<keyword>while</keyword>
<keyword>yield</keyword>
</keywordlist>
<keywordlist style="special">
<keyword>ArithmeticError</keyword>
<keyword>AssertionError</keyword>
<keyword>AttributeError</keyword>
<keyword>EnvironmentError</keyword>
<keyword>EOFError</keyword>
<keyword>Exception</keyword>
<keyword>FloatingPointError</keyword>
<keyword>ImportError</keyword>
<keyword>IndentationError</keyword>
<keyword>IndexError</keyword>
<keyword>IOError</keyword>
<keyword>KeyboardInterrupt</keyword>
<keyword>KeyError</keyword>
<keyword>LookupError</keyword>
<keyword>MemoryError</keyword>
<keyword>NameError</keyword>
<keyword>NotImplementedError</keyword>
<keyword>OSError</keyword>
<keyword>OverflowError</keyword>
<keyword>ReferenceError</keyword>
<keyword>RuntimeError</keyword>
<keyword>StandardError</keyword>
<keyword>StopIteration</keyword>
<keyword>SyntaxError</keyword>
<keyword>SystemError</keyword>
<keyword>SystemExit</keyword>
<keyword>TabError</keyword>
<keyword>TypeError</keyword>
<keyword>UnboundLocalError</keyword>
<keyword>UnicodeDecodeError</keyword>
<keyword>UnicodeEncodeError</keyword>
<keyword>UnicodeError</keyword>
<keyword>UnicodeTranslateError</keyword>
<keyword>ValueError</keyword>
<keyword>WindowsError</keyword>
<keyword>ZeroDivisionError</keyword>
<keyword>Warning</keyword>
<keyword>UserWarning</keyword>
<keyword>DeprecationWarning</keyword>
<keyword>PendingDeprecationWarning</keyword>
<keyword>SyntaxWarning</keyword>
<keyword>OverflowWarning</keyword>
<keyword>RuntimeWarning</keyword>
<keyword>FutureWarning</keyword>
<keyword>__import__</keyword>
<keyword>abs</keyword>
<keyword>apply</keyword>
<keyword>basestring</keyword>
<keyword>bool</keyword>
<keyword>buffer</keyword>
<keyword>callable</keyword>
<keyword>chr</keyword>
<keyword>classmethod</keyword>
<keyword>cmp</keyword>
<keyword>coerce</keyword>
<keyword>compile</keyword>
<keyword>complex</keyword>
<keyword>delattr</keyword>
<keyword>dict</keyword>
<keyword>dir</keyword>
<keyword>divmod</keyword>
<keyword>enumerate</keyword>
<keyword>eval</keyword>
<keyword>execfile</keyword>
<keyword>file</keyword>
<keyword>filter</keyword>
<keyword>float</keyword>
<keyword>getattr</keyword>
<keyword>globals</keyword>
<keyword>hasattr</keyword>
<keyword>hash</keyword>
<keyword>hex</keyword>
<keyword>id</keyword>
<keyword>input</keyword>
<keyword>int</keyword>
<keyword>intern</keyword>
<keyword>isinstance</keyword>
<keyword>issubclass</keyword>
<keyword>iter</keyword>
<keyword>len</keyword>
<keyword>list</keyword>
<keyword>locals</keyword>
<keyword>long</keyword>
<keyword>map</keyword>
<keyword>max</keyword>
<keyword>min</keyword>
<keyword>object</keyword>
<keyword>oct</keyword>
<keyword>open</keyword>
<keyword>ord</keyword>
<keyword>pow</keyword>
<keyword>property</keyword>
<keyword>range</keyword>
<keyword>raw_input</keyword>
<keyword>reduce</keyword>
<keyword>reload</keyword>
<keyword>repr</keyword>
<keyword>round</keyword>
<keyword>setattr</keyword>
<keyword>slice</keyword>
<keyword>staticmethod</keyword>
<keyword>str</keyword>
<keyword>sum</keyword>
<keyword>super</keyword>
<keyword>tuple</keyword>
<keyword>type</keyword>
<keyword>unichr</keyword>
<keyword>unicode</keyword>
<keyword>vars</keyword>
<keyword>xrange</keyword>
<keyword>zip</keyword>
</keywordlist>
<!-- Some Experiment keywords -->
<keywordlist style="special">
<keyword>set_pfg</keyword>
<keyword>set_pfg_wt</keyword>
<keyword>set_description</keyword>
<keyword>get_description</keyword>
<keyword>set_phase</keyword>
<keyword>set_frequency</keyword>
<keyword>ttl_pulse</keyword>
<keyword>rf_pulse</keyword>
<keyword>state_start</keyword>
<keyword>state_end</keyword>
<keyword>loop_start</keyword>
<keyword>loop_end</keyword>
<keyword>set_pts_local</keyword>
<keyword>wait</keyword>
<keyword>record</keyword>
</keywordlist>
<keywordlist style="datatype">
<keyword>Accumulation</keyword>
<keyword>Experiment</keyword>
<keyword>ADC_Result</keyword>
<keyword>MeasurementResult</keyword>
<keyword>AccumulatedValue</keyword>
</keywordlist>
<pattern style="comment">#.*$</pattern>
<pattern style="datatype">\bself\b</pattern>
<pattern style="number">\b([1-9][0-9]*|0)([Uu]([Ll]|LL|ll)?|([Ll]|LL|ll)[Uu]?)?\b</pattern>
<pattern style="number">\b([0-9]+[Ee][-]?[0-9]+|([0-9]*\.[0-9]+|[0-9]+\.)([Ee][-]?[0-9]+)?)[fFlL]?</pattern>
<pattern style="number">\b0[0-7]+([Uu]([Ll]|LL|ll)?|([Ll]|LL|ll)[Uu]?)?\b</pattern>
<pattern style="number">\b0[xX][0-9a-fA-F]+([Uu]([Ll]|LL|ll)?|([Ll]|LL|ll)[Uu]?)?\b</pattern>
</syntax>
+152
View File
@@ -0,0 +1,152 @@
#! /usr/bin/env python
import time
import sys
import os
import os.path
import tables
import damaris.data.DataPool as DataPool
import damaris.gui.ResultReader as ResultReader
import damaris.gui.ExperimentWriter as ExperimentWriter
import damaris.gui.BackendDriver as BackendDriver
import damaris.gui.ResultHandling as ResultHandling
import damaris.gui.ExperimentHandling as ExperimentHandling
def some_listener(event):
if event.subject=="__recentexperiment" or event.subject=="__recentresult":
r=event.origin.get("__recentresult",-1)+1
e=event.origin.get("__recentexperiment",-1)+1
if e!=0:
ratio=100.0*r/e
else:
ratio=100.0
print("\r%d/%d (%.0f%%)"%(r,e,ratio), end=' ')
class ScriptInterface:
def __init__(self, exp_script=None, res_script=None, backend_executable=None, spool_dir="spool"):
self.exp_script=exp_script
self.res_script=res_script
self.backend_executable=backend_executable
self.spool_dir=os.path.abspath(spool_dir)
self.exp_handling=self.res_handling=None
self.exp_writer=self.res_reader=self.back_driver=None
if self.backend_executable is not None:
self.back_driver=BackendDriver.BackendDriver(self.backend_executable, spool_dir)
if self.exp_script: self.exp_writer=self.back_driver.get_exp_writer()
if self.res_script: self.res_reader=self.back_driver.get_res_reader()
else:
self.back_driver=None
if self.exp_script: self.exp_writer=ExperimentWriter.ExperimentWriter(spool_dir)
if self.res_script: self.res_reader=ResultReader.ResultReader(spool_dir)
self.data=DataPool()
def runScripts(self):
# get script engines
if self.exp_script and self.exp_writer:
self.exp_handling=ExperimentHandling.ExperimentHandling(self.exp_script, self.exp_writer, self.data)
if self.res_script and self.res_reader:
self.res_handling=ResultHandling.ResultHandling(self.res_script, self.res_reader, self.data)
# start them
if self.exp_handling: self.exp_handling.start()
if self.back_driver is not None: self.back_driver.start()
if self.res_handling: self.res_handling.start()
def waitForScriptsEnding(self):
# time of last dump
dump_interval=600
next_dump_time=time.time()+dump_interval
# keyboard interrupts are handled in extra cleanup loop
try:
while [_f for _f in [self.exp_handling,self.res_handling,self.back_driver] if _f]:
time.sleep(0.1)
if time.time()>next_dump_time:
self.dump_data("pool/data_pool.h5")
next_dump_time+=dump_interval
if self.exp_handling is not None:
if not self.exp_handling.isAlive():
self.exp_handling.join()
if self.exp_handling.raised_exception:
print(": experiment script failed at line %d (function %s): %s"%(self.exp_handling.location[0],
self.exp_handling.location[1],
self.exp_handling.raised_exception))
else:
print(": experiment script finished")
self.exp_handling = None
if self.res_handling is not None:
if not self.res_handling.isAlive():
self.res_handling.join()
if self.res_handling.raised_exception:
print(": result script failed at line %d (function %s): %s"%(self.res_handling.location[0],
self.res_handling.location[1],
self.res_handling.raised_exception))
else:
print(": result script finished")
self.res_handling = None
if self.back_driver is not None:
if not self.back_driver.isAlive():
print(": backend finished")
self.back_driver=None
except KeyboardInterrupt:
still_running=[_f for _f in [self.exp_handling,self.res_handling,self.back_driver] if _f]
for r in still_running:
r.quit_flag.set()
for r in still_running:
r.join()
def dump_data(self, filename):
try:
# write data from pool
dump_file=tables.open_file(filename,mode="w",title="DAMARIS experiment data")
self.data.write_hdf5(dump_file, complib='zlib', complevel=6)
# write scripts
scriptgroup=dump_file.create_group("/","scripts","Used Scripts")
dump_file.create_array(scriptgroup,"experiment_script", self.exp_script)
dump_file.create_array(scriptgroup,"result_script", self.res_script)
dump_file.create_array(scriptgroup,"backend_executable", self.backend_executable)
dump_file.create_array(scriptgroup,"spool_directory", self.spool_dir)
dump_file.flush()
dump_file.close()
dump_file=None
# todo
except Exception as e:
print("dump failed", e)
if __name__=="__main__":
if len(sys.argv)==1:
print("%s: data_handling_script [spool directory]"%sys.argv[0])
sys.exit(1)
if len(sys.argv)==3:
spool_dir=os.getcwd()
else:
spool_dir=sys.argv[3]
expscriptfile=open(sys.argv[1])
expscript=expscriptfile.read()
resscriptfile=open(sys.argv[2])
resscript=resscriptfile.read()
si=ScriptInterface(expscript, resscript,"/usr/lib/damaris/backends/Mobilecore", spool_dir)
si.data.register_listener(some_listener)
si.runScripts()
si.waitForScriptsEnding()
si.dump_data("data_pool.h5")
si=None
View File
+120
View File
@@ -0,0 +1,120 @@
import serial
import re
import operator
from functools import reduce
DEBUG=False
reply_pattern = re.compile(r"\x02..(.*)\x03.", re.DOTALL)
# example answer '\x02PV279.8\x03/'
# [EOT] = \x04
# [STX] = \x02
# [ENQ] = \x05
# [ETX] = \x03
# [ACK] = \x06
# BCC = checksum
standard_device='0011'
EOT = '\x04'
STX = '\x02'
ENQ = '\x05'
ETX = '\x03'
ACK = '\x06'
NAK = '\x15'
"""
Paramter read example:
Master: [EOT]0011PV[ENQ]
Instrument: [STX]PV16.4[ETX]{BCC}
Writing data:
Master: [EOT] {GID}{GID}{UID}{UID}[STX]{CHAN}(c1)(c2)<DATA>[ETX](BCC)
"""
def checksum(message):
bcc = (reduce(operator.xor, list(map(ord,message))))
return chr(bcc)
class Eurotherm(object):
def __init__(self, serial_device, baudrate = 19200):
self.device = standard_device
# timeout: 110 ms to get all answers.
self.s = serial.Serial(serial_device,
baudrate = baudrate,
bytesize=7,
parity='E',
stopbits=1,
timeout=0.11)
self._expect_len = 50
def send_read_param(self, param):
self.s.write(EOT + self.device + param + ENQ)
def read_param(self, param):
self.s.flushInput()
self.send_read_param(param)
answer = self.s.read(self._expect_len)
m = reply_pattern.search(answer)
if m is None:
# Reading _expect_len bytes was not enough...
answer += self.s.read(200)
m = reply_pattern.search(answer)
if m is not None:
self._expect_len = len(answer)
return m.group(1)
else:
print("received:", repr(answer))
return None
def write_param(self, mnemonic, data):
if len(mnemonic) > 2:
raise ValueError
bcc = checksum(mnemonic + data + ETX)
mes = EOT+self.device+STX+mnemonic+data+ETX+bcc
if DEBUG:
for i in mes:
print(i,hex(ord(i)))
self.s.flushInput()
self.s.write(mes)
answer = self.s.read(1)
# print "received:", repr(answer)
if answer == "":
# raise IOError("No answer from device")
return None
return answer[-1] == ACK
def get_current_temperature(self):
temp = self.read_param('PV')
if temp is None:
temp = "0"
return temp
def set_temperature(self, temperature):
return self.write_param('SL', str(temperature))
def get_setpoint_temperature(self):
return self.read_param('SL')
if __name__ == '__main__':
import time
delta=5
date = time.strftime('%Y-%m-%d')
f = open('templog_%s'%date,'w')
f.write('# Start time: %s\n#delta t : %.1f s\n'%(time.asctime(), delta))
et = Eurotherm("/dev/ttyUSB0")
while True:
for i in range(120):
time.sleep(delta)
#t = time.strftime()
T = et.get_current_temperature()
l = '%f %s\n'%(time.time(),T)
print(time.asctime(), T)
f.write(l)
f.flush()
f.write('# MARK -- %s --\n'%(time.asctime()))
+246
View File
@@ -0,0 +1,246 @@
#!/usr/bin/env python2
# -*- encoding: utf-8 -*-
import optparse
import os, sys
import serial
import numpy as N
import struct
import binascii
import time
DEBUG = False
crc_test = "\x0d\x01\x00\x62\x00\x33"
crc_expected = 0xddd
def crc(message):
"""
(from "Modbus_over_serial_line_V1_02.pdf" at http://www.modbus.org)
6.2.2 CRC Generation
====================
The Cyclical Redundancy Checking (CRC) field is two bytes, containing a 16bit binary value. The CRC value is calculated by the
transmitting device, which appends the CRC to the message. The device that receives recalculates a CRC during receipt of the
message, and compares the calculated value to the actual value it received in the CRC field. If the two values are not equal, an error
results.
The CRC is started by first preloading a 16bit register to all 1s. Then a process begins of applying successive 8bit bytes of the
message to the current contents of the register. Only the eight bits of data in each character are used for generating the CRC. Start
and stop bits and the parity bit, do not apply to the CRC.
During generation of the CRC, each 8bit character is exclusive ORed with the register contents. Then the result is shifted in the
direction of the least significant bit (LSB), with a zero filled into the most significant bit (MSB) position. The LSB is extracted and
examined. If the LSB was a 1, the register is then exclusive ORed with a preset, fixed value. If the LSB was a 0, no exclusive OR takes
place.
This process is repeated until eight shifts have been performed. After the last (eighth) shift, the next 8bit character is exclusive ORed
with the registers current value, and the process repeats for eight more shifts as described above. The final content of the register,
after all the characters of the message have been applied, is the CRC value.
A procedure for generating a CRC is:
1. Load a 16bit register with FFFF hex (all 1s). Call this the CRC register.
2. Exclusive OR the first 8bit byte of the message with the loworder byte of the 16bit CRC register, putting the result in the
CRC register.
3. Shift the CRC register one bit to the right (toward the LSB), zerofilling the MSB. Extract and examine the LSB.
4. (If the LSB was 0): Repeat Step 3 (another shift).
(If the LSB was 1): Exclusive OR the CRC register with the polynomial value 0xA001 (1010 0000 0000 0001).
5. Repeat Steps 3 and 4 until 8 shifts have been performed. When this is done, a complete 8bit byte will have been
processed.
6. Repeat Steps 2 through 5 for the next 8bit byte of the message. Continue doing this until all bytes have been processed.
7. The final content of the CRC register is the CRC value.
8. When the CRC is placed into the message, its upper and lower bytes must be swapped as described below.
Placing the CRC into the Message
When the 16bit CRC (two 8bit bytes) is transmitted in the message, the low-order byte will be transmitted first, followed by the high-
order byte.
"""
crc = 0xffff # step 1
for byte in message:
if type(byte) == type(str):
crc ^= ord(byte) # step 2: xor with lower byte of crc
else:
crc ^= byte # step 2: xor with lower byte of crc
for i in range(8): # step 5: repeat 2-4 until 8 shifts were performed
if (crc & 0x1) == 0: # step 4: check LSB
mask = 0x0
else:
mask = 0xa001
crc >>= 1 # step 3: shift one bit to the right
crc ^= mask
if DEBUG: print("Message:", [i for i in message], " CRC:", hex(crc))
return chr(crc & 0xff) + chr(
(crc >> 8) & 0xff) # return lower byte, upper byte (shift to the right, return last byte) i
class Flow:
def __init__(self, device="/dev/ttyUSB0"):
self.s = serial.Serial(port=device, timeout=0.02)
self.s.readline()
self.s.timeout = 0.2
self.address = bytearray([0x3])
self.rcommands = {
"flow": [bytearray([0x3, 0x0, 0x0, 0x0, 0x2]), ">f"],
"temperature": [bytearray([0x3, 0x0, 0x2, 0x0, 0x2]), ">f"],
"setflow": [bytearray([0x3, 0x0, 0x6, 0x0, 0x2]), ">f"],
"total": [bytearray([0x3, 0x0, 0x8, 0x0, 0x2]), ">f"],
"alarm": [bytearray([0x3, 0x0, 0xc, 0x0, 0x1]), ">H"],
"hwerror": [bytearray([0x3, 0x0, 0xd, 0x0, 0x1]), ">H"],
"unit_flow": [bytearray([0x3, 0x0, 0x16, 0x0, 0x4]), ">8s"],
"medium": [bytearray([0x3, 0x00, 0x1a, 0x0, 0x4]), ">8s"],
"device": [bytearray([0x3, 0x00, 0x23, 0x0, 0x4]), ">8s"],
"unit_total": [bytearray([0x3, 0x40, 0x48, 0x0, 0x4]), ">8s"],
}
self.wcommands = {
"flow": [bytearray([0x06, 0x0, 0x6]), ">f"]
}
def send_data(self, data_and_type):
"""
send data to the device at self.address.
"""
data, type = data_and_type
if DEBUG: print("send", data_and_type, "data:", binascii.hexlify(data), "type:", type, self.address)
msg = self.address + data
msg_crc = msg + crc(msg)
if DEBUG: print("send:", repr(str(msg_crc)))
nbytes_sent = self.s.write(str(msg_crc))
if DEBUG: print("send:", self.s)
if DEBUG: print("Sent bytes:", nbytes_sent)
time.sleep(0.1) # this is necessary, otherwise no data can be received
return msg_crc
def recv_data(self):
"""
Reading Data
============
Function: 0x3
Format of the resulting string
Addr Func NoBytes Byte1...N CRCHi CRCLo
"""
message = bytearray(self.s.read(3))
if DEBUG: print("recv_data: header:", binascii.hexlify(message))
if message == "":
return
addr, func, nbytes = message
if func == 0x03:
data = self.s.read(nbytes)
if DEBUG: print("recv_data: data: %i %s" % (nbytes, binascii.hexlify(data)))
message += data
crc_data = self.s.read(2)
if DEBUG: print("recv_data: crc", repr(str(crc_data)))
if crc(message) != crc_data:
print("mismatch", crc(message), crc_data)
return data
def get_var(self, named_type="flow"):
"""
get variable from device, variables are definde in self.rcommands as list [bytearray[hiAd, loAd, numHi, numLo], type]
"""
cmd,fmt = self.rcommands[named_type]
if DEBUG: print("get_var", cmd,fmt)
self.send_data([cmd,fmt])
d = self.recv_data()
if d:
d = struct.unpack(fmt, d)[0]
return d
# msg = self.recv_data()
# print str(msg)
# float struct.unpack('>f', "4byte string")
# ushort struct.unpack('>H', "2byte string")
def current_flow(self):
# start register 0x0000
# 2 bytes
cmd = self.address + bytearray([0x3, 0x0, 0x0, 0x0, 0x2])
return cmd + crc(cmd)
def current_temperature(self):
# start register 0x0002
#
#
cmd = self.address + bytearray([0x3, 0x0, 0x2, 0x0, 0x2])
return cmd + crc(cmd)
def set_flow(self, flow):
"""
write multiple registers: 0x10
startHi
startLo
numregHi
numregLo
numbytes
"""
_flow = struct.pack(">f", flow)
# cmd = address + bytearray([0x10,0x0,0x6, 0x0, 0x2, 0x2]) + _flow
cmd1 = self.address + bytearray([0x06, 0x0, 0x6]) + _flow
self.s.write(cmd1 + crc(cmd1))
time.sleep(0.01)
# print self.s.readline()
# self.recv_data()
def cmd(self, data):
cmds = self.address + data
for i in cmds:
print(hex(ord(i)), end=' ')
return cmds + crc(cmds)
if __name__ == "__main__":
"""
This is the command line program to control the red-y flow control series.
Usage: ./flow_control.py [FLOW]
Several parameters are printed on every execution
The default serial device is /dev/ttyUSB0. If you want to change that create a
file called "~/.flow" with the first line containing an alternate path, i.e. /dev/ttyUSB1
If you want to enable debug output edit the script and set DEBUG=True.
Sample output:
user@pfg # flow
Device: GSCC9SA
Medium: N2B
Current Flow: 0.00
Current Set Flow: 0.00
Flow Unit: ln/min
Total Gas: 0.00
Total Unit: lnF
Current Temperature: 22.38 C
Alarm: 0
HW Error: 0
"""
conf = os.path.expanduser('~/.flow')
if os.path.exists(conf):
dev = open(conf).readlines()[0].strip()
redy = Flow(device=dev)
print("Device: %6s" % (redy.get_var("device")))
print("Medium: %6s" % (redy.get_var("medium")))
print("Current Flow: %6.2f" % (redy.get_var("flow")))
print("Current Set Flow: %6.2f" % (redy.get_var("setflow")))
print("Flow Unit: %6s" % (redy.get_var("unit_flow")))
print("Total Gas: %6.2f" % (redy.get_var("total")))
print("Total Unit: %6s" % (redy.get_var("unit_total")))
print("Current Temperature: %6.2f C" % (redy.get_var("temperature")))
print("Alarm: %8i" % (redy.get_var("alarm")))
print("HW Error: %8i" % (redy.get_var("hwerror")))
if len(sys.argv) > 1:
try:
float(sys.argv[1])
except:
raise SyntaxError("usage: %s <flow> # to set flow or empty to get current flow\nfirst line in ~/.flow defines serial tty, default: /dev/ttyUSB0" % (sys.argv[0]))
print("Set flow: %6.2f"%(float(sys.argv[1])))
redy.set_flow(float(sys.argv[1]))
+125
View File
@@ -0,0 +1,125 @@
__author__ = 'Markus Rosenstihl <markus.rosenstihl@physik.tu-darmstadt.de>'
import re
import serial
import time
import logging
import os
logfile = os.path.expanduser("~/.goniometer.log")
logging.basicConfig(filename=logfile,level=logging.INFO)
logger = logging.getLogger()
logger.name = "goniometer"
class goniometer:
"""
This class is meant to control the goniometer probe head from Marco Braun (AXYs)
"""
def __init__(self, dev="/dev/ttyUSB0",
baudrate=38400,
timeout=1,
return_to_origin=True,
loglevel=2,
logangle=False):
"""
Upon creating an instance the goniometer will be initialised and the current angle defined as being 0.
The goniometer log can be found in `~/.goniometer.log`.
:param return_to_origin: Set to true if you want the stepper to return to the beginning when
the class instance is deleted (for example with "del" ). Default: True
:type return_to_origin: bool
:param loglevel: This sets the log level 0=DEBUG,1=INFO,2=WARNING. Default: 2
:type loglevel: int
:param logangle: Should a history of the angles be kept? The log can be accessed in the `_angle_history`
variable. Default: False
:type logangle: bool
"""
self.serial = serial.Serial(port=dev, baudrate=baudrate, timeout=timeout)
logger.setLevel([logging.DEBUG,logging.INFO,logging.WARNING][loglevel])
self.serial.readlines() # empty serial buffer
self.current_angle = 0.0
self._angle_history = []
self.initialise()
self.return_to_origin = return_to_origin
self.logangel = logangle
def __del__(self):
if self.return_to_origin:
logging.info("Return to origin")
self._send("g1")
self._wait()
self.serial.close()
def _send(self, string_to_send):
formatted_string = "%s>\r\n"%string_to_send
logging.debug("send: %s"%(repr(formatted_string)))
self.serial.write(formatted_string)
def _recv(self):
retstr = self.serial.readline().strip().replace("\x00","")
search_result = re.search("<(\d+)>", retstr)
if search_result != None:
angle = float(search_result.group(1))/36.0
self.current_angle = angle
if self.logangle:
self._angle_history.append(angle)
logging.debug("%.2f"%angle)
return angle
else:
return None
def _wait(self):
logging.debug("Wait until rotation finnished (current: %.2f)"%(self.current_angle))
time.sleep(4)
while True:
retstr = self._recv()
if not retstr:
logging.debug("Rotation finnished (current: %.2f)"%(self.current_angle))
break
def initialise(self):
logging.info("Initialize goniometer")
self._send("R000")
self.current_angle = 0.0
def stop(self):
"""
Stops the current movement, not possible while doing :func:`step` and :func:`angle`
"""
logging.info("Stop goniometer")
self._send("S456")
def step(self, sample_rotation_degree=1.0):
"""
How many degrees should we step further. Negative values are not allowed, the stepper would return to 0.
A ValueError exception is raised in this case.
:param sample_rotation_degree: How many degrees to rotate. Default: 1
:type sample_rotation_degree: float
"""
if sample_rotation_degree <= 0:
raise ValueError("This does not what you expect, would move to the start position whatever the value")
logging.info("Step goniometer: %.2f"%sample_rotation_degree)
direction = "g" if (sample_rotation_degree < 0) else "G"
sample_rotation_degree = -1*sample_rotation_degree if sample_rotation_degree < 0 else sample_rotation_degree
steps = int(sample_rotation_degree%360)
self._send("%s%i"%(direction, steps ))
self._wait()
def angle(self, angle_degree):
"""
To what angle, in degrees, should we rotate. Mod 360 is calculated for `angle_degree`.
If angle is smaller than current angle an exception will be raised.
(From the underlying :func:`step` function)
TODO: rotate either back or over to the wanted position.
:param angle_degree: position in degree
:type angle_degree: float
"""
angle_degree %= 360
delta_degree = angle_degree - self.current_angle
logging.info("Rotate tp %.2f (from %.2f, i.e. delta=%.2f)"%(angle_degree, self.current_angle, delta_degree))
self.step(sample_rotation_degree=delta_degree)
+127
View File
@@ -0,0 +1,127 @@
import numpy as N
import sys
if sys.version_info > (2,6,0):
import numbers
else:
pass
if sys.version_info > (2,6,0):
def lin_range(start,stop,step):
if isinstance(step, numbers.Integral):
return N.linspace(start,stop,step)
else:
return N.arange(start,stop,step)
else:
def lin_range(start,stop,step):
return N.arange(start,stop,step)
def log_range(start, stop, stepno):
if (start<=0 or stop<=0 or stepno<1):
raise ValueError("start, stop must be positive and stepno must be >=1")
return N.logspace(N.log10(start),N.log10(stop), num=stepno)
def staggered_range(some_range, size=3):
m=0
if isinstance(some_range, N.ndarray):
is_numpy = True
some_range = list(some_range)
else:
is_numpy = False
new_list=[]
for k in range(len(some_range)):
for i in range(size):
try:
index = (m*size)
new_list.append(some_range.pop(index))
except IndexError:
break
m+=1
if is_numpy:
new_list = N.asarray(new_list+some_range)
else:
new_list+=some_range
return new_list
def combine_ranges(*ranges):
new_list = []
for r in ranges:
new_list.extend(r)
return new_list
combined_ranges=combine_ranges
def interleaved_range(some_list, left_out):
"""
in first run, do every n-th, then do n-1-th of the remaining values and so on...
"""
m=0
new_list = []
for j in range(left_out):
for i in range(len(some_list)):
if (i*left_out+m) < len(some_list):
new_list.append(some_list[i*left_out+m])
else:
m+=1
break
if isinstance(some_list, N.ndarray):
new_list = N.array(new_list)
return new_list
# These are the generators
def lin_range_iter(start,stop, step):
this_one=float(start)+0.0
if step>0:
while (this_one<=float(stop)):
yield this_one
this_one+=float(step)
else:
while (this_one>=float(stop)):
yield this_one
this_one+=float(step)
def log_range_iter(start, stop, stepno):
if (start<=0 or stop<=0 or stepno<1):
raise ValueError("start, stop must be positive and stepno must be >=1")
if int(stepno)==1:
factor=1.0
else:
factor=(stop/start)**(1.0/int(stepno-1))
for i in range(int(stepno)):
yield start*(factor**i)
def staggered_range_iter(some_range, size = 1):
"""
size=1: do one, drop one, ....
size=n: do 1 ... n, drop n+1 ... 2*n
in a second run the dropped values were done
"""
left_out=[]
try:
while True:
for i in range(size):
yield next(some_range)
for i in range(size):
left_out.append(next(some_range))
except StopIteration:
pass
# now do the droped ones
for i in left_out:
yield i
def combined_ranges_iter(*ranges):
"""
iterate over one range after the other
"""
for r in ranges:
for i in r:
yield i
combine_ranges_iter=combined_ranges_iter
+30
View File
@@ -0,0 +1,30 @@
import math
__all__ = ['rotate_signal']
def rotate_signal(timesignal, angle):
"Rotate <timesignal> by <angle> degrees"
# implicit change to float arrays!
if timesignal.get_number_of_channels()!=2:
raise Exception("rotation defined only for 2 channels")
# simple case 0, 90, 180, 270 degree
reduced_angle=divmod(angle, 90)
if abs(reduced_angle[1])<1e-6:
reduced_angle=reduced_angle[0]%4
if reduced_angle==0:
return
elif reduced_angle==1:
timesignal.y[1]*=-1
timesignal.y=[timesignal.y[1],timesignal.y[0]]
elif reduced_angle==2:
timesignal.y[0]*=-1
timesignal.y[1]*=-1
elif reduced_angle==3:
timesignal.y[0]*=-1
timesignal.y=[timesignal.y[1],timesignal.y[0]]
else:
sin_angle=math.sin(angle/180.0*math.pi)
cos_angle=math.cos(angle/180.0*math.pi)
timesignal.y=[cos_angle*timesignal.y[0]-sin_angle*timesignal.y[1],
sin_angle*timesignal.y[0]+cos_angle*timesignal.y[1]]
+97
View File
@@ -0,0 +1,97 @@
import sys
import numpy
import math
import os.path
import unittest
print "running tests on modules in source directory"
# assume, script is in tests directory and we are testing modules in src
sys.path[0:0]=[os.path.join(os.path.dirname(sys.argv[0]), "..", "src", "data")]
from MeasurementResult import *
class TestAccumulatedValueClass(unittest.TestCase):
def setUp(self):
# is called before each test
pass
def testInitialization_Empty(self):
a=AccumulatedValue()
self.assert_(a.mean() is None)
self.assert_(a.mean_error() is None)
self.assert_(a.sigma() is None)
self.assert_(a.n==0)
def testInitialization_1Value(self):
a=AccumulatedValue(0)
self.assert_(a.mean()==0)
self.assertAlmostEqual(a.mean_error(),0)
self.assertAlmostEqual(a.sigma(),0)
self.assert_(a.n==1)
def testInitialization_2Values(self):
a=AccumulatedValue(1, 0.1)
self.assert_(a.mean(),1.0)
self.assertAlmostEqual(a.mean_error(), 0.1)
self.assertAlmostEqual(a.sigma(), 0.1*math.sqrt(2.0))
self.assert_(a.n==2)
def testInitialization_3Values(self):
a=AccumulatedValue(1, 0.1, 10)
self.assertAlmostEqual(a.mean(),1)
self.assertAlmostEqual(a.mean_error(), 0.1)
self.assertAlmostEqual(a.sigma(), 0.1*math.sqrt(10.0))
self.assert_(a.n==10)
def testStatistics(self):
test_dataset=numpy.arange(10.0)
a=AccumulatedValue()
for i in test_dataset:
a+=i
self.assert_(a.n==len(test_dataset))
# sum x_i/n
self.assertAlmostEqual(a.mean(), test_dataset.mean())
# std_dev_n-1 x_i= sqrt(sum (x-xmean)**2/(n-1))
self.assertAlmostEqual(a.sigma(), math.sqrt(((test_dataset-a.mean())**2).sum()/(len(test_dataset)-1.)))
# std_dev_n-1 x_i/sqrt(n)
self.assertAlmostEqual(a.mean_error(), a.sigma()/math.sqrt(len(test_dataset)))
def tearDown(self):
# is called after each test
pass
class TestMeasurementResult(unittest.TestCase):
def testImplicitCast(self):
# check wether other data types will be converted to AccumulatedValue
m=MeasurementResult("TestData")
m[1.0]
m[2.0]=2
self.assert_(isinstance(m[1.0], AccumulatedValue))
self.assert_(isinstance(m[2.0], AccumulatedValue))
def testUninitalizedEntries(self):
# assure that entries with no data are listed as xdata
m=MeasurementResult("TestData")
a=m[2.0]
self.assert_(isinstance(a, AccumulatedValue))
self.assert_(2.0 in m)
self.assert_(2.0 not in m.get_xdata())
m[2.0]+=1
self.assert_(2.0 in m.get_xdata())
def testZeroError(self):
# AccumulatedValues with only one Accumulation should have 0 error
m=MeasurementResult("TestData")
m[0.0]
m[1.0]=AccumulatedValue()
m[2.0]=0
m[3.0]=AccumulatedValue(0,1.0)
k,v,e=m.get_errorplotdata()
self.assert_(2.0 in k and 3.0 in k)
self.assert_(1.0 not in k and 0.0 not in k)
self.assertAlmostEqual(e[k==2.0][0], 0)
self.assertAlmostEqual(e[k==3.0][0], 1.0)
if __name__=="__main__":
unittest.main()
+11
View File
@@ -0,0 +1,11 @@
def test():
e=Experiment()
e.ttl_pulse(length=1e-6, value=1)
e.wait(1e-3)
e.ttl_pulse(length=1e-6,value=1)
e.record(samples=1024,frequency=1e6,sensitivity=1)
return e
def experiment():
for i in range(100):
yield test()
+6
View File
@@ -0,0 +1,6 @@
def result():
for num,ts in enumerate(results):
data["ts %i"%num] = ts+0
data["fft"] = (ts+0).fft()
data["ppm"] = (ts+0).fft().ppm(300e6)
#print ts