Skip to content

Commit 2872042

Browse files
Merge pull request #841 from TheDeanLab/828-save-waveform-constants-with-acquisition-and-generate-method-to-load-it
828 save waveform constants with acquisition and generate method to load it
2 parents c1c0d11 + b4aeb15 commit 2872042

File tree

13 files changed

+309
-27
lines changed

13 files changed

+309
-27
lines changed

docs/source/user_guide/gui_walkthrough.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ File
2222

2323
The :guilabel:`File` menu lets a user create, load and save
2424
:ref:`experiment files <user_guide/software_configuration:experiment file>`,
25-
which store the states of the GUI and the hardware. This is useful if a user wants to
25+
which store the states of the GUI and the hardware. The menu also provides an option to load and save :ref:`waveform constants files <user_guide/software_configuration:waveform constants file>` to store the waveform constants used for a given experiment. This is useful if a user wants to
2626
perform an experiment with the same parameters multiple times, but close the software
27-
in between acquisitions. To facilitate reproducibility, an `experiment.yml` file is
28-
always saved with collected image data.
27+
in between acquisitions. To facilitate reproducibility, `experiment.yml` and `waveform_constants.yml` files are
28+
always saved with the collected image data.
2929

3030
The :guilabel:`File` menu also provides access to toggle the :guilabel:`Save Data` flag,
3131
also found under :guilabel:`Timepoint Settings` in the
1.09 KB
Loading

src/navigate/config/waveform_templates.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
"Confocal-Projection": {
77
"repeat": timepoints,
88
"expand": n_plane,
9-
}
9+
},
10+
"Bidirectional": {
11+
"repeat": 1,
12+
"expand": 2,
13+
}
1014
}

src/navigate/controller/controller.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,11 +737,19 @@ def execute(self, command, *args):
737737
return
738738
saving_settings = self.configuration["experiment"]["Saving"]
739739
file_directory = create_save_path(saving_settings)
740+
741+
# Save the experiment.yaml file.
740742
save_yaml_file(
741-
file_directory,
742-
self.configuration["experiment"],
743+
file_directory=file_directory,
744+
content_dict=self.configuration["experiment"],
743745
filename="experiment.yml",
744746
)
747+
748+
# Save the waveform_constants.yaml file.
749+
save_yaml_file(file_directory=file_directory,
750+
content_dict=self.configuration['waveform_constants'],
751+
filename="waveform_constants.yml")
752+
745753
self.camera_setting_controller.solvent = self.configuration["experiment"][
746754
"Saving"
747755
]["solvent"]

src/navigate/controller/sub_controllers/menu_controller.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,15 @@
6565
from navigate.tools.file_functions import save_yaml_file, load_yaml_file
6666
from navigate.tools.decorators import FeatureList
6767
from navigate.tools.common_functions import load_module_from_file
68-
from navigate.config.config import get_navigate_path
6968

7069

70+
# Misc. Local Imports
71+
from navigate.config.config import (
72+
update_config_dict,
73+
verify_waveform_constants,
74+
get_navigate_path,
75+
)
76+
7177
# Logger Setup
7278
p = __name__.split(".")[1]
7379
logger = logging.getLogger(p)
@@ -88,8 +94,10 @@ def __init__(self, char=None, keysym=None):
8894
"""
8995
#: str: The character that was pressed.
9096
self.char = char
97+
9198
#: str: The key that was pressed.
9299
self.keysym = keysym
100+
93101
#: int: The state of the keyboard.
94102
self.state = 0
95103

@@ -108,24 +116,34 @@ def __init__(self, view, parent_controller=None):
108116
The parent controller.
109117
"""
110118
super().__init__(view, parent_controller)
119+
111120
#: Controller: The parent controller.
112121
self.parent_controller = parent_controller
122+
113123
#: tk.canvas: The view class.
114124
self.view = view
125+
115126
#: tkinter.StringVar: Resolution value.
116127
self.resolution_value = tk.StringVar()
128+
117129
#: tkinter.IntVar: Feature id value.
118130
self.feature_id_val = tk.IntVar()
131+
119132
#: tkinter.IntVar: Disable stage limits.
120133
self.disable_stage_limits = tk.IntVar()
134+
121135
#: FakeEvent: Fake event.
122136
self.fake_event = None
137+
123138
#: list: List of feature list names.
124139
self.feature_list_names = []
140+
125141
#: int: System feature list count.
126142
self.system_feature_list_count = 0
143+
127144
#: int: Feature list count.
128145
self.feature_list_count = 0
146+
129147
#: str: Feature list file name.
130148
self.feature_list_file_name = "feature_lists.yaml"
131149

