pyForceDAQ 2.0.1.dev1__py3-none-any.whl → 2.0.2.dev0__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 (36) hide show
  1. pyforcedaq/__init__.py +2 -7
  2. pyforcedaq/__main__.py +10 -3
  3. pyforcedaq/{force → _lib}/_log.py +2 -1
  4. pyforcedaq/_lib/clock.py +40 -0
  5. pyforcedaq/{force → _lib}/data_recorder.py +82 -86
  6. pyforcedaq/_lib/lsl.py +61 -42
  7. pyforcedaq/_lib/misc.py +11 -13
  8. pyforcedaq/_lib/polling_time_profile.py +10 -8
  9. pyforcedaq/_lib/sensor.py +107 -0
  10. pyforcedaq/{force → _lib}/sensor_process.py +60 -40
  11. pyforcedaq/_lib/settings.py +211 -0
  12. pyforcedaq/_lib/types.py +40 -86
  13. pyforcedaq/_lib/udp_connection.py +13 -19
  14. pyforcedaq/daq/__init__.py +1 -1
  15. pyforcedaq/daq/_mock_sensor.py +9 -8
  16. pyforcedaq/daq/_use_nidaqmx.py +3 -1
  17. pyforcedaq/daq/_use_pydaqmx.py +2 -1
  18. pyforcedaq/force.py +11 -0
  19. pyforcedaq/gui/__init__.py +2 -2
  20. pyforcedaq/gui/_gui_status.py +50 -133
  21. pyforcedaq/gui/_layout.py +5 -7
  22. pyforcedaq/gui/_run.py +69 -172
  23. pyforcedaq/gui/launcher.py +51 -184
  24. {pyforcedaq-2.0.1.dev1.dist-info → pyforcedaq-2.0.2.dev0.dist-info}/METADATA +2 -2
  25. pyforcedaq-2.0.2.dev0.dist-info/RECORD +38 -0
  26. {pyforcedaq-2.0.1.dev1.dist-info → pyforcedaq-2.0.2.dev0.dist-info}/WHEEL +1 -1
  27. pyforcedaq/_lib/timer.py +0 -45
  28. pyforcedaq/daq/config.py +0 -13
  29. pyforcedaq/extras/expyriment_daq_control.py +0 -246
  30. pyforcedaq/extras/opensesame_daq_control.py +0 -280
  31. pyforcedaq/extras/remote_control.py +0 -93
  32. pyforcedaq/force/__init__.py +0 -13
  33. pyforcedaq/force/sensor.py +0 -196
  34. pyforcedaq/gui/_settings.py +0 -111
  35. pyforcedaq-2.0.1.dev1.dist-info/RECORD +0 -42
  36. {pyforcedaq-2.0.1.dev1.dist-info → pyforcedaq-2.0.2.dev0.dist-info}/entry_points.txt +0 -0
pyforcedaq/__init__.py CHANGED
@@ -7,9 +7,8 @@ launch the GUI force from your Python program:
7
7
  ``
8
8
  from pyforcedaq import gui
9
9
 
10
- gui.run(remote_control=False,
11
- ask_filename=True,
12
- calibration_file="FT_sensor1.cal")
10
+ gui.run(ask_filename=True,
11
+ calibration_file="FT_sensor1.cal") TODO
13
12
  ``
14
13
 
15
14
 
@@ -18,10 +17,6 @@ import relevant stuff to program your own force:
18
17
  from pyforcedaq import force
19
18
  ``
20
19
 
21
- import relevant stuff for remote control of the GUI force:
22
- ``
23
- from pyforcedaq import remote_control
24
- ``
25
20
 
26
21
  For function to support data handling see the folder pyForceDAQ/analysis
27
22
 
pyforcedaq/__main__.py CHANGED
@@ -24,17 +24,24 @@ def cli():
24
24
  help="Run with launcher GUI to edit settings and start recording",
25
25
  )
26
26
 
27
+ parser.add_argument(
28
+ "-o", "--omit-launcher",
29
+ action="store_true",
30
+ default=False,
31
+ help="Omit launcher GUI and start recording directly",
32
+ )
33
+
27
34
  args = parser.parse_args()
28
35
 
29
- if args.launcher:
36
+ if not args.omit_launcher:
30
37
  if len(args.SETTINGS_FILE) > 0:
