/* Copyright (C) 2011, 2012 Matthias Vogelgesang (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 #include #include #include #include #include #include #include #include "uca-camera.h" #include "uca-pco-camera.h" #include "uca-pco-enums.h" #define FG_TRY_PARAM(fg, error, 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)); \ return FALSE; \ } } #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 CHECK_AND_RETURN_ON_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 CHECK_AND_RETURN_VAL_ON_PCO_ERROR(err, val) \ if ((err) != PCO_NOERROR) { \ g_set_error (error, UCA_PCO_CAMERA_ERROR, \ UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, \ "libpco error %x", err); \ return val; \ } #define UCA_PCO_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_PCO_CAMERA, UcaPcoCameraPrivate)) static void uca_pco_camera_initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (UcaPcoCamera, uca_pco_camera, UCA_TYPE_CAMERA, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, uca_pco_camera_initable_iface_init)) #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 */ /** * UcaPcoCameraStorageMode: * @UCA_PCO_CAMERA_STORAGE_MODE_RECORDER: Record all frames and output live * preview frames in timely fashion, i.e. skipping those that cannot be handled. * @UCA_PCO_CAMERA_STORAGE_MODE_FIFO_BUFFER: Record frames in FIFO mode and * return every frame in live view. */ /** * UcaPcoCameraAcquireMode: * @UCA_PCO_CAMERA_ACQUIRE_MODE_AUTO: Take all images * @UCA_PCO_CAMERA_ACQUIRE_MODE_EXTERNAL: Use 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 * @UCA_PCO_CAMERA_TIMESTAMP_BOTH: Embed both types of timestamps */ /** * 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_HAS_DOUBLE_IMAGE_MODE, PROP_DOUBLE_IMAGE_MODE, PROP_OFFSET_MODE, PROP_RECORD_MODE, PROP_STORAGE_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_FRAMES_PER_SECOND, 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, PROP_RECORDED_FRAMES, PROP_IS_RECORDING, 0 }; static GParamSpec *pco_properties[N_PROPERTIES] = { NULL, }; /* * This structure defines type-specific properties of PCO cameras. */ typedef struct { int type; const char *so_file; int cl_type; int cl_format; gfloat max_frame_rate; gboolean has_camram; } pco_cl_map_entry; struct _UcaPcoCameraPrivate { GError *construct_error; pco_handle pco; pco_cl_map_entry *description; Fg_Struct *fg; guint fg_port; dma_mem *fg_mem; guint frame_width; guint frame_height; gsize buffer_size; 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; 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, "libDualAreaGray16.so", FG_CL_SINGLETAP_16_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->type != 0) { if (entry->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 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->description->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->buffer_size); return Fg_registerApcHandler(priv->fg, priv->fg_port, &ctrl, FG_APC_CONTROL_BASIC) == FG_OK; } static void check_pco_property_error (guint err, guint property_id) { if (err != PCO_NOERROR) { g_warning ("Call to libpco failed with error code %x for property `%s'", err, pco_properties[property_id]->name); } } static gboolean is_type (UcaPcoCameraPrivate *priv, int type) { return priv->description->type == type; } static void uca_pco_camera_start_recording (UcaCamera *camera, GError **error) { UcaPcoCameraPrivate *priv; guint16 binned_width; guint16 binned_height; gboolean use_extended; gboolean transfer_async; guint err; g_return_if_fail (UCA_IS_PCO_CAMERA (camera)); priv = UCA_PCO_CAMERA_GET_PRIVATE (camera); g_object_get (camera, "sensor-extended", &use_extended, "transfer-asynchronously", &transfer_async, 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 }; err = pco_set_roi (priv->pco, roi); CHECK_AND_RETURN_ON_PCO_ERROR (err); /* * 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->description->type == CAMERATYPE_PCO_EDGE ? 2 * priv->roi_width : priv->roi_width; priv->frame_width = priv->roi_width; priv->frame_height = priv->roi_height; priv->buffer_size = 2 * priv->frame_width * priv->frame_height; 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->buffer_size, 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 (is_type (priv, CAMERATYPE_PCO_DIMAX_STD) || is_type (priv, CAMERATYPE_PCO4000)) { err = pco_clear_active_segment (priv->pco); CHECK_AND_RETURN_ON_PCO_ERROR (err); } /* * Set the storage mode to FIFO buffer otherwise pco.4000 skips * frames that it is not able to send in time. */ if (is_type (priv, CAMERATYPE_PCO4000)) { err = pco_set_storage_mode (priv->pco, STORAGE_MODE_FIFO_BUFFER); CHECK_AND_RETURN_ON_PCO_ERROR (err); } priv->last_frame = 0; err = pco_arm_camera (priv->pco); CHECK_AND_RETURN_ON_PCO_ERROR (err); err = pco_start_recording (priv->pco); CHECK_AND_RETURN_ON_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) { UcaPcoCameraPrivate *priv; guint err; g_return_if_fail (UCA_IS_PCO_CAMERA (camera)); priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); err = pco_stop_recording (priv->pco); CHECK_AND_RETURN_ON_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); FG_SET_ERROR (err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_ACQUISITION); } static void uca_pco_camera_start_readout(UcaCamera *camera, GError **error) { UcaPcoCameraPrivate *priv; guint err; g_return_if_fail (UCA_IS_PCO_CAMERA (camera)); priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); /* * TODO: Check if readout mode is possible. This is not the case for the * edge. */ err = pco_get_num_images (priv->pco, priv->active_segment, &priv->num_recorded_images); CHECK_AND_RETURN_ON_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); priv->last_frame = 0; priv->current_image = 1; } static void uca_pco_camera_stop_readout(UcaCamera *camera, GError **error) { UcaPcoCameraPrivate *priv; guint err; g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); err = Fg_stopAcquireEx (priv->fg, priv->fg_port, priv->fg_mem, STOP_SYNC); FG_SET_ERROR (err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_GENERAL); err = Fg_setStatusEx (priv->fg, FG_UNBLOCK_ALL, 0, priv->fg_port, priv->fg_mem); FG_SET_ERROR (err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_GENERAL); } static void uca_pco_camera_trigger (UcaCamera *camera, GError **error) { UcaPcoCameraPrivate *priv; guint32 success; guint err; g_return_if_fail (UCA_IS_PCO_CAMERA (camera)); priv = UCA_PCO_CAMERA_GET_PRIVATE (camera); /* TODO: Check if we can trigger */ err = pco_force_trigger(priv->pco, &success); CHECK_AND_RETURN_ON_PCO_ERROR (err); if (!success) { g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, "Could not trigger frame acquisition"); } } static gboolean uca_pco_camera_grab(UcaCamera *camera, gpointer data, GError **error) { static const gint MAX_TIMEOUT = 5; UcaPcoCameraPrivate *priv; gboolean is_readout; guint16 *frame; guint err; g_return_val_if_fail (UCA_IS_PCO_CAMERA(camera), FALSE); priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); g_object_get (G_OBJECT (camera), "is-readout", &is_readout, NULL); if (is_readout) { if (priv->current_image == priv->num_recorded_images) return FALSE; /* * 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++; } err = pco_request_image(priv->pco); CHECK_AND_RETURN_VAL_ON_PCO_ERROR (err, FALSE); priv->last_frame = Fg_getLastPicNumberBlockingEx(priv->fg, priv->last_frame + 1, priv->fg_port, MAX_TIMEOUT, priv->fg_mem); if (priv->last_frame <= 0) { g_set_error (error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_FG_GENERAL, "%s", Fg_getLastErrorDescription (priv->fg)); return FALSE; } frame = Fg_getImagePtrEx (priv->fg, priv->last_frame, priv->fg_port, priv->fg_mem); if (priv->description->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->buffer_size); return TRUE; } 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); guint err = PCO_NOERROR; switch (property_id) { case PROP_SENSOR_EXTENDED: { guint16 format = g_value_get_boolean (value) ? SENSORFORMAT_EXTENDED : SENSORFORMAT_STANDARD; err = 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: { uint32_t exposure; uint32_t framerate; err = pco_get_framerate (priv->pco, &framerate, &exposure); exposure = (uint32_t) (g_value_get_double (value) * 1000 * 1000 * 1000); err = pco_set_framerate (priv->pco, framerate, exposure, false); } break; case PROP_FRAMES_PER_SECOND: { uint32_t exposure; uint32_t framerate; err = pco_get_framerate (priv->pco, &framerate, &exposure); framerate = (uint32_t) (g_value_get_double (value) * 1000); err = pco_set_framerate (priv->pco, framerate, exposure, true); } break; case PROP_SENSOR_ADCS: { const guint num_adcs = g_value_get_uint(value); err = pco_set_adc_mode(priv->pco, num_adcs); } 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) { err = pco_set_pixelrate(priv->pco, 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 err = pco_set_double_image_mode(priv->pco, g_value_get_boolean(value)); break; case PROP_OFFSET_MODE: err = 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); err = 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) err = pco_set_record_mode(priv->pco, RECORDER_SUBMODE_SEQUENCE); else if (mode == UCA_PCO_CAMERA_RECORD_MODE_RING_BUFFER) err = pco_set_record_mode(priv->pco, RECORDER_SUBMODE_RINGBUFFER); else g_warning("Unknown record mode"); } break; case PROP_STORAGE_MODE: { /* TODO: setting this is not possible for the edge */ UcaPcoCameraStorageMode mode = (UcaPcoCameraStorageMode) g_value_get_enum(value); if (mode == UCA_PCO_CAMERA_STORAGE_MODE_FIFO_BUFFER) err = pco_set_storage_mode (priv->pco, STORAGE_MODE_FIFO_BUFFER); else if (mode == UCA_PCO_CAMERA_STORAGE_MODE_RECORDER) err = pco_set_storage_mode (priv->pco, STORAGE_MODE_RECORDER); else g_warning("Unknown record mode"); } break; case PROP_ACQUIRE_MODE: { UcaPcoCameraAcquireMode mode = (UcaPcoCameraAcquireMode) g_value_get_enum(value); 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"); } break; case PROP_TRIGGER_MODE: { UcaCameraTrigger trigger_mode = (UcaCameraTrigger) g_value_get_enum(value); switch (trigger_mode) { case UCA_CAMERA_TRIGGER_AUTO: err = pco_set_trigger_mode(priv->pco, TRIGGER_MODE_AUTOTRIGGER); break; case UCA_CAMERA_TRIGGER_SOFTWARE: err = pco_set_trigger_mode(priv->pco, TRIGGER_MODE_SOFTWARETRIGGER); break; case UCA_CAMERA_TRIGGER_EXTERNAL: err = 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; err = pco_set_noise_filter_mode(priv->pco, filter_mode); } break; case PROP_TIMESTAMP_MODE: { guint16 table[] = { TIMESTAMP_MODE_OFF, TIMESTAMP_MODE_BINARY, TIMESTAMP_MODE_BINARYANDASCII, TIMESTAMP_MODE_ASCII, }; err = pco_set_timestamp_mode(priv->pco, table[g_value_get_enum(value)]); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } check_pco_property_error (err, property_id); } static void uca_pco_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(object); guint err = PCO_NOERROR; switch (property_id) { case PROP_SENSOR_EXTENDED: { guint16 format; err = 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->description->max_frame_rate); break; case PROP_SENSOR_BITDEPTH: g_value_set_uint(value, 16); break; case PROP_SENSOR_TEMPERATURE: { gint32 ccd, camera, power; err = 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; err = pco_get_adc_mode(priv->pco, &mode); 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; err = pco_get_pixelrate(priv->pco, &pixelrate); g_value_set_uint(value, pixelrate); } break; case PROP_EXPOSURE_TIME: { uint32_t exposure; uint32_t framerate; err = pco_get_framerate (priv->pco, &framerate, &exposure); g_value_set_double (value, exposure / 1000. / 1000. / 1000.); } break; case PROP_FRAMES_PER_SECOND: { uint32_t exposure; uint32_t framerate; err = pco_get_framerate (priv->pco, &framerate, &exposure); g_value_set_double (value, framerate / 1000.); } 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; err = pco_get_double_image_mode(priv->pco, &on); g_value_set_boolean(value, on); } break; case PROP_OFFSET_MODE: { bool on; err = 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->description->has_camram); break; case PROP_RECORDED_FRAMES: err = pco_get_num_images (priv->pco, priv->active_segment, &priv->num_recorded_images); g_value_set_uint(value, priv->num_recorded_images); break; case PROP_RECORD_MODE: { guint16 mode; err = 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 storage mode not handled"); } break; case PROP_STORAGE_MODE: { guint16 mode; err = pco_get_storage_mode (priv->pco, &mode); if (mode == STORAGE_MODE_RECORDER) g_value_set_enum (value, UCA_PCO_CAMERA_STORAGE_MODE_RECORDER); else if (mode == STORAGE_MODE_FIFO_BUFFER) g_value_set_enum (value, UCA_PCO_CAMERA_STORAGE_MODE_FIFO_BUFFER); else g_warning("pco record mode not handled"); } break; case PROP_ACQUIRE_MODE: { guint16 mode; err = 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; err = 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_SOFTWARE); 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; err = pco_get_name (priv->pco, &name); g_value_set_string (value, name); g_free(name); } break; case PROP_COOLING_POINT: { int16_t temperature; err = pco_get_cooling_temperature(priv->pco, &temperature); 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; err = 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; UcaPcoCameraTimestamp table[] = { UCA_PCO_CAMERA_TIMESTAMP_NONE, UCA_PCO_CAMERA_TIMESTAMP_BINARY, UCA_PCO_CAMERA_TIMESTAMP_BOTH, UCA_PCO_CAMERA_TIMESTAMP_ASCII }; err = pco_get_timestamp_mode (priv->pco, &mode); g_value_set_enum (value, table[mode]); } break; case PROP_IS_RECORDING: { bool is_recording; err = pco_is_recording (priv->pco, &is_recording); g_value_set_boolean (value, (gboolean) is_recording); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } check_pco_property_error (err, property_id); } 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_clear_error (&priv->construct_error); G_OBJECT_CLASS (uca_pco_camera_parent_class)->finalize (object); } static gboolean uca_pco_camera_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { UcaPcoCamera *camera; UcaPcoCameraPrivate *priv; g_return_val_if_fail (UCA_IS_PCO_CAMERA (initable), FALSE); if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialization not supported"); return FALSE; } camera = UCA_PCO_CAMERA (initable); priv = camera->priv; if (priv->construct_error != NULL) { if (error) *error = g_error_copy (priv->construct_error); return FALSE; } return TRUE; } static void uca_pco_camera_initable_iface_init (GInitableIface *iface) { iface->init = uca_pco_camera_initable_init; } 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->stop_readout = uca_pco_camera_stop_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_STORAGE_MODE] = g_param_spec_enum("storage-mode", "Storage mode", "Storage mode", UCA_TYPE_PCO_CAMERA_STORAGE_MODE, UCA_PCO_CAMERA_STORAGE_MODE_FIFO_BUFFER, 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_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_enum("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 gboolean setup_pco_camera (UcaPcoCameraPrivate *priv) { pco_cl_map_entry *map_entry; guint16 roi[4]; guint16 camera_type; guint16 camera_subtype; priv->pco = pco_init(); if (priv->pco == NULL) { g_set_error (&priv->construct_error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_INIT, "Initializing libpco failed"); return FALSE; } pco_get_camera_type (priv->pco, &camera_type, &camera_subtype); map_entry = get_pco_cl_map_entry (camera_type); if (map_entry == NULL) { g_set_error (&priv->construct_error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_UNSUPPORTED, "Camera type is not supported"); return FALSE; } priv->description = map_entry; pco_get_active_segment (priv->pco, &priv->active_segment); 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 (priv->pco, STORAGE_MODE_FIFO_BUFFER); pco_set_auto_transfer (priv->pco, 1); 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; priv->num_recorded_images = 0; return TRUE; } static gboolean setup_frame_grabber (UcaPcoCameraPrivate *priv) { guint32 fg_width; guint32 fg_height; int val; priv->fg_port = PORT_A; priv->fg = Fg_Init (priv->description->so_file, priv->fg_port); if (priv->fg == NULL) { g_set_error (&priv->construct_error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_FG_INIT, "%s", Fg_getLastErrorDescription(priv->fg)); return FALSE; } FG_TRY_PARAM (priv->fg, &priv->construct_error, FG_CAMERA_LINK_CAMTYP, &priv->description->cl_type, priv->fg_port); FG_TRY_PARAM (priv->fg, &priv->construct_error, FG_FORMAT, &priv->description->cl_format, priv->fg_port); fg_width = priv->description->type == CAMERATYPE_PCO_EDGE ? priv->width * 2 : priv->width; FG_TRY_PARAM (priv->fg, &priv->construct_error, FG_WIDTH, &fg_width, priv->fg_port); fg_height = priv->height; FG_TRY_PARAM (priv->fg, &priv->construct_error, FG_HEIGHT, &fg_height, priv->fg_port); val = FREE_RUN; FG_TRY_PARAM (priv->fg, &priv->construct_error, FG_TRIGGERMODE, &val, priv->fg_port); return TRUE; } static void override_property_ranges (UcaPcoCamera *camera) { UcaPcoCameraPrivate *priv; GObjectClass *oclass; priv = UCA_PCO_CAMERA_GET_PRIVATE (camera); oclass = G_OBJECT_CLASS (UCA_PCO_CAMERA_GET_CLASS (camera)); property_override_default_guint_value (oclass, "roi-width", priv->width); property_override_default_guint_value (oclass, "roi-height", priv->height); guint32 rates[4] = {0}; gint num_rates = 0; if (pco_get_available_pixelrates (priv->pco, rates, &num_rates) == PCO_NOERROR) { fill_pixelrates (priv, rates, num_rates); property_override_default_guint_value (oclass, "sensor-pixelrate", rates[0]); } override_temperature_range (priv); override_maximum_adcs (priv); } static void uca_pco_camera_init (UcaPcoCamera *self) { UcaCamera *camera; UcaPcoCameraPrivate *priv; self->priv = priv = UCA_PCO_CAMERA_GET_PRIVATE (self); priv->fg_mem = NULL; priv->description = NULL; priv->last_frame = 0; priv->grab_buffer = NULL; priv->construct_error = NULL; if (!setup_pco_camera (priv)) return; if (!setup_frame_grabber (priv)) return; fill_binnings (priv); override_property_ranges (self); camera = UCA_CAMERA (self); uca_camera_register_unit (camera, "sensor-width-extended", UCA_UNIT_PIXEL); uca_camera_register_unit (camera, "sensor-height-extended", UCA_UNIT_PIXEL); uca_camera_register_unit (camera, "sensor-temperature", UCA_UNIT_DEGREE_CELSIUS); uca_camera_register_unit (camera, "cooling-point", UCA_UNIT_DEGREE_CELSIUS); uca_camera_register_unit (camera, "cooling-point-min", UCA_UNIT_DEGREE_CELSIUS); uca_camera_register_unit (camera, "cooling-point-max", UCA_UNIT_DEGREE_CELSIUS); uca_camera_register_unit (camera, "cooling-point-default", UCA_UNIT_DEGREE_CELSIUS); uca_camera_register_unit (camera, "sensor-adcs", UCA_UNIT_COUNT); uca_camera_register_unit (camera, "sensor-max-adcs", UCA_UNIT_COUNT); } G_MODULE_EXPORT GType uca_camera_get_type (void) { return UCA_TYPE_PCO_CAMERA; }