1
|
|
###############################################################################
|
2
|
|
# pyrpl - DSP servo controller for quantum optics with the RedPitaya
|
3
|
|
# Copyright (C) 2014-2016 Leonhard Neuhaus (neuhaus@spectro.jussieu.fr)
|
4
|
|
#
|
5
|
|
# This program is free software: you can redistribute it and/or modify
|
6
|
|
# it under the terms of the GNU General Public License as published by
|
7
|
|
# the Free Software Foundation, either version 3 of the License, or
|
8
|
|
# (at your option) any later version.
|
9
|
|
#
|
10
|
|
# This program is distributed in the hope that it will be useful,
|
11
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
|
# GNU General Public License for more details.
|
14
|
|
#
|
15
|
|
# You should have received a copy of the GNU General Public License
|
16
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
|
###############################################################################
|
18
|
|
|
19
|
|
|
20
|
|
# Dummy version of CurveDB class
|
21
|
|
#
|
22
|
|
# The original version of this code uses a package called pyinstruments, which
|
23
|
|
# is a database for experimental measurement data.
|
24
|
|
# This class simulates the functionality of pyinstruments to guarantee
|
25
|
|
# standalone functionality of the rplockbox package.
|
26
|
|
# In case you
|
27
|
|
# For full performance, download pyinstruments to replace this class
|
28
|
|
# otherwise you can custimize here what is to be done to your data
|
29
|
|
#
|
30
|
3
|
import numpy as np
|
31
|
3
|
import pandas as pd
|
32
|
3
|
import os
|
33
|
3
|
import logging
|
34
|
3
|
import pickle as file_backend
|
35
|
|
#import json as file_backend # currently unable to store pandas
|
36
|
|
|
37
|
|
|
38
|
|
# optional override of CurveDB class with custom module, as defined in
|
39
|
|
# ./pyrpl/config/global_config.yml
|
40
|
3
|
try:
|
41
|
3
|
from . import global_config
|
42
|
3
|
CurveDB = __import__(global_config.general.curvedb).CurveDB
|
43
|
3
|
except:
|
44
|
3
|
from . import user_curve_dir
|
45
|
3
|
class CurveDB(object):
|
46
|
3
|
_dirname = user_curve_dir
|
47
|
3
|
file_extension = '.dat'
|
48
|
|
|
49
|
3
|
if not os.path.exists(_dirname): # if _dirname doesn't exist, some unexpected errors will occur.
|
50
|
0
|
os.mkdir(_dirname)
|
51
|
|
|
52
|
3
|
def __init__(self, name="some_curve"):
|
53
|
|
"""
|
54
|
|
A CurveDB object has
|
55
|
|
- name = string to give the curve a name
|
56
|
|
- pk = integer to uniquely identify the curve (the database primary key)
|
57
|
|
- data = pandas.Series() object to hold any data
|
58
|
|
- params = dict() with all kinds of parameters
|
59
|
|
"""
|
60
|
0
|
self.logger = logging.getLogger(name=__name__)
|
61
|
0
|
self.params = dict()
|
62
|
0
|
x, y = np.array([], dtype=np.float), np.array([], dtype=np.float)
|
63
|
0
|
self.data = (x, y)
|
64
|
0
|
self.name = name
|
65
|
|
|
66
|
3
|
@property
|
67
|
|
def name(self):
|
68
|
0
|
return self.params["name"]
|
69
|
|
|
70
|
3
|
@name.setter
|
71
|
|
def name(self, val):
|
72
|
0
|
self.params["name"] = val
|
73
|
0
|
return val
|
74
|
|
|
75
|
3
|
@classmethod
|
76
|
|
def create(cls, *args, **kwds):
|
77
|
|
"""
|
78
|
|
Creates a new curve, first arguments should be either
|
79
|
|
Series(y, index=x) or x, y.
|
80
|
|
kwds will be passed to self.params
|
81
|
|
"""
|
82
|
0
|
if len(args) == 0:
|
83
|
0
|
ser = (np.array([], dtype=np.float), np.array([], dtype=np.float))
|
84
|
0
|
if len(args) == 1:
|
85
|
0
|
if isinstance(args[0], pd.Series):
|
86
|
0
|
x, y = args[0].index.values, args[0].values
|
87
|
0
|
ser = (x, y)
|
88
|
0
|
elif isinstance(args[0], (np.array, list, tuple)):
|
89
|
0
|
ser = args[0]
|
90
|
|
else:
|
91
|
0
|
raise ValueError("cannot recognize argument %s as numpy.array or pandas.Series.", args[0])
|
92
|
0
|
elif len(args) == 2:
|
93
|
0
|
x = np.array(args[0])
|
94
|
0
|
y = np.array(args[1])
|
95
|
0
|
ser = (x, y)
|
96
|
|
else:
|
97
|
0
|
raise ValueError("first arguments should be either x or x, y")
|
98
|
0
|
obj = cls()
|
99
|
0
|
obj.data = ser
|
100
|
0
|
obj.params = kwds
|
101
|
0
|
if not 'name' in obj.params:
|
102
|
0
|
obj.params['name'] = 'new_curve'
|
103
|
0
|
pk = obj.pk # make a pk
|
104
|
0
|
if "childs" not in obj.params:
|
105
|
0
|
obj.params["childs"] = None
|
106
|
0
|
if ("autosave" not in kwds) or (kwds["autosave"]):
|
107
|
0
|
obj.save()
|
108
|
0
|
return obj
|
109
|
|
|
110
|
3
|
def plot(self):
|
111
|
0
|
self.data.plot()
|
112
|
|
|
113
|
|
# Implement the following methods if you want to save curves permanently
|
114
|
3
|
@classmethod
|
115
|
|
def get(cls, curve):
|
116
|
0
|
if isinstance(curve, CurveDB):
|
117
|
0
|
return curve
|
118
|
0
|
elif isinstance(curve, list):
|
119
|
0
|
return [CurveDB.get(c) for c in curve]
|
120
|
|
else:
|
121
|
0
|
with open(os.path.join(CurveDB._dirname, str(curve) + cls.file_extension),
|
122
|
|
'rb' if file_backend.__name__ == 'pickle' else 'r')\
|
123
|
|
as f:
|
124
|
|
# rb is for compatibility with python 3
|
125
|
|
# see http://stackoverflow.com/questions/5512811/builtins-typeerror-must-be-str-not-bytes
|
126
|
0
|
curve = CurveDB()
|
127
|
0
|
curve._pk, curve.params, data = file_backend.load(f)
|
128
|
0
|
curve.data = tuple([np.asarray(a) for a in data])
|
129
|
0
|
if isinstance(curve.data, pd.Series): # for backwards compatibility
|
130
|
0
|
x, y = curve.data.index.values, curve.data.values
|
131
|
0
|
curve.data = (x, y)
|
132
|
0
|
return curve
|
133
|
|
|
134
|
3
|
def save(self):
|
135
|
0
|
with open(os.path.join(self._dirname, str(self.pk) + self.file_extension),
|
136
|
|
'wb' if file_backend.__name__ == 'pickle' else 'w')\
|
137
|
|
as f:
|
138
|
|
# wb is for compatibility with python 3
|
139
|
|
# see http://stackoverflow.com/questions/5512811/builtins-typeerror-must-be-str-not-bytes
|
140
|
0
|
data = [a.tolist() for a in self.data]
|
141
|
0
|
file_backend.dump([self.pk, self.params, data], f, )
|
142
|
|
|
143
|
3
|
def delete(self):
|
144
|
|
# remove the file
|
145
|
0
|
delpk = self.pk
|
146
|
0
|
parent = self.parent
|
147
|
0
|
childs = self.childs
|
148
|
0
|
if isinstance(childs, list) and len(childs)> 0:
|
149
|
0
|
self.logger.debug("Deleting all childs of curve %d"%delpk)
|
150
|
0
|
for child in childs:
|
151
|
0
|
child.delete()
|
152
|
0
|
self.logger.debug("Deleting curve %d" % delpk)
|
153
|
0
|
try:
|
154
|
0
|
filename = os.path.join(self._dirname, str(self.pk) + self.file_extension)
|
155
|
0
|
os.remove(filename)
|
156
|
0
|
except OSError:
|
157
|
0
|
self.logger.warning("Could not find and remove the file %s. ",
|
158
|
|
filename)
|
159
|
0
|
if parent:
|
160
|
0
|
parentchilds = parent.childs
|
161
|
0
|
parentchilds.remove(delpk)
|
162
|
0
|
parent.childs = parentchilds
|
163
|
0
|
parent.save()
|
164
|
|
|
165
|
|
# Implement the following methods if you want to use a hierarchical
|
166
|
|
# structure for curves
|
167
|
3
|
@property
|
168
|
|
def childs(self):
|
169
|
0
|
try:
|
170
|
0
|
childs = self.params["childs"]
|
171
|
0
|
except KeyError:
|
172
|
0
|
return []
|
173
|
0
|
if childs is None:
|
174
|
0
|
return []
|
175
|
|
else:
|
176
|
0
|
try:
|
177
|
0
|
return CurveDB.get(childs)
|
178
|
0
|
except KeyError:
|
179
|
0
|
return []
|
180
|
|
|
181
|
3
|
@property
|
182
|
|
def parent(self):
|
183
|
0
|
try:
|
184
|
0
|
parentid = self.params["parent"]
|
185
|
0
|
except KeyError:
|
186
|
0
|
self.logger.debug("No parent found.")
|
187
|
0
|
return None
|
188
|
|
else:
|
189
|
0
|
return CurveDB.get(parentid)
|
190
|
|
|
191
|
3
|
def add_child(self, child_curve):
|
192
|
0
|
child = CurveDB.get(child_curve)
|
193
|
0
|
child.params["parent"] = self.pk
|
194
|
0
|
child.save()
|
195
|
0
|
childs = self.params["childs"] or []
|
196
|
0
|
self.params["childs"] = list(childs+[child.pk])
|
197
|
0
|
self.save()
|
198
|
|
|
199
|
3
|
@classmethod
|
200
|
|
def all_pks(cls):
|
201
|
|
"""
|
202
|
|
Returns:
|
203
|
|
list of int: A list of the primary keys of all CurveDB objects on the computer.
|
204
|
|
"""
|
205
|
0
|
pks = [int(f.split('.dat')[0])
|
206
|
|
for f in os.listdir(cls._dirname) if f.endswith('.dat')]
|
207
|
0
|
return sorted(pks, reverse=True)
|
208
|
|
|
209
|
3
|
@classmethod
|
210
|
|
def all(cls):
|
211
|
|
"""
|
212
|
|
Returns:
|
213
|
|
list of CurveDB: A list of all CurveDB objects on the computer.
|
214
|
|
"""
|
215
|
0
|
return [cls.get(pk) for pk in cls.all_pks()]
|
216
|
|
|
217
|
3
|
@property
|
218
|
|
def pk(self):
|
219
|
|
"""
|
220
|
|
(int): The primary Key of the
|
221
|
|
"""
|
222
|
0
|
if hasattr(self, "_pk"):
|
223
|
0
|
return self._pk
|
224
|
|
else:
|
225
|
0
|
pks = self.all_pks()
|
226
|
0
|
if len(pks) == 0:
|
227
|
0
|
self._pk = 1
|
228
|
|
else:
|
229
|
0
|
self._pk = max(pks) + 1
|
230
|
|
# create the file to make this pk choice persistent
|
231
|
0
|
with open(os.path.join(self._dirname,
|
232
|
|
str(self._pk) + ".dat"), 'w') as f:
|
233
|
0
|
f.close()
|
234
|
0
|
return self._pk
|
235
|
0
|
return -1
|
236
|
|
# a proper implementation will assign the database primary key for pk
|
237
|
|
# the primary key is used to load a curve from the storage into memory
|
238
|
|
|
239
|
3
|
def sort(self):
|
240
|
|
"""numerically sorts the data series so that indexing can be used"""
|
241
|
0
|
X, Y = self.data
|
242
|
0
|
xs = np.array([x for (x, y) in sorted(zip(X, Y))], dtype=np.float64)
|
243
|
0
|
ys = np.array([y for (x, y) in sorted(zip(X, Y))], dtype=np.float64)
|
244
|
0
|
self.data = (xs, ys)
|
245
|
|
|
246
|
3
|
def fit(self):
|
247
|
|
""" prototype for fitting a curve """
|
248
|
0
|
self.logger.warning("Not implemented")
|
249
|
0
|
pass
|
250
|
|
|
251
|
3
|
def get_child(self, name):
|
252
|
|
"""
|
253
|
|
Returns the child of the curve with name 'name'
|
254
|
|
|
255
|
|
Arguments:
|
256
|
|
name (str): Name of the child curve to be retrieved. If
|
257
|
|
several childs have the same name, the first one is
|
258
|
|
returned.
|
259
|
|
|
260
|
|
Returns:
|
261
|
|
CurveDB: the child curve
|
262
|
|
"""
|
263
|
0
|
for c in self.childs:
|
264
|
0
|
if c.name == name:
|
265
|
0
|
return c
|