Skip to content

Commit c1c0d11

Browse files
Merge pull request #808 from TheDeanLab/fix_exposure_time_error
camera exposure time in lightsheet mode
2 parents 3babee7 + 05fd5c5 commit c1c0d11

35 files changed

+491
-571
lines changed

src/navigate/config/config.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -768,18 +768,18 @@ def verify_waveform_constants(manager, configuration):
768768
"amplitude"
769769
],
770770
"offset": config_dict["remote_focus_device"]["offset"],
771-
"percent_smoothing": "0",
772-
"percent_delay": config_dict["remote_focus_device"][
773-
"delay_percent"
774-
],
771+
# "percent_smoothing": "0",
772+
# "delay": config_dict["remote_focus_device"][
773+
# "delay"
774+
# ],
775775
},
776776
)
777777
else:
778778
for k in [
779779
"amplitude",
780780
"offset",
781-
"percent_smoothing",
782-
"percent_delay",
781+
# "percent_smoothing",
782+
# "delay",
783783
]:
784784
if k not in waveform_dict[microscope_name][zoom][laser].keys():
785785
waveform_dict[microscope_name][zoom][laser][
@@ -893,6 +893,16 @@ def verify_waveform_constants(manager, configuration):
893893

894894
# other_constants
895895
waveform_dict = configuration["waveform_constants"]
896+
other_constants_dict = {
897+
"remote_focus_settle_duration": "0",
898+
"percent_smoothing": "0",
899+
"remote_focus_delay": config_dict["remote_focus_device"][
900+
"delay"
901+
],
902+
"remote_focus_ramp_falling": config_dict["remote_focus_device"][
903+
"ramp_falling"
904+
]
905+
}
896906
if (
897907
"other_constants" not in waveform_dict.keys()
898908
or type(waveform_dict["other_constants"]) is not DictProxy
@@ -901,12 +911,30 @@ def verify_waveform_constants(manager, configuration):
901911
manager,
902912
waveform_dict,
903913
"other_constants",
904-
{"remote_focus_settle_duration": "0"},
914+
other_constants_dict,
905915
)
906-
if "remote_focus_settle_duration" not in waveform_dict["other_constants"].keys():
907-
waveform_dict["other_constants"]["remote_focus_settle_duration"] = "0"
908-
else:
916+
for k in other_constants_dict.keys():
909917
try:
910-
float(waveform_dict["other_constants"]["remote_focus_settle_duration"])
911-
except ValueError:
912-
waveform_dict["other_constants"]["remote_focus_settle_duration"] = "0"
918+
float(waveform_dict["other_constants"][k])
919+
except (ValueError, KeyError):
920+
waveform_dict["other_constants"][k] = "0"
921+
922+
def verify_configuration(manager, configuration):
923+
"""Verify configuration files.
924+
925+
Supports old version of configurations.
926+
"""
927+
device_config = configuration["configuration"]["microscopes"]
928+
for microscope_name in device_config.keys():
929+
# camera
930+
# delay_percent -> delay
931+
camera_config = device_config[microscope_name]["camera"]
932+
if "delay" not in camera_config.keys():
933+
camera_config["delay"] = camera_config.get("delay_percent", 2)
934+
# remote focus
935+
# ramp_falling_percent -> ramp_falling
936+
remote_focus_config = device_config[microscope_name]["remote_focus_device"]
937+
if "ramp_falling" not in remote_focus_config.keys():
938+
remote_focus_config["ramp_falling"] = remote_focus_config.get("ramp_falling_percent", 5)
939+
if "delay" not in remote_focus_config.keys():
940+
remote_focus_config["delay"] = remote_focus_config.get("delay_percent", 0)

src/navigate/controller/controller.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
update_config_dict,
7979
verify_experiment_config,
8080
verify_waveform_constants,
81+
verify_configuration,
8182
get_navigate_path,
8283
)
8384
from navigate.tools.file_functions import create_save_path, save_yaml_file
@@ -153,6 +154,7 @@ def __init__(
153154
waveform_templates=waveform_templates_path,
154155
)
155156

157+
verify_configuration(self.manager, self.configuration)
156158
verify_experiment_config(self.manager, self.configuration)
157159
verify_waveform_constants(self.manager, self.configuration)
158160

@@ -950,6 +952,8 @@ def capture_image(self, command, mode, *args):
950952
)
951953

952954
self.stop_acquisition_flag = False
955+
start_time = time.time()
956+
self.camera_setting_controller.update_readout_time()
953957

