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

Improve API for deformable mirror (see NEWS.md)

parent 4e16b72e
......@@ -2,6 +2,21 @@
## Version 0.9.0
* The actuators commands sent to deformable mirrors are the sum of 3 terms: the
reference commands, some given perturbation commands, and the requested
commands. The perturbations are only valid for the next commands sent to the
deformable mirror. The reference commands are valid for the subsequent
commands sent to the mirror until a new reference commands are set.
* For deformable mirrors, the TAO library takes now directly takes care of
dealing with the actuators commands being the sum of different terms and of
clamping these values to the range `[cmin,cmax]` of acceptable values. Bad
command values (NaN) are automatically replaced by the mean `(cmin +
cmax)/2`. The *reset* command is strictly equivalent to a *send* command
with all requested actuators values set to zero. The `on_reset` callback is
thus no longer needed. As a result of these changes, the API for deformable
mirrors is much more simple.
* Support for Finger Lakes Instrumentation Kepler camera via the LIBFLIPRO SDK.
......
......@@ -235,9 +235,23 @@ uint8_t* alpao_mirror_mask_create_by_nacts(
// Aliases.
#define min(x, y) tao_min(x, y)
#define max(x, y) tao_max(x, y)
#define clamp(x, lo, hi) tao_clamp(x, lo, hi)
#define forced_store(ptr, val) tao_forced_store(ptr, val)
// Clamp x in the range [a,b] returning c if the result of clamping is a NaN.
// a ≤ b and c ∈ [a,b] must hold and, a, b, and c must be finite.
static inline double safe_clamp(
double x,
double a,
double b,
double c)
{
// The following expression assumes that a comparison with a NaN always
// yields false and that only x can be a NaN.
double y = (x < a ? a : x); // the greatest of x and a; NaN, if x is a NaN
double z = (y > b ? b : y); // the least of y and b; NaN, if y is a NaN
return (isfinite(z) ? z : c);
}
// For correct handling of errors, it is easier to split the code for
// alpao_open_mirror() in two functions using pointers to store other returned
// values than the device handle.
......@@ -357,8 +371,16 @@ alpao_mirror* alpao_open_mirror(
// Allocate zero-filled memory for the mirror structure plus enough aligned
// space for temporary commands.
size_t offset = TAO_ROUND_UP(sizeof(alpao_mirror), TAO_ALIGNMENT);
size_t size = offset + nacts*sizeof(Scalar);
size_t size, offset;
if (sizeof(Scalar) != sizeof(double)) {
// A temporary buffer is needed for the commands.
offset = TAO_ROUND_UP(sizeof(alpao_mirror), TAO_ALIGNMENT);
size = offset + nacts*sizeof(Scalar);
} else {
// No temporary buffer needed for the commands.
size = sizeof(alpao_mirror);
offset = 0;
}
alpao_mirror* dev = tao_malloc(size);
if (dev == NULL) {
free(serial);
......@@ -368,8 +390,12 @@ alpao_mirror* alpao_open_mirror(
memset(dev, 0, size);
forced_store(&dev->handle, handle);
forced_store(&dev->nacts, nacts);
forced_store(&dev->cmds, (char*)dev + offset);
forced_store(&dev->serial, serial);
if (sizeof(Scalar) != sizeof(double)) {
forced_store(&dev->cmds, (char*)dev + offset);
} else {
forced_store(&dev->cmds, NULL);
}
return dev;
}
......@@ -396,12 +422,11 @@ void alpao_close_mirror(
tao_status alpao_send_commands(
alpao_mirror* dev,
double *cmds,
const double* refs,
long n)
{
const double zero = 0;
const double vmin = -1;
const double vmax = +1;
const double cmin = -1;
const double cmax = +1;
const double cavg = 0;
if (dev == NULL || cmds == NULL) {
tao_store_error(__func__, TAO_BAD_ADDRESS);
return TAO_ERROR;
......@@ -410,21 +435,41 @@ tao_status alpao_send_commands(
tao_store_error(__func__, TAO_BAD_SIZE);
return TAO_ERROR;
}
Scalar* dev_cmds = dev->cmds;
if (refs == NULL) {
for (long i = 0; i < n; ++i) {
double val = clamp(cmds[i], vmin, vmax);
val = (isfinite(val) ? val : zero);
dev_cmds[i] = val;
cmds[i] = val;
// Check whether commands are all in range.
bool in_range = true;
for (long i = 0; i < n; ++i) {
double cval = cmds[i];
in_range &= ((cmin <= cval)&(cval <= cmax));
}
// Copy/fix commands.
Scalar* dev_cmds;
if (dev->cmds != NULL) {
// Scalar is not `double`.
dev_cmds = dev->cmds;
if (in_range) {
for (long i = 0; i < n; ++i) {
Scalar cval = cmds[i];
dev_cmds[i] = cval;
cmds[i] = cval;
}
} else {
for (long i = 0; i < n; ++i) {
Scalar cval = safe_clamp(cmds[i], cmin, cmax, cavg);
dev_cmds[i] = cval;
cmds[i] = cval;
}
}
} else {
for (long i = 0; i < n; ++i) {
double val = clamp(cmds[i] + refs[i], vmin, vmax);
val = (isfinite(val) ? val : zero);
dev_cmds[i] = val;
cmds[i] = val - refs[i];
// Scalar is `double`.
if (! in_range) {
for (long i = 0; i < n; ++i) {
double cval = safe_clamp(cmds[i], cmin, cmax, cavg);
cmds[i] = (isfinite(cval) ? cval : cavg);
}
}
dev_cmds = cmds;
}
if (asdkSend((asdkDM*)dev->handle, dev_cmds) != SUCCESS) {
push_last_error(__func__);
......
......@@ -24,37 +24,17 @@ static long* inds = NULL;
static uint8_t* msk = NULL;
static alpao_mirror* dev = NULL;
// Send the requested command.
// Send the requested command. On entry, `cmds` contains the `dm->nacts`
// actuators commands. On return, `cmds` contains the actuators commands
// modified to account for the deformable mirror limitations.
static tao_status on_send(
tao_remote_mirror* dm,
void* ctx)
void* ctx,
double* cmds)
{
// The context is the mirror device.
alpao_mirror* dev = ctx;
// Copy requested commands as actual commands and send them (which also
// updates the actual commands).
double* act_cmds = tao_remote_mirror_get_actual_commands(dm);
const double* req_cmds = tao_remote_mirror_get_requested_commands(dm);
const double* refs = tao_remote_mirror_get_reference(dm);
long nacts = dm->nacts;
for (long i = 0; i < nacts; ++i) {
act_cmds[i] = req_cmds[i];
}
return alpao_send_commands(dev, act_cmds, refs, nacts);
}
// Reset the deformable mirror. This amounts to sending zero commands.
static tao_status on_reset(
tao_remote_mirror* dm,
void* ctx)
{
double* req_cmds = tao_remote_mirror_get_requested_commands(dm);
long nacts = tao_remote_mirror_get_nacts(dm);
for (long i = 0; i < nacts; ++i) {
req_cmds[i] = 0.0;
}
return on_send(dm, ctx);
return alpao_send_commands(dev, cmds, dm->nacts);
}
// Release resources. This function is automatically called on normal exit.
......@@ -260,7 +240,6 @@ int main(
// instance).
tao_remote_mirror_operations ops = {
.on_send = on_send,
.on_reset = on_reset,
.name = ident,
.debug = debug
};
......
......@@ -41,8 +41,9 @@ typedef struct alpao_mirror alpao_mirror;
struct alpao_mirror {
void* const handle;///< Handle to device.
long const nacts;///< Number of actuators.
void* const cmds;///< Pointer to commands to send.
char const* const serial;///< Serial number of device.
void* const cmds;///< Buffer to temporarily store commands to send,
/// `NULL` if not needed.
};
/**
......@@ -70,12 +71,9 @@ extern void alpao_close_mirror(
* Apply actuator commands of an Alpao deformable mirror.
*
* This function applies given commands to the Alpao deformable mirror `dev`.
* If reference commands are supplied (i.e., if `refs` is not `NULL`) the
* command of the `i`-th actuator is `cmds[i] + refs[i]`; otherwise the command
* of the `i`-th actuator is `cmds[i]`. On return, `cmds[i]` is set with the
* commands effectively applied to the deformable mirror (i.e., after
* accounting for clipping and filtering of bad values) with the reference
* commands subtracted if specified.
* The requested command for the `i`-th actuator is `cmds[i]`. On return,
* `cmds[i]` is set with the commands effectively applied to the deformable
* mirror (i.e., after accounting for clipping and filtering of bad values).
*
* @param dev Address of Alpao mirror instance.
*
......@@ -90,7 +88,6 @@ extern void alpao_close_mirror(
extern tao_status alpao_send_commands(
alpao_mirror* dev,
double *cmds,
const double* refs,
long n);
/**
......
......@@ -21,14 +21,6 @@
#include "tao-generic.h"
#include "tao-remote-mirrors-private.h"
// Offset of the layout indices in a remote mirror.
#define LAYOUT_OFFSET_IN_REMOTE_MIRROR \
TAO_ROUND_UP(sizeof(tao_remote_mirror), sizeof(long))
// Offset of the reference values in a data-frame.
#define DATA_OFFSET_IN_DATAFRAME \
TAO_ROUND_UP(sizeof(tao_dataframe_header), sizeof(double))
// Yields number of bytes rounded-up to `TAO_ALIGNMENT`.
static inline size_t aligned(size_t size)
{
......@@ -38,39 +30,22 @@ static inline size_t aligned(size_t size)
//-----------------------------------------------------------------------------
// REMOTE DEFORMABLE MIRROR
// Serial number must be > 0
// Yields n-th output buffer, serial number n must be > 0.
static inline void* remote_object_get_buffer(
const tao_remote_object* obj,
tao_serial serial)
tao_serial n)
{
size_t offset = obj->offset + ((serial - 1)%obj->nbufs)*obj->stride;
size_t offset = obj->offset + ((n - 1)%obj->nbufs)*obj->stride;
return TAO_COMPUTED_ADDRESS(obj, offset);
}
static inline long* remote_mirror_get_inds(
const tao_remote_mirror* obj)
{
return TAO_COMPUTED_ADDRESS(obj, LAYOUT_OFFSET_IN_REMOTE_MIRROR);
}
static inline double* remote_mirror_get_refs(
const tao_remote_mirror* obj)
{
return TAO_COMPUTED_ADDRESS(obj, obj->refs_offset);
}
static inline double* remote_mirror_get_req_cmds(
const tao_remote_mirror* obj)
// Yields the DM interbal work-space to store actuators values (the refrence,
// then the perturbations, then the requested commands, then the actual
// commands).
static inline double* get_values(
const tao_remote_mirror* dm)
{
return TAO_COMPUTED_ADDRESS(
obj, obj->refs_offset + obj->nacts*sizeof(double));
}
static inline double* remote_mirror_get_act_cmds(
const tao_remote_mirror* obj)
{
return TAO_COMPUTED_ADDRESS(
obj, obj->refs_offset + 2*obj->nacts*sizeof(double));
return (double*)TAO_COMPUTED_ADDRESS(dm, dm->vals_offset);
}
// Check whether the server owning the remote object is alive.
......@@ -86,13 +61,12 @@ static inline bool is_alive(
static inline tao_dataframe_header* fetch_frame(
const tao_remote_mirror* obj,
tao_serial serial,
double** refs,
double** cmds)
double** data)
{
tao_dataframe_header* header = remote_object_get_buffer(
tao_remote_object_cast(obj), serial);
*refs = TAO_COMPUTED_ADDRESS(header, DATA_OFFSET_IN_DATAFRAME);
*cmds = (*refs) + obj->nacts;
*data = TAO_COMPUTED_ADDRESS(
header, TAO_ROUND_UP(sizeof(*header), sizeof(double)));
return header;
}
......@@ -141,17 +115,18 @@ tao_remote_mirror* tao_remote_mirror_create(
return NULL;
}
// Compute sizes and offsets of members.
size_t inds_size = ninds*sizeof(long);
size_t cmds_size = nacts*sizeof(double);
size_t inds_offset = LAYOUT_OFFSET_IN_REMOTE_MIRROR;
size_t refs_offset = TAO_ROUND_UP(inds_offset + inds_size, sizeof(double));
size_t inds_offset = TAO_OFFSET_OF(tao_remote_mirror, inds);
size_t vals_offset = TAO_ROUND_UP(inds_offset + inds_size, sizeof(double));
// Compute sizes and offsets of buffers. The stride is the (rounded) size
// of a data-frame buffer (header + refs + cmds).
size_t offset = aligned(refs_offset + 3*cmds_size);
size_t stride = aligned(DATA_OFFSET_IN_DATAFRAME + 2*cmds_size);
size_t offset = aligned(vals_offset + 4*cmds_size);
size_t stride = aligned(
TAO_ROUND_UP(sizeof(tao_dataframe_header), sizeof(double))
+ 4*cmds_size);
// Total size of instance.
size_t size = offset + nbufs*stride;
......@@ -167,10 +142,10 @@ tao_remote_mirror* tao_remote_mirror_create(
tao_forced_store(&obj->nacts, nacts);
tao_forced_store(&obj->dims[0], dim1);
tao_forced_store(&obj->dims[1], dim2);
tao_forced_store(&obj->refs_offset, refs_offset);
tao_forced_store(&obj->vals_offset, vals_offset);
tao_forced_store(&obj->cmin, cmin);
tao_forced_store(&obj->cmax, cmax);
long* dest_inds = remote_mirror_get_inds(obj);
long* dest_inds = (long*)obj->inds;
for (long i = 0; i < ninds; ++i) {
if (inds[i] >= 0) {
dest_inds[i] = inds[i];
......@@ -178,6 +153,11 @@ tao_remote_mirror* tao_remote_mirror_create(
dest_inds[i] = -1;
}
}
double cavg = (cmin + cmax)/2;
double* refs = get_values(obj);
for (long i = 0; i < nacts; ++i) {
refs[i] = cavg;
}
return obj;
}
......@@ -217,7 +197,7 @@ const long* tao_remote_mirror_get_layout(
dims[0] = obj->dims[0];
dims[1] = obj->dims[1];
}
return remote_mirror_get_inds(obj);
return obj->inds;
}
}
......@@ -227,7 +207,6 @@ double tao_remote_mirror_get_cmin(
return (obj == NULL) ? NAN : obj->cmin;
}
double tao_remote_mirror_get_cmax(
const tao_remote_mirror* obj)
{
......@@ -237,19 +216,7 @@ double tao_remote_mirror_get_cmax(
double* tao_remote_mirror_get_reference(
const tao_remote_mirror* obj)
{
return (obj == NULL) ? NULL : remote_mirror_get_refs(obj);
}
double* tao_remote_mirror_get_requested_commands(
const tao_remote_mirror* obj)
{
return (obj == NULL) ? NULL : remote_mirror_get_req_cmds(obj);
}
double* tao_remote_mirror_get_actual_commands(
const tao_remote_mirror* obj)
{
return (obj == NULL) ? NULL : remote_mirror_get_act_cmds(obj);
return (obj == NULL) ? NULL : get_values(obj);
}
static tao_serial send_simple_command(
......@@ -309,7 +276,7 @@ tao_serial tao_remote_mirror_set_reference(
}
// Prepare for i/o.
double* dest = remote_mirror_get_refs(obj);
double* dest = get_values(obj);
// Wait until server be ready for a new command.
tao_serial num = tao_remote_object_lock_for_command(
......@@ -353,7 +320,7 @@ tao_serial tao_remote_mirror_send_commands(
}
// Prepare for i/o.
double* dest = remote_mirror_get_req_cmds(obj);
double* dest = get_values(obj) + 2*obj->nacts;
// Wait until server ready for a new command.
tao_serial num = tao_remote_object_lock_for_command(
......@@ -381,8 +348,10 @@ tao_serial tao_remote_mirror_send_commands(
tao_status tao_remote_mirror_fetch_data(
const tao_remote_mirror* obj,
tao_serial serial,
double* refs,
double* cmds,
double* vals_0,
double* vals_1,
double* vals_2,
double* vals_3,
long nvals,
tao_dataframe_info* info)
{
......@@ -396,7 +365,8 @@ tao_status tao_remote_mirror_fetch_data(
tao_store_error(__func__, TAO_CORRUPTED);
return TAO_ERROR;
}
if (nvals != ((refs != NULL || cmds != NULL) ? obj->nacts : 0)) {
if ((vals_0 != NULL || vals_1 != NULL ||
vals_2 != NULL || vals_3 != NULL) && nvals != obj->nacts) {
tao_store_error(__func__, TAO_BAD_SIZE);
return TAO_ERROR;
}
......@@ -409,23 +379,27 @@ tao_status tao_remote_mirror_fetch_data(
if (info != NULL) {
memset(info, 0, sizeof(*info));
}
if (refs != NULL) {
memset(refs, 0, nbytes);
if (vals_0 != NULL) {
memset(vals_0, 0, nbytes);
}
if (vals_1 != NULL) {
memset(vals_1, 0, nbytes);
}
if (cmds != NULL) {
memset(cmds, 0, nbytes);
if (vals_2 != NULL) {
memset(vals_2, 0, nbytes);
}
if (vals_3 != NULL) {
memset(vals_3, 0, nbytes);
}
} else {
// FIXME: check whether serial is too high, server is running, etc.
double* src_refs;
double* src_cmds;
const tao_dataframe_header* header = fetch_frame(
obj, serial, &src_refs, &src_cmds);
double* data;
const tao_dataframe_header* header = fetch_frame(obj, serial, &data);
if (atomic_load(&header->serial) != serial) {
if (info != NULL) {
info->serial = 0;
info->mark = 0;
info->time = TAO_TIME(0, 0);
info->time = TAO_UNKNOWN_TIME;
}
return TAO_TIMEOUT;
}
......@@ -434,80 +408,42 @@ tao_status tao_remote_mirror_fetch_data(
info->mark = header->mark;
info->time = header->time;
}
if (refs != NULL) {
memcpy(refs, src_refs, nbytes);
if (vals_0 != NULL) {
memcpy(vals_0, data, nbytes);
}
if (vals_1 != NULL) {
memcpy(vals_1, data + obj->nacts, nbytes);
}
if (vals_2 != NULL) {
memcpy(vals_2, data + 2*obj->nacts, nbytes);
}
if (cmds != NULL) {
memcpy(cmds, src_cmds, nbytes);
if (vals_3 != NULL) {
memcpy(vals_3, data + 3*obj->nacts, nbytes);
}
if (atomic_load(&header->serial) != serial) {
// FIXME: This is an overwrite error!
if (info != NULL) {
info->serial = -1;
info->mark = -1;
info->time = TAO_UNKNOWN_TIME;
}
return TAO_TIMEOUT;
}
}
return TAO_OK;
}
/**
* Publish deformable mirror data-frame.
*
* This function shall be called by a deformable mirror server to store the
* contents of a deformable mirror data-frame in shared memory. The serial
* number of the published data-frame is incremented and other processes get
* notified that the shared data have changed.
*
* The remote deformable mirror must be locked by the caller
*
* @param obj Pointer to remote deformable mirror.
*
* @return @ref TAO_OK on success, @ref TAO_ERROR in case of failure.
*/
static tao_status publish_dataframe(
tao_remote_mirror* obj)
// Clamp x in the range [a,b]. a ≤ b must hold and a and b must be finite.
// Yields NaN if x is a NaN.
static inline double clamp(
double x,
double a,
double b)
{
// Check arguments.
if (obj == NULL) {
tao_store_error(__func__, TAO_BAD_ADDRESS);
return TAO_ERROR;
}
long nacts = obj->nacts;
if (nacts < 1 || obj->base.nbufs < 2) {
tao_store_error(__func__, TAO_CORRUPTED);
return TAO_ERROR;
}
if (obj->base.serial < 0) {
tao_store_error(__func__, TAO_BAD_SERIAL);
return TAO_ERROR;
}
// Increment counter of published frames.
tao_serial serial = ++obj->base.serial;
// Obtain publication time.
tao_time time;
tao_status status = tao_get_monotonic_time(&time);
if (status != TAO_OK) {
return TAO_ERROR;
}
// Write data-frame header and data. First set the data-frame serial
// number to zero to let others know that data-frame is being overwritten.
// Finally set the data-frame serial number to its value when all contents
// has been updated.
const double* src_refs = remote_mirror_get_refs(obj);
const double* src_cmds = remote_mirror_get_act_cmds(obj);
double* dst_refs;
double* dst_cmds;
tao_dataframe_header* header = fetch_frame(
obj, serial, &dst_refs, &dst_cmds);
size_t nbytes = nacts*sizeof(double);
atomic_store(&header->serial, 0); // 0 indicates invalid data-frame
header->mark = obj->mark;
header->time = time;
memcpy(dst_refs, src_refs, nbytes);
memcpy(dst_cmds, src_cmds, nbytes);
atomic_store(&header->serial, serial); // ≥ 1 indicates valid data-frame
return status;
// The following expression assumes that a comparison with a NaN always
// yields false and that only x can be a NaN.
double y = (x < a ? a : x); // the greatest of x and a; NaN, if x is a NaN
return (y > b ? b : y); // the least of y and b; NaN, if y is a NaN
}
tao_status tao_remote_mirror_run_loop(
......@@ -538,6 +474,16 @@ tao_status tao_remote_mirror_run_loop(
return TAO_ERROR;
}
// Extract members whose values are constant for the life-time of the DM.
long nacts = obj->nacts;
double* cmd_0 = get_values(obj); // reference commands
double* cmd_1 = cmd_0 + nacts; // perturbation commands
double* cmd_2 = cmd_1 + nacts; // requested commands
double* cmd_3 = cmd_2 + nacts; // actual commands
double cmin = obj->cmin; // min. actuator command
double cmax = obj->cmax; // max. actuator command
double cavg = (cmin + cmax)/2; // assumed command for a NaN
// Set state to indicate that initialization is completed.
obj->base.state = TAO_STATE_WAITING;
......@@ -551,7 +497,6 @@ tao_status tao_remote_mirror_run_loop(
// Run loop (on entry of the loop we own the lock on the remote mirror
// instance).
bool publish = false;
while (true) {
// Wait for next command.
while (status == TAO_OK && is_alive(obj)
......@@ -563,55 +508,84 @@ tao_status tao_remote_mirror_run_loop(
}
// Execute command.
if (obj->base.command == TAO_COMMAND_RESET) {
tao_command command = obj->base.command;
const char* command_name = tao_command_get_name(command);
if (command == TAO_COMMAND_RESET || command == TAO_COMMAND_SEND) {
if (ops->debug) {
fprintf(stderr, "%s: execute \"reset\" command\n", ops->name);
fprintf(stderr, "%s: Execute \"%s\" command\n",
command_name, ops->name);
}
status = ops->on_reset(obj, ctx);
if (status != TAO_OK) {
if (ops->debug) {