Skip to content

Commit 1683af7

Browse files
author
Samuel Hoermann
committed
Merge branch 'develop'
2 parents 2dda736 + 7cce1e3 commit 1683af7

File tree

6 files changed

+125
-75
lines changed

6 files changed

+125
-75
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ packages = ["src/ADScopeControl"]
1010

1111
[project]
1212
name = "PyADScopeControl"
13-
version = "1.1.8"
13+
version = "1.2.0"
1414
authors = [
1515
{ name="Christoph Schmidt", email="cschmidt.fs@gmail.com" },
1616
]

src/ADScopeControl/controller/BaseADScopeController.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from numpy import ndarray
1313

1414
from ADScopeControl.controller.mp_AD2Capture.MPCaptDevice import MPCaptDevice
15+
from ADScopeControl.controller.sweepHelpers import ramp
1516
from ADScopeControl.model.AD2ScopeModel import AD2ScopeModel
1617
from ADScopeControl.model.AD2Constants import AD2Constants
1718

@@ -315,6 +316,9 @@ def create_dataframe(self):
315316
columns=['Amplitude']))
316317

317318
self.set_recorded_data_time_axis()
319+
self.model.capturing_information.recorded_samples_df = self.model.supervisor_information.process_capture(
320+
self.model.capturing_information.recorded_samples_df
321+
)
318322

319323
def stop_capture(self):
320324
self.start_capture_flag.value = 0
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import numpy as np
2+
from numpy import ndarray
3+
4+
def ramp(
5+
t: ndarray,
6+
distance: float,
7+
speed: float,
8+
acceleration: float,
9+
deceleration: float = None
10+
) -> ndarray:
11+
if deceleration is None:
12+
deceleration = acceleration
13+
assert acceleration > 0
14+
assert deceleration > 0
15+
16+
distanceSign = np.sign(distance)
17+
absDistance = np.abs(distance)
18+
19+
# Calculate distance covered during acceleration
20+
timeToSpeed = speed / acceleration
21+
distanceAcceleration = 0.5 * speed * timeToSpeed
22+
23+
# Calculate distance covered during deceleration
24+
timeToBreak = speed / deceleration
25+
distanceDeceleration = 0.5 * speed * timeToBreak
26+
27+
# Calculate distance covered during constant speed
28+
distanceAtSpeed = absDistance - distanceAcceleration - distanceDeceleration
29+
assert distanceAtSpeed >= 0 # TODO: if smaller than zero, sweep is incomplete and
30+
# no constant speed is possible -> calculate when to switch to deceleration
31+
timeAtSpeed = distanceAtSpeed / speed
32+
33+
# Calculate total time
34+
totalTime = timeToSpeed + timeAtSpeed + timeToBreak
35+
36+
# conform measured time array to expected sweep duration
37+
dt = t[-1] - t[0]
38+
assert dt > 0
39+
_t = (t - t[0]) * totalTime / dt
40+
41+
# Create masks
42+
accMsk = _t < timeToSpeed
43+
spdMsk = (_t >= timeToSpeed) & (_t < timeToSpeed + timeAtSpeed)
44+
decMsk = _t >= (timeToSpeed + timeAtSpeed)
45+
46+
# Query time values
47+
netAccTimes = _t[accMsk]
48+
netDecTimes = _t[decMsk] - timeToSpeed - timeAtSpeed
49+
netAtSpeedTimes = _t[spdMsk] - timeToSpeed
50+
51+
# Query distance values
52+
return distanceSign * np.r_[
53+
netAccTimes**2 / 2 * acceleration,
54+
distanceAcceleration + netAtSpeedTimes * speed,
55+
distanceAcceleration + distanceAtSpeed + speed * netDecTimes - 0.5 * deceleration * netDecTimes ** 2,
56+
]

src/ADScopeControl/model/AD2ScopeModel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def dwf_version(self) -> str:
121121
return self._dwf_version
122122

123123
@dwf_version.setter
124-
def dwf_version(self, value: Array or str):
124+
def dwf_version(self, value: Array | str):
125125
if not isinstance(value, str):
126126
self._dwf_version = str(value.value.decode('UTF-8'))
127127
else:

src/ADScopeControl/model/submodels/AD2CaptDeviceSupervisorModel.py

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ def __init__(self):
2828
self._supervisor: object = None
2929
self._supervisor_model: object = None
3030