31
38
  print("Can't use launcher and settings file")
32
39
  exit()
33
40
 
34
41
  from .gui import launcher
35
- return launcher.run()
42
+ return launcher.run_launcher()
36
43
  else:
37
- gui.run_settings(args.SETTINGS_FILE)
44
+ gui.run_settings_file(args.SETTINGS_FILE)
38
45
 
39
46
 
40
47
 
@@ -1,6 +1,7 @@
1
+ import logging
1
2
  import os
2
3
  import sys
3
- import logging
4
+
4
5
 
5
6
  def set_logging(data_directory, log_file):
6
7
  base_dir = os.path.split(sys.argv[0])[0]
@@ -0,0 +1,40 @@
1
+ """A high-resolution monotonic timer based on LSL's local_clock() function.
2
+ """
3
+
4
+
5
+ from time import sleep
6
+
7
+ from pylsl import local_clock
8
+
9
+
10
+ def local_clock_ms():
11
+ """Returns the current time in milliseconds, based on LSL's local_clock()"""
12
+ return local_clock() * 1000
13
+
14
+ def wait_ms(waiting_time: int | float, looptime : int = 200) -> None:
15
+ """Wait for a certain amount of milliseconds.
16
+ """
17
+ start = local_clock_ms()
18
+ if waiting_time > looptime:
19
+ sleep((waiting_time - looptime) / 1000)
20
+ while local_clock_ms() < start + waiting_time:
21
+ pass
22
+
23
+ class StopWatch(object):#
24
+ """A simple timer"""
25
+
26
+ def __init__(self):
27
+ self._init_time = local_clock()
28
+
29
+ def reset_stopwatch(self):
30
+ """Reset the stopwatch time to zero.
31
+ """
32
+ self._init_time = local_clock()
33
+
34
+ @property
35
+ def time(self) -> float:
36
+ return local_clock() - self._init_time
37
+
38
+ @property
39
+ def time_ms(self) -> float:
40
+ return self.time * 1000
@@ -7,15 +7,21 @@ __author__ = "Oliver Lindemann"
7
7
  import atexit
8
8
  import gzip
9
9
  import logging
10
+ from fileinput import filename
11
+ from io import TextIOWrapper
10
12
  from pathlib import Path
11
13
  from time import asctime, localtime, strftime
14
+ from typing import List
12
15
 
13
- from pyforcedaq._lib import timer
16
+ from icecream import ic
14
17
 
15
18
  from .. import __version__ as forceDAQVersion