@@ -186,6 +204,20 @@ def initialize_menus(self):
186204
"<Control-S>",
187205
"<Control_L-S>",
188206
],
207+
"Load Waveform Constants": [
208+
"standard",
209+
self.load_waveform_constants,
210+
None,
211+
None,
212+
None,
213+
],
214+
"Save Waveform Constants": [
215+
"standard",
216+
self.save_waveform_constants,
217+
None,
218+
None,
219+
None,
220+
],
189221
"add_separator": [None],
190222
"Toggle Save Data": [
191223
"standard",
@@ -690,6 +722,45 @@ def save_experiment(self, *args):
690722
return
691723
save_yaml_file("", self.parent_controller.configuration["experiment"], filename)
692724

725+
def save_waveform_constants(self):
726+
"""Save a waveform constants file
727+
728+
Updates model.waveform_constants and saves it to file
729+
730+
"""
731+
filename = filedialog.asksaveasfilename(
732+
defaultextension=".yml", filetypes=[("Yaml file", "*.yml *.yaml")]
733+
)
734+
if not filename:
735+
return
736+
save_yaml_file(
737+
"", self.parent_controller.configuration["waveform_constants"], filename
738+
)
739+
740+
def load_waveform_constants(self):
741+
"""Load a waveform constants file"""
742+
743+
filename = filedialog.askopenfilename(
744+
defaultextension=".yml", filetypes=[("Yaml files", "*.yml *.yaml")]
745+
)
746+
if not filename:
747+
return
748+
749+
update_config_dict(
750+
self.parent_controller.manager,
751+
self.parent_controller.configuration,
752+
"waveform_constants",
753+
filename,
754+
)
755+
verify_waveform_constants(
756+
self.parent_controller.manager, self.parent_controller.configuration
757+
)
758+
759+
if hasattr(self.parent_controller, "waveform_popup_controller"):
760+
self.parent_controller.waveform_popup_controller.populate_experiment_values(
761+
force_update=True
762+
)
763+
693764
def load_images(self):
694765
"""Load images from a file."""
695766
filenames = filedialog.askopenfilenames(
@@ -762,9 +833,13 @@ def popup_autofocus_setting(self, *args):
762833
)
763834

764835
def popup_waveform_setting(self):
836+
"""Pop up the Waveform setting window.
837+
838+
If the window is already open, show it. Otherwise, create a new one."""
765839
if hasattr(self.parent_controller, "waveform_popup_controller"):
766840
self.parent_controller.waveform_popup_controller.showup()
767841
return
842+
768843
waveform_constants_popup = WaveformParameterPopupWindow(
769844
self.view, self.parent_controller.configuration_controller
770845
)

src/navigate/controller/sub_controllers/waveform_popup_controller.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,44 +75,57 @@ def __init__(self, view, parent_controller, waveform_constants_path):
7575
]
7676
#: dict: Galvo constants for the microscope.
7777
self.galvo_setting = self.resolution_info["galvo_constants"]
78+
7879
#: ConfigurationController: The configuration controller.
7980
self.configuration_controller = self.parent_controller.configuration_controller
81+
8082
#: str: The path to the waveform constants file.
8183
self.waveform_constants_path = waveform_constants_path
8284

8385
# Get mode and mag widgets
8486
#: dict: The widgets for the mode and magnification.
8587
self.widgets = self.view.get_widgets()
88+
8689
#: dict: The variables for the mode and magnification.
8790
self.variables = self.view.get_variables()
8891

8992
# Get configuration
9093
#: list: The lasers.
9194
self.lasers = self.configuration_controller.lasers_info
9295

93-
# Initialize
9496
#: str: The current resolution.
9597
self.resolution = None
98+
9699
#: str: The current magnification.
97100
self.mag = None
101+
98102
#: str: The current microscope operation mode.
99103
self.mode = "stop"
104+
100105
#: dict: Remote focus experiment dictionary.
101106
self.remote_focus_experiment_dict = None
107+
102108
#: bool: Flag to update galvo device.
103109
self.update_galvo_device_flag = None
110+
104111
#: bool: Flag to update waveform parameters.
105112
self.update_waveform_parameters_flag = False
113+
106114
#: bool: Flag to enable/disable waveforms.
107115
self.waveforms_enabled = True
116+
108117
#: dict: Dictionary of amplitude values.
109118
self.amplitude_dict = None
119+
110120
#: float: the minimum value of remote focus device
111121
self.laser_min = 0
122+
112123
#: float: the maximum value of remote focus device
113124
self.laser_max = 1.0
125+
114126
#: dict: Dictionary of galvo minimum values
115127
self.galvo_min = {}
128+
116129
#: dict: Dictionary of galvo maximum values
117130
self.galvo_max = {}
118131

@@ -289,19 +302,25 @@ def configure_widget_range(self):
289302
# amplitude.
290303
#
291304

