Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ Hardware implementations use `ccall` for vendor SDKs:
- `mcl_stage/*.jl` - Mad City Labs NanoDrive
- Serial devices (CrystaLaser, Vortran, Triggerscope) use `LibSerialPort`

### Camera Image Data Convention

**Convention:** Image data is stored and displayed as column-major `(H, W, N)` arrays where `data[row, col]` = `data[y, x]`.

**At DLL boundary (getdata):** C SDKs return row-major buffers. Must permute after reshape:
```julia
# WRONG: reshape(buffer, (W, H)) - Julia reads column-first, data is transposed
# CORRECT: permutedims(reshape(buffer, (W, H)), (2, 1)) -> (H, W)
```

**Display (Makie image/heatmap):** Both `image()` and `heatmap()` map dim1→x, dim2→y. For `(H, W)` data:
```julia
# Both work the same way:
image(permutedims(data); axis=(yreversed=true,)) # W→x, H→y, origin top-left
heatmap(permutedims(data); axis=(yreversed=true,)) # W→x, H→y, origin top-left
```

**Saving (HDF5):** No transform needed if getdata follows convention. Save `(H, W, N)` directly.

### Work in Progress

Some hardware modules are commented out in `MicroscopeControl.jl` while under development:
Expand Down
14 changes: 9 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name = "MicroscopeControl"
uuid = "aa70d9ae-4a1e-49fd-870a-8ccfd99f4c3e"
authors = ["klidke@unm.edu"]
version = "1.0.0-DEV"
authors = ["klidke@unm.edu"]

[deps]
CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
DAQmx = "bc903ccc-f951-4f60-9748-ff64248ad6aa"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
Expand All @@ -14,23 +15,26 @@ ImageView = "86fae568-95e7-573e-a6b2-d8a6b900c9ef"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
LibSerialPort = "a05a14c7-6e3b-5ba9-90a2-45558833e1df"
NIDAQ = "66b72792-1abf-55ab-8064-6e9051317175"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[sources]
DAQmx = {url = "https://github.com/LidkeLab/DAQmx.jl.git"}

[compat]
CairoMakie = "0.12, 0.13, 0.14, 0.15"
CEnum = "0.5.0"
CairoMakie = "0.12, 0.13, 0.14, 0.15"
FFTW = "1"
GLMakie = "0.10, 0.11, 0.12, 0.13"
HDF5 = "0.17"
ImageView = "0.12, 0.13"
Images = "0.26"
JLD2 = "0.5, 0.6"
NIDAQ = "0.6"
Reexport = "1.2.2"
Revise = "3"
Statistics = "1"
julia = "1.10"
julia = "1.11"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
80 changes: 17 additions & 63 deletions src/MicroscopeControl.jl
Original file line number Diff line number Diff line change
@@ -1,78 +1,32 @@
"""
MicroscopeControl is the main module for the MicroscopeControl package. It exports all the abstract types and methods for the instrument, as well as the implementations for the hardware interfaces. It also exports the methods for saving data to an HDF5 file, and the methods for controlling the camera, stage, light source, DAQ, and FPGA. Finally, it re-exports the GUI method for the user interface.
MicroscopeControl is the main module for the MicroscopeControl package.
It exports all abstract types and methods for instruments, as well as the
implementations for all hardware interfaces. It also provides methods for
saving data to HDF5 files, and the GUI methods for user interfaces.

Uses Reexport.jl to cleanly propagate exports from submodules.
"""
module MicroscopeControl

using HDF5
using Reexport

# To export the abstract types and methods for all the instruments
# Core instrument abstraction
include("instrument.jl")
export AbstractInstrument
export export_state, initialize, shutdown
export AbstractSystem, AbstractSystemState
export export_state, initialize, shutdown, get_state, set_state

# Including the hardware interfaces and implementations and the HDF5 file saving methods
# Hardware interfaces (abstract types + method signatures)
include("hardware_interfaces/HardwareInterfaces.jl")
include("hardware_implementations/HardwareImplementations.jl")
include("h5_file_saving.jl")

using .HardwareImplementations.SimulatedCamera
using .HardwareImplementations.DCAM4
using .HardwareImplementations.PI
using .HardwareImplementations.SimulatedStage
using .HardwareImplementations.MadCityLabs
using .HardwareImplementations.PI_N472
using .HardwareImplementations.ThorCamCSC
using .HardwareImplementations.SimulatedLight
using .HardwareImplementations.NIDAQcard
using .HardwareImplementations.TCubeLaserControl
using .HardwareImplementations.TransmissionDaqControl
using .HardwareImplementations.CrystaLaserControl
using .HardwareImplementations.VortranLaserControl
using .HardwareImplementations.OK_XEM
using .HardwareImplementations.ThorCamDCx
# using .HardwareImplementations.Triggerscope
# using .HardwareImplementations.MCLMicroPositioner
@reexport using .HardwareInterfaces

# # Export all HardwareImplementations modules
# export DCAM4, SimulatedCamera, SimulatedStage, PI, MadCityLabs, PI_N472, ThorCamCSC
# export SimulatedLight, NIDAQcard, TCubeLaserControl, TransmissionDaqControl
# export CrystaLaserControl, VortranLaserControl, OK_XEM
# Hardware implementations (concrete device drivers)
include("hardware_implementations/HardwareImplementations.jl")
@reexport using .HardwareImplementations

# Export h5 file saving methods
# HDF5 file saving utilities
include("h5_file_saving.jl")
export save_h5, save_attributes_and_data

# Re-export camera implementations
export SimCamera, DCAM4Camera, ThorCamCSCCamera, ThorCamDCXCamera
export start_sequence, start_live
export getlastframe, capture, live, sequence, abort, getdata
export setexposuretime, settriggermode, setroi!, setexposuretime!
export dcamprop_getvalue, DCAM_IDPROP_INTERNALFRAMERATE, CameraROI, dcamapi_uninit

# Re-export stage implementations
export PIStage, MCLStage, SimStage, N472
export move, getposition, stopmotion, getrange
export setvel, reference, servoxy, movexy, servo
export move_to_z, get_z_position

# Re-export light source implementations
export SimLight, TCubeLaser, DaqTrLight, CrystaLaser, VortranLaser
export light_on, light_off, setpower

# Re-export DAQ implementations
export NIdaq
export showdevices, showchannels, createtask, setvoltage, readvoltage, deletetask

# Re-export FPGA implementations
export XEM
export setexposure, enable, setupIO

# Re-export triggerscope implementations
# export Triggerscope4

#re-export objective positioner implementations
# export MclZPositioner

# Re-export common GUI methods
export gui

