summaryrefslogtreecommitdiffstats
path: root/docs/index.rst
diff options
context:
space:
mode:
authorMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2014-07-11 10:36:06 +0200
committerMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2014-07-11 10:36:06 +0200
commit97c66c2aee58370044b6444251266d7177caab9e (patch)
treeb491fa86d2e9e34ccea9cb62854f04741f1fc033 /docs/index.rst
parent802046908ae2b39c25fa11a056a02fecb0f20a38 (diff)
downloadlibuca-97c66c2aee58370044b6444251266d7177caab9e.tar.gz
libuca-97c66c2aee58370044b6444251266d7177caab9e.tar.bz2
libuca-97c66c2aee58370044b6444251266d7177caab9e.tar.xz
libuca-97c66c2aee58370044b6444251266d7177caab9e.zip
Move to Sphinx-based documentation
Diffstat (limited to 'docs/index.rst')
-rw-r--r--docs/index.rst690
1 files changed, 690 insertions, 0 deletions
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..0ac412a
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,690 @@
+Welcome
+=======
+
+libuca is a light-weight camera abstraction library, focused on scientific
+cameras used at the ANKA synchrotron.
+
+
+Quickstart
+==========
+
+Installation
+------------
+
+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.
+
+
+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
+
+- CMake,
+- a C compiler,
+- GLib and GObject development libraries and
+- necessary camera SDKs
+
+installed.
+
+For the base system, install ::
+
+ [Debian] sudo apt-get install libglib2.0 cmake gcc
+ [openSUSE] sudo zypper in glib2-devel cmake gcc
+
+In case you want to use the graphical user interface you also need the
+Gtk+ development libraries::
+
+ [Debian] sudo apt-get install libgtk+2.0-dev
+ [openSUSE] sudo zypper in gtk2-devel
+
+To generate bindings for third-party languages, you have to install ::
+
+ [Debian] sudo apt-get install gobject-introspection
+ [openSUSE] sudo zypper in gobject-introspection-devel
+
+
+Fetching the sources
+^^^^^^^^^^^^^^^^^^^^
+
+Untar the distribution ::
+
+ untar xfz libuca-x.y.z.tar.gz
+
+or clone the repository ::
+
+ git clone http://ufo.kit.edu/git/libuca
+
+and create a new, empty build directory inside::
+
+ cd libuca/
+ mkdir build
+
+
+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::
+
+ cd build/
+ cmake ..
+
+As long as the last line reads "Build files have been written to", the
+configuration stage is successful. In this case you can build ``libuca``
+with ::
+
+ make
+
+and install with ::
+
+ sudo make install
+
+If an *essential* dependency could not be found, the configuration stage
+will stop and build files will not be written. If a *non-essential*
+dependency (such as a certain camera SDK) is not found, the
+configuration stage will continue but that particular camera support not
+built.
+
+If you want to customize the build process you can pass several
+variables to CMake::
+
+ cmake .. -DPREFIX=/usr -DLIBDIR=/usr/lib64
+
+The former tells CMake to install into ``/usr`` instead of
+``/usr/local`` and the latter that we want to install the libraries and
+plugins into the ``lib64`` subdir instead of the default ``lib`` subdir
+as it is common on SUSE systems.
+
+
+First look at the API
+---------------------
+
+.. highlight:: c
+
+The API for accessing cameras is straightforward. First you need to
+include the necessary header files::
+
+ #include <glib-object.h>
+ #include <uca/uca-plugin-manager.h>
+ #include <uca/uca-camera.h>
+
+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 */
+
+ g_type_init ();
+
+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 <http://ufo.kit.edu/repos/libpco.git/>`__. To instantiate a
+camera we have to create a plugin manager first::
+
+ 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 other than ``NULL``::
+
+ if (camera == NULL) {
+ g_error ("Initialization: %s", error->message);
+ return 1;
+ }
+
+You should always remove the
+`reference <http://developer.gnome.org/gobject/stable/gobject-memory.html#gobject-memory-refcount>`__
+from the camera object when not using it in order to free all associated
+resources::
+
+ g_object_unref (camera);
+ return 0;
+ }
+
+Compile this program with ::
+
+ cc `pkg-config --cflags --libs libuca glib-2.0` foo.c -o foo
+
+Now, run ``foo`` and verify that no errors occur.
+
+
+Grabbing frames
+~~~~~~~~~~~~~~~
+
+To synchronously grab frames, first start the camera::
+
+ uca_camera_start_recording (camera, &error);
+ g_assert_no_error (error);
+
+Now, you have to allocate a suitably sized buffer and pass it to
+``uca_camera_grab``::
+
+ gpointer buffer = g_malloc0 (640 * 480 * 2);
+
+ uca_camera_grab (camera, buffer, &error);
+
+You have to make sure that the buffer is large enough by querying the
+size of the region of interest and the number of bits that are
+transferred.
+
+
+Getting and setting camera parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Because camera parameters vary tremendously between different vendors
+and products, they are realized with so-called GObject *properties*, a
+mechanism that maps string keys to typed and access restricted values.
+To get a value, you use the ``g_object_get`` function and provide memory
+where the result is stored::
+
+ guint roi_width;
+ gdouble exposure_time;
+
+ g_object_get (G_OBJECT(camera),
+ "roi-width", &roi_width,
+ "exposure-time", &exposure_time,
+ /* The NULL marks the end! */
+ NULL
+ );
+
+ g_print ("Width of the region of interest: %d\n", roi_width);
+ g_print ("Exposure time: %3.5s\n", exposure_time);
+
+In a similar way, properties are set with ``g_object_set``::
+
+ guint roi_width = 512;
+ gdouble exposure_time = 0.001;
+
+ g_object_set (G_OBJECT (camera),
+ "roi-width", roi_width,
+ "exposure-time", exposure_time,
+ NULL);
+
+Each property can be associated with a physical unit. To query for the
+unit call ``uca_camera_get_unit`` and pass a property name. The function
+will then return a value from the ``UcaUnit`` enum.
+
+Several essential camera parameters *must* be implemented by all
+cameras. To get a list of them consult the API reference for
+```UcaCamera`` <http://ufo.kit.edu/extra/libuca/reference/UcaCamera.html#UcaCamera.properties>`__.
+For camera specific parameters you need to consult the corresponding API
+reference for ``UfoFooCamera``. The latest nightly built reference can
+be found `here <http://ufo.kit.edu/extra/libuca/reference/>`__.
+
+
+Supported cameras
+=================
+
+The following cameras are supported:
+
+- pco.edge, pco.dimax, pco.4000 (all CameraLink) via
+ `libpco <http://ufo.kit.edu/repos/libpco.git/>`__. You need to have
+ the SiliconSoftware frame grabber SDK with the ``menable`` kernel
+ module installed.
+- PhotonFocus
+- Pylon
+- UFO Camera developed at KIT/IPE.
+
+
+Property documentation
+----------------------
+
+- `mock <mock.html>`__
+- `pco <pco.html>`__
+- `PhotonFocus <pf.html>`__
+- `Ufo Camera <ufo.html>`__
+
+
+More API
+========
+
+In the `last section <#first-look-at-the-api>`__, we had a quick glance
+over the basic API used to communicate with the camera. Now we will go
+into more detail.
+
+
+Instantiating cameras
+---------------------
+
+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_plugin_manager_get_available_cameras``::
+
+ GList *types;
+
+ types = uca_camera_get_available_cameras (manager);
+
+ for (GList *it = g_list_first; it != NULL; it = g_list_next (it))
+ g_print ("%s\n", (gchar *) it->data);
+
+ /* free the strings and the list */
+ g_list_foreach (types, (GFunc) g_free, NULL);
+ g_list_free (types);
+
+
+Errors
+------
+
+All public API functions take a location of a pointer to a ``GError``
+structure as a last argument. You can pass in a ``NULL`` value, in which
+case you cannot be notified about exceptional behavior. On the other
+hand, if you pass in a pointer to a ``GError``, it must be initialized
+with ``NULL`` so that you do not accidentally overwrite and miss an
+error occurred earlier.
+
+Read more about ``GError``\ s in the official GLib
+`documentation <http://developer.gnome.org/glib/stable/glib-Error-Reporting.html>`__.
+
+
+Recording
+---------
+
+Recording frames is independent of actually grabbing them and is started
+with ``uca_camera_start_recording``. You should always stop the
+recording with ``ufo_camera_stop_recording`` when you finished. When the
+recording has started, you can grab frames synchronously as described
+earlier. In this mode, a block to ``uca_camera_grab`` blocks until a
+frame is read from the camera. Grabbing might block indefinitely, when
+the camera is not functioning correctly or it is not triggered
+automatically.
+
+
+Triggering
+----------
+
+``libuca`` supports three trigger modes through the "trigger-mode"
+property:
+
+1. ``UCA_CAMERA_TRIGGER_AUTO``: Exposure is triggered by the camera
+ itself.
+2. ``UCA_CAMERA_TRIGGER_INTERNAL``: Exposure is triggered via software.
+3. ``UCA_CAMERA_TRIGGER_EXTERNAL``: Exposure is triggered by an external
+ hardware mechanism.
+
+With ``UCA_CAMERA_TRIGGER_INTERNAL`` you have to trigger with
+``uca_camera_trigger``::
+
+ /* thread A */
+ g_object_set (G_OBJECT (camera),
+ "trigger-mode", UCA_CAMERA_TRIGGER_INTERNAL,
+ NULL);
+
+ uca_camera_start_recording (camera, NULL);
+ uca_camera_grab (camera, &buffer, NULL);
+ uca_camera_stop_recording (camera, NULL);
+
+ /* thread B */
+ uca_camera_trigger (camera, NULL);
+
+
+Grabbing frames asynchronously
+------------------------------
+
+In some applications, it might make sense to setup asynchronous frame
+acquisition, for which you will not be blocked by a call to ``libuca``::
+
+ static void
+ callback (gpointer buffer, gpointer user_data)
+ {
+ /*
+ * Do something useful with the buffer and the string we have got.
+ */
+ }
+
+ static void
+ setup_async (UcaCamera *camera)
+ {
+ gchar *s = g_strdup ("lorem ipsum");
+
+ g_object_set (G_OBJECT (camera),
+ "transfer-asynchronously", TRUE,
+ NULL);
+
+ uca_camera_set_grab_func (camera, callback, s);
+ uca_camera_start_recording (camera, NULL);
+
+ /*
+ * We will return here and `callback` will be called for each newo
+ * new frame.
+ */
+ }
+
+
+Bindings
+========
+
+.. highlight:: python
+
+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::
+
+ from gi.repository import Uca
+
+ pm = Uca.PluginManager()
+
+ # List all cameras
+ print(pm.get_available_cameras())
+
+ # Load a camera
+ cam = pm.get_camerav('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.
+
+Integration with Numpy is relatively straightforward. The most important
+thing is to get the data pointer from a Numpy array to pass it to
+``uca_camera_grab``::
+
+ import numpy as np
+
+ def create_array_from(camera):
+ """Create a suitably sized Numpy array and return it together with the
+ arrays data pointer"""
+ bits = camera.props.sensor_bitdepth
+ dtype = np.uint16 if bits > 8 else np.uint8
+ a = np.zeros((cam.props.roi_height, cam.props.roi_width), dtype=dtype)
+ return a, a.__array_interface__['data'][0]
+
+ # Suppose 'camera' is a already available, you would get the camera data like
+ # this:
+ a, buf = create_array_from(camera)
+ camera.start_recording()
+ camera.grab(buf)
+
+ # Now data is in 'a' and we can use Numpy functions on it
+ print(np.mean(a))
+
+ camera.stop_recording()
+
+
+Integrating new cameras
+=======================
+
+A new camera is integrated by
+`sub-classing <http://developer.gnome.org/gobject/stable/howto-gobject.html>`__
+``UcaCamera`` and implement all 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.
+
+
+Tools
+=====
+
+Several tools are available to ensure ``libuca`` works as expected. All
+of them are located in ``build/test/`` and some of them are installed
+with ``make installed``.
+
+
+``uca-grab`` -- grabbing frames
+-------------------------------
+
+Grab with frames with ::
+
+ $ uca-grab --num-frames=10 camera-model
+
+store them on disk as ``frames.tif`` if ``libtiff`` is installed,
+otherwise as ``frame-00000.raw``, ``frame-000001.raw``. The raw format
+is a memory dump of the frames, so you might want to use
+`ImageJ <http://rsbweb.nih.gov/ij/>`__ to view them. You can also
+specify the output filename or filename prefix with the ``-o/--output``
+option::
+
+ $ uca-grab -n 10 --output=foobar.tif camera-model
+
+Instead of reading exactly *n* frames, you can also specify a duration
+in fractions of seconds::
+
+ $ uca-grab --duration=0.25 camera-model
+
+
+``uca-camera-control`` -- simple graphical user interface
+---------------------------------------------------------
+
+Shows the frames and displays them on screen. Moreover, you can change
+the camera properties in a side pane.
+
+
+``uca-benchmark`` -- check bandwidth
+------------------------------------
+
+Measure the memory bandwidth by taking subsequent frames and averaging
+the grabbing time:
+
+::
+
+ $ ./benchmark mock
+ # --- General information ---
+ # Sensor size: 640x480
+ # ROI size: 640x480
+ # Exposure time: 0.000010s
+ # type n_frames n_runs frames/s MiB/s
+ sync 100 3 29848.98 8744.82
+ async 100 3 15739.43 4611.16
+
+
+The GObject Tango device
+========================
+
+UcaDevice is a generic Tango Device that wraps ``libuca`` in order to
+make libuca controlled cameras available as Tango devices.
+
+
+Architecture
+------------
+
+UcaDevice is derived from GObjectDevice and adds libuca specific features like
+start/stop recording etc. The most important feature is *acquisition control*.
+UcaDevice is responsible for controlling acquisition of images from libuca. The
+last aquired image can be accessed by reading attribute ``SingleImage``.
+UcaDevice is most useful together with ImageDevice. If used together, UcaDevice
+sends each aquired image to ImageDevice, which in turn does configured
+post-processing like flipping, rotating or writing the image to disk.
+
+
+Attributes
+~~~~~~~~~~
+
+Besides the dynamic attributes translated from libuca properties
+UcaDevice has the following attributes:
+
+- ``NumberOfImages (Tango::DevLong)``: how many images should be
+ acquired? A value of -1 means unlimited *(read/write)*
+- ``ImageCounter (Tango::DevULong)``: current number of acquired images
+ *(read-only)*
+- ``CameraName (Tango::DevString)``: name of libuca object type
+ *(read-only)*
+- ``SingleImage (Tango::DevUChar)``: holds the last acquired image
+
+
+Acquisition Control
+~~~~~~~~~~~~~~~~~~~
+
+In UcaDevice acquisition control is mostly implemented by two
+``yat4tango::DeviceTasks``: *AcquisitionTask* and *GrabTask*.
+*GrabTask*'s only responsibility is to call ``grab`` on ``libuca``
+synchronously and post the data on to AcquisitionTask.
+
+*AcquisitionTask* is responsible for starting and stopping GrabTask and
+for passing image data on to ``ImageDevice`` (if exisiting) and to
+``UcaDevice`` for storage in attribute ``SingleImage``. It counts how
+many images have been acquired and checks this number against the
+configured ``NumberOfImages``. If the desired number is reached, it
+stops GrabTask, calls ``stop_recording`` on ``libuca`` and sets the
+Tango state to STANDBY.
+
+
+Plugins
+~~~~~~~
+
+As different cameras have different needs, plugins are used for special
+implementations. Plugins also makes UcaDevice and Tango Servers based on
+it more flexible and independent of libuca implementation.
+
+* PCO: The Pco plugin implements additional checks when writing ROI values.
+* Pylon: The Pylon plugin sets default values for ``roi-width`` and
+ ``roi-height`` from libuca properties ``roi-width-default`` and
+ ``roi-height-default``.
+
+
+Installation
+------------
+
+Detailed installation depends on the manifestation of UcaDevice. All
+manifestations depend on the following libraries:
+
+- YAT
+- YAT4Tango
+- Tango
+- GObjectDevice
+- ImageDevice
+
+
+Build
+~~~~~
+
+::
+
+ export PKG_CONFIG_PATH=/usr/lib/pkgconfig
+ export PYLON_ROOT=/usr/pylon
+ export LD_LIBRARY_PATH=$PYLON_ROOT/lib64:$PYLON_ROOT/genicam/bin/Linux64_x64
+ git clone git@iss-repo:UcaDevice.git
+ cd UcaDevice
+ mkdir build
+ cd build
+ cmake ..
+ make
+
+
+Setup in Tango Database / Jive
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before ``ds_UcaDevice`` can be started, it has to be registered manually
+in the Tango database. With ``Jive`` the following steps are necessary:
+
+1. Register Server Menu *Tools* → Server Wizard Server name → ds\_UcaDevice
+ Instance name → my\_server *(name can be chosen freely)* Next Cancel
+
+2. Register Classes and Instances In tab *Server*: context menu on
+ ds\_UcaDevice → my\_server → Add Class Class: UcaDevice Devices:
+ ``iss/name1/name2`` Register server same for class ImageDevice
+
+3. Start server ::
+
+ export TANGO_HOST=anka-tango:100xx
+ export UCA_DEVICE_PLUGINS_DIR=/usr/lib(64)
+ ds_UcaDevice pco my_server
+
+4. Setup properties for UcaDevice context menu on device → Device wizard
+ Property StorageDevice: *address of previously registered ImageDevice
+ instance*
+
+5. Setup properties for ImageDevice context menu on device → Device wizard
+ PixelSize: how many bytes per pixel for the images of this camera?
+ GrabbingDevice: *address of previously registered UcaDevice instance*
+
+6. Finish restart ds_UcaDevice
+
+FAQ
+---
+
+*UcaDevice refuses to start up...?* Most likely there is no instance
+registered for class UcaDevice. Register an instance for this class and
+it should work.
+
+*Why does UcaDevice depend on ImageDevice?* UcaDevice pushes each new
+Frame to ImageDevice. Polling is not only less efficient but also prone
+to errors, e.g. missed/double frames and so on. Perhaps we could use the
+Tango-Event-System here!
+
+Open Questions, Missing Features etc.
+-------------------------------------
+
+* *Why do we need to specify ``Storage`` for UcaDevice and ``GrabbingDevice``
+ for ImageDevice?*
+
+ ImageDevice needs the Tango-Address of UcaDevice to mirror all Attributes and
+ Commands and to forward them to it. UcaDevice needs the Tango-Address of
+ ImageDevice to push a new frame on reception. A more convenient solution could
+ be using conventions for the device names, e.g. of the form
+ ``$prefix/$instance_name/uca`` and ``$prefix/$instance_name/image``. That way
+ we could get rid of the two Device-Properties and an easier installation
+ without the process of registering the classes and instances in ``Jive``.
+
+* *Why does UcaDevice dynamically link to GObjectDevice?*
+
+ There is no good reason for it. Packaging and installing would be easier if we
+ linked statically to ``GObjectDevice`` because we would hide this dependency.
+ Having a separate ``GObjectDevice`` is generally a nice feature to make
+ ``GObjects`` available in Tango. However, there is currently no GObjectDevice
+ in use other than in the context of UcaDevice.
+
+* *Why must the plugin name be given as a command line parameter instead of a
+ Device-Property?*
+
+ There is no good reason for it. UcaDevice would be easier to use, if the
+ plugin was configured in the Tango database as a Device-Property for the
+ respective server instance.