954958
while True:
955959
if self.stop_acquisition_flag:
@@ -967,6 +971,7 @@ def capture_image(self, command, mode, *args):
967971
)
968972
self.execute("stop_acquire")
969973

974+
970975
# Display the Image in the View
971976
self.camera_view_controller.try_to_display_image(image_id=image_id)
972977
images_received += 1
@@ -978,6 +983,17 @@ def capture_image(self, command, mode, *args):
978983
mode=mode,
979984
stop=False,
980985
)
986+
# update framerate
987+
stop_time = time.time()
988+
frames_per_second = images_received / (stop_time - start_time)
989+
# Update the Framerate in the Camera Settings Tab
990+
self.camera_setting_controller.framerate_widgets["max_framerate"].set(
991+
frames_per_second
992+
)
993+
994+
# Update the Framerate in the Acquire Bar to provide an estimate of
995+
# the duration of time remaining.
996+
self.acquire_bar_controller.framerate = frames_per_second
981997

982998
logger.info(
983999
f"Navigate Controller - Captured {images_received}, " f"{mode} Images"
@@ -1223,18 +1239,10 @@ def update_event(self):
12231239
except RuntimeError:
12241240
time.sleep(0.001)
12251241
pass
1226-
1227-
elif event == "framerate":
1228-
# Update the Framerate in the Camera Settings Tab
1229-
self.camera_setting_controller.framerate_widgets["max_framerate"].set(
1230-
value
1231-
)
1232-
1233-
# Update the Framerate in the Acquire Bar to provide an estimate of
1234-
# the duration of time remaining.
1235-
self.acquire_bar_controller.framerate = value
12361242
elif event == "remove_positions":
12371243
self.multiposition_tab_controller.remove_positions(value)
1244+
elif event == "exposure_time":
1245+
self.channels_tab_controller.set_exposure_time(value[0], value[1])
12381246

12391247
# def exit_program(self):
12401248
# """Exit the program.

src/navigate/controller/sub_controllers/camera_setting_controller.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def populate_experiment_values(self):
236236
# Physical Dimensions
237237
self.calculate_physical_dimensions()
238238
# readout time
239-
self.calculate_readout_time()
239+
self.update_readout_time()
240240

241241
# after initialization
242242
self.in_initialization = False
@@ -349,7 +349,7 @@ def update_sensor_mode(self, *args):
349349
self.show_verbose_info("Light Sheet Camera Readout Mode")
350350

351351
# calculate readout time
352-
self.calculate_readout_time()
352+
self.update_readout_time()
353353

354354
def update_exposure_time(self, exposure_time):
355355
"""When camera exposure time is changed, recalculate readout time
@@ -360,7 +360,6 @@ def update_exposure_time(self, exposure_time):
360360
exposure time in seconds
361361
"""
362362
self.framerate_widgets["exposure_time"].set(exposure_time)
363-
self.calculate_readout_time()
364363

365364
def update_roi(self, btn_name):
366365
"""Update ROI width and height.
@@ -464,43 +463,23 @@ def calculate_physical_dimensions(self):
464463
self.roi_widgets["FOV_X"].set(physical_dimensions_x)
465464
self.roi_widgets["FOV_Y"].set(physical_dimensions_y)
466465

467-
def calculate_readout_time(self):
468-
"""Calculate camera readout time.
466+
def update_readout_time(self):
467+
"""Update camera readout time.
469468
470469
471470
TODO: Highly specific to Hamamatsu Orca Flash 4.0.
472471
Should find a way to pass this from the camera to here.
473472
This should be moved to the camera device/API,
474473
ideally by calling a command from the camera.
475474
"""
476-
477-
h = 9.74436e-6 # Readout timing constant
478475
sensor_mode = self.mode_widgets["Sensor"].get()
479-
if (self.readout_speed == 1) and (sensor_mode == "Normal"):
480-
h = 32.4812e-6
481-
# the ROI height 'subarray_vsize'
482-
vn = float(self.roi_widgets["Height"].get())
483-
exposure_time = float(self.framerate_widgets["exposure_time"].get())
484476

485477
if sensor_mode == "Normal":
486-
# Area sensor mode operation
487-
if self.trigger_source == 1:
488-
# Internal Trigger Source
489-
readout_time = exposure_time - ((vn / 2) * h)
490-
491-
if self.trigger_active in [1, 2]:
492-
# External Trigger Source
493-
# Edge == 1, Level == 2
494-
readout_time = exposure_time - ((vn / 2) * h + 10 * h)
495-
496-
elif self.trigger_active == 3:
497-
# External Trigger Source
498-
# Synchronous Readout == 3
499-
readout_time = exposure_time - ((vn / 2) * h + 5 * h)
478+
readout_time = self.camera_setting_dict["readout_time"]
500479

501480
elif sensor_mode == "Light-Sheet":
502481
# Progressive sensor mode operation
503-
readout_time = exposure_time - 1 / (exposure_time + (vn + 10) * h)
482+
readout_time = 0
504483

505484
# return readout_time
506485
self.framerate_widgets["readout_time"].set(readout_time)

src/navigate/controller/sub_controllers/channels_tab_controller.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,3 +848,18 @@ def verify_experiment_values(self):
848848
if self.microscope_state_dict["timepoints"] < 1:
849849
return "Timepoints should be at least 1!"
850850
return None
851+
852+
def set_exposure_time(self, channel, exposure_time):
853+
"""Set exposure time for a specified channel
854+
855+
Parameters
856+
----------
857+
channel : str
858+
Channel name, such as "channel_1", "channel_2",...
859+
exposure_time : float
860+
Exposure time in miliseconds.
861+
"""
862+
idx = int(channel[channel.index('_')+1:]) - 1
863+
self.channel_setting_controller.in_initialization = True
864+
self.channel_setting_controller.view.exptime_variables[idx].set(exposure_time)
865+
self.channel_setting_controller.in_initialization = False

src/navigate/controller/sub_controllers/waveform_popup_controller.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ def __init__(self, view, parent_controller, waveform_constants_path):
161161
self.variables["Delay"].trace_add("write", self.update_waveform_parameters)
162162
self.variables["Duty"].trace_add("write", self.update_waveform_parameters)
163163
self.variables["Smoothing"].trace_add("write", self.update_waveform_parameters)
164+
self.variables["Ramp_falling"].trace_add(
165+
"write", self.update_waveform_parameters
166+
)
164167

165168
# Save waveform constants
166169
self.view.get_buttons()["Save"].configure(command=self.save_waveform_constants)
@@ -380,26 +383,22 @@ def show_laser_info(self, *args):
380383
# Currently pulls from the original microscope configuration YAML file, not the
381384
# current microscope configuration according to the model
382385
self.update_waveform_parameters_flag = False
383-
waveform_configuration = self.resolution_info["remote_focus_constants"][
384-
self.resolution
385-
][self.mag][self.lasers[0]]
386386
self.widgets["Smoothing"].set(
387-
waveform_configuration.get("percent_smoothing", 0)
387+
self.resolution_info["other_constants"].get("percent_smoothing", 0)
388388
)
389389
self.widgets["Smoothing"].widget.trigger_focusout_validation()
390-
self.widgets["Delay"].set(waveform_configuration.get("percent_delay", 7.5))
390+
self.widgets["Delay"].set(
391+
self.resolution_info["other_constants"].get("remote_focus_delay", 7.5)
392+
)
391393
self.widgets["Delay"].widget.trigger_focusout_validation()
392-
if "other_constants" not in self.resolution_info:
393-
update_config_dict(
394-
self.parent_controller.manager,
395-
self.resolution_info,
396-
"other_constants",
397-
{"remote_focus_settle_duration": 0},
398-
)
399394
self.widgets["Duty"].set(
400395
self.resolution_info["other_constants"]["remote_focus_settle_duration"]
401396
)
402397
self.widgets["Duty"].widget.trigger_focusout_validation()
398+
self.widgets["Ramp_falling"].set(
399+
self.resolution_info["other_constants"]["remote_focus_ramp_falling"]
400+
)
401+
self.widgets["Ramp_falling"].widget.trigger_focusout_validation()
403402
self.update_waveform_parameters_flag = True
404403

405404
# update resolution value in central controller (menu)
@@ -491,21 +490,20 @@ def update_waveform_parameters(self, *args, **wargs):
491490
delay = float(self.widgets["Delay"].widget.get())
492491
duty_cycle = float(self.widgets["Duty"].widget.get())
493492
smoothing = float(self.widgets["Smoothing"].widget.get())
493+
ramp_falling = float(self.widgets["Ramp_falling"].widget.get())
494494
except ValueError:
495495
return
496496

497497
# Update the waveform parameters
498498
# all the lasers use the same delay, smoothing value
499-
for laser in self.lasers:
500-
self.resolution_info["remote_focus_constants"][self.resolution][self.mag][
501-
laser
502-
]["percent_delay"] = delay
503-
self.resolution_info["remote_focus_constants"][self.resolution][self.mag][
504-
laser
505-
]["percent_smoothing"] = smoothing
506499
self.resolution_info["other_constants"][
507500
"remote_focus_settle_duration"
508501
] = duty_cycle
502+
self.resolution_info["other_constants"][
503+
"remote_focus_ramp_falling"
504+
] = ramp_falling
505+
self.resolution_info["other_constants"]["remote_focus_delay"] = delay
506+
self.resolution_info["other_constants"]["percent_smoothing"] = smoothing
509507

510508
# Pass the values to the parent controller.
511509
self.event_id = self.view.popup.after(

src/navigate/controller/sub_controllers/waveform_tab_controller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ def plot_waveforms(self, event):
214214
# two pass
215215
for k in self.waveform_dict["camera_waveform"].keys():
216216
remote_focus_waveform = self.waveform_dict["remote_focus_waveform"][k]
217+
if remote_focus_waveform is None:
218+
continue
217219
max_remote_focus_waveform = np.maximum(max_remote_focus_waveform, np.max(remote_focus_waveform))
218220
min_remote_focus_waveform = np.minimum(min_remote_focus_waveform, np.min(remote_focus_waveform))
219221
camera_waveform = self.waveform_dict["camera_waveform"][k]

src/navigate/model/device_startup_functions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,11 @@ def load_devices(configuration, is_synthetic=False, plugin_devices={}) -> dict:
12401240
)
12411241
# if the serial number is with leading zeros, the yaml reader will convert it to a octal number
12421242
if camera_serial_number.startswith("0"):
1243-
devices["camera"][build_ref_name("_", device["type"], int(camera_serial_number, 8))] = camera
1243+
try:
1244+
oct_num = int(camera_serial_number, 8)
1245+
devices["camera"][build_ref_name("_", device["type"], oct_num)] = camera
1246+
except ValueError:
1247+
pass
12441248
else:
12451249
device_ref_name = build_ref_name(
12461250
"_", device["type"], device["serial_number"]

src/navigate/model/devices/APIs/hamamatsu/HamamatsuAPI.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ class DCAMDEV_STRING(Structure):
641641
"sensor_mode": 4194832, # 0x00400210, R/W, mode, "SENSOR MODE"
642642
"defect_correct_mode": 4653072, # 0x00470010, R/W, mode, "DEFECT CORRECT MODE"
643643
"binning": 4198672, # 0x00401110, R/W, mode, "BINNING"
644+
"subarray_mode": 4202832, # 0x00402150, R/W, mode, "SUBARRAY MODE"
644645
"readout_speed": 4194576, # 0x00400110, R/W, long, "READOUT SPEED"
645646
"readout_direction": 4194608, # 0x00400130, R/W, mode, "READOUT DIRECTION"
646647
"readout_time": 4206608, # 0x00403010, R/O, sec, "TIMING READOUT TIME"
@@ -649,8 +650,8 @@ class DCAMDEV_STRING(Structure):
649650
"trigger_polarity": 1049120, # 0x00100220, R/W, mode, "TRIGGER POLARITY"
650651
"trigger_source": 1048848, # 0x00100110, R/W, mode, "TRIGGER SOURCE"
651652
"trigger_delay": 1049184, # 0x00100260, /* R/W, sec, "TRIGGER DELAY"
652-
"internal_line_interval": 4208720, # 0x00403850, R/W, sec,
653-
# "INTERNAL LINE INTERVAL"
653+
"internal_line_interval": 4208720, # 0x00403850, R/W, sec, "INTERNAL LINE INTERVAL"
654+
"internal_line_speed": 4208704, # 0x00403840, R/W, m/sec, "INTERNAL LINE SPEED"
654655
"image_width": 4325904, # 0x00420210, R/O, long, "IMAGE WIDTH"
655656
"image_height": 4325920, # 0x00420220, R/O, long, "IMAGE HEIGHT"
656657
"exposuretime_control": 2031920, # 0x001F0130, R/W, mode,
@@ -1013,8 +1014,9 @@ def set_property_value(self, name, value):
10131014
return False
10141015

10151016
# Cast value to a multiple of step size
1016-
if property_value_step > 0:
1017-
value = int(value // property_value_step) * property_value_step
1017+
# The cast will cause accuracy problems when setting exposure time
1018+
# if property_value_step > 0:
1019+
# value = int(value // property_value_step) * property_value_step
10181020

10191021
if value < property_value_min:
10201022
print(

0 commit comments

Comments
 (0)