31-
self._wl_sweep_start: float = 0.0
32-
self._wl_sweep_stop: float = 0.0
33-
self._velocity: float = 0.0
34-
self._acceleration: float = 0.0
35-
self._deceleration: float = 0.0
36-
3731
# ==================================================================================================================
3832
# Connected Device Information
3933
# ==================================================================================================================
@@ -75,43 +69,9 @@ def supervisor_model(self, value: object):
7569
self._supervisor_model = value
7670
self.signals.supervisor_model_changed.emit()
7771

78-
@property
79-
def sweep_start_wavelength(self) -> float:
80-
return self._wl_sweep_start
81-
82-
@sweep_start_wavelength.setter
83-
def sweep_start_wavelength(self, value: float):
84-
self._wl_sweep_start = value
85-
86-
87-
@property
88-
def sweep_stop_wavelength(self) -> float:
89-
return self._wl_sweep_stop
90-
91-
@sweep_stop_wavelength.setter
92-
def sweep_stop_wavelength(self, value: float):
93-
self._wl_sweep_stop = value
94-
95-
@property
96-
def velocity(self) -> float:
97-
return self._velocity
98-
99-
@velocity.setter
100-
def velocity(self, value: float):
101-
self._velocity = value
102-
103-
@property
104-
def acceleration(self) -> float:
105-
return self._acceleration
106-
107-
@acceleration.setter
108-
def acceleration(self, value: float):
109-
self._acceleration = value
110-
111-
@property
112-
def deceleration(self) -> float:
113-
return self._deceleration
114-
115-
@deceleration.setter
116-
def deceleration(self, value: float):
117-
self._deceleration = value
72+
def process_capture(self, data):
73+
try:
74+
data = self.supervisor.process_capture(data)
75+
except Exception as e:
76+
self.logger.error(f"Error while processing capture by supervisor: {e}")
77+
return data

src/ADScopeControl/view/AD2CaptDeviceView.py

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pyqtgraph as pg
77
from PySide6.QtCore import QTimer
88
from PySide6.QtGui import QAction, QIcon, QPixmap
9-
from PySide6.QtWidgets import QMainWindow, QStatusBar, QMenu, QToolButton, QMessageBox
9+
from PySide6.QtWidgets import QMainWindow, QStatusBar, QMenu, QToolButton, QMessageBox, QFileDialog
1010
from WidgetCollection.Dialogs import AboutDialog
1111

1212
from pyqtgraph.dockarea import DockArea, Dock
@@ -25,7 +25,31 @@
2525
# get the version of the current module, given in the pyproject.toml
2626

2727

28+
def downsample_data(data, num_points):
29+
"""
30+
Downsample the data by selecting points at equal intervals.
2831
32+
Args:
33+
data (list): The original data list.
34+
num_points (int): The desired number of data points.
35+
36+
Returns:
37+
list: The downsampled data list.
38+
"""
39+
if num_points >= len(data):
40+
return data
41+
42+
interval = len(data) // num_points
43+
downsampled_data = [data[i * interval] for i in range(num_points)]
44+
45+
# If the length of the data is not exactly divisible by the interval,
46+
# append the last point to the downsampled data.
47+
if len(data) % num_points != 0:
48+
downsampled_data.append(data[-1])
49+
50+
return downsampled_data
51+
52+
previewDataPoints = 10000
2953

3054
class ControlWindow(QMainWindow):
3155

@@ -78,11 +102,11 @@ def __init__(self, model: AD2ScopeModel, controller: BaseADScopeController):
78102

79103
# Timer for periodically updating the plot
80104
self.capture_update_timer = QTimer()
81-
self.capture_update_timer.setInterval(10)
105+
self.capture_update_timer.setInterval(50)
82106
self.capture_update_timer.timeout.connect(self._on_capture_update_plot)
83107

84108
self.stream_update_timer = QTimer()
85-
self.stream_update_timer.setInterval(40)
109+
self.stream_update_timer.setInterval(50)
86110
self.stream_update_timer.timeout.connect(self._on_stream_update_timer_timeout)
87111

88112
self.autostop_capture = QTimer()
@@ -112,6 +136,11 @@ def _init_menu_bar(self):
112136

113137
self.file_menu.addSeparator()
114138

139+
self.act_save_data = QAction('Save Data', self)
140+
self.act_save_data.triggered.connect(self.save_data)
141+
self.act_save_data.setShortcut('Ctrl+S')
142+
self.file_menu.addAction(self.act_save_data)
143+
115144
self.act_view_data = QAction('View Data', self)
116145
self.act_view_data.triggered.connect(self.view_data)
117146
self.act_view_data.setShortcut('Ctrl+Alt+S')
@@ -130,6 +159,15 @@ def _init_menu_bar(self):
130159
self._ui.menu_file.setMenu(self.file_menu)
131160
self._ui.menu_file.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
132161