292-
def populate_experiment_values(self):
305+
def populate_experiment_values(self, force_update=False):
293306
"""Set experiment values."""
294307
self.remote_focus_experiment_dict = self.parent_controller.configuration[
295308
"experiment"
296309
]["MicroscopeState"]
297310
resolution_value = self.remote_focus_experiment_dict["microscope_name"]
298311
zoom_value = self.remote_focus_experiment_dict["zoom"]
299312
mag = zoom_value
300-
if (
301-
self.widgets["Mode"].get() == resolution_value
313+
if (not force_update
314+
and self.widgets["Mode"].get() == resolution_value
302315
and self.widgets["Mag"].get() == mag
303316
):
304317
return
318+
319+
# Microscope information
320+
self.resolution_info = self.parent_controller.configuration[
321+
"waveform_constants"
322+
]
323+
self.galvo_setting = self.resolution_info["galvo_constants"]
305324
self.widgets["Mode"].set(resolution_value)
306325
self.show_magnification(mag)
307326

src/navigate/model/devices/daq/daq_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def __init__(self, configuration):
7777

7878
#: dict: Sweep times for different channels
7979
self.sweep_times = None
80-
80+
8181
#: dict: exposure times for different channels
8282
self.exposure_times = None
8383

@@ -132,6 +132,7 @@ def calculate_all_waveforms(self, microscope_name, exposure_times, sweep_times):
132132
exposure_time = exposure_times[channel_key]
133133
sweep_time = sweep_times[channel_key]
134134

135+
# Create 5V TTL for 1 camera exposure.
135136
self.waveform_dict[channel_key] = camera_exposure(
136137
sample_rate=self.sample_rate,
137138
sweep_time=sweep_time,

src/navigate/model/devices/remote_focus/remote_focus_base.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
# Third Party Imports
3737

3838
# Local Imports
39-
from navigate.model.waveforms import remote_focus_ramp, smooth_waveform
39+
from navigate.model.waveforms import (
40+
remote_focus_ramp,
41+
smooth_waveform,
42+
remote_focus_ramp_triangular,
43+
)
4044

4145
# # Logger Setup
4246
p = __name__.split(".")[1]
@@ -114,12 +118,21 @@ def adjust(self, exposure_times, sweep_times, offset=None):
114118
waveform : numpy.ndarray
115119
Waveform for the remote focus device.
116120
"""
121+
# to determine if the waveform has to be triangular
122+
sensor_mode = self.configuration["experiment"]["CameraParameters"][
123+
"sensor_mode"
124+
]
125+
readout_direction = self.configuration["experiment"]["CameraParameters"][
126+
"readout_direction"
127+
]
117128

118129
self.waveform_dict = dict.fromkeys(self.waveform_dict, None)
119130
microscope_state = self.configuration["experiment"]["MicroscopeState"]
120131
waveform_constants = self.configuration["waveform_constants"]
121132
imaging_mode = microscope_state["microscope_name"]
122133
zoom = microscope_state["zoom"]
134+
# ramp_type = self.configuration["configuration"]["microscopes"][
135+
# self.microscope_name]['remote focus device']['ramp_type']
123136
self.sample_rate = self.configuration["configuration"]["microscopes"][
124137
self.microscope_name
125138
]["daq"]["sample_rate"]
@@ -182,16 +195,32 @@ def adjust(self, exposure_times, sweep_times, offset=None):
182195
remote_focus_offset += offset
183196

184197
# Calculate the Waveforms
185-
self.waveform_dict[channel_key] = remote_focus_ramp(
186-
sample_rate=self.sample_rate,
187-
exposure_time=exposure_time,
188-
sweep_time=self.sweep_time,
189-
remote_focus_delay=remote_focus_delay,
190-
camera_delay=self.camera_delay,
191-
fall=remote_focus_ramp_falling,
192-
amplitude=remote_focus_amplitude,
193-
offset=remote_focus_offset,
194-
)
198+
if sensor_mode == "Light-Sheet" and (
199+
readout_direction == "Bidirectional"
200+
or readout_direction == "Rev. Bidirectional"
201+
):
202+
self.waveform_dict[channel_key] = remote_focus_ramp_triangular(
203+
sample_rate=self.sample_rate,
204+
exposure_time=exposure_time,
205+
sweep_time=self.sweep_time,
206+
remote_focus_delay=remote_focus_delay,
207+
camera_delay=self.camera_delay,
208+
amplitude=remote_focus_amplitude,
209+
offset=remote_focus_offset,
210+
)
211+
samples *= 2
212+
213+
else:
214+
self.waveform_dict[channel_key] = remote_focus_ramp(
215+
sample_rate=self.sample_rate,
216+
exposure_time=exposure_time,
217+
sweep_time=self.sweep_time,
218+
remote_focus_delay=remote_focus_delay,
219+
camera_delay=self.camera_delay,
220+
fall=remote_focus_ramp_falling,
221+
amplitude=remote_focus_amplitude,
222+
offset=remote_focus_offset,
223+
)
195224

196225
# Smooth the Waveform if specified
197226
if percent_smoothing > 0:
@@ -209,4 +238,3 @@ def adjust(self, exposure_times, sweep_times, offset=None):
209238
] = self.remote_focus_min_voltage
210239

211240
return self.waveform_dict
212-

0 commit comments

Comments
 (0)