diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | plugins/mock/CMakeLists.txt | 18 | ||||
| -rw-r--r-- | plugins/mock/uca-mock-camera.c | 409 | ||||
| -rw-r--r-- | plugins/mock/uca-mock-camera.h | 65 | ||||
| -rw-r--r-- | plugins/package-plugin.sh.in | 1 | ||||
| -rw-r--r-- | plugins/pco/CMakeLists.txt | 30 | ||||
| -rw-r--r-- | plugins/pco/uca-pco-camera.c | 1398 | ||||
| -rw-r--r-- | plugins/pco/uca-pco-camera.h | 91 | ||||
| -rw-r--r-- | plugins/pf/CMakeLists.txt | 30 | ||||
| -rw-r--r-- | plugins/pf/uca-pf-camera.c | 351 | ||||
| -rw-r--r-- | plugins/pf/uca-pf-camera.h | 74 | ||||
| -rw-r--r-- | plugins/pylon/CMakeLists.txt | 24 | ||||
| -rw-r--r-- | plugins/pylon/uca-pylon-camera.c | 391 | ||||
| -rw-r--r-- | plugins/pylon/uca-pylon-camera.h | 74 | ||||
| -rw-r--r-- | plugins/ufo/CMakeLists.txt | 24 | ||||
| -rw-r--r-- | plugins/ufo/uca-ufo-camera.c | 497 | ||||
| -rw-r--r-- | plugins/ufo/uca-ufo-camera.h | 76 | 
17 files changed, 3558 insertions, 0 deletions
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000..5131280 --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(mock) +add_subdirectory(pf) +add_subdirectory(pco) +add_subdirectory(pylon) +add_subdirectory(ufo) diff --git a/plugins/mock/CMakeLists.txt b/plugins/mock/CMakeLists.txt new file mode 100644 index 0000000..75d8635 --- /dev/null +++ b/plugins/mock/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.8) +project(ucamock) + +set(UCA_CAMERA_NAME "mock") + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../package-plugin.sh.in +               ${CMAKE_CURRENT_BINARY_DIR}/../../package-plugin-${UCA_CAMERA_NAME}.sh) + +add_library(ucamock SHARED +            uca-mock-camera.c) + +target_link_libraries(ucamock +                      uca +                      ${UCA_DEPS}) + +install(TARGETS ucamock +        LIBRARY DESTINATION ${LIB_INSTALL_DIR}/uca +        COMPONENT ${UCA_CAMERA_NAME}) diff --git a/plugins/mock/uca-mock-camera.c b/plugins/mock/uca-mock-camera.c new file mode 100644 index 0000000..7cd4689 --- /dev/null +++ b/plugins/mock/uca-mock-camera.c @@ -0,0 +1,409 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#include <gmodule.h> +#include <string.h> +#include "uca-mock-camera.h" + +#define UCA_MOCK_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_MOCK_CAMERA, UcaMockCameraPrivate)) + +G_DEFINE_TYPE(UcaMockCamera, uca_mock_camera, UCA_TYPE_CAMERA) + +enum { +    PROP_FRAMERATE = N_BASE_PROPERTIES, +    N_PROPERTIES +}; + +static const gint mock_overrideables[] = { +    PROP_NAME, +    PROP_SENSOR_WIDTH, +    PROP_SENSOR_HEIGHT, +    PROP_SENSOR_BITDEPTH, +    PROP_SENSOR_HORIZONTAL_BINNING, +    PROP_SENSOR_HORIZONTAL_BINNINGS, +    PROP_SENSOR_VERTICAL_BINNING, +    PROP_SENSOR_VERTICAL_BINNINGS, +    PROP_TRIGGER_MODE, +    PROP_EXPOSURE_TIME, +    PROP_ROI_X, +    PROP_ROI_Y, +    PROP_ROI_WIDTH, +    PROP_ROI_HEIGHT, +    PROP_ROI_HEIGHT_MULTIPLIER, +    PROP_ROI_WIDTH_MULTIPLIER, +    PROP_SENSOR_MAX_FRAME_RATE, +    PROP_HAS_STREAMING, +    PROP_HAS_CAMRAM_RECORDING, +    0, +}; + +static GParamSpec *mock_properties[N_PROPERTIES] = { NULL, }; + +struct _UcaMockCameraPrivate { +    guint width; +    guint height; +    guint roi_x, roi_y, roi_width, roi_height; +    gfloat frame_rate; +    gfloat max_frame_rate; +    gdouble exposure_time; +    guint8 *dummy_data; +    guint current_frame; + +    gboolean thread_running; + +    GThread *grab_thread; +    GValueArray *binnings; +}; + +static const char g_digits[10][20] = { +    /* 0 */ +    { 0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0xff, 0x00, 0x00, 0xff, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0x00 }, +    /* 1 */ +    { 0x00, 0x00, 0xff, 0x00, +      0x00, 0xff, 0xff, 0x00, +      0x00, 0x00, 0xff, 0x00, +      0x00, 0x00, 0xff, 0x00, +      0x00, 0x00, 0xff, 0x00 }, +    /* 2 */ +    { 0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0x00, 0xff, 0x00, +      0x00, 0xff, 0x00, 0x00, +      0xff, 0xff, 0xff, 0xff }, +    /* 3 */ +    { 0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0x00, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0x00 }, +    /* 4 */ +    { 0xff, 0x00, 0x00, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0xff, 0xff, 0xff, 0xff, +      0x00, 0x00, 0x00, 0xff, +      0x00, 0x00, 0x00, 0xff }, +    /* 5 */ +    { 0xff, 0xff, 0xff, 0xff, +      0xff, 0x00, 0x00, 0x00, +      0x00, 0xff, 0xff, 0x00, +      0x00, 0x00, 0x00, 0xff, +      0xff, 0xff, 0xff, 0x00 }, +    /* 6 */ +    { 0x00, 0xff, 0xff, 0xff, +      0xff, 0x00, 0x00, 0x00, +      0xff, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0x00 }, +    /* 7 */ +    { 0xff, 0xff, 0xff, 0xff, +      0x00, 0x00, 0x00, 0xff, +      0x00, 0x00, 0xff, 0x00, +      0x00, 0xff, 0x00, 0x00, +      0xff, 0x00, 0x00, 0x00 }, +    /* 8 */ +    { 0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0x00 }, +    /* 9 */ +    { 0x00, 0xff, 0xff, 0x00, +      0xff, 0x00, 0x00, 0xff, +      0x00, 0xff, 0xff, 0xff, +      0x00, 0x00, 0x00, 0xff, +      0xff, 0xff, 0xff, 0x00 } +}; + +static const guint DIGIT_WIDTH = 4; +static const guint DIGIT_HEIGHT = 5; + +static void print_number(gchar *buffer, guint number, guint x, guint y, guint width) +{ +    for (int i = 0; i < DIGIT_WIDTH; i++) { +        for (int j = 0; j < DIGIT_HEIGHT; j++) { +            buffer[(y+j)*width + (x+i)] = g_digits[number][j*DIGIT_WIDTH+i]; +        } +    } +} + +static void print_current_frame(UcaMockCameraPrivate *priv, gchar *buffer) +{ +    guint number = priv->current_frame; +    guint divisor = 100000000; +    int x = 10; +    while (divisor > 1) { +        print_number(buffer, number / divisor, x, 10, priv->width); +        number = number % divisor; +        divisor = divisor / 10; +        x += DIGIT_WIDTH + 1; +    } +} + +static gpointer mock_grab_func(gpointer data) +{ +    UcaMockCamera *mock_camera = UCA_MOCK_CAMERA(data); +    g_return_val_if_fail(UCA_IS_MOCK_CAMERA(mock_camera), NULL); + +    UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(mock_camera); +    UcaCamera *camera = UCA_CAMERA(mock_camera); +    const gulong sleep_time = (gulong) G_USEC_PER_SEC / priv->frame_rate; + +    while (priv->thread_running) { +        camera->grab_func(priv->dummy_data, camera->user_data); +        g_usleep(sleep_time); +    } + +    return NULL; +} + +static void uca_mock_camera_start_recording(UcaCamera *camera, GError **error) +{ +    gboolean transfer_async = FALSE; +    UcaMockCameraPrivate *priv; +    g_return_if_fail(UCA_IS_MOCK_CAMERA(camera)); + +    priv = UCA_MOCK_CAMERA_GET_PRIVATE(camera); +    /* TODO: check that roi_x + roi_width < priv->width */ +    priv->dummy_data = (guint8 *) g_malloc0(priv->roi_width * priv->roi_height); + +    g_object_get(G_OBJECT(camera), +            "transfer-asynchronously", &transfer_async, +            NULL); + +    /* +     * In case asynchronous transfer is requested, we start a new thread that +     * invokes the grab callback, otherwise nothing will be done here. +     */ +    if (transfer_async) { +        GError *tmp_error = NULL; +        priv->thread_running = TRUE; +        priv->grab_thread = g_thread_create(mock_grab_func, camera, TRUE, &tmp_error); + +        if (tmp_error != NULL) { +            priv->thread_running = FALSE; +            g_propagate_error(error, tmp_error); +        } +    } +} + +static void uca_mock_camera_stop_recording(UcaCamera *camera, GError **error) +{ +    gboolean transfer_async = FALSE; +    UcaMockCameraPrivate *priv; +    g_return_if_fail(UCA_IS_MOCK_CAMERA(camera)); + +    priv = UCA_MOCK_CAMERA_GET_PRIVATE(camera); +    g_free(priv->dummy_data); +    priv->dummy_data = NULL; + +    g_object_get(G_OBJECT(camera), +            "transfer-asynchronously", &transfer_async, +            NULL); + +    if (transfer_async) { +        priv->thread_running = FALSE; +        g_thread_join(priv->grab_thread); +    } +} + +static void uca_mock_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +{ +    g_return_if_fail(UCA_IS_MOCK_CAMERA(camera)); +    g_return_if_fail(data != NULL); + +    UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(camera); + +    if (*data == NULL) +        *data = g_malloc0(priv->width * priv->height); + +    g_memmove(*data, priv->dummy_data, priv->width * priv->height); +    print_current_frame(priv, *data); +    priv->current_frame++; +} + +static void uca_mock_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ +    g_return_if_fail(UCA_IS_MOCK_CAMERA(object)); +    UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_EXPOSURE_TIME: +            priv->exposure_time = g_value_get_double(value); +            break; +        case PROP_FRAMERATE: +            priv->frame_rate = g_value_get_float(value); +            break; +        case PROP_ROI_X: +            priv->roi_x = g_value_get_uint(value); +            break; +        case PROP_ROI_Y: +            priv->roi_y = g_value_get_uint(value); +            break; +        case PROP_ROI_WIDTH: +            priv->roi_width = g_value_get_uint(value); +            break; +        case PROP_ROI_HEIGHT: +            priv->roi_height = g_value_get_uint(value); +            break; +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            return; +    } +} + +static void uca_mock_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ +    UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_NAME: +            g_value_set_string(value, "mock camera"); +            break; +        case PROP_SENSOR_WIDTH: +            g_value_set_uint(value, priv->width); +            break; +        case PROP_SENSOR_HEIGHT: +            g_value_set_uint(value, priv->height); +            break; +        case PROP_SENSOR_BITDEPTH: +            g_value_set_uint(value, 8); +            break; +        case PROP_SENSOR_HORIZONTAL_BINNING: +            g_value_set_uint(value, 1); +            break; +        case PROP_SENSOR_HORIZONTAL_BINNINGS: +            g_value_set_boxed(value, priv->binnings); +            break; +        case PROP_SENSOR_VERTICAL_BINNING: +            g_value_set_uint(value, 1); +            break; +        case PROP_SENSOR_VERTICAL_BINNINGS: +            g_value_set_boxed(value, priv->binnings); +            break; +        case PROP_EXPOSURE_TIME: +            g_value_set_double(value, priv->exposure_time); +            break; +        case PROP_TRIGGER_MODE: +            g_value_set_enum(value, UCA_CAMERA_TRIGGER_AUTO); +            break; +        case PROP_ROI_X: +            g_value_set_uint(value, priv->roi_x); +            break; +        case PROP_ROI_Y: +            g_value_set_uint(value, priv->roi_y); +            break; +        case PROP_ROI_WIDTH: +            g_value_set_uint(value, priv->roi_width); +            break; +        case PROP_ROI_HEIGHT: +            g_value_set_uint(value, priv->roi_height); +            break; +        case PROP_ROI_WIDTH_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_ROI_HEIGHT_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_SENSOR_MAX_FRAME_RATE: +            g_value_set_float(value, priv->max_frame_rate); +            break; +        case PROP_HAS_STREAMING: +            g_value_set_boolean(value, TRUE); +            break; +        case PROP_HAS_CAMRAM_RECORDING: +            g_value_set_boolean(value, FALSE); +            break; +        case PROP_FRAMERATE: +            g_value_set_float(value, priv->frame_rate); +            break; +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            break; +    } +} + +static void uca_mock_camera_finalize(GObject *object) +{ +    UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(object); + +    if (priv->thread_running) { +        priv->thread_running = FALSE; +        g_thread_join(priv->grab_thread); +    } + +    g_free(priv->dummy_data); +    g_value_array_free(priv->binnings); + +    G_OBJECT_CLASS(uca_mock_camera_parent_class)->finalize(object); +} + +static void uca_mock_camera_class_init(UcaMockCameraClass *klass) +{ +    GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +    gobject_class->set_property = uca_mock_camera_set_property; +    gobject_class->get_property = uca_mock_camera_get_property; +    gobject_class->finalize = uca_mock_camera_finalize; + +    UcaCameraClass *camera_class = UCA_CAMERA_CLASS(klass); +    camera_class->start_recording = uca_mock_camera_start_recording; +    camera_class->stop_recording = uca_mock_camera_stop_recording; +    camera_class->grab = uca_mock_camera_grab; + +    for (guint i = 0; mock_overrideables[i] != 0; i++) +        g_object_class_override_property(gobject_class, mock_overrideables[i], uca_camera_props[mock_overrideables[i]]); + +    mock_properties[PROP_FRAMERATE] = +        g_param_spec_float("frame-rate", +                "Frame rate", +                "Number of frames per second that are taken", +                1.0f, 100.0f, 100.0f, +                G_PARAM_READWRITE); + +    for (guint id = N_BASE_PROPERTIES; id < N_PROPERTIES; id++) +        g_object_class_install_property(gobject_class, id, mock_properties[id]); + +    g_type_class_add_private(klass, sizeof(UcaMockCameraPrivate)); +} + +static void uca_mock_camera_init(UcaMockCamera *self) +{ +    self->priv = UCA_MOCK_CAMERA_GET_PRIVATE(self); +    self->priv->roi_x = 0; +    self->priv->roi_y = 0; +    self->priv->width = self->priv->roi_width = 640; +    self->priv->height = self->priv->roi_height = 480; +    self->priv->frame_rate = self->priv->max_frame_rate = 100000.0f; +    self->priv->grab_thread = NULL; +    self->priv->current_frame = 0; + +    self->priv->binnings = g_value_array_new(1); +    GValue val = {0}; +    g_value_init(&val, G_TYPE_UINT); +    g_value_set_uint(&val, 1); +    g_value_array_append(self->priv->binnings, &val); +} + +G_MODULE_EXPORT UcaCamera * +uca_camera_impl_new (GError **error) +{ +    UcaCamera *camera = UCA_CAMERA (g_object_new (UCA_TYPE_MOCK_CAMERA, NULL)); +    return camera; +} diff --git a/plugins/mock/uca-mock-camera.h b/plugins/mock/uca-mock-camera.h new file mode 100644 index 0000000..9ee9190 --- /dev/null +++ b/plugins/mock/uca-mock-camera.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#ifndef __UCA_MOCK_CAMERA_H +#define __UCA_MOCK_CAMERA_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_MOCK_CAMERA             (uca_mock_camera_get_type()) +#define UCA_MOCK_CAMERA(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_MOCK_CAMERA, UcaMockCamera)) +#define UCA_IS_MOCK_CAMERA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_MOCK_CAMERA)) +#define UCA_MOCK_CAMERA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_MOCK_CAMERA, UfoMockCameraClass)) +#define UCA_IS_MOCK_CAMERA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_MOCK_CAMERA)) +#define UCA_MOCK_CAMERA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_MOCK_CAMERA, UcaMockCameraClass)) + +typedef struct _UcaMockCamera           UcaMockCamera; +typedef struct _UcaMockCameraClass      UcaMockCameraClass; +typedef struct _UcaMockCameraPrivate    UcaMockCameraPrivate; + +/** + * UcaMockCamera: + * + * Creates #UcaMockCamera instances by loading corresponding shared objects. The + * contents of the #UcaMockCamera structure are private and should only be + * accessed via the provided API. + */ +struct _UcaMockCamera { +    /*< private >*/ +    UcaCamera parent; + +    UcaMockCameraPrivate *priv; +}; + +/** + * UcaMockCameraClass: + * + * #UcaMockCamera class + */ +struct _UcaMockCameraClass { +    /*< private >*/ +    UcaCameraClass parent; +}; + +GType uca_mock_camera_get_type(void); + +G_END_DECLS + +#endif diff --git a/plugins/package-plugin.sh.in b/plugins/package-plugin.sh.in new file mode 100644 index 0000000..c624d52 --- /dev/null +++ b/plugins/package-plugin.sh.in @@ -0,0 +1 @@ +cpack -D CPACK_INSTALL_CMAKE_PROJECTS="${CMAKE_CURRENT_BINARY_DIR}/;Project;${UCA_CAMERA_NAME};/" -D CPACK_PACKAGE_FILE_NAME="uca-plugin-${UCA_CAMERA_NAME}-${UCA_VERSION_MAJOR}.${UCA_VERSION_MINOR}.${UCA_VERSION_PATCH}" diff --git a/plugins/pco/CMakeLists.txt b/plugins/pco/CMakeLists.txt new file mode 100644 index 0000000..7c016b9 --- /dev/null +++ b/plugins/pco/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapco) + +find_package(PCO) +find_package(FgLib5) +find_package(ClSerMe4) + +if (PCO_FOUND AND CLSERME4_FOUND AND FGLIB5_FOUND) +    set(UCA_CAMERA_NAME "pco") + +    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../package-plugin.sh.in +                   ${CMAKE_CURRENT_BINARY_DIR}/../../package-plugin-${UCA_CAMERA_NAME}.sh) + +    include_directories(${PCO_INCLUDE_DIRS} +                        ${CLSERME4_INCLUDE_DIR} +                        ${FGLIB5_INCLUDE_DIR}) + +    add_library(ucapco SHARED +                uca-pco-camera.c) + +    target_link_libraries(ucapco +                          ${UCA_DEPS} +                          ${PCO_LIBRARIES} +                          ${CLSERME4_LIBRARY} +                          ${FGLIB5_LIBRARY}) + +    install(TARGETS ucapco +            LIBRARY DESTINATION ${LIB_INSTALL_DIR}/uca +            COMPONENT ${UCA_CAMERA_NAME}) +endif() diff --git a/plugins/pco/uca-pco-camera.c b/plugins/pco/uca-pco-camera.c new file mode 100644 index 0000000..8bf2936 --- /dev/null +++ b/plugins/pco/uca-pco-camera.c @@ -0,0 +1,1398 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#include <gmodule.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <libpco/libpco.h> +#include <libpco/sc2_defs.h> +#include <fgrab_prototyp.h> +#include "uca-camera.h" +#include "uca-pco-camera.h" +#include "uca-enums.h" + +#define FG_TRY_PARAM(fg, camobj, param, val_addr, port)     \ +    { int r = Fg_setParameter(fg, param, val_addr, port);   \ +        if (r != FG_OK) {                                   \ +            g_set_error(error, UCA_PCO_CAMERA_ERROR,        \ +                    UCA_PCO_CAMERA_ERROR_FG_GENERAL,        \ +                    "%s", Fg_getLastErrorDescription(fg));  \ +            g_object_unref(camobj);                         \ +            return NULL;                                    \ +        } } + +#define FG_SET_ERROR(err, fg, err_type)                 \ +    if (err != FG_OK) {                                 \ +        g_set_error(error, UCA_PCO_CAMERA_ERROR,        \ +                err_type,                               \ +                "%s", Fg_getLastErrorDescription(fg));  \ +        return;                                         \ +    } + +#define HANDLE_PCO_ERROR(err)                       \ +    if ((err) != PCO_NOERROR) {                     \ +        g_set_error(error, UCA_PCO_CAMERA_ERROR,    \ +                UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL,\ +                "libpco error %x", err);            \ +        return;                                     \ +    } + +#define UCA_PCO_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_PCO_CAMERA, UcaPcoCameraPrivate)) + +G_DEFINE_TYPE(UcaPcoCamera, uca_pco_camera, UCA_TYPE_CAMERA) + +#define TIMEBASE_INVALID 0xDEAD + +/** + * UcaPcoCameraRecordMode: + * @UCA_PCO_CAMERA_RECORD_MODE_SEQUENCE: Store all frames and stop if necessary + * @UCA_PCO_CAMERA_RECORD_MODE_RING_BUFFER: Store frames in ring-buffer fashion + *      and overwrite if necessary + */ + +/** + * UcaPcoCameraAcquireMode: + * @UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO: Take all images + * @UCA_PCO_CAMERA_ACQUIRE_MODE_EXTERNAL: Use <acq enbl> signal + */ + +/** + * UcaPcoCameraTimestamp: + * @UCA_PCO_CAMERA_TIMESTAMP_NONE: Don't embed any timestamp + * @UCA_PCO_CAMERA_TIMESTAMP_BINARY: Embed a BCD-coded timestamp in the first + *     bytes + * @UCA_PCO_CAMERA_TIMESTAMP_ASCII: Embed a visible ASCII timestamp in the image + */ + +/** + * UcaPcoCameraError: + * @UCA_PCO_CAMERA_ERROR_LIBPCO_INIT: Initializing libpco failed + * @UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL: General libpco error + * @UCA_PCO_CAMERA_ERROR_UNSUPPORTED: Camera type is not supported + * @UCA_PCO_CAMERA_ERROR_FG_INIT: Framegrabber initialization failed + * @UCA_PCO_CAMERA_ERROR_FG_GENERAL: General framegrabber error + * @UCA_PCO_CAMERA_ERROR_FG_ACQUISITION: Framegrabber acquisition error + */ +GQuark uca_pco_camera_error_quark() +{ +    return g_quark_from_static_string("uca-pco-camera-error-quark"); +} + +enum { +    PROP_SENSOR_EXTENDED = N_BASE_PROPERTIES, +    PROP_SENSOR_WIDTH_EXTENDED, +    PROP_SENSOR_HEIGHT_EXTENDED, +    PROP_SENSOR_TEMPERATURE, +    PROP_SENSOR_PIXELRATES, +    PROP_SENSOR_PIXELRATE, +    PROP_SENSOR_ADCS, +    PROP_SENSOR_MAX_ADCS, +    PROP_DELAY_TIME, +    PROP_HAS_DOUBLE_IMAGE_MODE, +    PROP_DOUBLE_IMAGE_MODE, +    PROP_OFFSET_MODE, +    PROP_RECORD_MODE, +    PROP_ACQUIRE_MODE, +    PROP_COOLING_POINT, +    PROP_COOLING_POINT_MIN, +    PROP_COOLING_POINT_MAX, +    PROP_COOLING_POINT_DEFAULT, +    PROP_NOISE_FILTER, +    PROP_TIMESTAMP_MODE, +    N_PROPERTIES +}; + +static gint base_overrideables[] = { +    PROP_NAME, +    PROP_SENSOR_WIDTH, +    PROP_SENSOR_HEIGHT, +    PROP_SENSOR_BITDEPTH, +    PROP_SENSOR_HORIZONTAL_BINNING, +    PROP_SENSOR_HORIZONTAL_BINNINGS, +    PROP_SENSOR_VERTICAL_BINNING, +    PROP_SENSOR_VERTICAL_BINNINGS, +    PROP_SENSOR_MAX_FRAME_RATE, +    PROP_EXPOSURE_TIME, +    PROP_TRIGGER_MODE, +    PROP_ROI_X, +    PROP_ROI_Y, +    PROP_ROI_WIDTH, +    PROP_ROI_HEIGHT, +    PROP_ROI_WIDTH_MULTIPLIER, +    PROP_ROI_HEIGHT_MULTIPLIER, +    PROP_HAS_STREAMING, +    PROP_HAS_CAMRAM_RECORDING, +    0 +}; + +static GParamSpec *pco_properties[N_PROPERTIES] = { NULL, }; + +/* + * This structure defines type-specific properties of PCO cameras. + */ +typedef struct { +    int camera_type; +    const char *so_file; +    int cl_type; +    int cl_format; +    gfloat max_frame_rate; +    gboolean has_camram; +} pco_cl_map_entry; + +struct _UcaPcoCameraPrivate { +    pco_handle pco; +    pco_cl_map_entry *camera_description; + +    Fg_Struct *fg; +    guint fg_port; +    dma_mem *fg_mem; + +    guint frame_width; +    guint frame_height; +    gsize num_bytes; +    guint16 *grab_buffer; + +    guint16 width, height; +    guint16 width_ex, height_ex; +    guint16 binning_h, binning_v; +    guint16 roi_x, roi_y; +    guint16 roi_width, roi_height; +    guint16 roi_horizontal_steps, roi_vertical_steps; +    GValueArray *horizontal_binnings; +    GValueArray *vertical_binnings; +    GValueArray *pixelrates; + +    /* The time bases are given as pco time bases (TIMEBASE_NS and so on) */ +    guint16 delay_timebase; +    guint16 exposure_timebase; + +    frameindex_t last_frame; +    guint16 active_segment; +    guint num_recorded_images; +    guint current_image; +}; + +static pco_cl_map_entry pco_cl_map[] = { +    { CAMERATYPE_PCO_EDGE,       "libFullAreaGray8.so",  FG_CL_8BIT_FULL_10,        FG_GRAY,     30.0f, FALSE }, +    { CAMERATYPE_PCO4000,        "libDualAreaGray16.so", FG_CL_SINGLETAP_16_BIT,    FG_GRAY16,    5.0f, TRUE  }, +    { CAMERATYPE_PCO_DIMAX_STD,  "libFullAreaGray16.so", FG_CL_SINGLETAP_8_BIT,     FG_GRAY16, 1279.0f, TRUE  }, +    { 0, NULL, 0, 0, 0.0f, FALSE } +}; + +static pco_cl_map_entry *get_pco_cl_map_entry(int camera_type) +{ +    pco_cl_map_entry *entry = pco_cl_map; + +    while (entry->camera_type != 0) { +        if (entry->camera_type == camera_type) +            return entry; +        entry++; +    } + +    return NULL; +} + +static guint fill_binnings(UcaPcoCameraPrivate *priv) +{ +    uint16_t *horizontal = NULL; +    uint16_t *vertical = NULL; +    guint num_horizontal, num_vertical; + +    guint err = pco_get_possible_binnings(priv->pco, +            &horizontal, &num_horizontal, +            &vertical, &num_vertical); + +    GValue val = {0}; +    g_value_init(&val, G_TYPE_UINT); + +    if (err == PCO_NOERROR) { +        priv->horizontal_binnings = g_value_array_new(num_horizontal); +        priv->vertical_binnings = g_value_array_new(num_vertical); + +        for (guint i = 0; i < num_horizontal; i++) { +            g_value_set_uint(&val, horizontal[i]); +            g_value_array_append(priv->horizontal_binnings, &val); +        } + +        for (guint i = 0; i < num_vertical; i++) { +            g_value_set_uint(&val, vertical[i]); +            g_value_array_append(priv->vertical_binnings, &val); +        } +    } + +    free(horizontal); +    free(vertical); +    return err; +} + +static void fill_pixelrates(UcaPcoCameraPrivate *priv, guint32 rates[4], gint num_rates) +{ +    GValue val = {0}; +    g_value_init(&val, G_TYPE_UINT); +    priv->pixelrates = g_value_array_new(num_rates); + +    for (gint i = 0; i < num_rates; i++) { +        g_value_set_uint(&val, (guint) rates[i]); +        g_value_array_append(priv->pixelrates, &val); +    } +} + +static guint override_temperature_range(UcaPcoCameraPrivate *priv) +{ +    int16_t default_temp, min_temp, max_temp; +    guint err = pco_get_cooling_range(priv->pco, &default_temp, &min_temp, &max_temp); + +    if (err == PCO_NOERROR) { +        GParamSpecInt *spec = (GParamSpecInt *) pco_properties[PROP_COOLING_POINT]; +        spec->minimum = min_temp; +        spec->maximum = max_temp; +        spec->default_value = default_temp; +    } +    else +        g_warning("Unable to retrieve cooling range"); + +    return err; +} + +static void property_override_default_guint_value (GObjectClass *oclass, const gchar *property_name, guint new_default) +{ +    GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (g_object_class_find_property (oclass, property_name)); + +    if (pspec != NULL) +        pspec->default_value = new_default; +    else +        g_warning ("pspec for %s not found\n", property_name); +} + +static void override_maximum_adcs(UcaPcoCameraPrivate *priv) +{ +    GParamSpecInt *spec = (GParamSpecInt *) pco_properties[PROP_SENSOR_ADCS]; +    spec->maximum = pco_get_maximum_number_of_adcs(priv->pco); +} + +static gdouble convert_timebase(guint16 timebase) +{ +    switch (timebase) { +        case TIMEBASE_NS: +            return 1e-9; +        case TIMEBASE_US: +            return 1e-6; +        case TIMEBASE_MS: +            return 1e-3; +        default: +            g_warning("Unknown timebase"); +    } +    return 1e-3; +} + +static void read_timebase(UcaPcoCameraPrivate *priv) +{ +    pco_get_timebase(priv->pco, &priv->delay_timebase, &priv->exposure_timebase); +} + +static gdouble get_suitable_timebase(gdouble time) +{ +    if (time * 1e3 >= 1.0) +        return TIMEBASE_MS; +    if (time * 1e6 >= 1.0) +        return TIMEBASE_US; +    if (time * 1e9 >= 1.0) +        return TIMEBASE_NS; +    return TIMEBASE_INVALID; +} + +static int fg_callback(frameindex_t frame, struct fg_apc_data *apc) +{ +    UcaCamera *camera = UCA_CAMERA(apc); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); +    gpointer data = Fg_getImagePtrEx(priv->fg, frame, priv->fg_port, priv->fg_mem); + +    if (priv->camera_description->camera_type != CAMERATYPE_PCO_EDGE) +        camera->grab_func(data, camera->user_data); +    else { +        pco_get_reorder_func(priv->pco)(priv->grab_buffer, data, priv->frame_width, priv->frame_height); +        camera->grab_func(priv->grab_buffer, camera->user_data); +    } + +    return 0; +} + +static gboolean setup_fg_callback(UcaCamera *camera) +{ +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); +    struct FgApcControl ctrl; + +    /* Jeez, as if a NULL pointer would not be good enough. */ +    ctrl.data = (struct fg_apc_data *) camera; +    ctrl.version = 0; +    ctrl.func = &fg_callback; +    ctrl.flags = FG_APC_DEFAULTS; +    ctrl.timeout = 1; + +    if (priv->grab_buffer) +        g_free(priv->grab_buffer); + +    priv->grab_buffer = g_malloc0(priv->frame_width * priv->frame_height * sizeof(guint16)); + +    return Fg_registerApcHandler(priv->fg, priv->fg_port, &ctrl, FG_APC_CONTROL_BASIC) == FG_OK; +} + +static void uca_pco_camera_start_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); +    guint err = PCO_NOERROR; + +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); +    guint16 binned_width, binned_height; +    gboolean use_extended = FALSE; +    gboolean transfer_async = FALSE; + +    g_object_get (camera, "sensor-extended", &use_extended, NULL); + +    if (use_extended) { +        binned_width = priv->width_ex; +        binned_height = priv->height_ex; +    } +    else { +        binned_width = priv->width; +        binned_height = priv->height; +    } + +    binned_width /= priv->binning_h; +    binned_height /= priv->binning_v; + +    if ((priv->roi_x + priv->roi_width > binned_width) || (priv->roi_y + priv->roi_height > binned_height)) { +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_UNSUPPORTED, +                "ROI of size %ix%i @ (%i, %i) is outside of (binned) sensor size %ix%i\n", +                priv->roi_width, priv->roi_height, priv->roi_x, priv->roi_y, binned_width, binned_height); +        return; +    } + +    /* +     * All parameters are valid. Now, set them on the camera. +     */ +    guint16 roi[4] = { priv->roi_x + 1, priv->roi_y + 1, priv->roi_x + priv->roi_width, priv->roi_y + priv->roi_height }; + +    if (pco_set_roi(priv->pco, roi) != PCO_NOERROR) { +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, +                "Could not set ROI via pco_set_roi()"); +        return; +    } + +    g_object_get(G_OBJECT(camera), "transfer-asynchronously", &transfer_async, NULL); + +    /* +     * FIXME: We cannot set the binning here as this breaks communication with +     * the camera. Setting the binning works _before_ initializing the frame +     * grabber though. However, it is rather inconvenient to initialize and +     * de-initialize the frame grabber for each recording sequence. +     */ + +    /* if (pco_set_binning(priv->pco, priv->binning_h, priv->binning_v) != PCO_NOERROR) */ +    /*     g_warning("Cannot set binning\n"); */ + +    if (priv->frame_width != priv->roi_width || priv->frame_height != priv->roi_height || priv->fg_mem == NULL) { +        guint fg_width = priv->camera_description->camera_type == CAMERATYPE_PCO_EDGE ? 2 * priv->roi_width : priv->roi_width; + +        priv->frame_width = priv->roi_width; +        priv->frame_height = priv->roi_height; +        priv->num_bytes = 2; + +        Fg_setParameter(priv->fg, FG_WIDTH, &fg_width, priv->fg_port); +        Fg_setParameter(priv->fg, FG_HEIGHT, &priv->frame_height, priv->fg_port); + +        if (priv->fg_mem) +            Fg_FreeMemEx(priv->fg, priv->fg_mem); + +        const guint num_buffers = 2; +        priv->fg_mem = Fg_AllocMemEx(priv->fg, +                num_buffers * priv->frame_width * priv->frame_height * sizeof(uint16_t), num_buffers); + +        if (priv->fg_mem == NULL) { +            g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_FG_INIT, +                    "%s", Fg_getLastErrorDescription(priv->fg)); +            g_object_unref(camera); +            return; +        } +    } + +    if (transfer_async) +        setup_fg_callback(camera); + +    if ((priv->camera_description->camera_type == CAMERATYPE_PCO_DIMAX_STD) || +        (priv->camera_description->camera_type == CAMERATYPE_PCO4000)) +        pco_clear_active_segment(priv->pco); + +    priv->last_frame = 0; + +    err = pco_arm_camera(priv->pco); +    HANDLE_PCO_ERROR(err); + +    err = pco_start_recording(priv->pco); +    HANDLE_PCO_ERROR(err); + +    err = Fg_AcquireEx(priv->fg, priv->fg_port, GRAB_INFINITE, ACQ_STANDARD, priv->fg_mem); +    FG_SET_ERROR(err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_ACQUISITION); +} + +static void uca_pco_camera_stop_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); + +    guint err = pco_stop_recording(priv->pco); +    HANDLE_PCO_ERROR(err); + +    err = Fg_stopAcquireEx(priv->fg, priv->fg_port, priv->fg_mem, STOP_SYNC); +    FG_SET_ERROR(err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_ACQUISITION); + +    err = Fg_setStatusEx(priv->fg, FG_UNBLOCK_ALL, 0, priv->fg_port, priv->fg_mem); +    if (err == FG_INVALID_PARAMETER) +        g_warning(" Unable to unblock all\n"); +} + +static void uca_pco_camera_start_readout(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); + +    /* +     * TODO: Check if readout mode is possible. This is not the case for the +     * edge. +     */ + +    guint err = pco_get_active_segment(priv->pco, &priv->active_segment); +    HANDLE_PCO_ERROR(err); + +    err = pco_get_num_images(priv->pco, priv->active_segment, &priv->num_recorded_images); +    HANDLE_PCO_ERROR(err); + +    priv->current_image = 1; +} + +static void uca_pco_camera_trigger(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); + +    /* TODO: Check if we can trigger */ +    guint32 success = 0; +    pco_force_trigger(priv->pco, &success); + +    if (!success) +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, +                "Could not trigger frame acquisition"); +} + +static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +{ +    static const gint MAX_TIMEOUT = G_MAXINT; + +    g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); + +    gboolean is_readout = FALSE; +    g_object_get(G_OBJECT(camera), "is-readout", &is_readout, NULL); + +    if (is_readout) { +        if (priv->current_image == priv->num_recorded_images) { +            *data = NULL; +            return; +        } + +        /* +         * No joke, the pco firmware allows to read a range of images but +         * implements only reading single images ... +         */ +        pco_read_images(priv->pco, priv->active_segment, priv->current_image, priv->current_image); +        priv->current_image++; +    } + +    pco_request_image(priv->pco); +    priv->last_frame = Fg_getLastPicNumberBlockingEx(priv->fg, priv->last_frame+1, priv->fg_port, MAX_TIMEOUT, priv->fg_mem); + +    if (priv->last_frame <= 0) { +        guint err = FG_OK + 1; +        FG_SET_ERROR(err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_GENERAL); +    } + +    guint16 *frame = Fg_getImagePtrEx(priv->fg, priv->last_frame, priv->fg_port, priv->fg_mem); + +    if (*data == NULL) +        *data = g_malloc0(priv->frame_width * priv->frame_height * priv->num_bytes); + +    if (priv->camera_description->camera_type == CAMERATYPE_PCO_EDGE) +        pco_get_reorder_func(priv->pco)((guint16 *) *data, frame, priv->frame_width, priv->frame_height); +    else +        memcpy((gchar *) *data, (gchar *) frame, priv->frame_width * priv->frame_height * priv->num_bytes); +} + +static void uca_pco_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_SENSOR_EXTENDED: +            { +                guint16 format = g_value_get_boolean (value) ? SENSORFORMAT_EXTENDED : SENSORFORMAT_STANDARD; +                pco_set_sensor_format(priv->pco, format); +            } +            break; + +        case PROP_ROI_X: +            priv->roi_x = g_value_get_uint(value); +            break; + +        case PROP_ROI_Y: +            priv->roi_y = g_value_get_uint(value); +            break; + +        case PROP_ROI_WIDTH: +            { +                guint width = g_value_get_uint(value); + +                if (width % priv->roi_horizontal_steps) +                    g_warning("ROI width %i is not a multiple of %i", width, priv->roi_horizontal_steps); +                else +                    priv->roi_width = width; +            } +            break; + +        case PROP_ROI_HEIGHT: +            { +                guint height = g_value_get_uint(value); + +                if (height % priv->roi_vertical_steps) +                    g_warning("ROI height %i is not a multiple of %i", height, priv->roi_vertical_steps); +                else +                    priv->roi_height = height; +            } +            break; + +        case PROP_SENSOR_HORIZONTAL_BINNING: +            priv->binning_h = g_value_get_uint(value); +            break; + +        case PROP_SENSOR_VERTICAL_BINNING: +            priv->binning_v = g_value_get_uint(value); +            break; + +        case PROP_EXPOSURE_TIME: +            { +                const gdouble time = g_value_get_double(value); + +                if (priv->exposure_timebase == TIMEBASE_INVALID) +                    read_timebase(priv); + +                /* +                 * Lets check if we can express the time in the current time +                 * base. If not, we need to adjust that. +                 */ +                guint16 suitable_timebase = get_suitable_timebase(time); + +                if (suitable_timebase == TIMEBASE_INVALID) { +                    g_warning("Cannot set such a small exposure time"); +                } +                else { +                    if (suitable_timebase != priv->exposure_timebase) { +                        priv->exposure_timebase = suitable_timebase; +                        if (pco_set_timebase(priv->pco, priv->delay_timebase, suitable_timebase) != PCO_NOERROR) +                            g_warning("Cannot set exposure time base"); +                    } + +                    gdouble timebase = convert_timebase(suitable_timebase); +                    guint32 timesteps = time / timebase; +                    if (pco_set_exposure_time(priv->pco, timesteps) != PCO_NOERROR) +                        g_warning("Cannot set exposure time"); +                } +            } +            break; + +        case PROP_DELAY_TIME: +            { +                const gdouble time = g_value_get_double(value); + +                if (priv->delay_timebase == TIMEBASE_INVALID) +                    read_timebase(priv); + +                /* +                 * Lets check if we can express the time in the current time +                 * base. If not, we need to adjust that. +                 */ +                guint16 suitable_timebase = get_suitable_timebase(time); + +                if (suitable_timebase == TIMEBASE_INVALID) { +                    if (time == 0.0) { +                        /* +                         * If we want to suppress any pre-exposure delays, we +                         * can set the 0 seconds in whatever time base that is +                         * currently active. +                         */ +                        if (pco_set_delay_time(priv->pco, 0) != PCO_NOERROR) +                            g_warning("Cannot set zero delay time"); +                    } +                    else +                        g_warning("Cannot set such a small exposure time"); +                } +                else { +                    if (suitable_timebase != priv->delay_timebase) { +                        priv->delay_timebase = suitable_timebase; +                        if (pco_set_timebase(priv->pco, suitable_timebase, priv->exposure_timebase) != PCO_NOERROR) +                            g_warning("Cannot set delay time base"); +                    } + +                    gdouble timebase = convert_timebase(suitable_timebase); +                    guint32 timesteps = time / timebase; +                    if (pco_set_delay_time(priv->pco, timesteps) != PCO_NOERROR) +                        g_warning("Cannot set delay time"); +                } +            } +            break; + +        case PROP_SENSOR_ADCS: +            { +                const guint num_adcs = g_value_get_uint(value); +                if (pco_set_adc_mode(priv->pco, num_adcs) != PCO_NOERROR) +                    g_warning("Cannot set the number of ADCs per pixel\n"); +            } +            break; + +        case PROP_SENSOR_PIXELRATE: +            { +                guint desired_pixel_rate = g_value_get_uint(value); +                guint32 pixel_rate = 0; + +                for (guint i = 0; i < priv->pixelrates->n_values; i++) { +                    if (g_value_get_uint(g_value_array_get_nth(priv->pixelrates, i)) == desired_pixel_rate) { +                        pixel_rate = desired_pixel_rate; +                        break; +                    } +                } + +                if (pixel_rate != 0) { +                    if (pco_set_pixelrate(priv->pco, pixel_rate) != PCO_NOERROR) +                        g_warning("Cannot set pixel rate"); +                } +                else +                    g_warning("%i Hz is not possible. Please check the \"sensor-pixelrates\" property", desired_pixel_rate); +            } +            break; + +        case PROP_DOUBLE_IMAGE_MODE: +            if (!pco_is_double_image_mode_available(priv->pco)) +                g_warning("Double image mode is not available on this pco model"); +            else +                pco_set_double_image_mode(priv->pco, g_value_get_boolean(value)); +            break; + +        case PROP_OFFSET_MODE: +            pco_set_offset_mode(priv->pco, g_value_get_boolean(value)); +            break; + +        case PROP_COOLING_POINT: +            { +                int16_t temperature = (int16_t) g_value_get_int(value); +                pco_set_cooling_temperature(priv->pco, temperature); +            } +            break; + +        case PROP_RECORD_MODE: +            { +                /* TODO: setting this is not possible for the edge */ +                UcaPcoCameraRecordMode mode = (UcaPcoCameraRecordMode) g_value_get_enum(value); + +                if (mode == UCA_PCO_CAMERA_RECORD_MODE_SEQUENCE) +                    pco_set_record_mode(priv->pco, RECORDER_SUBMODE_SEQUENCE); +                else if (mode == UCA_PCO_CAMERA_RECORD_MODE_RING_BUFFER) +                    pco_set_record_mode(priv->pco, RECORDER_SUBMODE_RINGBUFFER); +                else +                    g_warning("Unknown record mode"); +            } +            break; + +        case PROP_ACQUIRE_MODE: +            { +                UcaPcoCameraAcquireMode mode = (UcaPcoCameraAcquireMode) g_value_get_enum(value); +                unsigned int err = PCO_NOERROR; + +                if (mode == UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO) +                    err = pco_set_acquire_mode(priv->pco, ACQUIRE_MODE_AUTO); +                else if (mode == UCA_PCO_CAMERA_ACQUIRE_MODE_EXTERNAL) +                    err = pco_set_acquire_mode(priv->pco, ACQUIRE_MODE_EXTERNAL); +                else +                    g_warning("Unknown acquire mode"); + +                if (err != PCO_NOERROR) +                    g_warning("Cannot set acquire mode"); +            } +            break; + +        case PROP_TRIGGER_MODE: +            { +                UcaCameraTrigger trigger_mode = (UcaCameraTrigger) g_value_get_enum(value); + +                switch (trigger_mode) { +                    case UCA_CAMERA_TRIGGER_AUTO: +                        pco_set_trigger_mode(priv->pco, TRIGGER_MODE_AUTOTRIGGER); +                        break; +                    case UCA_CAMERA_TRIGGER_INTERNAL: +                        pco_set_trigger_mode(priv->pco, TRIGGER_MODE_SOFTWARETRIGGER); +                        break; +                    case UCA_CAMERA_TRIGGER_EXTERNAL: +                        pco_set_trigger_mode(priv->pco, TRIGGER_MODE_EXTERNALTRIGGER); +                        break; +                } +            } +            break; + +        case PROP_NOISE_FILTER: +            { +                guint16 filter_mode = g_value_get_boolean(value) ? NOISE_FILTER_MODE_ON : NOISE_FILTER_MODE_OFF; +                pco_set_noise_filter_mode(priv->pco, filter_mode); +            } +            break; + +        case PROP_TIMESTAMP_MODE: +            { +                guint16 modes[] = { +                    TIMESTAMP_MODE_OFF,             /* 0 */ +                    TIMESTAMP_MODE_BINARY,          /* 1 = 1 << 0 */ +                    TIMESTAMP_MODE_ASCII,           /* 2 = 1 << 1 */ +                    TIMESTAMP_MODE_BINARYANDASCII,  /* 3 = 1 << 0 | 1 << 1 */ +                }; +                pco_set_timestamp_mode(priv->pco, modes[g_value_get_flags(value)]); +            } +            break; + +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            return; +    } +} + +static void uca_pco_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_SENSOR_EXTENDED: +            { +                guint16 format; +                pco_get_sensor_format(priv->pco, &format); +                g_value_set_boolean(value, format == SENSORFORMAT_EXTENDED); +            } +            break; + +        case PROP_SENSOR_WIDTH: +            g_value_set_uint(value, priv->width); +            break; + +        case PROP_SENSOR_HEIGHT: +            g_value_set_uint(value, priv->height); +            break; + +        case PROP_SENSOR_WIDTH_EXTENDED: +            g_value_set_uint(value, priv->width_ex < priv->width ? priv->width : priv->width_ex); +            break; + +        case PROP_SENSOR_HEIGHT_EXTENDED: +            g_value_set_uint(value, priv->height_ex < priv->height ? priv->height : priv->height_ex); +            break; + +        case PROP_SENSOR_HORIZONTAL_BINNING: +            g_value_set_uint(value, priv->binning_h); +            break; + +        case PROP_SENSOR_HORIZONTAL_BINNINGS: +            g_value_set_boxed(value, priv->horizontal_binnings); +            break; + +        case PROP_SENSOR_VERTICAL_BINNING: +            g_value_set_uint(value, priv->binning_v); +            break; + +        case PROP_SENSOR_VERTICAL_BINNINGS: +            g_value_set_boxed(value, priv->vertical_binnings); +            break; + +        case PROP_SENSOR_MAX_FRAME_RATE: +            g_value_set_float(value, priv->camera_description->max_frame_rate); +            break; + +        case PROP_SENSOR_BITDEPTH: +            g_value_set_uint(value, 16); +            break; + +        case PROP_SENSOR_TEMPERATURE: +            { +                gint32 ccd, camera, power; +                pco_get_temperature(priv->pco, &ccd, &camera, &power); +                g_value_set_double(value, ccd / 10.0); +            } +            break; + +        case PROP_SENSOR_ADCS: +            { +                /* +                 * Up to now, the ADC mode corresponds directly to the number of +                 * ADCs in use. +                 */ +                pco_adc_mode mode; +                if (pco_get_adc_mode(priv->pco, &mode) != PCO_NOERROR) +                    g_warning("Cannot read number of ADCs per pixel"); +                g_value_set_uint(value, mode); +            } +            break; + +        case PROP_SENSOR_MAX_ADCS: +            { +                GParamSpecUInt *spec = (GParamSpecUInt *) pco_properties[PROP_SENSOR_ADCS]; +                g_value_set_uint(value, spec->maximum); +            } +            break; + +        case PROP_SENSOR_PIXELRATES: +            g_value_set_boxed(value, priv->pixelrates); +            break; + +        case PROP_SENSOR_PIXELRATE: +            { +                guint32 pixelrate; +                pco_get_pixelrate(priv->pco, &pixelrate); +                g_value_set_uint(value, pixelrate); +            } +            break; + +        case PROP_EXPOSURE_TIME: +            { +                uint32_t exposure_time; +                pco_get_exposure_time(priv->pco, &exposure_time); + +                if (priv->exposure_timebase == TIMEBASE_INVALID) +                    read_timebase(priv); + +                g_value_set_double(value, convert_timebase(priv->exposure_timebase) * exposure_time); +            } +            break; + +        case PROP_DELAY_TIME: +            { +                uint32_t delay_time; +                pco_get_delay_time(priv->pco, &delay_time); + +                if (priv->delay_timebase == TIMEBASE_INVALID) +                    read_timebase(priv); + +                g_value_set_double(value, convert_timebase(priv->delay_timebase) * delay_time); +            } +            break; + +        case PROP_HAS_DOUBLE_IMAGE_MODE: +            g_value_set_boolean(value, pco_is_double_image_mode_available(priv->pco)); +            break; + +        case PROP_DOUBLE_IMAGE_MODE: +            if (!pco_is_double_image_mode_available(priv->pco)) +                g_warning("Double image mode is not available on this pco model"); +            else { +                bool on; +                pco_get_double_image_mode(priv->pco, &on); +                g_value_set_boolean(value, on); +            } +            break; + +        case PROP_OFFSET_MODE: +            { +                bool on; +                pco_get_offset_mode(priv->pco, &on); +                g_value_set_boolean(value, on); +            } +            break; + +        case PROP_HAS_STREAMING: +            g_value_set_boolean(value, TRUE); +            break; + +        case PROP_HAS_CAMRAM_RECORDING: +            g_value_set_boolean(value, priv->camera_description->has_camram); +            break; + +        case PROP_RECORD_MODE: +            { +                guint16 mode; +                pco_get_record_mode(priv->pco, &mode); + +                if (mode == RECORDER_SUBMODE_SEQUENCE) +                    g_value_set_enum(value, UCA_PCO_CAMERA_RECORD_MODE_SEQUENCE); +                else if (mode == RECORDER_SUBMODE_RINGBUFFER) +                    g_value_set_enum(value, UCA_PCO_CAMERA_RECORD_MODE_RING_BUFFER); +                else +                    g_warning("pco record mode not handled"); +            } +            break; + +        case PROP_ACQUIRE_MODE: +            { +                guint16 mode; +                pco_get_acquire_mode(priv->pco, &mode); + +                if (mode == ACQUIRE_MODE_AUTO) +                    g_value_set_enum(value, UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO); +                else if (mode == ACQUIRE_MODE_EXTERNAL) +                    g_value_set_enum(value, UCA_PCO_CAMERA_ACQUIRE_MODE_EXTERNAL); +                else +                    g_warning("pco acquire mode not handled"); +            } +            break; + +        case PROP_TRIGGER_MODE: +            { +                guint16 mode; +                pco_get_trigger_mode(priv->pco, &mode); + +                switch (mode) { +                    case TRIGGER_MODE_AUTOTRIGGER: +                        g_value_set_enum(value, UCA_CAMERA_TRIGGER_AUTO); +                        break; +                    case TRIGGER_MODE_SOFTWARETRIGGER: +                        g_value_set_enum(value, UCA_CAMERA_TRIGGER_INTERNAL); +                        break; +                    case TRIGGER_MODE_EXTERNALTRIGGER: +                        g_value_set_enum(value, UCA_CAMERA_TRIGGER_EXTERNAL); +                        break; +                    default: +                        g_warning("pco trigger mode not handled"); +                } +            } +            break; + +        case PROP_ROI_X: +            g_value_set_uint(value, priv->roi_x); +            break; + +        case PROP_ROI_Y: +            g_value_set_uint(value, priv->roi_y); +            break; + +        case PROP_ROI_WIDTH: +            g_value_set_uint(value, priv->roi_width); +            break; + +        case PROP_ROI_HEIGHT: +            g_value_set_uint(value, priv->roi_height); +            break; + +        case PROP_ROI_WIDTH_MULTIPLIER: +            g_value_set_uint(value, priv->roi_horizontal_steps); +            break; + +        case PROP_ROI_HEIGHT_MULTIPLIER: +            g_value_set_uint(value, priv->roi_vertical_steps); +            break; + +        case PROP_NAME: +            { +                gchar *name = NULL; +                pco_get_name(priv->pco, &name); +                g_value_set_string(value, name); +                g_free(name); +            } +            break; + +        case PROP_COOLING_POINT: +            { +                int16_t temperature; +                if (pco_get_cooling_temperature(priv->pco, &temperature) != PCO_NOERROR) +                    g_warning("Cannot read cooling temperature\n"); +                g_value_set_int(value, temperature); +            } +            break; + +        case PROP_COOLING_POINT_MIN: +            { +                GParamSpecInt *spec = (GParamSpecInt *) pco_properties[PROP_COOLING_POINT]; +                g_value_set_int(value, spec->minimum); +            } +            break; + +        case PROP_COOLING_POINT_MAX: +            { +                GParamSpecInt *spec = (GParamSpecInt *) pco_properties[PROP_COOLING_POINT]; +                g_value_set_int(value, spec->maximum); +            } +            break; + +        case PROP_COOLING_POINT_DEFAULT: +            { +                GParamSpecInt *spec = (GParamSpecInt *) pco_properties[PROP_COOLING_POINT]; +                g_value_set_int(value, spec->default_value); +            } +            break; + +        case PROP_NOISE_FILTER: +            { +                guint16 mode; +                pco_get_noise_filter_mode(priv->pco, &mode); +                g_value_set_boolean(value, mode != NOISE_FILTER_MODE_OFF); +            } +            break; + +        case PROP_TIMESTAMP_MODE: +            { +                guint16 mode; +                pco_get_timestamp_mode(priv->pco, &mode); + +                switch (mode) { +                    case TIMESTAMP_MODE_OFF: +                        g_value_set_flags(value, UCA_PCO_CAMERA_TIMESTAMP_NONE); +                        break; +                    case TIMESTAMP_MODE_BINARY: +                        g_value_set_flags(value, UCA_PCO_CAMERA_TIMESTAMP_BINARY); +                        break; +                    case TIMESTAMP_MODE_BINARYANDASCII: +                        g_value_set_flags(value, +                                UCA_PCO_CAMERA_TIMESTAMP_BINARY | UCA_PCO_CAMERA_TIMESTAMP_ASCII); +                        break; +                    case TIMESTAMP_MODE_ASCII: +                        g_value_set_flags(value, UCA_PCO_CAMERA_TIMESTAMP_ASCII); +                        break; +                } +            } +            break; + +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            break; +    } +} + +static void uca_pco_camera_finalize(GObject *object) +{ +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(object); + +    if (priv->horizontal_binnings) +        g_value_array_free(priv->horizontal_binnings); + +    if (priv->vertical_binnings) +        g_value_array_free(priv->vertical_binnings); + +    if (priv->pixelrates) +        g_value_array_free(priv->pixelrates); + +    if (priv->fg) { +        if (priv->fg_mem) +            Fg_FreeMemEx(priv->fg, priv->fg_mem); + +        Fg_FreeGrabber(priv->fg); +    } + +    if (priv->pco) +        pco_destroy(priv->pco); + +    g_free(priv->grab_buffer); + +    G_OBJECT_CLASS(uca_pco_camera_parent_class)->finalize(object); +} + +static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) +{ +    GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +    gobject_class->set_property = uca_pco_camera_set_property; +    gobject_class->get_property = uca_pco_camera_get_property; +    gobject_class->finalize = uca_pco_camera_finalize; + +    UcaCameraClass *camera_class = UCA_CAMERA_CLASS(klass); +    camera_class->start_recording = uca_pco_camera_start_recording; +    camera_class->stop_recording = uca_pco_camera_stop_recording; +    camera_class->start_readout = uca_pco_camera_start_readout; +    camera_class->trigger = uca_pco_camera_trigger; +    camera_class->grab = uca_pco_camera_grab; + +    for (guint i = 0; base_overrideables[i] != 0; i++) +        g_object_class_override_property(gobject_class, base_overrideables[i], uca_camera_props[base_overrideables[i]]); + +    /** +     * UcaPcoCamera:sensor-extended: +     * +     * Activate larger sensor area that contains surrounding pixels for dark +     * references and dummies. Use #UcaPcoCamera:sensor-width-extended and +     * #UcaPcoCamera:sensor-height-extended to query the resolution of the +     * larger area. +     */ +    pco_properties[PROP_SENSOR_EXTENDED] = +        g_param_spec_boolean("sensor-extended", +            "Use extended sensor format", +            "Use extended sensor format", +            FALSE, G_PARAM_READWRITE); + +    pco_properties[PROP_SENSOR_WIDTH_EXTENDED] = +        g_param_spec_uint("sensor-width-extended", +            "Width of extended sensor", +            "Width of the extended sensor in pixels", +            1, G_MAXUINT, 1, +            G_PARAM_READABLE); + +    pco_properties[PROP_SENSOR_HEIGHT_EXTENDED] = +        g_param_spec_uint("sensor-height-extended", +            "Height of extended sensor", +            "Height of the extended sensor in pixels", +            1, G_MAXUINT, 1, +            G_PARAM_READABLE); + +    /** +     * UcaPcoCamera:sensor-pixelrate: +     * +     * Read and write the pixel rate or clock of the sensor in terms of Hertz. +     * Make sure to query the possible pixel rates through the +     * #UcaPcoCamera:sensor-pixelrates property. Any other value will be +     * rejected by the camera. +     */ +    pco_properties[PROP_SENSOR_PIXELRATE] = +        g_param_spec_uint("sensor-pixelrate", +            "Pixel rate", +            "Pixel rate", +            1, G_MAXUINT, 1, +            G_PARAM_READWRITE); + +    pco_properties[PROP_SENSOR_PIXELRATES] = +        g_param_spec_value_array("sensor-pixelrates", +            "Array of possible sensor pixel rates", +            "Array of possible sensor pixel rates", +            pco_properties[PROP_SENSOR_PIXELRATE], +            G_PARAM_READABLE); + +    pco_properties[PROP_NAME] = +        g_param_spec_string("name", +            "Name of the camera", +            "Name of the camera", +            "", G_PARAM_READABLE); + +    pco_properties[PROP_SENSOR_TEMPERATURE] = +        g_param_spec_double("sensor-temperature", +            "Temperature of the sensor", +            "Temperature of the sensor in degree Celsius", +            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, +            G_PARAM_READABLE); + +    pco_properties[PROP_HAS_DOUBLE_IMAGE_MODE] = +        g_param_spec_boolean("has-double-image-mode", +            "Is double image mode supported by this model", +            "Is double image mode supported by this model", +            FALSE, G_PARAM_READABLE); + +    pco_properties[PROP_DOUBLE_IMAGE_MODE] = +        g_param_spec_boolean("double-image-mode", +            "Use double image mode", +            "Use double image mode", +            FALSE, G_PARAM_READWRITE); + +    pco_properties[PROP_OFFSET_MODE] = +        g_param_spec_boolean("offset-mode", +            "Use offset mode", +            "Use offset mode", +            FALSE, G_PARAM_READWRITE); + +    pco_properties[PROP_RECORD_MODE] = +        g_param_spec_enum("record-mode", +            "Record mode", +            "Record mode", +            UCA_TYPE_PCO_CAMERA_RECORD_MODE, UCA_PCO_CAMERA_RECORD_MODE_SEQUENCE, +            G_PARAM_READWRITE); + +    pco_properties[PROP_ACQUIRE_MODE] = +        g_param_spec_enum("acquire-mode", +            "Acquire mode", +            "Acquire mode", +            UCA_TYPE_PCO_CAMERA_ACQUIRE_MODE, UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO, +            G_PARAM_READWRITE); + +    pco_properties[PROP_DELAY_TIME] = +        g_param_spec_double("delay-time", +            "Delay time", +            "Delay before starting actual exposure", +            0.0, G_MAXDOUBLE, 0.0, +            G_PARAM_READWRITE); + +    pco_properties[PROP_NOISE_FILTER] = +        g_param_spec_boolean("noise-filter", +            "Noise filter", +            "Noise filter", +            FALSE, G_PARAM_READWRITE); + +    /** +     * UcaPcoCamera:cooling-point: +     * +     * The value range is set arbitrarily, because we are not yet connected to +     * the camera and just don't know the cooling range. We override these +     * values in #uca_pco_camera_new(). +     */ +    pco_properties[PROP_COOLING_POINT] = +        g_param_spec_int("cooling-point", +            "Cooling point of the camera", +            "Cooling point of the camera in degree celsius", +            0, 10, 5, G_PARAM_READWRITE); + +    pco_properties[PROP_COOLING_POINT_MIN] = +        g_param_spec_int("cooling-point-min", +            "Minimum cooling point", +            "Minimum cooling point in degree celsius", +            G_MININT, G_MAXINT, 0, G_PARAM_READABLE); + +    pco_properties[PROP_COOLING_POINT_MAX] = +        g_param_spec_int("cooling-point-max", +            "Maximum cooling point", +            "Maximum cooling point in degree celsius", +            G_MININT, G_MAXINT, 0, G_PARAM_READABLE); + +    pco_properties[PROP_COOLING_POINT_DEFAULT] = +        g_param_spec_int("cooling-point-default", +            "Default cooling point", +            "Default cooling point in degree celsius", +            G_MININT, G_MAXINT, 0, G_PARAM_READABLE); + +    pco_properties[PROP_SENSOR_ADCS] = +        g_param_spec_uint("sensor-adcs", +            "Number of ADCs to use", +            "Number of ADCs to use", +            1, 2, 1, +            G_PARAM_READWRITE); + +    pco_properties[PROP_SENSOR_MAX_ADCS] = +        g_param_spec_uint("sensor-max-adcs", +            "Maximum number of ADCs", +            "Maximum number of ADCs that can be set with \"sensor-adcs\"", +            1, G_MAXUINT, 1, +            G_PARAM_READABLE); + +    pco_properties[PROP_TIMESTAMP_MODE] = +        g_param_spec_flags("timestamp-mode", +            "Timestamp mode", +            "Timestamp mode", +            UCA_TYPE_PCO_CAMERA_TIMESTAMP, UCA_PCO_CAMERA_TIMESTAMP_NONE, +            G_PARAM_READWRITE); + +    for (guint id = N_BASE_PROPERTIES; id < N_PROPERTIES; id++) +        g_object_class_install_property(gobject_class, id, pco_properties[id]); + +    g_type_class_add_private(klass, sizeof(UcaPcoCameraPrivate)); +} + +static void uca_pco_camera_init(UcaPcoCamera *self) +{ +    self->priv = UCA_PCO_CAMERA_GET_PRIVATE(self); +    self->priv->fg = NULL; +    self->priv->fg_mem = NULL; +    self->priv->pco = NULL; +    self->priv->horizontal_binnings = NULL; +    self->priv->vertical_binnings = NULL; +    self->priv->pixelrates = NULL; +    self->priv->camera_description = NULL; +    self->priv->last_frame = 0; +    self->priv->grab_buffer = NULL; + +    self->priv->delay_timebase = TIMEBASE_INVALID; +    self->priv->exposure_timebase = TIMEBASE_INVALID; +} + +G_MODULE_EXPORT UcaCamera * +uca_camera_impl_new (GError **error) +{ +    pco_handle pco = pco_init(); + +    if (pco == NULL) { +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_INIT, +                "Initializing libpco failed"); +        return NULL; +    } + +    UcaPcoCamera *camera = g_object_new(UCA_TYPE_PCO_CAMERA, NULL); +    UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); +    priv->pco = pco; + +    pco_get_resolution(priv->pco, &priv->width, &priv->height, &priv->width_ex, &priv->height_ex); +    pco_get_binning(priv->pco, &priv->binning_h, &priv->binning_v); +    pco_set_storage_mode(pco, STORAGE_MODE_RECORDER); +    pco_set_auto_transfer(pco, 1); + +    guint16 roi[4]; +    pco_get_roi(priv->pco, roi); +    pco_get_roi_steps(priv->pco, &priv->roi_horizontal_steps, &priv->roi_vertical_steps); + +    priv->roi_x = roi[0] - 1; +    priv->roi_y = roi[1] - 1; +    priv->roi_width = roi[2] - roi[0] + 1; +    priv->roi_height = roi[3] - roi[1] + 1; + +    guint16 camera_type, camera_subtype; +    pco_get_camera_type(priv->pco, &camera_type, &camera_subtype); +    pco_cl_map_entry *map_entry = get_pco_cl_map_entry(camera_type); +    priv->camera_description = map_entry; + +    if (map_entry == NULL) { +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_UNSUPPORTED, +                "Camera type is not supported"); +        g_object_unref(camera); +        return NULL; +    } + +    priv->fg_port = PORT_A; +    priv->fg = Fg_Init(map_entry->so_file, priv->fg_port); + +    if (priv->fg == NULL) { +        g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_FG_INIT, +                "%s", Fg_getLastErrorDescription(priv->fg)); +        g_object_unref(camera); +        return NULL; +    } + +    const guint32 fg_height = priv->height; +    const guint32 fg_width = camera_type == CAMERATYPE_PCO_EDGE ? priv->width * 2 : priv->width; + +    FG_TRY_PARAM(priv->fg, camera, FG_CAMERA_LINK_CAMTYP, &map_entry->cl_type, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_FORMAT, &map_entry->cl_format, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_WIDTH, &fg_width, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_HEIGHT, &fg_height, priv->fg_port); + +    int val = FREE_RUN; +    FG_TRY_PARAM(priv->fg, camera, FG_TRIGGERMODE, &val, priv->fg_port); + +    fill_binnings(priv); + +    /* +     * Here we override property ranges because we didn't know them at property +     * installation time. +     */ +    GObjectClass *camera_class = G_OBJECT_CLASS (UCA_CAMERA_GET_CLASS (camera)); +    property_override_default_guint_value (camera_class, "roi-width", priv->width); +    property_override_default_guint_value (camera_class, "roi-height", priv->height); + +    guint32 rates[4] = {0}; +    gint num_rates = 0; + +    if (pco_get_available_pixelrates(priv->pco, rates, &num_rates) == PCO_NOERROR) { +        GObjectClass *pco_camera_class = G_OBJECT_CLASS (UCA_PCO_CAMERA_GET_CLASS (camera)); + +        fill_pixelrates(priv, rates, num_rates); +        property_override_default_guint_value (pco_camera_class, "sensor-pixelrate", rates[0]); +    } + +    override_temperature_range (priv); +    override_maximum_adcs (priv); + +    return UCA_CAMERA (camera); +} diff --git a/plugins/pco/uca-pco-camera.h b/plugins/pco/uca-pco-camera.h new file mode 100644 index 0000000..73ae44e --- /dev/null +++ b/plugins/pco/uca-pco-camera.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#ifndef __UCA_PCO_CAMERA_H +#define __UCA_PCO_CAMERA_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_PCO_CAMERA             (uca_pco_camera_get_type()) +#define UCA_PCO_CAMERA(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_PCO_CAMERA, UcaPcoCamera)) +#define UCA_IS_PCO_CAMERA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_PCO_CAMERA)) +#define UCA_PCO_CAMERA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UCA_TYPE_PCO_CAMERA, UcaPcoCameraClass)) +#define UCA_IS_PCO_CAMERA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_PCO_CAMERA)) +#define UCA_PCO_CAMERA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_PCO_CAMERA, UcaPcoCameraClass)) + +#define UCA_PCO_CAMERA_ERROR uca_pco_camera_error_quark() +typedef enum { +    UCA_PCO_CAMERA_ERROR_LIBPCO_INIT, +    UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, +    UCA_PCO_CAMERA_ERROR_UNSUPPORTED, +    UCA_PCO_CAMERA_ERROR_FG_INIT, +    UCA_PCO_CAMERA_ERROR_FG_GENERAL, +    UCA_PCO_CAMERA_ERROR_FG_ACQUISITION +} UcaPcoCameraError; + +typedef struct _UcaPcoCamera           UcaPcoCamera; +typedef struct _UcaPcoCameraClass      UcaPcoCameraClass; +typedef struct _UcaPcoCameraPrivate    UcaPcoCameraPrivate; + +typedef enum { +    UCA_PCO_CAMERA_RECORD_MODE_SEQUENCE, +    UCA_PCO_CAMERA_RECORD_MODE_RING_BUFFER, +} UcaPcoCameraRecordMode; + +typedef enum { +    UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO, +    UCA_PCO_CAMERA_ACQUIRE_MODE_EXTERNAL +} UcaPcoCameraAcquireMode; + +typedef enum { +    UCA_PCO_CAMERA_TIMESTAMP_NONE       = 0, +    UCA_PCO_CAMERA_TIMESTAMP_BINARY     = 1 << 0, +    UCA_PCO_CAMERA_TIMESTAMP_ASCII      = 1 << 1 +} UcaPcoCameraTimestamp; + +/** + * UcaPcoCamera: + * + * Creates #UcaPcoCamera instances by loading corresponding shared objects. The + * contents of the #UcaPcoCamera structure are private and should only be + * accessed via the provided API. + */ +struct _UcaPcoCamera { +    /*< private >*/ +    UcaCamera parent; + +    UcaPcoCameraPrivate *priv; +}; + +/** + * UcaPcoCameraClass: + * + * #UcaPcoCamera class + */ +struct _UcaPcoCameraClass { +    /*< private >*/ +    UcaCameraClass parent; +}; + +GType uca_pco_camera_get_type(void); + +G_END_DECLS + +#endif diff --git a/plugins/pf/CMakeLists.txt b/plugins/pf/CMakeLists.txt new file mode 100644 index 0000000..f392603 --- /dev/null +++ b/plugins/pf/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapf) + +find_package(PF) +find_package(FgLib5) +find_package(ClSerMe4) + +if (PF_FOUND AND CLSERME4_FOUND AND FGLIB5_FOUND) +    set(UCA_CAMERA_NAME "pco") + +    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../package-plugin.sh.in +                   ${CMAKE_CURRENT_BINARY_DIR}/../../package-plugin-${UCA_CAMERA_NAME}.sh) + +    include_directories(${PF_INCLUDE_DIRS} +                        ${CLSERME4_INCLUDE_DIR} +                        ${FGLIB5_INCLUDE_DIR}) + +    add_library(ucapf SHARED +                uca-pf-camera.c) + +    target_link_libraries(ucapf +                          ${UCA_DEPS} +                          ${PF_LIBRARIES} +                          ${CLSERME4_LIBRARY} +                          ${FGLIB5_LIBRARY}) + +    install(TARGETS ucapf +            LIBRARY DESTINATION ${LIB_INSTALL_DIR}/uca +            COMPONENT ${UCA_CAMERA_NAME}) +endif() diff --git a/plugins/pf/uca-pf-camera.c b/plugins/pf/uca-pf-camera.c new file mode 100644 index 0000000..35b5edd --- /dev/null +++ b/plugins/pf/uca-pf-camera.c @@ -0,0 +1,351 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#include <gmodule.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fgrab_struct.h> +#include <fgrab_prototyp.h> +#include <libpf/pfcam.h> +#include <errno.h> +#include "uca-camera.h" +#include "uca-pf-camera.h" + +#define FG_TRY_PARAM(fg, camobj, param, val_addr, port)     \ +    { int r = Fg_setParameter(fg, param, val_addr, port);   \ +        if (r != FG_OK) {                                   \ +            g_set_error(error, UCA_PF_CAMERA_ERROR,         \ +                    UCA_PF_CAMERA_ERROR_FG_GENERAL,         \ +                    "%s", Fg_getLastErrorDescription(fg));  \ +            g_object_unref(camobj);                         \ +            return NULL;                                    \ +        } } + +#define FG_SET_ERROR(err, fg, err_type)                 \ +    if (err != FG_OK) {                                 \ +        g_set_error(error, UCA_PF_CAMERA_ERROR,         \ +                err_type,                               \ +                "%s", Fg_getLastErrorDescription(fg));  \ +        return;                                         \ +    } + +#define UCA_PF_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_PF_CAMERA, UcaPfCameraPrivate)) + +G_DEFINE_TYPE(UcaPfCamera, uca_pf_camera, UCA_TYPE_CAMERA) + +/** + * UcaPfCameraError: + * @UCA_PF_CAMERA_ERROR_INIT: Initializing pcilib failed + * @UCA_PF_CAMERA_ERROR_FG_GENERAL: Frame grabber errors + * @UCA_PF_CAMERA_ERROR_FG_ACQUISITION: Acquisition errors related to the frame + *      grabber + * @UCA_PF_CAMERA_ERROR_START_RECORDING: Starting failed + * @UCA_PF_CAMERA_ERROR_STOP_RECORDING: Stopping failed + */ +GQuark uca_pf_camera_error_quark() +{ +    return g_quark_from_static_string("uca-pf-camera-error-quark"); +} + +static gint base_overrideables[] = { +    PROP_NAME, +    PROP_SENSOR_WIDTH, +    PROP_SENSOR_HEIGHT, +    PROP_SENSOR_BITDEPTH, +    PROP_SENSOR_HORIZONTAL_BINNING, +    PROP_SENSOR_HORIZONTAL_BINNINGS, +    PROP_SENSOR_VERTICAL_BINNING, +    PROP_SENSOR_VERTICAL_BINNINGS, +    PROP_SENSOR_MAX_FRAME_RATE, +    PROP_EXPOSURE_TIME, +    PROP_ROI_X, +    PROP_ROI_Y, +    PROP_ROI_WIDTH, +    PROP_ROI_HEIGHT, +    PROP_ROI_WIDTH_MULTIPLIER, +    PROP_ROI_HEIGHT_MULTIPLIER, +    PROP_HAS_STREAMING, +    PROP_HAS_CAMRAM_RECORDING, +    0 +}; + +enum { +    PROP_FOO = N_BASE_PROPERTIES, +    N_PROPERTIES +}; + + +struct _UcaPfCameraPrivate { +    guint roi_width; +    guint roi_height; +    guint last_frame; + +    Fg_Struct *fg; +    guint fg_port; +    dma_mem *fg_mem; +}; + +/* + * We just embed our private structure here. + */ +struct { +    UcaCamera *camera; +} fg_apc_data; + +static int me4_callback(frameindex_t frame, struct fg_apc_data *apc) +{ +    UcaCamera *camera = UCA_CAMERA(apc); +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(camera); +    camera->grab_func(Fg_getImagePtrEx(priv->fg, frame, priv->fg_port, priv->fg_mem), camera->user_data); +    return 0; +} + +static void uca_pf_camera_start_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PF_CAMERA(camera)); +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(camera); +    guint err = FG_OK; + +    if (priv->fg_mem == NULL) { +        const guint num_buffers = 2; +        priv->fg_mem = Fg_AllocMemEx(priv->fg, num_buffers * priv->roi_width * priv->roi_height, num_buffers); + +        if (priv->fg_mem == NULL) { +            g_set_error(error, UCA_PF_CAMERA_ERROR, UCA_PF_CAMERA_ERROR_FG_ACQUISITION, +                    "%s", Fg_getLastErrorDescription(priv->fg)); +            g_object_unref(camera); +            return; +        } +    } + +    gboolean transfer_async = FALSE; +    g_object_get(G_OBJECT(camera), +            "transfer-asynchronously", &transfer_async, +            NULL); + +    if (transfer_async) { +        struct FgApcControl ctrl = { +            .version = 0, +            .data = (struct fg_apc_data *) camera, +            .func = &me4_callback, +            .flags = FG_APC_DEFAULTS, +            .timeout = 1 +        }; + +        err = Fg_registerApcHandler(priv->fg, priv->fg_port, &ctrl, FG_APC_CONTROL_BASIC); +        FG_SET_ERROR(err, priv->fg, UCA_PF_CAMERA_ERROR_FG_ACQUISITION); +    } + +    err = Fg_AcquireEx(priv->fg, priv->fg_port, GRAB_INFINITE, ACQ_STANDARD, priv->fg_mem); +    FG_SET_ERROR(err, priv->fg, UCA_PF_CAMERA_ERROR_FG_ACQUISITION); +} + +static void uca_pf_camera_stop_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PF_CAMERA(camera)); +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(camera); + +    guint err = Fg_stopAcquireEx(priv->fg, priv->fg_port, priv->fg_mem, STOP_SYNC); +    FG_SET_ERROR(err, priv->fg, UCA_PF_CAMERA_ERROR_FG_ACQUISITION); + +    err = Fg_setStatusEx(priv->fg, FG_UNBLOCK_ALL, 0, priv->fg_port, priv->fg_mem); +    if (err == FG_INVALID_PARAMETER) +        g_print(" Unable to unblock all\n"); +} + +static void uca_pf_camera_start_readout(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PF_CAMERA(camera)); +    g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_IMPLEMENTED, +            "This photon focus camera does not support recording to internal memory"); +} + +static void uca_pf_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +{ +    g_return_if_fail(UCA_IS_PF_CAMERA(camera)); +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(camera); + +    priv->last_frame = Fg_getLastPicNumberBlockingEx(priv->fg, priv->last_frame+1, priv->fg_port, 5, priv->fg_mem); + +    if (priv->last_frame <= 0) { +        guint err = FG_OK + 1; +        FG_SET_ERROR(err, priv->fg, UCA_PF_CAMERA_ERROR_FG_GENERAL); +    } + +    guint16 *frame = Fg_getImagePtrEx(priv->fg, priv->last_frame, priv->fg_port, priv->fg_mem); + +    if (*data == NULL) +        *data = g_malloc0(priv->roi_width * priv->roi_height); + +    memcpy((gchar *) *data, (gchar *) frame, priv->roi_width * priv->roi_height); +} + +static void uca_pf_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ +    switch (property_id) { +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            return; +    } +} + +static void uca_pf_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ +    switch (property_id) { +        case PROP_SENSOR_WIDTH: +            g_value_set_uint(value, 1280); +            break; +        case PROP_SENSOR_HEIGHT: +            g_value_set_uint(value, 1024); +            break; +        case PROP_SENSOR_BITDEPTH: +            g_value_set_uint(value, 8); +            break; +        case PROP_SENSOR_HORIZONTAL_BINNING: +            break; +        case PROP_SENSOR_HORIZONTAL_BINNINGS: +            break; +        case PROP_SENSOR_VERTICAL_BINNING: +            break; +        case PROP_SENSOR_VERTICAL_BINNINGS: +            break; +        case PROP_SENSOR_MAX_FRAME_RATE: +            g_value_set_float(value, 488.0); +            break; +        case PROP_HAS_STREAMING: +            g_value_set_boolean(value, TRUE); +            break; +        case PROP_HAS_CAMRAM_RECORDING: +            g_value_set_boolean(value, FALSE); +            break; +        case PROP_EXPOSURE_TIME: +            break; +        case PROP_ROI_X: +            g_value_set_uint(value, 0); +            break; +        case PROP_ROI_Y: +            g_value_set_uint(value, 0); +            break; +        case PROP_ROI_WIDTH: +            g_value_set_uint(value, 1280); +            break; +        case PROP_ROI_HEIGHT: +            g_value_set_uint(value, 1024); +            break; +        case PROP_ROI_WIDTH_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_ROI_HEIGHT_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_NAME: +            g_value_set_string(value, "Photon Focus MV2-D1280-640-CL"); +            break; +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            break; +    } +} + +static void uca_pf_camera_finalize(GObject *object) +{ +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(object); + +    if (priv->fg) { +        if (priv->fg_mem) +            Fg_FreeMemEx(priv->fg, priv->fg_mem); + +        Fg_FreeGrabber(priv->fg); +    } + +    G_OBJECT_CLASS(uca_pf_camera_parent_class)->finalize(object); +} + +static void uca_pf_camera_class_init(UcaPfCameraClass *klass) +{ +    GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +    gobject_class->set_property = uca_pf_camera_set_property; +    gobject_class->get_property = uca_pf_camera_get_property; +    gobject_class->finalize = uca_pf_camera_finalize; + +    UcaCameraClass *camera_class = UCA_CAMERA_CLASS(klass); +    camera_class->start_recording = uca_pf_camera_start_recording; +    camera_class->stop_recording = uca_pf_camera_stop_recording; +    camera_class->start_readout = uca_pf_camera_start_readout; +    camera_class->grab = uca_pf_camera_grab; + +    for (guint i = 0; base_overrideables[i] != 0; i++) +        g_object_class_override_property(gobject_class, base_overrideables[i], uca_camera_props[base_overrideables[i]]); + +    g_type_class_add_private(klass, sizeof(UcaPfCameraPrivate)); +} + +static void uca_pf_camera_init(UcaPfCamera *self) +{ +    self->priv = UCA_PF_CAMERA_GET_PRIVATE(self); +    self->priv->fg = NULL; +    self->priv->fg_mem = NULL; +    self->priv->last_frame = 0; +} + +G_MODULE_EXPORT UcaCamera * +uca_camera_impl_new (GError **error) +{ +    static const gchar *so_file = "libFullAreaGray8.so"; +    static const int camera_link_type = FG_CL_8BIT_FULL_8; +    static const int camera_format = FG_GRAY; + +    /* +    gint num_ports; +    if (pfPortInit(&num_ports) < 0) { +        g_set_error(error, UCA_PF_CAMERA_ERROR, UCA_PF_CAMERA_ERROR_INIT, +                "Could not initialize ports"); +        return NULL; +    } + +    if (pfDeviceOpen(0) < 0) { +        g_set_error(error, UCA_PF_CAMERA_ERROR, UCA_PF_CAMERA_ERROR_INIT, +                "Could not open device"); +        return NULL; +    } +    */ + +    UcaPfCamera *camera = g_object_new(UCA_TYPE_PF_CAMERA, NULL); +    UcaPfCameraPrivate *priv = UCA_PF_CAMERA_GET_PRIVATE(camera); + +    priv->fg_port = PORT_A; +    priv->fg = Fg_Init(so_file, priv->fg_port); + +    /* TODO: get this from the camera */ +    priv->roi_width = 1280; +    priv->roi_height = 1024; + +    if (priv->fg == NULL) { +        g_set_error(error, UCA_PF_CAMERA_ERROR, UCA_PF_CAMERA_ERROR_INIT, +                "%s", Fg_getLastErrorDescription(priv->fg)); +        g_object_unref(camera); +        return NULL; +    } + +    FG_TRY_PARAM(priv->fg, camera, FG_CAMERA_LINK_CAMTYP, &camera_link_type, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_FORMAT, &camera_format, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_WIDTH, &priv->roi_width, priv->fg_port); +    FG_TRY_PARAM(priv->fg, camera, FG_HEIGHT, &priv->roi_height, priv->fg_port); + +    return UCA_CAMERA (camera); +} diff --git a/plugins/pf/uca-pf-camera.h b/plugins/pf/uca-pf-camera.h new file mode 100644 index 0000000..3a309aa --- /dev/null +++ b/plugins/pf/uca-pf-camera.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#ifndef __UCA_PF_CAMERA_H +#define __UCA_PF_CAMERA_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_PF_CAMERA             (uca_pf_camera_get_type()) +#define UCA_PF_CAMERA(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_PF_CAMERA, UcaPfCamera)) +#define UCA_IS_PF_CAMERA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_PF_CAMERA)) +#define UCA_PF_CAMERA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UCA_TYPE_PF_CAMERA, UcaPfCameraClass)) +#define UCA_IS_PF_CAMERA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_PF_CAMERA)) +#define UCA_PF_CAMERA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_PF_CAMERA, UcaPfCameraClass)) + +#define UCA_PF_CAMERA_ERROR uca_pf_camera_error_quark() +typedef enum { +    UCA_PF_CAMERA_ERROR_INIT, +    UCA_PF_CAMERA_ERROR_FG_GENERAL, +    UCA_PF_CAMERA_ERROR_FG_ACQUISITION, +    UCA_PF_CAMERA_ERROR_START_RECORDING, +    UCA_PF_CAMERA_ERROR_STOP_RECORDING +} UcaPfCameraError; + +typedef struct _UcaPfCamera           UcaPfCamera; +typedef struct _UcaPfCameraClass      UcaPfCameraClass; +typedef struct _UcaPfCameraPrivate    UcaPfCameraPrivate; + +/** + * UcaPfCamera: + * + * Creates #UcaPfCamera instances by loading corresponding shared objects. The + * contents of the #UcaPfCamera structure are private and should only be + * accessed via the provided API. + */ +struct _UcaPfCamera { +    /*< private >*/ +    UcaCamera parent; + +    UcaPfCameraPrivate *priv; +}; + +/** + * UcaPfCameraClass: + * + * #UcaPfCamera class + */ +struct _UcaPfCameraClass { +    /*< private >*/ +    UcaCameraClass parent; +}; + +GType uca_pf_camera_get_type(void); + +G_END_DECLS + +#endif diff --git a/plugins/pylon/CMakeLists.txt b/plugins/pylon/CMakeLists.txt new file mode 100644 index 0000000..18823a4 --- /dev/null +++ b/plugins/pylon/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapylon) + +find_package(Pylon) + +if (PYLON_FOUND) +    set(UCA_CAMERA_NAME "pylon") + +    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../package-plugin.sh.in +                   ${CMAKE_CURRENT_BINARY_DIR}/../../package-plugin-${UCA_CAMERA_NAME}.sh) + +    include_directories(${LIBPYLONCAM_INCLUDEDIR}) + +    add_library(ucapylon SHARED +                uca-pylon-camera.c) + +    target_link_libraries(ucapylon +                          ${UCA_DEPS} +                          ${LIBPYLONCAM_LIBRARIES}) + +    install(TARGETS ucapylon +            LIBRARY DESTINATION ${LIB_INSTALL_DIR}/uca +            COMPONENT ${UCA_CAMERA_NAME}) +endif() diff --git a/plugins/pylon/uca-pylon-camera.c b/plugins/pylon/uca-pylon-camera.c new file mode 100644 index 0000000..541b69b --- /dev/null +++ b/plugins/pylon/uca-pylon-camera.c @@ -0,0 +1,391 @@ +/* Copyright (C) 2012 Volker Kaiser <volker.kaiser@softwareschneiderei> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <libpyloncam/pylon_camera.h> +#include "uca-camera.h" +#include "uca-pylon-camera.h" + +#define UCA_PYLON_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_PYLON_CAMERA, UcaPylonCameraPrivate)) + +G_DEFINE_TYPE(UcaPylonCamera, uca_pylon_camera, UCA_TYPE_CAMERA) + +/** + * UcapylonCameraError: + * @UCA_PYLON_CAMERA_ERROR_LIBPYLON_INIT: Initializing libpylon failed + * @UCA_PYLON_CAMERA_ERROR_LIBPYLON_GENERAL: General libpylon error + * @UCA_PYLON_CAMERA_ERROR_UNSUPPORTED: Camera type is not supported + * @UCA_PYLON_CAMERA_ERROR_FG_INIT: Framegrabber initialization failed + * @UCA_PYLON_CAMERA_ERROR_FG_GENERAL: General framegrabber error + * @UCA_PYLON_CAMERA_ERROR_FG_ACQUISITION: Framegrabber acquisition error + */ +GQuark uca_pylon_camera_error_quark() +{ +    return g_quark_from_static_string("uca-pylon-camera-error-quark"); +} + +enum { +    PROP_ROI_WIDTH_DEFAULT = N_BASE_PROPERTIES, +    PROP_ROI_HEIGHT_DEFAULT, +    PROP_GAIN, +    N_PROPERTIES +}; + +static gint base_overrideables[] = { +    PROP_NAME, +    PROP_SENSOR_WIDTH, +    PROP_SENSOR_HEIGHT, +    PROP_SENSOR_BITDEPTH, +    PROP_SENSOR_HORIZONTAL_BINNING, +    PROP_SENSOR_HORIZONTAL_BINNINGS, +    PROP_SENSOR_VERTICAL_BINNING, +    PROP_SENSOR_VERTICAL_BINNINGS, +    PROP_SENSOR_MAX_FRAME_RATE, +    PROP_TRIGGER_MODE, +    PROP_EXPOSURE_TIME, +    PROP_ROI_X, +    PROP_ROI_Y, +    PROP_ROI_WIDTH, +    PROP_ROI_HEIGHT, +    PROP_ROI_WIDTH_MULTIPLIER, +    PROP_ROI_HEIGHT_MULTIPLIER, +    PROP_HAS_STREAMING, +    PROP_HAS_CAMRAM_RECORDING, +    0 +}; + +static GParamSpec *pylon_properties[N_PROPERTIES] = { NULL, }; + + +struct _UcaPylonCameraPrivate { +    guint bit_depth; +    gsize num_bytes; + +    guint width; +    guint height; +    guint16 roi_x, roi_y; +    guint16 roi_width, roi_height; +    GValueArray *binnings; +}; + + +static void pylon_get_roi(GObject *object, GError** error) +{ +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(object); +    pylon_camera_get_roi(&priv->roi_x, &priv->roi_y, &priv->roi_width, &priv->roi_height, error); +} + +static void pylon_set_roi(GObject *object, GError** error) +{ +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(object); +    pylon_camera_set_roi(priv->roi_x, priv->roi_y, priv->roi_width, priv->roi_height, error); +} + +UcaPylonCamera *uca_pylon_camera_new(GError **error) +{ +    UcaPylonCamera *camera = g_object_new(UCA_TYPE_PYLON_CAMERA, NULL); +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(camera); + +    pylon_camera_new("/usr/local/lib64", "141.52.111.110", error); + +    if (*error != NULL) +        return NULL; + +    pylon_camera_get_sensor_size(&priv->width, &priv->height, error); + +    if (*error != NULL) +        return NULL; + +    return camera; +} + +static void uca_pylon_camera_start_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PYLON_CAMERA(camera)); +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(camera); + +    priv->num_bytes = 2; +    pylon_camera_start_acquision(error); + +} + +static void uca_pylon_camera_stop_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_PYLON_CAMERA(camera)); +    pylon_camera_stop_acquision(error); +} + +static void uca_pylon_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +{ +    g_return_if_fail(UCA_IS_PYLON_CAMERA(camera)); +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(camera); + +    if (*data == NULL) { +        *data = g_malloc0(priv->width * priv->height * priv->num_bytes); +    } +    pylon_camera_grab(data, error); +} + +static void uca_pylon_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(object); +    GError* error = NULL; + +    switch (property_id) { +        case PROP_SENSOR_HORIZONTAL_BINNING: +          /* intentional fall-through*/ +        case PROP_SENSOR_VERTICAL_BINNING: +          /* intentional fall-through*/ +        case PROP_TRIGGER_MODE: +          break; + +        case PROP_ROI_X: +          { +              priv->roi_x = g_value_get_uint(value); +              pylon_set_roi(object, &error); +          } +          break; + +        case PROP_ROI_Y: +          { +              priv->roi_y = g_value_get_uint(value); +              pylon_set_roi(object, &error); +          } +          break; + +        case PROP_ROI_WIDTH: +          { +              priv->roi_width = g_value_get_uint(value); +              pylon_set_roi(object, &error); +          } +          break; + +        case PROP_ROI_HEIGHT: +          { +              priv->roi_height = g_value_get_uint(value); +              pylon_set_roi(object, &error); +          } +          break; + +        case PROP_EXPOSURE_TIME: +          pylon_camera_set_exposure_time(g_value_get_double(value), &error); +          break; + +        case PROP_GAIN: +            pylon_camera_set_gain(g_value_get_int(value), &error); +            break; + +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            return; +    } +} + +static void uca_pylon_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(object); +    GError* error = NULL; + +    switch (property_id) { +        case PROP_SENSOR_WIDTH: +            pylon_camera_get_sensor_size(&priv->width, &priv->height, &error); +            g_value_set_uint(value, priv->width); +            break; + +        case PROP_SENSOR_HEIGHT: +            pylon_camera_get_sensor_size(&priv->width, &priv->height, &error); +            g_value_set_uint(value, priv->height); +            break; + +        case PROP_SENSOR_BITDEPTH: +            pylon_camera_get_bit_depth(&priv->bit_depth, &error); +            g_value_set_uint(value, priv->bit_depth); +            break; + +        case PROP_SENSOR_HORIZONTAL_BINNING: +            g_value_set_uint(value, 1); +            break; + +        case PROP_SENSOR_HORIZONTAL_BINNINGS: +            g_value_set_boxed(value, priv->binnings); +            break; + +        case PROP_SENSOR_VERTICAL_BINNING: +            g_value_set_uint(value, 1); +            break; + +        case PROP_SENSOR_VERTICAL_BINNINGS: +            g_value_set_boxed(value, priv->binnings); +            break; + +        case PROP_SENSOR_MAX_FRAME_RATE: +            g_value_set_float(value, 0.0); +            break; + +        case PROP_TRIGGER_MODE: +            g_value_set_enum(value, UCA_CAMERA_TRIGGER_AUTO); +            break; + +        case PROP_HAS_STREAMING: +            g_value_set_boolean(value, FALSE); +            break; + +        case PROP_HAS_CAMRAM_RECORDING: +            g_value_set_boolean(value, FALSE); +            break; + +        case PROP_ROI_X: +            { +                pylon_get_roi(object, &error); +                g_value_set_uint(value, priv->roi_x); +            } +            break; + +        case PROP_ROI_Y: +            { +                pylon_get_roi(object, &error); +                g_value_set_uint(value, priv->roi_y); +            } +            break; + +        case PROP_ROI_WIDTH: +            { +                pylon_get_roi(object, &error); +                g_value_set_uint(value, priv->roi_width); +            } +            break; + +        case PROP_ROI_HEIGHT: +            { +                pylon_get_roi(object, &error); +                g_value_set_uint(value, priv->roi_height); +            } +            break; + +        case PROP_ROI_WIDTH_DEFAULT: +            pylon_camera_get_sensor_size(&priv->width, &priv->height, &error); +            g_value_set_uint(value, priv->width); +            break; + +        case PROP_ROI_HEIGHT_DEFAULT: +            pylon_camera_get_sensor_size(&priv->width, &priv->height, &error); +            g_value_set_uint(value, priv->height); +            break; + +        case PROP_GAIN: +            { +                gint gain=0; +                pylon_camera_get_gain(&gain, &error); +                g_value_set_int(value, gain); +            } +            break; + +        case PROP_ROI_WIDTH_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; + +        case PROP_ROI_HEIGHT_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; + +        case PROP_EXPOSURE_TIME: +            { +                gdouble exp_time = 0.0; +                pylon_camera_get_exposure_time(&exp_time, &error); +                g_value_set_double(value, exp_time); +            } +            break; + +        case PROP_NAME: +            g_value_set_string(value, "Pylon Camera"); +            break; + +        default: +            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            break; +    } +} + +static void uca_pylon_camera_finalize(GObject *object) +{ +    UcaPylonCameraPrivate *priv = UCA_PYLON_CAMERA_GET_PRIVATE(object); +    g_value_array_free(priv->binnings); + +    G_OBJECT_CLASS(uca_pylon_camera_parent_class)->finalize(object); +} + +static void uca_pylon_camera_class_init(UcaPylonCameraClass *klass) +{ +    GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +    gobject_class->set_property = uca_pylon_camera_set_property; +    gobject_class->get_property = uca_pylon_camera_get_property; +    gobject_class->finalize = uca_pylon_camera_finalize; + +    UcaCameraClass *camera_class = UCA_CAMERA_CLASS(klass); +    camera_class->start_recording = uca_pylon_camera_start_recording; +    camera_class->stop_recording = uca_pylon_camera_stop_recording; +    camera_class->grab = uca_pylon_camera_grab; + +    for (guint i = 0; base_overrideables[i] != 0; i++) +        g_object_class_override_property(gobject_class, base_overrideables[i], uca_camera_props[base_overrideables[i]]); + +    pylon_properties[PROP_NAME] = +        g_param_spec_string("name", +            "Name of the camera", +            "Name of the camera", +            "", G_PARAM_READABLE); + +    pylon_properties[PROP_ROI_WIDTH_DEFAULT] = +        g_param_spec_uint("roi-width-default", +            "ROI width default value", +            "ROI width default value", +            0, G_MAXUINT, 0, +            G_PARAM_READABLE); + +    pylon_properties[PROP_ROI_HEIGHT_DEFAULT] = +        g_param_spec_uint("roi-height-default", +            "ROI height default value", +            "ROI height default value", +            0, G_MAXUINT, 0, +            G_PARAM_READABLE); + +    pylon_properties[PROP_GAIN] = +        g_param_spec_int("gain", +            "gain", +            "gain", +            0, G_MAXINT, 0, +            G_PARAM_READWRITE); + +    for (guint id = N_BASE_PROPERTIES; id < N_PROPERTIES; id++) +        g_object_class_install_property(gobject_class, id, pylon_properties[id]); + +    g_type_class_add_private(klass, sizeof(UcaPylonCameraPrivate)); +} + +static void uca_pylon_camera_init(UcaPylonCamera *self) +{ +    self->priv = UCA_PYLON_CAMERA_GET_PRIVATE(self); + +    /* binnings */ +    GValue val = {0}; +    g_value_init(&val, G_TYPE_UINT); +    g_value_set_uint(&val, 1); +    self->priv->binnings  = g_value_array_new(1); +    g_value_array_append(self->priv->binnings, &val); +} diff --git a/plugins/pylon/uca-pylon-camera.h b/plugins/pylon/uca-pylon-camera.h new file mode 100644 index 0000000..eebf63c --- /dev/null +++ b/plugins/pylon/uca-pylon-camera.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#ifndef __UCA_PYLON_CAMERA_H +#define __UCA_PYLON_CAMERA_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_PYLON_CAMERA             (uca_pylon_camera_get_type()) +#define UCA_PYLON_CAMERA(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_PYLON_CAMERA, UcaPylonCamera)) +#define UCA_IS_PYLON_CAMERA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_PYLON_CAMERA)) +#define UCA_PYLON_CAMERA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_PYLON_CAMERA, UfoPylonCameraClass)) +#define UCA_IS_PYLON_CAMERA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_PYLON_CAMERA)) +#define UCA_PYLON_CAMERA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_PYLON_CAMERA, UcaPylonCameraClass)) + +#define UCA_PYLON_CAMERA_ERROR uca_pylon_camera_error_quark() +typedef enum { +    UCA_PYLON_CAMERA_ERROR_LIBPYLON_INIT, +    UCA_PYLON_CAMERA_ERROR_LIBPYLON_GENERAL, +    UCA_PYLON_CAMERA_ERROR_UNSUPPORTED, +} UcaPylonCameraError; + +typedef struct _UcaPylonCamera           UcaPylonCamera; +typedef struct _UcaPylonCameraClass      UcaPylonCameraClass; +typedef struct _UcaPylonCameraPrivate    UcaPylonCameraPrivate; + +/** + * UcaPylonCamera: + * + * Creates #UcaPylonCamera instances by loading corresponding shared objects. The + * contents of the #UcaPylonCamera structure are private and should only be + * accessed via the provided API. + */ +struct _UcaPylonCamera { +    /*< private >*/ +    UcaCamera parent; + +    UcaPylonCameraPrivate *priv; +}; + +/** + * UcaPylonCameraClass: + * + * #UcaPylonCamera class + */ +struct _UcaPylonCameraClass { +    /*< private >*/ +    UcaCameraClass parent; +}; + +UcaPylonCamera *uca_pylon_camera_new(GError **error); + +GType uca_pylon_camera_get_type(void); + +G_END_DECLS + +#endif diff --git a/plugins/ufo/CMakeLists.txt b/plugins/ufo/CMakeLists.txt new file mode 100644 index 0000000..fe89668 --- /dev/null +++ b/plugins/ufo/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8) +project(ucaufo) + +find_package(IPE) + +if (IPE_FOUND) +    set(UCA_CAMERA_NAME "ufo") + +    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../package-plugin.sh.in +                   ${CMAKE_CURRENT_BINARY_DIR}/../../package-plugin-${UCA_CAMERA_NAME}.sh) + +    include_directories(${IPE_INCLUDE_DIRS}) + +    add_library(ucaufo SHARED +                uca-ufo-camera.c) + +    target_link_libraries(ucaufo +                          ${UCA_DEPS} +                          ${IPE_LIBRARIES}) + +    install(TARGETS ucaufo +            LIBRARY DESTINATION ${LIB_INSTALL_DIR}/uca +            COMPONENT ${UCA_CAMERA_NAME}) +endif() diff --git a/plugins/ufo/uca-ufo-camera.c b/plugins/ufo/uca-ufo-camera.c new file mode 100644 index 0000000..7542fdf --- /dev/null +++ b/plugins/ufo/uca-ufo-camera.c @@ -0,0 +1,497 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#include <gmodule.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <pcilib.h> +#include <errno.h> +#include "uca-camera.h" +#include "uca-ufo-camera.h" + +#define PCILIB_SET_ERROR(err, err_type)                 \ +    if (err != 0) {                                     \ +        g_set_error(error, UCA_UFO_CAMERA_ERROR,        \ +                err_type,                               \ +                "pcilib: %s", strerror(err));           \ +        return;                                         \ +    } + +#define UCA_UFO_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_UFO_CAMERA, UcaUfoCameraPrivate)) + +G_DEFINE_TYPE(UcaUfoCamera, uca_ufo_camera, UCA_TYPE_CAMERA) + +static const guint SENSOR_WIDTH = 2048; +static const guint SENSOR_HEIGHT = 1088; +static const gdouble EXPOSURE_TIME_SCALE = 2.69e6; + +/** + * UcaUfoCameraError: + * @UCA_UFO_CAMERA_ERROR_INIT: Initializing pcilib failed + * @UCA_UFO_CAMERA_ERROR_START_RECORDING: Starting failed + * @UCA_UFO_CAMERA_ERROR_STOP_RECORDING: Stopping failed + * @UCA_UFO_CAMERA_ERROR_TRIGGER: Triggering a frame failed + * @UCA_UFO_CAMERA_ERROR_NEXT_EVENT: No event happened + * @UCA_UFO_CAMERA_ERROR_NO_DATA: No data was transmitted + * @UCA_UFO_CAMERA_ERROR_MAYBE_CORRUPTED: Data is potentially corrupted + */ +GQuark uca_ufo_camera_error_quark() +{ +    return g_quark_from_static_string("uca-ufo-camera-error-quark"); +} + +enum { +    PROP_SENSOR_TEMPERATURE = N_BASE_PROPERTIES, +    PROP_FPGA_TEMPERATURE, +    PROP_UFO_START, +    N_MAX_PROPERTIES = 512 +}; + +static gint base_overrideables[] = { +    PROP_NAME, +    PROP_SENSOR_WIDTH, +    PROP_SENSOR_HEIGHT, +    PROP_SENSOR_HORIZONTAL_BINNING, +    PROP_SENSOR_VERTICAL_BINNING, +    PROP_SENSOR_MAX_FRAME_RATE, +    PROP_SENSOR_BITDEPTH, +    PROP_EXPOSURE_TIME, +    PROP_ROI_X, +    PROP_ROI_Y, +    PROP_ROI_WIDTH, +    PROP_ROI_HEIGHT, +    PROP_ROI_WIDTH_MULTIPLIER, +    PROP_ROI_HEIGHT_MULTIPLIER, +    PROP_HAS_STREAMING, +    PROP_HAS_CAMRAM_RECORDING, +    PROP_TRIGGER_MODE, +    0, +}; + +typedef struct _RegisterInfo { +    gchar  *name; +    guint   cached_value; +} RegisterInfo; + +static GParamSpec *ufo_properties[N_MAX_PROPERTIES] = { NULL, }; + +static guint N_PROPERTIES; +static GHashTable *ufo_property_table; /* maps from prop_id to RegisterInfo* */ + +struct _UcaUfoCameraPrivate { +    pcilib_t           *handle; +    pcilib_timeout_t    timeout; +    guint               n_bits; +    enum { +        FPGA_48MHZ = 0, +        FPGA_40MHZ +    }                   frequency; +}; + +static void +error_handler (const char *format, ...) +{ +    va_list args; +    gchar *message; + +    va_start (args, format); +    message = g_strdup_vprintf (format, args); +    g_warning ("%s", message); +    va_end (args); +} + +static guint +read_register_value (pcilib_t *handle, const gchar *name) +{ +    pcilib_register_value_t reg_value; + +    pcilib_read_register(handle, NULL, name, ®_value); +    return (guint) reg_value; +} + +static int event_callback(pcilib_event_id_t event_id, pcilib_event_info_t *info, void *user) +{ +    UcaCamera *camera = UCA_CAMERA(user); +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(camera); +    size_t error = 0; + +    void *buffer = pcilib_get_data(priv->handle, event_id, PCILIB_EVENT_DATA, &error); + +    if (buffer == NULL) { +        pcilib_trigger(priv->handle, PCILIB_EVENT0, 0, NULL); +        return PCILIB_STREAMING_CONTINUE; +    } + +    camera->grab_func(buffer, camera->user_data); +    pcilib_return_data(priv->handle, event_id, PCILIB_EVENT_DATA, buffer); +    pcilib_trigger(priv->handle, PCILIB_EVENT0, 0, NULL); + +    return PCILIB_STREAMING_CONTINUE; +} + +G_MODULE_EXPORT UcaCamera * +uca_camera_impl_new (GError **error) +{ +    pcilib_model_t model = PCILIB_MODEL_DETECT; +    pcilib_model_description_t *model_description; +    pcilib_t *handle = pcilib_open("/dev/fpga0", model); +    guint prop = PROP_UFO_START; +    guint adc_resolution; + +    if (handle == NULL) { +        g_set_error(error, UCA_UFO_CAMERA_ERROR, UCA_UFO_CAMERA_ERROR_INIT, +                "Initializing pcilib failed"); +        return NULL; +    } + +    pcilib_set_error_handler(&error_handler, &error_handler); + +    /* Generate properties from model description */ +    model_description = pcilib_get_model_description(handle); +    ufo_property_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + +    for (guint i = 0; model_description->registers[i].name != NULL; i++) { +        GParamFlags flags = 0; +        RegisterInfo *reg_info; +        gchar *prop_name; +        pcilib_register_description_t *reg; +        pcilib_register_value_t value; + +        reg = &model_description->registers[i]; + +        switch (reg->mode) { +            case PCILIB_REGISTER_R: +                flags = G_PARAM_READABLE; +                break; +            case PCILIB_REGISTER_W: +            case PCILIB_REGISTER_W1C: +                flags = G_PARAM_WRITABLE; +                break; +            case PCILIB_REGISTER_RW: +            case PCILIB_REGISTER_RW1C: +                flags = G_PARAM_READWRITE; +                break; +        } + +        pcilib_read_register (handle, NULL, reg->name, &value); +        reg_info = g_new0 (RegisterInfo, 1); +        reg_info->name = g_strdup (reg->name); +        reg_info->cached_value = (guint32) value; + +        g_hash_table_insert (ufo_property_table, GINT_TO_POINTER (prop), reg_info); +        prop_name = g_strdup_printf ("ufo-%s", reg->name); + +        ufo_properties[prop++] = g_param_spec_uint ( +                prop_name, reg->description, reg->description, +                0, G_MAXUINT, reg->defvalue, flags); + +        g_free (prop_name); +    } + +    N_PROPERTIES = prop; + +    UcaUfoCamera *camera = g_object_new(UCA_TYPE_UFO_CAMERA, NULL); +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(camera); + +    priv->frequency = read_register_value (handle, "bit_mode"); +    adc_resolution = read_register_value (handle, "adc_resolution"); + +    switch (adc_resolution) { +        case 0: +            priv->n_bits = 10; +            break; +        case 1: +            priv->n_bits = 11; +            break; +        case 2: +            priv->n_bits = 12; +            break; +    } + +    priv->handle = handle; + +    return UCA_CAMERA (camera); +} + +static void uca_ufo_camera_start_recording(UcaCamera *camera, GError **error) +{ +    UcaUfoCameraPrivate *priv; +    gdouble exposure_time; +    int     err; + +    g_return_if_fail(UCA_IS_UFO_CAMERA(camera)); + +    priv = UCA_UFO_CAMERA_GET_PRIVATE(camera); +    err = pcilib_start(priv->handle, PCILIB_EVENT_DATA, PCILIB_EVENT_FLAGS_DEFAULT); +    PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_START_RECORDING); + +    gboolean transfer_async = FALSE; +    g_object_get(G_OBJECT(camera), +            "transfer-asynchronously", &transfer_async, +            "exposure-time", &exposure_time, +            NULL); + +    priv->timeout = ((pcilib_timeout_t) (exposure_time * 1000 + 50.0) * 1000); + +    if (transfer_async) { +        pcilib_trigger(priv->handle, PCILIB_EVENT0, 0, NULL); +        pcilib_stream(priv->handle, &event_callback, camera); +    } +} + +static void uca_ufo_camera_stop_recording(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_UFO_CAMERA(camera)); +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(camera); +    int err = pcilib_stop(priv->handle, PCILIB_EVENT_FLAGS_DEFAULT); +    PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_START_RECORDING); +} + +static void uca_ufo_camera_start_readout(UcaCamera *camera, GError **error) +{ +    g_return_if_fail(UCA_IS_UFO_CAMERA(camera)); +    g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_IMPLEMENTED, +            "Ufo camera does not support recording to internal memory"); +} + +static void uca_ufo_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +{ +    g_return_if_fail(UCA_IS_UFO_CAMERA(camera)); +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(camera); +    pcilib_event_id_t   event_id; +    pcilib_event_info_t event_info; +    size_t err; + +    const gsize size = SENSOR_WIDTH * SENSOR_HEIGHT * sizeof(guint16); + +    err = pcilib_trigger(priv->handle, PCILIB_EVENT0, 0, NULL); +    PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_TRIGGER); + +    err = pcilib_get_next_event(priv->handle, priv->timeout, &event_id, sizeof(pcilib_event_info_t), &event_info); +    PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_NEXT_EVENT); + +    if (*data == NULL) +        *data = g_malloc0(SENSOR_WIDTH * SENSOR_HEIGHT * sizeof(guint16)); + +    gpointer src = pcilib_get_data(priv->handle, event_id, PCILIB_EVENT_DATA, &err); + +    if (src == NULL) +        PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_NO_DATA); + +    /* +     * Apparently, we checked that err equals total size in previous version. +     * This is problematic because errno is positive and size could be equal +     * even when an error condition is met, e.g. with a very small ROI. However, +     * we don't know if src will always be NULL when an error occured. +     */ +    /* assert(err == size); */ + +    memcpy(*data, src, size); + +    /* +     * Another problem here. What does this help us? At this point we have +     * already overwritten the original buffer but can only know here if the +     * data is corrupted. +     */ +    err = pcilib_return_data(priv->handle, event_id, PCILIB_EVENT_DATA, data); +    PCILIB_SET_ERROR(err, UCA_UFO_CAMERA_ERROR_MAYBE_CORRUPTED); +} + +static void +uca_ufo_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_EXPOSURE_TIME: +            { +                const guint frequency = priv->frequency == FPGA_40MHZ ? 40 : 48; +                pcilib_register_value_t reg_value = (pcilib_register_value_t) 129 / frequency * 1000 * 1000 * g_value_get_double(value); +                pcilib_write_register(priv->handle, NULL, "cmosis_exp_time", reg_value); +            } +            break; +        case PROP_ROI_X: +        case PROP_ROI_Y: +        case PROP_ROI_WIDTH: +        case PROP_ROI_HEIGHT: +            g_debug("ROI feature not implemented yet"); +            break; + +        default: +            { +                RegisterInfo *reg_info = g_hash_table_lookup (ufo_property_table, GINT_TO_POINTER (property_id)); + +                if (reg_info != NULL) { +                    pcilib_register_value_t reg_value; + +                    reg_value = g_value_get_uint (value); +                    pcilib_write_register(priv->handle, NULL, reg_info->name, reg_value); +                    pcilib_read_register (priv->handle, NULL, reg_info->name, ®_value); +                    reg_info->cached_value = (guint) reg_value; +                } +                else +                    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            } +            return; +    } +} + + +static void +uca_ufo_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(object); + +    switch (property_id) { +        case PROP_SENSOR_WIDTH: +            g_value_set_uint(value, SENSOR_WIDTH); +            break; +        case PROP_SENSOR_HEIGHT: +            g_value_set_uint(value, SENSOR_HEIGHT); +            break; +        case PROP_SENSOR_BITDEPTH: +            g_value_set_uint (value, priv->n_bits); +            break; +        case PROP_SENSOR_HORIZONTAL_BINNING: +            g_value_set_uint(value, 1); +            break; +        case PROP_SENSOR_VERTICAL_BINNING: +            g_value_set_uint(value, 1); +            break; +        case PROP_SENSOR_MAX_FRAME_RATE: +            g_value_set_float(value, 340.0); +            break; +        case PROP_SENSOR_TEMPERATURE: +            { +                const double a = priv->frequency == FPGA_48MHZ ? 0.3 : 0.25; +                const double b = priv->frequency == FPGA_48MHZ ? 1000 : 1200; +                guint32 temperature; + +                temperature = read_register_value (priv->handle, "sensor_temperature"); +                g_value_set_double (value, a * (temperature - b)); +            } +            break; +        case PROP_FPGA_TEMPERATURE: +            { +                const double a = 503.975 / 1024.0; +                const double b = 273.15; +                guint32 temperature; + +                temperature = read_register_value (priv->handle, "fpga_temperature"); +                g_value_set_double (value, a * temperature - b); +            } +            break; +        case PROP_EXPOSURE_TIME: +            { +                const gdouble frequency = priv->frequency == FPGA_40MHZ ? 40.0 : 48.0; +                g_value_set_double (value, read_register_value (priv->handle, "cmosis_exp_time") * 129 / frequency / 1000 / 1000 ); +            } +            break; +        case PROP_HAS_STREAMING: +            g_value_set_boolean(value, TRUE); +            break; +        case PROP_HAS_CAMRAM_RECORDING: +            g_value_set_boolean(value, FALSE); +            break; +        case PROP_ROI_X: +            g_value_set_uint(value, 0); +            break; +        case PROP_ROI_Y: +            g_value_set_uint(value, 0); +            break; +        case PROP_ROI_WIDTH: +            g_value_set_uint(value, SENSOR_WIDTH); +            break; +        case PROP_ROI_HEIGHT: +            g_value_set_uint(value, SENSOR_HEIGHT); +            break; +        case PROP_ROI_WIDTH_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_ROI_HEIGHT_MULTIPLIER: +            g_value_set_uint(value, 1); +            break; +        case PROP_NAME: +            g_value_set_string(value, "Ufo Camera w/ CMOSIS CMV2000"); +            break; +        case PROP_TRIGGER_MODE: +            break; +        default: +            { +                RegisterInfo *reg_info = g_hash_table_lookup (ufo_property_table, GINT_TO_POINTER (property_id)); + +                if (reg_info != NULL) +                    g_value_set_uint (value, reg_info->cached_value); +                else +                    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +            } +            break; +    } +} + +static void uca_ufo_camera_finalize(GObject *object) +{ +    UcaUfoCameraPrivate *priv = UCA_UFO_CAMERA_GET_PRIVATE(object); +    pcilib_close(priv->handle); +    G_OBJECT_CLASS(uca_ufo_camera_parent_class)->finalize(object); +} + +static void uca_ufo_camera_class_init(UcaUfoCameraClass *klass) +{ +    GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +    gobject_class->set_property = uca_ufo_camera_set_property; +    gobject_class->get_property = uca_ufo_camera_get_property; +    gobject_class->finalize = uca_ufo_camera_finalize; + +    UcaCameraClass *camera_class = UCA_CAMERA_CLASS(klass); +    camera_class->start_recording = uca_ufo_camera_start_recording; +    camera_class->stop_recording = uca_ufo_camera_stop_recording; +    camera_class->start_readout = uca_ufo_camera_start_readout; +    camera_class->grab = uca_ufo_camera_grab; + +    for (guint i = 0; base_overrideables[i] != 0; i++) +        g_object_class_override_property(gobject_class, base_overrideables[i], uca_camera_props[base_overrideables[i]]); + +    ufo_properties[PROP_SENSOR_TEMPERATURE] = +        g_param_spec_double("sensor-temperature", +            "Temperature of the sensor", +            "Temperature of the sensor in degree Celsius", +            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, +            G_PARAM_READABLE); + +    ufo_properties[PROP_FPGA_TEMPERATURE] = +        g_param_spec_double("fpga-temperature", +            "Temperature of the FPGA", +            "Temperature of the FPGA in degree Celsius", +            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, +            G_PARAM_READABLE); + +    /* +     * This automatic property installation includes the properties created +     * dynamically in uca_ufo_camera_new(). +     */ +    for (guint id = N_BASE_PROPERTIES; id < N_PROPERTIES; id++) +        g_object_class_install_property(gobject_class, id, ufo_properties[id]); + +    g_type_class_add_private(klass, sizeof(UcaUfoCameraPrivate)); +} + +static void uca_ufo_camera_init(UcaUfoCamera *self) +{ +    self->priv = UCA_UFO_CAMERA_GET_PRIVATE(self); +} diff --git a/plugins/ufo/uca-ufo-camera.h b/plugins/ufo/uca-ufo-camera.h new file mode 100644 index 0000000..7030389 --- /dev/null +++ b/plugins/ufo/uca-ufo-camera.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu> +   (Karlsruhe Institute of Technology) + +   This library is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as published by the +   Free Software Foundation; either version 2.1 of the License, or (at your +   option) any later version. + +   This library is distributed in the hope that it will be useful, but WITHOUT +   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +   details. + +   You should have received a copy of the GNU Lesser General Public License along +   with this library; if not, write to the Free Software Foundation, Inc., 51 +   Franklin St, Fifth Floor, Boston, MA 02110, USA */ + +#ifndef __UCA_UFO_CAMERA_H +#define __UCA_UFO_CAMERA_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_UFO_CAMERA             (uca_ufo_camera_get_type()) +#define UCA_UFO_CAMERA(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_UFO_CAMERA, UcaUfoCamera)) +#define UCA_IS_UFO_CAMERA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_UFO_CAMERA)) +#define UCA_UFO_CAMERA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UCA_TYPE_UFO_CAMERA, UcaUfoCameraClass)) +#define UCA_IS_UFO_CAMERA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_UFO_CAMERA)) +#define UCA_UFO_CAMERA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_UFO_CAMERA, UcaUfoCameraClass)) + +#define UCA_UFO_CAMERA_ERROR uca_ufo_camera_error_quark() +typedef enum { +    UCA_UFO_CAMERA_ERROR_INIT, +    UCA_UFO_CAMERA_ERROR_START_RECORDING, +    UCA_UFO_CAMERA_ERROR_STOP_RECORDING, +    UCA_UFO_CAMERA_ERROR_TRIGGER, +    UCA_UFO_CAMERA_ERROR_NEXT_EVENT, +    UCA_UFO_CAMERA_ERROR_NO_DATA, +    UCA_UFO_CAMERA_ERROR_MAYBE_CORRUPTED +} UcaUfoCameraError; + +typedef struct _UcaUfoCamera           UcaUfoCamera; +typedef struct _UcaUfoCameraClass      UcaUfoCameraClass; +typedef struct _UcaUfoCameraPrivate    UcaUfoCameraPrivate; + +/** + * UcaUfoCamera: + * + * Creates #UcaUfoCamera instances by loading corresponding shared objects. The + * contents of the #UcaUfoCamera structure are private and should only be + * accessed via the provided API. + */ +struct _UcaUfoCamera { +    /*< private >*/ +    UcaCamera parent; + +    UcaUfoCameraPrivate *priv; +}; + +/** + * UcaUfoCameraClass: + * + * #UcaUfoCamera class + */ +struct _UcaUfoCameraClass { +    /*< private >*/ +    UcaCameraClass parent; +}; + +GType uca_ufo_camera_get_type(void); + +G_END_DECLS + +#endif  | 