162+
def save_data(self):
163+
self.controller.create_dataframe()
164+
165+
# Create a file dialog to save the dataframe
166+
file_path, _ = QFileDialog.getSaveFileName(self, "Save Data", "", "CSV Files (*.csv)")
167+
if file_path:
168+
self.model.capturing_information.recorded_samples_df.to_csv(file_path)
169+
self.status_bar.showMessage(f"Data saved to {file_path}")
170+
133171
def view_data(self):
134172
self.controller.create_dataframe()
135173
pdview(self.model.capturing_information.recorded_samples_df)
@@ -146,7 +184,7 @@ def _init_UI_live_plot(self):
146184
self.scope_original = pg.PlotWidget(title="AD2 Acquisition")
147185
self.scope_original.plotItem.showGrid(x=True, y=True, alpha=1)
148186
d1.addWidget(self.scope_original)
149-
self.scope_original.setYRange(-1.5, 1.5, padding=0)
187+
#self.scope_original.setYRange(-1.5, 1.5, padding=0)
150188

151189
self.scope_captured = pg.PlotWidget(title="Captured Data")
152190
self.scope_captured.plotItem.showGrid(x=True, y=True, alpha=1)
@@ -211,36 +249,17 @@ def _connect_signals(self):
211249
# Supervision Information
212250
self.model.supervisor_information.signals.supervisor_name_changed.connect(self._on_supervisor_name_changed)
213251
self.model.supervisor_information.signals.supervised_changed.connect(self._on_supervised_changed)
214-
self.model.supervisor_information.signals.supervisor_model_changed.connect(self._on_supervised_model_changed)
215-
216-
217252

218253
# ==================================================================================================================
219254
# Slots
220255
# ==================================================================================================================
221-
def _on_supervisor_sweep_start_wavelength_changed(self, sweep_start_wavelength):
222-
print(f"Sweep Start Wavelength: {sweep_start_wavelength}")
223-
224-
def _on_supervisor_sweep_end_wavelength_changed(self, sweep_end_wavelength):
225-
print(f"Sweep End Wavelength: {sweep_end_wavelength}")
226-
227256

228257
def _on_supervisor_name_changed(self, supervisor_name):
229258
self.supervisor_info.supervisor_name = supervisor_name
230259

231260
def _on_supervised_changed(self, supervised):
232261
self.supervisor_info.supervised = supervised
233262

234-
def _on_supervised_model_changed(self):
235-
self.model.supervisor_information.supervisor_model.signals.sweep_start_wavelength_changed.connect(
236-
self._on_supervisor_sweep_start_wavelength_changed
237-
)
238-
239-
self.model.supervisor_information.supervisor_model.signals.sweep_stop_wavelength_changed.connect(
240-
self._on_supervisor_sweep_end_wavelength_changed
241-
242-
)
243-
244263
def _on_dwf_version_changed(self, dwf_version):
245264
"""
246265
Gets called if the DWF version changes. Updates the UI.
@@ -432,9 +451,16 @@ def _on_capture_update_plot(self):
432451
if len(self.model.capturing_information.recorded_samples) > 0:
433452
self.scope_captured.clear()
434453
# print(self.ad2device.recorded_samples)
435-
d = self.model.capturing_information.recorded_samples_preview
436454

437-
self.scope_captured.plot(d, pen=pg.mkPen(width=1))
455+
# Downsample the data
456+
downsampled_data = downsample_data(
457+
self.model.capturing_information.recorded_samples,
458+
previewDataPoints
459+
)
460+
461+
# Plot the downsampled data
462+
self.scope_captured.plot(downsampled_data, pen=pg.mkPen(width=1))
463+
438464
# print(f"Length: {len(self.controller.recorded_sample_stream)}")
439465

440466
#if len(self.controller.status_dqueue) > 0:
@@ -449,8 +475,12 @@ def _on_stream_update_timer_timeout(self):
449475
# print(self.ad2device.recorded_samples)
450476

451477
self.scope_original.plot(
452-
np.array(self.controller.streaming_dqueue), # [::100],
453-
pen=pg.mkPen(width=1))
478+
downsample_data(
479+
np.array(self.controller.streaming_dqueue),
480+
previewDataPoints
481+
),
482+
pen=pg.mkPen(width=1)
483+
)
454484
# self._ui.lcd_unconsumed_stream.display(self.model.capturing_information.unconsumed_stream_samples)
455485

456486
# ============== Connected Device Information

0 commit comments

Comments
 (0)