Commit d54b7e10 authored by Éric Thiébaut's avatar Éric Thiébaut
Browse files

Add Shack-Hartmann wavefront sensor

parent f307decb
......@@ -21,6 +21,8 @@ export
writefits,
writefits!
using TwoDimensional
using EasyFITS
import EasyFITS: readfits, writefits, writefits!
......@@ -36,18 +38,62 @@ import Sockets: send
import Statistics: mean
function abort end
function algorithm end
function camera end
function configure end
function device end
function image_size end
function layout end
function measurements end
function reference end
function resume end
function send! end
function set_reference! end
function start end
function stop end
function subimages_bounding_boxes end
function suspend end
"""
Tao.initialize(alg)
initializes algorithm `alg`. In a control loop, this method is called once
before entering the loop.
See also: [`Tao.measure!`](@ref) and [`Tao.postprocess`](@ref).
"""
function initialize end
"""
Tao.measure!(dest, alg, [wgt,] img) -> dest
computes the wavefront measurements into `dest` with algorithm `alg` and given
the wavefront sensor image `img` and, optionally, its respective weights `wgt`.
In a control loop, this method is called immediately after each acquired image
and it shall deliver the measurements as quickly as possible. If additional
processing is required, they can be performed by the method `Tao.postprocess`
which is called after sending the commands and before waiting for the next
image.
See also: [`Tao.initialize`](@ref) and [`Tao.postprocess`](@ref),
"""
function measure! end
"""
Tao.postprocess(alg)
applies any postprocessing required by algorithm `alg`. In a control loop,
this method is called after sending the commands and before waiting for the
next image.
See also: [`Tao.initialize`] and [`Tao.measure!`],
"""
function postprocess end
include("types.jl")
include("utils.jl")
include("weighted.jl")
......
#
# shackhartmann.jl --
#
# Implement Shack-Hartmann wavefront sensors in TAO.
#
#------------------------------------------------------------------------------
#
# This file is part of TAO software (https://git-cral.univ-lyon1.fr/tao)
# licensed under the MIT license.
#
# Copyright (C) 2018-2021, Éric Thiébaut.
#
# FIXME: doc!
"""
length(geom) # number of sub-images
Tao.image_size(geom) # size of the wavefront sensor image
Tao.subimages_bounding_boxes(geom) # bounding-boxes of the sub-images
Tao.layout(geom) # indexed layout of the sub-images
"""
struct ShackHartmannGeometry
dims::NTuple{2,Int} # wavefront sensor image dimensions
boxes::Vector{BoundingBox{Int}} # bounding boxes of the sub-images
inds::Matrix{Int} # indexed layout
function ShackHartmannGeometry(dims::NTuple{2,Integer},
boxes::AbstractVector{<:BoundingBox{<:Integer}},
inds::AbstractMatrix{<:Integer})
# Minimal check, not as completely as `indexed_layout` but enough to
# make sure that indices are not out of bounds.
dims[1] 1 && dims[2] 1 || error("invalid image dimensions")
cnt = 0
imin = typemax(Int)
imax = typemin(Int)
for i in inds
if i > 0
cnt += 1
imin = min(imin, Int(i))
imax = max(imax, Int(i))
end
end
(imin == 1 && imax == cnt) || error("invalid layout indice(s)")
length(boxes) == cnt || error("invalid number of bounding-boxes")
imagebox = BoundingBox(1:dims[1], 1:dims[2])
for box in boxes
if isempty(box) || !(box imagebox)
error("invalid sub-image bounding-box:", box)
end
end
new(dims, boxes, inds)
end
end
Base.length(geom::ShackHartmannGeometry) = length(subimages_bounding_boxes(geom))
subimages_bounding_boxes(geom::ShackHartmannGeometry) = geom.boxes
layout(geom::ShackHartmannGeometry) = geom.inds
image_size(geom::ShackHartmannGeometry) = geom.dims
function ShackHartmannGeometry(dims::NTuple{2,Integer},
boxes::AbstractVector{<:BoundingBox{<:Integer}},
mask::AbstractMatrix{Bool},
orient::Integer = 0)
cnt, inds = indexed_layout(inds)
ShackHartmannGeometry(dims, boxes, inds)
end
# FIXME: doc!
struct ShackHartmannSensor{T,
S<:AbstractWavefrontSensorMeasure{T},
A<:AbstractWavefrontSensorAlgorithm{S},
C<:AbstractCamera{T}} <: AbstractWavefrontSensor{S}
alg::A # wavefront sensor algorithm
cam::C # image provider connected to the wavefront sensor camera
ref::Vector{Point{T}} # WFS reference positions
geom::ShackHartmannGeometry
# To avoid (re)allocations, the wavefront sensor measurements are stored in
# an array that is owned by the wavefront sensor object (much like the
# image provider which caches the pre-processed image and its weights).
meas::Vector{S} # cached measurements
function ShackHartmannSensor{T,S,A,C}(alg::A,
cam::C,
ref::AbstractVector{Point{<:Real}},
geom::ShackHartmannGeometry,
meas::AbstractVector{S}) where {
T,
S<:AbstractWavefrontSensorMeasure{T},
A<:AbstractWavefrontSensorAlgorithm{S},
C<:AbstractCamera{T}
}
# Minimal check to make sure that indices are not out of bounds.
length(meas) == length(geom) || error("invalid number of measurements")
new{T,S,A,C}(alg, cam, ref, geom, meas)
end
end
# Constructors.
function ShackHartmannSensor(alg::A,
cam::C,
ref::AbstractVector{Point{<:Real}},
geom::ShackHartmannGeometry) where {
T<:AbstractFloat,
S<:AbstractWavefrontSensorMeasure{T},
A<:AbstractWavefrontSensorAlgorithm{S},
C<:AbstractCamera{T}}
ShackHartmannSensor{T,S,A,C}(alg, cam, ref, geom, Vector{S}(undef, cnt))
end
# Implement API of a wavefront-sensor.
Base.length(wfs::ShackHartmannSensor) = length(geometry(wfs))
Base.size(wfs::ShackHartmannSensor) = size(layout(wfs))
algorithm(wfs::ShackHartmannSensor) = wfs.alg
camera(wfs::ShackHartmannSensor) = wfs.cam
geometry(wfs::ShackHartmannSensor) = wfs.geom
device(wfs::ShackHartmannSensor) = device(camera(wfs))
image_size(wfs::ShackHartmannSensor) = image_size(geometry(wfs))
layout(wfs::ShackHartmannSensor) = layout(geometry(wfs))
measurements(wfs::ShackHartmannSensor) = wfs.meas
reference(wfs::ShackHartmannSensor) = wfs.ref
set_reference!(wfs::ShackHartmannSensor, ref::AbstractVector{<:Point}) = begin
@assert length(ref) == length(wfs)
copyto!(reference(wfs), ref)
set_reference!(algorithm(wfs), reference(wfs))
nothing
end
subimages_bounding_boxes(wfs::ShackHartmannSensor) =
subimages_bounding_boxes(geometry(wfs))
......@@ -17,16 +17,46 @@ export
AbstractWavefrontSensor,
AbstractWavefrontSensorAlgorithm,
AbstractWavefrontSensorMeasure,
ShackHartmannGeometry,
ShackHartmannSensor,
WavefrontSlope,
WeightedWavefrontSlope
WeightedWavefrontSlope,
abort,
algorithm,
camera,
device,
image_size,
layout,
measurements,
reference,
reset,
resume,
set_reference!,
start,
stop,
subimages_bounding_boxes,
suspend
using ArrayTools
using TwoDimensional
using ..Tao
using ..Tao: AbstractCamera, indexed_layout, to_float
import ..Tao:
floating_point_type,
reference, set_reference!, device, camera, start, stop, abort
abort,
algorithm,
camera,
device,
image_size,
layout,
measurements,
reference,
reset,
resume,
set_reference!,
start,
stop,
subimages_bounding_boxes,
suspend
import Base: Array, convert, copyto!
......@@ -88,11 +118,13 @@ types descendant of`Tao.AbstractWavefrontSlope{T}`.
"""
abstract type AbstractWavefrontSlope{T} <: AbstractWavefrontSensorMeasure{T} end
# FIXME: doc!
struct WavefrontSlope{T} <: AbstractWavefrontSlope{T}
x::T
y::T
end
# FIXME: doc!
struct WeightedWavefrontSlope{T} <: AbstractWavefrontSlope{T}
x::T
y::T
......@@ -108,6 +140,7 @@ WavefrontSlope(xy::NTuple{2,Real}) = WavefrontSlope(xy...)
WavefrontSlope(P::Point) = WavefrontSlope(P.x, P.y)
WavefrontSlope(S::AbstractWavefrontSlope) = WavefrontSlope(S.x, S.y)
WavefrontSlope{T}(S::WavefrontSlope{T}) where {T} = S
WavefrontSlope{T}(xy::NTuple{2,Real}) where {T<:AbstractFloat} = WavefrontSlope{T}(xy...)
WavefrontSlope{T}(P::Point) where {T<:AbstractFloat} = WavefrontSlope{T}(P.x, P.y)
WavefrontSlope{T}(S::AbstractWavefrontSlope) where {T<:AbstractFloat} = WavefrontSlope{T}(S.x, S.y)
......@@ -121,6 +154,7 @@ WeightedWavefrontSlope(tup::NTuple{5,Real}) = WeightedWavefrontSlope(tup...)
WeightedWavefrontSlope(xy::NTuple{2,Real}, wgt::NTuple{3,Real}) =
WeightedWavefrontSlope(xy..., wgt...)
WeightedWavefrontSlope{T}(S::WeightedWavefrontSlope{T}) where {T} = S
WeightedWavefrontSlope{T}(S::WeightedWavefrontSlope) where {T<:AbstractFloat} =
WeightedWavefrontSlope{T}(S.x, S.y, S.wxx, S.wxy, S.wyy)
WeightedWavefrontSlope{T}(tup::NTuple{5,Real}) where {T<:AbstractFloat} =
......@@ -140,6 +174,10 @@ convert(::Type{T}, P::AbstractPoint) where {T<:WavefrontSlope} = T(P.x, P.y)
convert(::Type{T}, xy::NTuple{2,Real}) where {T<:WavefrontSlope} = T(xy)
convert(::Type{T}, tup::NTuple{5,Real}) where {T<:WeightedWavefrontSlope} = T(tup)
convert(::Type{T}, S::T) where {T<:AbstractWavefrontSlope} = S
convert(::Type{T}, S::AbstractWavefrontSlope) where {T<:WavefrontSlope} = T(S)
convert(::Type{T}, S::WeightedWavefrontSlope) where {T<:WeightedWavefrontSlope} = T(S)
Base.iterate(itr::AbstractWavefrontSlope) = begin
vals = Tuple(itr)
return (vals[1], (vals, 2))
......@@ -173,28 +211,32 @@ end
yields the algorithm used by the wavefront sensor `wfs` to compute the
measurements.
""" algorithm
"""
function algorithm end
"""
Tao.camera(wfs) -> cam
yields the camera used by the wavefront sensor `wfs`.
""" camera
"""
function camera end
"""
Tao.device(wfs) -> dev
yields the camera device used by the wavefront sensor `wfs`.
""" device
"""
function device end
"""
Tao.reference(wfs) -> ref
yields the set of reference positions of the wavefront sensor `wfs`.
""" reference
"""
function reference end
"""
Tao.set_reference!(wfs, ref) -> reference(wfs)
......@@ -202,7 +244,8 @@ yields the set of reference positions of the wavefront sensor `wfs`.
overwrites the set of reference positions of the wavefront sensor `wfs` with
the set of positions `ref` and return the resulting reference positions.
""" set_reference!
"""
function set_reference! end
"""
Tao.measurements(wfs) -> data
......@@ -215,8 +258,10 @@ result is an array of the measurements whose types are given by `eltype(wfs)`.
For efficiency reasons, the last measurements are stored by the wavefront
sensor and should be copied if the caller wants to modify them.
""" measurements
"""
function measurements end
# Camera API.
"""
Tao.start(wfs, args...; kwds...)
......@@ -227,7 +272,7 @@ start(wfs::AbstractWavefrontSensor, args...; kwds...) =
start(camera(wfs), args...; kwds...)
"""
Tao.stop(wfs)
Tao.stop(wfs, args...; kwds...)
stops acquisition by the camera associated with wavefront sensor `wfs`.
......@@ -242,12 +287,31 @@ aborts acquisition by the camera associated with wavefront sensor `wfs`.
"""
abort(wfs::AbstractWavefrontSensor) = abort(camera(wfs))
"""
Tao.suspend(wfs, args...; kwds...)
suspends acquisition by the camera associated with wavefront sensor `wfs`.
"""
suspend(wfs::AbstractWavefrontSensor) = suspend(camera(wfs))
"""
Tao.resume(wfs, args...; kwds...)
suspends acquisition by the camera associated with wavefront sensor `wfs`.
"""
resume(wfs::AbstractWavefrontSensor) = resume(camera(wfs))
"""
Tao.reset(wfs) -> wfs
resets the measurement algorithm used by the wavefront sensor `wfs`.
""" reset
"""
reset(wfs::AbstractWavefrontSensor) = reset(algorithm(wfs))
include("shackhartmann.jl")
"""
Tao.flatten(A) -> B
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment