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

Very preliminary version of interface to FLI cameras

parent b86591a6
FLILogs
\ No newline at end of file
#
# Makefile.am -
#
# Rules to build TAO auxiliary library for FLI cameras.
#
#------------------------------------------------------------------------------
#
# This file if part of TAO real-time software licensed under the MIT license
# (https://git-cral.univ-lyon1.fr/tao/tao-rt).
#
# Settings for the FLI SDK.
FLI_INCDIR = @FLI_INCDIR@
FLI_LIBDIR = @FLI_LIBDIR@
FLI_DEFS = @FLI_DEFS@
FLI_LIBS = @FLI_LIBS@
# Other libraries.
OTHER_LIBS = -lpthread -lusb-1.0 $(LIBM)
# Settings for TAO library and its components.
TAO_DEFS = -I$(srcdir)/../base
TAO_LIBS = ../base/libtao.la $(OTHER_LIBS)
#TAO_FLI_DEPS = $(FLI_DEPS)
TAO_FLI_LIBS = libtao-fli.la $(FLI_LIBS)
# Common settings for all targets.
COMMON_DEFS = -I$(srcdir) $(TAO_DEFS) $(FLI_DEFS)
COMMON_LIBS = $(TAO_FLI_LIBS) $(TAO_LIBS)
# Define AM_... macros to simplify per-target settings.
#
# Note: If target_CFLAGS is defined, $(target_CFLAGS) and $(CFLAGS) are used;
# otherwise, $(AM_CFLAGS) and $(CFLAGS) used. This rule applies for
# CPPFLAGS, LDFLAGS and many others (see Automake documentation).
#
AM_CPPFLAGS = $(COMMON_DEFS)
lib_LTLIBRARIES = libtao-fli.la
include_HEADERS = tao-fli-cameras.h
libtao_fli_la_SOURCES = \
fli-core.c
libtao_fli_la_LIBADD = $(FLI_LIBS) $(TAO_LIBS)
libtao_fli_la_LDFLAGS = -version-info @version_info@
bin_PROGRAMS = fli_server
fli_server_SOURCES = fli-server.c
fli_server_CPPFLAGS = $(COMMON_DEFS)
fli_server_LDADD = $(COMMON_LIBS)
// fli-core.h -
//
// Implementation of the interface to FLI (Finger Lakes Instrumentation)
// cameras in TAO (a Toolkit for Adaptive Optics).
//
//-----------------------------------------------------------------------------
//
// This file if part of TAO real-time software licensed under the MIT license
// (https://git-cral.univ-lyon1.fr/tao/tao-rt).
#include <stdio.h>
#include <math.h>
#include "libfipro.h"
#include "tao-fli-cameras.h" // for the prototype of tao_foo_camera_open
#include "tao-threads.h" // for mutexes
#include "tao-errors.h" // for error management
#include "tao-cameras-private.h" // for the definition of tao_camera
// Assumed maximum number of connected cameras.
#define MAX_CONNECTED_CAMERAS 8
// List of available cameras and mutex to protect access to this list.
FPRODEVICEINFO device_list[MAX_CONNECTED_CAMERA];
long ndevices = 0;
tao_mutex mutex = TAO_MUTEX_INITIALIZER;
// Retrieve list of connected devices. Caller must have locked the resource.
static tao_status update_device_list();
// A handle fo a camera (see <libfipro.h>).
typedef int32 fli_handle;
typedef struct fli_camera_ {
tao_camera base; // A FLI camera is also a TAO camera.
fli_handle handle;
} fli_camera;
// Function to deal with errors.
static void fli_error(const char* func, int code);
// All functions of the FLI SDK return either nothing (`void`) or
// an integer code which is nonnegative on success and negative on error.
// The following macro is a helper to report errors with the name of the
// function from the FLI SDK.
#define FLI_CALL(func, args, on_error) \
do { \
int code = func args; \
if (code < 0) { \
fli_error(#func, code); \
on_error; \
} \
} while (0)
// Set the exposure time (in seconds) and the frame rate (in frames per
// second).
static tao_status set_exposure(
fli_camera* dev,
double exposure,
double fps);
//-----------------------------------------------------------------------------
// Declaration of private methods and of the table grouping these methods for
// the unified camera API.
static tao_status on_initialize(
tao_camera* cam);
static tao_status on_finalize(
tao_camera* cam);
static tao_status on_reset(
tao_camera* cam);
static tao_status on_update_config(
tao_camera* cam);
static tao_status on_check_config(
tao_camera* cam,
const tao_camera_config* cfg);
static tao_status on_set_config(
tao_camera* cam,
const tao_camera_config* cfg);
static tao_status on_start(
tao_camera* cam);
static tao_status on_stop(
tao_camera* cam);
static tao_status on_wait_buffer(
tao_camera* cam,
tao_acquisition_buffer* buf,
double secs,
int drop);
static tao_camera_ops ops = {
.name = "FLICamera",
.initialize = on_initialize,
.finalize = on_finalize,
.reset = on_reset,
.update_config = on_update_config,
.check_config = on_check_config,
.set_config = on_set_config,
.start = on_start,
.stop = on_stop,
.wait_buffer: on_wait_buffer
};
//-----------------------------------------------------------------------------
// Definition of the public function to connect to a Foo camera.
tao_camera* tao_foo_camera_open(
int num)
{
// First lock global resources.
if (tao_mutex_lock(&mutex) != TAO_OK) {
return NULL;
}
// Initialize result to NULL in case of error.
tao_camera* cam = NULL;
// Update list of devices.
if (update_device_list() != TAO_OK) {
goto unlock;
}
// Check whether camera index is in range.
if (num < 0 || num >= ndevices) {
tao_store_error(__func__, TAO_BAD_DEVICE);
goto unlock;
}
// Allocate camera instance with the device information provided as
// contextual data. The remaining initialization will done by the
// `on_initialize` virtual method.
cam = tao_camera_create(&ops, &device_list[num], sizeof(fli_camera));
if (cam != NULL) {
// Context, i.e. device information, must no longer be referenced.
cam->ctx = NULL;
}
// Eventually unlock global resources.
unlock:
if (tao_mutex_unlock(&mutex) != TAO_OK) {
// Failed to unlock resources, destroy the camera if created.
if (cam != NULL) {
tao_camera_destroy(cam);
cam = NULL;
}
}
return cam;
}
static tao_status on_initialize(
tao_camera* cam)
{
// The camera is a FLI camera.
fli_camera* dev = (tao_fli_camera*)cam;
// The contextual data is the device information.
FPRODEVICEINFO* devinfo = cam->ctx;
// Open the camera.
dev->handle = -1;
FLI_CALL(FPROCam_Open,(devinfo, &dev->handle), return TAO_ERROR);
if (dev->handle < 0) {
tao_store_error(__func__, TAO_BAD_DEVICE);
return TAO_ERROR;
}
// Retrieve camera capabilities.
FPROCAP cap;
uint32_t size = sizeof(cap);
FLI_CALL(FPROSensor_GetCapabilities,(dev->handle, &cap, &size),
return TAO_ERROR);
cam->info.sensorwidth = cap.uiMaxPixelImageWidth;
cam->info.sensorheight = cap.uiMaxPixelImageHeight;
return TAO_OK;
}
static tao_status on_finalize(
tao_camera* cam)
{
// The camera is a FLI camera.
fli_camera* dev = (tao_fli_camera*)cam;
// Close the device.
if (dev->handle >= 0) {
fli_handle = handle = dev->handle;
dev->handle = -1; // never close more than once
FLI_CALL(FPROCam_Close,(handle), return TAO_ERROR);
}
return TAO_OK;
}
static tao_status on_reset(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_update_config(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_check_config(
tao_camera* cam,
const tao_camera_config* cfg)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_set_config(
tao_camera* cam,
const tao_camera_config* cfg)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_start(
tao_camera* cam)
{
// See FPROFrame_CaptureStart();
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_stop(
tao_camera* cam)
{
// See FPROFrame_CaptureStop();
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_wait_buffer(
tao_camera* cam,
tao_acquisition_buffer* buf,
double secs,
int drop)
{
// NOTE: Call FPROFrame_GetVideoFrame() or FPROFrame_GetVideoFrameEx() with
// a secondary thread that may call FPROFrame_CaptureAbort() to
// cancel the acquisition.
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
//-----------------------------------------------------------------------------
// UTILITIES.
// Retrieve list of connected cameras. Caller must have locked the resource.
static tao_status update_device_list()
{
uint32_t num = MAX_CONNECTED_CAMERAS;
FLI_CALL(FPROCam_GetCameraList, (device_list, &num), return TAO_ERROR);
if (num >= MAX_CONNECTED_CAMERAS) {
fprint(stderr,
"Warning: There may be more than %d connected cameras. You "
"should change\n the value of macro "
"`MAX_CONNECTED_CAMERAS` in file `%s`\n",
MAX_CONNECTED_CAMERAS, __FILE__);
}
ndevices = num;
return TAO_OK;
}
#define NANOSECONDS_PER_SECOND 1e9
// Set the exposure time (in seconds) and the frame rate (in frames per
// second).
static tao_status set_exposure(
fli_camera* dev,
double exposure,
double fps)
{
// In the SDK, the exposure time and the frame delay are in nanoseconds.
uint64_t exposure_ns = lround(exposure*NANOSECONDS_PER_SECOND);
uint64_t framedelay_ns = lround(NANOSECONDS_PER_SECOND/framerate)
- exposuretime_ns;
uint64_t actual_exposure_ns;
uint64_t actual_framedelay_ns;
FLI_CALL(Ctrl_SetExposureEx,
(dev->handle, exposure_ns, framedelay_ns, false,
&actual_exposure_ns, &actual_framedelay_ns), return TAO_ERROR);
dev->base.info.config.exposuretime =
actual_exposuretime_ns/NANOSECONDS_PER_SECOND;
dev->base.info.config.framerate =
NANOSECONDS_PER_SECOND/(actual_exposuretime_ns
+ actual_framedelay_ns);
return TAO_OK;
}
//-----------------------------------------------------------------------------
// ERROR MANAGEMENT.
static void get_error_info(
int code,
const char** reason_ptr,
const char** info_ptr)
{
// For now, we do not provide any additional error information. TAO will
// simply reports the error code and the name of the function where the
// error occurred.
if (reason_ptr != NULL) {
*reason_ptr = NULL;
}
if (info_ptr != NULL) {
*info_ptr = NULL;
}
}
static void fli_error(
const char* func,
int code)
{
tao_store_other_error(func, code, get_error_info);
}
// tao-fli-cameras.h -
//
// Declarations of the interface to FLI (Finger Lakes Instrumentation)
// cameras in TAO (a Toolkit for Adaptive Optics).
//
//-----------------------------------------------------------------------------
//
// This file if part of TAO real-time software licensed under the MIT license
// (https://git-cral.univ-lyon1.fr/tao/tao-rt).
#ifndef TAO_FLI_CAMERAS_H_
#define TAO_FLI_CAMERAS_H_ 1
#include <tao-cameras.h>
TAO_BEGIN_DECLS
/// @defgroup FliCameras Interface to FLI (Finger Lakes Instrumentation)
/// cameras.
///
/// @ingroup Cameras
///
/// @brief Interface to Fli (Finger Lakes Instrumentation) cameras.
///
/// @{
/// @brief Open a Fli camera.
///
/// This function connects to the Fli camera identified by `id` and returns
/// a handle to manage it. The caller is responsible of eventually calling
/// `tao_camera_destroy` to close the camera device and release associated
/// resources.
///
/// @param num Number of the camera.
///
/// @return The address of the new (initialized) camera; `NULL` in case of
/// failure.
///
extern tao_camera* tao_fli_camera_open(int num);
/// @}
TAO_END_DECLS
#endif // TAO_FLI_CAMERAS_H_
# Interfacing a new camera type in TAO
Implementing an interface to a new type of cameras, say the infamous `Foo`
cameras, in TAO requires at least 2 files:
- a public header file;
- a C code file.
## Public header file
The minimal public header file, say `tao-foo-cameras.h`, is something like:
```{.c}
#ifndef TAO_FOO_CAMERAS_H_
#define TAO_FOO_CAMERAS_H_ 1
#include <tao-cameras.h>
TAO_BEGIN_DECLS
/// @defgroup FooCameras Interface to Foo cameras.
///
/// @ingroup Cameras
///
/// @brief Interface to Foo cameras.
///
/// Some description of this module.
///
/// @{
/// @brief Open a Foo camera.
///
/// This function connects to the Foo camera identified by `id` and returns
/// a handle to manage it. The caller is responsible of eventually calling
/// `tao_camera_destroy` to close the camera device and release associated
/// resources.
///
/// @param id Identifier of the camera.
///
/// @return The address of the new (initialized) camera; `NULL` in case of
/// failure.
///
extern tao_camera* tao_foo_camera_open(int id);
/// @}
TAO_END_DECLS
#endif // TAO_FOO_CAMERAS_H_
```
Preprocessor directives with the `TAO_FOO_CAMERAS_H_` macro are to allow for
multiple includes of the file without breaking the compilation.
Most comments are to provide some documentation that can be automatically built
by [Doxygen](https://www.doxygen.org/index.html).
The surrounding by the macros `TAO_BEGIN_DECLS` and `TAO_END_DECLS` is to be
able to link with C++ in spite of TAO libraries being written in C for maximum
portability. These macros are provided by `<tao-basics.h>` which is
automatically included by `<tao-cameras.h>`.
The only mandatory public declaration is the prototype of the function that
can be called to connect to a given `Foo` camera. Argument list may be
anything, the return value must however be a pointer to a `tao_camera`
structure which is created by calling `tao_camera_create()`.
# Template C code
Below is a template of the C code that you have to fill to implement the
unified camera interface. This interface consists in 9 functions. In this
template, all these functions return an error indicating that they are not yet
implemented (of course since supporting these family of cameras is new in TAO).
```{.c}
#include "tao-foo-cameras.h" // needed for the prototype of tao_foo_camera_open
#include "tao-errors.h" // for error management
#include "tao-cameras-private.h" // for the definition of tao_camera structure
//-----------------------------------------------------------------------------
// Declaration of private methods and of the table grouping these methods for
// the unified camera API.
static tao_status on_initialize(
tao_camera* cam);
static tao_status on_finalize(
tao_camera* cam);
static tao_status on_reset(
tao_camera* cam);
static tao_status on_update_config(
tao_camera* cam);
static tao_status on_check_config(
tao_camera* cam,
const tao_camera_config* cfg);
static tao_status on_set_config(
tao_camera* cam,
const tao_camera_config* cfg);
static tao_status on_start(
tao_camera* cam);
static tao_status on_stop(
tao_camera* cam);
static tao_status on_wait_buffer(
tao_camera* cam,
tao_acquisition_buffer* buf,
double secs,
int drop);
static tao_camera_ops ops = {
.name = "FooCamera", // FIXME: Change the name!
.initialize = on_initialize,
.finalize = on_finalize,
.reset = on_reset,
.update_config = on_update_config,
.check_config = on_check_config,
.set_config = on_set_config,
.start = on_start,
.stop = on_stop,
.wait_buffer: on_wait_buffer
};
//-----------------------------------------------------------------------------
// Definition of the public function to connect to a Foo camera.
tao_camera* tao_foo_camera_open(
int id)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return NULL;
}
//-----------------------------------------------------------------------------
// Definition of the methods to manage Foo cameras.
static tao_status on_initialize(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_finalize(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_reset(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_update_config(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_check_config(
tao_camera* cam,
const tao_camera_config* cfg)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_set_config(
tao_camera* cam,
const tao_camera_config* cfg)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_start(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_stop(
tao_camera* cam)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
static tao_status on_wait_buffer(
tao_camera* cam,
tao_acquisition_buffer* buf,
double secs,
int drop)
{
tao_store_error(__func__, TAO_NOT_YET_IMPLEMENTED);
return TAO_ERROR;
}
```