16
- from .._lib.process_priority_manager import ProcessPriorityManager
17
- from .._lib.timer import app_clock
18
- from .._lib.types import (
19
+ from . import _log
20
+ from .clock import wait_ms
21
+ from .process_priority_manager import ProcessPriorityManager
22
+ from .sensor_process import SensorProcess
23
+ from .settings import RecordingSettings, SensorSettings
24
+ from .types import (
19
25
  TAG_COMMENTS,
20
26
  TAG_DAQEVENT,
21
27
  TAG_UDPDATA,
@@ -24,20 +30,19 @@ from .._lib.types import (
24
30
  PollingPriority,
25
31
  UDPData,
26
32
  )
27
- from .._lib.types import GUIRemoteControlCommands as RemoteCmd
28
- from .._lib.udp_connection import UDPConnectionProcess
29
- from .sensor import SensorSettings
30
- from .sensor_process import SensorProcess
33
+ from .udp_connection import UDPConnectionProcess
34
+
35
+ _log.set_logging(data_directory="data", log_file="recording.log")
31
36
 
32
37
  NEWLINE = "\n"
33
38
 
34
39
  class DataRecorder(object):
35
40
  """handles multiple sensors and udp connection"""
36
41
 
37
- def __init__(self, force_sensor_settings:SensorSettings | list,
38
- poll_udp_connection=False,
39
- write_deviceid = False,
40
- polling_priority:str | None = None):
42
+ def __init__(self,
43
+ recording_settings: RecordingSettings,
44
+ force_sensor_settings:SensorSettings | List[SensorSettings],
45
+ poll_udp_connection: bool = False):
41
46
 
42
47
 
43
48
  """queue_data will be saved
@@ -47,13 +52,10 @@ class DataRecorder(object):
47
52
  {REALTIME} or {NORMAL} or None
48
53
  """
49
54
 
50
- self._write_deviceid = write_deviceid
51
-
52
55
  if not isinstance(force_sensor_settings, list):
53
56
  force_sensor_settings = [force_sensor_settings]
54
- # write forces and triggers as define in sensor1
55
- self._write_forces = force_sensor_settings[0].array_write_forces()
56
- self._write_trigger = force_sensor_settings[0].array_write_trigger()
57
+
58
+ self.recording_settings = recording_settings
57
59
 
58
60
  # create sensor processes
59
61
  self._force_sensor_processes =[]
@@ -62,7 +64,8 @@ class DataRecorder(object):
62
64
  if not isinstance(fs, SensorSettings):
63
65
  raise RuntimeError("Recorder needs a list of Force Sensor Settings!")
64
66
  else:
65
- fst = SensorProcess(settings = fs,
67
+ fst = SensorProcess(sensor_settings = fs,
68
+ recording_settings=recording_settings,
66
69
  pipe_buffered_data_after_pause=True)
67
70
  fst.start()
68
71
  event_trigger.append(fst.event_trigger)
@@ -70,20 +73,19 @@ class DataRecorder(object):
70
73
 
71
74
  # create udp connection process
72
75
  if poll_udp_connection:
73
- self.udp = UDPConnectionProcess(event_trigger=event_trigger,
74
- event_ignore_tag = RemoteCmd.COMMAND_STR)
76
+ self.udp = UDPConnectionProcess(event_trigger=event_trigger)
75
77
  self.udp.start()
76
78
  else:
77
79
  self.udp = None
78
80
 
79
- # process managing
81
+ # process managing FIYME needed?
80
82
  self._proc_manager = ProcessPriorityManager()
81
83
  self._proc_manager.add_subprocess(self.udp)
82
84
  self._proc_manager.add_subprocess(self._force_sensor_processes)
83
- if polling_priority is not None:
85
+ if self.recording_settings.priority is not None:
84
86
  level = PollingPriority.NORMAL
85
87
  else:
86
- level = PollingPriority.get_priority(polling_priority)
88
+ level = PollingPriority.get_priority(self.recording_settings.priority)
87
89
  self._proc_manager.set_subprocess_priorities(level=level, disable_gc=False)
88
90
 
89
91
  logging.info("Main process priority: %s", self._proc_manager.get_main_priority())
@@ -92,9 +94,13 @@ class DataRecorder(object):
92
94
  self._is_recording = False
93
95
  self._file = None
94
96
  self._daq_event = []
95
- self.filename: str | None = None
97
+ self.path_open_file = Path("")
96
98
  atexit.register(self.quit)
97
99
 
100
+ @property
101
+ def is_saving_data(self):
102
+ """Property indicates whether a data file is open"""
103
+ return self._file is not None
98
104
 
99
105
  @property
100
106
  def is_alive(self):
@@ -149,16 +155,16 @@ class DataRecorder(object):
149
155
  buffer = []
150
156
  while True:
151
157
  try:
152
- data = self.udp.receive_queue.get_nowait()
153
- except:
158
+ data = self.udp.receive_queue.get_nowait() # type: ignore
159
+ except AttributeError:
154
160
  # until queue empty or no udp connection
155
161
  break
156
162
  buffer.append(data)
157
163
  if len(buffer)>0:
158
- self._save_data(buffer)
164
+ self._write_data(buffer)
159
165
  return buffer
160
166
 
161
- def _save_data(self, data_buffer: list,
167
+ def _write_data(self, data_buffer: list,
162
168
  recording_screen=None,
163
169
  float_decimal_places: int = 4) -> None:
164
170
  """ writes data to disk and set counters
@@ -168,18 +174,20 @@ class DataRecorder(object):
168
174
  #DOC output format
169
175
 
170
176
  BLOCKSIZE = 10000 # for recording screen feedback only
171
-
177
+ write_forces = self.recording_settings.array_write_forces()
178
+ write_trigger = self.recording_settings.array_write_trigger()
179
+ write_deviceid = len(self.recording_settings.device_labels)>1
172
180
  float_format = "{0:." + str(float_decimal_places) + "f},"
173
181
  buffer_len = len(data_buffer)
174
182
  for c, d in enumerate(data_buffer):
175
183
  if self._file is not None:
176
184
  if isinstance(d, ForceSensorData):
177
185
  line = f"{d.time}, {d.acquisition_delay},"
178
- if self._write_deviceid:
179
- line += f"{d.device_id},"
180
- for x in d.selected_forces(select=self._write_forces):
186
+ if write_deviceid:
187
+ line += f"{d.sensor_id},"
188
+ for x in d.selected_forces(select=write_forces):
181
189
  line += float_format.format(x)
182
- for x in d.selected_trigger(select=self._write_trigger):
190
+ for x in d.selected_trigger(select=write_trigger):
183
191
  if isinstance(x, int):
184
192
  line += f"{x},"
185
193
  else:
@@ -190,8 +198,7 @@ class DataRecorder(object):
190
198
  self._file_write(f"{TAG_DAQEVENT},{d.time},{str(d.code)}" + NEWLINE)
191
199
 
192
200
  elif isinstance(d, UDPData):
193
- if not d.is_remote_control_command:
194
- self._file_write(f"{TAG_UDPDATA},{d.time},{d.unicode}" + NEWLINE)
201
+ self._file_write(f"{TAG_UDPDATA},{d.time},{d.unicode}" + NEWLINE)
195
202
 
196
203
  if recording_screen is not None and c % BLOCKSIZE == 0:
197
204
  recording_screen.stimulus(
@@ -199,16 +206,19 @@ class DataRecorder(object):
199
206
  buffer_len//BLOCKSIZE)).present()
200
207
 
201
208
  def _file_write(self, s: str) -> None:
202
- self._file.write(s.encode())
203
209
 
204
- def save_daq_event(self, code, time: float | None = None) -> None:
210
+ if isinstance(self._file, gzip.GzipFile):
211
+ self._file.write(s.encode("utf-8"))
212
+ elif isinstance(self._file, TextIOWrapper):
213
+ self._file.write(s)
214
+
215
+
216
+ def store_daq_event(self, code: str | int | float, time: float | None = None) -> None:
205
217
  """Set marker code in file
206
218
 
207
219
  DAQEvent will be timestamps and occur in the data output
208
220
 
209
221
  """
210
- if time is None:
211
- time = app_clock.time
212
222
  self._daq_event.append(DAQEvents(time = time, code = code))
213
223
 
214
224
 
@@ -250,19 +260,19 @@ class DataRecorder(object):
250
260
  for fsp in self._force_sensor_processes:
251
261
  fsp.pause_polling()
252
262
 
253
- timer.wait(500)
263
+ wait_ms(500)
254
264
 
255
265
  # get data
256
266
  for fsp in self._force_sensor_processes:
257
267
  buffer = fsp.get_buffer()
258
- self._save_data(buffer, recording_screen)
268
+ self._write_data(buffer, recording_screen)
259
269
  data.extend(buffer)
260
270
 
261
271
  # udp event
262
272
  data.extend(self.process_and_write_udp_events())
263
273
 
264
274
  # soft trigger
265
- self._save_data(self._daq_event)
275
+ self._write_data(self._daq_event)
266
276
  data.extend(self._daq_event)
267
277
  self._daq_event = []
268
278
  return data
@@ -288,12 +298,11 @@ class DataRecorder(object):
288
298
  for x in self._force_sensor_processes:
289
299
  x.event_bias_is_available.wait()
290
300
 
291
- def open_data_file(self, filename: str,
301
+ def open_data_file(self,
302
+ filename: str | Path,
292
303
  subdirectory: str = "data",
293
- time_stamp_filename: bool = False,
294
304
  varnames: bool = True,
295
- comment_line: str = "",
296
- zipped: bool = False) -> Path:
305
+ comment_line: str = "") -> Path:
297
306
  """Create a data file
298
307
 
299
308
  Only if data file has been opened, data will be saved!
@@ -311,9 +320,6 @@ class DataRecorder(object):
311
320
  write variable names in first line of data output
312
321
  comment_line : string, optional
313
322
  add some comments at the beginning of the data output file
314
- zippers : boolean, optional
315
- are the data zipped or not. Note: Saving zipped data after pause
316
- takes much longer.
317
323
 
318
324
  Returns
319
325
  -------
@@ -321,68 +327,57 @@ class DataRecorder(object):
321
327
  full path the actually used file (incl. timestamp)
322
328
 
323
329
  """
324
- data_dir = Path.cwd() / subdirectory
325
- data_dir.mkdir(exist_ok=True)
326
330
  self.close_data_file()
327
331
 
328
- if filename is None or len(filename) == 0:
329
- filename = "daq_recording.csv"
330
-
331
- if zipped:
332
- suffix = ".gz"
332
+ # create filename
333
+ data_dir = Path.cwd() / subdirectory
334
+ data_dir.mkdir(exist_ok=True)
335
+ if self.recording_settings.zip_data:
336
+ filename = Path(filename).with_suffix(".csv.gz")
333
337
  else:
334
- suffix = ""
338
+ filename = Path(filename).with_suffix(".csv")
335
339
 
336
- cnt = 0
337
340
  while True:
338
- flname = filename
339
- if cnt>0:
340
- x = flname.find(".")
341
- if x<0:
342
- x = len(flname)
343
- flname = flname[:x] + "_{0}".format(cnt) + flname[x:]
344
-
345
- if time_stamp_filename:
346
- self.filename = flname + "_" + \
347
- strftime("%Y%m%d%H%M", localtime()) + suffix
348
- else:
349
- self.filename = flname + suffix
350
-
351
- full_path_file = data_dir / self.filename
352
- if full_path_file.is_file():
341
+ self.path_open_file = data_dir / filename
342
+ if self.path_open_file.is_file():
353
343
  # print "data file already exists, adding counter"
354
- cnt += 1
344
+ filename = Path(filename.stem + "_" + strftime("%m%d%H%M", localtime()) + \
345
+ filename.suffix)
355
346
  else:
356
347
  break
357
348
 
358
- if zipped:
359
- self._file = gzip.open(full_path_file, 'w+')
349
+ if self.recording_settings.zip_data:
350
+ self._file = gzip.open(self.path_open_file, 'w')
360
351
  else:
361
- self._file = open(full_path_file, 'w+')
362
- print("Data file: {}".format(full_path_file))
352
+ self._file = open(self.path_open_file, 'w')
353
+ print("Data file: {}".format(self.path_open_file))
363
354
 
364
355
  self._file_write(TAG_COMMENTS + "Recorded at {0} with pyForceDAQ {1}\n".format(
365
356
  asctime(localtime()), forceDAQVersion))
366
- logging.info("new file: {}".format(filename))
357
+ logging.info("new file: {}".format(self.path_open_file))
367
358
 
368
359
  for s in self.sensor_settings_list:
369
- txt = " Sensor: id={0}, name={1}, cal-file={2}\n".format(s.device_id,
370
- s.sensor_name, s.calibration_file)
360
+ txt = f" Sensor: label={s.device_label}, cal-file={s.calibration_file}\n"
371
361
  self._file_write(TAG_COMMENTS + txt)
372
362
 
373
363
  if len(comment_line)>0:
374
364
  self._file_write(TAG_COMMENTS + comment_line + "\n")
365
+
375
366
  if varnames:
367
+ write_forces = self.recording_settings.array_write_forces()
368
+ write_trigger = self.recording_settings.array_write_trigger()
369
+ write_deviceid = len(self.recording_settings.device_labels)>1
376
370
  line = "time,delay,"
377
- if self._write_deviceid: line += "device_tag,"
371
+ if write_deviceid:
372
+ line += "device_tag,"
378
373
  for x in range(6):
379
- if self._write_forces[x]:
374
+ if write_forces[x]:
380
375
  line += ForceSensorData.forces_names[x] + ","
381
- if self._write_trigger[0]: line += "trigger1,"
382
- if self._write_trigger[1]: line += "trigger2,"
376
+ if write_trigger[0]: line += "trigger1,"
377
+ if write_trigger[1]: line += "trigger2,"
383
378
  self._file_write(line[:-1] + NEWLINE)
384
379
 
385
- return full_path_file
380
+ return self.path_open_file
386
381
 
387
382
  def close_data_file(self) -> None:
388
383
  """Close the data file
@@ -394,3 +389,4 @@ class DataRecorder(object):
394
389
  if self._file is not None:
395
390
  self._file.close()
396
391
  self._file = None
392
+ self.path_open_file = Path("")
pyforcedaq/_lib/lsl.py CHANGED
@@ -12,45 +12,64 @@ from pylsl import (
12
12
  )
13
13
 
14
14
 
15
- def init(
16
- name: str,
17
- n_channels: int,
18
- stream_id: str,
19
- freq: int,
20
- channel_format: int,
21
- metadata: dict | None = None,
22
- ) -> StreamOutlet:
23
- """
24
- Initialise a LSL stream
25
-
26
- Args:
27
- name: name of the stream
28
- n_channels: number of channels per sample
29
- channel_format: format/type of each channel (ex: string, int, ...)
30
- same format for each channel
31
- stream_id: unique identifier of the stream
32
- content_type: content type of stream. By convention LSL uses the content
33
- types defined in the XDF file format specification where
34
- applicable
35
- freq: sampling rate in Hz
36
-
37
- Return:
38
- outlet: StreamOulet to push samples with LSL
39
- """
40
- # LSL
41
- # StreamInfo takes name t
42
- info = StreamInfo(name, "force",
43
- channel_count=n_channels,
44
- nominal_srate=freq,
45
- channel_format=channel_format,
46
- source_id=stream_id)
47
-
48
- # Check if there is metadata to add to the lsl stream
49
- if metadata:
50
- # Get xml object of the stream created earlier
51
- xml_info = info.desc()
52
- # Add meta data to xml object
53
- for key, data in metadata.items():
54
- xml_info.append_child_value(key, str(data))
55
-
56
- return StreamOutlet(info)
15
+ class LSLSream():
16
+
17
+ def __init__(self):
18
+ self.outlet = None
19
+ self._is_init = False
20
+
21
+ @property
22
+ def is_init(self):
23
+ return self._is_init
24
+
25
+ def init(self,
26
+ name: str,
27
+ n_channels: int,
28
+ stream_id: str,
29
+ freq: int,
30
+ channel_format: int,
31
+ metadata: dict | None = None,
32
+ ):
33
+ """
34
+ Initialise a LSL stream
35
+
36
+ Args:
37
+ name: name of the stream
38
+ n_channels: number of channels per sample
39
+ channel_format: format/type of each channel (ex: string, int, ...)
40
+ same format for each channel
41
+ stream_id: unique identifier of the stream
42
+ content_type: content type of stream. By convention LSL uses the content
43
+ types defined in the XDF file format specification where
44
+ applicable
45
+ freq: sampling rate in Hz
46
+
47
+ Return:
48
+ outlet: StreamOulet to push samples with LSL
49
+ """
50
+ if self._is_init:
51
+ return
52
+
53
+ info = StreamInfo(name, "force",
54
+ channel_count=n_channels,
55
+ nominal_srate=freq,
56
+ channel_format=channel_format,
57
+ source_id=stream_id)
58
+
59
+ # Check if there is metadata to add to the lsl stream
60
+ if metadata:
61
+ # Get xml object of the stream created earlier
62
+ xml_info = info.desc()
63
+ # Add meta data to xml object
64
+ for key, data in metadata.items():
65
+ xml_info.append_child_value(key, str(data))
66
+
67
+ self._is_init = True
68
+ self.outlet = StreamOutlet(info)
69
+
70
+ def push_sample(self, sample: list):
71
+ """Push a sample to the LSL stream if it is initialized."""
72
+ if not self._is_init:
73
+ # Don't do anything
74
+ return
75
+ self.outlet.push_sample(sample) # type: ignore
pyforcedaq/_lib/misc.py CHANGED
@@ -1,6 +1,4 @@
1
- from os import listdir, path
2
-
3
- from .timer import get_time_ms
1
+ from .clock import local_clock_ms
4
2
 
5
3
 
6
4
  def N2g(N):
@@ -9,9 +7,9 @@ def N2g(N):
9
7
 
10
8
  class MinMaxDetector(object):
11
9
 
12
- def __init__(self, start_value, duration):
10
+ def __init__(self, start_value, duration_ms):
13
11
  self._minmax = [start_value, start_value]
14
- self._duration = duration
12
+ self._duration_ms = duration_ms
15
13
  self._level_change_time = None
16
14
 
17
15
  def process(self, value):
@@ -19,7 +17,7 @@ class MinMaxDetector(object):
19
17
  level change has occurred, otherwise None"""
20
18
 
21
19
  if self._level_change_time is not None:
22
- if (get_time_ms() - self._level_change_time) >= self._duration:
20
+ if (local_clock_ms() - self._level_change_time) >= self._duration_ms:
23
21
  return tuple(self._minmax)
24
22
 
25
23
  if value > self._minmax[1]:
@@ -28,7 +26,7 @@ class MinMaxDetector(object):
28
26
  self._minmax[0] = value
29
27
 
30
28
  elif self._minmax[0] != value: # level change just occurred
31
- self._level_change_time = get_time_ms()
29
+ self._level_change_time = local_clock_ms()
32
30
  return self.process(value)
33
31
 
34
32
  return None
@@ -37,12 +35,12 @@ class MinMaxDetector(object):
37
35
  def is_sampling_for_minmax(self):
38
36
  """true true if currently sampling for minmax"""
39
37
  return (self._level_change_time is not None) and \
40
- (get_time_ms() - self._level_change_time) < self._duration
38
+ (local_clock_ms() - self._level_change_time) < self._duration_ms
41
39
 
42
- # def find_calibration_file(calibration_folder: str, sensor_name: str,
40
+ # def find_calibration_file(calibration_folder: str, device_label: str,
43
41
  # calibration_suffix=".cal") -> str:
44
42
 
45
- # needle = 'Serial="{0}"'.format(sensor_name)
43
+ # needle = 'Serial="{0}"'.format(device_label)
46
44
  # calibration_files = []
47
45
  # for x in listdir(path.abspath(calibration_folder)):
48
46
  # filename = path.join(calibration_folder, x)
@@ -51,18 +49,18 @@ class MinMaxDetector(object):
51
49
  # for l in fl:
52
50
  # if l.find(needle)>0:
53
51
  # print("Found calibration file for sensor '{0}' : {1}.".format(
54
- # sensor_name, filename))
52
+ # device_label, filename))
55
53
  # calibration_files.append(filename)
56
54
 
57
55
  # if len(calibration_files) == 1:
58
56
  # return calibration_files[0]
59
57
  # elif len(calibration_files) > 1:
60
- # print("Multiple calibration files found for sensor '{0}'".format(sensor_name))
58
+ # print("Multiple calibration files found for sensor '{0}'".format(device_label))
61
59
  # for f in calibration_files:
62
60
  # print(" - {0}".format(f))
63
61
  # print("Please ensure that only one calibration file exists for each sensor")
64
62
  # else:
65
- # print("No calibration file found for sensor '{0}'.".format(sensor_name))
63
+ # print("No calibration file found for sensor '{0}'.".format(device_label))
66
64
  # exit()
67
65
 
68
66
  #Sensor History with moving average filtering and distance, velocity
@@ -1,26 +1,28 @@
1
1
  import numpy as np
2
2
 
3
- from .timer import get_time_ms
3
+ from .clock import local_clock
4
4
 
5
5
 
6
6
  class PollingTimeProfile(object):
7
7
 
8
- def __init__(self, timing_range=10):
8
+ def __init__(self, timing_range_ms=10):
9
9
  self._last = None
10
- self._timing_range = 10
10
+ self._timing_range_ms = 10
11
11
  self._zero_cnt = 0
12
12
 
13
13
  #self._zero_time_polling_frequency = {}
14
- self.profile_frequency = np.array([0] * (timing_range + 1))
14
+ self.profile_frequency = np.array([0] * (timing_range_ms + 1))
15
15
 
16
16
  def stop(self):
17
17
  self._last = None
18
18
 
19
- def update(self, time_ms):
19
+ def update(self, time: float):
20
+
21
+ time_ms = int(time * 1000)
20
22
  if self._last is not None:
21
23
  d = time_ms - self._last
22
- if d > self._timing_range:
23
- d = self._timing_range
24
+ if d > self._timing_range_ms:
25
+ d = self._timing_range_ms
24
26
  self.profile_frequency[d] += 1
25
27
 
26
28
  #if d == 0:
@@ -35,7 +37,7 @@ class PollingTimeProfile(object):
35
37
  self._last = time_ms
36
38
 
37
39
  def tick(self):
38
- self.update(get_time_ms())
40
+ self.update(local_clock())
39
41
 
40
42
  @property
41
43
  def profile_percent(self):