# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Stage-scanned light sheet imaging 
#
# This notebook explains how to perform stage-scanned light sheet imaging. 
#
# To acquire a z-stack, the camera is set to 'External Start' mode. The stage moves at a constant speed
# during the acqusition. When the stage initiates the scanning, it sends out a TTL to trigger the camera
# to start acquisiton.
#
# To acquire a time lapse 3D dataset, the process described above is repeated for n times 
# (n: number of time points).
#
# Note: 
# To avoid motion blur during the stage scan, a method called 'Light-sheet stablized stage scanning 
# (LS3)' is used. With this method, A galo mirror is used to offset the stage motion during each frame.
# The galvo is controlled independently by another python script using the NI-DAQmax API.
# Details of the LS3 methods can be found in: 
# https://www.biorxiv.org/content/10.1101/2020.09.22.309229v1 

from pycromanager import Bridge, Acquisition


# ### Define a hook function to start the scanning of the stage

def move_stage(event):
    message = "scan"
    core.set_serial_port_command(port, message, "\r")
    return event


# ### Construct java objects

bridge = Bridge()
core = bridge.get_core()
mm = bridge.get_studio()

# ### Acquisition parameter

# +
nb_timepoints = 5
scan_step = 2.0           # unit: um
stage_scan_range = 200.0  # unit: um
interval = 1              # interval time between each time point, unit: second
exposureMs = core.get_exposure()
nrSlices = int(stage_scan_range / scan_step)

save_directory = r'E:\data'
save_name = 'test'
    
port = "COM4"
speed = scan_step / exposureMs
# -

# ### Stage settings
# Note: an ASI MS200 stage is used here. If you have a different stage, consult the manual to find out 
# the correct way to operate it.

# +
# set backlash
message = "backlash x=0.02 y=0.0"
print("set backlash: " + message)
core.set_serial_port_command(port, message, "\r")

# set default speed
message = "speed x=10 y=10"
core.set_serial_port_command(port, message, "\r")

# set speed. note: here x-axis is the stage motion axis.
message = "speed x=" + "{:.4f}".format(speed)
print("set speed to scan: ", message)
core.set_serial_port_command(port, message, "\r")

# set current position to zero
message = "zero"
core.set_serial_port_command(port, message, "\r")

# set the scan range
message = "scanr x=0.0 y=" + "{:.4f}".format(stage_scan_range / 1000)
print("scan range: " + message)
core.set_serial_port_command(port, message, "\r")
# -

# ### Camera settings
# Note: an Hamamasty Flash 4.0 camear is used here. If you have a different camera, consult the manual 
# to find out the correct way to set the parameter.

# Camera trigger settings
core.set_property("HamamatsuHam_DCAM", "TRIGGER SOURCE", "EXTERNAL")
core.set_property("HamamatsuHam_DCAM", "TRIGGER DELAY", "0.0")

# ### The main function to perform the time lampse 3D imaging

if __name__ == '__main__':
    # generate the multi-dimensional events 
    events = []
    for t in range(nb_timepoints):
        event = []
        for z in range(nrSlices):
            event.append({'axes': {'time': t, 'z': z}, 'min_start_time': interval})
        events.append(event)
    print(events)

    with Acquisition(directory=save_directory,
                     name=save_name,
                     pre_hardware_hook_fn=sleep,
                     post_camera_hook_fn=move_stage) as acq:
        for t in range(nb_timepoints):
            acq.acquire(events[t])
        acq.await_completion()

    # set back camera property
    core.set_property("HamamatsuHam_DCAM", "TRIGGER SOURCE", "INTERNAL")

    # set the stage to default speed
    message = "speed x=10 y=10"
    core.set_serial_port_command(port, message, "\r")

StarcoderdataJupyterScriptsDedupFiltered