diff options
-rw-r--r-- | CMakeLists.txt | 49 | ||||
-rw-r--r-- | NEWS | 29 | ||||
-rw-r--r-- | cmake/FindClSerMe4.cmake | 2 | ||||
-rw-r--r-- | cmake/FindFgLib5.cmake | 2 | ||||
-rw-r--r-- | docs/Makefile | 12 | ||||
-rw-r--r-- | docs/base.html | 67 | ||||
-rwxr-xr-x | docs/gen-changelog | 20 | ||||
-rw-r--r-- | docs/manual.md | 132 | ||||
-rw-r--r-- | docs/mock.html | 70 | ||||
-rw-r--r-- | docs/pco.html | 130 | ||||
-rw-r--r-- | docs/style.css | 99 | ||||
-rw-r--r-- | package.sh.in | 7 | ||||
-rw-r--r-- | plugins/CMakeLists.txt | 5 | ||||
-rw-r--r-- | plugins/mock/CMakeLists.txt | 18 | ||||
-rw-r--r-- | plugins/mock/uca-mock-camera.c (renamed from src/cameras/uca-mock-camera.c) | 42 | ||||
-rw-r--r-- | plugins/mock/uca-mock-camera.h (renamed from src/cameras/uca-mock-camera.h) | 2 | ||||
-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 (renamed from src/cameras/uca-pco-camera.c) | 592 | ||||
-rw-r--r-- | plugins/pco/uca-pco-camera.h (renamed from src/cameras/uca-pco-camera.h) | 9 | ||||
-rw-r--r-- | plugins/pf/CMakeLists.txt | 30 | ||||
-rw-r--r-- | plugins/pf/uca-pf-camera.c (renamed from src/cameras/uca-pf-camera.c) | 103 | ||||
-rw-r--r-- | plugins/pf/uca-pf-camera.h (renamed from src/cameras/uca-pf-camera.h) | 2 | ||||
-rw-r--r-- | plugins/pylon/CMakeLists.txt | 23 | ||||
-rw-r--r-- | plugins/pylon/uca-pylon-camera.c (renamed from src/cameras/uca-pylon-camera.c) | 0 | ||||
-rw-r--r-- | plugins/pylon/uca-pylon-camera.h (renamed from src/cameras/uca-pylon-camera.h) | 0 | ||||
-rw-r--r-- | plugins/ufo/CMakeLists.txt | 24 | ||||
-rw-r--r-- | plugins/ufo/uca-ufo-camera.c (renamed from src/cameras/uca-ufo-camera.c) | 27 | ||||
-rw-r--r-- | plugins/ufo/uca-ufo-camera.h (renamed from src/cameras/uca-ufo-camera.h) | 2 | ||||
-rw-r--r-- | src/CMakeLists.txt | 276 | ||||
-rw-r--r-- | src/uca-camera.c | 560 | ||||
-rw-r--r-- | src/uca-camera.h | 62 | ||||
-rw-r--r-- | src/uca-docs.xml.in | 4 | ||||
-rw-r--r-- | src/uca-plugin-manager.c | 345 | ||||
-rw-r--r-- | src/uca-plugin-manager.h | 65 | ||||
-rw-r--r-- | src/uca.types.in | 2 | ||||
-rw-r--r-- | test/CMakeLists.txt | 86 | ||||
-rw-r--r-- | test/control.c | 336 | ||||
-rw-r--r-- | test/control.glade | 302 | ||||
-rw-r--r-- | test/enum.c | 88 | ||||
-rw-r--r-- | test/grab_pylon.c | 96 | ||||
-rw-r--r-- | test/gtester.xsl | 12 | ||||
-rw-r--r-- | test/perf-overhead.c | 143 | ||||
-rwxr-xr-x | test/run.py | 31 | ||||
-rw-r--r-- | test/test-all.c | 214 | ||||
-rw-r--r-- | test/test-mock.c | 214 | ||||
-rw-r--r-- | tools/CMakeLists.txt | 36 | ||||
-rw-r--r-- | tools/benchmark.c (renamed from test/benchmark.c) | 32 | ||||
-rw-r--r-- | tools/gen-doc.c | 155 | ||||
-rw-r--r-- | tools/grab-async.c (renamed from test/grab-async.c) | 44 | ||||
-rw-r--r-- | tools/grab.c (renamed from test/grab.c) | 42 | ||||
-rw-r--r-- | tools/gui/CMakeLists.txt | 43 | ||||
-rw-r--r-- | tools/gui/control.c | 622 | ||||
-rw-r--r-- | tools/gui/control.glade | 635 | ||||
-rw-r--r-- | tools/gui/egg-histogram-view.c | 524 | ||||
-rw-r--r-- | tools/gui/egg-histogram-view.h | 66 | ||||
-rw-r--r-- | tools/gui/egg-property-cell-renderer.c (renamed from test/egg-property-cell-renderer.c) | 0 | ||||
-rw-r--r-- | tools/gui/egg-property-cell-renderer.h (renamed from test/egg-property-cell-renderer.h) | 0 | ||||
-rw-r--r-- | tools/gui/egg-property-tree-view.c (renamed from test/egg-property-tree-view.c) | 1 | ||||
-rw-r--r-- | tools/gui/egg-property-tree-view.h (renamed from test/egg-property-tree-view.h) | 0 | ||||
-rw-r--r-- | tools/gui/ring-buffer.c | 64 | ||||
-rw-r--r-- | tools/gui/ring-buffer.h | 28 | ||||
-rw-r--r-- | tools/perf-overhead.c | 181 |
63 files changed, 4616 insertions, 2222 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index dffd35d..bef3712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ project(uca C) set(TARNAME "libuca") set(UCA_VERSION_MAJOR "1") -set(UCA_VERSION_MINOR "0") -set(UCA_VERSION_PATCH "2dev") +set(UCA_VERSION_MINOR "1") +set(UCA_VERSION_PATCH "0dev") set(UCA_DESCRIPTION "Unified Camera Access") set(UCA_VERSION_STRING "${UCA_VERSION_MAJOR}.${UCA_VERSION_MINOR}.${UCA_VERSION_PATCH}") @@ -16,7 +16,7 @@ set(PACKAGE_TARNAME "${TARNAME}") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "http://ufo.kit.edu/ufo/newticket") -set(CPACK_GENERATOR "DEB;RPM;") +set(CPACK_GENERATOR "RPM") set(CPACK_PACKAGE_RELEASE 3) set(CPACK_DEBIAN_PACKAGE_NAME "libuca") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Matthias Vogelgesang <matthias.vogelgesang@kit.edu>") @@ -25,7 +25,48 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY ${UCA_DESCRIPTION}) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") -SET(UCA_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(UCA_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}") + +set(UCA_ENUM_HDRS + ${CMAKE_CURRENT_SOURCE_DIR}/src/uca-camera.h + ${CMAKE_CURRENT_SOURCE_DIR}/plugins/pco/uca-pco-camera.h) + + +# --- Common configuration --------------------------------------------------- + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/package.sh.in + ${CMAKE_CURRENT_BINARY_DIR}/package.sh) + + +# --- Common flags ------------------------------------------------------------ + +add_definitions("-std=c99 -Wall") + + +# --- Common libraries -------------------------------------------------------- + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +find_package(PkgConfig) +find_program(GLIB2_MKENUMS glib-mkenums REQUIRED) +pkg_check_modules(GLIB2 glib-2.0>=2.24 REQUIRED) +pkg_check_modules(GOBJECT2 gobject-2.0>=2.24 REQUIRED) +pkg_check_modules(GMODULE2 gmodule-2.0>=2.24 REQUIRED) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${GLIB2_INCLUDE_DIRS} + ${GOBJECT2_INCLUDE_DIRS}) + +set(UCA_DEPS + ${GLIB2_LIBRARIES} + ${GOBJECT2_LIBRARIES} + ${GMODULE2_LIBRARIES}) add_subdirectory(src) +add_subdirectory(plugins) add_subdirectory(test) +add_subdirectory(tools) @@ -1,3 +1,31 @@ +Changes in libuca 1.1 +===================== + +Plugin System +------------- + +A new plugin manager is used to instantiate camera objects from a shared +library. Instead of calling `uca_camera_new`, a plugin manager is created that +looks in pre- and user-defined directories for DSOs that match +`libuca[A-Za-z].so` and used to instantiate: + + UcaPluginManager *manager; + UcaCamera *camera; + + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, "foo", &error); + +The plugin manager adds a dependency on GModule (pkg-config package +`gmodule-2.0`) that is part of GLib. + +Minor changes +------------- + +- It is now possible to generate GObject introspection meta data to bind libuca + to all languages that support GObject introspection. +- Added uca_camera_stop_readout() to cleanup after using + uca_camera_start_readout(). + Changes in libuca 1.0 aka 0.6 ============================= @@ -14,7 +42,6 @@ instead of trying to initialize each camera first and having the user decide what to use, the user must now determine the used camera at compile time or use the factory pattern to delegate this to run-time. - Tango Wrapper ------------- diff --git a/cmake/FindClSerMe4.cmake b/cmake/FindClSerMe4.cmake index 2251c9a..803b620 100644 --- a/cmake/FindClSerMe4.cmake +++ b/cmake/FindClSerMe4.cmake @@ -13,6 +13,7 @@ FIND_PATH(CLSERME4_INCLUDE_DIR clser.h ${SISODIR4}/include ${SISODIRME4}/include ${CMAKE_INSTALL_PREFIX}/include + "/opt/siso/include" ) INCLUDE(SisoLibDir) @@ -26,6 +27,7 @@ FIND_LIBRARY(CLSERME4_LIBRARY NAMES clserme4 clsersisome4 $ENV{CLSERME4} ${LIB_DIRS} ${CMAKE_INSTALL_PREFIX}/lib + "/opt/siso/lib" ) IF(CLSERME4_INCLUDE_DIR AND CLSERME4_LIBRARY) diff --git a/cmake/FindFgLib5.cmake b/cmake/FindFgLib5.cmake index cba0002..3f6a0c1 100644 --- a/cmake/FindFgLib5.cmake +++ b/cmake/FindFgLib5.cmake @@ -10,6 +10,7 @@ FIND_PATH(FGLIB5_INCLUDE_DIR fgrab_define.h "${CMAKE_INSTALL_PREFIX}/include" "${SISODIR5}/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Silicon Software GmbH\\Runtime5;Info]/include" + "/opt/siso/include" ) INCLUDE(SisoLibDir) @@ -23,6 +24,7 @@ FIND_LIBRARY(FGLIB5_LIBRARY NAMES fglib5 ${LIB_DIRS} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Silicon Software GmbH\\Runtime5;Info]/lib/${COMPILER_LIB_DIR}" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Silicon Software GmbH\\Runtime5;Info]/lib" + "/opt/siso/lib" ) IF(FGLIB5_INCLUDE_DIR AND FGLIB5_LIBRARY) diff --git a/docs/Makefile b/docs/Makefile index 53dc6fd..ff7113d 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -10,11 +10,15 @@ html: manual.html clean: rm -f manual.pdf manual.html -manual.pdf: manual.md - $(PANDOC) $(OPTS) manual.md -o manual.pdf +manual.pdf: manual.md gen-changelog + ./gen-changelog + $(PANDOC) $(OPTS) manual.md z-changes-*.md -o manual.pdf + rm z-changes-*.md -manual.html: manual.md style.css - $(PANDOC) $(OPTS) manual.md -H webfonts.html -c style.css -o manual.html +manual.html: manual.md style.css gen-changelog + ./gen-changelog + $(PANDOC) $(OPTS) manual.md z-changes-*.md -H webfonts.html -c style.css -o manual.html + rm z-changes-*.md ifeq ($(PANDOC),) $(warning Pandoc not found!) diff --git a/docs/base.html b/docs/base.html new file mode 100644 index 0000000..e86807f --- /dev/null +++ b/docs/base.html @@ -0,0 +1,67 @@ +<html><head><link rel="stylesheet" href="style.css" type="text/css" /><link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700|Droid+Serif:400,400italic|Inconsolata' rel='stylesheet' type='text/css'><title>Basic camera — properties</title></head><body><div id="header"><h1 class="title">Property documentation of Basic camera</h1><h2>Properties</h2><ul id="toc"><li><code><a href=#name>"name"</a></code></li><li><code><a href=#sensor-width>"sensor-width"</a></code></li><li><code><a href=#sensor-height>"sensor-height"</a></code></li><li><code><a href=#sensor-bitdepth>"sensor-bitdepth"</a></code></li><li><code><a href=#sensor-horizontal-binning>"sensor-horizontal-binning"</a></code></li><li><code><a href=#sensor-horizontal-binnings>"sensor-horizontal-binnings"</a></code></li><li><code><a href=#sensor-vertical-binning>"sensor-vertical-binning"</a></code></li><li><code><a href=#sensor-vertical-binnings>"sensor-vertical-binnings"</a></code></li><li><code><a href=#sensor-max-frame-rate>"sensor-max-frame-rate"</a></code></li><li><code><a href=#trigger-mode>"trigger-mode"</a></code></li><li><code><a href=#exposure-time>"exposure-time"</a></code></li><li><code><a href=#roi-x0>"roi-x0"</a></code></li><li><code><a href=#roi-y0>"roi-y0"</a></code></li><li><code><a href=#roi-width>"roi-width"</a></code></li><li><code><a href=#roi-height>"roi-height"</a></code></li><li><code><a href=#roi-width-multiplier>"roi-width-multiplier"</a></code></li><li><code><a href=#roi-height-multiplier>"roi-height-multiplier"</a></code></li><li><code><a href=#has-streaming>"has-streaming"</a></code></li><li><code><a href=#has-camram-recording>"has-camram-recording"</a></code></li><li><code><a href=#transfer-asynchronously>"transfer-asynchronously"</a></code></li><li><code><a href=#is-recording>"is-recording"</a></code></li><li><code><a href=#is-readout>"is-readout"</a></code></li></ul><h2>Details</h2><dl><dt id="name"><a href="#toc">name</a></dt> +<dd><pre><code class="prop-type">"name" : gchararray : Read-only</code></pre> +<p>Name of the camera</p> +</dd><dt id="sensor-width"><a href="#toc">sensor-width</a></dt> +<dd><pre><code class="prop-type">"sensor-width" : guint : Read-only</code></pre> +<p>Width of the sensor in pixels</p> +</dd><dt id="sensor-height"><a href="#toc">sensor-height</a></dt> +<dd><pre><code class="prop-type">"sensor-height" : guint : Read-only</code></pre> +<p>Height of the sensor in pixels</p> +</dd><dt id="sensor-bitdepth"><a href="#toc">sensor-bitdepth</a></dt> +<dd><pre><code class="prop-type">"sensor-bitdepth" : guint : Read-only</code></pre> +<p>Number of bits per pixel</p> +</dd><dt id="sensor-horizontal-binning"><a href="#toc">sensor-horizontal-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in horizontal direction</p> +</dd><dt id="sensor-horizontal-binnings"><a href="#toc">sensor-horizontal-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in horizontal direction</p> +</dd><dt id="sensor-vertical-binning"><a href="#toc">sensor-vertical-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in vertical direction</p> +</dd><dt id="sensor-vertical-binnings"><a href="#toc">sensor-vertical-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in vertical direction</p> +</dd><dt id="sensor-max-frame-rate"><a href="#toc">sensor-max-frame-rate</a></dt> +<dd><pre><code class="prop-type">"sensor-max-frame-rate" : gfloat : Read-only</code></pre> +<p>Maximum frame rate at full frame resolution</p> +</dd><dt id="trigger-mode"><a href="#toc">trigger-mode</a></dt> +<dd><pre><code class="prop-type">"trigger-mode" : UcaCameraTrigger : Read / Write</code></pre> +<p>Trigger mode</p> +</dd><dt id="exposure-time"><a href="#toc">exposure-time</a></dt> +<dd><pre><code class="prop-type">"exposure-time" : gdouble : Read / Write</code></pre> +<p>Exposure time in seconds</p> +</dd><dt id="roi-x0"><a href="#toc">roi-x0</a></dt> +<dd><pre><code class="prop-type">"roi-x0" : guint : Read / Write</code></pre> +<p>Horizontal coordinate</p> +</dd><dt id="roi-y0"><a href="#toc">roi-y0</a></dt> +<dd><pre><code class="prop-type">"roi-y0" : guint : Read / Write</code></pre> +<p>Vertical coordinate</p> +</dd><dt id="roi-width"><a href="#toc">roi-width</a></dt> +<dd><pre><code class="prop-type">"roi-width" : guint : Read / Write</code></pre> +<p>Width of the region of interest</p> +</dd><dt id="roi-height"><a href="#toc">roi-height</a></dt> +<dd><pre><code class="prop-type">"roi-height" : guint : Read / Write</code></pre> +<p>Height of the region of interest</p> +</dd><dt id="roi-width-multiplier"><a href="#toc">roi-width-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-width-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of horizontal ROI</p> +</dd><dt id="roi-height-multiplier"><a href="#toc">roi-height-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-height-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of vertical ROI</p> +</dd><dt id="has-streaming"><a href="#toc">has-streaming</a></dt> +<dd><pre><code class="prop-type">"has-streaming" : gboolean : Read-only</code></pre> +<p>Is the camera able to stream the data</p> +</dd><dt id="has-camram-recording"><a href="#toc">has-camram-recording</a></dt> +<dd><pre><code class="prop-type">"has-camram-recording" : gboolean : Read-only</code></pre> +<p>Is the camera able to record the data in-camera</p> +</dd><dt id="transfer-asynchronously"><a href="#toc">transfer-asynchronously</a></dt> +<dd><pre><code class="prop-type">"transfer-asynchronously" : gboolean : Read / Write</code></pre> +<p>Specify whether data should be transfered asynchronously using a specified callback</p> +</dd><dt id="is-recording"><a href="#toc">is-recording</a></dt> +<dd><pre><code class="prop-type">"is-recording" : gboolean : Read-only</code></pre> +<p>Is the camera currently recording</p> +</dd><dt id="is-readout"><a href="#toc">is-readout</a></dt> +<dd><pre><code class="prop-type">"is-readout" : gboolean : Read-only</code></pre> +<p>Is camera in readout mode</p> +</dd></dl></body></html> diff --git a/docs/gen-changelog b/docs/gen-changelog new file mode 100755 index 0000000..51650f1 --- /dev/null +++ b/docs/gen-changelog @@ -0,0 +1,20 @@ +#!/bin/bash + +FROM=$1 +TO=$2 +OUTPUT=$3 +series=('v1.0' 'v1.0.1') + +logname="z-changes-${series[0]}.md" + +echo "## Changes for stable branch ${series[0]} +" >> $logname + +for ((i = 1; i < ${#series[@]}; i++)) do + curr=${series[i]} + prev=${series[i-1]} + + echo "### Changes from $prev to $curr +" >> $logname + git log --pretty=format:'* [%h](http://ufo.kit.edu/repos/libuca.git/commit/?id=%h): %s' $prev..$curr >> $logname +done diff --git a/docs/manual.md b/docs/manual.md index 69abae8..a68400e 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -12,7 +12,18 @@ Before installing `libuca` itself, you should install any drivers and SDKs needed to access the cameras you want to access through `libuca`. Now you have two options: install pre-built packages or build from source. -## Building from source +### Installing packages + +Packages for the core library and all plugins are currently provided for +openSUSE. To install them run `zypper`: + + sudo zypper in libuca-x.y.z-x86_64.rpm + sudo zypper in uca-plugin-*.rpm + +To install development files such as headers, you have to install the +`libuca-x.y.z-devel.rpm` package. + +### Building from source Building the library and installing from source is simple and straightforward. Make sure you have @@ -38,7 +49,8 @@ repository][repo], you also need Git: [repo]: http://ufo.kit.edu/repos/libuca.git/ -### Fetching the sources + +#### Fetching the sources Untar the distribution @@ -54,7 +66,7 @@ and create a new, empty build directory inside: mkdir build -### Configuring and building +#### Configuring and building Now you need to create the Makefile with CMake. Go into the build directory and point CMake to the `libuca` top-level directory: @@ -86,7 +98,7 @@ latter that 64 should be appended to any library paths. This is necessary on Linux distributions that expect 64-bit libraries in `/usr[/local]/lib64`. -### Building this manual +#### Building this manual Make sure you have [Pandoc][] installed. With Debian/Ubuntu this can be achieved with @@ -107,6 +119,7 @@ necessary header files: ~~~ {.c} #include <glib-object.h> +#include <uca-plugin-manager.h> #include <uca-camera.h> ~~~ @@ -116,6 +129,7 @@ Then you need to setup the type system: int main (int argc, char *argv[]) { + UcaPluginManager *manager; UcaCamera *camera; GError *error = NULL; /* this _must_ be set to NULL */ @@ -124,10 +138,12 @@ main (int argc, char *argv[]) Now you can instantiate new camera _objects_. Each camera is identified by a human-readable string, in this case we want to access any pco camera that is -supported by [libpco][]: +supported by [libpco][]. To instantiate a camera we have to create a plugin +manager first: ~~~ {.c} - camera = uca_camera_new ("pco", &error); + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, "pco", &error); ~~~ Errors are indicated with a returned value `NULL` and `error` set to a value @@ -241,6 +257,16 @@ The following cameras are supported: * Pylon * UFO Camera developed at KIT/IPE. +## Property documentation + +* [Basic camera properties][base-doc] +* [pco][pco-doc] +* [mock][mock-doc] + +[base-doc]: base.html +[pco-doc]: pco.html +[mock-doc]: mock.html + # More API @@ -252,38 +278,19 @@ communicate with the camera. Now we will go into more detail. We have already seen how to instantiate a camera object from a name. If you have more than one camera connected to a machine, you will most likely want the user decide which to use. To do so, you can enumerate all camera strings with -`uca_camera_get_types`: +`uca_plugin_manager_get_available_cameras`: ~~~ {.c} - gchar **types; + GList *types; - types = uca_camera_get_types (); + types = uca_camera_get_available_cameras (manager); - for (guint i = 0; types[i] != NULL; i++) - g_print ("%s\n", types[i]); + for (GList *it = g_list_first; it != NULL; it = g_list_next (it)) + g_print ("%s\n", (gchar *) it->data); - /* free the string array */ - g_strfreev (types); -~~~ - -If you _know_ which camera you want to use you can instantiate the sub-classed -camera object directly. In this case we create a pco-based camera: - -~~~ {.c} -#include <glib-object.h> -#include <uca/uca-camera-pco.h> - -int -main (int argc, char *argv[]) -{ - UcaPcoCamera *camera; - GError *error = NULL; - - g_type_init (); - camera = uca_pco_camera_new (&error); - g_object_unref (camera); - return 0; -} + /* free the strings and the list */ + g_list_foreach (types, (GFunc) g_free, NULL); + g_list_free (types); ~~~ [last section]: #first-look-at-the-api @@ -374,6 +381,37 @@ setup_async (UcaCamera *camera) } ~~~ + +# Bindings + +Since version 1.1, libuca generates GObject introspection meta data if +`g-ir-scanner` and `g-ir-compiler` can be found. When the XML description +`Uca-x.y.gir` and the typelib `Uca-x.y.typelib` are installed, GI-aware +languages can access libuca and create and modify cameras, for example in +Python: + +~~~ {.python} +from gi.repository import Uca + +pm = Uca.PluginManager() + +# List all cameras +print(pm.get_available_cameras()) + +# Load a camera +cam = pm.get_camera('pco') + +# You can read and write properties in two ways +cam.set_properties(exposure_time=0.05) +cam.props.roi_width = 1024 +~~~ + +Note, that the naming of classes and properties depends on the GI implementation +of the target language. For example with Python, the namespace prefix `uca_` +becomes the module name `Uca` and dashes separating property names become +underscores. + + # Integrating new cameras A new camera is integrated by [sub-classing][] `UcaCamera` and implement all @@ -381,6 +419,31 @@ virtual methods. The simplest way is to take the `mock` camera and rename all occurences. Note, that if you class is going to be called `FooBar`, the upper case variant is `FOO_BAR` and the lower case variant is `foo_bar`. +In order to fully implement a camera, you need to override at least the +following virtual methods: + +* `start_recording`: Take suitable actions so that a subsequent call to + `grab` delivers an image or blocks until one is exposed. +* `stop_recording`: Stop recording so that subsequent calls to `grab` + fail. +* `grab`: Return an image from the camera or block until one is ready. + +## Asynchronous operation + +When the camera supports asynchronous acquisition and announces it with a true +boolean value for `"transfer-asynchronously"`, a mechanism must be setup up +during `start_recording` so that for each new frame the grab func callback is +called. + +## Cameras with internal memory + +Cameras such as the pco.dimax record into their own on-board memory rather than +streaming directly to the host PC. In this case, both `start_recording` and +`stop_recording` initiate and end acquisition to the on-board memory. To +initiate a data transfer, the host calls `start_readout` which must be suitably +implemented. The actual data transfer happens either with `grab` or +asynchronously. + [sub-classing]: http://developer.gnome.org/gobject/stable/howto-gobject.html @@ -426,3 +489,6 @@ grabbing time: # The GObject Tango device [TODO: Get more information from Volker Kaiser and/or Mihael Koep] + + +# ChangeLog diff --git a/docs/mock.html b/docs/mock.html new file mode 100644 index 0000000..0d38fc8 --- /dev/null +++ b/docs/mock.html @@ -0,0 +1,70 @@ +<html><head><link rel="stylesheet" href="style.css" type="text/css" /><link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700|Droid+Serif:400,400italic|Inconsolata' rel='stylesheet' type='text/css'><title>mock — properties</title></head><body><div id="header"><h1 class="title">Property documentation of mock</h1><h2>Properties</h2><ul id="toc"><li><code><a href=#name>"name"</a></code></li><li><code><a href=#sensor-width>"sensor-width"</a></code></li><li><code><a href=#sensor-height>"sensor-height"</a></code></li><li><code><a href=#sensor-bitdepth>"sensor-bitdepth"</a></code></li><li><code><a href=#sensor-horizontal-binning>"sensor-horizontal-binning"</a></code></li><li><code><a href=#sensor-horizontal-binnings>"sensor-horizontal-binnings"</a></code></li><li><code><a href=#sensor-vertical-binning>"sensor-vertical-binning"</a></code></li><li><code><a href=#sensor-vertical-binnings>"sensor-vertical-binnings"</a></code></li><li><code><a href=#sensor-max-frame-rate>"sensor-max-frame-rate"</a></code></li><li><code><a href=#trigger-mode>"trigger-mode"</a></code></li><li><code><a href=#exposure-time>"exposure-time"</a></code></li><li><code><a href=#roi-x0>"roi-x0"</a></code></li><li><code><a href=#roi-y0>"roi-y0"</a></code></li><li><code><a href=#roi-width>"roi-width"</a></code></li><li><code><a href=#roi-height>"roi-height"</a></code></li><li><code><a href=#roi-width-multiplier>"roi-width-multiplier"</a></code></li><li><code><a href=#roi-height-multiplier>"roi-height-multiplier"</a></code></li><li><code><a href=#has-streaming>"has-streaming"</a></code></li><li><code><a href=#has-camram-recording>"has-camram-recording"</a></code></li><li><code><a href=#transfer-asynchronously>"transfer-asynchronously"</a></code></li><li><code><a href=#is-recording>"is-recording"</a></code></li><li><code><a href=#is-readout>"is-readout"</a></code></li><li><code><a href=#frame-rate>"frame-rate"</a></code></li></ul><h2>Details</h2><dl><dt id="name"><a href="#toc">name</a></dt> +<dd><pre><code class="prop-type">"name" : gchararray : Read-only</code></pre> +<p>Name of the camera</p> +</dd><dt id="sensor-width"><a href="#toc">sensor-width</a></dt> +<dd><pre><code class="prop-type">"sensor-width" : guint : Read-only</code></pre> +<p>Width of the sensor in pixels</p> +</dd><dt id="sensor-height"><a href="#toc">sensor-height</a></dt> +<dd><pre><code class="prop-type">"sensor-height" : guint : Read-only</code></pre> +<p>Height of the sensor in pixels</p> +</dd><dt id="sensor-bitdepth"><a href="#toc">sensor-bitdepth</a></dt> +<dd><pre><code class="prop-type">"sensor-bitdepth" : guint : Read-only</code></pre> +<p>Number of bits per pixel</p> +</dd><dt id="sensor-horizontal-binning"><a href="#toc">sensor-horizontal-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in horizontal direction</p> +</dd><dt id="sensor-horizontal-binnings"><a href="#toc">sensor-horizontal-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in horizontal direction</p> +</dd><dt id="sensor-vertical-binning"><a href="#toc">sensor-vertical-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in vertical direction</p> +</dd><dt id="sensor-vertical-binnings"><a href="#toc">sensor-vertical-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in vertical direction</p> +</dd><dt id="sensor-max-frame-rate"><a href="#toc">sensor-max-frame-rate</a></dt> +<dd><pre><code class="prop-type">"sensor-max-frame-rate" : gfloat : Read-only</code></pre> +<p>Maximum frame rate at full frame resolution</p> +</dd><dt id="trigger-mode"><a href="#toc">trigger-mode</a></dt> +<dd><pre><code class="prop-type">"trigger-mode" : UcaCameraTrigger : Read / Write</code></pre> +<p>Trigger mode</p> +</dd><dt id="exposure-time"><a href="#toc">exposure-time</a></dt> +<dd><pre><code class="prop-type">"exposure-time" : gdouble : Read / Write</code></pre> +<p>Exposure time in seconds</p> +</dd><dt id="roi-x0"><a href="#toc">roi-x0</a></dt> +<dd><pre><code class="prop-type">"roi-x0" : guint : Read / Write</code></pre> +<p>Horizontal coordinate</p> +</dd><dt id="roi-y0"><a href="#toc">roi-y0</a></dt> +<dd><pre><code class="prop-type">"roi-y0" : guint : Read / Write</code></pre> +<p>Vertical coordinate</p> +</dd><dt id="roi-width"><a href="#toc">roi-width</a></dt> +<dd><pre><code class="prop-type">"roi-width" : guint : Read / Write</code></pre> +<p>Width of the region of interest</p> +</dd><dt id="roi-height"><a href="#toc">roi-height</a></dt> +<dd><pre><code class="prop-type">"roi-height" : guint : Read / Write</code></pre> +<p>Height of the region of interest</p> +</dd><dt id="roi-width-multiplier"><a href="#toc">roi-width-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-width-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of horizontal ROI</p> +</dd><dt id="roi-height-multiplier"><a href="#toc">roi-height-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-height-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of vertical ROI</p> +</dd><dt id="has-streaming"><a href="#toc">has-streaming</a></dt> +<dd><pre><code class="prop-type">"has-streaming" : gboolean : Read-only</code></pre> +<p>Is the camera able to stream the data</p> +</dd><dt id="has-camram-recording"><a href="#toc">has-camram-recording</a></dt> +<dd><pre><code class="prop-type">"has-camram-recording" : gboolean : Read-only</code></pre> +<p>Is the camera able to record the data in-camera</p> +</dd><dt id="transfer-asynchronously"><a href="#toc">transfer-asynchronously</a></dt> +<dd><pre><code class="prop-type">"transfer-asynchronously" : gboolean : Read / Write</code></pre> +<p>Specify whether data should be transfered asynchronously using a specified callback</p> +</dd><dt id="is-recording"><a href="#toc">is-recording</a></dt> +<dd><pre><code class="prop-type">"is-recording" : gboolean : Read-only</code></pre> +<p>Is the camera currently recording</p> +</dd><dt id="is-readout"><a href="#toc">is-readout</a></dt> +<dd><pre><code class="prop-type">"is-readout" : gboolean : Read-only</code></pre> +<p>Is camera in readout mode</p> +</dd><dt id="frame-rate"><a href="#toc">frame-rate</a></dt> +<dd><pre><code class="prop-type">"frame-rate" : gfloat : Read / Write</code></pre> +<p>Number of frames per second that are taken</p> +</dd></dl></body></html> diff --git a/docs/pco.html b/docs/pco.html new file mode 100644 index 0000000..3f764e1 --- /dev/null +++ b/docs/pco.html @@ -0,0 +1,130 @@ +<html><head><link rel="stylesheet" href="style.css" type="text/css" /><link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700|Droid+Serif:400,400italic|Inconsolata' rel='stylesheet' type='text/css'><title>pco — properties</title></head><body><div id="header"><h1 class="title">Property documentation of pco</h1><h2>Properties</h2><ul id="toc"><li><code><a href=#name>"name"</a></code></li><li><code><a href=#sensor-width>"sensor-width"</a></code></li><li><code><a href=#sensor-height>"sensor-height"</a></code></li><li><code><a href=#sensor-bitdepth>"sensor-bitdepth"</a></code></li><li><code><a href=#sensor-horizontal-binning>"sensor-horizontal-binning"</a></code></li><li><code><a href=#sensor-horizontal-binnings>"sensor-horizontal-binnings"</a></code></li><li><code><a href=#sensor-vertical-binning>"sensor-vertical-binning"</a></code></li><li><code><a href=#sensor-vertical-binnings>"sensor-vertical-binnings"</a></code></li><li><code><a href=#sensor-max-frame-rate>"sensor-max-frame-rate"</a></code></li><li><code><a href=#trigger-mode>"trigger-mode"</a></code></li><li><code><a href=#exposure-time>"exposure-time"</a></code></li><li><code><a href=#frames-per-second>"frames-per-second"</a></code></li><li><code><a href=#roi-x0>"roi-x0"</a></code></li><li><code><a href=#roi-y0>"roi-y0"</a></code></li><li><code><a href=#roi-width>"roi-width"</a></code></li><li><code><a href=#roi-height>"roi-height"</a></code></li><li><code><a href=#roi-width-multiplier>"roi-width-multiplier"</a></code></li><li><code><a href=#roi-height-multiplier>"roi-height-multiplier"</a></code></li><li><code><a href=#has-streaming>"has-streaming"</a></code></li><li><code><a href=#has-camram-recording>"has-camram-recording"</a></code></li><li><code><a href=#transfer-asynchronously>"transfer-asynchronously"</a></code></li><li><code><a href=#is-recording>"is-recording"</a></code></li><li><code><a href=#is-readout>"is-readout"</a></code></li><li><code><a href=#sensor-extended>"sensor-extended"</a></code></li><li><code><a href=#sensor-width-extended>"sensor-width-extended"</a></code></li><li><code><a href=#sensor-height-extended>"sensor-height-extended"</a></code></li><li><code><a href=#sensor-temperature>"sensor-temperature"</a></code></li><li><code><a href=#sensor-pixelrates>"sensor-pixelrates"</a></code></li><li><code><a href=#sensor-pixelrate>"sensor-pixelrate"</a></code></li><li><code><a href=#sensor-adcs>"sensor-adcs"</a></code></li><li><code><a href=#sensor-max-adcs>"sensor-max-adcs"</a></code></li><li><code><a href=#delay-time>"delay-time"</a></code></li><li><code><a href=#has-double-image-mode>"has-double-image-mode"</a></code></li><li><code><a href=#double-image-mode>"double-image-mode"</a></code></li><li><code><a href=#offset-mode>"offset-mode"</a></code></li><li><code><a href=#record-mode>"record-mode"</a></code></li><li><code><a href=#acquire-mode>"acquire-mode"</a></code></li><li><code><a href=#cooling-point>"cooling-point"</a></code></li><li><code><a href=#cooling-point-min>"cooling-point-min"</a></code></li><li><code><a href=#cooling-point-max>"cooling-point-max"</a></code></li><li><code><a href=#cooling-point-default>"cooling-point-default"</a></code></li><li><code><a href=#noise-filter>"noise-filter"</a></code></li><li><code><a href=#timestamp-mode>"timestamp-mode"</a></code></li></ul><h2>Details</h2><dl><dt id="name"><a href="#toc">name</a></dt> +<dd><pre><code class="prop-type">"name" : gchararray : Read-only</code></pre> +<p>Name of the camera</p> +</dd><dt id="sensor-width"><a href="#toc">sensor-width</a></dt> +<dd><pre><code class="prop-type">"sensor-width" : guint : Read-only</code></pre> +<p>Width of the sensor in pixels</p> +</dd><dt id="sensor-height"><a href="#toc">sensor-height</a></dt> +<dd><pre><code class="prop-type">"sensor-height" : guint : Read-only</code></pre> +<p>Height of the sensor in pixels</p> +</dd><dt id="sensor-bitdepth"><a href="#toc">sensor-bitdepth</a></dt> +<dd><pre><code class="prop-type">"sensor-bitdepth" : guint : Read-only</code></pre> +<p>Number of bits per pixel</p> +</dd><dt id="sensor-horizontal-binning"><a href="#toc">sensor-horizontal-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in horizontal direction</p> +</dd><dt id="sensor-horizontal-binnings"><a href="#toc">sensor-horizontal-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-horizontal-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in horizontal direction</p> +</dd><dt id="sensor-vertical-binning"><a href="#toc">sensor-vertical-binning</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binning" : guint : Read / Write</code></pre> +<p>Number of sensor ADCs that are combined to one pixel in vertical direction</p> +</dd><dt id="sensor-vertical-binnings"><a href="#toc">sensor-vertical-binnings</a></dt> +<dd><pre><code class="prop-type">"sensor-vertical-binnings" : GValueArray : Read-only</code></pre> +<p>Array of possible binnings in vertical direction</p> +</dd><dt id="sensor-max-frame-rate"><a href="#toc">sensor-max-frame-rate</a></dt> +<dd><pre><code class="prop-type">"sensor-max-frame-rate" : gfloat : Read-only</code></pre> +<p>Maximum frame rate at full frame resolution</p> +</dd><dt id="trigger-mode"><a href="#toc">trigger-mode</a></dt> +<dd><pre><code class="prop-type">"trigger-mode" : UcaCameraTrigger : Read / Write</code></pre> +<p>Trigger mode</p> +</dd><dt id="exposure-time"><a href="#toc">exposure-time</a></dt> +<dd><pre><code class="prop-type">"exposure-time" : gdouble : Read / Write</code></pre> +<p>Exposure time in seconds</p> +</dd><dt id="frames-per-second"><a href="#toc">frames-per-second</a></dt> +<dd><pre><code class="prop-type">"frames-per-second" : gdouble : Read / Write</code></pre> +<p>Frames per second</p> +</dd><dt id="roi-x0"><a href="#toc">roi-x0</a></dt> +<dd><pre><code class="prop-type">"roi-x0" : guint : Read / Write</code></pre> +<p>Horizontal coordinate</p> +</dd><dt id="roi-y0"><a href="#toc">roi-y0</a></dt> +<dd><pre><code class="prop-type">"roi-y0" : guint : Read / Write</code></pre> +<p>Vertical coordinate</p> +</dd><dt id="roi-width"><a href="#toc">roi-width</a></dt> +<dd><pre><code class="prop-type">"roi-width" : guint : Read / Write</code></pre> +<p>Width of the region of interest</p> +</dd><dt id="roi-height"><a href="#toc">roi-height</a></dt> +<dd><pre><code class="prop-type">"roi-height" : guint : Read / Write</code></pre> +<p>Height of the region of interest</p> +</dd><dt id="roi-width-multiplier"><a href="#toc">roi-width-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-width-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of horizontal ROI</p> +</dd><dt id="roi-height-multiplier"><a href="#toc">roi-height-multiplier</a></dt> +<dd><pre><code class="prop-type">"roi-height-multiplier" : guint : Read-only</code></pre> +<p>Minimum possible step size of vertical ROI</p> +</dd><dt id="has-streaming"><a href="#toc">has-streaming</a></dt> +<dd><pre><code class="prop-type">"has-streaming" : gboolean : Read-only</code></pre> +<p>Is the camera able to stream the data</p> +</dd><dt id="has-camram-recording"><a href="#toc">has-camram-recording</a></dt> +<dd><pre><code class="prop-type">"has-camram-recording" : gboolean : Read-only</code></pre> +<p>Is the camera able to record the data in-camera</p> +</dd><dt id="transfer-asynchronously"><a href="#toc">transfer-asynchronously</a></dt> +<dd><pre><code class="prop-type">"transfer-asynchronously" : gboolean : Read / Write</code></pre> +<p>Specify whether data should be transfered asynchronously using a specified callback</p> +</dd><dt id="is-recording"><a href="#toc">is-recording</a></dt> +<dd><pre><code class="prop-type">"is-recording" : gboolean : Read-only</code></pre> +<p>Is the camera currently recording</p> +</dd><dt id="is-readout"><a href="#toc">is-readout</a></dt> +<dd><pre><code class="prop-type">"is-readout" : gboolean : Read-only</code></pre> +<p>Is camera in readout mode</p> +</dd><dt id="sensor-extended"><a href="#toc">sensor-extended</a></dt> +<dd><pre><code class="prop-type">"sensor-extended" : gboolean : Read / Write</code></pre> +<p>Use extended sensor format</p> +</dd><dt id="sensor-width-extended"><a href="#toc">sensor-width-extended</a></dt> +<dd><pre><code class="prop-type">"sensor-width-extended" : guint : Read-only</code></pre> +<p>Width of the extended sensor in pixels</p> +</dd><dt id="sensor-height-extended"><a href="#toc">sensor-height-extended</a></dt> +<dd><pre><code class="prop-type">"sensor-height-extended" : guint : Read-only</code></pre> +<p>Height of the extended sensor in pixels</p> +</dd><dt id="sensor-temperature"><a href="#toc">sensor-temperature</a></dt> +<dd><pre><code class="prop-type">"sensor-temperature" : gdouble : Read-only</code></pre> +<p>Temperature of the sensor in degree Celsius</p> +</dd><dt id="sensor-pixelrates"><a href="#toc">sensor-pixelrates</a></dt> +<dd><pre><code class="prop-type">"sensor-pixelrates" : GValueArray : Read-only</code></pre> +<p>Array of possible sensor pixel rates</p> +</dd><dt id="sensor-pixelrate"><a href="#toc">sensor-pixelrate</a></dt> +<dd><pre><code class="prop-type">"sensor-pixelrate" : guint : Read / Write</code></pre> +<p>Pixel rate</p> +</dd><dt id="sensor-adcs"><a href="#toc">sensor-adcs</a></dt> +<dd><pre><code class="prop-type">"sensor-adcs" : guint : Read / Write</code></pre> +<p>Number of ADCs to use</p> +</dd><dt id="sensor-max-adcs"><a href="#toc">sensor-max-adcs</a></dt> +<dd><pre><code class="prop-type">"sensor-max-adcs" : guint : Read-only</code></pre> +<p>Maximum number of ADCs that can be set with "sensor-adcs"</p> +</dd><dt id="delay-time"><a href="#toc">delay-time</a></dt> +<dd><pre><code class="prop-type">"delay-time" : gdouble : Read / Write</code></pre> +<p>Delay before starting actual exposure</p> +</dd><dt id="has-double-image-mode"><a href="#toc">has-double-image-mode</a></dt> +<dd><pre><code class="prop-type">"has-double-image-mode" : gboolean : Read-only</code></pre> +<p>Is double image mode supported by this model</p> +</dd><dt id="double-image-mode"><a href="#toc">double-image-mode</a></dt> +<dd><pre><code class="prop-type">"double-image-mode" : gboolean : Read / Write</code></pre> +<p>Use double image mode</p> +</dd><dt id="offset-mode"><a href="#toc">offset-mode</a></dt> +<dd><pre><code class="prop-type">"offset-mode" : gboolean : Read / Write</code></pre> +<p>Use offset mode</p> +</dd><dt id="record-mode"><a href="#toc">record-mode</a></dt> +<dd><pre><code class="prop-type">"record-mode" : UcaPcoCameraRecordMode : Read / Write</code></pre> +<p>Record mode</p> +</dd><dt id="acquire-mode"><a href="#toc">acquire-mode</a></dt> +<dd><pre><code class="prop-type">"acquire-mode" : UcaPcoCameraAcquireMode : Read / Write</code></pre> +<p>Acquire mode</p> +</dd><dt id="cooling-point"><a href="#toc">cooling-point</a></dt> +<dd><pre><code class="prop-type">"cooling-point" : gint : Read / Write</code></pre> +<p>Cooling point of the camera in degree celsius</p> +</dd><dt id="cooling-point-min"><a href="#toc">cooling-point-min</a></dt> +<dd><pre><code class="prop-type">"cooling-point-min" : gint : Read-only</code></pre> +<p>Minimum cooling point in degree celsius</p> +</dd><dt id="cooling-point-max"><a href="#toc">cooling-point-max</a></dt> +<dd><pre><code class="prop-type">"cooling-point-max" : gint : Read-only</code></pre> +<p>Maximum cooling point in degree celsius</p> +</dd><dt id="cooling-point-default"><a href="#toc">cooling-point-default</a></dt> +<dd><pre><code class="prop-type">"cooling-point-default" : gint : Read-only</code></pre> +<p>Default cooling point in degree celsius</p> +</dd><dt id="noise-filter"><a href="#toc">noise-filter</a></dt> +<dd><pre><code class="prop-type">"noise-filter" : gboolean : Read / Write</code></pre> +<p>Noise filter</p> +</dd><dt id="timestamp-mode"><a href="#toc">timestamp-mode</a></dt> +<dd><pre><code class="prop-type">"timestamp-mode" : UcaPcoCameraTimestamp : Read / Write</code></pre> +<p>Timestamp mode</p> +</dd></dl></body></html> diff --git a/docs/style.css b/docs/style.css index a9fc9ca..beccf45 100644 --- a/docs/style.css +++ b/docs/style.css @@ -1,35 +1,34 @@ - /* --- Reset ------------------------------------------------------ */ body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, -p, blockquote, th, td { - margin: 0; +p, blockquote, th, td { + margin: 0; padding: 0; } table { - border-collapse: collapse; + border-collapse: collapse; border-spacing: 0; } fieldset, img { - border: 0; - } - address, caption, cite, dfn, th, var { + border: 0; +} +address, caption, cite, dfn, th, var { font-style: normal; - font-weight: normal; - } - caption, th { + font-weight: normal; +} +caption, th { text-align: left; } h1, h2, h3, h4, h5, h6 { - font-size: 100%; + font-size: 100%; font-weight: normal; } q:before, q:after { - content: ''; - } - abbr, acronym { + content: ''; +} +abbr, acronym { border: 0; } @@ -52,10 +51,6 @@ p { margin-bottom: 24px; } -/* p > code { */ -/* font-size: 0.8em; */ -/* } */ - h1, h2, h3, h4, h5, h6 { margin-bottom: 24px; font-family: "Droid Sans", sans-serif; @@ -65,6 +60,9 @@ h1, h2, h3, h4, h5, h6 { h1 { font-size: 1.5em; line-height: 1em; + margin-top: 2em; + padding-bottom: 0.5em; + border-bottom: 2px dotted #bbb; } h2 { @@ -87,11 +85,29 @@ h5, h6 { line-height: 1.5em; } -h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a { - color: black; +h1 > a { + color: #555; text-decoration: none; } +a { + color: #8e3557; + text-decoration: none; +} + +a:hover { + color: #88a33e; +} + +h1:hover :after, +h2:hover :after, +h3:hover :after, +h4:hover :after, +h5:hover :after, +h6:hover :after { + content: " \2191"; +} + pre { margin-bottom: 24px; line-height: 1.2em; @@ -103,19 +119,62 @@ code { } ul, ol { + list-style-position: inside; + padding-left: 1em; + text-indent: -1em; + margin-left: 0; margin-bottom: 24px; } +ul#toc { + list-style-type: none; +} + +li + li { + margin-top: 0.1em; +} + ul ul, ol ol { margin: 0 0 0 24px; } +table { + width: 100%; + margin-bottom: 24px; +} + +th { + color: #555; + font-family: 'Droid Sans', sans-serif; + font-weight: bold; + border-bottom: 1px dotted #bbb; +} + +td, th { + padding: 2px; +} + +dl { + margin-bottom: 24px; +} + +dt { + font-style: italic; +} + +dd { + margin-top: 12px; + padding-left: 1em; +} + .title { font-size: xx-large; + color: #555; } .author { font-size: 1.25em; font-weight: normal; + color: #555; } diff --git a/package.sh.in b/package.sh.in new file mode 100644 index 0000000..7c15b78 --- /dev/null +++ b/package.sh.in @@ -0,0 +1,7 @@ +cpack -D CPACK_INSTALL_CMAKE_PROJECTS="${CMAKE_CURRENT_BINARY_DIR}/;Project;libraries;/" +cpack -D CPACK_INSTALL_CMAKE_PROJECTS="${CMAKE_CURRENT_BINARY_DIR}/;Project;headers;/" -D CPACK_PACKAGE_FILE_NAME="libuca-${UCA_VERSION_MAJOR}.${UCA_VERSION_MINOR}.${UCA_VERSION_PATCH}-devel" + +# Build packages for all available cameras +for shell_script in `find -name 'package-plugin-*.sh'`; do + sh $shell_script +done 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..d5b6771 --- /dev/null +++ b/plugins/mock/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.8) +project(ucamock C) + +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/src/cameras/uca-mock-camera.c b/plugins/mock/uca-mock-camera.c index 59c5ae8..bf3124e 100644 --- a/src/cameras/uca-mock-camera.c +++ b/plugins/mock/uca-mock-camera.c @@ -15,6 +15,7 @@ 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" @@ -36,7 +37,6 @@ static const gint mock_overrideables[] = { PROP_SENSOR_HORIZONTAL_BINNINGS, PROP_SENSOR_VERTICAL_BINNING, PROP_SENSOR_VERTICAL_BINNINGS, - PROP_TRIGGER_MODE, PROP_EXPOSURE_TIME, PROP_ROI_X, PROP_ROI_Y, @@ -146,9 +146,10 @@ static void print_number(gchar *buffer, guint number, guint x, guint y, guint wi static void print_current_frame(UcaMockCameraPrivate *priv, gchar *buffer) { guint number = priv->current_frame; - guint divisor = 100000000; + guint divisor = 10000000; int x = 10; - while (divisor > 1) { + + while (divisor > 0) { print_number(buffer, number / divisor, x, 10, priv->width); number = number % divisor; divisor = divisor / 10; @@ -156,20 +157,6 @@ static void print_current_frame(UcaMockCameraPrivate *priv, gchar *buffer) } } -/** - * uca_mock_camera_new: - * @error: Location for error - * - * Create a new #UcaMockCamera object. - * - * Returns: A newly created #UcaMockCamera object - */ -UcaMockCamera *uca_mock_camera_new(GError **error) -{ - UcaMockCamera *camera = g_object_new(UCA_TYPE_MOCK_CAMERA, NULL); - return camera; -} - static gpointer mock_grab_func(gpointer data) { UcaMockCamera *mock_camera = UCA_MOCK_CAMERA(data); @@ -208,7 +195,7 @@ static void uca_mock_camera_start_recording(UcaCamera *camera, GError **error) if (transfer_async) { GError *tmp_error = NULL; priv->thread_running = TRUE; - priv->grab_thread = g_thread_create(mock_grab_func, camera, TRUE, &tmp_error); + priv->grab_thread = g_thread_create(mock_grab_func, camera, TRUE, &tmp_error); if (tmp_error != NULL) { priv->thread_running = FALSE; @@ -232,7 +219,7 @@ static void uca_mock_camera_stop_recording(UcaCamera *camera, GError **error) NULL); if (transfer_async) { - priv->thread_running = FALSE; + priv->thread_running = FALSE; g_thread_join(priv->grab_thread); } } @@ -314,9 +301,6 @@ static void uca_mock_camera_get_property(GObject *object, guint property_id, GVa 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; @@ -358,7 +342,7 @@ static void uca_mock_camera_finalize(GObject *object) UcaMockCameraPrivate *priv = UCA_MOCK_CAMERA_GET_PRIVATE(object); if (priv->thread_running) { - priv->thread_running = FALSE; + priv->thread_running = FALSE; g_thread_join(priv->grab_thread); } @@ -383,7 +367,7 @@ static void uca_mock_camera_class_init(UcaMockCameraClass *klass) 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] = + mock_properties[PROP_FRAMERATE] = g_param_spec_float("frame-rate", "Frame rate", "Number of frames per second that are taken", @@ -406,10 +390,20 @@ static void uca_mock_camera_init(UcaMockCamera *self) self->priv->frame_rate = self->priv->max_frame_rate = 100000.0f; self->priv->grab_thread = NULL; self->priv->current_frame = 0; + self->priv->exposure_time = 0.05; 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); + + uca_camera_register_unit (UCA_CAMERA (self), "frame-rate", UCA_UNIT_COUNT); +} + +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/src/cameras/uca-mock-camera.h b/plugins/mock/uca-mock-camera.h index 92030f8..9ee9190 100644 --- a/src/cameras/uca-mock-camera.h +++ b/plugins/mock/uca-mock-camera.h @@ -58,8 +58,6 @@ struct _UcaMockCameraClass { UcaCameraClass parent; }; -UcaMockCamera *uca_mock_camera_new(GError **error); - GType uca_mock_camera_get_type(void); G_END_DECLS 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..a526f4d --- /dev/null +++ b/plugins/pco/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapco C) + +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/src/cameras/uca-pco-camera.c b/plugins/pco/uca-pco-camera.c index a56392f..bb7bd5a 100644 --- a/src/cameras/uca-pco-camera.c +++ b/plugins/pco/uca-pco-camera.c @@ -15,6 +15,7 @@ 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> @@ -50,7 +51,7 @@ "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) @@ -76,6 +77,7 @@ G_DEFINE_TYPE(UcaPcoCamera, uca_pco_camera, UCA_TYPE_CAMERA) * @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 */ /** @@ -127,6 +129,7 @@ static gint base_overrideables[] = { 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, @@ -136,6 +139,7 @@ static gint base_overrideables[] = { PROP_ROI_HEIGHT_MULTIPLIER, PROP_HAS_STREAMING, PROP_HAS_CAMRAM_RECORDING, + PROP_RECORDED_FRAMES, 0 }; @@ -186,10 +190,10 @@ struct _UcaPcoCameraPrivate { guint current_image; }; -static pco_cl_map_entry pco_cl_map[] = { +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 }, + { CAMERATYPE_PCO_DIMAX_STD, "libDualAreaGray16.so", FG_CL_SINGLETAP_8_BIT, FG_GRAY16, 1279.0f, TRUE }, { 0, NULL, 0, 0, 0.0f, FALSE } }; @@ -200,20 +204,21 @@ static pco_cl_map_entry *get_pco_cl_map_entry(int camera_type) while (entry->camera_type != 0) { if (entry->camera_type == camera_type) return entry; - entry++; + entry++; } return NULL; } -static guint fill_binnings(UcaPcoCameraPrivate *priv) +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, + guint err = pco_get_possible_binnings(priv->pco, + &horizontal, &num_horizontal, &vertical, &num_vertical); GValue val = {0}; @@ -239,19 +244,21 @@ static guint fill_binnings(UcaPcoCameraPrivate *priv) return err; } -static void fill_pixelrates(UcaPcoCameraPrivate *priv, guint32 rates[4], gint num_rates) +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_set_uint(&val, (guint) rates[i]); g_value_array_append(priv->pixelrates, &val); } } -static guint override_temperature_range(UcaPcoCameraPrivate *priv) +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); @@ -268,7 +275,8 @@ static guint override_temperature_range(UcaPcoCameraPrivate *priv) return err; } -static void property_override_default_guint_value (GObjectClass *oclass, const gchar *property_name, guint new_default) +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)); @@ -278,13 +286,15 @@ static void property_override_default_guint_value (GObjectClass *oclass, const g g_warning ("pspec for %s not found\n", property_name); } -static void override_maximum_adcs(UcaPcoCameraPrivate *priv) +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) +static gdouble +convert_timebase(guint16 timebase) { switch (timebase) { case TIMEBASE_NS: @@ -299,12 +309,14 @@ static gdouble convert_timebase(guint16 timebase) return 1e-3; } -static void read_timebase(UcaPcoCameraPrivate *priv) +static void +read_timebase(UcaPcoCameraPrivate *priv) { pco_get_timebase(priv->pco, &priv->delay_timebase, &priv->exposure_timebase); } -static gdouble get_suitable_timebase(gdouble time) +static gdouble +get_suitable_timebase(gdouble time) { if (time * 1e3 >= 1.0) return TIMEBASE_MS; @@ -315,94 +327,24 @@ static gdouble get_suitable_timebase(gdouble time) return TIMEBASE_INVALID; } -UcaPcoCamera *uca_pco_camera_new(GError **error) +static gdouble +get_internal_delay (UcaPcoCamera *camera) { - 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; + if (camera->priv->camera_description->camera_type == CAMERATYPE_PCO_DIMAX_STD) { + gdouble sensor_rate; + g_object_get (camera, "sensor-pixelrate", &sensor_rate, NULL); + + if (sensor_rate == 55000000.0) + return 0.000079; + else if (sensor_rate == 62500000.0) + return 0.000069; } - 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 camera; + return 0.0; } -static int fg_callback(frameindex_t frame, struct fg_apc_data *apc) +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); @@ -418,7 +360,8 @@ static int fg_callback(frameindex_t frame, struct fg_apc_data *apc) return 0; } -static gboolean setup_fg_callback(UcaCamera *camera) +static gboolean +setup_fg_callback(UcaCamera *camera) { UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); struct FgApcControl ctrl; @@ -438,7 +381,18 @@ static gboolean setup_fg_callback(UcaCamera *camera) 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) +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 void +uca_pco_camera_start_recording(UcaCamera *camera, GError **error) { g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); guint err = PCO_NOERROR; @@ -506,7 +460,7 @@ static void uca_pco_camera_start_recording(UcaCamera *camera, GError **error) Fg_FreeMemEx(priv->fg, priv->fg_mem); const guint num_buffers = 2; - priv->fg_mem = Fg_AllocMemEx(priv->fg, + 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) { @@ -536,7 +490,8 @@ static void uca_pco_camera_start_recording(UcaCamera *camera, GError **error) FG_SET_ERROR(err, priv->fg, UCA_PCO_CAMERA_ERROR_FG_ACQUISITION); } -static void uca_pco_camera_stop_recording(UcaCamera *camera, GError **error) +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); @@ -550,28 +505,49 @@ static void uca_pco_camera_stop_recording(UcaCamera *camera, GError **error) 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"); + + HANDLE_PCO_ERROR(err); } -static void uca_pco_camera_start_readout(UcaCamera *camera, GError **error) +static void +uca_pco_camera_start_readout(UcaCamera *camera, GError **error) { + guint err; + 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. + * 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); + 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_trigger(UcaCamera *camera, GError **error) +static void +uca_pco_camera_stop_readout(UcaCamera *camera, GError **error) +{ + g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); + UcaPcoCameraPrivate *priv = UCA_PCO_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_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_trigger(UcaCamera *camera, GError **error) { g_return_if_fail(UCA_IS_PCO_CAMERA(camera)); UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(camera); @@ -582,10 +558,11 @@ static void uca_pco_camera_trigger(UcaCamera *camera, GError **error) if (!success) g_set_error(error, UCA_PCO_CAMERA_ERROR, UCA_PCO_CAMERA_ERROR_LIBPCO_GENERAL, - "Could not trigger frame acquisition"); + "Could not trigger frame acquisition"); } -static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +static void +uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **error) { static const gint MAX_TIMEOUT = G_MAXINT; @@ -597,7 +574,8 @@ static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **erro if (is_readout) { if (priv->current_image == priv->num_recorded_images) { - *data = NULL; + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_END_OF_STREAM, + "End of data stream"); return; } @@ -610,8 +588,8 @@ static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **erro } pco_request_image(priv->pco); - priv->last_frame = Fg_getLastPicNumberBlockingEx(priv->fg, priv->last_frame+1, priv->fg_port, MAX_TIMEOUT, priv->fg_mem); - + 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); @@ -620,7 +598,7 @@ static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **erro 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); + *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); @@ -628,15 +606,17 @@ static void uca_pco_camera_grab(UcaCamera *camera, gpointer *data, GError **erro 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) +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; - pco_set_sensor_format(priv->pco, format); + guint16 format = g_value_get_boolean (value) ? SENSORFORMAT_EXTENDED : SENSORFORMAT_STANDARD; + err = pco_set_sensor_format(priv->pco, format); } break; @@ -651,7 +631,7 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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 @@ -662,7 +642,7 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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 @@ -681,7 +661,7 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons case PROP_EXPOSURE_TIME: { const gdouble time = g_value_get_double(value); - + if (priv->exposure_timebase == TIMEBASE_INVALID) read_timebase(priv); @@ -692,27 +672,49 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons guint16 suitable_timebase = get_suitable_timebase(time); if (suitable_timebase == TIMEBASE_INVALID) { - g_warning("Cannot set such a small exposure time"); + 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"); + err = pco_set_timebase(priv->pco, priv->delay_timebase, suitable_timebase); + break; } 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"); + err = pco_set_exposure_time(priv->pco, timesteps); } } break; + case PROP_FRAMES_PER_SECOND: + { + gdouble n_frames_per_second; + gdouble exposure_time; + gdouble delay; + + /* + * We want to expose n frames in one second, each frame takes + * exposure time + delay time. Thus we have + * + * 1s = n * (t_exp + t_delay) <=> t_exp = 1s/n - t_delay. + */ + delay = get_internal_delay (UCA_PCO_CAMERA (object)); + n_frames_per_second = g_value_get_double (value); + exposure_time = 1.0 / n_frames_per_second - delay; + + if (exposure_time <= 0.0) + g_warning ("Too many frames per second requested."); + else + g_object_set (object, "exposure-time", exposure_time, NULL); + } + break; + case PROP_DELAY_TIME: { const gdouble time = g_value_get_double(value); - + if (priv->delay_timebase == TIMEBASE_INVALID) read_timebase(priv); @@ -729,32 +731,28 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons * 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"); + err = pco_set_delay_time(priv->pco, 0); } else - g_warning("Cannot set such a small exposure time"); + 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"); + err = pco_set_timebase(priv->pco, suitable_timebase, priv->exposure_timebase); } 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"); + err = pco_set_delay_time(priv->pco, timesteps); } } 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"); + const guint num_adcs = g_value_get_uint(value); + err = pco_set_adc_mode(priv->pco, num_adcs); } break; @@ -765,14 +763,13 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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; + 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"); + 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); @@ -783,17 +780,17 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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)); + err = 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)); + 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); - pco_set_cooling_temperature(priv->pco, temperature); + int16_t temperature = (int16_t) g_value_get_int(value); + err = pco_set_cooling_temperature(priv->pco, temperature); } break; @@ -803,9 +800,9 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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); + err = 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); + err = pco_set_record_mode(priv->pco, RECORDER_SUBMODE_RINGBUFFER); else g_warning("Unknown record mode"); } @@ -814,7 +811,6 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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); @@ -822,9 +818,6 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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; @@ -834,13 +827,13 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons switch (trigger_mode) { case UCA_CAMERA_TRIGGER_AUTO: - pco_set_trigger_mode(priv->pco, TRIGGER_MODE_AUTOTRIGGER); + err = pco_set_trigger_mode(priv->pco, TRIGGER_MODE_AUTOTRIGGER); break; - case UCA_CAMERA_TRIGGER_INTERNAL: - pco_set_trigger_mode(priv->pco, TRIGGER_MODE_SOFTWARETRIGGER); + case UCA_CAMERA_TRIGGER_SOFTWARE: + err = pco_set_trigger_mode(priv->pco, TRIGGER_MODE_SOFTWARETRIGGER); break; case UCA_CAMERA_TRIGGER_EXTERNAL: - pco_set_trigger_mode(priv->pco, TRIGGER_MODE_EXTERNALTRIGGER); + err = pco_set_trigger_mode(priv->pco, TRIGGER_MODE_EXTERNALTRIGGER); break; } } @@ -849,19 +842,20 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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); + err = 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)]); + 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; @@ -869,34 +863,38 @@ static void uca_pco_camera_set_property(GObject *object, guint property_id, cons 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) +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; - pco_get_sensor_format(priv->pco, &format); + guint16 format; + err = pco_get_sensor_format(priv->pco, &format); g_value_set_boolean(value, format == SENSORFORMAT_EXTENDED); } break; - case PROP_SENSOR_WIDTH: + case PROP_SENSOR_WIDTH: g_value_set_uint(value, priv->width); break; - case PROP_SENSOR_HEIGHT: + case PROP_SENSOR_HEIGHT: g_value_set_uint(value, priv->height); break; - case PROP_SENSOR_WIDTH_EXTENDED: + 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: + case PROP_SENSOR_HEIGHT_EXTENDED: g_value_set_uint(value, priv->height_ex < priv->height ? priv->height : priv->height_ex); break; @@ -926,8 +924,8 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_SENSOR_TEMPERATURE: { - gint32 ccd, camera, power; - pco_get_temperature(priv->pco, &ccd, &camera, &power); + gint32 ccd, camera, power; + err = pco_get_temperature(priv->pco, &ccd, &camera, &power); g_value_set_double(value, ccd / 10.0); } break; @@ -938,9 +936,8 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal * 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"); + pco_adc_mode mode; + err = pco_get_adc_mode(priv->pco, &mode); g_value_set_uint(value, mode); } break; @@ -958,8 +955,8 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_SENSOR_PIXELRATE: { - guint32 pixelrate; - pco_get_pixelrate(priv->pco, &pixelrate); + guint32 pixelrate; + err = pco_get_pixelrate(priv->pco, &pixelrate); g_value_set_uint(value, pixelrate); } break; @@ -967,7 +964,7 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_EXPOSURE_TIME: { uint32_t exposure_time; - pco_get_exposure_time(priv->pco, &exposure_time); + err = pco_get_exposure_time(priv->pco, &exposure_time); if (priv->exposure_timebase == TIMEBASE_INVALID) read_timebase(priv); @@ -976,10 +973,21 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal } break; + case PROP_FRAMES_PER_SECOND: + { + gdouble exposure_time; + gdouble delay; + + delay = get_internal_delay (UCA_PCO_CAMERA (object)); + g_object_get (object, "exposure-time", &exposure_time, NULL); + g_value_set_double (value, 1.0 / (exposure_time + delay)); + } + break; + case PROP_DELAY_TIME: { uint32_t delay_time; - pco_get_delay_time(priv->pco, &delay_time); + err = pco_get_delay_time(priv->pco, &delay_time); if (priv->delay_timebase == TIMEBASE_INVALID) read_timebase(priv); @@ -997,7 +1005,7 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal g_warning("Double image mode is not available on this pco model"); else { bool on; - pco_get_double_image_mode(priv->pco, &on); + err = pco_get_double_image_mode(priv->pco, &on); g_value_set_boolean(value, on); } break; @@ -1005,7 +1013,7 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_OFFSET_MODE: { bool on; - pco_get_offset_mode(priv->pco, &on); + err = pco_get_offset_mode(priv->pco, &on); g_value_set_boolean(value, on); } break; @@ -1018,10 +1026,15 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal g_value_set_boolean(value, priv->camera_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; - pco_get_record_mode(priv->pco, &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); @@ -1035,7 +1048,7 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_ACQUIRE_MODE: { guint16 mode; - pco_get_acquire_mode(priv->pco, &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); @@ -1048,15 +1061,15 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_TRIGGER_MODE: { - guint16 mode; - pco_get_trigger_mode(priv->pco, &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_INTERNAL); + g_value_set_enum(value, UCA_CAMERA_TRIGGER_SOFTWARE); break; case TRIGGER_MODE_EXTERNALTRIGGER: g_value_set_enum(value, UCA_CAMERA_TRIGGER_EXTERNAL); @@ -1091,20 +1104,19 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal g_value_set_uint(value, priv->roi_vertical_steps); break; - case PROP_NAME: + case PROP_NAME: { gchar *name = NULL; - pco_get_name(priv->pco, &name); - g_value_set_string(value, name); + err = 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"); + int16_t temperature; + err = pco_get_cooling_temperature(priv->pco, &temperature); g_value_set_int(value, temperature); } break; @@ -1133,41 +1145,36 @@ static void uca_pco_camera_get_property(GObject *object, guint property_id, GVal case PROP_NOISE_FILTER: { guint16 mode; - pco_get_noise_filter_mode(priv->pco, &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; - 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; - } + 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; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); - break; + return; } + + check_pco_property_error (err, property_id); } -static void uca_pco_camera_finalize(GObject *object) +static void +uca_pco_camera_finalize(GObject *object) { UcaPcoCameraPrivate *priv = UCA_PCO_CAMERA_GET_PRIVATE(object); @@ -1195,7 +1202,8 @@ static void uca_pco_camera_finalize(GObject *object) G_OBJECT_CLASS(uca_pco_camera_parent_class)->finalize(object); } -static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) +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; @@ -1206,6 +1214,7 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *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; @@ -1220,20 +1229,20 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) * #UcaPcoCamera:sensor-height-extended to query the resolution of the * larger area. */ - pco_properties[PROP_SENSOR_EXTENDED] = + 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] = + 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] = + pco_properties[PROP_SENSOR_HEIGHT_EXTENDED] = g_param_spec_uint("sensor-height-extended", "Height of extended sensor", "Height of the extended sensor in pixels", @@ -1248,21 +1257,21 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) * #UcaPcoCamera:sensor-pixelrates property. Any other value will be * rejected by the camera. */ - pco_properties[PROP_SENSOR_PIXELRATE] = + 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] = + 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] = + pco_properties[PROP_NAME] = g_param_spec_string("name", "Name of the camera", "Name of the camera", @@ -1275,38 +1284,38 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READABLE); - pco_properties[PROP_HAS_DOUBLE_IMAGE_MODE] = + 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] = + 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] = + 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", + 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", + 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", @@ -1314,7 +1323,7 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE); - pco_properties[PROP_NOISE_FILTER] = + pco_properties[PROP_NOISE_FILTER] = g_param_spec_boolean("noise-filter", "Noise filter", "Noise filter", @@ -1327,46 +1336,46 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) * the camera and just don't know the cooling range. We override these * values in #uca_pco_camera_new(). */ - pco_properties[PROP_COOLING_POINT] = + 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] = + 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] = + 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] = + 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] = + + pco_properties[PROP_SENSOR_ADCS] = g_param_spec_uint("sensor-adcs", "Number of ADCs to use", "Number of ADCs to use", - 1, 2, 1, + 1, 2, 1, G_PARAM_READWRITE); - pco_properties[PROP_SENSOR_MAX_ADCS] = + 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, + 1, G_MAXUINT, 1, G_PARAM_READABLE); pco_properties[PROP_TIMESTAMP_MODE] = - g_param_spec_flags("timestamp-mode", + g_param_spec_enum("timestamp-mode", "Timestamp mode", "Timestamp mode", UCA_TYPE_PCO_CAMERA_TIMESTAMP, UCA_PCO_CAMERA_TIMESTAMP_NONE, @@ -1378,8 +1387,11 @@ static void uca_pco_camera_class_init(UcaPcoCameraClass *klass) g_type_class_add_private(klass, sizeof(UcaPcoCameraPrivate)); } -static void uca_pco_camera_init(UcaPcoCamera *self) +static void +uca_pco_camera_init(UcaPcoCamera *self) { + UcaCamera *camera; + self->priv = UCA_PCO_CAMERA_GET_PRIVATE(self); self->priv->fg = NULL; self->priv->fg_mem = NULL; @@ -1393,4 +1405,106 @@ static void uca_pco_camera_init(UcaPcoCamera *self) self->priv->delay_timebase = TIMEBASE_INVALID; self->priv->exposure_timebase = TIMEBASE_INVALID; + + 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); + uca_camera_register_unit (camera, "delay-time", UCA_UNIT_SECOND); +} + +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_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(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; + priv->num_recorded_images = 0; + + 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/src/cameras/uca-pco-camera.h b/plugins/pco/uca-pco-camera.h index fdb709a..d134fb1 100644 --- a/src/cameras/uca-pco-camera.h +++ b/plugins/pco/uca-pco-camera.h @@ -55,9 +55,10 @@ typedef enum { } UcaPcoCameraAcquireMode; typedef enum { - UCA_PCO_CAMERA_TIMESTAMP_NONE = 0, - UCA_PCO_CAMERA_TIMESTAMP_BINARY = 1 << 0, - UCA_PCO_CAMERA_TIMESTAMP_ASCII = 1 << 1 + UCA_PCO_CAMERA_TIMESTAMP_NONE, + UCA_PCO_CAMERA_TIMESTAMP_BINARY, + UCA_PCO_CAMERA_TIMESTAMP_ASCII, + UCA_PCO_CAMERA_TIMESTAMP_BOTH } UcaPcoCameraTimestamp; /** @@ -84,8 +85,6 @@ struct _UcaPcoCameraClass { UcaCameraClass parent; }; -UcaPcoCamera *uca_pco_camera_new(GError **error); - GType uca_pco_camera_get_type(void); G_END_DECLS diff --git a/plugins/pf/CMakeLists.txt b/plugins/pf/CMakeLists.txt new file mode 100644 index 0000000..ef11f8f --- /dev/null +++ b/plugins/pf/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapf C) + +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/src/cameras/uca-pf-camera.c b/plugins/pf/uca-pf-camera.c index 5bc8c6b..473ffd3 100644 --- a/src/cameras/uca-pf-camera.c +++ b/plugins/pf/uca-pf-camera.c @@ -15,6 +15,7 @@ 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> @@ -99,52 +100,6 @@ struct _UcaPfCameraPrivate { dma_mem *fg_mem; }; -UcaPfCamera *uca_pf_camera_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 camera; -} - /* * We just embed our private structure here. */ @@ -226,7 +181,7 @@ static void uca_pf_camera_grab(UcaCamera *camera, gpointer *data, GError **error 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); @@ -252,10 +207,10 @@ static void uca_pf_camera_set_property(GObject *object, guint property_id, const static void uca_pf_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { - case PROP_SENSOR_WIDTH: + case PROP_SENSOR_WIDTH: g_value_set_uint(value, 1280); break; - case PROP_SENSOR_HEIGHT: + case PROP_SENSOR_HEIGHT: g_value_set_uint(value, 1024); break; case PROP_SENSOR_BITDEPTH: @@ -279,6 +234,7 @@ static void uca_pf_camera_get_property(GObject *object, guint property_id, GValu g_value_set_boolean(value, FALSE); break; case PROP_EXPOSURE_TIME: + g_value_set_double(value, 1. / 488.0); break; case PROP_ROI_X: g_value_set_uint(value, 0); @@ -298,7 +254,7 @@ static void uca_pf_camera_get_property(GObject *object, guint property_id, GValu case PROP_ROI_HEIGHT_MULTIPLIER: g_value_set_uint(value, 1); break; - case PROP_NAME: + case PROP_NAME: g_value_set_string(value, "Photon Focus MV2-D1280-640-CL"); break; default: @@ -347,3 +303,50 @@ static void uca_pf_camera_init(UcaPfCamera *self) 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/src/cameras/uca-pf-camera.h b/plugins/pf/uca-pf-camera.h index 7e3fe2c..3a309aa 100644 --- a/src/cameras/uca-pf-camera.h +++ b/plugins/pf/uca-pf-camera.h @@ -67,8 +67,6 @@ struct _UcaPfCameraClass { UcaCameraClass parent; }; -UcaPfCamera *uca_pf_camera_new(GError **error); - GType uca_pf_camera_get_type(void); G_END_DECLS diff --git a/plugins/pylon/CMakeLists.txt b/plugins/pylon/CMakeLists.txt new file mode 100644 index 0000000..9a3c15c --- /dev/null +++ b/plugins/pylon/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8) +project(ucapylon C) + + +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/src/cameras/uca-pylon-camera.c b/plugins/pylon/uca-pylon-camera.c index 541b69b..541b69b 100644 --- a/src/cameras/uca-pylon-camera.c +++ b/plugins/pylon/uca-pylon-camera.c diff --git a/src/cameras/uca-pylon-camera.h b/plugins/pylon/uca-pylon-camera.h index eebf63c..eebf63c 100644 --- a/src/cameras/uca-pylon-camera.h +++ b/plugins/pylon/uca-pylon-camera.h diff --git a/plugins/ufo/CMakeLists.txt b/plugins/ufo/CMakeLists.txt new file mode 100644 index 0000000..c7fd21b --- /dev/null +++ b/plugins/ufo/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8) +project(ucaufo C) + +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/src/cameras/uca-ufo-camera.c b/plugins/ufo/uca-ufo-camera.c index 5f59f4a..2b289a5 100644 --- a/src/cameras/uca-ufo-camera.c +++ b/plugins/ufo/uca-ufo-camera.c @@ -15,6 +15,7 @@ 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> @@ -100,6 +101,7 @@ struct _UcaUfoCameraPrivate { FPGA_48MHZ = 0, FPGA_40MHZ } frequency; + UcaCameraTrigger trigger; }; static void @@ -143,7 +145,8 @@ static int event_callback(pcilib_event_id_t event_id, pcilib_event_info_t *info, return PCILIB_STREAMING_CONTINUE; } -UcaUfoCamera *uca_ufo_camera_new(GError **error) +G_MODULE_EXPORT UcaCamera * +uca_camera_impl_new (GError **error) { pcilib_model_t model = PCILIB_MODEL_DETECT; pcilib_model_description_t *model_description; @@ -223,7 +226,7 @@ UcaUfoCamera *uca_ufo_camera_new(GError **error) priv->handle = handle; - return camera; + return UCA_CAMERA (camera); } static void uca_ufo_camera_start_recording(UcaCamera *camera, GError **error) @@ -242,6 +245,7 @@ static void uca_ufo_camera_start_recording(UcaCamera *camera, GError **error) g_object_get(G_OBJECT(camera), "transfer-asynchronously", &transfer_async, "exposure-time", &exposure_time, + "trigger-mode", &priv->trigger, NULL); priv->timeout = ((pcilib_timeout_t) (exposure_time * 1000 + 50.0) * 1000); @@ -263,8 +267,11 @@ static void uca_ufo_camera_stop_recording(UcaCamera *camera, GError **error) 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_stop_readout(UcaCamera *camera, GError **error) +{ + g_return_if_fail(UCA_IS_UFO_CAMERA(camera)); } static void uca_ufo_camera_grab(UcaCamera *camera, gpointer *data, GError **error) @@ -277,8 +284,10 @@ static void uca_ufo_camera_grab(UcaCamera *camera, gpointer *data, GError **erro 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); + if (priv->trigger != UCA_CAMERA_TRIGGER_EXTERNAL) { + 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); @@ -330,6 +339,10 @@ uca_ufo_camera_set_property(GObject *object, guint property_id, const GValue *va g_debug("ROI feature not implemented yet"); break; + case PROP_TRIGGER_MODE: + priv->trigger = g_value_get_enum (value); + break; + default: { RegisterInfo *reg_info = g_hash_table_lookup (ufo_property_table, GINT_TO_POINTER (property_id)); @@ -428,6 +441,7 @@ uca_ufo_camera_get_property(GObject *object, guint property_id, GValue *value, G g_value_set_string(value, "Ufo Camera w/ CMOSIS CMV2000"); break; case PROP_TRIGGER_MODE: + g_value_set_enum (value, priv->trigger); break; default: { @@ -460,6 +474,7 @@ static void uca_ufo_camera_class_init(UcaUfoCameraClass *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->stop_readout = uca_ufo_camera_stop_readout; camera_class->grab = uca_ufo_camera_grab; for (guint i = 0; base_overrideables[i] != 0; i++) diff --git a/src/cameras/uca-ufo-camera.h b/plugins/ufo/uca-ufo-camera.h index 0b52ffb..7030389 100644 --- a/src/cameras/uca-ufo-camera.h +++ b/plugins/ufo/uca-ufo-camera.h @@ -69,8 +69,6 @@ struct _UcaUfoCameraClass { UcaCameraClass parent; }; -UcaUfoCamera *uca_ufo_camera_new(GError **error); - GType uca_ufo_camera_get_type(void); G_END_DECLS diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 033587e..160c52b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,130 +1,15 @@ cmake_minimum_required(VERSION 2.8) +project(uca C) # --- Set sources ------------------------------------------------------------- set(uca_SRCS uca-camera.c + uca-plugin-manager.c ) set(uca_HDRS - uca-camera.h) - -set(cameras) - -# --- Find packages and libraries --------------------------------------------- -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) - -# --- Find camera interfaces -find_package(PCO) -find_package(PF) -find_package(IPE) -find_package(Pylon) -find_package(DEXELA) - -# --- Find frame grabber interfaces -find_package(FgLib5) -find_package(ClSerMe4) - -# --- Miscellanous packages -find_package(PkgConfig) -find_program(GLIB2_MKENUMS glib-mkenums REQUIRED) -pkg_check_modules(GLIB2 glib-2.0>=2.24 REQUIRED) -pkg_check_modules(GOBJECT2 gobject-2.0>=2.24 REQUIRED) - -set(uca_LIBS - ${GLIB2_LIBRARIES} - ${GOBJECT2_LIBRARIES}) - -# --- Build options ----------------------------------------------------------- -option(HAVE_MOCK_CAMERA "Camera: Dummy" ON) - - -# --- Add sources if camera/framegrabber access sources are available --------- -if (PF_FOUND) - option(HAVE_PHOTON_FOCUS "Camera: Photon Focus MV2-D1280-640-CL-8" ON) - - if (HAVE_PHOTON_FOCUS AND CLSERME4_FOUND AND FGLIB5_FOUND) - list(APPEND uca_SRCS cameras/uca-pf-camera.c) - list(APPEND uca_HDRS cameras/uca-pf-camera.h) - list(APPEND cameras "Pf") - - set(uca_LIBS ${uca_LIBS} - ${CLSERME4_LIBRARY} - ${FGLIB5_LIBRARY} - ${PF_LIBRARIES}) - - include_directories(${PF_INCLUDE_DIRS} - ${CLSERME4_INCLUDE_DIR} - ${FGLIB5_INCLUDE_DIR}) - endif() -endif() - -if (PCO_FOUND AND CLSERME4_FOUND AND FGLIB5_FOUND) - option(HAVE_PCO_CL "Camera: CameraLink-based pco" ON) - - if (HAVE_PCO_CL) - list(APPEND uca_SRCS cameras/uca-pco-camera.c) - list(APPEND uca_HDRS cameras/uca-pco-camera.h) - list(APPEND cameras "Pco") - - set(uca_LIBS ${uca_LIBS} - ${PCO_LIBRARIES} - ${CLSERME4_LIBRARY} - ${FGLIB5_LIBRARY}) - - include_directories( - ${PCO_INCLUDE_DIRS} - ${CLSERME4_INCLUDE_DIR} - ${FGLIB5_INCLUDE_DIR}) - endif() -endif() - -if (IPE_FOUND) - option(HAVE_UFO_CAMERA "Camera: Custom based on Xilinx FPGA" ON) - - if (HAVE_UFO_CAMERA) - list(APPEND uca_SRCS cameras/uca-ufo-camera.c) - list(APPEND uca_HDRS cameras/uca-ufo-camera.h) - list(APPEND cameras "Ufo") - - set(uca_LIBS ${uca_LIBS} ${IPE_LIBRARIES}) - - include_directories(${IPE_INCLUDE_DIRS}) - endif() -endif() - -if (PYLON_FOUND) - option(HAVE_PYLON_CAMERA "Camera: Pylon based (Basler)" ON) - - if (HAVE_PYLON_CAMERA) - list(APPEND uca_SRCS cameras/uca-pylon-camera.c) - list(APPEND uca_HDRS cameras/uca-pylon-camera.h) - list(APPEND cameras "Pylon") - set(uca_LIBS ${uca_LIBS} ${LIBPYLONCAM_LIBRARIES}) - - include_directories(${LIBPYLONCAM_INCLUDEDIR}) - link_directories(${LIBPYLONCAM_LIBDIR}) - endif() -endif() - -if (DEXELA_FOUND) - option(HAVE_DEXELA_CL "Camera: CameraLink-based dexela detector" ON) - - if (HAVE_DEXELA_CL) - list(APPEND uca_SRCS cameras/uca-dexela-camera.c) - list(APPEND uca_HDRS cameras/uca-dexela-camera.h) - list(APPEND cameras "Ufo") - - set(uca_LIBS ${uca_LIBS} ${DEXELA_LIBRARIES}) - - include_directories(${DEXELA_INCLUDE_DIRS}) - endif() -endif() - -if (HAVE_MOCK_CAMERA) - list(APPEND uca_SRCS cameras/uca-mock-camera.c) - list(APPEND uca_HDRS cameras/uca-mock-camera.h) - list(APPEND cameras "Mock") -endif() + uca-camera.h + uca-plugin-manager.h) # --- Generate enum file add_custom_command( @@ -132,9 +17,9 @@ add_custom_command( COMMAND ${GLIB2_MKENUMS} ARGS --template uca-enums.h.template - ${uca_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.h + ${UCA_ENUM_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.h WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${uca_HDRS} + DEPENDS ${UCA_ENUM_HDRS} ${CMAKE_CURRENT_SOURCE_DIR}/uca-enums.h.template) add_custom_command( @@ -142,39 +27,94 @@ add_custom_command( COMMAND ${GLIB2_MKENUMS} ARGS --template uca-enums.c.template - ${uca_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.c + ${UCA_ENUM_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${uca_HDRS} + DEPENDS ${UCA_ENUM_HDRS} ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.h ${CMAKE_CURRENT_SOURCE_DIR}/uca-enums.c.template ) -# --- Configure step + +# --- Configure --------------------------------------------------------------- + +find_program(INTROSPECTION_SCANNER "g-ir-scanner") +find_program(INTROSPECTION_COMPILER "g-ir-compiler") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config.h) + ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +set(prefix ${CMAKE_INSTALL_PREFIX}) +if (CI_INSTALL_PREFIX) + set(prefix ${CI_INSTALL_PREFIX}) +endif() + +set(exec_prefix "\${prefix}") +set(libdir ${prefix}/${LIB_INSTALL_DIR}) +set(includedir "\${prefix}/include") +set(VERSION ${UCA_VERSION_STRING}) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/uca.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/uca.pc" @ONLY IMMEDIATE) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/cameras - ${GLIB2_INCLUDE_DIRS} - ${GOBJECT2_INCLUDE_DIRS}) # --- Build target ------------------------------------------------------------ -add_definitions("-std=c99 -Wall") -add_library(uca SHARED - ${uca_SRCS} +add_library(uca SHARED + ${uca_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/uca-enums.c) set_target_properties(uca PROPERTIES VERSION ${UCA_ABI_VERSION} SOVERSION ${UCA_VERSION_MAJOR}) -target_link_libraries(uca - ${uca_LIBS}) +target_link_libraries(uca ${UCA_DEPS}) + + +# --- Build introspection files ----------------------------------------------- + +if (INTROSPECTION_SCANNER AND INTROSPECTION_COMPILER) + option(WITH_GIR "Build introspection files" ON) + + if (WITH_GIR) + set(GIR_PREFIX "Uca-${UCA_ABI_VERSION}") + set(GIR_XML "${GIR_PREFIX}.gir") + set(GIR_TYPELIB "${GIR_PREFIX}.typelib") + set(_gir_input) + + foreach(_src ${uca_SRCS} ${uca_HDRS}) + list(APPEND _gir_input "${CMAKE_CURRENT_SOURCE_DIR}/${_src}") + endforeach() + + add_custom_command(OUTPUT ${GIR_XML} + COMMAND ${INTROSPECTION_SCANNER} + --namespace=Uca + --nsversion=${UCA_ABI_VERSION} + --library=uca + --no-libtool + --include=GObject-2.0 + --include=GModule-2.0 + --output ${GIR_XML} + --warn-all + ${_gir_input} + DEPENDS ${uca_SRCS} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_command(OUTPUT ${GIR_TYPELIB} + COMMAND ${INTROSPECTION_COMPILER} + -o ${GIR_TYPELIB} + ${GIR_XML} + DEPENDS ${GIR_XML} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_target(gir ALL DEPENDS ${GIR_XML} ${GIR_TYPELIB}) + add_dependencies(gir uca) + + endif() +endif() + # --- Build documentation ----------------------------------------------------- + pkg_check_modules(GTK_DOC gtk-doc) if(GTK_DOC_FOUND) @@ -185,11 +125,12 @@ if(GTK_DOC_FOUND) set(docs_out "${docs_dir}/reference") file(MAKE_DIRECTORY ${docs_out}) - set(reference_files + set(reference_files "${docs_out}/index.html" "${docs_out}/api-index-full.html" "${docs_out}/ch01.html" "${docs_out}/UcaCamera.html" + "${docs_out}/UcaPluginManager.html" "${docs_out}/style.css" "${docs_out}/uca.devhelp2" "${docs_out}/home.png" @@ -197,18 +138,6 @@ if(GTK_DOC_FOUND) "${docs_out}/right.png" "${docs_out}/up.png") - # Put in uca-docs.xml and uca.types all cameras that are built - set(_xml_doc_input) - set(_types_input) - foreach (_cam ${cameras}) - # add camera to the installed documentation - list(APPEND reference_files "${docs_out}/Uca${_cam}Camera.html") - - string(TOLOWER ${_cam} _cam) - set(_xml_doc_input "${_xml_doc_input}\n<xi:include href=\"xml/uca-${_cam}-camera.xml\"/>") - set(_types_input "${_types_input}\nuca_${_cam}_camera_get_type") - endforeach() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/uca-docs.xml.in ${docs_out}/uca-docs.xml) @@ -264,42 +193,36 @@ endif() # --- Install target ---------------------------------------------------------- -# check for 64 bit -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(LIB_INSTALL_DIR "lib64") -else() - set(LIB_INSTALL_DIR "lib") -endif() install(TARGETS uca - LIBRARY DESTINATION ${LIB_INSTALL_DIR}) + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + COMPONENT libraries) -install(FILES ${uca_HDRS} - DESTINATION include/uca) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/uca.pc + DESTINATION lib/pkgconfig + COMPONENT libraries) -# --- install pkg-config file -set(prefix ${CMAKE_INSTALL_PREFIX}) -if (CI_INSTALL_PREFIX) - set(prefix ${CI_INSTALL_PREFIX}) -endif() +install(FILES ${uca_HDRS} + DESTINATION include/uca + COMPONENT headers) -set(exec_prefix "\${prefix}") -set(libdir ${prefix}/${LIB_INSTALL_DIR}) -set(includedir "\${prefix}/include") -set(VERSION ${UCA_VERSION_STRING}) +if(WITH_GIR) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GIR_XML} + DESTINATION share/gir-1.0 + COMPONENT libraries) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/uca.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/uca.pc" @ONLY IMMEDIATE) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GIR_TYPELIB} + DESTINATION ${LIB_INSTALL_DIR}/girepository-1.0 + COMPONENT libraries) +endif() -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/uca.pc DESTINATION lib/pkgconfig) +# --- Generate package description -------------------------------------------- set(CPACK_PACKAGE_DESCRIPTION "Unified Camera Access library") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Abstract interface for different camera classes and frame grabber devices") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GObject-based library for accessing scientific cameras") set(CPACK_PACKAGE_NAME "libuca") -# --- Distro specific -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1)") - # this doesn't work when building RPMs on Jenkins set(CPACK_SET_DESTDIR ON) @@ -310,13 +233,12 @@ set(CPACK_PACKAGE_VERSION_MINOR ${UCA_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${UCA_VERSION_PATCH}) set(VERSION ${UCA_VERSION_STRING}) -set(CPACK_GENERATOR "DEB;RPM;") +set(CPACK_GENERATOR "RPM;") + set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES ".git" "tags" ".bzr" ".swp") set(CPACK_SOURCE_PACKAGE_FILE_NAME "libuca-${UCA_VERSION_STRING}" CACHE INTERNAL "tarball basename") -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${UCA_VERSION_STRING}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}") +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${UCA_VERSION_STRING}-${CMAKE_SYSTEM_PROCESSOR}") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../libuca.spec.in" - "${CMAKE_CURRENT_BINARY_DIR}/../libuca.spec" @ONLY IMMEDIATE) include(CPack) diff --git a/src/uca-camera.c b/src/uca-camera.c index d9d2d6a..53b2d7a 100644 --- a/src/uca-camera.c +++ b/src/uca-camera.c @@ -15,35 +15,19 @@ with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA */ +/** + * SECTION:uca-camera + * @Short_description: Base class representing a camera + * @Title: UcaCamera + * + * UcaCamera is the base camera from which a real hardware camera derives from. + */ + #include <glib.h> #include "config.h" #include "uca-camera.h" #include "uca-enums.h" -#ifdef HAVE_PCO_CL -#include "cameras/uca-pco-camera.h" -#endif - -#ifdef HAVE_PYLON_CAMERA -#include "cameras/uca-pylon-camera.h" -#endif - -#ifdef HAVE_MOCK_CAMERA -#include "cameras/uca-mock-camera.h" -#endif - -#ifdef HAVE_UFO_CAMERA -#include "cameras/uca-ufo-camera.h" -#endif - -#ifdef HAVE_PHOTON_FOCUS -#include "cameras/uca-pf-camera.h" -#endif - -#ifdef HAVE_DEXELA_CL -#include "cameras/uca-dexela-camera.h" -#endif - #define UCA_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_CAMERA, UcaCameraPrivate)) G_DEFINE_TYPE(UcaCamera, uca_camera, G_TYPE_OBJECT) @@ -52,7 +36,7 @@ G_DEFINE_TYPE(UcaCamera, uca_camera, G_TYPE_OBJECT) * UcaCameraTrigger: * @UCA_CAMERA_TRIGGER_AUTO: Trigger automatically * @UCA_CAMERA_TRIGGER_EXTERNAL: Trigger from an external source - * @UCA_CAMERA_TRIGGER_INTERNAL: Trigger internally from software using + * @UCA_CAMERA_TRIGGER_SOFTWARE: Trigger from software using * #uca_camera_trigger */ @@ -63,33 +47,31 @@ G_DEFINE_TYPE(UcaCamera, uca_camera, G_TYPE_OBJECT) * @UCA_CAMERA_ERROR_NOT_RECORDING: Camera is not recording * @UCA_CAMERA_ERROR_NO_GRAB_FUNC: No grab callback was set * @UCA_CAMERA_ERROR_NOT_IMPLEMENTED: Virtual function is not implemented + * @UCA_CAMERA_ERROR_END_OF_STREAM: Data stream has ended. */ GQuark uca_camera_error_quark() { - return g_quark_from_static_string("uca-camera-error-quark"); + return g_quark_from_static_string ("uca-camera-error-quark"); } -static gchar *uca_camera_types[] = { -#ifdef HAVE_PCO_CL - "pco", -#endif -#ifdef HAVE_PYLON_CAMERA - "pylon", -#endif -#ifdef HAVE_MOCK_CAMERA - "mock", -#endif -#ifdef HAVE_UFO_CAMERA - "ufo", -#endif -#ifdef HAVE_PHOTON_FOCUS - "pf", -#endif -#ifdef HAVE_DEXELA_CL - "dexela", -#endif - NULL -}; +/** + * UcaUnit: + * @UCA_UNIT_NA: Not applicable + * @UCA_UNIT_METER: Length in SI meter + * @UCA_UNIT_SECOND: Time in SI second + * @UCA_UNIT_PIXEL: Number of pixels in one dimension + * @UCA_UNIT_DEGREE_CELSIUS: Temperature in degree Celsius + * @UCA_UNIT_COUNT: Generic number + * + * Units should be registered by camera implementations using + * uca_camera_register_unit() and can be queried by client programs with + * uca_camera_get_unit(). + */ + +GQuark uca_unit_quark () +{ + return g_quark_from_static_string ("uca-unit-quark"); +} enum { LAST_SIGNAL @@ -112,6 +94,7 @@ const gchar *uca_camera_props[N_BASE_PROPERTIES] = { "sensor-max-frame-rate", "trigger-mode", "exposure-time", + "frames-per-second", "roi-x0", "roi-y0", "roi-width", @@ -120,6 +103,7 @@ const gchar *uca_camera_props[N_BASE_PROPERTIES] = { "roi-height-multiplier", "has-streaming", "has-camram-recording", + "recorded-frames", "transfer-asynchronously", "is-recording", "is-readout" @@ -133,7 +117,14 @@ struct _UcaCameraPrivate { gboolean transfer_async; }; -static void uca_camera_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +static void +uca_camera_set_property_unit (GParamSpec *pspec, UcaUnit unit) +{ + g_param_spec_set_qdata (pspec, UCA_UNIT_QUARK, GINT_TO_POINTER (unit)); +} + +static void +uca_camera_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { UcaCameraPrivate *priv = UCA_CAMERA_GET_PRIVATE(object); @@ -147,26 +138,53 @@ static void uca_camera_set_property(GObject *object, guint property_id, const GV priv->transfer_async = g_value_get_boolean(value); break; + case PROP_FRAMES_PER_SECOND: + { + gdouble frames_per_second; + + frames_per_second = g_value_get_double (value); + g_object_set (object, "exposure-time", 1. / frames_per_second, NULL); + } + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } -static void uca_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +static void +uca_camera_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { - UcaCameraPrivate *priv = UCA_CAMERA_GET_PRIVATE(object); + UcaCameraPrivate *priv = UCA_CAMERA_GET_PRIVATE (object); switch (property_id) { case PROP_IS_RECORDING: - g_value_set_boolean(value, priv->is_recording); + g_value_set_boolean (value, priv->is_recording); break; case PROP_IS_READOUT: - g_value_set_boolean(value, priv->is_readout); + g_value_set_boolean (value, priv->is_readout); break; case PROP_TRANSFER_ASYNCHRONOUSLY: - g_value_set_boolean(value, priv->transfer_async); + g_value_set_boolean (value, priv->transfer_async); + break; + + case PROP_TRIGGER_MODE: + g_value_set_enum (value, UCA_CAMERA_TRIGGER_AUTO); + break; + + case PROP_FRAMES_PER_SECOND: + { + gdouble exposure_time; + + g_object_get (object, "exposure-time", &exposure_time, NULL); + g_value_set_double (value, 1. / exposure_time); + } + break; + + case PROP_RECORDED_FRAMES: + g_value_set_uint (value, 0); break; default: @@ -174,11 +192,19 @@ static void uca_camera_get_property(GObject *object, guint property_id, GValue * } } -static void uca_camera_class_init(UcaCameraClass *klass) +static void +uca_camera_finalize (GObject *object) +{ + G_OBJECT_CLASS (uca_camera_parent_class)->finalize (object); +} + +static void +uca_camera_class_init (UcaCameraClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->set_property = uca_camera_set_property; gobject_class->get_property = uca_camera_get_property; + gobject_class->finalize = uca_camera_finalize; klass->start_recording = NULL; klass->stop_recording = NULL; @@ -310,6 +336,13 @@ static void uca_camera_class_init(UcaCameraClass *klass) 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE); + camera_properties[PROP_FRAMES_PER_SECOND] = + g_param_spec_double(uca_camera_props[PROP_FRAMES_PER_SECOND], + "Frames per second", + "Frames per second", + 0.0, G_MAXDOUBLE, 1.0, + G_PARAM_READWRITE); + camera_properties[PROP_HAS_STREAMING] = g_param_spec_boolean(uca_camera_props[PROP_HAS_STREAMING], "Streaming capability", @@ -322,6 +355,20 @@ static void uca_camera_class_init(UcaCameraClass *klass) "Is the camera able to record the data in-camera", FALSE, G_PARAM_READABLE); + /** + * UcaCamera:recorded-frames + * + * Number of frames that are recorded into internal camera memory. + * + * Since: 1.1 + */ + camera_properties[PROP_RECORDED_FRAMES] = + g_param_spec_uint(uca_camera_props[PROP_RECORDED_FRAMES], + "Number of frames recorded into internal camera memory", + "Number of frames recorded into internal camera memory", + 0, G_MAXUINT, 0, + G_PARAM_READABLE); + camera_properties[PROP_TRANSFER_ASYNCHRONOUSLY] = g_param_spec_boolean(uca_camera_props[PROP_TRANSFER_ASYNCHRONOUSLY], "Specify whether data should be transfered asynchronously", @@ -346,7 +393,8 @@ static void uca_camera_class_init(UcaCameraClass *klass) g_type_class_add_private(klass, sizeof(UcaCameraPrivate)); } -static void uca_camera_init(UcaCamera *camera) +static void +uca_camera_init (UcaCamera *camera) { camera->grab_func = NULL; @@ -355,105 +403,21 @@ static void uca_camera_init(UcaCamera *camera) camera->priv->is_readout = FALSE; camera->priv->transfer_async = FALSE; - /* - * This here would be the best place to instantiate the tango server object, - * along these lines: - * - * // I'd prefer if you expose a single C method, so we don't have to - * // compile uca-camera.c with g++ - * tango_handle = tango_server_new(camera); - * - * void tango_server_new(UcaCamera *camera) - * { - * // Do whatever is necessary. In the end you will have some kind of - * // Tango object t which needs to somehow hook up to the properties. A - * // list of all available properties can be enumerated with - * // g_object_class_list_properties(G_OBJECT_CLASS(camera), - * // &n_properties); - * - * // For setting/getting properties, use g_object_get/set_property() or - * // g_object_get/set() whatever is more suitable. - * } - */ -} - -static UcaCamera *uca_camera_new_from_type(const gchar *type, GError **error) -{ -#ifdef HAVE_MOCK_CAMERA - if (!g_strcmp0(type, "mock")) - return UCA_CAMERA(uca_mock_camera_new(error)); -#endif - -#ifdef HAVE_PCO_CL - if (!g_strcmp0(type, "pco")) - return UCA_CAMERA(uca_pco_camera_new(error)); -#endif - -#ifdef HAVE_PYLON_CAMERA - if (!g_strcmp0(type, "pylon")) - return UCA_CAMERA(uca_pylon_camera_new(error)); -#endif - -#ifdef HAVE_UFO_CAMERA - if (!g_strcmp0(type, "ufo")) - return UCA_CAMERA(uca_ufo_camera_new(error)); -#endif - -#ifdef HAVE_PHOTON_FOCUS - if (!g_strcmp0(type, "pf")) - return UCA_CAMERA(uca_pf_camera_new(error)); -#endif - -#ifdef HAVE_DEXELA_CL - if (!g_strcmp0(type, "dexela")) - return UCA_CAMERA(uca_dexela_camera_new(error)); -#endif - - return NULL; -} - -/** - * uca_camera_get_types: - * - * Enumerate all camera types that can be instantiated with uca_camera_new(). - * - * Returns: An array of strings with camera types. The list should be freed with - * g_strfreev(). - */ -gchar **uca_camera_get_types() -{ - return g_strdupv(uca_camera_types); -} - -/** - * uca_camera_new: - * @type: Type name of the camera - * @error: Location to store an error or %NULL - * - * Factory method for instantiating cameras by names listed in - * uca_camera_get_types(). - * - * Returns: A new #UcaCamera of the correct type or %NULL if type was not found - */ -UcaCamera *uca_camera_new(const gchar *type, GError **error) -{ - UcaCamera *camera = NULL; - GError *tmp_error = NULL; - - camera = uca_camera_new_from_type(type, &tmp_error); - - if (tmp_error != NULL) { - g_propagate_error(error, tmp_error); - return NULL; - } - - if ((tmp_error == NULL) && (camera == NULL)) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_FOUND, - "Camera type %s not found", type); - return NULL; - } - - return camera; + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_WIDTH], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_HEIGHT], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_BITDEPTH], UCA_UNIT_COUNT); + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_HORIZONTAL_BINNING], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_VERTICAL_BINNING], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_SENSOR_MAX_FRAME_RATE], UCA_UNIT_COUNT); + uca_camera_set_property_unit (camera_properties[PROP_EXPOSURE_TIME], UCA_UNIT_SECOND); + uca_camera_set_property_unit (camera_properties[PROP_FRAMES_PER_SECOND], UCA_UNIT_COUNT); + uca_camera_set_property_unit (camera_properties[PROP_ROI_X], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_ROI_Y], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_ROI_WIDTH], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_ROI_HEIGHT], UCA_UNIT_PIXEL); + uca_camera_set_property_unit (camera_properties[PROP_ROI_WIDTH_MULTIPLIER], UCA_UNIT_COUNT); + uca_camera_set_property_unit (camera_properties[PROP_ROI_HEIGHT_MULTIPLIER], UCA_UNIT_COUNT); + uca_camera_set_property_unit (camera_properties[PROP_RECORDED_FRAMES], UCA_UNIT_COUNT); } /** @@ -465,38 +429,47 @@ UcaCamera *uca_camera_new(const gchar *type, GError **error) * #UcaCameraGrabFunc callback is set, frames are automatically transfered to * the client program, otherwise you must use uca_camera_grab(). */ -void uca_camera_start_recording(UcaCamera *camera, GError **error) +void +uca_camera_start_recording (UcaCamera *camera, GError **error) { - g_return_if_fail(UCA_IS_CAMERA(camera)); + UcaCameraClass *klass; + GError *tmp_error = NULL; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_return_if_fail (UCA_IS_CAMERA (camera)); - UcaCameraClass *klass = UCA_CAMERA_GET_CLASS(camera); + klass = UCA_CAMERA_GET_CLASS (camera); - g_return_if_fail(klass != NULL); - g_return_if_fail(klass->start_recording != NULL); + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->start_recording != NULL); + + g_static_mutex_lock (&mutex); if (camera->priv->is_recording) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, "Camera is already recording"); - return; + goto start_recording_unlock; } if (camera->priv->transfer_async && (camera->grab_func == NULL)) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NO_GRAB_FUNC, + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NO_GRAB_FUNC, "No grab callback function set"); - return; + goto start_recording_unlock; } - GError *tmp_error = NULL; (*klass->start_recording)(camera, &tmp_error); if (tmp_error == NULL) { camera->priv->is_readout = FALSE; camera->priv->is_recording = TRUE; /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ - g_object_notify(G_OBJECT(camera), "is-recording"); + g_object_notify (G_OBJECT (camera), "is-recording"); } else - g_propagate_error(error, tmp_error); + g_propagate_error (error, tmp_error); + +start_recording_unlock: + g_static_mutex_unlock (&mutex); } /** @@ -506,32 +479,41 @@ void uca_camera_start_recording(UcaCamera *camera, GError **error) * * Stop recording. */ -void uca_camera_stop_recording(UcaCamera *camera, GError **error) +void +uca_camera_stop_recording (UcaCamera *camera, GError **error) { - g_return_if_fail(UCA_IS_CAMERA(camera)); + UcaCameraClass *klass; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; - UcaCameraClass *klass = UCA_CAMERA_GET_CLASS(camera); + g_return_if_fail (UCA_IS_CAMERA (camera)); - g_return_if_fail(klass != NULL); - g_return_if_fail(klass->stop_recording != NULL); + klass = UCA_CAMERA_GET_CLASS (camera); - if (!camera->priv->is_recording) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, - "Camera is not recording"); - return; - } + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->stop_recording != NULL); - GError *tmp_error = NULL; - (*klass->stop_recording)(camera, &tmp_error); + g_static_mutex_lock (&mutex); - if (tmp_error == NULL) { - camera->priv->is_readout = FALSE; - camera->priv->is_recording = FALSE; - /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ - g_object_notify(G_OBJECT(camera), "is-recording"); + if (!camera->priv->is_recording) { + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, + "Camera is not recording"); } - else - g_propagate_error(error, tmp_error); + else { + GError *tmp_error = NULL; + + (*klass->stop_recording)(camera, &tmp_error); + + if (tmp_error == NULL) { + camera->priv->is_readout = FALSE; + camera->priv->is_recording = FALSE; + /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ + g_object_notify (G_OBJECT (camera), "is-recording"); + } + else + g_propagate_error (error, tmp_error); + } + + g_static_mutex_unlock (&mutex); } /** @@ -544,31 +526,84 @@ void uca_camera_stop_recording(UcaCamera *camera, GError **error) * uca_camera_stop_recording(). Frames have to be picked up with * ufo_camera_grab(). */ -void uca_camera_start_readout(UcaCamera *camera, GError **error) +void +uca_camera_start_readout (UcaCamera *camera, GError **error) { - g_return_if_fail(UCA_IS_CAMERA(camera)); + UcaCameraClass *klass; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_return_if_fail (UCA_IS_CAMERA(camera)); - UcaCameraClass *klass = UCA_CAMERA_GET_CLASS(camera); + klass = UCA_CAMERA_GET_CLASS(camera); - g_return_if_fail(klass != NULL); - g_return_if_fail(klass->start_readout != NULL); + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->start_readout != NULL); + + g_static_mutex_lock (&mutex); if (camera->priv->is_recording) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, - "Camera is still recording"); - return; + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, + "Camera is still recording"); + } + else { + GError *tmp_error = NULL; + + (*klass->start_readout) (camera, &tmp_error); + + if (tmp_error == NULL) { + camera->priv->is_readout = TRUE; + /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ + g_object_notify (G_OBJECT (camera), "is-readout"); + } + else + g_propagate_error (error, tmp_error); } - GError *tmp_error = NULL; - (*klass->start_readout)(camera, &tmp_error); + g_static_mutex_unlock (&mutex); +} - if (tmp_error == NULL) { - camera->priv->is_readout = TRUE; - /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ - g_object_notify(G_OBJECT(camera), "is-readout"); +/** + * uca_camera_stop_readout: + * @camera: A #UcaCamera object + * @error: Location to store a #UcaCameraError error or %NULL + * + * Stop reading out frames. + * + * Since: 1.1 + */ +void +uca_camera_stop_readout (UcaCamera *camera, GError **error) +{ + UcaCameraClass *klass; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_return_if_fail (UCA_IS_CAMERA(camera)); + + klass = UCA_CAMERA_GET_CLASS(camera); + + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->start_readout != NULL); + + g_static_mutex_lock (&mutex); + + if (camera->priv->is_recording) { + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, + "Camera is still recording"); } - else - g_propagate_error(error, tmp_error); + else { + GError *tmp_error = NULL; + + (*klass->stop_readout) (camera, &tmp_error); + + if (tmp_error == NULL) { + camera->priv->is_readout = FALSE; + g_object_notify (G_OBJECT (camera), "is-readout"); + } + else + g_propagate_error (error, tmp_error); + } + + g_static_mutex_unlock (&mutex); } /** @@ -579,7 +614,8 @@ void uca_camera_start_readout(UcaCamera *camera, GError **error) * * Set the grab function that is called whenever a frame is readily transfered. */ -void uca_camera_set_grab_func(UcaCamera *camera, UcaCameraGrabFunc func, gpointer user_data) +void +uca_camera_set_grab_func(UcaCamera *camera, UcaCameraGrabFunc func, gpointer user_data) { camera->grab_func = func; camera->user_data = user_data; @@ -595,22 +631,27 @@ void uca_camera_set_grab_func(UcaCamera *camera, UcaCameraGrabFunc func, gpointe * You must have called uca_camera_start_recording() before, otherwise you will * get a #UCA_CAMERA_ERROR_NOT_RECORDING error. */ -void uca_camera_trigger(UcaCamera *camera, GError **error) +void +uca_camera_trigger (UcaCamera *camera, GError **error) { - g_return_if_fail(UCA_IS_CAMERA(camera)); + UcaCameraClass *klass; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; - UcaCameraClass *klass = UCA_CAMERA_GET_CLASS(camera); + g_return_if_fail (UCA_IS_CAMERA (camera)); - g_return_if_fail(klass != NULL); - g_return_if_fail(klass->trigger != NULL); + klass = UCA_CAMERA_GET_CLASS (camera); - if (!camera->priv->is_recording) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, - "Camera is not recording"); - return; - } + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->trigger != NULL); + + g_static_mutex_lock (&mutex); - (*klass->trigger)(camera, error); + if (!camera->priv->is_recording) + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, "Camera is not recording"); + else + (*klass->trigger) (camera, error); + + g_static_mutex_unlock (&mutex); } /** @@ -626,23 +667,96 @@ void uca_camera_trigger(UcaCamera *camera, GError **error) * * You must have called uca_camera_start_recording() before, otherwise you will * get a #UCA_CAMERA_ERROR_NOT_RECORDING error. + * + * If *data is %NULL after returning from uca_camera_grab() and error is also + * %NULL, the data stream has ended. For example, with cameras that support + * in-camera memory, all frames have been transfered. */ -void uca_camera_grab(UcaCamera *camera, gpointer *data, GError **error) +void +uca_camera_grab (UcaCamera *camera, gpointer *data, GError **error) { - g_return_if_fail(UCA_IS_CAMERA(camera)); + UcaCameraClass *klass; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_return_if_fail (UCA_IS_CAMERA(camera)); + + klass = UCA_CAMERA_GET_CLASS (camera); + + g_return_if_fail (klass != NULL); + g_return_if_fail (klass->grab != NULL); + g_return_if_fail (data != NULL); + + g_static_mutex_lock (&mutex); + + if (!camera->priv->is_recording && !camera->priv->is_readout) + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, "Camera is neither recording nor in readout mode"); + else + (*klass->grab) (camera, data, error); - UcaCameraClass *klass = UCA_CAMERA_GET_CLASS(camera); + g_static_mutex_unlock (&mutex); +} - g_return_if_fail(klass != NULL); - g_return_if_fail(klass->grab != NULL); - g_return_if_fail(data != NULL); +/** + * uca_camera_register_unit: + * @camera: A #UcaCamera object + * @prop_name: Name of a property + * @unit: #UcaUnit + * + * Associates @prop_name of @camera with a specific @unit. + * + * Since: 1.1 + */ +void +uca_camera_register_unit (UcaCamera *camera, + const gchar *prop_name, + UcaUnit unit) +{ + UcaCameraClass *klass; + GObjectClass *oclass; + GParamSpec *pspec; - if (!camera->priv->is_recording && !camera->priv->is_readout) { - g_set_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, - "Camera is neither recording nor in readout mode"); + klass = UCA_CAMERA_GET_CLASS (camera); + oclass = G_OBJECT_CLASS (klass); + pspec = g_object_class_find_property (oclass, prop_name); + + if (pspec == NULL) { + g_warning ("Camera does not have property `%s'", prop_name); return; } - (*klass->grab)(camera, data, error); + uca_camera_set_property_unit (pspec, unit); +} + +/** + * uca_camera_get_unit: + * @camera: A #UcaCamera object + * @prop_name: Name of a property + * + * Returns the unit associated with @prop_name of @camera. + * + * Returns: A #UcaUnit value associated with @prop_name. If there is none, the + * value will be #UCA_UNIT_NA. + * Since: 1.1 + */ +UcaUnit +uca_camera_get_unit (UcaCamera *camera, + const gchar *prop_name) +{ + UcaCameraClass *klass; + GObjectClass *oclass; + GParamSpec *pspec; + gpointer data; + + klass = UCA_CAMERA_GET_CLASS (camera); + oclass = G_OBJECT_CLASS (klass); + pspec = g_object_class_find_property (oclass, prop_name); + + if (pspec == NULL) { + g_warning ("Camera does not have property `%s'", prop_name); + return UCA_UNIT_NA; + } + + data = g_param_spec_get_qdata (pspec, UCA_UNIT_QUARK); + return data == NULL ? UCA_UNIT_NA : GPOINTER_TO_INT (data); } diff --git a/src/uca-camera.h b/src/uca-camera.h index 8924fa8..87996c6 100644 --- a/src/uca-camera.h +++ b/src/uca-camera.h @@ -29,23 +29,36 @@ G_BEGIN_DECLS #define UCA_IS_CAMERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_CAMERA)) #define UCA_CAMERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_CAMERA, UcaCameraClass)) -#define UCA_CAMERA_ERROR uca_camera_error_quark() +#define UCA_CAMERA_ERROR uca_camera_error_quark() +#define UCA_UNIT_QUARK uca_unit_quark() + GQuark uca_camera_error_quark(void); +GQuark uca_unit_quark(void); typedef enum { UCA_CAMERA_ERROR_NOT_FOUND, UCA_CAMERA_ERROR_RECORDING, UCA_CAMERA_ERROR_NOT_RECORDING, UCA_CAMERA_ERROR_NO_GRAB_FUNC, - UCA_CAMERA_ERROR_NOT_IMPLEMENTED + UCA_CAMERA_ERROR_NOT_IMPLEMENTED, + UCA_CAMERA_ERROR_END_OF_STREAM } UcaCameraError; typedef enum { UCA_CAMERA_TRIGGER_AUTO, - UCA_CAMERA_TRIGGER_INTERNAL, + UCA_CAMERA_TRIGGER_SOFTWARE, UCA_CAMERA_TRIGGER_EXTERNAL } UcaCameraTrigger; +typedef enum { + UCA_UNIT_NA, + UCA_UNIT_METER, + UCA_UNIT_SECOND, + UCA_UNIT_PIXEL, + UCA_UNIT_DEGREE_CELSIUS, + UCA_UNIT_COUNT +} UcaUnit; + typedef struct _UcaCamera UcaCamera; typedef struct _UcaCameraClass UcaCameraClass; typedef struct _UcaCameraPrivate UcaCameraPrivate; @@ -63,6 +76,7 @@ enum { PROP_SENSOR_MAX_FRAME_RATE, PROP_TRIGGER_MODE, PROP_EXPOSURE_TIME, + PROP_FRAMES_PER_SECOND, PROP_ROI_X, PROP_ROI_Y, PROP_ROI_WIDTH, @@ -71,6 +85,7 @@ enum { PROP_ROI_HEIGHT_MULTIPLIER, PROP_HAS_STREAMING, PROP_HAS_CAMRAM_RECORDING, + PROP_RECORDED_FRAMES, /* These properties are handled internally */ PROP_TRANSFER_ASYNCHRONOUSLY, @@ -110,24 +125,39 @@ struct _UcaCameraClass { GObjectClass parent; void (*start_recording) (UcaCamera *camera, GError **error); - void (*stop_recording) (UcaCamera *camera, GError **error); - void (*start_readout) (UcaCamera *camera, GError **error); - void (*trigger) (UcaCamera *camera, GError **error); - void (*grab) (UcaCamera *camera, gpointer *data, GError **error); + void (*stop_recording) (UcaCamera *camera, GError **error); + void (*start_readout) (UcaCamera *camera, GError **error); + void (*stop_readout) (UcaCamera *camera, GError **error); + void (*trigger) (UcaCamera *camera, GError **error); + void (*grab) (UcaCamera *camera, gpointer *data, GError **error); void (*recording_started) (UcaCamera *camera); void (*recording_stopped) (UcaCamera *camera); }; -gchar **uca_camera_get_types(); -UcaCamera *uca_camera_new(const gchar *type, GError **error); - -void uca_camera_start_recording(UcaCamera *camera, GError **error); -void uca_camera_stop_recording(UcaCamera *camera, GError **error); -void uca_camera_start_readout(UcaCamera *camera, GError **error); -void uca_camera_trigger(UcaCamera *camera, GError **error); -void uca_camera_grab(UcaCamera *camera, gpointer *data, GError **error); -void uca_camera_set_grab_func(UcaCamera *camera, UcaCameraGrabFunc func, gpointer user_data); +UcaCamera * uca_camera_new (const gchar *type, + GError **error); +void uca_camera_start_recording (UcaCamera *camera, + GError **error); +void uca_camera_stop_recording (UcaCamera *camera, + GError **error); +void uca_camera_start_readout (UcaCamera *camera, + GError **error); +void uca_camera_stop_readout (UcaCamera *camera, + GError **error); +void uca_camera_trigger (UcaCamera *camera, + GError **error); +void uca_camera_grab (UcaCamera *camera, + gpointer *data, + GError **error); +void uca_camera_set_grab_func (UcaCamera *camera, + UcaCameraGrabFunc func, + gpointer user_data); +void uca_camera_register_unit (UcaCamera *camera, + const gchar *prop_name, + UcaUnit unit); +UcaUnit uca_camera_get_unit (UcaCamera *camera, + const gchar *prop_name); GType uca_camera_get_type(void); diff --git a/src/uca-docs.xml.in b/src/uca-docs.xml.in index 43a830d..093fd36 100644 --- a/src/uca-docs.xml.in +++ b/src/uca-docs.xml.in @@ -16,8 +16,8 @@ <chapter> <title>Unified Camera Access</title> - <xi:include href="xml/uca-camera.xml"/> - ${_xml_doc_input} + <xi:include href="xml/uca-camera.xml"/> + <xi:include href="xml/uca-plugin-manager.xml"/> </chapter> <index id="api-index-full"> diff --git a/src/uca-plugin-manager.c b/src/uca-plugin-manager.c new file mode 100644 index 0000000..e99f478 --- /dev/null +++ b/src/uca-plugin-manager.c @@ -0,0 +1,345 @@ +/* Copyright (C) 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 */ + +/** + * SECTION:uca-plugin-manager + * @Short_description: Load an #UcaFilter from a shared object + * @Title: UcaPluginManager + * + * The plugin manager opens shared object modules searched for in locations + * specified with uca_plugin_manager_add_path(). A #UcaCamera can be + * instantiated with uca_plugin_manager_new_camera() with a one-to-one mapping + * between filter name xyz and module name libucaxyz.so. Any errors are reported + * as one of #UcaPluginManagerError codes. To get a list of available camera + * names call uca_plugin_manager_get_available_cameras(). + * + * By default, any path listed in the %UCA_CAMERA_PATH environment variable is + * added to the search path. + * + * @Since: 1.1 + */ +#include <gmodule.h> +#include "uca-plugin-manager.h" + +G_DEFINE_TYPE (UcaPluginManager, uca_plugin_manager, G_TYPE_OBJECT) + +#define UCA_PLUGIN_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_PLUGIN_MANAGER, UcaPluginManagerPrivate)) + +struct _UcaPluginManagerPrivate { + GList *search_paths; +}; + +static const gchar *MODULE_PATTERN = "libuca([A-Za-z]+)"; + +typedef UcaCamera * (*GetCameraFunc) (GError **error); + +/** + * UcaPluginManagerError: + * @UCA_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND: The module could not be found + * @UCA_PLUGIN_MANAGER_ERROR_MODULE_OPEN: Module could not be opened + * @UCA_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND: Necessary entry symbol was not + * found + * + * Possible errors that uca_plugin_manager_get_filter() can return. + */ +GQuark +uca_plugin_manager_error_quark (void) +{ + return g_quark_from_static_string ("uca-plugin-manager-error-quark"); +} + +/** + * uca_plugin_manager_new: + * + * Create a plugin manager object to instantiate camera objects. + * + * Return value: A new plugin manager object. + */ +UcaPluginManager * +uca_plugin_manager_new () +{ + return UCA_PLUGIN_MANAGER (g_object_new (UCA_TYPE_PLUGIN_MANAGER, NULL)); +} + +/** + * uca_plugin_manager_add_path: + * @manager: A #UcaPluginManager + * @path: Path to look for camera modules + * + * Add a search path to the plugin manager. + */ +void +uca_plugin_manager_add_path (UcaPluginManager *manager, + const gchar *path) +{ + g_return_if_fail (UCA_IS_PLUGIN_MANAGER (manager)); + + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + UcaPluginManagerPrivate *priv; + + priv = manager->priv; + priv->search_paths = g_list_append (priv->search_paths, + g_strdup (path)); + } +} + +static GList * +get_camera_module_paths (const gchar *path) +{ + GRegex *pattern; + GDir *dir; + GList *result = NULL; + + pattern = g_regex_new (MODULE_PATTERN, 0, 0, NULL); + dir = g_dir_open (path, 0, NULL); + + if (dir != NULL) { + GMatchInfo *match_info = NULL; + const gchar *name = g_dir_read_name (dir); + + while (name != NULL) { + if (g_regex_match (pattern, name, 0, &match_info)) + result = g_list_append (result, g_build_filename (path, name, NULL)); + + g_match_info_free (match_info); + name = g_dir_read_name (dir); + } + } + + return result; +} + +static GList * +scan_search_paths (GList *search_paths) +{ + GList *camera_paths = NULL; + + for (GList *it = g_list_first (search_paths); it != NULL; it = g_list_next (it)) { + camera_paths = g_list_concat (camera_paths, + get_camera_module_paths ((const gchar*) it->data)); + } + + return camera_paths; +} + +static void +transform_camera_module_path_to_name (gchar *path, GList **result) +{ + GRegex *pattern; + GMatchInfo *match_info; + + pattern = g_regex_new (MODULE_PATTERN, 0, 0, NULL); + g_regex_match (pattern, path, 0, &match_info); + + *result = g_list_append (*result, g_match_info_fetch (match_info, 1)); + g_match_info_free (match_info); +} + +static void +list_free_full (GList *list) +{ + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); +} + +/** + * uca_plugin_manager_get_available_cameras: + * @manager: A #UcaPluginManager + * + * Return value: (element-type utf8) (transfer full): A list with strings of + * available camera names. You have to free the individual strings with + * g_list_foreach(list, (GFunc) g_free, NULL) and the list itself with + * g_list_free. + */ +GList * +uca_plugin_manager_get_available_cameras (UcaPluginManager *manager) +{ + UcaPluginManagerPrivate *priv; + GList *camera_paths; + GList *camera_names = NULL; + + g_return_val_if_fail (UCA_IS_PLUGIN_MANAGER (manager), NULL); + + priv = manager->priv; + camera_paths = scan_search_paths (priv->search_paths); + + g_list_foreach (camera_paths, (GFunc) transform_camera_module_path_to_name, &camera_names); + list_free_full (camera_paths); + + return camera_names; +} + +static gchar * +find_camera_module_path (GList *search_paths, const gchar *name) +{ + gchar *result = NULL; + GList *paths; + + paths = scan_search_paths (search_paths); + + for (GList *it = g_list_first (paths); it != NULL; it = g_list_next (it)) { + gchar *path = (gchar *) it->data; + gchar *basename = g_path_get_basename ((gchar *) path); + + if (g_strrstr (basename, name)) { + result = g_strdup (path); + g_free (basename); + break; + } + + g_free (basename); + } + + list_free_full (paths); + return result; +} + +/** + * uca_plugin_manager_get_camera: + * @manager: A #UcaPluginManager + * @name: Name of the camera module, that maps to libuca<name>.so + * @error: Location for a #GError + * + * Create a new camera instance with camera @name. + * + * Returns: (transfer full): A new #UcaCamera object. + */ +UcaCamera * +uca_plugin_manager_get_camera (UcaPluginManager *manager, + const gchar *name, + GError **error) +{ + UcaPluginManagerPrivate *priv; + UcaCamera *camera; + GModule *module; + GetCameraFunc *func; + gchar *module_path; + GError *tmp_error = NULL; + + const gchar *symbol_name = "uca_camera_impl_new"; + + g_return_val_if_fail (UCA_IS_PLUGIN_MANAGER (manager) && (name != NULL), NULL); + + priv = manager->priv; + module_path = find_camera_module_path (priv->search_paths, name); + + if (module_path == NULL) { + g_set_error (error, UCA_PLUGIN_MANAGER_ERROR, UCA_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND, + "Camera module `%s' not found", name); + return NULL; + } + + module = g_module_open (module_path, G_MODULE_BIND_LAZY); + g_free (module_path); + + if (!module) { + g_set_error (error, UCA_PLUGIN_MANAGER_ERROR, UCA_PLUGIN_MANAGER_ERROR_MODULE_OPEN, + "Camera module `%s' could not be opened: %s", name, g_module_error ()); + return NULL; + } + + func = g_malloc0 (sizeof (GetCameraFunc)); + + if (!g_module_symbol (module, symbol_name, (gpointer *) func)) { + g_set_error (error, UCA_PLUGIN_MANAGER_ERROR, UCA_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND, + "%s", g_module_error ()); + g_free (func); + + if (!g_module_close (module)) + g_warning ("%s", g_module_error ()); + + return NULL; + } + + camera = (*func) (&tmp_error); + + if (tmp_error != NULL) { + g_propagate_error (error, tmp_error); + return NULL; + } + + return camera; +} + +static void +uca_plugin_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +uca_plugin_manager_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); + break; + } +} + +static void +uca_plugin_manager_dispose (GObject *object) +{ + G_OBJECT_CLASS (uca_plugin_manager_parent_class)->dispose (object); +} + +static void +uca_plugin_manager_finalize (GObject *object) +{ + UcaPluginManagerPrivate *priv = UCA_PLUGIN_MANAGER_GET_PRIVATE (object); + + g_list_foreach (priv->search_paths, (GFunc) g_free, NULL); + g_list_free (priv->search_paths); + + G_OBJECT_CLASS (uca_plugin_manager_parent_class)->finalize (object); +} + +static void +uca_plugin_manager_class_init (UcaPluginManagerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = uca_plugin_manager_get_property; + gobject_class->set_property = uca_plugin_manager_set_property; + gobject_class->dispose = uca_plugin_manager_dispose; + gobject_class->finalize = uca_plugin_manager_finalize; + + g_type_class_add_private (klass, sizeof (UcaPluginManagerPrivate)); +} + +static void +uca_plugin_manager_init (UcaPluginManager *manager) +{ + UcaPluginManagerPrivate *priv; + const gchar *uca_camera_path; + + manager->priv = priv = UCA_PLUGIN_MANAGER_GET_PRIVATE (manager); + priv->search_paths = NULL; + + uca_camera_path = g_getenv ("UCA_CAMERA_PATH"); + + if (uca_camera_path != NULL) + uca_plugin_manager_add_path (manager, uca_camera_path); + + uca_plugin_manager_add_path (manager, "/usr/lib/uca"); + uca_plugin_manager_add_path (manager, "/usr/lib64/uca"); + uca_plugin_manager_add_path (manager, "/usr/local/lib/uca"); + uca_plugin_manager_add_path (manager, "/usr/local/lib64/uca"); +} diff --git a/src/uca-plugin-manager.h b/src/uca-plugin-manager.h new file mode 100644 index 0000000..6c3ab4e --- /dev/null +++ b/src/uca-plugin-manager.h @@ -0,0 +1,65 @@ +#ifndef __UCA_PLUGIN_MANAGER_H +#define __UCA_PLUGIN_MANAGER_H + +#include <glib-object.h> +#include "uca-camera.h" + +G_BEGIN_DECLS + +#define UCA_TYPE_PLUGIN_MANAGER (uca_plugin_manager_get_type()) +#define UCA_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), UCA_TYPE_PLUGIN_MANAGER, UcaPluginManager)) +#define UCA_IS_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), UCA_TYPE_PLUGIN_MANAGER)) +#define UCA_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), UCA_TYPE_PLUGIN_MANAGER, UcaPluginManagerClass)) +#define UCA_IS_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), UCA_TYPE_PLUGIN_MANAGER)) +#define UCA_PLUGIN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), UCA_TYPE_PLUGIN_MANAGER, UcaPluginManagerClass)) + +#define UCA_PLUGIN_MANAGER_ERROR uca_plugin_manager_error_quark() +GQuark uca_plugin_manager_error_quark(void); + +typedef enum { + UCA_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND, + UCA_PLUGIN_MANAGER_ERROR_MODULE_OPEN, + UCA_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND +} UcaPluginManagerError; + +typedef struct _UcaPluginManager UcaPluginManager; +typedef struct _UcaPluginManagerClass UcaPluginManagerClass; +typedef struct _UcaPluginManagerPrivate UcaPluginManagerPrivate; + +/** + * UcaPluginManager: + * + * Creates #UcaFilter instances by loading corresponding shared objects. The + * contents of the #UcaPluginManager structure are private and should only be + * accessed via the provided API. + */ +struct _UcaPluginManager { + /*< private >*/ + GObject parent_instance; + + UcaPluginManagerPrivate *priv; +}; + +/** + * UcaPluginManagerClass: + * + * #UcaPluginManager class + */ +struct _UcaPluginManagerClass { + /*< private >*/ + GObjectClass parent_class; +}; + +UcaPluginManager *uca_plugin_manager_new (void); +void uca_plugin_manager_add_path (UcaPluginManager *manager, + const gchar *path); +GList *uca_plugin_manager_get_available_cameras + (UcaPluginManager *manager); +UcaCamera *uca_plugin_manager_get_camera (UcaPluginManager *manager, + const gchar *name, + GError **error); +GType uca_plugin_manager_get_type (void); + +G_END_DECLS + +#endif diff --git a/src/uca.types.in b/src/uca.types.in index a9ce408..7526948 100644 --- a/src/uca.types.in +++ b/src/uca.types.in @@ -1,2 +1,2 @@ uca_camera_get_type -${_types_input} +uca_plugin_manager_get_type diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ce45d71..f98def0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,86 +1,8 @@ cmake_minimum_required(VERSION 2.8) -add_definitions("--std=c99 -Wall") +add_executable(test-mock test-mock.c) -# --- Find packages and libraries --------------------------------------------- -find_package(PkgConfig) +target_link_libraries(test-mock uca ${UCA_DEPS}) -pkg_check_modules(GTK2 gtk+-2.0>=2.22) -pkg_check_modules(GTHREAD2 gthread-2.0) -pkg_check_modules(GLIB2 glib-2.0>=2.24 REQUIRED) -pkg_check_modules(GOBJECT2 gobject-2.0>=2.24 REQUIRED) - -#include_directories(${CMAKE_SOURCE_DIR}/src) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/control.glade ${CMAKE_CURRENT_BINARY_DIR}) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.py ${CMAKE_CURRENT_BINARY_DIR}) - -# --- Build targets ----------------------------------------------------------- -include_directories( - ${GLIB2_INCLUDE_DIRS} - ${GOBJECT2_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR}/../src/ - ${CMAKE_CURRENT_SOURCE_DIR}/../src - ) - -if (HAVE_PYLON_CAMERA) - set(GENICAM_ROOT $ENV{PYLON_ROOT}/genicam) - # check for 32/64 bit - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(PYLON_LIB_DIRS $ENV{PYLON_ROOT}/lib64 $ENV{PYLON_ROOT}/bin ${GENICAM_ROOT}/bin/Linux64_x64 - ${GENICAM_ROOT}/bin/Linux64_x64/GenApi/Generic) - else() - set(PYLON_LIB_DIRS $ENV{PYLON_ROOT}/lib64 $ENV{PYLON_ROOT}/bin ${GENICAM_ROOT}/bin/Linux32_i86 - ${GENICAM_ROOT}/bin/Linux32_i86/GenApi/Generic) - endif() - link_directories(${PYLON_LIB_DIRS} ${LIBPYLONCAM_LIBDIR}) -endif() - -add_executable(grab grab.c) -add_executable(grab-async grab-async.c) -add_executable(benchmark benchmark.c) - -target_link_libraries(benchmark uca ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) -target_link_libraries(grab uca ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) -target_link_libraries(grab-async uca ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) - -add_executable(grab_pylon grab_pylon.c) -target_link_libraries(grab_pylon uca ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) - -if (NOT DEFINED WITH_CONTROL_GUI) - set(WITH_CONTROL_GUI TRUE) -endif() - -if (GTK2_FOUND AND WITH_CONTROL_GUI) - include_directories(${GTK2_INCLUDE_DIRS}) - - add_executable(control - control.c - egg-property-cell-renderer.c - egg-property-tree-view.c) - - target_link_libraries(control uca - ${GTK2_LIBRARIES} ${GTHREAD2_LIBRARIES}) - - install(TARGETS control - RUNTIME DESTINATION bin) - - install(FILES control.glade - DESTINATION share/libuca) -endif() - -if (HAVE_MOCK_CAMERA) - add_executable(test-mock test-mock.c) - - target_link_libraries(test-mock - uca - ${GLIB2_LIBRARIES} - ${GOBJECT2_LIBRARIES}) - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gtester.xsl - ${CMAKE_CURRENT_BINARY_DIR}/gtester.xsl) -endif() - -add_executable(test-all test-all.c) -target_link_libraries(test-all uca - ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gtester.xsl + ${CMAKE_CURRENT_BINARY_DIR}/gtester.xsl) diff --git a/test/control.c b/test/control.c deleted file mode 100644 index eaa88e3..0000000 --- a/test/control.c +++ /dev/null @@ -1,336 +0,0 @@ -/* 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 <glib/gprintf.h> -#include <gtk/gtk.h> -#include <gdk/gdk.h> -#include <gdk/gdkkeysyms.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <errno.h> - -#include "config.h" -#include "uca-camera.h" -#include "egg-property-tree-view.h" - - -typedef struct { - gboolean running; - gboolean store; - - guchar *buffer, *pixels; - GdkPixbuf *pixbuf; - GtkWidget *image; - GtkTreeModel *property_model; - UcaCamera *camera; - - GtkStatusbar *statusbar; - guint statusbar_context_id; - - int timestamp; - int width; - int height; - int pixel_size; -} ThreadData; - -typedef struct { - ThreadData *thread_data; - GtkTreeStore *tree_store; -} ValueCellData; - -enum { - COLUMN_NAME = 0, - COLUMN_VALUE, - COLUMN_EDITABLE, - NUM_COLUMNS -}; - - -static void convert_8bit_to_rgb(guchar *output, guchar *input, int width, int height) -{ - for (int i = 0, j = 0; i < width*height; i++) { - output[j++] = input[i]; - output[j++] = input[i]; - output[j++] = input[i]; - } -} - -static void convert_16bit_to_rgb(guchar *output, guchar *input, int width, int height) -{ - guint16 *in = (guint16 *) input; - guint16 min = G_MAXUINT16, max = 0; - gfloat spread = 0.0f; - - for (int i = 0; i < width * height; i++) { - guint16 v = in[i]; - if (v < min) - min = v; - if (v > max) - max = v; - } - - spread = (gfloat) max - min; - - if (spread > 0.0f) { - for (int i = 0, j = 0; i < width*height; i++) { - guchar val = (guint8) (((in[i] - min) / spread) * 255.0f); - output[j++] = val; - output[j++] = val; - output[j++] = val; - } - } -} - -static void *grab_thread(void *args) -{ - ThreadData *data = (ThreadData *) args; - gchar filename[FILENAME_MAX] = {0,}; - gint counter = 0; - - while (data->running) { - uca_camera_grab(data->camera, (gpointer) &data->buffer, NULL); - - if (data->store) { - snprintf(filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter++); - FILE *fp = fopen(filename, "wb"); - fwrite(data->buffer, data->width*data->height, data->pixel_size, fp); - fclose(fp); - } - - /* FIXME: We should actually check if this is really a new frame and - * just do nothing if it is an already displayed one. */ - if (data->pixel_size == 1) - convert_8bit_to_rgb(data->pixels, data->buffer, data->width, data->height); - else if (data->pixel_size == 2) { - convert_16bit_to_rgb(data->pixels, data->buffer, data->width, data->height); - } - - gdk_threads_enter(); - gdk_flush(); - gtk_image_clear(GTK_IMAGE(data->image)); - gtk_image_set_from_pixbuf(GTK_IMAGE(data->image), data->pixbuf); - gtk_widget_queue_draw_area(data->image, 0, 0, data->width, data->height); - gdk_threads_leave(); - } - return NULL; -} - -gboolean on_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) -{ - return FALSE; -} - -void on_destroy(GtkWidget *widget, gpointer data) -{ - ThreadData *td = (ThreadData *) data; - td->running = FALSE; - g_object_unref(td->camera); - gtk_main_quit(); -} - -static void on_toolbutton_run_clicked(GtkWidget *widget, gpointer args) -{ - ThreadData *data = (ThreadData *) args; - - if (data->running) - return; - - GError *error = NULL; - data->running = TRUE; - - uca_camera_start_recording(data->camera, &error); - - if (error != NULL) { - g_printerr("Failed to start recording: %s\n", error->message); - return; - } - - if (!g_thread_create(grab_thread, data, FALSE, &error)) { - g_printerr("Failed to create thread: %s\n", error->message); - return; - } -} - -static void on_toolbutton_stop_clicked(GtkWidget *widget, gpointer args) -{ - ThreadData *data = (ThreadData *) args; - data->running = FALSE; - data->store = FALSE; - GError *error = NULL; - uca_camera_stop_recording(data->camera, &error); - - if (error != NULL) - g_printerr("Failed to stop: %s\n", error->message); -} - -static void on_toolbutton_record_clicked(GtkWidget *widget, gpointer args) -{ - ThreadData *data = (ThreadData *) args; - data->timestamp = (int) time(0); - data->store = TRUE; - GError *error = NULL; - - gtk_statusbar_push(data->statusbar, data->statusbar_context_id, "Recording..."); - - if (data->running != TRUE) { - data->running = TRUE; - uca_camera_start_recording(data->camera, &error); - - if (!g_thread_create(grab_thread, data, FALSE, &error)) - g_printerr("Failed to create thread: %s\n", error->message); - } -} - -static void create_main_window(GtkBuilder *builder, const gchar* camera_name) -{ - static ThreadData td; - - GError *error = NULL; - UcaCamera *camera = uca_camera_new(camera_name, &error); - - if ((camera == NULL) || (error != NULL)) { - g_error("%s\n", error->message); - gtk_main_quit(); - } - - guint bits_per_sample; - g_object_get(camera, - "roi-width", &td.width, - "roi-height", &td.height, - "sensor-bitdepth", &bits_per_sample, - NULL); - - GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); - GtkWidget *image = GTK_WIDGET(gtk_builder_get_object(builder, "image")); - GtkWidget *property_tree_view = egg_property_tree_view_new (G_OBJECT (camera)); - GtkContainer *scrolled_property_window = GTK_CONTAINER (gtk_builder_get_object (builder, "scrolledwindow2")); - - gtk_container_add (scrolled_property_window, property_tree_view); - gtk_widget_show_all (GTK_WIDGET (scrolled_property_window)); - - GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, td.width, td.height); - gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf); - - td.pixel_size = bits_per_sample > 8 ? 2 : 1; - td.image = image; - td.pixbuf = pixbuf; - td.buffer = (guchar *) g_malloc(td.pixel_size * td.width * td.height); - td.pixels = gdk_pixbuf_get_pixels(pixbuf); - td.running = FALSE; - td.statusbar = GTK_STATUSBAR(gtk_builder_get_object(builder, "statusbar")); - td.statusbar_context_id = gtk_statusbar_get_context_id(td.statusbar, "Recording Information"); - td.store = FALSE; - td.camera = camera; - td.property_model = GTK_TREE_MODEL(gtk_builder_get_object(builder, "camera-properties")); - - g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), &td); - g_signal_connect(gtk_builder_get_object(builder, "toolbutton_run"), - "clicked", G_CALLBACK(on_toolbutton_run_clicked), &td); - g_signal_connect(gtk_builder_get_object(builder, "toolbutton_stop"), - "clicked", G_CALLBACK(on_toolbutton_stop_clicked), &td); - g_signal_connect(gtk_builder_get_object(builder, "toolbutton_record"), - "clicked", G_CALLBACK(on_toolbutton_record_clicked), &td); - - gtk_widget_show(image); - gtk_widget_show(window); -} - -static void on_button_proceed_clicked(GtkWidget *widget, gpointer data) -{ - GtkBuilder *builder = GTK_BUILDER(data); - GtkWidget *choice_window = GTK_WIDGET(gtk_builder_get_object(builder, "choice-window")); - GtkTreeView *treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview-cameras")); - GtkListStore *list_store = GTK_LIST_STORE(gtk_builder_get_object(builder, "camera-types")); - - GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); - GList *selected_rows = gtk_tree_selection_get_selected_rows(selection, NULL); - GtkTreeIter iter; - - gtk_widget_destroy(choice_window); - gboolean valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, selected_rows->data); - - if (valid) { - gchar *data; - gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, 0, &data, -1); - create_main_window(builder, data); - g_free(data); - } - - g_list_foreach(selected_rows, (GFunc) gtk_tree_path_free, NULL); - g_list_free(selected_rows); -} - -static void on_treeview_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data) -{ - if (event->keyval == GDK_KEY_Return) - gtk_widget_grab_focus(GTK_WIDGET(data)); -} - -static void create_choice_window(GtkBuilder *builder) -{ - gchar **camera_types = uca_camera_get_types(); - - GtkWidget *choice_window = GTK_WIDGET(gtk_builder_get_object(builder, "choice-window")); - GtkTreeView *treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview-cameras")); - GtkListStore *list_store = GTK_LIST_STORE(gtk_builder_get_object(builder, "camera-types")); - GtkButton *proceed_button = GTK_BUTTON(gtk_builder_get_object(builder, "button-proceed")); - GtkTreeIter iter; - - for (guint i = 0; camera_types[i] != NULL; i++) { - gtk_list_store_append(list_store, &iter); - gtk_list_store_set(list_store, &iter, 0, camera_types[i], -1); - } - - gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter); - - if (valid) { - GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); - gtk_tree_selection_unselect_all(selection); - gtk_tree_selection_select_path(selection, gtk_tree_model_get_path(GTK_TREE_MODEL(list_store), &iter)); - } - - g_strfreev(camera_types); - g_signal_connect(proceed_button, "clicked", G_CALLBACK(on_button_proceed_clicked), builder); - g_signal_connect(treeview, "key-press-event", G_CALLBACK(on_treeview_keypress), proceed_button); - gtk_widget_show_all(GTK_WIDGET(choice_window)); -} - -int main(int argc, char *argv[]) -{ - GError *error = NULL; - - g_thread_init(NULL); - gdk_threads_init(); - gtk_init(&argc, &argv); - - GtkBuilder *builder = gtk_builder_new(); - - if (!gtk_builder_add_from_file(builder, CONTROL_GLADE_PATH, &error)) { - g_print("Error: %s\n", error->message); - return 1; - } - - create_choice_window(builder); - gtk_builder_connect_signals(builder, NULL); - - gdk_threads_enter(); - gtk_main(); - gdk_threads_leave(); - - return 0; -} diff --git a/test/control.glade b/test/control.glade deleted file mode 100644 index d7ba2fc..0000000 --- a/test/control.glade +++ /dev/null @@ -1,302 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.20"/> - <!-- interface-naming-policy project-wide --> - <object class="GtkListStore" id="camera-types"> - <columns> - <!-- column-name name --> - <column type="gchararray"/> - </columns> - </object> - <object class="GtkListStore" id="camera-properties"> - <columns> - <!-- column-name PropertyName --> - <column type="gchararray"/> - <!-- column-name PropertyValue --> - <column type="gchararray"/> - <!-- column-name writeable --> - <column type="gboolean"/> - </columns> - </object> - <object class="GtkWindow" id="window"> - <property name="title" translatable="yes">Camera Control</property> - <property name="default_width">1024</property> - <property name="default_height">768</property> - <signal name="delete_event" handler="on_delete_event"/> - <child> - <object class="GtkVBox" id="vbox1"> - <property name="visible">True</property> - <child> - <object class="GtkMenuBar" id="menubar1"> - <property name="visible">True</property> - <child> - <object class="GtkMenuItem" id="menuitem1"> - <property name="visible">True</property> - <property name="label" translatable="yes">_File</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="menu_file"> - <property name="visible">True</property> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem1"> - <property name="label">gtk-new</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem2"> - <property name="label">gtk-open</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem3"> - <property name="label">gtk-save</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem4"> - <property name="label">gtk-save-as</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separatormenuitem1"> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem_quit"> - <property name="label">gtk-quit</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <signal name="activate" handler="gtk_main_quit"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menuitem4"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Help</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="menu_help"> - <property name="visible">True</property> - <child> - <object class="GtkImageMenuItem" id="imagemenuitem_about"> - <property name="label">gtk-about</property> - <property name="visible">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="toolbar"> - <property name="visible">True</property> - <child> - <object class="GtkToolButton" id="toolbutton_run"> - <property name="visible">True</property> - <property name="label" translatable="yes">Run</property> - <property name="use_underline">True</property> - <property name="stock_id">gtk-media-play</property> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - <child> - <object class="GtkToolButton" id="toolbutton_record"> - <property name="visible">True</property> - <property name="label" translatable="yes">Record</property> - <property name="use_underline">True</property> - <property name="stock_id">gtk-media-record</property> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - <child> - <object class="GtkToolButton" id="toolbutton_stop"> - <property name="visible">True</property> - <property name="label" translatable="yes">Stop</property> - <property name="use_underline">True</property> - <property name="stock_id">gtk-media-stop</property> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHPaned" id="hpaned1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">6</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow1"> - <property name="width_request">300</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport1"> - <property name="visible">True</property> - <property name="resize_mode">queue</property> - <child> - <object class="GtkImage" id="image"> - <property name="visible">True</property> - <property name="stock">gtk-missing-image</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">False</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">True</property> - </packing> - </child> - </object> - <packing> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkStatusbar" id="statusbar"> - <property name="visible">True</property> - <property name="spacing">2</property> - </object> - <packing> - <property name="expand">False</property> - <property name="position">3</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkAdjustment" id="adjustment_scale"> - <property name="value">65535</property> - <property name="lower">1</property> - <property name="upper">65535</property> - <property name="step_increment">1</property> - <property name="page_increment">10</property> - </object> - <object class="GtkWindow" id="choice-window"> - <property name="border_width">6</property> - <child> - <object class="GtkVBox" id="vbox3"> - <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <object class="GtkTreeView" id="treeview-cameras"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="model">camera-types</property> - <child> - <object class="GtkTreeViewColumn" id="treeviewcolumn1"> - <property name="title">Choose camera</property> - <child> - <object class="GtkCellRendererText" id="cellrenderertext1"/> - <attributes> - <attribute name="text">0</attribute> - </attributes> - </child> - </object> - </child> - </object> - <packing> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox1"> - <property name="visible">True</property> - <property name="spacing">6</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="button-cancel"> - <property name="label">gtk-quit</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="gtk_main_quit"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button-proceed"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="padding">6</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> -</interface> diff --git a/test/enum.c b/test/enum.c deleted file mode 100644 index 75ca596..0000000 --- a/test/enum.c +++ /dev/null @@ -1,88 +0,0 @@ -/* 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 <stdio.h> -#include "uca.h" - -int count_dots(const char *s) -{ - int res = 0; - while (*(s++) != '\0') - if (*s == '.') - res++; - return res; -} - -void print_level(int depth) -{ - for (int i = 0; i < depth; i++) - printf("| "); - printf("|-- "); -} - -int main(int argc, char *argv[]) -{ - uca *u = uca_init(NULL); - if (u == NULL) { - printf("Couldn't find a camera\n"); - return 1; - } - - /* take first camera */ - uca_camera *cam = u->cameras; - - const size_t num_bytes = 256; - char string_value[num_bytes]; - uint32_t uint32_value; - uint8_t uint8_value; - - while (cam != NULL) { - for (int i = 0; i < UCA_PROP_LAST; i++) { - uca_property *prop = uca_get_full_property(i); - print_level(count_dots(prop->name)); - printf("%s = ", prop->name); - switch (prop->type) { - case uca_string: - if (uca_cam_get_property(cam, i, string_value, num_bytes) == UCA_NO_ERROR) { - printf("%s ", string_value); - } - else - printf("n/a"); - break; - case uca_uint32t: - if (uca_cam_get_property(cam, i, &uint32_value, 0) == UCA_NO_ERROR) { - printf("%u %s", uint32_value, uca_unit_map[prop->unit]); - } - else - printf("n/a"); - break; - case uca_uint8t: - if (uca_cam_get_property(cam, i, &uint8_value, 0) == UCA_NO_ERROR) { - printf("%u %s", uint8_value, uca_unit_map[prop->unit]); - } - else - printf("n/a"); - break; - } - printf("\n"); - } - cam = cam->next; - } - - uca_destroy(u); - return 0; -} diff --git a/test/grab_pylon.c b/test/grab_pylon.c deleted file mode 100644 index 2f9b5a0..0000000 --- a/test/grab_pylon.c +++ /dev/null @@ -1,96 +0,0 @@ -/* 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 <glib-object.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include "uca-camera.h" - -#define handle_error(errno) {if ((errno) != UCA_NO_ERROR) printf("error at <%s:%i>\n", \ - __FILE__, __LINE__);} - -static UcaCamera *camera = NULL; - -void sigint_handler(int signal) -{ - printf("Closing down libuca\n"); - uca_camera_stop_recording(camera, NULL); - g_object_unref(camera); - exit(signal); -} - -int main(int argc, char *argv[]) -{ - GError *error = NULL; - (void) signal(SIGINT, sigint_handler); - - g_type_init(); - camera = uca_camera_new("pylon", &error); - - if (camera == NULL) { - g_print("Couldn't initialize camera\n"); - return 1; - } - - guint width, height, bits; - g_object_get(G_OBJECT(camera), - "sensor-width", &width, - "sensor-height", &height, - "sensor-bitdepth", &bits, - NULL); - - const int pixel_size = bits == 8 ? 1 : 2; - g_print("allocate buffer %u, %u, %d\n", width, height, pixel_size); - gpointer buffer = g_malloc0(width * height * pixel_size); - - gchar filename[FILENAME_MAX]; - - for (int i = 0; i < 2; i++) { - gint counter = 0; - g_print("Start recording\n"); - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); - - while (counter < 10) { - g_print(" grab frame ... "); - uca_camera_grab(camera, &buffer, &error); - if (error != NULL) { - g_print("\nError: %s\n", error->message); - goto cleanup; - } - g_print("done\n"); - - snprintf(filename, FILENAME_MAX, "frame-%08i.raw", counter++); - FILE *fp = fopen(filename, "wb"); - fwrite(buffer, width*height, pixel_size, fp); - fclose(fp); - //g_usleep(2 * G_USEC_PER_SEC); - } - - g_print("Stop recording\n"); - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); - } - -cleanup: - uca_camera_stop_recording(camera, NULL); - g_object_unref(camera); - g_free(buffer); - - return error != NULL ? 1 : 0; -} diff --git a/test/gtester.xsl b/test/gtester.xsl index e8236d6..f2a5e97 100644 --- a/test/gtester.xsl +++ b/test/gtester.xsl @@ -1,7 +1,7 @@ <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> - + <!-- I can't believe I have to do this --> <!-- Based on this code: http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx @@ -29,19 +29,21 @@ <xsl:template match="/"> <xsl:for-each select="gtester"> <testsuite> + <!-- currently we do not support different test binaries and only take the path + of the first as the testsuite name --> <xsl:attribute name="name"> <xsl:value-of select="testbinary[1]/@path"/> </xsl:attribute> <xsl:attribute name="tests"> - <xsl:value-of select="count(testbinary[1]/testcase)"/> + <xsl:value-of select="count(testbinary/testcase[not(@skipped='1')])"/> </xsl:attribute> <xsl:attribute name="time"> - <xsl:value-of select="sum(testbinary[1]/testcase/duration)"/> + <xsl:value-of select="sum(testbinary/duration)"/> </xsl:attribute> <xsl:attribute name="failures"> - <xsl:value-of select="count(testbinary[1]/testcase/status[@result='failed'])"/> + <xsl:value-of select="count(testbinary/testcase/status[@result='failed'])"/> </xsl:attribute> - <xsl:for-each select="testbinary[1]/testcase"> + <xsl:for-each select="testbinary/testcase[not(@skipped='1')]"> <testcase> <xsl:variable name="classname"> <xsl:call-template name="strreplace"> diff --git a/test/perf-overhead.c b/test/perf-overhead.c deleted file mode 100644 index 20ca6c3..0000000 --- a/test/perf-overhead.c +++ /dev/null @@ -1,143 +0,0 @@ -/* 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 <glib-object.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "uca-camera.h" - -#define handle_error(errno) {if ((errno) != UCA_NO_ERROR) printf("error at <%s:%i>\n", \ - __FILE__, __LINE__);} - -typedef struct { - guint counter; - gsize size; - gpointer destination; -} thread_data; - -static UcaCamera *camera = NULL; - -static void sigint_handler(int signal) -{ - printf("Closing down libuca\n"); - uca_camera_stop_recording(camera, NULL); - g_object_unref(camera); - exit(signal); -} - -static void test_synchronous_operation(UcaCamera *camera) -{ - GError *error = NULL; - guint width, height, bits; - g_object_get(G_OBJECT(camera), - "sensor-width", &width, - "sensor-height", &height, - "sensor-bitdepth", &bits, - NULL); - - const int pixel_size = bits == 8 ? 1 : 2; - const gsize size = width * height * pixel_size; - const guint n_trials = 10000; - gpointer buffer = g_malloc0(size); - - uca_camera_start_recording(camera, &error); - GTimer *timer = g_timer_new(); - - for (guint n = 0; n < n_trials; n++) - uca_camera_grab(camera, &buffer, &error); - - gdouble total_time = g_timer_elapsed(timer, NULL); - g_timer_stop(timer); - - g_print("Synchronous data transfer\n"); - g_print(" Bandwidth: %3.2f MB/s\n", size * n_trials / 1024. / 1024. / total_time); - g_print(" Throughput: %3.2f frames/s\n", n_trials / total_time); - - uca_camera_stop_recording(camera, &error); - g_free(buffer); - g_timer_destroy(timer); -} - -static void grab_func(gpointer data, gpointer user_data) -{ - static GStaticMutex mutex = G_STATIC_MUTEX_INIT; - - thread_data *d = (thread_data *) user_data; - g_memmove(d->destination, data, d->size); - g_static_mutex_lock(&mutex); - d->counter++; - g_static_mutex_unlock(&mutex); -} - -static void test_asynchronous_operation(UcaCamera *camera) -{ - GError *error = NULL; - guint width, height, bits; - - g_object_get(G_OBJECT(camera), - "sensor-width", &width, - "sensor-height", &height, - "sensor-bitdepth", &bits, - NULL); - - const guint pixel_size = bits == 8 ? 1 : 2; - - thread_data d = { - .counter = 0, - .size = width * height * pixel_size, - .destination = g_malloc0(width * height * pixel_size) - }; - - g_object_set(G_OBJECT(camera), - "transfer-asynchronously", TRUE, - NULL); - - uca_camera_set_grab_func(camera, &grab_func, &d); - uca_camera_start_recording(camera, &error); - g_usleep(G_USEC_PER_SEC); - uca_camera_stop_recording(camera, &error); - - g_print("Asynchronous data transfer\n"); - g_print(" Bandwidth: %3.2f MB/s\n", d.size * d.counter / 1024. / 1024.); - g_print(" Throughput: %i frames/s\n", d.counter); - - g_free(d.destination); -} - -int main(int argc, char *argv[]) -{ - GError *error = NULL; - (void) signal(SIGINT, sigint_handler); - - g_type_init(); - camera = uca_camera_new("mock", &error); - - if (camera == NULL) { - g_print("Couldn't initialize camera\n"); - return 1; - } - - test_synchronous_operation(camera); - g_print("\n"); - test_asynchronous_operation(camera); - - g_object_unref(camera); - - return error != NULL ? 1 : 0; -} diff --git a/test/run.py b/test/run.py deleted file mode 100755 index 7982617..0000000 --- a/test/run.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python - -import Tkinter -import threading -import subprocess - -class App(object): - def __init__(self, parent): - self.parent = parent - self.canvas = Tkinter.Canvas(root, width=300, height=300) - self.canvas.pack() - self.rect = self.canvas.create_rectangle(0, 0, 300, 300, fill='yellow') - self.run_button = Tkinter.Button(parent, text='Start grabbing', command=self.run_click) - self.run_button.pack() - - def run_grab(self): - proc = subprocess.Popen(['./grab']) - proc.wait() - self.canvas.itemconfig(self.rect, fill='red') - self.run_button.config(state=Tkinter.NORMAL, text='Close', command=self.parent.destroy) - - def run_click(self): - self.canvas.itemconfig(self.rect, fill='green') - self.run_button.config(state=Tkinter.DISABLED) - thread = threading.Thread(None, self.run_grab) - thread.start() - -if __name__ == '__main__': - root = Tkinter.Tk() - app = App(root) - root.mainloop() diff --git a/test/test-all.c b/test/test-all.c deleted file mode 100644 index 9526d4f..0000000 --- a/test/test-all.c +++ /dev/null @@ -1,214 +0,0 @@ - -#include <glib.h> -#include "uca-camera.h" -#include "cameras/uca-mock-camera.h" - -typedef struct { - UcaCamera *camera; -} Fixture; - -typedef void (*UcaFixtureFunc) (Fixture *fixture, gconstpointer data); - -static void fixture_setup(Fixture *fixture, gconstpointer data) -{ - const gchar *type = (gchar *) data; - GError *error = NULL; - fixture->camera = uca_camera_new(type, &error); - g_assert_no_error(error); - g_assert(fixture->camera); -} - -static void fixture_teardown(Fixture *fixture, gconstpointer data) -{ - g_object_unref(fixture->camera); -} - -static void on_property_change(gpointer instance, GParamSpec *pspec, gpointer user_data) -{ - gboolean *success = (gboolean *) user_data; - *success = TRUE; -} - -static void test_factory() -{ - GError *error = NULL; - UcaCamera *camera = uca_camera_new("fox994m3a0yxmy", &error); - g_assert_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_FOUND); - g_assert(camera == NULL); -} - -static void test_recording(Fixture *fixture, gconstpointer data) -{ - GError *error = NULL; - UcaCamera *camera = UCA_CAMERA(fixture->camera); - - uca_camera_stop_recording(camera, &error); - g_assert_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING); - g_error_free(error); - - error = NULL; - gboolean success = FALSE; - g_signal_connect(G_OBJECT(camera), "notify::is-recording", - (GCallback) on_property_change, &success); - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); - g_assert(success == TRUE); - - success = FALSE; - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); - g_assert(success == TRUE); -} - -static void grab_func(gpointer data, gpointer user_data) -{ - gboolean *success = (gboolean *) user_data; - *success = TRUE; -} - -static void test_recording_async(Fixture *fixture, gconstpointer data) -{ - UcaCamera *camera = UCA_CAMERA(fixture->camera); - - gboolean success = FALSE; - uca_camera_set_grab_func(camera, grab_func, &success); - - gfloat max_frame_rate = 1.0f; - g_object_get(G_OBJECT(camera), - "sensor-max-frame-rate", &max_frame_rate, - NULL); - g_assert(max_frame_rate != 0.0f); - - g_object_set(G_OBJECT(camera), - "transfer-asynchronously", TRUE, - NULL); - - GError *error = NULL; - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); - - const gulong sleep_time = G_USEC_PER_SEC / ((gulong) (max_frame_rate / 2.0f)); - g_usleep(sleep_time); - - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); - g_assert(success == TRUE); -} - -static void test_recording_grab(Fixture *fixture, gconstpointer data) -{ - UcaCamera *camera = UCA_CAMERA(fixture->camera); - GError *error = NULL; - gpointer frame = NULL; - - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); - - uca_camera_grab(camera, &frame, &error); - g_assert_no_error(error); - g_assert(frame != NULL); - - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); -} - -static void test_recording_property(Fixture *fixture, gconstpointer data) -{ - UcaCamera *camera = UCA_CAMERA(fixture->camera); - - gboolean is_recording = FALSE; - uca_camera_start_recording(camera, NULL); - g_object_get(G_OBJECT(camera), - "is-recording", &is_recording, - NULL); - g_assert(is_recording == TRUE); - - uca_camera_stop_recording(camera, NULL); - g_object_get(G_OBJECT(camera), - "is-recording", &is_recording, - NULL); - g_assert(is_recording == FALSE); -} - -static void test_base_properties(Fixture *fixture, gconstpointer data) -{ - UcaCamera *camera = UCA_CAMERA(fixture->camera); - guint n_properties = 0; - GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(camera), &n_properties); - GValue val = {0}; - - for (guint i = 0; i < n_properties; i++) { - g_value_init(&val, properties[i]->value_type); - g_object_get_property(G_OBJECT(camera), properties[i]->name, &val); - g_value_unset(&val); - } - - g_free(properties); -} - -static void test_binnings_properties(Fixture *fixture, gconstpointer data) -{ - UcaCamera *camera = UCA_CAMERA(fixture->camera); - - GValueArray *array = NULL; - g_object_get(G_OBJECT(camera), - "sensor-horizontal-binnings", &array, - NULL); - - GValue *value = g_value_array_get_nth(array, 0); - g_assert(value != NULL); - g_assert(g_value_get_uint(value) == 1); -} - - -int main(int argc, char *argv[]) -{ - g_type_init(); - g_test_init(&argc, &argv, NULL); - g_test_bug_base("http://ufo.kit.edu/ufo/ticket"); - - g_test_add_func("/factory", test_factory); - - gchar **types = NULL; - types = argc > 1 ? argv + 1 : uca_camera_get_types(); - - /* - * paths and test_funcs MUST correspond! - */ - static const gchar *paths[] = { - "/recording", - "/recording/grab", - "/recording/asynchronous", - "/properties/base", - "/properties/recording", - "/properties/binnings", - NULL - }; - - static UcaFixtureFunc test_funcs[] = { - test_recording, - test_recording_grab, - test_recording_async, - test_base_properties, - test_recording_property, - test_binnings_properties, - }; - - for (guint i = 0; i < g_strv_length(types); i++) { - guint j = 0; - - while (paths[j] != NULL) { - gchar *new_path = g_strdup_printf("/%s%s", types[i], paths[j]); - g_test_add(new_path, Fixture, types[i], fixture_setup, test_funcs[j], fixture_teardown); - g_free(new_path); - j++; - } - } - - gint result = g_test_run(); - - if (argc == 1) - g_strfreev(types); - - return result; -} diff --git a/test/test-mock.c b/test/test-mock.c index 7594a3a..85c1ba4 100644 --- a/test/test-mock.c +++ b/test/test-mock.c @@ -1,176 +1,228 @@ #include <glib.h> #include "uca-camera.h" -#include "cameras/uca-mock-camera.h" +#include "uca-plugin-manager.h" typedef struct { - UcaMockCamera *camera; + UcaPluginManager *manager; + UcaCamera *camera; } Fixture; -static void fixture_setup(Fixture *fixture, gconstpointer data) +static void +fixture_setup (Fixture *fixture, gconstpointer data) { GError *error = NULL; - fixture->camera = uca_mock_camera_new(&error); - g_assert(error == NULL); - g_assert(fixture->camera); + + fixture->manager = uca_plugin_manager_new (); + uca_plugin_manager_add_path (fixture->manager, "./src"); + + fixture->camera = uca_plugin_manager_get_camera (fixture->manager, "mock", &error); + g_assert (error == NULL); + g_assert (fixture->camera); } -static void fixture_teardown(Fixture *fixture, gconstpointer data) +static void +fixture_teardown (Fixture *fixture, gconstpointer data) { - g_object_unref(fixture->camera); + g_object_unref (fixture->camera); + g_object_unref (fixture->manager); } -static void on_property_change(gpointer instance, GParamSpec *pspec, gpointer user_data) +static void +on_property_change (gpointer instance, GParamSpec *pspec, gpointer user_data) { gboolean *success = (gboolean *) user_data; *success = TRUE; } -static void test_factory() +static void +test_factory (Fixture *fixture, gconstpointer data) { GError *error = NULL; - UcaCamera *camera = uca_camera_new("fox994m3a0yxmy", &error); - g_assert_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_FOUND); - g_assert(camera == NULL); + UcaCamera *camera = uca_plugin_manager_get_camera (fixture->manager, "fox994m3a0yxmy", &error); + g_assert_error (error, UCA_PLUGIN_MANAGER_ERROR, UCA_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND); + g_assert (camera == NULL); } -static void test_recording(Fixture *fixture, gconstpointer data) +static void +test_recording (Fixture *fixture, gconstpointer data) { GError *error = NULL; - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); - uca_camera_stop_recording(camera, &error); - g_assert_error(error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING); - g_error_free(error); + uca_camera_stop_recording (camera, &error); + g_assert_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING); + g_error_free (error); error = NULL; - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); + uca_camera_start_recording (camera, &error); + g_assert_no_error (error); + uca_camera_stop_recording (camera, &error); + g_assert_no_error (error); } -static void test_recording_signal(Fixture *fixture, gconstpointer data) +static void +test_recording_signal (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); gboolean success = FALSE; - g_signal_connect(G_OBJECT(camera), "notify::is-recording", + g_signal_connect (G_OBJECT (camera), "notify::is-recording", (GCallback) on_property_change, &success); - uca_camera_start_recording(camera, NULL); - g_assert(success == TRUE); + uca_camera_start_recording (camera, NULL); + g_assert (success == TRUE); success = FALSE; - uca_camera_stop_recording(camera, NULL); - g_assert(success == TRUE); + uca_camera_stop_recording (camera, NULL); + g_assert (success == TRUE); } -static void grab_func(gpointer data, gpointer user_data) +static void +grab_func (gpointer data, gpointer user_data) { guint *count = (guint *) user_data; *count += 1; } -static void test_recording_async(Fixture *fixture, gconstpointer data) +static void +test_recording_async (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); guint count = 0; - uca_camera_set_grab_func(camera, grab_func, &count); + uca_camera_set_grab_func (camera, grab_func, &count); - g_object_set(G_OBJECT(camera), + g_object_set (G_OBJECT (camera), "frame-rate", 10.0, "transfer-asynchronously", TRUE, NULL); GError *error = NULL; - uca_camera_start_recording(camera, &error); - g_assert_no_error(error); + uca_camera_start_recording (camera, &error); + g_assert_no_error (error); /* * We sleep for an 1/8 of a second at 10 frames per second, thus we should * record 2 frames. */ - g_usleep(G_USEC_PER_SEC / 8); + g_usleep (G_USEC_PER_SEC / 8); - uca_camera_stop_recording(camera, &error); - g_assert_no_error(error); - g_assert_cmpint(count, ==, 2); + uca_camera_stop_recording (camera, &error); + g_assert_no_error (error); + g_assert_cmpint (count, ==, 2); } -static void test_recording_property(Fixture *fixture, gconstpointer data) +static void +test_recording_property (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); gboolean is_recording = FALSE; - uca_camera_start_recording(camera, NULL); - g_object_get(G_OBJECT(camera), + uca_camera_start_recording (camera, NULL); + g_object_get (G_OBJECT (camera), "is-recording", &is_recording, NULL); - g_assert(is_recording == TRUE); + g_assert (is_recording == TRUE); - uca_camera_stop_recording(camera, NULL); - g_object_get(G_OBJECT(camera), + uca_camera_stop_recording (camera, NULL); + g_object_get (G_OBJECT (camera), "is-recording", &is_recording, NULL); - g_assert(is_recording == FALSE); + g_assert (is_recording == FALSE); } -static void test_base_properties(Fixture *fixture, gconstpointer data) +static void +test_base_properties (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); guint n_properties = 0; - GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(camera), &n_properties); + GParamSpec **properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (camera), &n_properties); GValue val = {0}; for (guint i = 0; i < n_properties; i++) { - g_value_init(&val, properties[i]->value_type); - g_object_get_property(G_OBJECT(camera), properties[i]->name, &val); - g_value_unset(&val); + g_value_init (&val, properties[i]->value_type); + g_object_get_property (G_OBJECT (camera), properties[i]->name, &val); + g_value_unset (&val); } - g_free(properties); + g_free (properties); +} + +static void +test_fps_property (Fixture *fixture, gconstpointer data) +{ + gdouble frames_per_second; + gdouble exposure_time = 0.5; + + g_object_set (G_OBJECT (fixture->camera), + "exposure-time", exposure_time, + NULL); + g_object_get (G_OBJECT (fixture->camera), + "frames-per-second", &frames_per_second, + NULL); + + /* + * The mock camera does not override the "frames-per-second" property, so we + * check the implementation from the base camera. + */ + g_assert_cmpfloat (frames_per_second, ==, 1.0 / exposure_time); +} + +static void +test_property_units (Fixture *fixture, gconstpointer data) +{ + /* Default camera properties */ + g_assert (uca_camera_get_unit (fixture->camera, "sensor-width") == UCA_UNIT_PIXEL); + g_assert (uca_camera_get_unit (fixture->camera, "name") == UCA_UNIT_NA); + + /* Mock-specific properties */ + g_assert (uca_camera_get_unit (fixture->camera, "frame-rate") == UCA_UNIT_COUNT); } -static void test_binnings_properties(Fixture *fixture, gconstpointer data) +static void +test_binnings_properties (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); GValueArray *array = NULL; - g_object_get(G_OBJECT(camera), + g_object_get (G_OBJECT (camera), "sensor-horizontal-binnings", &array, NULL); - GValue *value = g_value_array_get_nth(array, 0); - g_assert(value != NULL); - g_assert(g_value_get_uint(value) == 1); + GValue *value = g_value_array_get_nth (array, 0); + g_assert (value != NULL); + g_assert (g_value_get_uint (value) == 1); } -static void test_signal(Fixture *fixture, gconstpointer data) +static void +test_signal (Fixture *fixture, gconstpointer data) { - UcaCamera *camera = UCA_CAMERA(fixture->camera); + UcaCamera *camera = UCA_CAMERA (fixture->camera); gboolean success = FALSE; - g_signal_connect(camera, "notify::frame-rate", (GCallback) on_property_change, &success); - g_object_set(G_OBJECT(camera), + g_signal_connect (camera, "notify::frame-rate", (GCallback) on_property_change, &success); + g_object_set (G_OBJECT (camera), "frame-rate", 30.0, NULL); - g_assert(success == TRUE); + g_assert (success == TRUE); } -int main(int argc, char *argv[]) +int main (int argc, char *argv[]) { - g_type_init(); - g_test_init(&argc, &argv, NULL); - g_test_bug_base("http://ufo.kit.edu/ufo/ticket"); - - g_test_add_func("/factory", test_factory); - g_test_add("/recording", Fixture, NULL, fixture_setup, test_recording, fixture_teardown); - g_test_add("/recording/signal", Fixture, NULL, fixture_setup, test_recording_signal, fixture_teardown); - g_test_add("/recording/asynchronous", Fixture, NULL, fixture_setup, test_recording_async, fixture_teardown); - g_test_add("/properties/base", Fixture, NULL, fixture_setup, test_base_properties, fixture_teardown); - g_test_add("/properties/recording", Fixture, NULL, fixture_setup, test_recording_property, fixture_teardown); - g_test_add("/properties/binnings", Fixture, NULL, fixture_setup, test_binnings_properties, fixture_teardown); - g_test_add("/signal", Fixture, NULL, fixture_setup, test_signal, fixture_teardown); - - return g_test_run(); + g_type_init (); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://ufo.kit.edu/ufo/ticket"); + + g_test_add ("/factory", Fixture, NULL, fixture_setup, test_factory, fixture_teardown); + g_test_add ("/recording", Fixture, NULL, fixture_setup, test_recording, fixture_teardown); + g_test_add ("/recording/signal", Fixture, NULL, fixture_setup, test_recording_signal, fixture_teardown); + g_test_add ("/recording/asynchronous", Fixture, NULL, fixture_setup, test_recording_async, fixture_teardown); + g_test_add ("/properties/base", Fixture, NULL, fixture_setup, test_base_properties, fixture_teardown); + g_test_add ("/properties/recording", Fixture, NULL, fixture_setup, test_recording_property, fixture_teardown); + g_test_add ("/properties/binnings", Fixture, NULL, fixture_setup, test_binnings_properties, fixture_teardown); + g_test_add ("/properties/frames-per-second", Fixture, NULL, fixture_setup, test_fps_property, fixture_teardown); + g_test_add ("/properties/units", Fixture, NULL, fixture_setup, test_property_units, fixture_teardown); + g_test_add ("/signal", Fixture, NULL, fixture_setup, test_signal, fixture_teardown); + + return g_test_run (); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..0e4d28e --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8) + +add_definitions("--std=c99 -Wall") + +# --- Find packages and libraries --------------------------------------------- +find_package(PkgConfig) + +pkg_check_modules(GLIB2 glib-2.0>=2.24 REQUIRED) +pkg_check_modules(GOBJECT2 gobject-2.0>=2.24 REQUIRED) + +# --- Build targets ----------------------------------------------------------- +include_directories( + ${GLIB2_INCLUDE_DIRS} + ${GOBJECT2_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../src/ + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ) + +set(libs uca ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES}) + +add_executable(gen-doc gen-doc.c) +target_link_libraries(gen-doc ${libs}) + +add_executable(grab grab.c) +target_link_libraries(grab ${libs}) + +add_executable(grab-async grab-async.c) +target_link_libraries(grab-async ${libs}) + +add_executable(benchmark benchmark.c) +target_link_libraries(benchmark ${libs}) + +add_executable(perf perf-overhead.c) +target_link_libraries(perf ${libs}) + +add_subdirectory(gui) diff --git a/test/benchmark.c b/tools/benchmark.c index 0b4f6f8..bff8b50 100644 --- a/test/benchmark.c +++ b/tools/benchmark.c @@ -20,6 +20,7 @@ #include <string.h> #include <stdlib.h> #include "uca-camera.h" +#include "uca-plugin-manager.h" typedef void (*GrabFrameFunc) (UcaCamera *camera, gpointer buffer, guint n_frames); @@ -37,20 +38,25 @@ sigint_handler(int signal) static void print_usage (void) { - gchar **types; + GList *types; + UcaPluginManager *manager; - g_print ("Usage: benchmark ("); - types = uca_camera_get_types (); + manager = uca_plugin_manager_new (); + g_print ("Usage: benchmark [ "); + types = uca_plugin_manager_get_available_cameras (manager); - for (guint i = 0; types[i] != NULL; i++) { - if (types[i+1] == NULL) - g_print ("%s)", types[i]); - else - g_print ("%s | ", types[i]); + if (types == NULL) { + g_print ("] -- no camera plugin found\n"); + return; } - g_print ("\n"); - g_strfreev (types); + for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) { + gchar *name = (gchar *) it->data; + if (g_list_next (it) == NULL) + g_print ("%s ]\n", name); + else + g_print ("%s, ", name); + } } static void @@ -227,10 +233,12 @@ benchmark (UcaCamera *camera) int main (int argc, char *argv[]) { + UcaPluginManager *manager; GIOChannel *log_channel; GError *error = NULL; (void) signal (SIGINT, sigint_handler); + g_type_init(); if (argc < 2) { print_usage(); @@ -241,8 +249,8 @@ main (int argc, char *argv[]) g_assert_no_error (error); g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_handler, log_channel); - g_type_init(); - camera = uca_camera_new(argv[1], &error); + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, argv[1], &error); if (camera == NULL) { g_error ("Initialization: %s", error->message); diff --git a/tools/gen-doc.c b/tools/gen-doc.c new file mode 100644 index 0000000..f555a5f --- /dev/null +++ b/tools/gen-doc.c @@ -0,0 +1,155 @@ +/* Copyright (C) 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 <glib-object.h> +#include "uca-plugin-manager.h" +#include "uca-camera.h" + + +static void +print_usage (void) +{ + GList *types; + UcaPluginManager *manager; + + manager = uca_plugin_manager_new (); + g_print ("Usage: gen-doc [ "); + types = uca_plugin_manager_get_available_cameras (manager); + + if (types == NULL) { + g_print ("] -- no camera plugin found\n"); + return; + } + + for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) { + gchar *name = (gchar *) it->data; + if (g_list_next (it) == NULL) + g_print ("%s ]\n", name); + else + g_print ("%s, ", name); + } +} + +static const gchar * +get_flags_description (GParamSpec *pspec) +{ + static const gchar *descriptions[] = { + "", + "Read-only", + "Write-only", + "Read / Write", + "" + }; + + if (pspec->flags >= 3) + return descriptions[3]; + + return descriptions[pspec->flags]; +} + +static void +print_property_toc (GParamSpec **pspecs, guint n_props) +{ + g_print ("<h2>Properties</h2><ul id=\"toc\">"); + + for (guint i = 0; i < n_props; i++) { + GParamSpec *pspec = pspecs[i]; + const gchar *name = g_param_spec_get_name (pspec); + + g_print ("<li><code><a href=#%s>\"%s\"</a></code></li>", name, name); + } + + g_print ("</ul>"); +} + +static void +print_property_descriptions (GParamSpec **pspecs, guint n_props) +{ + g_print ("<h2>Details</h2><dl>"); + + for (guint i = 0; i < n_props; i++) { + GParamSpec *pspec = pspecs[i]; + const gchar *name = g_param_spec_get_name (pspec); + + g_print ("<dt id=\"%s\"><a href=\"#toc\">%s</a></dt>\n", name, name); + g_print ("<dd>"); + g_print ("<pre><code class=\"prop-type\">\"%s\" : %s : %s</code></pre>\n", + name, + g_type_name (pspec->value_type), + get_flags_description (pspec)); + g_print ("<p>%s</p>\n", g_param_spec_get_blurb (pspec)); + g_print ("</dd>"); + } + + g_print ("</dl>"); +} + +static void +print_properties (UcaCamera *camera) +{ + GObjectClass *oclass; + GParamSpec **pspecs; + guint n_props; + + oclass = G_OBJECT_GET_CLASS (camera); + pspecs = g_object_class_list_properties (oclass, &n_props); + + print_property_toc (pspecs, n_props); + print_property_descriptions (pspecs, n_props); + + g_free (pspecs); +} + +static const gchar *html_header = "<html><head>\ +<link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\" />\ +<link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700|Droid+Serif:400,400italic|Inconsolata' rel='stylesheet' type='text/css'>\ +<title>%s — properties</title></head><body>"; +static const gchar *html_footer = "</body></html>"; + +int main(int argc, char *argv[]) +{ + UcaPluginManager *manager; + UcaCamera *camera; + gchar *name; + GError *error = NULL; + + g_type_init(); + manager = uca_plugin_manager_new (); + + if (argc < 2) { + name = g_strdup ("Basic camera"); + camera = g_object_new (UCA_TYPE_CAMERA, NULL); + } + else { + name = argv[1]; + camera = uca_plugin_manager_get_camera (manager, name, &error); + } + + if (camera == NULL) { + g_print("Error during initialization: %s\n", error->message); + print_usage(); + return 1; + } + + g_print (html_header, name); + g_print ("<div id=\"header\"><h1 class=\"title\">Property documentation of %s</h1>", name); + print_properties (camera); + g_print ("%s\n", html_footer); + + g_object_unref (camera); + g_object_unref (manager); +} diff --git a/test/grab-async.c b/tools/grab-async.c index e1ec114..2c4bf04 100644 --- a/test/grab-async.c +++ b/tools/grab-async.c @@ -19,6 +19,7 @@ #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include "uca-plugin-manager.h" #include "uca-camera.h" static UcaCamera *camera = NULL; @@ -29,7 +30,8 @@ typedef struct { guint counter; } CallbackData; -static void sigint_handler(int signal) +static void +sigint_handler(int signal) { printf("Closing down libuca\n"); uca_camera_stop_recording(camera, NULL); @@ -37,7 +39,8 @@ static void sigint_handler(int signal) exit(signal); } -static void grab_callback(gpointer data, gpointer user_data) +static void +grab_callback(gpointer data, gpointer user_data) { CallbackData *cbd = (CallbackData *) user_data; gchar *filename = g_strdup_printf("frame-%04i.raw", cbd->counter++); @@ -49,16 +52,49 @@ static void grab_callback(gpointer data, gpointer user_data) g_free(filename); } -int main(int argc, char *argv[]) +static void +print_usage (void) +{ + GList *types; + UcaPluginManager *manager; + + manager = uca_plugin_manager_new (); + g_print ("Usage: benchmark [ "); + types = uca_plugin_manager_get_available_cameras (manager); + + if (types == NULL) { + g_print ("] -- no camera plugin found\n"); + return; + } + + for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) { + gchar *name = (gchar *) it->data; + if (g_list_next (it) == NULL) + g_print ("%s ]\n", name); + else + g_print ("%s, ", name); + } +} + +int +main(int argc, char *argv[]) { CallbackData cbd; guint sensor_width, sensor_height; gchar *name; + UcaPluginManager *manager; GError *error = NULL; (void) signal(SIGINT, sigint_handler); g_type_init(); - camera = uca_camera_new("pco", &error); + + if (argc < 2) { + print_usage(); + return 1; + } + + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, argv[1], &error); if (camera == NULL) { g_print("Error during initialization: %s\n", error->message); diff --git a/test/grab.c b/tools/grab.c index 41e6d88..1518997 100644 --- a/test/grab.c +++ b/tools/grab.c @@ -19,6 +19,7 @@ #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include "uca-plugin-manager.h" #include "uca-camera.h" static UcaCamera *camera = NULL; @@ -31,26 +32,33 @@ static void sigint_handler(int signal) exit(signal); } -static void print_usage(void) +static void +print_usage (void) { - gchar **types; - - g_print("Usage: grab ("); - types = uca_camera_get_types(); - - for (guint i = 0; types[i] != NULL; i++) { - if (types[i+1] == NULL) - g_print("%s)", types[i]); - else - g_print("%s | ", types[i]); + GList *types; + UcaPluginManager *manager; + + manager = uca_plugin_manager_new (); + g_print ("Usage: benchmark [ "); + types = uca_plugin_manager_get_available_cameras (manager); + + if (types == NULL) { + g_print ("] -- no camera plugin found\n"); + return; } - g_print("\n"); - g_strfreev(types); + for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) { + gchar *name = (gchar *) it->data; + if (g_list_next (it) == NULL) + g_print ("%s ]\n", name); + else + g_print ("%s, ", name); + } } int main(int argc, char *argv[]) { + UcaPluginManager *manager; GError *error = NULL; (void) signal(SIGINT, sigint_handler); @@ -59,13 +67,15 @@ int main(int argc, char *argv[]) guint bits; gchar *name; + g_type_init(); + if (argc < 2) { print_usage(); return 1; } - g_type_init(); - camera = uca_camera_new(argv[1], &error); + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, argv[1], &error); if (camera == NULL) { g_print("Error during initialization: %s\n", error->message); @@ -100,7 +110,7 @@ int main(int argc, char *argv[]) g_free(name); g_print("Sensor: %ix%i px\n", sensor_width, sensor_height); - g_print("ROI: %ix%i @ (%i, %i), steps: %i, %i\n", + g_print("ROI: %ix%i @ (%i, %i), steps: %i, %i\n", roi_width, roi_height, roi_x, roi_y, roi_width_multiplier, roi_height_multiplier); const int pixel_size = bits == 8 ? 1 : 2; diff --git a/tools/gui/CMakeLists.txt b/tools/gui/CMakeLists.txt new file mode 100644 index 0000000..1000ac6 --- /dev/null +++ b/tools/gui/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 2.8) + +add_definitions("--std=c99 -Wall") + +# --- Find packages and libraries --------------------------------------------- +find_package(PkgConfig) + +pkg_check_modules(GTK2 gtk+-2.0>=2.22) +pkg_check_modules(GTHREAD2 gthread-2.0) +pkg_check_modules(GLIB2 glib-2.0>=2.24 REQUIRED) +pkg_check_modules(GOBJECT2 gobject-2.0>=2.24 REQUIRED) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/control.glade ${CMAKE_CURRENT_BINARY_DIR}) + +# --- Build targets ----------------------------------------------------------- +include_directories( + ${GLIB2_INCLUDE_DIRS} + ${GOBJECT2_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../../src/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../src + ) + +if (GTK2_FOUND) + include_directories(${GTK2_INCLUDE_DIRS}) + + add_executable(control + control.c + ring-buffer.c + egg-property-cell-renderer.c + egg-property-tree-view.c + egg-histogram-view.c) + + target_link_libraries(control uca + ${GTK2_LIBRARIES} + ${GTHREAD2_LIBRARIES} + m) + + install(TARGETS control + RUNTIME DESTINATION bin) + + install(FILES control.glade + DESTINATION share/libuca) +endif() diff --git a/tools/gui/control.c b/tools/gui/control.c new file mode 100644 index 0000000..877473e --- /dev/null +++ b/tools/gui/control.c @@ -0,0 +1,622 @@ +/* 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 <glib/gprintf.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <math.h> + +#include "config.h" +#include "ring-buffer.h" +#include "uca-camera.h" +#include "uca-plugin-manager.h" +#include "egg-property-tree-view.h" +#include "egg-histogram-view.h" + +typedef enum { + IDLE, + RUNNING, + RECORDING +} State; + +typedef struct { + UcaCamera *camera; + GtkWidget *main_window; + GdkPixbuf *pixbuf; + GtkWidget *image; + GtkWidget *start_button; + GtkWidget *stop_button; + GtkWidget *record_button; + GtkWidget *download_button; + GtkComboBox *zoom_box; + + GtkDialog *download_dialog; + GtkProgressBar *download_progressbar; + GtkWidget *download_close_button; + GtkAdjustment *download_adjustment; + + GtkWidget *histogram_view; + GtkToggleButton *histogram_button; + GtkAdjustment *frame_slider; + + RingBuffer *buffer; + guchar *pixels; + gint display_width, display_height; + gdouble zoom_factor; + State state; + gboolean data_in_camram; + + gint timestamp; + gint width, height; + gint pixel_size; +} ThreadData; + +static UcaPluginManager *plugin_manager; +static gsize mem_size = 2048; + +static void +convert_grayscale_to_rgb (ThreadData *data, gpointer buffer) +{ + gdouble min; + gdouble max; + gdouble factor; + guint8 *output; + gint i = 0; + gint stride; + + egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min, &max); + factor = 255.0 / (max - min); + output = data->pixels; + stride = (gint) 1 / data->zoom_factor; + + if (data->pixel_size == 1) { + guint8 *input = (guint8 *) buffer; + + for (gint y = 0; y < data->display_height; y++) { + gint offset = y * stride * data->width; + + for (gint x = 0; x < data->display_width; x++, offset += stride) { + guchar val = (guchar) ((input[offset] - min) * factor); + + output[i++] = val; + output[i++] = val; + output[i++] = val; + } + } + } + else if (data->pixel_size == 2) { + guint16 *input = (guint16 *) buffer; + + for (gint y = 0; y < data->display_height; y++) { + gint offset = y * stride * data->width; + + for (gint x = 0; x < data->display_width; x++, offset += stride) { + guchar val = (guchar) ((input[offset] - min) * factor); + + output[i++] = val; + output[i++] = val; + output[i++] = val; + } + } + } +} + +static void +update_pixbuf (ThreadData *data) +{ + gdk_flush (); + gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); + gtk_widget_queue_draw_area (data->image, 0, 0, data->display_width, data->display_height); + + if (gtk_toggle_button_get_active (data->histogram_button)) + gtk_widget_queue_draw (data->histogram_view); +} + +static void +update_pixbuf_dimensions (ThreadData *data) +{ + if (data->pixbuf != NULL) + g_object_unref (data->pixbuf); + + data->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, data->display_width, data->display_height); + data->pixels = gdk_pixbuf_get_pixels (data->pixbuf); + gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); +} + +static gpointer +preview_frames (void *args) +{ + ThreadData *data = (ThreadData *) args; + gint counter = 0; + + while (data->state == RUNNING) { + gpointer buffer; + + buffer = ring_buffer_get_current_pointer (data->buffer); + uca_camera_grab (data->camera, &buffer, NULL); + convert_grayscale_to_rgb (data, buffer); + + gdk_threads_enter (); + update_pixbuf (data); + gdk_threads_leave (); + + counter++; + } + return NULL; +} + +static gpointer +record_frames (gpointer args) +{ + ThreadData *data; + gpointer buffer; + guint n_frames = 0; + + data = (ThreadData *) args; + ring_buffer_reset (data->buffer); + + while (data->state == RECORDING) { + buffer = ring_buffer_get_current_pointer (data->buffer); + uca_camera_grab (data->camera, &buffer, NULL); + ring_buffer_proceed (data->buffer); + n_frames++; + } + + n_frames = ring_buffer_get_num_blocks (data->buffer); + + gdk_threads_enter (); + gtk_adjustment_set_upper (data->frame_slider, n_frames - 1); + gtk_adjustment_set_value (data->frame_slider, n_frames - 1); + gdk_threads_leave (); + + return NULL; +} + +gboolean +on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + return FALSE; +} + +void +on_destroy (GtkWidget *widget, ThreadData *data) +{ + data->state = IDLE; + g_object_unref (data->camera); + ring_buffer_free (data->buffer); + + gtk_main_quit (); +} + +static void +set_tool_button_state (ThreadData *data) +{ + gtk_widget_set_sensitive (data->start_button, + data->state == IDLE); + gtk_widget_set_sensitive (data->stop_button, + data->state == RUNNING || data->state == RECORDING); + gtk_widget_set_sensitive (data->record_button, + data->state == IDLE); + gtk_widget_set_sensitive (data->download_button, + data->data_in_camram); + gtk_widget_set_sensitive (GTK_WIDGET (data->zoom_box), + data->state == IDLE); +} + +static void +update_current_frame (ThreadData *data) +{ + gpointer buffer; + gint index; + + index = (gint) gtk_adjustment_get_value (data->frame_slider); + buffer = ring_buffer_get_pointer (data->buffer, index); + convert_grayscale_to_rgb (data, buffer); + update_pixbuf (data); +} + +static void +on_frame_slider_changed (GtkAdjustment *adjustment, ThreadData *data) +{ + if (data->state == IDLE) + update_current_frame (data); +} + +static void +on_start_button_clicked (GtkWidget *widget, ThreadData *data) +{ + GError *error = NULL; + + uca_camera_start_recording (data->camera, &error); + + if (error != NULL) { + g_printerr ("Failed to start recording: %s\n", error->message); + return; + } + + data->state = RUNNING; + set_tool_button_state (data); + + if (!g_thread_create (preview_frames, data, FALSE, &error)) { + g_printerr ("Failed to create thread: %s\n", error->message); + data->state = IDLE; + set_tool_button_state (data); + } +} + +static void +on_stop_button_clicked (GtkWidget *widget, ThreadData *data) +{ + GError *error = NULL; + + g_object_get (data->camera, "has-camram-recording", &data->data_in_camram, NULL); + data->state = IDLE; + set_tool_button_state (data); + uca_camera_stop_recording (data->camera, &error); + + if (error != NULL) + g_printerr ("Failed to stop: %s\n", error->message); + +} + +static void +on_record_button_clicked (GtkWidget *widget, ThreadData *data) +{ + GError *error = NULL; + + uca_camera_start_recording (data->camera, &error); + + if (error != NULL) { + g_printerr ("Failed to start recording: %s\n", error->message); + } + + data->timestamp = (int) time (0); + data->state = RECORDING; + set_tool_button_state (data); + + if (!g_thread_create (record_frames, data, FALSE, &error)) { + g_printerr ("Failed to create thread: %s\n", error->message); + data->state = IDLE; + set_tool_button_state (data); + } +} + +static gpointer +download_frames (ThreadData *data) +{ + gpointer buffer; + guint n_frames; + guint current_frame = 1; + GError *error = NULL; + + g_object_get (data->camera, "recorded-frames", &n_frames, NULL); + gdk_threads_enter (); + gtk_widget_set_sensitive (data->download_close_button, FALSE); + gtk_adjustment_set_upper (data->download_adjustment, n_frames); + gdk_threads_leave (); + + uca_camera_start_readout (data->camera, &error); + + if (error != NULL) { + g_printerr ("Failed to start read out of camera memory: %s\n", error->message); + return NULL; + } + + ring_buffer_reset (data->buffer); + + while (error == NULL) { + buffer = ring_buffer_get_current_pointer (data->buffer); + uca_camera_grab (data->camera, &buffer, &error); + ring_buffer_proceed (data->buffer); + gdk_threads_enter (); + gtk_adjustment_set_value (data->download_adjustment, current_frame++); + gdk_threads_leave (); + } + + if (error->code == UCA_CAMERA_ERROR_END_OF_STREAM) { + guint n_frames = ring_buffer_get_num_blocks (data->buffer); + + gtk_adjustment_set_upper (data->frame_slider, n_frames - 1); + gtk_adjustment_set_value (data->frame_slider, n_frames - 1); + } + else + g_printerr ("Error while reading out frames: %s\n", error->message); + + g_error_free (error); + error = NULL; + + uca_camera_stop_readout (data->camera, &error); + + if (error != NULL) + g_printerr ("Failed to stop reading out of camera memory: %s\n", error->message); + + gdk_threads_enter (); + gtk_widget_set_sensitive (data->download_close_button, TRUE); + gdk_threads_leave (); + + return NULL; +} + +static void +on_download_button_clicked (GtkWidget *widget, ThreadData *data) +{ + GError *error = NULL; + + if (!g_thread_create ((GThreadFunc) download_frames, data, FALSE, &error)) { + g_printerr ("Failed to create thread: %s\n", error->message); + } + + gtk_widget_set_sensitive (data->main_window, FALSE); + gtk_window_set_modal (GTK_WINDOW (data->download_dialog), TRUE); + gtk_dialog_run (data->download_dialog); + gtk_widget_hide (GTK_WIDGET (data->download_dialog)); + gtk_window_set_modal (GTK_WINDOW (data->download_dialog), FALSE); + gtk_widget_set_sensitive (data->main_window, TRUE); + gtk_window_set_modal (GTK_WINDOW (data->download_dialog), TRUE); +} + +static void +on_histogram_changed (EggHistogramView *view, ThreadData *data) +{ + if (data->state == IDLE) + update_current_frame (data); +} + +static void +on_zoom_changed (GtkComboBox *widget, ThreadData *data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gdouble factor; + + enum { + DISPLAY_COLUMN, + FACTOR_COLUMN + }; + + model = gtk_combo_box_get_model (widget); + gtk_combo_box_get_active_iter (widget, &iter); + gtk_tree_model_get (model, &iter, FACTOR_COLUMN, &factor, -1); + + data->display_width = (gint) data->width * factor; + data->display_height = (gint) data->height * factor; + data->zoom_factor = factor; + update_pixbuf_dimensions (data); +} + +static void +create_main_window (GtkBuilder *builder, const gchar* camera_name) +{ + static ThreadData td; + UcaCamera *camera; + GtkWidget *window; + GtkWidget *image; + GtkWidget *histogram_view; + GtkWidget *property_tree_view; + GdkPixbuf *pixbuf; + GtkBox *histogram_box; + GtkContainer *property_window; + GtkAdjustment *max_bin_adjustment; + RingBuffer *ring_buffer; + gsize image_size; + guint n_frames; + guint bits_per_sample; + guint pixel_size; + guint width, height; + GError *error = NULL; + + camera = uca_plugin_manager_get_camera (plugin_manager, camera_name, &error); + + if ((camera == NULL) || (error != NULL)) { + g_error ("%s\n", error->message); + gtk_main_quit (); + } + + g_object_get (camera, + "roi-width", &width, + "roi-height", &height, + "sensor-bitdepth", &bits_per_sample, + NULL); + + histogram_view = egg_histogram_view_new (); + property_tree_view = egg_property_tree_view_new (G_OBJECT (camera)); + property_window = GTK_CONTAINER (gtk_builder_get_object (builder, "property-window")); + image = GTK_WIDGET (gtk_builder_get_object (builder, "image")); + histogram_box = GTK_BOX (gtk_builder_get_object (builder, "histogram-box")); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); + max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max-bin-value-adjustment")); + + td.zoom_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, "zoom-box")); + td.start_button = GTK_WIDGET (gtk_builder_get_object (builder, "start-button")); + td.stop_button = GTK_WIDGET (gtk_builder_get_object (builder, "stop-button")); + td.record_button = GTK_WIDGET (gtk_builder_get_object (builder, "record-button")); + td.download_button = GTK_WIDGET (gtk_builder_get_object (builder, "download-button")); + td.histogram_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "histogram-checkbutton")); + td.frame_slider = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "frames-adjustment")); + + td.download_dialog = GTK_DIALOG (gtk_builder_get_object (builder, "download-dialog")); + td.download_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "download-adjustment")); + td.download_close_button = GTK_WIDGET (gtk_builder_get_object (builder, "download-close-button")); + + /* Set initial data */ + pixel_size = bits_per_sample > 8 ? 2 : 1; + image_size = pixel_size * width * height; + n_frames = mem_size * 1024 * 1024 / image_size; + ring_buffer = ring_buffer_new (image_size, n_frames); + + egg_histogram_view_set_data (EGG_HISTOGRAM_VIEW (histogram_view), + ring_buffer_get_current_pointer (ring_buffer), + width * height, bits_per_sample, 256); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height); + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + + gtk_adjustment_set_value (max_bin_adjustment, pow (2, bits_per_sample) - 1); + + g_message ("Allocated memory for %d frames", n_frames); + + td.pixel_size = pixel_size; + td.image = image; + td.pixbuf = NULL; + td.pixels = NULL; + td.buffer = ring_buffer; + td.state = IDLE; + td.camera = camera; + td.width = td.display_width = width; + td.height = td.display_height = height; + td.zoom_factor = 1.0; + td.histogram_view = histogram_view; + td.data_in_camram = FALSE; + td.main_window = window; + + update_pixbuf_dimensions (&td); + set_tool_button_state (&td); + + /* Hook up signals */ + g_object_bind_property (gtk_builder_get_object (builder, "min-bin-value-adjustment"), "value", + td.histogram_view, "minimum-bin-value", + G_BINDING_DEFAULT); + + g_object_bind_property (max_bin_adjustment, "value", + td.histogram_view, "maximum-bin-value", + G_BINDING_DEFAULT); + + g_signal_connect (td.frame_slider, "value-changed", G_CALLBACK (on_frame_slider_changed), &td); + g_signal_connect (td.start_button, "clicked", G_CALLBACK (on_start_button_clicked), &td); + g_signal_connect (td.stop_button, "clicked", G_CALLBACK (on_stop_button_clicked), &td); + g_signal_connect (td.record_button, "clicked", G_CALLBACK (on_record_button_clicked), &td); + g_signal_connect (td.download_button, "clicked", G_CALLBACK (on_download_button_clicked), &td); + g_signal_connect (td.zoom_box, "changed", G_CALLBACK (on_zoom_changed), &td); + g_signal_connect (histogram_view, "changed", G_CALLBACK (on_histogram_changed), &td); + g_signal_connect (window, "destroy", G_CALLBACK (on_destroy), &td); + + /* Layout */ + gtk_container_add (property_window, property_tree_view); + gtk_box_pack_start (histogram_box, td.histogram_view, TRUE, TRUE, 6); + + gtk_widget_show_all (window); +} + +static void +on_button_proceed_clicked (GtkWidget *widget, gpointer data) +{ + GtkBuilder *builder = GTK_BUILDER (data); + GtkWidget *choice_window = GTK_WIDGET (gtk_builder_get_object (builder, "choice-window")); + GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "treeview-cameras")); + GtkListStore *list_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "camera-types")); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL); + GtkTreeIter iter; + + gtk_widget_destroy (choice_window); + gboolean valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, selected_rows->data); + + if (valid) { + gchar *data; + gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, 0, &data, -1); + create_main_window (builder, data); + g_free (data); + } + + g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected_rows); +} + +static void +on_treeview_keypress (GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (event->keyval == GDK_KEY_Return) + gtk_widget_grab_focus (GTK_WIDGET (data)); +} + +static void +create_choice_window (GtkBuilder *builder) +{ + GList *camera_types = uca_plugin_manager_get_available_cameras (plugin_manager); + + GtkWidget *choice_window = GTK_WIDGET (gtk_builder_get_object (builder, "choice-window")); + GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "treeview-cameras")); + GtkListStore *list_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "camera-types")); + GtkButton *proceed_button = GTK_BUTTON (gtk_builder_get_object (builder, "proceed-button")); + GtkTreeIter iter; + + for (GList *it = g_list_first (camera_types); it != NULL; it = g_list_next (it)) { + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, 0, g_strdup ((gchar *) it->data), -1); + } + + gboolean valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); + + if (valid) { + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), &iter)); + } + + g_signal_connect (proceed_button, "clicked", G_CALLBACK (on_button_proceed_clicked), builder); + g_signal_connect (treeview, "key-press-event", G_CALLBACK (on_treeview_keypress), proceed_button); + gtk_widget_show_all (GTK_WIDGET (choice_window)); + + g_list_foreach (camera_types, (GFunc) g_free, NULL); + g_list_free (camera_types); +} + +int +main (int argc, char *argv[]) +{ + GtkBuilder *builder; + GOptionContext *context; + GError *error = NULL; + + static GOptionEntry entries[] = + { + { "mem-size", 'm', 0, G_OPTION_ARG_INT, &mem_size, "Memory in megabytes to allocate for frame storage", "M" }, + { NULL } + }; + + context = g_option_context_new ("- control libuca cameras"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_print ("Option parsing failed: %s\n", error->message); + return 1; + } + + g_thread_init (NULL); + gdk_threads_init (); + gtk_init (&argc, &argv); + + builder = gtk_builder_new (); + + if (!gtk_builder_add_from_file (builder, CONTROL_GLADE_PATH, &error)) { + g_print ("Could not load UI file: %s\n", error->message); + return 1; + } + + plugin_manager = uca_plugin_manager_new (); + create_choice_window (builder); + gtk_builder_connect_signals (builder, NULL); + + gdk_threads_enter (); + gtk_main (); + gdk_threads_leave (); + + g_object_unref (plugin_manager); + return 0; +} diff --git a/tools/gui/control.glade b/tools/gui/control.glade new file mode 100644 index 0000000..eec9dde --- /dev/null +++ b/tools/gui/control.glade @@ -0,0 +1,635 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk+" version="2.20"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkListStore" id="camera-types"> + <columns> + <!-- column-name name --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkListStore" id="camera-properties"> + <columns> + <!-- column-name PropertyName --> + <column type="gchararray"/> + <!-- column-name PropertyValue --> + <column type="gchararray"/> + <!-- column-name writeable --> + <column type="gboolean"/> + </columns> + </object> + <object class="GtkWindow" id="window"> + <property name="title" translatable="yes">Camera Control</property> + <property name="default_width">1024</property> + <property name="default_height">768</property> + <signal name="delete_event" handler="on_delete_event"/> + <child> + <object class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <child> + <object class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + <child> + <object class="GtkMenuItem" id="menuitem1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu_file"> + <property name="visible">True</property> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem1"> + <property name="label">gtk-new</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem2"> + <property name="label">gtk-open</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem3"> + <property name="label">gtk-save</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem4"> + <property name="label">gtk-save-as</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem" id="separatormenuitem1"> + <property name="visible">True</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem_quit"> + <property name="label">gtk-quit</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="gtk_main_quit"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkMenuItem" id="menuitem4"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu_help"> + <property name="visible">True</property> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem_about"> + <property name="label">gtk-about</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkToolbar" id="toolbar"> + <property name="visible">True</property> + <child> + <object class="GtkToolButton" id="start-button"> + <property name="visible">True</property> + <property name="label" translatable="yes">Run</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-media-play</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="record-button"> + <property name="visible">True</property> + <property name="label" translatable="yes">Record</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-media-record</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="stop-button"> + <property name="visible">True</property> + <property name="label" translatable="yes">Stop</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-media-stop</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="download-button"> + <property name="visible">True</property> + <property name="label" translatable="yes">Download</property> + <property name="use_underline">True</property> + <property name="icon_name">network-receive</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHPaned" id="hpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="border_width">6</property> + <child> + <object class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="width_request">640</property> + <property name="height_request">480</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <object class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="resize_mode">queue</property> + <child> + <object class="GtkImage" id="image"> + <property name="visible">True</property> + <property name="stock">gtk-missing-image</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + <child> + <object class="GtkNotebook" id="notebook1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkHBox" id="histogram-box"> + <property name="visible">True</property> + <property name="border_width">10</property> + <child> + <placeholder/> + </child> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Minimum Value:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + <property name="x_padding">6</property> + <property name="y_padding">6</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinbutton1"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="adjustment">min-bin-value-adjustment</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_EXPAND</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Maximum Value:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + <property name="x_padding">6</property> + <property name="y_padding">6</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinbutton2"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="adjustment">max-bin-value-adjustment</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_EXPAND</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="histogram-checkbutton"> + <property name="label" translatable="yes">Live Update</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="border_width">6</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Histogram</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkTable" id="table2"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkHScale" id="hscale1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">frames-adjustment</property> + <property name="digits">0</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Frame:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Zoom:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="zoom-box"> + <property name="visible">True</property> + <property name="model">zoom-values</property> + <child> + <object class="GtkCellRendererText" id="cellrenderertext2"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Preview</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + </object> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + </object> + <packing> + <property name="resize">False</property> + <property name="shrink">True</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="property-window"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + <object class="GtkListStore" id="zoom-values"> + <columns> + <!-- column-name display --> + <column type="gchararray"/> + <!-- column-name factor --> + <column type="gdouble"/> + </columns> + <data> + <row> + <col id="0" translatable="yes">100 %</col> + <col id="1">1</col> + </row> + <row> + <col id="0" translatable="yes">50 %</col> + <col id="1">0.5</col> + </row> + <row> + <col id="0" translatable="yes">25 %</col> + <col id="1">0.25</col> + </row> + </data> + </object> + <object class="GtkWindow" id="choice-window"> + <property name="border_width">6</property> + <child> + <object class="GtkVBox" id="vbox3"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <object class="GtkTreeView" id="treeview-cameras"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="model">camera-types</property> + <property name="headers_clickable">False</property> + <property name="search_column">0</property> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn1"> + <property name="title">Choose camera</property> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHButtonBox" id="hbuttonbox1"> + <property name="visible">True</property> + <property name="spacing">6</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="cancel-button"> + <property name="label">gtk-quit</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="gtk_main_quit"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="proceed-button"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="padding">6</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + <object class="GtkAdjustment" id="min-bin-value-adjustment"> + <property name="upper">65535</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="max-bin-value-adjustment"> + <property name="value">256</property> + <property name="upper">65535</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="frames-adjustment"> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkDialog" id="download-dialog"> + <property name="border_width">5</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <object class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Downloading Frames …</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkProgressBar" id="download-progressbar"> + <property name="visible">True</property> + <property name="adjustment">download-adjustment</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="download-close-button"> + <property name="label">gtk-close</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="0">download-close-button</action-widget> + </action-widgets> + </object> + <object class="GtkAdjustment" id="download-adjustment"> + <property name="upper">100</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <property name="page_size">10</property> + </object> +</interface> diff --git a/tools/gui/egg-histogram-view.c b/tools/gui/egg-histogram-view.c new file mode 100644 index 0000000..812af7a --- /dev/null +++ b/tools/gui/egg-histogram-view.c @@ -0,0 +1,524 @@ +/* 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 <math.h> +#include "egg-histogram-view.h" + +G_DEFINE_TYPE (EggHistogramView, egg_histogram_view, GTK_TYPE_DRAWING_AREA) + +#define EGG_HISTOGRAM_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), EGG_TYPE_HISTOGRAM_VIEW, EggHistogramViewPrivate)) + +#define MIN_WIDTH 128 +#define MIN_HEIGHT 128 +#define BORDER 2 + +struct _EggHistogramViewPrivate +{ + GdkCursorType cursor_type; + gboolean grabbing; + + /* This could be moved into a real histogram class */ + guint n_bins; + gint *bins; + gint *grabbed; + gint min_border; /* threshold set in screen units */ + gint max_border; + + gdouble min_value; /* lowest value of the first bin */ + gdouble max_value; /* highest value of the last bin */ + gdouble range; + + gpointer data; + gint n_elements; + gint n_bits; +}; + +enum +{ + PROP_0, + PROP_MINIMUM_BIN_VALUE, + PROP_MAXIMUM_BIN_VALUE, + N_PROPERTIES +}; + +enum +{ + CHANGED, + LAST_SIGNAL +}; + +static GParamSpec *egg_histogram_view_properties[N_PROPERTIES] = { NULL, }; + +static guint egg_histogram_view_signals[LAST_SIGNAL] = { 0 }; + + +GtkWidget * +egg_histogram_view_new (void) +{ + EggHistogramView *view; + + view = EGG_HISTOGRAM_VIEW (g_object_new (EGG_TYPE_HISTOGRAM_VIEW, NULL)); + return GTK_WIDGET (view); +} + +void +egg_histogram_view_set_data (EggHistogramView *view, + gpointer data, + guint n_elements, + guint n_bits, + guint n_bins) +{ + EggHistogramViewPrivate *priv; + + g_return_if_fail (EGG_IS_HISTOGRAM_VIEW (view)); + priv = view->priv; + + if (priv->bins != NULL) + g_free (priv->bins); + + priv->data = data; + priv->bins = g_malloc0 (n_bins * sizeof (guint)); + priv->n_bins = n_bins; + priv->n_bits = n_bits; + priv->n_elements = n_elements; + + priv->min_value = 0.0; + priv->max_value = (gint) pow(2, n_bits) - 1; + + priv->min_border = 0; + priv->max_border = 256; + priv->range = priv->max_value - priv->min_value; +} + +void +egg_histogram_get_visible_range (EggHistogramView *view, gdouble *min, gdouble *max) +{ + EggHistogramViewPrivate *priv; + GtkAllocation allocation; + gdouble width; + + g_return_if_fail (EGG_IS_HISTOGRAM_VIEW (view)); + + gtk_widget_get_allocation (GTK_WIDGET (view), &allocation); + width = (gdouble) allocation.width - 2 * BORDER; + priv = view->priv; + + *min = (priv->min_border - 2) / width * priv->range; + *max = (priv->max_border - 2) / width * priv->range; +} + +static void +set_max_border (EggHistogramView *view) +{ + GtkAllocation allocation; + + g_return_if_fail (EGG_IS_HISTOGRAM_VIEW (view)); + gtk_widget_get_allocation (GTK_WIDGET (view), &allocation); + view->priv->max_border = allocation.width - 2 * BORDER; +} + +static void +compute_histogram (EggHistogramViewPrivate *priv) +{ + guint n_bins = priv->n_bins - 1; + + for (guint i = 0; i < priv->n_bins; i++) + priv->bins[i] = 0; + + if (priv->n_bits == 8) { + guint8 *data = (guint8 *) priv->data; + + for (guint i = 0; i < priv->n_elements; i++) { + guint8 v = data[i]; + + if (v >= priv->min_value && v <= priv->max_value) { + guint index = (guint) round (((gdouble) v) / priv->max_value * n_bins); + priv->bins[index]++; + } + } + } + else { + guint16 *data = (guint16 *) priv->data; + + for (guint i = 0; i < priv->n_elements; i++) { + guint16 v = data[i]; + + if (v >= priv->min_value && v <= priv->max_value) { + guint index = (guint) floor (((gdouble ) v) / priv->max_value * n_bins); + priv->bins[index]++; + } + } + } +} + +static void +set_cursor_type (EggHistogramView *view, GdkCursorType cursor_type) +{ + if (cursor_type != view->priv->cursor_type) { + GdkCursor *cursor = gdk_cursor_new (cursor_type); + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET(view)), cursor); + gdk_cursor_unref (cursor); + view->priv->cursor_type = cursor_type; + } +} + +static void +egg_histogram_view_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = MIN_WIDTH; + requisition->height = MIN_HEIGHT; +} + +static void +draw_bins (EggHistogramViewPrivate *priv, + cairo_t *cr, + gint width, + gint height) +{ + gdouble skip = ((gdouble) width) / priv->n_bins; + gdouble x = BORDER; + gdouble ys = height + BORDER - 1; + gint max_value = 0; + + for (guint i = 0; i < priv->n_bins; i++) { + if (priv->bins[i] > max_value) + max_value = priv->bins[i]; + } + + if (max_value == 0) + return; + + for (guint i = 0; i < priv->n_bins && x < (width - BORDER); i++, x += skip) { + if (priv->bins[i] == 0) + continue; + + gint y = (gint) ((height - 2) * priv->bins[i]) / max_value; + cairo_move_to (cr, round (x), ys); + cairo_line_to (cr, round (x), ys - y); + cairo_stroke (cr); + } +} + +static gboolean +egg_histogram_view_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + EggHistogramViewPrivate *priv; + GtkAllocation allocation; + GtkStyle *style; + cairo_t *cr; + gint width, height; + + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (widget); + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + style = gtk_widget_get_style (widget); + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]); + cairo_paint (cr); + + /* Draw the background */ + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]); + cairo_paint (cr); + + gtk_widget_get_allocation (widget, &allocation); + width = allocation.width - 2 * BORDER; + height = allocation.height - 2 * BORDER; + + gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]); + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + cairo_translate (cr, 0.5, 0.5); + cairo_rectangle (cr, BORDER, BORDER, width - 1, height - 1); + cairo_stroke (cr); + + if (priv->bins == NULL) + goto cleanup; + + compute_histogram (priv); + + /* Draw border areas */ + gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]); + + cairo_rectangle (cr, BORDER, BORDER, priv->min_border + 0.5, height - 1); + cairo_fill (cr); + + cairo_rectangle (cr, priv->max_border + 0.5, BORDER, width - priv->max_border + 0.5, height - 1); + cairo_fill (cr); + + /* Draw spikes */ + gdk_cairo_set_source_color (cr, &style->black); + draw_bins (priv, cr, width, height); + +cleanup: + cairo_destroy (cr); + return FALSE; +} + +static void +egg_histogram_view_finalize (GObject *object) +{ + EggHistogramViewPrivate *priv; + + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (object); + + if (priv->bins) + g_free (priv->bins); + + G_OBJECT_CLASS (egg_histogram_view_parent_class)->finalize (object); +} + +static void +egg_histogram_view_dispose (GObject *object) +{ + G_OBJECT_CLASS (egg_histogram_view_parent_class)->dispose (object); +} + +static void +egg_histogram_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EggHistogramViewPrivate *priv; + + g_return_if_fail (EGG_IS_HISTOGRAM_VIEW (object)); + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (object); + + switch (property_id) { + case PROP_MINIMUM_BIN_VALUE: + { + gdouble v = g_value_get_double (value); + + if (v > priv->max_value) + g_warning ("Minimum value `%f' larger than maximum value `%f'", + v, priv->max_value); + else { + priv->min_value = v; + priv->range = priv->max_value - v; + priv->min_border = 0; + } + } + break; + + case PROP_MAXIMUM_BIN_VALUE: + { + gdouble v = g_value_get_double (value); + + if (v < priv->min_value) + g_warning ("Maximum value `%f' larger than minimum value `%f'", + v, priv->min_value); + else { + priv->max_value = v; + priv->range = v - priv->min_value; + set_max_border (EGG_HISTOGRAM_VIEW (object)); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } +} + +static void +egg_histogram_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EggHistogramViewPrivate *priv; + + g_return_if_fail (EGG_IS_HISTOGRAM_VIEW (object)); + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (object); + + switch (property_id) { + case PROP_MINIMUM_BIN_VALUE: + g_value_set_double (value, priv->min_value); + break; + + case PROP_MAXIMUM_BIN_VALUE: + g_value_set_double (value, priv->max_value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } +} + +static gboolean +is_on_border (EggHistogramViewPrivate *priv, + gint x) +{ + gint d1 = (priv->min_border + BORDER) - x; + gint d2 = (priv->max_border + BORDER) - x; + return ABS (d1) < 6 || ABS (d2) < 6; +} + +static gint * +get_grabbed_border (EggHistogramViewPrivate *priv, + gint x) +{ + gint d1 = (priv->min_border + BORDER) - x; + gint d2 = (priv->max_border + BORDER) - x; + + if (ABS (d1) < 6) + return &priv->min_border; + else if (ABS (d2) < 6) + return &priv->max_border; + + return NULL; +} + +static gboolean +egg_histogram_view_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + EggHistogramView *view; + EggHistogramViewPrivate *priv; + + view = EGG_HISTOGRAM_VIEW (widget); + priv = view->priv; + + if (priv->grabbing) { + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + if ((event->x + BORDER > 0) && (event->x + BORDER < allocation.width)) { + *priv->grabbed = event->x; + gtk_widget_queue_draw (widget); + } + } + else { + if (is_on_border (priv, event->x)) + set_cursor_type (view, GDK_FLEUR); + else + set_cursor_type (view, GDK_ARROW); + } + + return TRUE; +} + +static gboolean +egg_histogram_view_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + EggHistogramView *view; + + view = EGG_HISTOGRAM_VIEW (widget); + set_cursor_type (view, GDK_ARROW); + view->priv->grabbing = FALSE; + g_signal_emit (widget, egg_histogram_view_signals[CHANGED], 0); + + return TRUE; +} + +static gboolean +egg_histogram_view_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + EggHistogramView *view; + EggHistogramViewPrivate *priv; + + view = EGG_HISTOGRAM_VIEW (widget); + priv = view->priv; + + if (is_on_border (priv, event->x)) { + priv->grabbing = TRUE; + priv->grabbed = get_grabbed_border (priv, event->x); + set_cursor_type (view, GDK_FLEUR); + } + + return TRUE; +} + +static void +egg_histogram_view_class_init (EggHistogramViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->set_property = egg_histogram_view_set_property; + object_class->get_property = egg_histogram_view_get_property; + object_class->dispose = egg_histogram_view_dispose; + object_class->finalize = egg_histogram_view_finalize; + + widget_class->size_request = egg_histogram_view_size_request; + widget_class->expose_event = egg_histogram_view_expose; + widget_class->button_press_event = egg_histogram_view_button_press; + widget_class->button_release_event = egg_histogram_view_button_release; + widget_class->motion_notify_event = egg_histogram_view_motion_notify; + + egg_histogram_view_properties[PROP_MINIMUM_BIN_VALUE] = + g_param_spec_double ("minimum-bin-value", + "Smallest possible bin value", + "Smallest possible bin value, everything below is discarded.", + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE); + + egg_histogram_view_properties[PROP_MAXIMUM_BIN_VALUE] = + g_param_spec_double ("maximum-bin-value", + "Largest possible bin value", + "Largest possible bin value, everything above is discarded.", + 0.0, G_MAXDOUBLE, 256.0, + G_PARAM_READWRITE); + + g_object_class_install_property (object_class, PROP_MINIMUM_BIN_VALUE, egg_histogram_view_properties[PROP_MINIMUM_BIN_VALUE]); + g_object_class_install_property (object_class, PROP_MAXIMUM_BIN_VALUE, egg_histogram_view_properties[PROP_MAXIMUM_BIN_VALUE]); + + egg_histogram_view_signals[CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, + G_STRUCT_OFFSET (EggHistogramViewClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (EggHistogramViewPrivate)); +} + +static void +egg_histogram_view_init (EggHistogramView *view) +{ + EggHistogramViewPrivate *priv; + + view->priv = priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (view); + + priv->bins = NULL; + priv->data = NULL; + priv->n_bins = 0; + priv->n_elements = 0; + priv->min_value = priv->min_border = 0; + priv->max_value = priv->max_border = 256; + + priv->cursor_type = GDK_ARROW; + priv->grabbing = FALSE; + + gtk_widget_add_events (GTK_WIDGET (view), + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_POINTER_MOTION_MASK); +} diff --git a/tools/gui/egg-histogram-view.h b/tools/gui/egg-histogram-view.h new file mode 100644 index 0000000..7a62fca --- /dev/null +++ b/tools/gui/egg-histogram-view.h @@ -0,0 +1,66 @@ +/* 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 EGG_HISTOGRAM_VIEW_H +#define EGG_HISTOGRAM_VIEW_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_HISTOGRAM_VIEW (egg_histogram_view_get_type()) +#define EGG_HISTOGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EGG_TYPE_HISTOGRAM_VIEW, EggHistogramView)) +#define EGG_IS_HISTOGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EGG_TYPE_HISTOGRAM_VIEW)) +#define EGG_HISTOGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EGG_TYPE_HISTOGRAM_VIEW, EggHistogramViewClass)) +#define EGG_IS_HISTOGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EGG_TYPE_HISTOGRAM_VIEW)) +#define EGG_HISTOGRAM_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_HISTOGRAM_VIEW, EggHistogramViewClass)) + +typedef struct _EggHistogramView EggHistogramView; +typedef struct _EggHistogramViewClass EggHistogramViewClass; +typedef struct _EggHistogramViewPrivate EggHistogramViewPrivate; + +struct _EggHistogramView +{ + GtkDrawingArea parent_instance; + + /*< private >*/ + EggHistogramViewPrivate *priv; +}; + +struct _EggHistogramViewClass +{ + GtkDrawingAreaClass parent_class; + + /* signals */ + void (* changed) (EggHistogramView *view); +}; + +GType egg_histogram_view_get_type (void); +GtkWidget * egg_histogram_view_new (void); +void egg_histogram_view_set_data (EggHistogramView *view, + gpointer data, + guint n_elements, + guint n_bits, + guint n_bins); +void egg_histogram_get_visible_range + (EggHistogramView *view, + gdouble *min, + gdouble *max); + +G_END_DECLS + +#endif diff --git a/test/egg-property-cell-renderer.c b/tools/gui/egg-property-cell-renderer.c index 9df5cc3..9df5cc3 100644 --- a/test/egg-property-cell-renderer.c +++ b/tools/gui/egg-property-cell-renderer.c diff --git a/test/egg-property-cell-renderer.h b/tools/gui/egg-property-cell-renderer.h index d4dbe02..d4dbe02 100644 --- a/test/egg-property-cell-renderer.h +++ b/tools/gui/egg-property-cell-renderer.h diff --git a/test/egg-property-tree-view.c b/tools/gui/egg-property-tree-view.c index f4ed2fb..52d1e10 100644 --- a/test/egg-property-tree-view.c +++ b/tools/gui/egg-property-tree-view.c @@ -111,4 +111,3 @@ egg_property_tree_view_init (EggPropertyTreeView *tree_view) tree_view->priv = priv = EGG_PROPERTY_TREE_VIEW_GET_PRIVATE (tree_view); priv->list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, GTK_TYPE_ADJUSTMENT); } - diff --git a/test/egg-property-tree-view.h b/tools/gui/egg-property-tree-view.h index e8fd0fe..e8fd0fe 100644 --- a/test/egg-property-tree-view.h +++ b/tools/gui/egg-property-tree-view.h diff --git a/tools/gui/ring-buffer.c b/tools/gui/ring-buffer.c new file mode 100644 index 0000000..ec2638c --- /dev/null +++ b/tools/gui/ring-buffer.c @@ -0,0 +1,64 @@ + +#include <math.h> +#include "ring-buffer.h" + +RingBuffer * +ring_buffer_new (gsize block_size, + gsize n_blocks) +{ + RingBuffer *buffer; + + buffer = g_new0 (RingBuffer, 1); + buffer->block_size = block_size; + buffer->n_blocks_total = n_blocks; + buffer->n_blocks_used = 0; + buffer->current_index = 0; + buffer->data = g_malloc0_n (n_blocks, block_size); + + return buffer; +} + +void +ring_buffer_free (RingBuffer *buffer) +{ + g_free (buffer->data); + g_free (buffer); +} + +void +ring_buffer_reset (RingBuffer *buffer) +{ + buffer->n_blocks_used = 0; + buffer->current_index = 0; +} + +gpointer +ring_buffer_get_current_pointer (RingBuffer *buffer) +{ + return buffer->data + (buffer->current_index % buffer->n_blocks_total) * buffer->block_size; +} + +gpointer +ring_buffer_get_pointer (RingBuffer *buffer, + guint index) +{ + g_assert (index < buffer->n_blocks_total); + return buffer->data + ((buffer->current_index - buffer->n_blocks_used + index) % buffer->n_blocks_total) * buffer->block_size; +} + +guint +ring_buffer_get_num_blocks (RingBuffer *buffer) +{ + return buffer->n_blocks_used; +} + +void +ring_buffer_proceed (RingBuffer *buffer) +{ + buffer->current_index++; + + if (buffer->n_blocks_used < buffer->n_blocks_total) + buffer->n_blocks_used++; + else + buffer->current_index = buffer->current_index % buffer->n_blocks_total; +} diff --git a/tools/gui/ring-buffer.h b/tools/gui/ring-buffer.h new file mode 100644 index 0000000..9966eb7 --- /dev/null +++ b/tools/gui/ring-buffer.h @@ -0,0 +1,28 @@ +#ifndef RING_BUFFER_H +#define RING_BUFFER_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct { + guchar *data; + gsize block_size; + guint n_blocks_total; + guint n_blocks_used; + guint current_index; +} RingBuffer; + +RingBuffer * ring_buffer_new (gsize block_size, + gsize n_blocks); +void ring_buffer_free (RingBuffer *buffer); +void ring_buffer_reset (RingBuffer *buffer); +gpointer ring_buffer_get_current_pointer (RingBuffer *buffer); +gpointer ring_buffer_get_pointer (RingBuffer *buffer, + guint index); +guint ring_buffer_get_num_blocks (RingBuffer *buffer); +void ring_buffer_proceed (RingBuffer *buffer); + +G_END_DECLS + +#endif diff --git a/tools/perf-overhead.c b/tools/perf-overhead.c new file mode 100644 index 0000000..6735e6f --- /dev/null +++ b/tools/perf-overhead.c @@ -0,0 +1,181 @@ +/* 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 <glib-object.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "uca-plugin-manager.h" +#include "uca-camera.h" + +#define handle_error(errno) {if ((errno) != UCA_NO_ERROR) printf("error at <%s:%i>\n", \ + __FILE__, __LINE__);} + +typedef struct { + guint counter; + gsize size; + gpointer destination; +} thread_data; + +static UcaCamera *camera = NULL; + +static void +sigint_handler (int signal) +{ + printf ("Closing down libuca\n"); + uca_camera_stop_recording (camera, NULL); + g_object_unref (camera); + exit (signal); +} + +static void +print_usage (void) +{ + GList *types; + UcaPluginManager *manager; + + manager = uca_plugin_manager_new (); + g_print ("Usage: benchmark [ "); + types = uca_plugin_manager_get_available_cameras (manager); + + if (types == NULL) { + g_print ("] -- no camera plugin found\n"); + return; + } + + for (GList *it = g_list_first (types); it != NULL; it = g_list_next (it)) { + gchar *name = (gchar *) it->data; + if (g_list_next (it) == NULL) + g_print ("%s ]\n", name); + else + g_print ("%s, ", name); + } +} + +static void +test_synchronous_operation (UcaCamera *camera) +{ + GError *error = NULL; + guint width, height, bits; + g_object_get (G_OBJECT (camera), + "sensor-width", &width, + "sensor-height", &height, + "sensor-bitdepth", &bits, + NULL); + + const int pixel_size = bits == 8 ? 1 : 2; + const gsize size = width * height * pixel_size; + const guint n_trials = 10000; + gpointer buffer = g_malloc0(size); + + uca_camera_start_recording (camera, &error); + GTimer *timer = g_timer_new (); + + for (guint n = 0; n < n_trials; n++) + uca_camera_grab (camera, &buffer, &error); + + gdouble total_time = g_timer_elapsed (timer, NULL); + g_timer_stop (timer); + + g_print ("Synchronous data transfer\n"); + g_print (" Bandwidth: %3.2f MB/s\n", size * n_trials / 1024. / 1024. / total_time); + g_print (" Throughput: %3.2f frames/s\n", n_trials / total_time); + + uca_camera_stop_recording (camera, &error); + g_free (buffer); + g_timer_destroy (timer); +} + +static void +grab_func (gpointer data, gpointer user_data) +{ + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + thread_data *d = (thread_data *) user_data; + g_memmove (d->destination, data, d->size); + g_static_mutex_lock (&mutex); + d->counter++; + g_static_mutex_unlock (&mutex); +} + +static void +test_asynchronous_operation (UcaCamera *camera) +{ + GError *error = NULL; + guint width, height, bits; + + g_object_get (G_OBJECT (camera), + "sensor-width", &width, + "sensor-height", &height, + "sensor-bitdepth", &bits, + NULL); + + const guint pixel_size = bits == 8 ? 1 : 2; + + thread_data d = { + .counter = 0, + .size = width * height * pixel_size, + .destination = g_malloc0(width * height * pixel_size) + }; + + g_object_set (G_OBJECT (camera), + "transfer-asynchronously", TRUE, + NULL); + + uca_camera_set_grab_func (camera, &grab_func, &d); + uca_camera_start_recording (camera, &error); + g_usleep (G_USEC_PER_SEC); + uca_camera_stop_recording (camera, &error); + + g_print ("Asynchronous data transfer\n"); + g_print (" Bandwidth: %3.2f MB/s\n", d.size * d.counter / 1024. / 1024.); + g_print (" Throughput: %i frames/s\n", d.counter); + + g_free (d.destination); +} + +int +main (int argc, char *argv[]) +{ + UcaPluginManager *manager; + GError *error = NULL; + (void) signal (SIGINT, sigint_handler); + + g_type_init (); + if (argc < 2) { + print_usage (); + return 1; + } + + manager = uca_plugin_manager_new (); + camera = uca_plugin_manager_get_camera (manager, argv[1], &error); + + if (camera == NULL) { + g_print ("Error during initialization: %s\n", error->message); + return 1; + } + + test_synchronous_operation (camera); + g_print ("\n"); + test_asynchronous_operation (camera); + + g_object_unref (camera); + g_object_unref (manager); + + return error != NULL ? 1 : 0; +} |