pyForceDAQ 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. pyforcedaq/__init__.py +43 -0
  2. pyforcedaq/__main__.py +42 -0
  3. pyforcedaq/_lib/__init__.py +1 -0
  4. pyforcedaq/_lib/lsl.py +56 -0
  5. pyforcedaq/_lib/misc.py +126 -0
  6. pyforcedaq/_lib/polling_time_profile.py +52 -0
  7. pyforcedaq/_lib/process_priority_manager.py +148 -0
  8. pyforcedaq/_lib/timer.py +45 -0
  9. pyforcedaq/_lib/types.py +400 -0
  10. pyforcedaq/_lib/udp_connection.py +326 -0
  11. pyforcedaq/daq/__init__.py +19 -0
  12. pyforcedaq/daq/_daq_read_Analog_pydaqmx.py +114 -0
  13. pyforcedaq/daq/_daq_read_analog_nidaqmx.py +84 -0
  14. pyforcedaq/daq/_mock_sensor.py +80 -0
  15. pyforcedaq/daq/_pyATIDAQ.py +306 -0
  16. pyforcedaq/daq/config.py +13 -0
  17. pyforcedaq/extras/__init__.py +0 -0
  18. pyforcedaq/extras/convert.py +275 -0
  19. pyforcedaq/extras/expyriment_daq_control.py +246 -0
  20. pyforcedaq/extras/opensesame_daq_control.py +280 -0
  21. pyforcedaq/extras/read_force_data.py +89 -0
  22. pyforcedaq/extras/remote_control.py +93 -0
  23. pyforcedaq/force/__init__.py +13 -0
  24. pyforcedaq/force/_log.py +18 -0
  25. pyforcedaq/force/data_recorder.py +400 -0
  26. pyforcedaq/force/sensor.py +200 -0
  27. pyforcedaq/force/sensor_process.py +251 -0
  28. pyforcedaq/gui/__init__.py +6 -0
  29. pyforcedaq/gui/_gui_status.py +306 -0
  30. pyforcedaq/gui/_layout.py +104 -0
  31. pyforcedaq/gui/_level_indicator.py +59 -0
  32. pyforcedaq/gui/_pg_surface.py +100 -0
  33. pyforcedaq/gui/_plotter.py +234 -0
  34. pyforcedaq/gui/_run.py +522 -0
  35. pyforcedaq/gui/_scaling.py +71 -0
  36. pyforcedaq/gui/_settings.py +98 -0
  37. pyforcedaq/gui/forceDAQ_logo.png +0 -0
  38. pyforcedaq/gui/launcher.py +249 -0
  39. pyforcedaq-2.0.0.dist-info/METADATA +15 -0
  40. pyforcedaq-2.0.0.dist-info/RECORD +42 -0
  41. pyforcedaq-2.0.0.dist-info/WHEEL +4 -0
  42. pyforcedaq-2.0.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,306 @@