end
7 changes: 7 additions & 0 deletions src/h5_file_saving.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ function save_group_recursive(h5parent, group_name::String, attributes::Dict, da
# Save data
if !isnothing(data)
write(h5group, "data", data)
# Add dimension convention metadata for image data
if ndims(data) >= 2
dset = h5group["data"]
attrs(dset)["dimension_order"] = ndims(data) == 2 ? "HW" : "HWN"
attrs(dset)["dimension_labels"] = ndims(data) == 2 ? ["height", "width"] : ["height", "width", "frames"]
attrs(dset)["memory_layout"] = "column_major_julia"
end
end

# Save attributes
Expand Down
53 changes: 48 additions & 5 deletions src/hardware_implementations/HardwareImplementations.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,71 @@
"""
HardwareImplementations module is a container for all hardware implementations
HardwareImplementations module is a container for all hardware implementations.
Uses Reexport.jl to automatically propagate exports from submodules.
"""
module HardwareImplementations

using Reexport
using ..MicroscopeControl

# Camera implementations
include("simulated_camera/SimulatedCamera.jl")
@reexport using .SimulatedCamera

include("dcam4_camera/DCAM4.jl")
@reexport using .DCAM4

include("thorcam_csc/ThorCamCSC.jl")
@reexport using .ThorCamCSC

include("thorcam_dcx/ThorCamDCx.jl")
@reexport using .ThorCamDCx

# Stage implementations
include("pi_stage/PI.jl")
@reexport using .PI

include("simulated_stage/SimulatedStage.jl")
@reexport using .SimulatedStage

include("mcl_stage/MadCityLabs.jl")
@reexport using .MadCityLabs

include("pi_N472/PI_N472.jl")
@reexport using .PI_N472

include("thorcam_csc/ThorCamCSC.jl")
include("thorcam_dcx/ThorCamDCx.jl")
include("simulated_light/SimulatedLight.jl")
# DAQ implementation (must come before modules that depend on it)
include("nidaq/NIDAQcard.jl")
@reexport using .NIDAQcard

# Light source implementations
include("simulated_light/SimulatedLight.jl")
@reexport using .SimulatedLight

include("tcube_laser/TCubeLaserControl.jl")
@reexport using .TCubeLaserControl

include("daq_transmission_light/TransmissionDaqControl.jl")
@reexport using .TransmissionDaqControl

include("crysta_laser_561/CrystaLaserControl.jl")
@reexport using .CrystaLaserControl

include("vortran_laser_488/VortranLaserControl.jl")
@reexport using .VortranLaserControl

# FPGA implementation (depends on NIDAQcard)
include("ok_xem/OK_XEM.jl")
@reexport using .OK_XEM

# SLM implementation
include("meadowlark_slm/Meadowlark.jl")
@reexport using .Meadowlark

# Work in progress - uncomment when ready
# include("triggerscope/Triggerscope.jl")
# @reexport using .Triggerscope

# include("mcl_micro_positioner/MCLMicroPositioner.jl")
# @reexport using .MCLMicroPositioner

end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A Module for controlling a laser through a NIDAQ card.
"""
module CrystaLaserControl

using NIDAQ
using DAQmx

using ...MicroscopeControl.HardwareInterfaces.LightSourceInterface
using ...MicroscopeControl.HardwareImplementations.NIDAQcard
Expand Down
78 changes: 43 additions & 35 deletions src/hardware_implementations/crysta_laser_561/interface_methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
"""
function initialize(light::CrystaLaser)
light.properties.is_on = false
daq = light.daq
channelsAO = light.channelsAO
channelsDO = light.channelsDO
return nothing
end

Expand All @@ -19,63 +16,74 @@ Set the power of the laser by setting the voltage of the NIDAQ card.
- `voltage::Float64`: The voltage to set the laser to.
"""
function LightSourceInterface.setpower(light::CrystaLaser, voltage::Float64)
daq = light.daq
min_voltage = light.min_voltage
max_voltage = light.max_voltage
channelsAO = light.channelsAO
channelsDO = light.channelsDO
if isempty(light.channelsAO)
@warn "CrystaLaser: no AO channels available for setpower"
return
end
if voltage < light.min_voltage || voltage > light.max_voltage
@error "The voltage should be between $(light.min_voltage) and $(light.max_voltage)"
return
end
t = NIDAQcard.createtask(daq,"AO",channelsAO[1])
NIDAQcard.setvoltage(daq,t, voltage)
NIDAQcard.deletetask(daq,t)
try
t = NIDAQcard.createtask(light.daq, "AO", light.channelsAO[1])
NIDAQcard.setvoltage(light.daq, t, voltage)
NIDAQcard.deletetask(light.daq, t)
catch e
@warn "CrystaLaser setpower failed: $e"
end
end

"""
light_on(light::CrystaLaser)
"""
function LightSourceInterface.light_on(light::CrystaLaser)
light.properties.is_on = true
# power = 20.0
channelsAO = light.channelsAO
channelsDO = light.channelsDO
# voltage = (power-10)/90*(light.max_voltage-light.min_voltage)+light.min_voltage
# light.properties.power = power
voltage = 1.0
daq = light.daq
t = NIDAQcard.createtask(daq,"AO",light.channelsAO[1])
NIDAQcard.setvoltage(daq,t, voltage)
NIDAQcard.deletetask(daq,t)
if isempty(light.channelsAO)
@warn "CrystaLaser: no AO channels available for light_on"
return
end
try
t = NIDAQcard.createtask(light.daq, "AO", light.channelsAO[1])
NIDAQcard.setvoltage(light.daq, t, 1.0)
NIDAQcard.deletetask(light.daq, t)
catch e
@warn "CrystaLaser light_on failed: $e"
end
end

"""
light_off(light::CrystaLaser)
"""
function LightSourceInterface.light_off(light::CrystaLaser)
light.properties.is_on = false
daq = light.daq
channelsAO = light.channelsAO
channelsDO = light.channelsDO
voltage::Float64 = 0.0
t = NIDAQcard.createtask(daq,"AO",light.channelsAO[1])
NIDAQcard.setvoltage(daq,t, voltage)
NIDAQcard.deletetask(daq,t)
if isempty(light.channelsAO)
@warn "CrystaLaser: no AO channels available for light_off"
return
end
try
t = NIDAQcard.createtask(light.daq, "AO", light.channelsAO[1])
NIDAQcard.setvoltage(light.daq, t, 0.0)
NIDAQcard.deletetask(light.daq, t)
catch e
@warn "CrystaLaser light_off failed: $e"
end
end

"""
shutdown(light::CrystaLaser)
"""
function shutdown(light::CrystaLaser)
light.properties.is_on = false
daq = light.daq
channelsAO = light.channelsAO
channelsDO = light.channelsDO
voltage::Float64 = 0.0
t = NIDAQcard.createtask(daq,"AO",light.channelsAO[1])
NIDAQcard.setvoltage(daq,t, voltage)
NIDAQcard.deletetask(daq,t)
if isempty(light.channelsAO)
return
end
try
t = NIDAQcard.createtask(light.daq, "AO", light.channelsAO[1])
NIDAQcard.setvoltage(light.daq, t, 0.0)
NIDAQcard.deletetask(light.daq, t)
catch e
@warn "CrystaLaser shutdown failed: $e"
end
end

"""
Expand Down
Loading
Loading