1
+ """pyATIDAQ: Python wrapper for atidaq c library
2
+
3
+ Notes
4
+ -----
5
+ see ftconfig.h & ftsharedrt.h for details
6
+
7
+ See COPYING file distributed along with the pyForceDAQ copyright and license terms.
8
+ """
9
+
10
+ __author__ = "Oliver Lindemann"
11
+
12
+ from ctypes import *
13
+ from sys import platform
14
+
15
+ from .._lib.misc import find_calibration_file
16
+
17
+ # ### DATA TYPES ####
18
+ VOLTAGE_SAMPLE_TYPE = c_float * 7
19
+ FT_SAMPLE_TYPE = c_float * 6
20
+ DISPLACEMENT_VECTOR = c_float * 8
21
+ UNITS = c_char_p
22
+ MAX_AXES = 6
23
+ MAX_GAUGES = 8
24
+
25
+
26
+ # calibration information required for F/T conversions
27
+ class RTCoefs(Structure):
28
+ _fields_ = [
29
+ ('NumChannels', c_ushort),
30
+ ('NumAxes', c_ushort),
31
+ ('working_matrix', (c_float * MAX_AXES) * MAX_GAUGES),
32
+ ('bias_slopes', c_float * MAX_GAUGES),
33
+ ('gain_slopes', c_float * MAX_GAUGES),
34
+ ('thermistor', c_float),
35
+ ('bias_vector', c_float * (MAX_GAUGES + 1)),
36
+ ('TCbias_vector', c_float * MAX_GAUGES)]
37
+
38
+
39
+ class Transform(Structure):
40
+ _fields_ = [
41
+ ('TT', c_float * 6), # displacement/rotation vector dx, dy, dz, rx, ry, r
42
+ ('DistUnits', UNITS), # units of dx, dy, dz
43
+ ('AngleUnits', UNITS)] # units of rx, ry, rz
44
+
45
+
46
+ class Configuration(Structure):
47
+ _fields_ = [
48
+ ('ForceUnits', UNITS), # force units of output
49
+ ('TorqueUnits', UNITS), # torque units of output
50
+ ('UserTransform', Transform), # coordinate system transform set by user
51
+ ('TempCompEnabled', c_bool)] # is temperature compensation enabled?
52
+
53
+
54
+ class Calibration(Structure):
55
+ _fields_ = [
56
+ ('BasicMatrix', (c_float * MAX_AXES) * MAX_GAUGES), # non-usable matrix; use rt.working_matrix for calculations
57
+ ('ForceUnits', UNITS), # force units of basic matrix, as read from file; constant
58
+ ('TorqueUnits', UNITS), # torque units of basic matrix, as read from file; constant
59
+ ('TempCompAvailable', c_bool), # does this calibration have optional temperature compensation?
60
+ ('BasicTransform', Transform), # built-in coordinate transform; for internal use
61
+ ('MaxLoads', c_float * MAX_AXES), # maximum loads of each axis, in units above
62
+ ('AxisNames', c_char_p * MAX_AXES), # names of each axis
63
+ ('Serial', c_char_p), # serial number of transducer (such as "FT4566")
64
+ ('BodyStyle', c_char_p), # transducer's body style (such as "Delta")
65
+ ('PartNumber', c_char_p), # calibration part number (such as "US-600-3600")
66
+ ('Family', c_char_p), # family of transducer (typ. "DAQ")
67
+ ('CalDate', c_char_p), # date of calibration
68
+ ('cfg', Configuration), # struct containing configurable parameters
69
+ ('rt', RTCoefs)] # struct containing coefficients used in realtime calculations
70
+
71
+
72
+ calibration_p = POINTER(Calibration)
73
+
74
+
75
+ class ATI_CDLL(object):
76
+
77
+ def __init__(self):
78
+
79
+ if platform.startswith('linux'):
80
+ lib_path = "/usr/lib/atidaq.so"
81
+ elif platform == 'win32':
82
+ lib_path = "C:\\Windows\\System\\atidaq.dll"
83
+ else:
84
+ raise RuntimeError("Your plattform is not supported")
85
+
86
+ self.cdll = CDLL(lib_path)
87
+
88
+ self.cdll.createCalibration.argtype = [c_char_p, c_ushort]
89
+ self.cdll.createCalibration.restype = calibration_p
90
+
91
+ self.cdll.destroyCalibration.argtype = [calibration_p]
92
+ self.cdll.destroyCalibration.restype = None
93
+
94
+ self.cdll.SetToolTransform.argtype = [calibration_p, DISPLACEMENT_VECTOR,
95
+ c_char_p, c_char_p]
96
+ self.cdll.SetToolTransform.restype = c_short
97
+
98
+ self.cdll.SetForceUnits.argtype = [calibration_p, c_char_p]
99
+ self.cdll.SetForceUnits.restype = c_short
100
+
101
+ self.cdll.SetTorqueUnits.argtype = [calibration_p, c_char_p]
102
+ self.cdll.SetTorqueUnits.restype = c_short
103
+
104
+ self.cdll.SetTempComp.argtype = [calibration_p, c_char_p]
105
+ self.cdll.SetTempComp.restype = c_short
106
+
107
+ self.cdll.Bias.argtype = [calibration_p, POINTER(c_float)]
108
+ self.cdll.Bias.restype = None
109
+
110
+ self.cdll.ConvertToFT.argtype = [calibration_p, POINTER(c_float),
111
+ POINTER(c_float)]
112
+ self.cdll.ConvertToFT.restype = None
113
+
114
+ self.cdll.printCalInfo.argtype = [calibration_p]
115
+ self.cdll.printCalInfo.restype = None
116
+
117
+ self._calibration = None
118
+
119
+ def __del__(self):
120
+ self.destroyCalibration()
121
+
122
+ def calibration(self):
123
+ """The current calibration
124
+ Returns
125
+ cal: POINTER(Calibration), calibration pointer
126
+ initialized Calibration struct
127
+
128
+ """
129
+ return self._calibration
130
+
131
+ def createCalibration(self, CalFilePath, index):
132
+ """ Loads calibration info for a transducer into a new Calibration struct
133
+ Parameters:
134
+ CalFilePath: c_char_p
135
+ the name and path of the calibration file
136
+ index: c_ushort
137
+ the number of the calibration within the file (usually 1)
138
+ NULL: Could not load the desired calibration.
139
+ Notes: For each Calibration object initialized by this function,
140
+ destroyCalibration must be called for cleanup.
141
+ """
142
+
143
+ self._calibration = self.cdll.createCalibration(CalFilePath.encode(), index)
144
+ if self._calibration == 0:
145
+ raise RuntimeError("Specified calibration could not be loaded.")
146
+
147
+
148
+ def destroyCalibration(self):
149
+ """Frees memory allocated for Calibration struct by a successful
150
+ call to createCalibration. Must be called when Calibration
151
+ struct is no longer needed.
152
+ """
153
+
154
+ return self.cdll.destroyCalibration(self._calibration)
155
+
156
+ def setToolTransform(self, Vector, DistUnits, AngleUnits):
157
+ """Performs a 6-axis translation/rotation on the transducer's coordinate system.
158
+ Parameters:
159
+ Vector: array of float
160
+ displacements and rotations in the order Dx, Dy, Dz, Rx, Ry, Rz
161
+ DistUnits: c_char_p
162
+ units of Dx, Dy, Dz
163
+ AngleUnits: c_char_p
164
+ units of Rx, Ry, Rz
165
+ """
166
+
167
+ error = self.cdll.SetToolTransform(self._calibration, DISPLACEMENT_VECTOR(*Vector),
168
+ DistUnits.encode(),
169
+ AngleUnits.encode())
170
+ if error:
171
+ if error == 1:
172
+ raise RuntimeError("Invalid Calibration struct.")
173
+ elif error == 2:
174
+ raise RuntimeError("Invalid distance units.")
175
+ elif error == 3:
176
+ raise RuntimeError("Invalid angle units.")
177
+ else:
178
+ raise RuntimeError("Unknown error.")
179
+
180
+
181
+
182
+ def setForceUnits(self, NewUnits):
183
+ """Sets the units of force output
184
+ Parameters:
185
+ NewUnits: c_char_p
186
+ units for force output
187
+ ("lb","klb","N","kN","g","kg")
188
+ """
189
+
190
+ error = self.cdll.SetForceUnits(self._calibration,
191
+ NewUnits.encode())
192
+ if error:
193
+ if error == 1:
194
+ raise RuntimeError("Invalid Calibration struct.")
195
+ elif error == 2:
196
+ raise RuntimeError("Invalid force units.")
197
+ else:
198
+ raise RuntimeError("Unknown error.")
199
+
200
+
201
+ def setTorqueUnits(self, NewUnits):
202
+ """Sets the units of torque output
203
+ Parameters:
204
+ NewUnits: c_char_p
205
+ units for torque output
206
+ ("in-lb","ft-lb","N-m","N-mm","kg-cm")
207
+ """
208
+ error = self.cdll.SetTorqueUnits(self._calibration,
209
+ NewUnits.encode())
210
+ if error:
211
+ if error == 1:
212
+ raise RuntimeError("Invalid Calibration struct.")
213
+ elif error == 2:
214
+ raise RuntimeError("Invalid torque units.")
215
+ else:
216
+ raise RuntimeError("Unknown error.")
217
+
218
+
219
+ def setTempComp(self, TCEnabled):
220
+ """Enables or disables temperature compensation, if available
221
+ Parameters:
222
+ TCEnabled: c_char_p
223
+ 0 = temperature compensation off
224
+ 1 = temperature compensation on
225
+ """
226
+
227
+ error = self.cdll.SetTempComp(self._calibration,
228
+ TCEnabled.encode())
229
+ if error:
230
+ if error == 1:
231
+ raise RuntimeError("Invalid Calibration struct.")
232
+ elif error == 2:
233
+ raise RuntimeError("Not available on this transducer system")
234
+ else:
235
+ raise RuntimeError("Unknown error.")
236
+
237
+ def bias(self, voltages):
238
+ """Stores a voltage reading to be subtracted from subsequent readings,
239
+ effectively "zeroing" the transducer output to remove tooling weight, etc.
240
+ Parameters:
241
+ voltages: array of float
242
+ array of voltages acquired by DAQ system
243
+ """
244
+
245
+ return self.cdll.Bias(self._calibration, VOLTAGE_SAMPLE_TYPE(*voltages))
246
+
247
+ def convertToFT(self, voltages, reverse_parameters=[]):
248
+ """Converts an array of voltages into forces and torques and
249
+ returns them in result
250
+ Parameters:
251
+ voltages: array of float
252
+ array of voltages acquired by DAQ system
253
+ reverse_parameters: array of integer
254
+ list of ids of parameter that should be reversed due to problems calibration with
255
+ the calibration
256
+ Returns:
257
+ array of force-torque values (typ. 6 elements)
258
+ """
259
+ ft_array = FT_SAMPLE_TYPE()
260
+ self.cdll.ConvertToFT(self._calibration, VOLTAGE_SAMPLE_TYPE(*voltages),
261
+ byref(ft_array))
262
+ rtn = list(map(lambda x: x, ft_array)) # convert ctype array to python
263
+ # array
264
+ for x in reverse_parameters:
265
+ rtn[x] = -1*rtn[x]
266
+ return rtn
267
+
268
+ def printCalInfo(self):
269
+ """print Calibration info on the console
270
+ """
271
+
272
+ return self.cdll.printCalInfo(self._calibration)
273
+
274
+ def print_calibration_info(calibration_file):
275
+ """convenient function to print calibration file infos"""
276
+ atidaq = ATI_CDLL()
277
+ index = c_short(1)
278
+ calibration = atidaq.createCalibration(calibration_file, index)
279
+ atidaq.printCalInfo(calibration)
280
+ atidaq.destroyCalibration(calibration)
281
+
282
+
283
+ if __name__ == "__main__":
284
+ # test module
285
+
286
+ # FT_sensor1.cal
287
+ # Bias reading:
288
+ # 0.265100 -0.017700 -0.038400 -0.042700 -0.189100 0.137300 -3.242300
289
+ # Measurement:
290
+ # -3.286300 0.387500 -3.487700 0.404300 -3.934100 0.547400 -3.210600
291
+ # Result:
292
+ # -0.065867 0.123803 111.156731 0.039974 0.040417 0.079049
293
+
294
+ #filename = raw_input("Calibration file: ")
295
+ filename = find_calibration_file("calibration", "FT30436")
296
+ atidaq = ATI_CDLL()
297
+ # get calibration
298
+ index = c_short(1)
299
+ atidaq.createCalibration(filename, index)
300
+ atidaq.setForceUnits("N")
301
+ atidaq.setTorqueUnits("N-m")
302
+
303
+ atidaq.bias([0.2651, -0.0177, -0.0384, -0.0427, -0.1891, 0.1373, -3.2423])
304
+ atidaq.printCalInfo()
305
+ print("\nConverting")
306
+ print(atidaq.convertToFT([-3.2863, 0.3875, -3.4877, 0.4043, -3.9341, 0.5474, -3.2106]))
@@ -0,0 +1,13 @@
1
+ class DAQConfiguration(object):
2
+ """Settings required for NI-DAQ"""
3
+ def __init__(self, device_name: str, channels: str = "ai0:7",
4
+ rate: int = 1000, minVal: float = -10, maxVal: float = 10):
5
+ self.device_name = device_name
6
+ self.channels = channels
7
+ self.rate = rate
8
+ self.minVal = minVal
9
+ self.maxVal = maxVal
10
+
11
+ @property
12
+ def physicalChannel(self):
13
+ return "{0}/{1}".format(self.device_name, self.channels)
File without changes
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ Functions to convert force data
5
+
6
+ This module can be also executed.
7
+ """
8
+
9
+ __author__ = 'Oliver Lindemann'
10
+
11
+ import os
12
+ import sys
13
+ import gzip
14
+ import numpy as np
15
+ from .read_force_data import read_raw_data, data_frame_to_text
16
+
17
+ PAUSE_CRITERION = 500
18
+ MSEC_PER_SAMPLES = 1
19
+ REF_SAMPLE_PROBE = 1000
20
+ MIN_DELAY_ENDSTREAM = 2
21
+ CONVERTED_SUFFIX = ".conv.csv.gz"
22
+ CONVERTED_SUBFOLDER = "converted"
23
+
24
+ def _periods_from_daq_events(daq_events):
25
+
26
+ periods = {}
27
+ started = None
28
+ sensor_id = None
29
+ evt = np.array(daq_events["value"])
30
+ times = np.array(daq_events["time"]).astype(int)
31
+ idx = np.argsort(times)
32
+
33
+ for t, v in zip(times[idx], evt[idx]):
34
+ try:
35
+ sensor_id = int(v.split(":")[1])
36
+ except:
37
+ sensor_id = None
38
+
39
+ if sensor_id not in periods:
40
+ periods[sensor_id] = []
41
+
42
+ if v.startswith("started"):
43
+ if started is None:
44
+ started = t
45
+ else:
46
+ periods[sensor_id].append((started, None))
47
+ started = None
48
+ elif v.startswith("pause"):
49
+ periods[sensor_id].append((started, t))
50
+ started = None
51
+
52
+ # sort remaining
53
+ if started is not None:
54
+ periods[sensor_id].append((started, None))
55
+
56
+ return periods
57
+
58
+ def _pauses_idx_from_timeline(time, pause_criterion):
59
+ pauses_idx = np.where(np.diff(time) > pause_criterion)[0]
60
+ last_pause = -1
61
+ rtn = []
62
+ for idx in np.append(pauses_idx, len(time)-1):
63
+ rtn.append((last_pause+1, idx))
64
+ last_pause = idx
65
+ return rtn
66
+
67
+ def _most_frequent_value(values):
68
+ (v, cnt) = np.unique(values, return_counts=True)
69
+ idx = np.argmax(cnt)
70
+ return v[idx]
71
+
72
+ def print_histogram(values):
73
+ (v, cnt) = np.unique(values, return_counts=True)
74
+ for a,b in zip(v,cnt):
75
+ print("{} -- {}".format(a,b))
76
+
77
+
78
+ def _end_stream_sample(timestamps, min_delay=MIN_DELAY_ENDSTREAM):
79
+ """finds end of the data stream, that is, sample before next long waiting
80
+ sample or returns None if no end can be detected"""
81
+
82
+ next_t_diffs = np.diff(timestamps)
83
+ try:
84
+ return np.where(next_t_diffs >= min_delay)[0][0] #+1-1
85
+ except:
86
+ return None
87
+
88
+
89
+ def _linear_timeline_matched_by_single_reference_sample(irregular_timeline,
90
+ id_ref_sample, msec_per_sample):
91
+ """match timeline that differences between the two is minimal
92
+ new times can not be after irregular times
93
+ """
94
+ t_ref = irregular_timeline[id_ref_sample]
95
+ t_first = t_ref - (id_ref_sample*msec_per_sample)
96
+ t_last = t_first + ((len(irregular_timeline) - 1) * msec_per_sample)
97
+ return np.arange(t_first, t_last + msec_per_sample, step=msec_per_sample)
98
+
99
+
100
+ def _timeline_matched_by_delay_chunked_samples(times, msec_per_sample):
101
+
102
+ rtn = np.empty(len(times))*np.NaN
103
+ p = 0
104
+ while p<len(times):
105
+ next_ref_sample = _end_stream_sample(times[p:])
106
+ if next_ref_sample is not None:
107
+ ref_time = times[p+next_ref_sample]
108
+ rtn[p:(p+next_ref_sample+1)] = np.arange(
109
+ start = ref_time - (next_ref_sample*msec_per_sample),
110
+ stop = ref_time + msec_per_sample,
111
+ step = msec_per_sample)
112
+ p = p + next_ref_sample + 1
113
+
114
+ else:
115
+ # no further refence samples
116
+ rtn[p:] = times[p:]
117
+ break
118
+
119
+ return rtn
120
+
121
+ class Method(object):
122
+
123
+ types = {1: "single reference sample (forced linearity)",
124
+ 2: "multiple delayed chunked samples (no linearity assumed)"}
125
+
126
+ def __init__(self, id):
127
+ if id not in Method.types:
128
+ raise RuntimeError("Unkown resampling method")
129
+ self.id = id
130
+
131
+ @property
132
+ def description(self):
133
+ return Method.types[self.id]
134
+
135
+ @staticmethod
136
+ def get_method_from_description(description):
137
+ for id, desc in Method.types.items():
138
+ if desc == description:
139
+ return Method(id)
140
+ return None
141
+
142
+
143
+ def _adjusted_timestamps(timestamps, pauses_idx, evt_periods, method):
144
+ """
145
+ method=Method(1): _linear_timeline_matched_by_single_reference_sample
146
+ method=Method(2): _timeline_matched_by_delay_chunked_samples
147
+ """
148
+
149
+ # adapting timestamps
150
+ rtn = np.empty(len(timestamps))*np.NaN
151
+ period_counter = 0
152
+ for idx, evt_per in zip(pauses_idx, evt_periods):
153
+ # loop over periods
154
+
155
+ # logging
156
+ period_counter += 1
157
+ n_samples = idx[1] - idx[0] + 1
158
+ if evt_per[1]: # end time
159
+ sample_diff = n_samples - (1+(evt_per[1]-evt_per[0])//MSEC_PER_SAMPLES)
160
+ if sample_diff!=0:
161
+ print("Period {}: Sample difference of {}".format(
162
+ period_counter, sample_diff))
163
+ else:
164
+ print("Period {}: No pause sampling time.".format(period_counter))
165
+
166
+ #convert times
167
+ times = timestamps[idx[0]:idx[1] + 1]
168
+ if method.id==1:
169
+ # match refe samples
170
+ next_ref = _end_stream_sample(times[REF_SAMPLE_PROBE:(REF_SAMPLE_PROBE + 1000)])
171
+ if next_ref is None:
172
+ next_ref = 0
173
+ newtimes = _linear_timeline_matched_by_single_reference_sample(
174
+ times, id_ref_sample=REF_SAMPLE_PROBE + next_ref,
175
+ msec_per_sample=MSEC_PER_SAMPLES)
176
+ elif method.id==2:
177
+ # using delays
178
+ newtimes = _timeline_matched_by_delay_chunked_samples(times,
179
+ msec_per_sample=MSEC_PER_SAMPLES)
180
+ else:
181
+ newtimes = times
182
+
183
+ rtn[idx[0]:idx[1] + 1] = newtimes
184
+
185
+ return rtn.astype(int)
186
+
187
+
188
+ def converted_filename(flname):
189
+ """returns path and filename of the converted data file"""
190
+ if flname.endswith(".gz"):
191
+ tmp = flname[:-7]
192
+ else:
193
+ tmp = flname[:-4]
194
+
195
+ path, new_filename = os.path.split(tmp)
196
+ converted_path = os.path.join(path, CONVERTED_SUBFOLDER)
197
+ return converted_path, new_filename + CONVERTED_SUFFIX
198
+
199
+ def convert_raw_data(filepath, method, save_time_adjustments=False,
200
+ keep_delay_variable=False):
201
+ """preprocessing raw pyForceData:
202
+
203
+ """
204
+ # todo only one sensor
205
+ assert(isinstance(method, Method))
206
+
207
+ filepath = os.path.join(os.path.split(sys.argv[0])[0], filepath)
208
+ print("Converting {}".format(filepath))
209
+ print("Method: {}".format(method.description))
210
+
211
+ data, udp_event, daq_events, comments = read_raw_data(filepath)
212
+ print("{} samples".format(len(data["time"])))
213
+
214
+ sensor_id = 1
215
+ if not keep_delay_variable:
216
+ data.pop("delay", None)
217
+
218
+ timestamps = np.array(data["time"]).astype(int)
219
+
220
+ #pauses
221
+ pauses_idx = _pauses_idx_from_timeline(timestamps, pause_criterion=PAUSE_CRITERION)
222
+ evt_periods = _periods_from_daq_events(daq_events)
223
+
224
+ if len(pauses_idx) != len(evt_periods[sensor_id]):
225
+ raise RuntimeError("Pauses in DAQ events do not match recording pauses")
226
+ else:
227
+ data["time"] = _adjusted_timestamps(timestamps=timestamps,
228
+ pauses_idx=pauses_idx,
229
+ evt_periods=evt_periods[
230
+ sensor_id],
231
+ method=method)
232
+
233
+ if save_time_adjustments:
234
+ data["time_adjustment"] = timestamps-data["time"]
235
+ #print("Time difference historgram")
236
+ #print_histogram(data["time_adjustment"])
237
+
238
+ #save
239
+ folder, new_filename = converted_filename(filepath)
240
+ try:
241
+ os.makedirs(folder)
242
+ except:
243
+ pass
244
+ new_filename = os.path.join(folder, new_filename)
245
+
246
+ with gzip.open(new_filename, "wt") as fl:
247
+ fl.write(comments.strip() + "\n")
248
+ fl.write(data_frame_to_text(data))
249
+
250
+ def get_all_data_files(folder):
251
+ rtn = []
252
+ for flname in os.listdir(folder):
253
+ if (flname.endswith(".csv") or flname.endswith(".csv.gz")) and not \
254
+ flname.endswith(CONVERTED_SUFFIX):
255
+ flname = os.path.join(folder, flname)
256
+ rtn.append(flname)
257
+ return rtn
258
+
259
+ def get_all_unconverted_data_files(folder):
260
+ rtn = []
261
+ files = get_all_data_files(folder)
262
+
263
+
264
+ try: # make subfolder
265
+ c_path, _ = converted_filename(files[0])
266
+ converted_files = os.listdir(c_path)
267
+ except:
268
+ converted_files = []
269
+
270
+ for flname in files:
271
+ _, c_flname = converted_filename(flname)
272
+ if c_flname not in converted_files:
273
+ rtn.append(flname)
274
+ return rtn
275
+