diff -pruN 0.5.0-1/.b4-config 0.5.2-2/.b4-config
--- 0.5.0-1/.b4-config	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/.b4-config	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,2 @@
+[b4]
+  send-series-to = libcamera-devel@lists.libcamera.org
diff -pruN 0.5.0-1/Documentation/Doxyfile-common.in 0.5.2-2/Documentation/Doxyfile-common.in
--- 0.5.0-1/Documentation/Doxyfile-common.in	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/Doxyfile-common.in	2025-08-07 13:46:17.000000000 +0000
@@ -57,7 +57,8 @@ GENERATE_LATEX         = NO
 MACRO_EXPANSION        = YES
 EXPAND_ONLY_PREDEF     = YES
 
-INCLUDE_PATH           = "@TOP_SRCDIR@/include/libcamera"
+INCLUDE_PATH           = "@TOP_BUILDDIR@/include" \
+                         "@TOP_SRCDIR@/include"
 INCLUDE_FILE_PATTERNS  = *.h
 
 IMAGE_PATH             = "@TOP_SRCDIR@/Documentation/images"
diff -pruN 0.5.0-1/Documentation/Doxyfile-internal.in 0.5.2-2/Documentation/Doxyfile-internal.in
--- 0.5.0-1/Documentation/Doxyfile-internal.in	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/Doxyfile-internal.in	2025-08-07 13:46:17.000000000 +0000
@@ -3,6 +3,8 @@
 @INCLUDE_PATH          = @TOP_BUILDDIR@/Documentation
 @INCLUDE               = Doxyfile-common
 
+GENERATE_TAGFILE       = @TOP_BUILDDIR@/Documentation/internal-api-html/tagfile.xml
+
 HIDE_UNDOC_CLASSES     = NO
 HIDE_UNDOC_MEMBERS     = NO
 HTML_OUTPUT            = internal-api-html
diff -pruN 0.5.0-1/Documentation/Doxyfile-public.in 0.5.2-2/Documentation/Doxyfile-public.in
--- 0.5.0-1/Documentation/Doxyfile-public.in	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/Doxyfile-public.in	2025-08-07 13:46:17.000000000 +0000
@@ -3,6 +3,8 @@
 @INCLUDE_PATH          = @TOP_BUILDDIR@/Documentation
 @INCLUDE               = Doxyfile-common
 
+GENERATE_TAGFILE       = @TOP_BUILDDIR@/Documentation/api-html/tagfile.xml
+
 HIDE_UNDOC_CLASSES     = YES
 HIDE_UNDOC_MEMBERS     = YES
 HTML_OUTPUT            = api-html
diff -pruN 0.5.0-1/Documentation/conf.py 0.5.2-2/Documentation/conf.py
--- 0.5.0-1/Documentation/conf.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/conf.py	2025-08-07 13:46:17.000000000 +0000
@@ -21,8 +21,8 @@
 # -- Project information -----------------------------------------------------
 
 project = 'libcamera'
-copyright = '2018-2019, The libcamera documentation authors'
-author = u'Kieran Bingham, Jacopo Mondi, Laurent Pinchart, Niklas Söderlund'
+copyright = '2018-2025, The libcamera documentation authors'
+author = 'The libcamera documentation authors'
 
 # Version information is provided by the build environment, through the
 # sphinx command line.
@@ -37,7 +37,8 @@ author = u'Kieran Bingham, Jacopo Mondi,
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
-    'sphinx.ext.graphviz'
+    'sphinx.ext.graphviz',
+    'sphinxcontrib.doxylink',
 ]
 
 graphviz_output_format = 'svg'
@@ -65,15 +66,22 @@ language = 'en'
 # directories to ignore when looking for source files.
 # This pattern also affects html_static_path and html_extra_path.
 exclude_patterns = [
-    '_build',
-    'Thumbs.db',
-    '.DS_Store',
     'documentation-contents.rst',
 ]
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = None
 
+doxylink = {
+    'doxy-pub': (
+        'Documentation/api-html/tagfile.xml',
+        '../api-html/',
+    ),
+    'doxy-int': (
+        'Documentation/internal-api-html/tagfile.xml',
+        '../internal-api-html/',
+    ),
+}
 
 # -- Options for HTML output -------------------------------------------------
 
@@ -103,78 +111,3 @@ html_static_path = []
 # 'searchbox.html']``.
 #
 # html_sidebars = {}
-
-
-# -- Options for HTMLHelp output ---------------------------------------------
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'libcameradoc'
-
-
-# -- Options for LaTeX output ------------------------------------------------
-
-latex_elements = {
-    # The paper size ('letterpaper' or 'a4paper').
-    #
-    # 'papersize': 'letterpaper',
-
-    # The font size ('10pt', '11pt' or '12pt').
-    #
-    # 'pointsize': '10pt',
-
-    # Additional stuff for the LaTeX preamble.
-    #
-    # 'preamble': '',
-
-    # Latex figure (float) alignment
-    #
-    # 'figure_align': 'htbp',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-    (master_doc, 'libcamera.tex', 'libcamera Documentation',
-     author, 'manual'),
-]
-
-
-# -- Options for manual page output ------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'libcamera', 'libcamera Documentation',
-     [author], 1)
-]
-
-
-# -- Options for Texinfo output ----------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-    (master_doc, 'libcamera', 'libcamera Documentation',
-     author, 'libcamera', 'One line description of project.',
-     'Miscellaneous'),
-]
-
-
-# -- Options for Epub output -------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = project
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#
-# epub_identifier = ''
-
-# A unique identification for the text.
-#
-# epub_uid = ''
-
-# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
diff -pruN 0.5.0-1/Documentation/environment_variables.rst 0.5.2-2/Documentation/environment_variables.rst
--- 0.5.0-1/Documentation/environment_variables.rst	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/environment_variables.rst	2025-08-07 13:46:17.000000000 +0000
@@ -90,7 +90,7 @@ The optional `category <Log categories_>
 defined by each file in the source base using the logging infrastructure. It
 can include a wildcard ('*') character at the end to match multiple categories.
 
-For more information refer to the `API documentation <https://libcamera.org/api-html/log_8h.html#details>`__.
+For more information refer to the :doxy-int:`API documentation <log.h>`.
 
 Examples:
 
diff -pruN 0.5.0-1/Documentation/guides/application-developer.rst 0.5.2-2/Documentation/guides/application-developer.rst
--- 0.5.0-1/Documentation/guides/application-developer.rst	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/guides/application-developer.rst	2025-08-07 13:46:17.000000000 +0000
@@ -46,15 +46,14 @@ defined names and types without the need
 Camera Manager
 --------------
 
-Every libcamera-based application needs an instance of a `CameraManager`_ that
-runs for the life of the application. When the Camera Manager starts, it
+Every libcamera-based application needs an instance of a :doxy-pub:`CameraManager`
+that runs for the life of the application. When the Camera Manager starts, it
 enumerates all the cameras detected in the system. Behind the scenes, libcamera
 abstracts and manages the complex pipelines that kernel drivers expose through
 the `Linux Media Controller`_ and `Video for Linux`_ (V4L2) APIs, meaning that
 an application doesn't need to handle device or driver specific details.
 
-.. _CameraManager: https://libcamera.org/api-html/classlibcamera_1_1CameraManager.html
-.. _Linux Media Controller: https://www.kernel.org/doc/html/latest/media/uapi/mediactl/media-controller-intro.html
+.. _Linux Media Controller: https://www.kernel.org/doc/html/latest/userspace-api/media/mediactl/media-controller.html
 .. _Video for Linux: https://www.linuxtv.org/docs.php
 
 Before the ``int main()`` function, create a global shared pointer
@@ -210,10 +209,9 @@ function. If the new values are not supp
 validation process adjusts the parameters to what it considers to be the closest
 supported values.
 
-The ``validate`` function returns a `Status`_ which applications shall check to
-see if the Pipeline Handler adjusted the configuration.
-
-.. _Status: https://libcamera.org/api-html/classlibcamera_1_1CameraConfiguration.html#a64163f21db2fe1ce0a6af5a6f6847744
+The ``validate`` function returns a :doxy-pub:`CameraConfiguration::Status`
+which applications shall check to see if the Pipeline Handler adjusted the
+configuration.
 
 For example, the code above set the width and height to 640x480, but if the
 camera cannot produce an image that large, it might adjust the configuration to
@@ -348,10 +346,10 @@ camera device, and associate a buffer fo
 Event handling and callbacks
 ----------------------------
 
-The libcamera library uses the concept of `signals and slots` (similar to `Qt
-Signals and Slots`_) to connect events with callbacks to handle them.
+The libcamera library uses the concept of :doxy-pub:`signals and slots <Signal>`
+(similar to `Qt Signals and Slots`_) to connect events with callbacks to handle
+them.
 
-.. _signals and slots: https://libcamera.org/api-html/classlibcamera_1_1Signal.html#details
 .. _Qt Signals and Slots: https://doc.qt.io/qt-6/signalsandslots.html
 
 The ``Camera`` device emits two signals that applications can connect to in
@@ -400,9 +398,7 @@ Request completion events can be emitted
 for example, by unexpected application shutdown. To avoid an application
 processing invalid image data, it's worth checking that the request has
 completed successfully. The list of request completion statuses is available in
-the `Request::Status`_ class enum documentation.
-
-.. _Request::Status: https://www.libcamera.org/api-html/classlibcamera_1_1Request.html#a2209ba8d51af8167b25f6e3e94d5c45b
+the :doxy-pub:`Request::Status` class enum documentation.
 
 .. code:: cpp
 
@@ -422,9 +418,7 @@ Iterating through the map allows applica
 in this request, and access the metadata associated to each frame.
 
 The metadata buffer contains information such the capture status, a timestamp,
-and the bytes used, as described in the `FrameMetadata`_ documentation.
-
-.. _FrameMetaData: https://libcamera.org/api-html/structlibcamera_1_1FrameMetadata.html
+and the bytes used, as described in the :doxy-pub:`FrameMetadata` documentation.
 
 .. code:: cpp
 
@@ -515,13 +509,11 @@ and queue all the previously created req
 Event processing
 ~~~~~~~~~~~~~~~~
 
-libcamera creates an internal execution thread at `CameraManager::start()`_
+libcamera creates an internal execution thread at :doxy-pub:`CameraManager::start()`
 time to decouple its own event processing from the application's main thread.
 Applications are thus free to manage their own execution opportunely, and only
 need to respond to events generated by libcamera emitted through signals.
 
-.. _CameraManager::start(): https://libcamera.org/api-html/classlibcamera_1_1CameraManager.html#a49e322880a2a26013bb0076788b298c5
-
 Real-world applications will likely either integrate with the event loop of the
 framework they use, or create their own event loop to respond to user events.
 For the simple application presented in this example, it is enough to prevent
@@ -618,7 +610,7 @@ accordingly. In this example, the applic
 
    simple_cam = executable('simple-cam',
        'simple-cam.cpp',
-       dependencies: dependency('libcamera', required : true))
+       dependencies: dependency('libcamera'))
 
 The ``dependencies`` line instructs meson to ask ``pkgconfig`` (or ``cmake``) to
 locate the ``libcamera`` library,  which the test application will be
diff -pruN 0.5.0-1/Documentation/guides/pipeline-handler.rst 0.5.2-2/Documentation/guides/pipeline-handler.rst
--- 0.5.0-1/Documentation/guides/pipeline-handler.rst	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/guides/pipeline-handler.rst	2025-08-07 13:46:17.000000000 +0000
@@ -83,49 +83,49 @@ functionalities described above. Below i
 .. TODO: (MediaDevice) Reference to the Media Device API (possibly with versioning requirements)
 .. TODO: (IPAInterface) refer to the IPA guide
 
--  `MediaDevice <https://libcamera.org/api-html/classlibcamera_1_1MediaDevice.html>`_:
+-  :doxy-int:`MediaDevice`:
    Instances of this class are associated with a kernel media controller
    device and its connected objects.
 
--  `DeviceEnumerator <https://libcamera.org/api-html/classlibcamera_1_1DeviceEnumerator.html>`_:
+-  :doxy-int:`DeviceEnumerator`:
    Enumerates all media devices attached to the system and the media entities
    registered with it, by creating instances of the ``MediaDevice`` class and
    storing them.
 
--  `DeviceMatch <https://libcamera.org/api-html/classlibcamera_1_1DeviceMatch.html>`_:
+-  :doxy-int:`DeviceMatch`:
    Describes a media device search pattern using entity names, or other
    properties.
 
--  `V4L2VideoDevice <https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html>`_:
+-  :doxy-int:`V4L2VideoDevice`:
    Models an instance of a V4L2 video device constructed with the path to a V4L2
    video device node.
 
--  `V4L2SubDevice <https://libcamera.org/api-html/classlibcamera_1_1V4L2Subdevice.html>`_:
+-  :doxy-int:`V4L2Subdevice`:
    Provides an API to the sub-devices that model the hardware components of a
    V4L2 device.
 
--  `CameraSensor <https://libcamera.org/api-html/classlibcamera_1_1CameraSensor.html>`_:
+-  :doxy-int:`CameraSensor`:
    Abstracts camera sensor handling by hiding the details of the V4L2 subdevice
    kernel API and caching sensor information.
 
--  `Camera::Private <https://libcamera.org/api-html/classlibcamera_1_1Camera_1_1Private.html>`_:
+-  :doxy-int:`Camera::Private`:
    Represents device-specific data a pipeline handler associates to each Camera
    instance.
 
--  `StreamConfiguration <https://libcamera.org/api-html/structlibcamera_1_1StreamConfiguration.html>`_:
+-  :doxy-int:`StreamConfiguration`:
    Models the current configuration of an image stream produced by the camera by
    reporting its format and sizes.
 
--  `CameraConfiguration <https://libcamera.org/api-html/classlibcamera_1_1CameraConfiguration.html>`_:
+-  :doxy-int:`CameraConfiguration`:
    Represents the current configuration of a camera, which includes a list of
    stream configurations for each active stream in a capture session. When
    validated, it is applied to the camera.
 
--  `IPAInterface <https://libcamera.org/api-html/classlibcamera_1_1IPAInterface.html>`_:
+-  :doxy-int:`IPAInterface`:
    The interface to the Image Processing Algorithm (IPA) module which performs
    the computation of the image processing pipeline tuning parameters.
 
--  `ControlList <https://libcamera.org/api-html/classlibcamera_1_1ControlList.html>`_:
+-  :doxy-int:`ControlList`:
    A list of control items, indexed by Control<> instances or by numerical index
    which contains values used by application and IPA to change parameters of
    image streams, used to return to applications and share with IPA the metadata
@@ -191,10 +191,8 @@ to the libcamera build options in the to
 
 
 In *vivid.cpp* add the pipeline handler to the ``libcamera`` namespace, defining
-a `PipelineHandler`_ derived class named PipelineHandlerVivid, and add stub
-implementations for the overridden class members.
-
-.. _PipelineHandler: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html
+a :doxy-int:`PipelineHandler` derived class named PipelineHandlerVivid, and add
+stub implementations for the overridden class members.
 
 .. code-block:: cpp
 
@@ -213,7 +211,7 @@ implementations for the overridden class
           std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
           int start(Camera *camera, const ControlList *controls) override;
-          void stop(Camera *camera) override;
+          void stopDevice(Camera *camera) override;
 
           int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -247,7 +245,7 @@ implementations for the overridden class
           return -1;
    }
 
-   void PipelineHandlerVivid::stop(Camera *camera)
+   void PipelineHandlerVivid::stopDevice(Camera *camera)
    {
    }
 
@@ -266,21 +264,17 @@ implementations for the overridden class
    } /* namespace libcamera */
 
 Note that you must register the ``PipelineHandler`` subclass with the pipeline
-handler factory using the `REGISTER_PIPELINE_HANDLER`_ macro which
+handler factory using the :doxy-int:`REGISTER_PIPELINE_HANDLER` macro which
 registers it and creates a global symbol to reference the class and make it
 available to try and match devices.
 String "vivid" is the name assigned to the pipeline, matching the pipeline
 subdirectory name in the source tree.
 
-.. _REGISTER_PIPELINE_HANDLER: https://libcamera.org/api-html/pipeline__handler_8h.html
-
 For debugging and testing a pipeline handler during development, you can define
 a log message category for the pipeline handler. The ``LOG_DEFINE_CATEGORY``
 macro and ``LIBCAMERA_LOG_LEVELS`` environment variable help you use the inbuilt
-libcamera `logging infrastructure`_ that allow for the inspection of internal
-operations in a user-configurable way.
-
-.. _logging infrastructure: https://libcamera.org/api-html/log_8h.html
+libcamera :doxy-int:`logging infrastructure <log.h>` that allow for the
+inspection of internal operations in a user-configurable way.
 
 Add the following before the ``PipelineHandlerVivid`` class declaration:
 
@@ -326,21 +320,18 @@ system configuration, by matching a ``De
 have been registered in the system and allows the pipeline handler to be
 initialized.
 
-The main entry point of a pipeline handler is the `match()`_ class member
-function. When the ``CameraManager`` is started (using the `start()`_ function),
-all the registered pipeline handlers are iterated and their ``match`` function
-called with an enumerator of all devices it found on a system.
+The main entry point of a pipeline handler is the :doxy-int:`PipelineHandler::match`
+class member function. When the ``CameraManager`` is started (using the
+:doxy-int:`CameraManager::start` function), all the registered pipeline
+handlers are iterated and their ``match`` function called with an enumerator of
+all devices it found on a system.
 
 The match function should identify if there are suitable devices available in
 the ``DeviceEnumerator`` which the pipeline supports, returning ``true`` if it
 matches a device, and ``false`` if it does not. To do this, construct a
-`DeviceMatch`_ class with the name of the ``MediaController`` device to match.
-You can specify the search further by adding specific media entities to the
-search using the ``.add()`` function on the DeviceMatch.
-
-.. _match(): https://www.libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a7cd5b652a2414b543ec20ba9dabf61b6
-.. _start(): https://libcamera.org/api-html/classlibcamera_1_1CameraManager.html#a49e322880a2a26013bb0076788b298c5
-.. _DeviceMatch: https://libcamera.org/api-html/classlibcamera_1_1DeviceMatch.html
+:doxy-int:`DeviceMatch` class with the name of the ``MediaController`` device
+to match. You can specify the search further by adding specific media entities
+to the search using the ``.add()`` function on the DeviceMatch.
 
 This example uses search patterns that match vivid, but when developing a new
 pipeline handler, you should change this value to suit your device identifier.
@@ -355,11 +346,9 @@ following:
    return false; // Prevent infinite loops for now
 
 With the device matching criteria defined, attempt to acquire exclusive access
-to the matching media controller device with the `acquireMediaDevice`_ function.
-If the function attempts to acquire a device it has already matched, it returns
-``false``.
-
-.. _acquireMediaDevice: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a77e424fe704e7b26094164b9189e0f84
+to the matching media controller device with the :doxy-int:`PipelineHandler::acquireMediaDevice`
+function. If the function attempts to acquire a device it has already matched,
+it returns ``false``.
 
 Add the following below ``dm.add("vivid-000-vid-cap");``:
 
@@ -421,12 +410,9 @@ receivers port output.
 The Pipeline Handler is responsible for defining the set of Streams associated
 with the Camera.
 
-Each Camera has instance-specific data represented using the `Camera::Private`_
+Each Camera has instance-specific data represented using the :doxy-int:`Camera::Private`
 class, which can be extended for the specific needs of the pipeline handler.
 
-.. _Camera::Private: https://libcamera.org/api-html/classlibcamera_1_1Camera_1_1Private.html
-
-
 To support the Camera we will later register, we need to create a Camera::Private
 class that we can implement for our specific Pipeline Handler.
 
@@ -476,11 +462,9 @@ and is usually responsible for opening a
 
 We can now implement the ``init`` function for our example Pipeline Handler to
 create a new V4L2 video device from the media entity, which we can specify using
-the `MediaDevice::getEntityByName`_ function from the MediaDevice. As our
-example is based upon the simplistic Vivid test device, we only need to open a
-single capture device named 'vivid-000-vid-cap' by the device.
-
-.. _MediaDevice::getEntityByName: https://libcamera.org/api-html/classlibcamera_1_1MediaDevice.html#ad5d9279329ef4987ceece2694b33e230
+the :doxy-int:`MediaDevice::getEntityByName` function from the MediaDevice. As
+our example is based upon the simplistic Vivid test device, we only need to
+open a single capture device named 'vivid-000-vid-cap' by the device.
 
 .. code-block:: cpp
 
@@ -514,21 +498,18 @@ handler.
 Once the camera data has been initialized, the Camera device instances and the
 associated streams have to be registered. Create a set of streams for the
 camera, which for this device is only one. You create a camera using the static
-`Camera::create`_ function, passing the Camera::Private instance, the id of the
-camera, and the streams available. Then register the camera with the pipeline
-handler and camera manager using `registerCamera`_.
+:doxy-int:`Camera::create` function, passing the Camera::Private instance, the
+id of the camera, and the streams available. Then register the camera with the
+pipeline handler and camera manager using :doxy-int:`PipelineHandler::registerCamera`.
 
 Finally with a successful construction, we return 'true' indicating that the
 PipelineHandler successfully matched and constructed a device.
 
-.. _Camera::create: https://libcamera.org/api-html/classlibcamera_1_1Camera.html#a453740e0d2a2f495048ae307a85a2574
-.. _registerCamera: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#adf02a7f1bbd87aca73c0e8d8e0e6c98b
-
 .. code-block:: cpp
 
    std::set<Stream *> streams{ &data->stream_ };
-   std::shared_ptr<Camera> camera = Camera::create(this, data->video_->deviceName(), streams);
-   registerCamera(std::move(camera), std::move(data));
+   std::shared_ptr<Camera> camera = Camera::create(std::move(data), data->video_->deviceName(), streams);
+   registerCamera(std::move(camera));
 
    return true;
 
@@ -554,8 +535,7 @@ Our match function should now look like
 
    	/* Create and register the camera. */
    	std::set<Stream *> streams{ &data->stream_ };
-   	const std::string &id = data->video_->deviceName();
-   	std::shared_ptr<Camera> camera = Camera::create(data.release(), id, streams);
+   	std::shared_ptr<Camera> camera = Camera::create(std::move(data), data->video_->deviceName(), streams);
    	registerCamera(std::move(camera));
 
    	return true;
@@ -586,33 +566,26 @@ interface, and device interaction interf
 Registering controls and properties
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The libcamera `controls framework`_ allows an application to configure the
-streams capture parameters on a per-frame basis and is also used to advertise
-immutable properties of the ``Camera`` device.
+The libcamera :doxy-int:`controls framework <controls.h>` allows an application
+to configure the streams capture parameters on a per-frame basis and is also
+used to advertise immutable properties of the ``Camera`` device.
 
 The libcamera controls and properties are defined in YAML form which is
 processed to automatically generate documentation and interfaces. Controls are
-defined by the src/libcamera/`control_ids_core.yaml`_ file and camera properties
-are defined by src/libcamera/`properties_ids_core.yaml`_.
-
-.. _controls framework: https://libcamera.org/api-html/controls_8h.html
-.. _control_ids_core.yaml: https://libcamera.org/api-html/control__ids_8h.html
-.. _properties_ids_core.yaml: https://libcamera.org/api-html/property__ids_8h.html
+defined by the :doxy-int:`src/libcamera/control_ids_core.yaml <control_ids.h>`
+file and camera properties are defined by
+:doxy-int:`src/libcamera/property_ids_core.yaml <property_ids.h>`.
 
 Pipeline handlers can optionally register the list of controls an application
 can set as well as a list of immutable camera properties. Being both
 Camera-specific values, they are represented in the ``Camera::Private`` base
 class, which provides two members for this purpose: the
-`Camera::Private::controlInfo_`_ and the `Camera::Private::properties_`_ fields.
-
-.. _Camera::Private::controlInfo_: https://libcamera.org/api-html/classlibcamera_1_1Camera_1_1Private.html#ab4e183eb4dabe929d1b2bbbb519b969f
-.. _Camera::Private::properties_: https://libcamera.org/api-html/classlibcamera_1_1Camera_1_1Private.html#ad31f12f5ed9c1fbe25750902f4791064
+:doxy-int:`Camera::Private::controlInfo_` and the
+:doxy-int:`Camera::Private::properties_` fields.
 
 The ``controlInfo_`` field represents a map of ``ControlId`` instances
 associated with the limits of valid values supported for the control. More
-information can be found in the `ControlInfoMap`_ class documentation.
-
-.. _ControlInfoMap: https://libcamera.org/api-html/classlibcamera_1_1ControlInfoMap.html
+information can be found in the :doxy-int:`ControlInfoMap` class documentation.
 
 Pipeline handlers register controls to expose the tunable device and IPA
 parameters to applications. Our example pipeline handler only exposes trivial
@@ -754,10 +727,8 @@ This configuration and validation proces
 specific class derived from a common base implementation and interface.
 
 To support validation in our example pipeline handler, Create a new class called
-``VividCameraConfiguration`` derived from the base `CameraConfiguration`_ class
-which we can implement and use within our ``PipelineHandlerVivid`` class.
-
-.. _CameraConfiguration: https://libcamera.org/api-html/classlibcamera_1_1CameraConfiguration.html
+``VividCameraConfiguration`` derived from the base :doxy-int:`CameraConfiguration`
+class which we can implement and use within our ``PipelineHandlerVivid`` class.
 
 The derived ``CameraConfiguration`` class must override the base class
 ``validate()`` function, where the stream configuration inspection and
@@ -779,20 +750,16 @@ adjustment happens.
     }
 
 Applications generate a ``CameraConfiguration`` instance by calling the
-`Camera::generateConfiguration()`_ function, which calls into the pipeline
-implementation of the overridden `PipelineHandler::generateConfiguration()`_
-function.
-
-.. _Camera::generateConfiguration(): https://libcamera.org/api-html/classlibcamera_1_1Camera.html#a25c80eb7fc9b1cf32692ce0c7f09991d
-.. _PipelineHandler::generateConfiguration(): https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a7932e87735695500ce1f8c7ae449b65b
+:doxy-int:`Camera::generateConfiguration` function, which calls into the
+pipeline implementation of the overridden
+:doxy-int:`PipelineHandler::generateConfiguration` function.
 
 Configurations are generated by receiving a list of ``StreamRole`` instances,
 which libcamera uses as predefined ways an application intends to use a camera
-(You can read the full list in the `StreamRole API`_ documentation). These are
-optional hints on how an application intends to use a stream, and a pipeline
-handler should return an ideal configuration for each role that is requested.
-
-.. _StreamRole API: https://libcamera.org/api-html/stream_8h.html#file_a295d1f5e7828d95c0b0aabc0a8baac03
+(You can read the full list in the :doxy-int:`StreamRole` API documentation).
+These are optional hints on how an application intends to use a stream, and a
+pipeline handler should return an ideal configuration for each role that is
+requested.
 
 In the pipeline handler ``generateConfiguration`` implementation, remove the
 ``return nullptr;``, create a new instance of the ``CameraConfiguration``
@@ -800,8 +767,7 @@ derived class, and assign it to a base c
 
 .. code-block:: cpp
 
-   VividCameraData *data = cameraData(camera);
-   CameraConfiguration *config = new VividCameraConfiguration();
+   auto config = std::make_unique<VividCameraConfiguration>();
 
 A ``CameraConfiguration`` is specific to each pipeline, so you can only create
 it from the pipeline handler code path. Applications can also generate an empty
@@ -829,9 +795,7 @@ To generate a ``StreamConfiguration``, y
 frame sizes which are supported as outputs of the stream. You can fetch a map of
 the ``V4LPixelFormat`` and ``SizeRange`` supported by the underlying output
 device, but the pipeline handler needs to convert this to a
-``libcamera::PixelFormat`` type to pass to applications. We do this here using
-``std::transform`` to convert the formats and populate a new ``PixelFormat`` map
-as shown below.
+``libcamera::PixelFormat`` type to pass to applications.
 
 Continue adding the following code example to our ``generateConfiguration``
 implementation.
@@ -841,20 +805,16 @@ implementation.
    std::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =
            data->video_->formats();
    std::map<PixelFormat, std::vector<SizeRange>> deviceFormats;
-   std::transform(v4l2Formats.begin(), v4l2Formats.end(),
-          std::inserter(deviceFormats, deviceFormats.begin()),
-          [&](const decltype(v4l2Formats)::value_type &format) {
-              return decltype(deviceFormats)::value_type{
-                  format.first.toPixelFormat(),
-                  format.second
-              };
-          });
-
-The `StreamFormats`_ class holds information about the pixel formats and frame
-sizes that a stream can support. The class groups size information by the pixel
-format, which can produce it.
 
-.. _StreamFormats: https://libcamera.org/api-html/classlibcamera_1_1StreamFormats.html
+   for (auto &[v4l2PixelFormat, sizes] : v4l2Formats) {
+           PixelFormat pixelFormat = v4l2PixelFormat.toPixelFormat();
+           if (pixelFormat.isValid())
+                   deviceFormats.try_emplace(pixelFormat, std::move(sizes));
+   }
+
+The :doxy-int:`StreamFormats` class holds information about the pixel formats
+and frame sizes that a stream can support. The class groups size information by
+the pixel format, which can produce it.
 
 The code below uses the ``StreamFormats`` class to represent all of the
 supported pixel formats, associated with a list of frame sizes. It then
@@ -899,16 +859,14 @@ Add the following code to complete the i
    return config;
 
 To validate a camera configuration, a pipeline handler must implement the
-`CameraConfiguration::validate()`_ function in its derived class to inspect all
-the stream configuration associated to it, make any adjustments required to make
-the configuration valid, and return the validation status.
+:doxy-int:`CameraConfiguration::validate` function in its derived class to
+inspect all the stream configuration associated to it, make any adjustments
+required to make the configuration valid, and return the validation status.
 
 If changes are made, it marks the configuration as ``Adjusted``, however if the
 requested configuration is not supported and cannot be adjusted it shall be
 refused and marked as ``Invalid``.
 
-.. _CameraConfiguration::validate(): https://libcamera.org/api-html/classlibcamera_1_1CameraConfiguration.html#a29f8f263384c6149775b6011c7397093
-
 The validation phase makes sure all the platform-specific constraints are
 respected by the requested configuration. The most trivial examples being making
 sure the requested image formats are supported and the image alignment
@@ -938,9 +896,9 @@ Add the following function implementatio
 
            StreamConfiguration &cfg = config_[0];
 
-           const std::vector<libcamera::PixelFormat> formats = cfg.formats().pixelformats();
+           const std::vector<libcamera::PixelFormat> &formats = cfg.formats().pixelformats();
            if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) {
-                  cfg.pixelFormat = cfg.formats().pixelformats()[0];
+                  cfg.pixelFormat = formats[0];
                   LOG(VIVID, Debug) << "Adjusting format to " << cfg.pixelFormat.toString();
                   status = Adjusted;
            }
@@ -1002,13 +960,10 @@ With the configuration generated, and op
 pipeline handler needs a function that allows an application to apply a
 configuration to the hardware devices.
 
-The `PipelineHandler::configure()`_ function receives a valid
-`CameraConfiguration`_ and applies the settings to hardware devices, using its
-parameters to prepare a device for a streaming session with the desired
-properties.
-
-.. _PipelineHandler::configure(): https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a930f2a9cdfb51dfb4b9ca3824e84fc29
-.. _CameraConfiguration: https://libcamera.org/api-html/classlibcamera_1_1CameraConfiguration.html
+The :doxy-int:`PipelineHandler::configure` function receives a valid
+:doxy-int:`CameraConfiguration` and applies the settings to hardware devices,
+using its parameters to prepare a device for a streaming session with the
+desired properties.
 
 Replace the contents of the stubbed ``PipelineHandlerVivid::configure`` function
 with the following to obtain the camera data and stream configuration. This
@@ -1023,16 +978,14 @@ system accordingly.
    StreamConfiguration &cfg = config->at(0);
    int ret;
 
-The Vivid capture device is a V4L2 video device, so we use a `V4L2DeviceFormat`_
-with the fourcc and size attributes to apply directly to the capture device
-node. The fourcc attribute is a `V4L2PixelFormat`_ and differs from the
-``libcamera::PixelFormat``. Converting the format requires knowledge of the
-plane configuration for multiplanar formats, so you must explicitly convert it
-using the helper ``V4L2VideoDevice::toV4L2PixelFormat()`` provided by the
-V4L2VideoDevice instance that the format will be applied on.
-
-.. _V4L2DeviceFormat: https://libcamera.org/api-html/classlibcamera_1_1V4L2DeviceFormat.html
-.. _V4L2PixelFormat: https://libcamera.org/api-html/classlibcamera_1_1V4L2PixelFormat.html
+The Vivid capture device is a V4L2 video device, so we use a
+:doxy-int:`V4L2DeviceFormat` with the fourcc and size attributes to apply
+directly to the capture device node. The fourcc attribute is a
+:doxy-int:`V4L2PixelFormat` and differs from the ``libcamera::PixelFormat``.
+Converting the format requires knowledge of the plane configuration for
+multiplanar formats, so you must explicitly convert it using the helper
+``V4L2VideoDevice::toV4L2PixelFormat()`` provided by the V4L2VideoDevice
+instance that the format will be applied on.
 
 Add the following code beneath the code from above:
 
@@ -1043,12 +996,10 @@ Add the following code beneath the code
    format.size = cfg.size;
 
 Set the video device format defined above using the
-`V4L2VideoDevice::setFormat()`_ function. You should check if the kernel
-driver has adjusted the format, as this shows the pipeline handler has failed to
-handle the validation stages correctly, and the configure operation shall also
-fail.
-
-.. _V4L2VideoDevice::setFormat(): https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#ad67b47dd9327ce5df43350b80c083cca
+:doxy-int:`V4L2VideoDevice::setFormat` function. You should check if the kernel
+driver has adjusted the format, as this shows the pipeline handler has failed
+to handle the validation stages correctly, and the configure operation shall
+also fail.
 
 Continue the implementation with the following code:
 
@@ -1064,10 +1015,8 @@ Continue the implementation with the fol
 
 Finally, store and set stream-specific data reflecting the state of the stream.
 Associate the configuration with the stream by using the
-`StreamConfiguration::setStream`_ function, and set the values of individual
-stream configuration members as required.
-
-.. _StreamConfiguration::setStream: https://libcamera.org/api-html/structlibcamera_1_1StreamConfiguration.html#a74a0eb44dad1b00112c7c0443ae54a12
+:doxy-int:`StreamConfiguration::setStream` function, and set the values of
+individual stream configuration members as required.
 
 .. NOTE: the cfg.setStream() call here associates the stream to the
    StreamConfiguration however that should quite likely be done as part of
@@ -1090,9 +1039,7 @@ Initializing device controls
 Pipeline handlers can optionally initialize the video devices and camera sensor
 controls at system configuration time, to make sure they are defaulted to sane
 values. Handling of device controls is again performed using the libcamera
-`controls framework`_.
-
-.. _Controls Framework: https://libcamera.org/api-html/controls_8h.html
+:doxy-int:`controls framework <controls.h>`.
 
 This section is particularly specific to Vivid as it sets the initial values of
 controls to match `Vivid Controls`_ defined by the kernel driver. You won't need
@@ -1113,10 +1060,8 @@ come directly from the kernel sources:
    #define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE  + 2)
 
 We can now use the V4L2 control IDs to prepare a list of controls with the
-`ControlList`_ class, and set them using the `ControlList::set()`_ function.
-
-.. _ControlList: https://libcamera.org/api-html/classlibcamera_1_1ControlList.html
-.. _ControlList::set(): https://libcamera.org/api-html/classlibcamera_1_1ControlList.html#a74a1a29abff5243e6e37ace8e24eb4ba
+:doxy-int:`ControlList` class, and set them using the :doxy-int:`ControlList::set`
+function.
 
 In our pipeline ``configure`` function, add the following code after the format
 has been set and checked to initialise the ControlList and apply it to the
@@ -1158,7 +1103,7 @@ available to the devices which have to b
 images. At the end of a capture session the ``Camera`` device needs to be
 stopped, to gracefully clean up any allocated memory and stop the hardware
 devices. Pipeline handlers implement two functions for these purposes, the
-``start()`` and ``stop()`` functions.
+``start()`` and ``stopDevice()`` functions.
 
 The memory initialization phase that happens at ``start()`` time serves to
 configure video devices to be able to use memory buffers exported as dma-buf
@@ -1167,28 +1112,22 @@ provide application facing streams alway
 in V4L2 terminology, buffers of V4L2_MEMORY_DMABUF memory type.
 
 libcamera also provides an API to allocate and export memory to applications
-realized through the `exportFrameBuffers`_ function and the
-`FrameBufferAllocator`_ class which will be presented later.
-
-.. _exportFrameBuffers: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a6312a69da7129c2ed41f9d9f790adf7c
-.. _FrameBufferAllocator: https://libcamera.org/api-html/classlibcamera_1_1FrameBufferAllocator.html
+realized through the :doxy-int:`PipelineHandler::exportFrameBuffers` function
+and the :doxy-int:`FrameBufferAllocator` class which will be presented later.
 
 Please refer to the V4L2VideoDevice API documentation, specifically the
-`allocateBuffers`_, `importBuffers`_ and `exportBuffers`_ functions for a
+:doxy-int:`allocateBuffers <V4L2VideoDevice::allocateBuffers>`,
+:doxy-int:`importBuffers <V4L2VideoDevice::importBuffers>` and
+:doxy-int:`exportBuffers <V4L2VideoDevice::exportBuffers>` functions for a
 detailed description of the video device memory management.
 
-.. _allocateBuffers: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a3a1a77e5e6c220ea7878e89485864a1c
-.. _importBuffers: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a154f5283d16ebd5e15d63e212745cb64
-.. _exportBuffers: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#ae9c0b0a68f350725b63b73a6da5a2ecd
-
-Video memory buffers are represented in libcamera by the `FrameBuffer`_ class.
-A ``FrameBuffer`` instance has to be associated to each ``Stream`` which is part
-of a capture ``Request``. Pipeline handlers should prepare the capture devices
-by importing the dma-buf file descriptors it needs to operate on. This operation
-is performed by using the ``V4L2VideoDevice`` API, which provides an
-``importBuffers()`` function that prepares the video device accordingly.
-
-.. _FrameBuffer: https://libcamera.org/api-html/classlibcamera_1_1FrameBuffer.html
+Video memory buffers are represented in libcamera by the
+:doxy-int:`FrameBuffer` class.  A ``FrameBuffer`` instance has to be associated
+to each ``Stream`` which is part of a capture ``Request``. Pipeline handlers
+should prepare the capture devices by importing the dma-buf file descriptors it
+needs to operate on. This operation is performed by using the
+``V4L2VideoDevice`` API, which provides an ``importBuffers()`` function that
+prepares the video device accordingly.
 
 Implement the pipeline handler ``start()`` function by replacing the stub
 version with the following code:
@@ -1212,20 +1151,17 @@ complex pipeline handlers in the libcame
 
 Applications might want to use memory allocated in the video devices instead of
 allocating it from other parts of the system. libcamera provides an abstraction
-to assist with this task in the `FrameBufferAllocator`_ class. The
+to assist with this task in the :doxy-int:`FrameBufferAllocator` class. The
 ``FrameBufferAllocator`` reserves memory for a ``Stream`` in the video device
 and exports it as dma-buf file descriptors. From this point on, the allocated
 ``FrameBuffer`` are associated to ``Stream`` instances in a ``Request`` and then
 imported by the pipeline hander in exactly the same fashion as if they were
 allocated elsewhere.
 
-.. _FrameBufferAllocator: https://libcamera.org/api-html/classlibcamera_1_1FrameBufferAllocator.html
-
 Pipeline handlers support the ``FrameBufferAllocator`` operations by
-implementing the `exportFrameBuffers`_ function, which will allocate memory in
-the video device associated with a stream and export it.
-
-.. _exportFrameBuffers: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a6312a69da7129c2ed41f9d9f790adf7c
+implementing the :doxy-int:`PipelineHandler::exportFrameBuffers` function,
+which will allocate memory in the video device associated with a stream and
+export it.
 
 Implement the ``exportFrameBuffers`` stub function with the following code to
 handle this:
@@ -1252,19 +1188,16 @@ with the following code:
    return 0;
 
 The function starts the video device associated with the stream with the
-`streamOn`_ function. If the call fails, the error value is propagated to the
-caller and the `releaseBuffers`_ function releases any buffers to leave the
-device in a consistent state. If your pipeline handler uses any image processing
-algorithms, or other devices you should also stop them.
-
-.. _streamOn: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a588a5dc9d6f4c54c61136ac43ff9a8cc
-.. _releaseBuffers: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a191619c152f764e03bc461611f3fcd35
+:doxy-int:`V4L2VideoDevice::streamOn` function. If the call fails, the error
+value is propagated to the caller and the
+:doxy-int:`V4L2VideoDevice::releaseBuffers` function releases any buffers to
+leave the device in a consistent state. If your pipeline handler uses any image
+processing algorithms, or other devices you should also stop them.
 
 Of course we also need to handle the corresponding actions to stop streaming on
-a device, Add the following to the ``stop`` function, to stop the stream with
-the `streamOff`_ function and release all buffers.
-
-.. _streamOff: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a61998710615bdf7aa25a046c8565ed66
+a device, Add the following to the ``stopDevice()`` function, to stop the
+stream with the :doxy-int:`V4L2VideoDevice::streamOff` function and release
+all buffers.
 
 .. code-block:: cpp
 
@@ -1283,12 +1216,11 @@ When an application sends a capture requ
 which video devices have to be provided with buffers to generate a frame from
 the enabled streams.
 
-This example pipeline handler identifies the buffer using the `findBuffer`_
-helper from the only supported stream and queues it to the capture device
-directly with the `queueBuffer`_ function provided by the V4L2VideoDevice.
-
-.. _findBuffer: https://libcamera.org/api-html/classlibcamera_1_1Request.html#ac66050aeb9b92c64218945158559c4d4
-.. _queueBuffer: https://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a594cd594686a8c1cf9ae8dba0b2a8a75
+This example pipeline handler identifies the buffer using the
+:doxy-int:`Request::findBuffer` helper from the only supported stream and
+queues it to the capture device directly with the
+:doxy-int:`V4L2VideoDevice::queueBuffer` function provided by the
+V4L2VideoDevice.
 
 Replace the stubbed contents of ``queueRequestDevice`` with the following:
 
@@ -1383,11 +1315,9 @@ where appropriate by setting controls on
 handler is responsible for understanding the correct procedure for applying
 controls to the device they support.
 
-This example pipeline handler applies controls during the `queueRequestDevice`_
-function for each request, and applies them to the capture device through the
-capture node.
-
-.. _queueRequestDevice: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a106914cca210640c9da9ee1f0419e83c
+This example pipeline handler applies controls during the
+:doxy-int:`PipelineHandler::queueRequestDevice` function for each request, and
+applies them to the capture device through the capture node.
 
 In the ``queueRequestDevice`` function, replace the following:
 
@@ -1424,11 +1354,10 @@ Slots`_) to connect event sources with c
 
 As a general summary, a ``Slot`` can be connected to a ``Signal``, which when
 emitted triggers the execution of the connected slots.  A detailed description
-of the libcamera implementation is available in the `libcamera Signal and Slot`_
-classes documentation.
+of the libcamera implementation is available in the :doxy-int:`libcamera Signal
+and Slot <Signal>` classes documentation.
 
 .. _Qt Signals and Slots: https://doc.qt.io/qt-6/signalsandslots.html
-.. _libcamera Signal and Slot: https://libcamera.org/api-html/classlibcamera_1_1Signal.html#details
 
 In order to notify applications about the availability of new frames and data,
 the ``Camera`` device exposes two ``Signals`` to which applications can connect
@@ -1444,18 +1373,17 @@ The ``bufferComplete`` and ``requestComp
 ``Camera`` device upon notifications received from the pipeline handler, which
 tracks the buffers and request completion status.
 
-The single buffer completion notification is implemented by pipeline handlers by
-`connecting`_ the ``bufferReady`` signal of the capture devices they have queued
-buffers to, to a member function slot that handles processing of the completed
-frames. When a buffer is ready, the pipeline handler must propagate the
-completion of that buffer to the Camera by using the PipelineHandler base class
-``completeBuffer`` function. When all of the buffers referenced by a ``Request``
-have been completed, the pipeline handler must again notify the ``Camera`` using
-the PipelineHandler base class ``completeRequest`` function. The PipelineHandler
-class implementation makes sure the request completion notifications are
-delivered to applications in the same order as they have been submitted.
-
-.. _connecting: https://libcamera.org/api-html/classlibcamera_1_1Signal.html#aa04db72d5b3091ffbb4920565aeed382
+The single buffer completion notification is implemented by pipeline handlers
+by :doxy-int:`connecting <Signal::connect>` the ``bufferReady`` signal of the
+capture devices they have queued buffers to, to a member function slot that
+handles processing of the completed frames. When a buffer is ready, the
+pipeline handler must propagate the completion of that buffer to the Camera by
+using the PipelineHandler base class ``completeBuffer`` function. When all of
+the buffers referenced by a ``Request`` have been completed, the pipeline
+handler must again notify the ``Camera`` using the PipelineHandler base class
+``completeRequest`` function. The PipelineHandler class implementation makes
+sure the request completion notifications are delivered to applications in the
+same order as they have been submitted.
 
 Returning to the ``int VividCameraData::init()`` function, add the following
 above the closing ``return 0;`` to connect the pipeline handler ``bufferReady``
diff -pruN 0.5.0-1/Documentation/libcamera_architecture.rst 0.5.2-2/Documentation/libcamera_architecture.rst
--- 0.5.0-1/Documentation/libcamera_architecture.rst	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/libcamera_architecture.rst	2025-08-07 13:46:17.000000000 +0000
@@ -68,9 +68,7 @@ Camera Manager
   Each application's instance of the Camera Manager ensures that only a single
   application can take control of a camera device at once.
 
-  Read the `Camera Manager API`_ documentation for more details.
-
-.. _Camera Manager API: https://libcamera.org/api-html/classlibcamera_1_1CameraManager.html
+  Read the :doxy-pub:`CameraManager` API documentation for more details.
 
 Camera Device
   The Camera class represents a single item of camera hardware that is capable
@@ -85,9 +83,7 @@ Camera Device
   object that all other API operations interact with from configuration to
   capture.
 
-  Read the `Camera API`_ documentation for more details.
-
-.. _Camera API: https://libcamera.org/api-html/classlibcamera_1_1Camera.html
+  Read the :doxy-pub:`Camera` API documentation for more details.
 
 Pipeline Handler
   The Pipeline Handler manages the complex pipelines exposed by the kernel
@@ -107,11 +103,10 @@ Pipeline Handler
   they detect and support on the running system, and are responsible for
   managing the interactions with a camera device.
 
-  More details can be found in the `PipelineHandler API`_ documentation, and the
+  More details can be found in the :doxy-int:`PipelineHandler` API
+  documentation, and the
   :doc:`Pipeline Handler Writers Guide <guides/pipeline-handler>`.
 
-.. _PipelineHandler API: https://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html
-
 Image Processing Algorithms
   Together with the hardware image processing and hardware statistics
   collection, the Image Processing Algorithms (IPA) implement 3A (Auto-Exposure,
diff -pruN 0.5.0-1/Documentation/meson.build 0.5.2-2/Documentation/meson.build
--- 0.5.0-1/Documentation/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/Documentation/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -81,16 +81,16 @@ if doxygen.found() and dot.found()
                                  '@INPUT@',
                              ])
 
-    custom_target('doxygen-public',
-                  input : [
-                      doxyfile,
-                      doxyfile_common,
-                  ],
-                  output : 'api-html',
-                  command : [doxygen, doxyfile],
-                  install : true,
-                  install_dir : doc_install_dir,
-                  install_tag : 'doc')
+    doxygen_public = custom_target('doxygen-public',
+                                   input : [
+                                       doxyfile,
+                                       doxyfile_common,
+                                   ],
+                                   output : 'api-html',
+                                   command : [doxygen, doxyfile],
+                                   install : true,
+                                   install_dir : doc_install_dir,
+                                   install_tag : 'doc')
 
     # This is the internal documentation, which hard-codes a list of directories
     # to parse in its doxyfile.
@@ -99,29 +99,49 @@ if doxygen.found() and dot.found()
                               output : 'Doxyfile-internal',
                               configuration : cdata)
 
-    custom_target('doxygen-internal',
-                  input : [
-                      doxyfile,
-                      doxyfile_common,
-                      doxygen_internal_input,
-                  ],
-                  output : 'internal-api-html',
-                  command : [doxygen, doxyfile],
-                  install : true,
-                  install_dir : doc_install_dir,
-                  install_tag : 'doc-internal')
+    doxygen_internal = custom_target('doxygen-internal',
+                                     input : [
+                                         doxyfile,
+                                         doxyfile_common,
+                                         doxygen_public_input,
+                                         doxygen_internal_input,
+                                     ],
+                                     output : 'internal-api-html',
+                                     command : [doxygen, doxyfile],
+                                     install : true,
+                                     install_dir : doc_install_dir,
+                                     install_tag : 'doc-internal')
 endif
 
 #
 # Sphinx
 #
 
-sphinx = find_program('sphinx-build-3', required : false)
-if not sphinx.found()
-    sphinx = find_program('sphinx-build', required : get_option('documentation'))
-endif
+sphinx = find_program('sphinx-build-3', 'sphinx-build',
+                      required : get_option('documentation'))
 
 if sphinx.found()
+    # Many distributions do not provide a recent-enough version of the doxylink
+    # module. This results in a build error with a cryptic message. Check the
+    # version manually and print clear error messages.
+    py_mod = import('python')
+    py3 = py_mod.find_installation('python3')
+
+    mod = 'sphinxcontrib.doxylink'
+    min_version = '>=1.6.1'
+    version = run_command(py3, '-c',
+                          'import @0@ ; print(@0@.__version__)'.format(mod),
+                          check : false).stdout().strip()
+
+    if version == ''
+        error('@0@ module not found'.format(mod))
+    endif
+
+    if not version.version_compare(min_version)
+        error('@0@ module v@1@ is too old, @2@ is needed'
+              .format(mod, version, min_version))
+    endif
+
     docs_sources = [
         'camera-sensor-model.rst',
         'code-of-conduct.rst',
@@ -155,6 +175,10 @@ if sphinx.found()
                   input : docs_sources,
                   output : 'html',
                   build_by_default : true,
+                  depends : [
+                      doxygen_public,
+                      doxygen_internal,
+                  ],
                   install : true,
                   install_dir : doc_install_dir,
                   install_tag : 'doc')
diff -pruN 0.5.0-1/README.rst 0.5.2-2/README.rst
--- 0.5.0-1/README.rst	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/README.rst	2025-08-07 13:46:17.000000000 +0000
@@ -44,7 +44,7 @@ A C++ toolchain: [required]
         Either {g++, clang}
 
 Meson Build system: [required]
-        meson (>= 0.60) ninja-build pkg-config
+        meson (>= 0.63) ninja-build pkg-config
 
 for the libcamera core: [required]
         libyaml-dev python3-yaml python3-ply python3-jinja2
@@ -67,7 +67,8 @@ for device hotplug enumeration: [optiona
         libudev-dev
 
 for documentation: [optional]
-        python3-sphinx doxygen graphviz texlive-latex-extra
+        doxygen graphviz python3-sphinx python3-sphinxcontrib.doxylink (>= 1.6.1)
+        texlive-latex-extra
 
 for gstreamer: [optional]
         libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
@@ -83,9 +84,10 @@ for cam: [optional]
         - libdrm-dev: Enables the KMS sink
         - libjpeg-dev: Enables MJPEG on the SDL sink
         - libsdl2-dev: Enables the SDL sink
+        - libtiff-dev: Enables writing DNG
 
 for qcam: [optional]
-        libtiff-dev qt6-base-dev qt6-tools-dev-tools
+        libtiff-dev qt6-base-dev
 
 for tracing with lttng: [optional]
         liblttng-ust-dev python3-jinja2 lttng-tools
diff -pruN 0.5.0-1/debian/changelog 0.5.2-2/debian/changelog
--- 0.5.0-1/debian/changelog	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/changelog	2025-08-17 20:47:35.000000000 +0000
@@ -1,3 +1,27 @@
+libcamera (0.5.2-2) unstable; urgency=medium
+
+  * Adjust VCS fields in d/control and d/gbp.conf for unstable.
+  * Upload to unstable.
+
+ -- Dylan Aïssi <daissi@debian.org>  Sun, 17 Aug 2025 22:47:35 +0200
+
+libcamera (0.5.2-1) experimental; urgency=medium
+
+  * New upstream version 0.5.2
+  * Add python3-sphinxcontrib.doxylink in Build-Depends-Indep
+  * Refresh debian/copyright
+
+ -- Dylan Aïssi <daissi@debian.org>  Thu, 07 Aug 2025 17:16:21 +0200
+
+libcamera (0.5.1-1) experimental; urgency=medium
+
+  * New upstream version 0.5.1
+  * Refresh patches
+  * Install IPA modules in usr/lib/*/libcamera/ipa
+  * Adjust VCS field in d/control for experimental
+
+ -- Dylan Aïssi <daissi@debian.org>  Tue, 03 Jun 2025 21:16:11 +0200
+
 libcamera (0.5.0-1) experimental; urgency=medium
 
   * New upstream version 0.5.0
diff -pruN 0.5.0-1/debian/control 0.5.2-2/debian/control
--- 0.5.0-1/debian/control	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/control	2025-08-17 20:47:35.000000000 +0000
@@ -39,6 +39,7 @@ Build-Depends-Indep: doxygen <!nodoc>,
                      libjs-sphinxdoc <!nodoc>,
                      libjs-underscore <!nodoc>,
                      python3-sphinx <!nodoc>,
+                     python3-sphinxcontrib.doxylink <!nodoc>
 Standards-Version: 4.7.2
 Vcs-Browser: https://salsa.debian.org/multimedia-team/libcamera
 Vcs-Git: https://salsa.debian.org/multimedia-team/libcamera.git
diff -pruN 0.5.0-1/debian/copyright 0.5.2-2/debian/copyright
--- 0.5.0-1/debian/copyright	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/copyright	2025-08-17 20:47:35.000000000 +0000
@@ -5,22 +5,22 @@ Source: https://libcamera.org/
 
 Files: *
 Copyright:
- 2018—2024 Libcamera contributors
+ 2018—2025 Libcamera contributors
  2019      Giulio Benetti
  2018—2019 Jacopo Mondi
- 2018—2024 Kieran Bingham
- 2018—2024 Laurent Pinchart
+ 2018—2025 Kieran Bingham
+ 2018—2025 Laurent Pinchart
  2019      Mickael Guene
  2018—2019 Niklas Söderlund
  2019—2024 Paul Elder
  2018—2024 Google Inc.
- 2021—2024 Ideas on Board Oy
+ 2021—2025 Ideas on Board Oy
 License: GPL-2+
 
 Files:
  */*/meson.build
 Copyright:
- 2018—2024 Libcamera contributors
+ 2018—2025 Libcamera contributors
 License: CC0-1.0
 
 Files:
@@ -30,7 +30,7 @@ Files:
  src/apps/common/dng_writer.*
  include/libcamera/*
 Copyright:
- 2018—2024 Libcamera contributors
+ 2018—2025 Libcamera contributors
  2019      Giulio Benetti
  2018—2019 Jacopo Mondi
  2018—2019 Kieran Bingham
@@ -39,10 +39,10 @@ Copyright:
  2018—2019 Niklas Söderlund
  2019      Paul Elder
  2018—2022 Google Inc.
- 2019—2023 Raspberry Pi Ltd.
- 2021—2024 Ideas on Board Oy
+ 2019—2025 Raspberry Pi Ltd.
+ 2021—2025 Ideas on Board Oy
  2023 Linaro Ltd
- 2023—2024 Red Hat Inc.
+ 2023—2025 Red Hat Inc.
 License: LGPL-2.1+
 
 Files:
@@ -91,7 +91,7 @@ Copyright:
 License: Expat
 
 Files: include/linux/bcm2835-isp.h
-Copyright: 2019—2022 Raspberry Pi Ltd
+Copyright: 2019—2025 Raspberry Pi Ltd
 License: GPL-2+ with Linux-syscall-note exception or BSD-3-Clause
 
 Files:
@@ -105,7 +105,7 @@ Files:
  src/libcamera/pipeline/rpi/*
  utils/raspberrypi/ctt/*
 Copyright:
- 2019—2024 Raspberry Pi Ltd
+ 2019—2025 Raspberry Pi Ltd
 License: BSD-2-Clause
 
 Files: include/linux/rkisp1-config.h
@@ -113,7 +113,7 @@ Copyright: 2017, Rockchip Electronics Co
 License: GPL-2+
 
 Files: Documentation/*
-Copyright: 2020—2024 Libcamera contributors
+Copyright: 2020—2025 Libcamera contributors
 License: CC-BY-SA-4.0
 
 Files: Documentation/theme/static/search.png
@@ -130,7 +130,7 @@ Copyright:
  2019 Emmanuel Arias <emmanuelarias30@gmail.com>
  2019—2022 Andrej Shadura <andrewsh@debian.org>
  2020 IOhannes m zmölnig <umlaeute@debian.org>
- 2022-2024 Collabora Ltd.
+ 2022-2025 Collabora Ltd.
 License: GPL-2+
 
 License: GPL-2+
diff -pruN 0.5.0-1/debian/gbp.conf 0.5.2-2/debian/gbp.conf
--- 0.5.0-1/debian/gbp.conf	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/gbp.conf	2025-08-17 20:47:35.000000000 +0000
@@ -1,5 +1,5 @@
 [DEFAULT]
-debian-branch=debian/experimental
+debian-branch=debian/unstable
 upstream-branch=upstream/latest
 pristine-tar = True
 
diff -pruN 0.5.0-1/debian/libcamera-ipa.install 0.5.2-2/debian/libcamera-ipa.install
--- 0.5.0-1/debian/libcamera-ipa.install	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/libcamera-ipa.install	2025-08-17 20:47:35.000000000 +0000
@@ -1,4 +1,3 @@
-usr/lib/*/libcamera/ipa_*.so
-usr/lib/*/libcamera/ipa_*.so.sign
+usr/lib/*/libcamera/ipa
 usr/libexec/*/libcamera/*_ipa_proxy
 usr/share/libcamera
diff -pruN 0.5.0-1/debian/patches/Disable_libunwind_integration.patch 0.5.2-2/debian/patches/Disable_libunwind_integration.patch
--- 0.5.0-1/debian/patches/Disable_libunwind_integration.patch	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/patches/Disable_libunwind_integration.patch	2025-08-17 20:47:35.000000000 +0000
@@ -5,7 +5,7 @@ Author: Dylan Aïssi <daissi@debian.org>
 
 --- a/meson_options.txt
 +++ b/meson_options.txt
-@@ -41,6 +41,11 @@
+@@ -42,6 +42,11 @@
          value : 'auto',
          description : 'Compile the lc-compliance test application')
  
diff -pruN 0.5.0-1/debian/patches/skip_failing_tests.patch 0.5.2-2/debian/patches/skip_failing_tests.patch
--- 0.5.0-1/debian/patches/skip_failing_tests.patch	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/patches/skip_failing_tests.patch	2025-08-17 20:47:35.000000000 +0000
@@ -50,8 +50,8 @@ Forwarded: not-needed
 +#    {'name': 'file', 'sources': ['file.cpp']},
      {'name': 'flags', 'sources': ['flags.cpp']},
      {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']},
-     {'name': 'message', 'sources': ['message.cpp']},
-@@ -65,13 +65,13 @@
+     {'name': 'matrix', 'sources': ['matrix.cpp']},
+@@ -66,13 +66,13 @@
      {'name': 'object-delete', 'sources': ['object-delete.cpp']},
      {'name': 'object-invoke', 'sources': ['object-invoke.cpp']},
      {'name': 'pixel-format', 'sources': ['pixel-format.cpp']},
diff -pruN 0.5.0-1/debian/rules 0.5.2-2/debian/rules
--- 0.5.0-1/debian/rules	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/rules	2025-08-17 20:47:35.000000000 +0000
@@ -54,8 +54,8 @@ override_dh_strip:
 	dh_strip -a
 	MESON_INSTALL_DESTDIR_PREFIX=. ./src/ipa/ipa-sign-install.sh \
 		./obj-${DEB_HOST_GNU_TYPE}/src/ipa-priv-key.pem \
-		debian/libcamera-ipa/usr/lib/${DEB_HOST_MULTIARCH}/libcamera/ipa_*.so
-	for IPA_MOD in debian/libcamera-ipa/usr/lib/*/libcamera/ipa_*.so; \
+		debian/libcamera-ipa/usr/lib/${DEB_HOST_MULTIARCH}/libcamera/ipa/ipa_*.so
+	for IPA_MOD in debian/libcamera-ipa/usr/lib/*/libcamera/ipa/ipa_*.so; \
 	do \
 	  echo "Verifying signature of $${IPA_MOD}"; \
 	  ./obj-${DEB_HOST_GNU_TYPE}/src/apps/ipa-verify/ipa_verify $${IPA_MOD}; \
diff -pruN 0.5.0-1/debian/tests/check-IPA-modules-signatures 0.5.2-2/debian/tests/check-IPA-modules-signatures
--- 0.5.0-1/debian/tests/check-IPA-modules-signatures	2025-04-08 11:30:03.000000000 +0000
+++ 0.5.2-2/debian/tests/check-IPA-modules-signatures	2025-08-17 20:47:35.000000000 +0000
@@ -1,7 +1,7 @@
 #!/bin/sh -e
 # autopkgtest check: Verify signatures of installed IPA modules.
 
-for IPA_MOD in /usr/lib/*/libcamera/ipa_*.so
+for IPA_MOD in /usr/lib/*/libcamera/ipa/ipa_*.so
 do
   echo "Verifying signature of ${IPA_MOD}"
   ipa_verify "${IPA_MOD}"
diff -pruN 0.5.0-1/include/libcamera/base/bound_method.h 0.5.2-2/include/libcamera/base/bound_method.h
--- 0.5.0-1/include/libcamera/base/bound_method.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/base/bound_method.h	2025-08-07 13:46:17.000000000 +0000
@@ -33,16 +33,12 @@ template<typename R, typename... Args>
 class BoundMethodPack : public BoundMethodPackBase
 {
 public:
-	BoundMethodPack(const Args &... args)
-		: args_(args...)
+	template<typename... Ts>
+	BoundMethodPack(Ts &&...args)
+		: args_(std::forward<Ts>(args)...)
 	{
 	}
 
-	R returnValue()
-	{
-		return ret_;
-	}
-
 	std::tuple<typename std::remove_reference_t<Args>...> args_;
 	R ret_;
 };
@@ -51,12 +47,9 @@ template<typename... Args>
 class BoundMethodPack<void, Args...> : public BoundMethodPackBase
 {
 public:
-	BoundMethodPack(const Args &... args)
-		: args_(args...)
-	{
-	}
-
-	void returnValue()
+	template<typename... Ts>
+	BoundMethodPack(Ts &&...args)
+		: args_(std::forward<Ts>(args)...)
 	{
 	}
 
@@ -130,23 +123,25 @@ public:
 
 	BoundMethodFunctor(T *obj, Object *object, Func func,
 			   ConnectionType type = ConnectionTypeAuto)
-		: BoundMethodArgs<R, Args...>(obj, object, type), func_(func)
+		: BoundMethodArgs<R, Args...>(obj, object, type), func_(std::move(func))
 	{
 	}
 
 	R activate(Args... args, bool deleteMethod = false) override
 	{
 		if (!this->object_)
-			return func_(args...);
+			return func_(std::forward<Args>(args)...);
+
+		auto pack = std::make_shared<PackType>(std::forward<Args>(args)...);
+		[[maybe_unused]] bool sync = BoundMethodBase::activatePack(pack, deleteMethod);
 
-		auto pack = std::make_shared<PackType>(args...);
-		bool sync = BoundMethodBase::activatePack(pack, deleteMethod);
-		return sync ? pack->returnValue() : R();
+		if constexpr (!std::is_void_v<R>)
+			return sync ? std::move(pack->ret_) : R();
 	}
 
 	R invoke(Args... args) override
 	{
-		return func_(args...);
+		return func_(std::forward<Args>(args)...);
 	}
 
 private:
@@ -171,18 +166,20 @@ public:
 	{
 		if (!this->object_) {
 			T *obj = static_cast<T *>(this->obj_);
-			return (obj->*func_)(args...);
+			return (obj->*func_)(std::forward<Args>(args)...);
 		}
 
-		auto pack = std::make_shared<PackType>(args...);
-		bool sync = BoundMethodBase::activatePack(pack, deleteMethod);
-		return sync ? pack->returnValue() : R();
+		auto pack = std::make_shared<PackType>(std::forward<Args>(args)...);
+		[[maybe_unused]] bool sync = BoundMethodBase::activatePack(pack, deleteMethod);
+
+		if constexpr (!std::is_void_v<R>)
+			return sync ? std::move(pack->ret_) : R();
 	}
 
 	R invoke(Args... args) override
 	{
 		T *obj = static_cast<T *>(this->obj_);
-		return (obj->*func_)(args...);
+		return (obj->*func_)(std::forward<Args>(args)...);
 	}
 
 private:
@@ -203,7 +200,7 @@ public:
 
 	R activate(Args... args, [[maybe_unused]] bool deleteMethod = false) override
 	{
-		return (*func_)(args...);
+		return (*func_)(std::forward<Args>(args)...);
 	}
 
 	R invoke(Args...) override
diff -pruN 0.5.0-1/include/libcamera/base/log.h 0.5.2-2/include/libcamera/base/log.h
--- 0.5.0-1/include/libcamera/base/log.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/base/log.h	2025-08-07 13:46:17.000000000 +0000
@@ -96,12 +96,12 @@ public:
 protected:
 	virtual std::string logPrefix() const = 0;
 
-	LogMessage _log(const LogCategory *category, LogSeverity severity,
+	LogMessage _log(const LogCategory &category, LogSeverity severity,
 			const char *fileName = __builtin_FILE(),
 			unsigned int line = __builtin_LINE()) const;
 };
 
-LogMessage _log(const LogCategory *category, LogSeverity severity,
+LogMessage _log(const LogCategory &category, LogSeverity severity,
 		const char *fileName = __builtin_FILE(),
 		unsigned int line = __builtin_LINE());
 
@@ -109,9 +109,9 @@ LogMessage _log(const LogCategory *categ
 #define _LOG_CATEGORY(name) logCategory##name
 
 #define _LOG1(severity) \
-	_log(nullptr, Log##severity).stream()
+	_log(LogCategory::defaultCategory(), Log##severity).stream()
 #define _LOG2(category, severity) \
-	_log(&_LOG_CATEGORY(category)(), Log##severity).stream()
+	_log(_LOG_CATEGORY(category)(), Log##severity).stream()
 
 /*
  * Expand the LOG() macro to _LOG1() or _LOG2() based on the number of
diff -pruN 0.5.0-1/include/libcamera/base/utils.h 0.5.2-2/include/libcamera/base/utils.h
--- 0.5.0-1/include/libcamera/base/utils.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/base/utils.h	2025-08-07 13:46:17.000000000 +0000
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include <libcamera/base/class.h>
 #include <libcamera/base/private.h>
 
 #ifndef __DOXYGEN__
@@ -428,6 +429,43 @@ private:
 	std::vector<std::function<void()>> actions_;
 };
 
+#ifndef __DOXYGEN__
+template<typename EF>
+class scope_exit
+{
+public:
+	template<typename Fn,
+		 std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Fn>>, scope_exit> &&
+				  std::is_constructible_v<EF, Fn>> * = nullptr>
+	explicit scope_exit(Fn &&fn)
+		: exitFunction_(std::forward<Fn>(fn))
+	{
+		static_assert(std::is_nothrow_constructible_v<EF, Fn>);
+	}
+
+	~scope_exit()
+	{
+		if (active_)
+			exitFunction_();
+	}
+
+	void release()
+	{
+		active_ = false;
+	}
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(scope_exit)
+
+	EF exitFunction_;
+	bool active_ = true;
+};
+
+template<typename EF>
+scope_exit(EF) -> scope_exit<EF>;
+
+#endif /* __DOXYGEN__ */
+
 } /* namespace utils */
 
 #ifndef __DOXYGEN__
diff -pruN 0.5.0-1/include/libcamera/camera.h 0.5.2-2/include/libcamera/camera.h
--- 0.5.0-1/include/libcamera/camera.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/camera.h	2025-08-07 13:46:17.000000000 +0000
@@ -165,6 +165,8 @@ private:
 	friend class FrameBufferAllocator;
 	int exportFrameBuffers(Stream *stream,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+
+	void patchControlList(ControlList &controls);
 };
 
 } /* namespace libcamera */
diff -pruN 0.5.0-1/include/libcamera/control_ids.h.in 0.5.2-2/include/libcamera/control_ids.h.in
--- 0.5.0-1/include/libcamera/control_ids.h.in	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/control_ids.h.in	2025-08-07 13:46:17.000000000 +0000
@@ -49,6 +49,7 @@ extern const std::array<const ControlVal
 extern const std::map<std::string, {{ctrl.type}}> {{ctrl.name}}NameValueMap;
 {% endif -%}
 extern const Control<{{ctrl.type}}> {{ctrl.name}};
+#define LIBCAMERA_HAS_{{vendor|upper}}_VENDOR_{{mode|upper}}_{{ctrl.name|snake_case|upper}}
 {% endfor -%}
 
 {% if vendor != 'libcamera' %}
diff -pruN 0.5.0-1/include/libcamera/controls.h 0.5.2-2/include/libcamera/controls.h
--- 0.5.0-1/include/libcamera/controls.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/controls.h	2025-08-07 13:46:17.000000000 +0000
@@ -120,7 +120,7 @@ struct control_type<Point> {
 };
 
 template<typename T, std::size_t N>
-struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> {
+struct control_type<Span<T, N>, std::enable_if_t<control_type<std::remove_cv_t<T>>::size == 0>> : public control_type<std::remove_cv_t<T>> {
 	static constexpr std::size_t size = N;
 };
 
diff -pruN 0.5.0-1/include/libcamera/framebuffer.h 0.5.2-2/include/libcamera/framebuffer.h
--- 0.5.0-1/include/libcamera/framebuffer.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/framebuffer.h	2025-08-07 13:46:17.000000000 +0000
@@ -26,6 +26,7 @@ struct FrameMetadata {
 		FrameSuccess,
 		FrameError,
 		FrameCancelled,
+		FrameStartup,
 	};
 
 	struct Plane {
diff -pruN 0.5.0-1/include/libcamera/internal/camera.h 0.5.2-2/include/libcamera/internal/camera.h
--- 0.5.0-1/include/libcamera/internal/camera.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/camera.h	2025-08-07 13:46:17.000000000 +0000
@@ -10,6 +10,7 @@
 #include <atomic>
 #include <list>
 #include <memory>
+#include <queue>
 #include <set>
 #include <stdint.h>
 #include <string>
@@ -36,6 +37,7 @@ public:
 	const PipelineHandler *pipe() const { return pipe_.get(); }
 
 	std::list<Request *> queuedRequests_;
+	std::queue<Request *> waitingRequests_;
 	ControlInfoMap controlInfo_;
 	ControlList properties_;
 
diff -pruN 0.5.0-1/include/libcamera/internal/camera_manager.h 0.5.2-2/include/libcamera/internal/camera_manager.h
--- 0.5.0-1/include/libcamera/internal/camera_manager.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/camera_manager.h	2025-08-07 13:46:17.000000000 +0000
@@ -65,7 +65,6 @@ private:
 	std::unique_ptr<DeviceEnumerator> enumerator_;
 
 	std::unique_ptr<IPAManager> ipaManager_;
-	ProcessManager processManager_;
 };
 
 } /* namespace libcamera */
diff -pruN 0.5.0-1/include/libcamera/internal/clock_recovery.h 0.5.2-2/include/libcamera/internal/clock_recovery.h
--- 0.5.0-1/include/libcamera/internal/clock_recovery.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/clock_recovery.h	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Raspberry Pi Ltd
+ *
+ * Camera recovery algorithm
+ */
+#pragma once
+
+#include <stdint.h>
+
+namespace libcamera {
+
+class ClockRecovery
+{
+public:
+	ClockRecovery();
+
+	void configure(unsigned int numSamples = 100, unsigned int maxJitter = 2000,
+		       unsigned int minSamples = 10, unsigned int errorThreshold = 50000);
+	void reset();
+
+	void addSample();
+	void addSample(uint64_t input, uint64_t output);
+
+	uint64_t getOutput(uint64_t input);
+
+private:
+	/* Approximate number of samples over which the model state persists. */
+	unsigned int numSamples_;
+	/* Remove any output jitter larger than this immediately. */
+	unsigned int maxJitter_;
+	/* Number of samples required before we start to use model estimates. */
+	unsigned int minSamples_;
+	/* Threshold above which we assume the wallclock has been reset. */
+	unsigned int errorThreshold_;
+
+	/* How many samples seen (up to numSamples_). */
+	unsigned int count_;
+	/* This gets subtracted from all input values, just to make the numbers easier. */
+	uint64_t inputBase_;
+	/* As above, for the output. */
+	uint64_t outputBase_;
+	/* The previous input sample. */
+	uint64_t lastInput_;
+	/* The previous output sample. */
+	uint64_t lastOutput_;
+
+	/* Average x value seen so far. */
+	double xAve_;
+	/* Average y value seen so far */
+	double yAve_;
+	/* Average x^2 value seen so far. */
+	double x2Ave_;
+	/* Average x*y value seen so far. */
+	double xyAve_;
+
+	/*
+	 * The latest estimate of linear parameters to derive the output clock
+	 * from the input.
+	 */
+	double slope_;
+	double offset_;
+
+	/* Use this cumulative error to monitor for spontaneous clock updates. */
+	double error_;
+};
+
+} /* namespace libcamera */
diff -pruN 0.5.0-1/include/libcamera/internal/delayed_controls.h 0.5.2-2/include/libcamera/internal/delayed_controls.h
--- 0.5.0-1/include/libcamera/internal/delayed_controls.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/delayed_controls.h	2025-08-07 13:46:17.000000000 +0000
@@ -10,13 +10,15 @@
 #include <stdint.h>
 #include <unordered_map>
 
+#include <libcamera/base/object.h>
+
 #include <libcamera/controls.h>
 
 namespace libcamera {
 
 class V4L2Device;
 
-class DelayedControls
+class DelayedControls : public Object
 {
 public:
 	struct ControlParams {
diff -pruN 0.5.0-1/include/libcamera/internal/ipa_data_serializer.h 0.5.2-2/include/libcamera/internal/ipa_data_serializer.h
--- 0.5.0-1/include/libcamera/internal/ipa_data_serializer.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/ipa_data_serializer.h	2025-08-07 13:46:17.000000000 +0000
@@ -309,7 +309,6 @@ public:
 	serialize(const Flags<E> &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
 	{
 		std::vector<uint8_t> dataVec;
-		dataVec.reserve(sizeof(Flags<E>));
 		appendPOD<uint32_t>(dataVec, static_cast<typename Flags<E>::Type>(data));
 
 		return { dataVec, {} };
diff -pruN 0.5.0-1/include/libcamera/internal/ipa_module.h 0.5.2-2/include/libcamera/internal/ipa_module.h
--- 0.5.0-1/include/libcamera/internal/ipa_module.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/ipa_module.h	2025-08-07 13:46:17.000000000 +0000
@@ -29,7 +29,7 @@ public:
 	bool isValid() const;
 
 	const struct IPAModuleInfo &info() const;
-	const std::vector<uint8_t> signature() const;
+	const std::vector<uint8_t> &signature() const;
 	const std::string &path() const;
 
 	bool load();
diff -pruN 0.5.0-1/include/libcamera/internal/matrix.h 0.5.2-2/include/libcamera/internal/matrix.h
--- 0.5.0-1/include/libcamera/internal/matrix.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/matrix.h	2025-08-07 13:46:17.000000000 +0000
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <sstream>
+#include <type_traits>
 #include <vector>
 
 #include <libcamera/base/log.h>
@@ -20,17 +21,19 @@ namespace libcamera {
 LOG_DECLARE_CATEGORY(Matrix)
 
 #ifndef __DOXYGEN__
-template<typename T, unsigned int Rows, unsigned int Cols,
-	 std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
-#else
-template<typename T, unsigned int Rows, unsigned int Cols>
+template<typename T>
+bool matrixInvert(Span<const T> dataIn, Span<T> dataOut, unsigned int dim,
+		  Span<T> scratchBuffer, Span<unsigned int> swapBuffer);
 #endif /* __DOXYGEN__ */
+
+template<typename T, unsigned int Rows, unsigned int Cols>
 class Matrix
 {
+	static_assert(std::is_arithmetic_v<T>, "Matrix type must be arithmetic");
+
 public:
-	Matrix()
+	constexpr Matrix()
 	{
-		data_.fill(static_cast<T>(0));
 	}
 
 	Matrix(const std::array<T, Rows * Cols> &data)
@@ -38,7 +41,12 @@ public:
 		std::copy(data.begin(), data.end(), data_.begin());
 	}
 
-	static Matrix identity()
+	Matrix(const Span<const T, Rows * Cols> data)
+	{
+		std::copy(data.begin(), data.end(), data_.begin());
+	}
+
+	static constexpr Matrix identity()
 	{
 		Matrix ret;
 		for (size_t i = 0; i < std::min(Rows, Cols); i++)
@@ -66,14 +74,14 @@ public:
 		return out.str();
 	}
 
-	Span<const T, Rows * Cols> data() const { return data_; }
+	constexpr Span<const T, Rows * Cols> data() const { return data_; }
 
-	Span<const T, Cols> operator[](size_t i) const
+	constexpr Span<const T, Cols> operator[](size_t i) const
 	{
 		return Span<const T, Cols>{ &data_.data()[i * Cols], Cols };
 	}
 
-	Span<T, Cols> operator[](size_t i)
+	constexpr Span<T, Cols> operator[](size_t i)
 	{
 		return Span<T, Cols>{ &data_.data()[i * Cols], Cols };
 	}
@@ -90,8 +98,30 @@ public:
 		return *this;
 	}
 
+	Matrix<T, Rows, Cols> inverse(bool *ok = nullptr) const
+	{
+		static_assert(Rows == Cols, "Matrix must be square");
+
+		Matrix<T, Rows, Cols> inverse;
+		std::array<T, Rows * Cols * 2> scratchBuffer;
+		std::array<unsigned int, Rows> swapBuffer;
+		bool res = matrixInvert(Span<const T>(data_),
+					Span<T>(inverse.data_),
+					Rows,
+					Span<T>(scratchBuffer),
+					Span<unsigned int>(swapBuffer));
+		if (ok)
+			*ok = res;
+		return inverse;
+	}
+
 private:
-	std::array<T, Rows * Cols> data_;
+	/*
+	 * \todo The initializer is only necessary for the constructor to be
+	 * constexpr in C++17. Remove the initializer as soon as we are on
+	 * C++20.
+	 */
+	std::array<T, Rows * Cols> data_ = {};
 };
 
 #ifndef __DOXYGEN__
@@ -123,21 +153,16 @@ Matrix<U, Rows, Cols> operator*(const Ma
 	return d * m;
 }
 
-#ifndef __DOXYGEN__
-template<typename T,
-	 unsigned int R1, unsigned int C1,
-	 unsigned int R2, unsigned int C2,
-	 std::enable_if_t<C1 == R2> * = nullptr>
-#else
-template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned in C2>
-#endif /* __DOXYGEN__ */
-Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)
+template<typename T1, unsigned int R1, unsigned int C1, typename T2, unsigned int R2, unsigned int C2>
+constexpr Matrix<std::common_type_t<T1, T2>, R1, C2> operator*(const Matrix<T1, R1, C1> &m1,
+							       const Matrix<T2, R2, C2> &m2)
 {
-	Matrix<T, R1, C2> result;
+	static_assert(C1 == R2, "Matrix dimensions must match for multiplication");
+	Matrix<std::common_type_t<T1, T2>, R1, C2> result;
 
 	for (unsigned int i = 0; i < R1; i++) {
 		for (unsigned int j = 0; j < C2; j++) {
-			T sum = 0;
+			std::common_type_t<T1, T2> sum = 0;
 
 			for (unsigned int k = 0; k < C1; k++)
 				sum += m1[i][k] * m2[k][j];
@@ -150,7 +175,7 @@ Matrix<T, R1, C2> operator*(const Matrix
 }
 
 template<typename T, unsigned int Rows, unsigned int Cols>
-Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
+constexpr Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
 {
 	Matrix<T, Rows, Cols> result;
 
diff -pruN 0.5.0-1/include/libcamera/internal/media_device.h 0.5.2-2/include/libcamera/internal/media_device.h
--- 0.5.0-1/include/libcamera/internal/media_device.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/media_device.h	2025-08-07 13:46:17.000000000 +0000
@@ -55,6 +55,8 @@ public:
 
 	Signal<> disconnected;
 
+	std::vector<MediaEntity *> locateEntities(unsigned int function);
+
 protected:
 	std::string logPrefix() const override;
 
diff -pruN 0.5.0-1/include/libcamera/internal/media_pipeline.h 0.5.2-2/include/libcamera/internal/media_pipeline.h
--- 0.5.0-1/include/libcamera/internal/media_pipeline.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/media_pipeline.h	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Media pipeline support
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+class CameraSensor;
+class MediaEntity;
+class MediaLink;
+class MediaPad;
+struct V4L2SubdeviceFormat;
+
+class MediaPipeline
+{
+public:
+	int init(MediaEntity *source, std::string_view sink);
+	int initLinks();
+	int configure(CameraSensor *sensor, V4L2SubdeviceFormat *);
+
+private:
+	struct Entity {
+		/* The media entity, always valid. */
+		MediaEntity *entity;
+		/*
+		 * Whether or not the entity is a subdev that supports the
+		 * routing API.
+		 */
+		bool supportsRouting;
+		/*
+		 * The local sink pad connected to the upstream entity, null for
+		 * the camera sensor at the beginning of the pipeline.
+		 */
+		const MediaPad *sink;
+		/*
+		 * The local source pad connected to the downstream entity, null
+		 * for the video node at the end of the pipeline.
+		 */
+		const MediaPad *source;
+		/*
+		 * The link on the source pad, to the downstream entity, null
+		 * for the video node at the end of the pipeline.
+		 */
+		MediaLink *sourceLink;
+	};
+
+	std::list<Entity> entities_;
+};
+
+} /* namespace libcamera */
diff -pruN 0.5.0-1/include/libcamera/internal/meson.build 0.5.2-2/include/libcamera/internal/meson.build
--- 0.5.0-1/include/libcamera/internal/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -11,6 +11,7 @@ libcamera_internal_headers = files([
     'camera_manager.h',
     'camera_sensor.h',
     'camera_sensor_properties.h',
+    'clock_recovery.h',
     'control_serializer.h',
     'control_validator.h',
     'converter.h',
@@ -32,6 +33,7 @@ libcamera_internal_headers = files([
     'matrix.h',
     'media_device.h',
     'media_object.h',
+    'media_pipeline.h',
     'pipeline_handler.h',
     'process.h',
     'pub_key.h',
diff -pruN 0.5.0-1/include/libcamera/internal/pipeline_handler.h 0.5.2-2/include/libcamera/internal/pipeline_handler.h
--- 0.5.0-1/include/libcamera/internal/pipeline_handler.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/pipeline_handler.h	2025-08-07 13:46:17.000000000 +0000
@@ -8,7 +8,6 @@
 #pragma once
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <sys/types.h>
 #include <vector>
@@ -34,7 +33,8 @@ class PipelineHandler : public std::enab
 			public Object
 {
 public:
-	PipelineHandler(CameraManager *manager);
+	PipelineHandler(CameraManager *manager,
+			unsigned int maxQueuedRequestsDevice = 32);
 	virtual ~PipelineHandler();
 
 	virtual bool match(DeviceEnumerator *enumerator) = 0;
@@ -81,6 +81,7 @@ protected:
 	virtual void releaseDevice(Camera *camera);
 
 	CameraManager *manager_;
+	const unsigned int maxQueuedRequestsDevice_;
 
 private:
 	void unlockMediaDevices();
@@ -89,13 +90,11 @@ private:
 	virtual void disconnect();
 
 	void doQueueRequest(Request *request);
-	void doQueueRequests();
+	void doQueueRequests(Camera *camera);
 
 	std::vector<std::shared_ptr<MediaDevice>> mediaDevices_;
 	std::vector<std::weak_ptr<Camera>> cameras_;
 
-	std::queue<Request *> waitingRequests_;
-
 	const char *name_;
 	unsigned int useCount_;
 
diff -pruN 0.5.0-1/include/libcamera/internal/process.h 0.5.2-2/include/libcamera/internal/process.h
--- 0.5.0-1/include/libcamera/internal/process.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/process.h	2025-08-07 13:46:17.000000000 +0000
@@ -7,11 +7,11 @@
 
 #pragma once
 
-#include <signal.h>
 #include <string>
-#include <vector>
 
+#include <libcamera/base/class.h>
 #include <libcamera/base/signal.h>
+#include <libcamera/base/span.h>
 #include <libcamera/base/unique_fd.h>
 
 namespace libcamera {
@@ -31,8 +31,8 @@ public:
 	~Process();
 
 	int start(const std::string &path,
-		  const std::vector<std::string> &args = std::vector<std::string>(),
-		  const std::vector<int> &fds = std::vector<int>());
+		  Span<const std::string> args = {},
+		  Span<const int> fds = {});
 
 	ExitStatus exitStatus() const { return exitStatus_; }
 	int exitCode() const { return exitCode_; }
@@ -42,43 +42,16 @@ public:
 	Signal<enum ExitStatus, int> finished;
 
 private:
-	void closeAllFdsExcept(const std::vector<int> &fds);
-	int isolate();
-	void died(int wstatus);
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(Process)
+
+	void onPidfdNotify();
 
 	pid_t pid_;
-	bool running_;
 	enum ExitStatus exitStatus_;
 	int exitCode_;
 
-	friend class ProcessManager;
-};
-
-class ProcessManager
-{
-public:
-	ProcessManager();
-	~ProcessManager();
-
-	void registerProcess(Process *proc);
-
-	static ProcessManager *instance();
-
-	int writePipe() const;
-
-	const struct sigaction &oldsa() const;
-
-private:
-	static ProcessManager *self_;
-
-	void sighandler();
-
-	std::list<Process *> processes_;
-
-	struct sigaction oldsa_;
-
-	EventNotifier *sigEvent_;
-	UniqueFD pipe_[2];
+	UniqueFD pidfd_;
+	std::unique_ptr<EventNotifier> pidfdNotify_;
 };
 
 } /* namespace libcamera */
diff -pruN 0.5.0-1/include/libcamera/internal/vector.h 0.5.2-2/include/libcamera/internal/vector.h
--- 0.5.0-1/include/libcamera/internal/vector.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/internal/vector.h	2025-08-07 13:46:17.000000000 +0000
@@ -13,6 +13,7 @@
 #include <numeric>
 #include <optional>
 #include <ostream>
+#include <type_traits>
 
 #include <libcamera/base/log.h>
 #include <libcamera/base/span.h>
@@ -42,8 +43,12 @@ public:
 
 	constexpr Vector(const std::array<T, Rows> &data)
 	{
-		for (unsigned int i = 0; i < Rows; i++)
-			data_[i] = data[i];
+		std::copy(data.begin(), data.end(), data_.begin());
+	}
+
+	constexpr Vector(const Span<const T, Rows> data)
+	{
+		std::copy(data.begin(), data.end(), data_.begin());
 	}
 
 	const T &operator[](size_t i) const
@@ -291,13 +296,13 @@ private:
 template<typename T>
 using RGB = Vector<T, 3>;
 
-template<typename T, unsigned int Rows, unsigned int Cols>
-Vector<T, Rows> operator*(const Matrix<T, Rows, Cols> &m, const Vector<T, Cols> &v)
+template<typename T, typename U, unsigned int Rows, unsigned int Cols>
+Vector<std::common_type_t<T, U>, Rows> operator*(const Matrix<T, Rows, Cols> &m, const Vector<U, Cols> &v)
 {
-	Vector<T, Rows> result;
+	Vector<std::common_type_t<T, U>, Rows> result;
 
 	for (unsigned int i = 0; i < Rows; i++) {
-		T sum = 0;
+		std::common_type_t<T, U> sum = 0;
 		for (unsigned int j = 0; j < Cols; j++)
 			sum += m[i][j] * v[j];
 		result[i] = sum;
diff -pruN 0.5.0-1/include/libcamera/ipa/mali-c55.mojom 0.5.2-2/include/libcamera/ipa/mali-c55.mojom
--- 0.5.0-1/include/libcamera/ipa/mali-c55.mojom	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/ipa/mali-c55.mojom	2025-08-07 13:46:17.000000000 +0000
@@ -28,7 +28,7 @@ interface IPAMaliC55Interface {
 };
 
 interface IPAMaliC55EventInterface {
-	paramsComputed(uint32 request);
+	paramsComputed(uint32 request, uint32 bytesused);
 	statsProcessed(uint32 request, libcamera.ControlList metadata);
 	setSensorControls(libcamera.ControlList sensorControls);
 };
diff -pruN 0.5.0-1/include/libcamera/ipa/raspberrypi.mojom 0.5.2-2/include/libcamera/ipa/raspberrypi.mojom
--- 0.5.0-1/include/libcamera/ipa/raspberrypi.mojom	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/ipa/raspberrypi.mojom	2025-08-07 13:46:17.000000000 +0000
@@ -52,7 +52,8 @@ struct ConfigResult {
 
 struct StartResult {
 	libcamera.ControlList controls;
-	int32 dropFrameCount;
+	int32 startupFrameCount;
+	int32 invalidFrameCount;
 };
 
 struct PrepareParams {
diff -pruN 0.5.0-1/include/libcamera/meson.build 0.5.2-2/include/libcamera/meson.build
--- 0.5.0-1/include/libcamera/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/include/libcamera/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -90,6 +90,7 @@ foreach mode, entry : controls_map
                                      command : [gen_controls, '-o', '@OUTPUT@',
                                                 '--mode', mode, '-t', template_file,
                                                 '-r', ranges_file, '@INPUT@'],
+                                     depend_files : [py_mod_controls],
                                      env : py_build_env,
                                      install : true,
                                      install_dir : libcamera_headers_install_dir)
diff -pruN 0.5.0-1/meson.build 0.5.2-2/meson.build
--- 0.5.0-1/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 
 project('libcamera', 'c', 'cpp',
     meson_version : '>= 0.63',
-    version : '0.5.0',
+    version : '0.5.2',
     default_options : [
         'werror=true',
         'warning_level=2',
@@ -74,6 +74,10 @@ cc = meson.get_compiler('c')
 cxx = meson.get_compiler('cpp')
 config_h = configuration_data()
 
+if cc.has_header_symbol('unistd.h', 'close_range', prefix : '#define _GNU_SOURCE')
+    config_h.set('HAVE_CLOSE_RANGE', 1)
+endif
+
 if cc.has_header_symbol('fcntl.h', 'F_ADD_SEALS', prefix : '#define _GNU_SOURCE')
     config_h.set('HAVE_FILE_SEALS', 1)
 endif
@@ -261,7 +265,7 @@ subdir('Documentation')
 subdir('test')
 
 if not meson.is_cross_build()
-    kernel_version_req = '>= 5.0.0'
+    kernel_version_req = '>= 5.4.0'
     kernel_version = run_command('uname', '-r', check : true).stdout().strip()
     if not kernel_version.version_compare(kernel_version_req)
         warning('The current running kernel version @0@ is too old to run libcamera.'
diff -pruN 0.5.0-1/meson_options.txt 0.5.2-2/meson_options.txt
--- 0.5.0-1/meson_options.txt	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/meson_options.txt	2025-08-07 13:46:17.000000000 +0000
@@ -18,6 +18,7 @@ option('cam',
 
 option('documentation',
         type : 'feature',
+        value : 'auto',
         description : 'Generate the project documentation')
 
 option('doc_werror',
diff -pruN 0.5.0-1/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild 0.5.2-2/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild
--- 0.5.0-1/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/package/gentoo/media-libs/libcamera/libcamera-9999.ebuild	1970-01-01 00:00:00.000000000 +0000
@@ -1,45 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-# Copyright 2019 Google Inc.
-
-EAPI=6
-PYTHON_COMPAT=( python3_{7..10} )
-
-inherit git-r3 meson python-any-r1
-
-DESCRIPTION="Camera support library for Linux"
-HOMEPAGE="http://libcamera.org"
-EGIT_REPO_URI="https://git.libcamera.org/libcamera/libcamera.git"
-EGIT_BRANCH="master"
-
-LICENSE="LGPL-2.1+"
-SLOT="0"
-KEYWORDS="*"
-IUSE="debug doc test udev"
-
-RDEPEND="
-	>=net-libs/gnutls-3.3:=
-	udev? ( virtual/libudev )
-"
-
-DEPEND="
-	${RDEPEND}
-	dev-libs/openssl
-	$(python_gen_any_dep 'dev-python/pyyaml[${PYTHON_USEDEP}]')
-"
-
-src_configure() {
-	local emesonargs=(
-		$(meson_feature doc documentation)
-		$(meson_use test)
-		--buildtype $(usex debug debug plain)
-	)
-	meson_src_configure
-}
-
-src_compile() {
-	meson_src_compile
-}
-
-src_install() {
-	meson_src_install
-}
diff -pruN 0.5.0-1/src/android/camera_device.cpp 0.5.2-2/src/android/camera_device.cpp
--- 0.5.0-1/src/android/camera_device.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/android/camera_device.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -1079,7 +1079,7 @@ int CameraDevice::processCaptureRequest(
 		buffer.internalBuffer = frameBuffer;
 
 		descriptor->request_->addBuffer(sourceStream->stream(),
-						frameBuffer, nullptr);
+						frameBuffer);
 
 		requestedStreams.insert(sourceStream);
 	}
diff -pruN 0.5.0-1/src/apps/cam/camera_session.cpp 0.5.2-2/src/apps/cam/camera_session.cpp
--- 0.5.0-1/src/apps/cam/camera_session.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/camera_session.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -62,11 +62,32 @@ CameraSession::CameraSession(CameraManag
 		return;
 	}
 
-	std::vector<StreamRole> roles = StreamKeyValueParser::roles(options_[OptStream]);
+	std::vector<StreamRole> roles =
+		StreamKeyValueParser::roles(options_[OptStream]);
+	std::vector<std::vector<StreamRole>> tryRoles;
+	if (!roles.empty()) {
+		/*
+		 * If the roles are explicitly specified then there's no need
+		 * to try other roles
+		 */
+		tryRoles.push_back(roles);
+	} else {
+		tryRoles.push_back({ StreamRole::Viewfinder });
+		tryRoles.push_back({ StreamRole::Raw });
+	}
+
+	std::unique_ptr<CameraConfiguration> config;
+	bool valid = false;
+	for (std::vector<StreamRole> &rolesIt : tryRoles) {
+		config = camera_->generateConfiguration(rolesIt);
+		if (config && config->size() == rolesIt.size()) {
+			roles = rolesIt;
+			valid = true;
+			break;
+		}
+	}
 
-	std::unique_ptr<CameraConfiguration> config =
-		camera_->generateConfiguration(roles);
-	if (!config || config->size() != roles.size()) {
+	if (!valid) {
 		std::cerr << "Failed to get default stream configuration"
 			  << std::endl;
 		return;
@@ -215,7 +236,17 @@ void CameraSession::listProperties() con
 		const ControlId *id = properties::properties.at(key);
 
 		std::cout << "Property: " << id->name() << " = "
-			  << value.toString() << std::endl;
+			  << value.toString();
+
+		if (!id->enumerators().empty()) {
+			int32_t val = value.get<int32_t>();
+			const auto &iter = id->enumerators().find(val);
+
+			if (iter != id->enumerators().end())
+				std::cout << " (" << iter->second << ")";
+		}
+
+		std::cout << std::endl;
 	}
 }
 
diff -pruN 0.5.0-1/src/apps/cam/capture_script.cpp 0.5.2-2/src/apps/cam/capture_script.cpp
--- 0.5.0-1/src/apps/cam/capture_script.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/capture_script.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -8,6 +8,7 @@
 #include "capture_script.h"
 
 #include <iostream>
+#include <memory>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -521,45 +522,22 @@ ControlValue CaptureScript::parseArrayCo
 	case ControlTypeNone:
 		break;
 	case ControlTypeBool: {
-		/*
-		 * This is unpleasant, but we cannot use an std::vector<> as its
-		 * boolean type overload does not allow to access the raw data,
-		 * as boolean values are stored in a bitmask for efficiency.
-		 *
-		 * As we need a contiguous memory region to wrap in a Span<>,
-		 * use an array instead but be strict about not overflowing it
-		 * by limiting the number of controls we can store.
-		 *
-		 * Be loud but do not fail, as the issue would present at
-		 * runtime and it's not fatal.
-		 */
-		static constexpr unsigned int kMaxNumBooleanControls = 1024;
-		std::array<bool, kMaxNumBooleanControls> values;
-		unsigned int idx = 0;
+		auto values = std::make_unique<bool[]>(repr.size());
 
-		for (const std::string &s : repr) {
-			bool val;
+		for (std::size_t i = 0; i < repr.size(); i++) {
+			const auto &s = repr[i];
 
 			if (s == "true") {
-				val = true;
+				values[i] = true;
 			} else if (s == "false") {
-				val = false;
+				values[i] = false;
 			} else {
 				unpackFailure(id, s);
 				return value;
 			}
-
-			if (idx == kMaxNumBooleanControls) {
-				std::cerr << "Cannot parse more than "
-					  << kMaxNumBooleanControls
-					  << " boolean controls" << std::endl;
-				break;
-			}
-
-			values[idx++] = val;
 		}
 
-		value = Span<bool>(values.data(), idx);
+		value = Span<bool>(values.get(), repr.size());
 		break;
 	}
 	case ControlTypeByte: {
@@ -600,10 +578,6 @@ ControlValue CaptureScript::parseArrayCo
 		value = Span<const float>(values.data(), values.size());
 		break;
 	}
-	case ControlTypeString: {
-		value = Span<const std::string>(repr.data(), repr.size());
-		break;
-	}
 	default:
 		std::cerr << "Unsupported control type" << std::endl;
 		break;
diff -pruN 0.5.0-1/src/apps/cam/drm.cpp 0.5.2-2/src/apps/cam/drm.cpp
--- 0.5.0-1/src/apps/cam/drm.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/drm.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -450,8 +450,6 @@ int Device::openCard()
 	}
 
 	for (struct dirent *res; (res = readdir(folder));) {
-		uint64_t cap;
-
 		if (strncmp(res->d_name, "card", 4))
 			continue;
 
@@ -465,15 +463,22 @@ int Device::openCard()
 		}
 
 		/*
-		 * Skip devices that don't support the modeset API, to avoid
-		 * selecting a DRM device corresponding to a GPU. There is no
-		 * modeset capability, but the kernel returns an error for most
-		 * caps if mode setting isn't support by the driver. The
-		 * DRM_CAP_DUMB_BUFFER capability is one of those, other would
-		 * do as well. The capability value itself isn't relevant.
+		 * Skip non-display devices. While this could in theory be done
+		 * by checking for support of the mode setting API, some
+		 * out-of-tree render-only GPU drivers (namely powervr)
+		 * incorrectly set the DRIVER_MODESET driver feature. Check for
+		 * the presence of at least one CRTC, encoder and connector
+		 * instead.
 		 */
-		ret = drmGetCap(fd_, DRM_CAP_DUMB_BUFFER, &cap);
-		if (ret < 0) {
+		std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> resources{
+			drmModeGetResources(fd_),
+			&drmModeFreeResources
+		};
+		if (!resources ||
+		    resources->count_connectors <= 0 ||
+		    resources->count_crtcs <= 0 ||
+		    resources->count_encoders <= 0) {
+			resources.reset();
 			drmClose(fd_);
 			fd_ = -1;
 			continue;
diff -pruN 0.5.0-1/src/apps/cam/meson.build 0.5.2-2/src/apps/cam/meson.build
--- 0.5.0-1/src/apps/cam/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -34,6 +34,7 @@ if libsdl2.found()
     cam_sources += files([
         'sdl_sink.cpp',
         'sdl_texture.cpp',
+        'sdl_texture_1plane.cpp',
         'sdl_texture_yuv.cpp',
     ])
 
diff -pruN 0.5.0-1/src/apps/cam/sdl_sink.cpp 0.5.2-2/src/apps/cam/sdl_sink.cpp
--- 0.5.0-1/src/apps/cam/sdl_sink.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_sink.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -11,6 +11,7 @@
 #include <fcntl.h>
 #include <iomanip>
 #include <iostream>
+#include <optional>
 #include <signal.h>
 #include <sstream>
 #include <string.h>
@@ -22,6 +23,7 @@
 #include "../common/event_loop.h"
 #include "../common/image.h"
 
+#include "sdl_texture_1plane.h"
 #ifdef HAVE_LIBJPEG
 #include "sdl_texture_mjpg.h"
 #endif
@@ -31,6 +33,46 @@ using namespace libcamera;
 
 using namespace std::chrono_literals;
 
+namespace {
+
+std::optional<SDL_PixelFormatEnum> singlePlaneFormatToSDL(const libcamera::PixelFormat &f)
+{
+	switch (f) {
+	case libcamera::formats::RGB888:
+		return SDL_PIXELFORMAT_BGR24;
+	case libcamera::formats::BGR888:
+		return SDL_PIXELFORMAT_RGB24;
+	case libcamera::formats::RGBA8888:
+		return SDL_PIXELFORMAT_ABGR32;
+	case libcamera::formats::ARGB8888:
+		return SDL_PIXELFORMAT_BGRA32;
+	case libcamera::formats::BGRA8888:
+		return SDL_PIXELFORMAT_ARGB32;
+	case libcamera::formats::ABGR8888:
+		return SDL_PIXELFORMAT_RGBA32;
+#if SDL_VERSION_ATLEAST(2, 29, 1)
+	case libcamera::formats::RGBX8888:
+		return SDL_PIXELFORMAT_XBGR32;
+	case libcamera::formats::XRGB8888:
+		return SDL_PIXELFORMAT_BGRX32;
+	case libcamera::formats::BGRX8888:
+		return SDL_PIXELFORMAT_XRGB32;
+	case libcamera::formats::XBGR8888:
+		return SDL_PIXELFORMAT_RGBX32;
+#endif
+	case libcamera::formats::YUYV:
+		return SDL_PIXELFORMAT_YUY2;
+	case libcamera::formats::UYVY:
+		return SDL_PIXELFORMAT_UYVY;
+	case libcamera::formats::YVYU:
+		return SDL_PIXELFORMAT_YVYU;
+	}
+
+	return {};
+}
+
+} /* namespace */
+
 SDLSink::SDLSink()
 	: window_(nullptr), renderer_(nullptr), rect_({}),
 	  init_(false)
@@ -62,25 +104,20 @@ int SDLSink::configure(const libcamera::
 	rect_.w = cfg.size.width;
 	rect_.h = cfg.size.height;
 
-	switch (cfg.pixelFormat) {
+	if (auto sdlFormat = singlePlaneFormatToSDL(cfg.pixelFormat))
+		texture_ = std::make_unique<SDLTexture1Plane>(rect_, *sdlFormat, cfg.stride);
 #ifdef HAVE_LIBJPEG
-	case libcamera::formats::MJPEG:
+	else if (cfg.pixelFormat == libcamera::formats::MJPEG)
 		texture_ = std::make_unique<SDLTextureMJPG>(rect_);
-		break;
 #endif
 #if SDL_VERSION_ATLEAST(2, 0, 16)
-	case libcamera::formats::NV12:
+	else if (cfg.pixelFormat == libcamera::formats::NV12)
 		texture_ = std::make_unique<SDLTextureNV12>(rect_, cfg.stride);
-		break;
 #endif
-	case libcamera::formats::YUYV:
-		texture_ = std::make_unique<SDLTextureYUYV>(rect_, cfg.stride);
-		break;
-	default:
-		std::cerr << "Unsupported pixel format "
-			  << cfg.pixelFormat.toString() << std::endl;
+	else {
+		std::cerr << "Unsupported pixel format " << cfg.pixelFormat << std::endl;
 		return -EINVAL;
-	};
+	}
 
 	return 0;
 }
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture.h 0.5.2-2/src/apps/cam/sdl_texture.h
--- 0.5.0-1/src/apps/cam/sdl_texture.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture.h	2025-08-07 13:46:17.000000000 +0000
@@ -7,7 +7,7 @@
 
 #pragma once
 
-#include <vector>
+#include <libcamera/base/span.h>
 
 #include <SDL2/SDL.h>
 
@@ -19,7 +19,7 @@ public:
 	SDLTexture(const SDL_Rect &rect, uint32_t pixelFormat, const int stride);
 	virtual ~SDLTexture();
 	int create(SDL_Renderer *renderer);
-	virtual void update(const std::vector<libcamera::Span<const uint8_t>> &data) = 0;
+	virtual void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) = 0;
 	SDL_Texture *get() const { return ptr_; }
 
 protected:
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_1plane.cpp 0.5.2-2/src/apps/cam/sdl_texture_1plane.cpp
--- 0.5.0-1/src/apps/cam/sdl_texture_1plane.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_1plane.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * SDL single plane textures
+ */
+
+#include "sdl_texture_1plane.h"
+
+#include <assert.h>
+
+void SDLTexture1Plane::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
+{
+	assert(data.size() == 1);
+	assert(data[0].size_bytes() == std::size_t(rect_.h) * std::size_t(stride_));
+	SDL_UpdateTexture(ptr_, nullptr, data[0].data(), stride_);
+}
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_1plane.h 0.5.2-2/src/apps/cam/sdl_texture_1plane.h
--- 0.5.0-1/src/apps/cam/sdl_texture_1plane.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_1plane.h	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * SDL single plane textures
+ */
+
+#pragma once
+
+#include "sdl_texture.h"
+
+class SDLTexture1Plane final : public SDLTexture
+{
+public:
+	using SDLTexture::SDLTexture;
+
+	void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
+};
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_mjpg.cpp 0.5.2-2/src/apps/cam/sdl_texture_mjpg.cpp
--- 0.5.0-1/src/apps/cam/sdl_texture_mjpg.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_mjpg.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -76,7 +76,7 @@ int SDLTextureMJPG::decompress(Span<cons
 	return 0;
 }
 
-void SDLTextureMJPG::update(const std::vector<libcamera::Span<const uint8_t>> &data)
+void SDLTextureMJPG::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
 {
 	decompress(data[0]);
 	SDL_UpdateTexture(ptr_, nullptr, rgb_.get(), stride_);
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_mjpg.h 0.5.2-2/src/apps/cam/sdl_texture_mjpg.h
--- 0.5.0-1/src/apps/cam/sdl_texture_mjpg.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_mjpg.h	2025-08-07 13:46:17.000000000 +0000
@@ -14,7 +14,7 @@ class SDLTextureMJPG : public SDLTexture
 public:
 	SDLTextureMJPG(const SDL_Rect &rect);
 
-	void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
+	void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
 
 private:
 	int decompress(libcamera::Span<const uint8_t> data);
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_yuv.cpp 0.5.2-2/src/apps/cam/sdl_texture_yuv.cpp
--- 0.5.0-1/src/apps/cam/sdl_texture_yuv.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_yuv.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -15,19 +15,9 @@ SDLTextureNV12::SDLTextureNV12(const SDL
 {
 }
 
-void SDLTextureNV12::update(const std::vector<libcamera::Span<const uint8_t>> &data)
+void SDLTextureNV12::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
 {
-	SDL_UpdateNVTexture(ptr_, &rect_, data[0].data(), stride_,
+	SDL_UpdateNVTexture(ptr_, nullptr, data[0].data(), stride_,
 			    data[1].data(), stride_);
 }
 #endif
-
-SDLTextureYUYV::SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride)
-	: SDLTexture(rect, SDL_PIXELFORMAT_YUY2, stride)
-{
-}
-
-void SDLTextureYUYV::update(const std::vector<libcamera::Span<const uint8_t>> &data)
-{
-	SDL_UpdateTexture(ptr_, &rect_, data[0].data(), stride_);
-}
diff -pruN 0.5.0-1/src/apps/cam/sdl_texture_yuv.h 0.5.2-2/src/apps/cam/sdl_texture_yuv.h
--- 0.5.0-1/src/apps/cam/sdl_texture_yuv.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/cam/sdl_texture_yuv.h	2025-08-07 13:46:17.000000000 +0000
@@ -14,13 +14,6 @@ class SDLTextureNV12 : public SDLTexture
 {
 public:
 	SDLTextureNV12(const SDL_Rect &rect, unsigned int stride);
-	void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
+	void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
 };
 #endif
-
-class SDLTextureYUYV : public SDLTexture
-{
-public:
-	SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride);
-	void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
-};
diff -pruN 0.5.0-1/src/apps/common/image.cpp 0.5.2-2/src/apps/common/image.cpp
--- 0.5.0-1/src/apps/common/image.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/common/image.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -98,12 +98,12 @@ unsigned int Image::numPlanes() const
 
 Span<uint8_t> Image::data(unsigned int plane)
 {
-	assert(plane <= planes_.size());
+	assert(plane < planes_.size());
 	return planes_[plane];
 }
 
 Span<const uint8_t> Image::data(unsigned int plane) const
 {
-	assert(plane <= planes_.size());
+	assert(plane < planes_.size());
 	return planes_[plane];
 }
diff -pruN 0.5.0-1/src/apps/common/stream_options.cpp 0.5.2-2/src/apps/common/stream_options.cpp
--- 0.5.0-1/src/apps/common/stream_options.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/common/stream_options.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -42,9 +42,8 @@ KeyValueParser::Options StreamKeyValuePa
 
 std::vector<StreamRole> StreamKeyValueParser::roles(const OptionValue &values)
 {
-	/* If no configuration values to examine default to viewfinder. */
 	if (values.empty())
-		return { StreamRole::Viewfinder };
+		return {};
 
 	const std::vector<OptionValue> &streamParameters = values.toArray();
 
diff -pruN 0.5.0-1/src/apps/lc-compliance/helpers/capture.cpp 0.5.2-2/src/apps/lc-compliance/helpers/capture.cpp
--- 0.5.0-1/src/apps/lc-compliance/helpers/capture.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/helpers/capture.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -23,12 +23,29 @@ Capture::~Capture()
 	stop();
 }
 
-void Capture::configure(StreamRole role)
+void Capture::configure(libcamera::Span<const libcamera::StreamRole> roles)
 {
-	config_ = camera_->generateConfiguration({ role });
+	assert(!roles.empty());
 
+	config_ = camera_->generateConfiguration(roles);
 	if (!config_)
-		GTEST_SKIP() << "Role not supported by camera";
+		GTEST_SKIP() << "Roles not supported by camera";
+
+	ASSERT_EQ(config_->size(), roles.size()) << "Unexpected number of streams in configuration";
+
+	/*
+	 * Set the buffers count to the largest value across all streams.
+	 * \todo: Should all streams from a Camera have the same buffer count ?
+	 */
+	auto largest =
+		std::max_element(config_->begin(), config_->end(),
+				 [](const StreamConfiguration &l, const StreamConfiguration &r)
+				 { return l.bufferCount < r.bufferCount; });
+
+	assert(largest != config_->end());
+
+	for (auto &cfg : *config_)
+		cfg.bufferCount = largest->bufferCount;
 
 	if (config_->validate() != CameraConfiguration::Valid) {
 		config_.reset();
@@ -103,29 +120,37 @@ void Capture::start()
 	assert(!allocator_.allocated());
 	assert(requests_.empty());
 
-	Stream *stream = config_->at(0).stream();
-	int count = allocator_.allocate(stream);
-
-	ASSERT_GE(count, 0) << "Failed to allocate buffers";
-	EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
-
-	const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_.buffers(stream);
+	const auto bufferCount = config_->at(0).bufferCount;
 
 	/* No point in testing less requests then the camera depth. */
-	if (queueLimit_ && *queueLimit_ < buffers.size()) {
-		GTEST_SKIP() << "Camera needs " << buffers.size()
+	if (queueLimit_ && *queueLimit_ < bufferCount) {
+		GTEST_SKIP() << "Camera needs " << bufferCount
 			     << " requests, can't test only " << *queueLimit_;
 	}
 
-	for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
+	for (std::size_t i = 0; i < bufferCount; i++) {
 		std::unique_ptr<Request> request = camera_->createRequest();
 		ASSERT_TRUE(request) << "Can't create request";
+		requests_.push_back(std::move(request));
+	}
 
-		ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
+	for (const auto &cfg : *config_) {
+		Stream *stream = cfg.stream();
 
-		requests_.push_back(std::move(request));
+		int count = allocator_.allocate(stream);
+		ASSERT_GE(count, 0) << "Failed to allocate buffers";
+
+		const auto &buffers = allocator_.buffers(stream);
+		ASSERT_EQ(buffers.size(), bufferCount) << "Mismatching buffer count";
+
+		for (std::size_t i = 0; i < bufferCount; i++) {
+			ASSERT_EQ(requests_[i]->addBuffer(stream, buffers[i].get()), 0)
+				<< "Failed to add buffer to request";
+		}
 	}
 
+	ASSERT_TRUE(allocator_.allocated());
+
 	camera_->requestCompleted.connect(this, &Capture::requestComplete);
 
 	ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
@@ -140,7 +165,12 @@ void Capture::stop()
 
 	camera_->requestCompleted.disconnect(this);
 
-	Stream *stream = config_->at(0).stream();
 	requests_.clear();
-	allocator_.free(stream);
+
+	for (const auto &cfg : *config_) {
+		EXPECT_EQ(allocator_.free(cfg.stream()), 0)
+			<< "Failed to free buffers associated with stream";
+	}
+
+	EXPECT_FALSE(allocator_.allocated());
 }
diff -pruN 0.5.0-1/src/apps/lc-compliance/helpers/capture.h 0.5.2-2/src/apps/lc-compliance/helpers/capture.h
--- 0.5.0-1/src/apps/lc-compliance/helpers/capture.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/helpers/capture.h	2025-08-07 13:46:17.000000000 +0000
@@ -20,7 +20,7 @@ public:
 	Capture(std::shared_ptr<libcamera::Camera> camera);
 	~Capture();
 
-	void configure(libcamera::StreamRole role);
+	void configure(libcamera::Span<const libcamera::StreamRole> roles);
 	void run(unsigned int captureLimit, std::optional<unsigned int> queueLimit = {});
 
 private:
diff -pruN 0.5.0-1/src/apps/lc-compliance/meson.build 0.5.2-2/src/apps/lc-compliance/meson.build
--- 0.5.0-1/src/apps/lc-compliance/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -15,6 +15,7 @@ lc_compliance_sources = files([
     'environment.cpp',
     'helpers/capture.cpp',
     'main.cpp',
+    'test_base.cpp',
     'tests/capture_test.cpp',
 ])
 
diff -pruN 0.5.0-1/src/apps/lc-compliance/test_base.cpp 0.5.2-2/src/apps/lc-compliance/test_base.cpp
--- 0.5.0-1/src/apps/lc-compliance/test_base.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/test_base.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, Collabora Ltd.
+ *
+ * Base definitions for tests
+ */
+
+#include "test_base.h"
+
+#include "environment.h"
+
+void CameraHolder::acquireCamera()
+{
+	Environment *env = Environment::get();
+
+	camera_ = env->cm()->get(env->cameraId());
+
+	ASSERT_EQ(camera_->acquire(), 0);
+}
+
+void CameraHolder::releaseCamera()
+{
+	if (!camera_)
+		return;
+
+	camera_->release();
+	camera_.reset();
+}
diff -pruN 0.5.0-1/src/apps/lc-compliance/test_base.h 0.5.2-2/src/apps/lc-compliance/test_base.h
--- 0.5.0-1/src/apps/lc-compliance/test_base.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/test_base.h	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, Collabora Ltd.
+ *
+ * Base definitions for tests
+ */
+#pragma once
+
+#include <libcamera/libcamera.h>
+
+#include <gtest/gtest.h>
+
+class CameraHolder
+{
+protected:
+	void acquireCamera();
+	void releaseCamera();
+
+	std::shared_ptr<libcamera::Camera> camera_;
+};
diff -pruN 0.5.0-1/src/apps/lc-compliance/tests/capture_test.cpp 0.5.2-2/src/apps/lc-compliance/tests/capture_test.cpp
--- 0.5.0-1/src/apps/lc-compliance/tests/capture_test.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/lc-compliance/tests/capture_test.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -8,72 +8,54 @@
 
 #include "capture.h"
 
-#include <iostream>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <vector>
 
 #include <gtest/gtest.h>
 
-#include "environment.h"
+#include "test_base.h"
 
 namespace {
 
 using namespace libcamera;
 
-const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
-
-const StreamRole ROLES[] = {
-	StreamRole::Raw,
-	StreamRole::StillCapture,
-	StreamRole::VideoRecording,
-	StreamRole::Viewfinder
-};
-
-class SingleStream : public testing::TestWithParam<std::tuple<StreamRole, int>>
+class SimpleCapture : public testing::TestWithParam<std::tuple<std::vector<StreamRole>, int>>, public CameraHolder
 {
 public:
-	static std::string nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info);
+	static std::string nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info);
 
 protected:
 	void SetUp() override;
 	void TearDown() override;
-
-	std::shared_ptr<Camera> camera_;
 };
 
 /*
  * We use gtest's SetUp() and TearDown() instead of constructor and destructor
  * in order to be able to assert on them.
  */
-void SingleStream::SetUp()
+void SimpleCapture::SetUp()
 {
-	Environment *env = Environment::get();
-
-	camera_ = env->cm()->get(env->cameraId());
-
-	ASSERT_EQ(camera_->acquire(), 0);
+	acquireCamera();
 }
 
-void SingleStream::TearDown()
+void SimpleCapture::TearDown()
 {
-	if (!camera_)
-		return;
-
-	camera_->release();
-	camera_.reset();
+	releaseCamera();
 }
 
-std::string SingleStream::nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info)
+std::string SimpleCapture::nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info)
 {
-	std::map<StreamRole, std::string> rolesMap = {
-		{ StreamRole::Raw, "Raw" },
-		{ StreamRole::StillCapture, "StillCapture" },
-		{ StreamRole::VideoRecording, "VideoRecording" },
-		{ StreamRole::Viewfinder, "Viewfinder" }
-	};
+	const auto &[roles, numRequests] = info.param;
+	std::ostringstream ss;
+
+	for (StreamRole r : roles)
+		ss << r << '_';
 
-	std::string roleName = rolesMap[std::get<0>(info.param)];
-	std::string numRequestsName = std::to_string(std::get<1>(info.param));
+	ss << '_' << numRequests;
 
-	return roleName + "_" + numRequestsName;
+	return ss.str();
 }
 
 /*
@@ -83,13 +65,13 @@ std::string SingleStream::nameParameters
  * failure is a camera that completes less requests than the number of requests
  * queued.
  */
-TEST_P(SingleStream, Capture)
+TEST_P(SimpleCapture, Capture)
 {
-	auto [role, numRequests] = GetParam();
+	const auto &[roles, numRequests] = GetParam();
 
 	Capture capture(camera_);
 
-	capture.configure(role);
+	capture.configure(roles);
 
 	capture.run(numRequests, numRequests);
 }
@@ -101,14 +83,14 @@ TEST_P(SingleStream, Capture)
  * a camera that does not clean up correctly in its error path but is only
  * tested by single-capture applications.
  */
-TEST_P(SingleStream, CaptureStartStop)
+TEST_P(SimpleCapture, CaptureStartStop)
 {
-	auto [role, numRequests] = GetParam();
+	const auto &[roles, numRequests] = GetParam();
 	unsigned int numRepeats = 3;
 
 	Capture capture(camera_);
 
-	capture.configure(role);
+	capture.configure(roles);
 
 	for (unsigned int starts = 0; starts < numRepeats; starts++)
 		capture.run(numRequests, numRequests);
@@ -121,21 +103,43 @@ TEST_P(SingleStream, CaptureStartStop)
  * is a camera that does not handle cancelation of buffers coming back from the
  * video device while stopping.
  */
-TEST_P(SingleStream, UnbalancedStop)
+TEST_P(SimpleCapture, UnbalancedStop)
 {
-	auto [role, numRequests] = GetParam();
+	const auto &[roles, numRequests] = GetParam();
 
 	Capture capture(camera_);
 
-	capture.configure(role);
+	capture.configure(roles);
 
 	capture.run(numRequests);
 }
 
-INSTANTIATE_TEST_SUITE_P(CaptureTests,
-			 SingleStream,
-			 testing::Combine(testing::ValuesIn(ROLES),
+const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
+
+const std::vector<StreamRole> SINGLEROLES[] = {
+	{ StreamRole::Raw, },
+	{ StreamRole::StillCapture, },
+	{ StreamRole::VideoRecording, },
+	{ StreamRole::Viewfinder, },
+};
+
+const std::vector<StreamRole> MULTIROLES[] = {
+	{ StreamRole::Raw, StreamRole::StillCapture },
+	{ StreamRole::Raw, StreamRole::VideoRecording },
+	{ StreamRole::StillCapture, StreamRole::VideoRecording },
+	{ StreamRole::VideoRecording, StreamRole::VideoRecording },
+};
+
+INSTANTIATE_TEST_SUITE_P(SingleStream,
+			 SimpleCapture,
+			 testing::Combine(testing::ValuesIn(SINGLEROLES),
+					  testing::ValuesIn(NUMREQUESTS)),
+			 SimpleCapture::nameParameters);
+
+INSTANTIATE_TEST_SUITE_P(MultiStream,
+			 SimpleCapture,
+			 testing::Combine(testing::ValuesIn(MULTIROLES),
 					  testing::ValuesIn(NUMREQUESTS)),
-			 SingleStream::nameParameters);
+			 SimpleCapture::nameParameters);
 
 } /* namespace */
diff -pruN 0.5.0-1/src/apps/qcam/main_window.cpp 0.5.2-2/src/apps/qcam/main_window.cpp
--- 0.5.0-1/src/apps/qcam/main_window.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/qcam/main_window.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -356,6 +356,9 @@ int MainWindow::startCapture()
 
 	/* Verify roles are supported. */
 	switch (roles.size()) {
+	case 0:
+		roles.push_back(StreamRole::Viewfinder);
+		break;
 	case 1:
 		if (roles[0] != StreamRole::Viewfinder) {
 			qWarning() << "Only viewfinder supported for single stream";
diff -pruN 0.5.0-1/src/apps/qcam/meson.build 0.5.2-2/src/apps/qcam/meson.build
--- 0.5.0-1/src/apps/qcam/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/apps/qcam/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -42,6 +42,13 @@ qt6_cpp_args = [
     '-Wno-extra-semi',
 ]
 
+# gcc 12 and 13 output a false positive variable shadowing warning with Qt
+# 6.9.0 and newer. Silence it.
+if qt6_dep.version().version_compare('>=6.9.0') and \
+   cxx.version().version_compare('>=12') and cxx.version().version_compare('<14')
+    qt6_cpp_args += ['-Wno-shadow']
+endif
+
 resources = qt6.preprocess(moc_headers : qcam_moc_headers,
                            qresources : qcam_resources,
                            dependencies : qt6_dep)
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamera-controls.cpp.in 0.5.2-2/src/gstreamer/gstlibcamera-controls.cpp.in
--- 0.5.0-1/src/gstreamer/gstlibcamera-controls.cpp.in	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamera-controls.cpp.in	2025-08-07 13:46:17.000000000 +0000
@@ -14,50 +14,10 @@
 #include <libcamera/geometry.h>
 
 #include "gstlibcamera-controls.h"
+#include "gstlibcamera-utils.h"
 
 using namespace libcamera;
 
-static void value_set_rectangle(GValue *value, const Rectangle &rect)
-{
-	Point top_left = rect.topLeft();
-	Size size = rect.size();
-
-	GValue x = G_VALUE_INIT;
-	g_value_init(&x, G_TYPE_INT);
-	g_value_set_int(&x, top_left.x);
-	gst_value_array_append_and_take_value(value, &x);
-
-	GValue y = G_VALUE_INIT;
-	g_value_init(&y, G_TYPE_INT);
-	g_value_set_int(&y, top_left.y);
-	gst_value_array_append_and_take_value(value, &y);
-
-	GValue width = G_VALUE_INIT;
-	g_value_init(&width, G_TYPE_INT);
-	g_value_set_int(&width, size.width);
-	gst_value_array_append_and_take_value(value, &width);
-
-	GValue height = G_VALUE_INIT;
-	g_value_init(&height, G_TYPE_INT);
-	g_value_set_int(&height, size.height);
-	gst_value_array_append_and_take_value(value, &height);
-}
-
-static Rectangle value_get_rectangle(const GValue *value)
-{
-	const GValue *r;
-	r = gst_value_array_get_value(value, 0);
-	int x = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 1);
-	int y = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 2);
-	int w = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 3);
-	int h = g_value_get_int(r);
-
-	return Rectangle(x, y, w, h);
-}
-
 {% for vendor, ctrls in controls %}
 {%- for ctrl in ctrls if ctrl.is_enum %}
 static const GEnumValue {{ ctrl.name|snake_case }}_types[] = {
@@ -68,7 +28,7 @@ static const GEnumValue {{ ctrl.name|sna
 		"{{ enum.gst_name }}"
 	},
 {%- endfor %}
-	{0, NULL, NULL}
+	{0, nullptr, nullptr}
 };
 
 #define TYPE_{{ ctrl.name|snake_case|upper }} \
@@ -173,7 +133,7 @@ bool GstCameraControls::getProperty(guin
 			GValue element = G_VALUE_INIT;
 {%- if ctrl.is_rectangle %}
 			g_value_init(&element, GST_TYPE_PARAM_ARRAY_LIST);
-			value_set_rectangle(&element, control[i]);
+			gst_libcamera_gvalue_set_rectangle(&element, control[i]);
 {%- else %}
 			g_value_init(&element, G_TYPE_{{ ctrl.gtype|upper }});
 			g_value_set_{{ ctrl.gtype }}(&element, control[i]);
@@ -182,7 +142,7 @@ bool GstCameraControls::getProperty(guin
 		}
 {%- else %}
 {%- if ctrl.is_rectangle %}
-		value_set_rectangle(value, control);
+		gst_libcamera_gvalue_set_rectangle(value, control);
 {%- else %}
 		g_value_set_{{ ctrl.gtype }}(value, control);
 {%- endif %}
@@ -246,7 +206,7 @@ bool GstCameraControls::setProperty(guin
 					  i);
 				return true;
 			}
-			values[i] = value_get_rectangle(element);
+			values[i] = gst_libcamera_gvalue_get_rectangle(element);
 {%- else %}
 			values[i] = g_value_get_{{ ctrl.gtype }}(element);
 {%- endif %}
@@ -265,7 +225,7 @@ bool GstCameraControls::setProperty(guin
 				  "array of size 4");
 			return true;
 		}
-		Rectangle val = value_get_rectangle(value);
+		Rectangle val = gst_libcamera_gvalue_get_rectangle(value);
 {%- else %}
 		auto val = g_value_get_{{ ctrl.gtype }}(value);
 {%- endif %}
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamera-utils.cpp 0.5.2-2/src/gstreamer/gstlibcamera-utils.cpp
--- 0.5.0-1/src/gstreamer/gstlibcamera-utils.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamera-utils.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -8,19 +8,21 @@
 
 #include "gstlibcamera-utils.h"
 
+#include <string>
+
 #include <libcamera/control_ids.h>
 #include <libcamera/formats.h>
 
 using namespace libcamera;
 
-static struct {
+static const struct {
 	GstVideoFormat gst_format;
 	PixelFormat format;
 } format_map[] = {
 	/* Compressed */
 	{ GST_VIDEO_FORMAT_ENCODED, formats::MJPEG },
 
-	/* Bayer formats, gstreamer only supports 8-bit */
+	/* Bayer formats */
 	{ GST_VIDEO_FORMAT_ENCODED, formats::SBGGR8 },
 	{ GST_VIDEO_FORMAT_ENCODED, formats::SGBRG8 },
 	{ GST_VIDEO_FORMAT_ENCODED, formats::SGRBG8 },
@@ -317,20 +319,42 @@ bare_structure_from_format(const PixelFo
 		return gst_structure_new("video/x-raw", "format", G_TYPE_STRING,
 					 gst_video_format_to_string(gst_format), nullptr);
 
-	switch (format) {
-	case formats::MJPEG:
+	if (format == formats::MJPEG)
 		return gst_structure_new_empty("image/jpeg");
 
-	case formats::SBGGR8:
-	case formats::SGBRG8:
-	case formats::SGRBG8:
-	case formats::SRGGB8:
-		return gst_structure_new("video/x-bayer", "format", G_TYPE_STRING,
-					 bayer_format_to_string(format), nullptr);
+	const gchar *s = bayer_format_to_string(format);
+	if (s)
+		return gst_structure_new("video/x-bayer", "format",
+					 G_TYPE_STRING, s, nullptr);
 
-	default:
-		return nullptr;
+	return nullptr;
+}
+
+static const struct {
+	ControlType c_type;
+	GType g_type;
+} control_type_gtype_map[] = {
+	{ ControlTypeBool, G_TYPE_BOOLEAN },
+	{ ControlTypeByte, G_TYPE_UINT },
+	{ ControlTypeUnsigned16, G_TYPE_UINT },
+	{ ControlTypeUnsigned32, G_TYPE_UINT },
+	{ ControlTypeInteger32, G_TYPE_INT },
+	{ ControlTypeInteger64, G_TYPE_INT64 },
+	{ ControlTypeFloat, G_TYPE_FLOAT },
+	{ ControlTypeString, G_TYPE_STRING },
+	{ ControlTypeRectangle, GST_TYPE_ARRAY },
+	{ ControlTypeSize, GST_TYPE_ARRAY },
+	{ ControlTypePoint, GST_TYPE_ARRAY },
+};
+
+static GType
+control_type_to_gtype(const ControlType &type)
+{
+	for (auto &a : control_type_gtype_map) {
+		if (a.c_type == type)
+			return a.g_type;
 	}
+	return G_TYPE_INVALID;
 }
 
 GstCaps *
@@ -494,9 +518,12 @@ void gst_libcamera_configure_stream_from
 
 	/* Configure colorimetry */
 	if (gst_structure_has_field(s, "colorimetry")) {
-		const gchar *colorimetry_str = gst_structure_get_string(s, "colorimetry");
+		const gchar *colorimetry_str;
 		GstVideoColorimetry colorimetry;
 
+		gst_structure_fixate_field(s, "colorimetry");
+		colorimetry_str = gst_structure_get_string(s, "colorimetry");
+
 		if (!gst_video_colorimetry_from_string(&colorimetry, colorimetry_str))
 			g_critical("Invalid colorimetry %s", colorimetry_str);
 
@@ -581,6 +608,53 @@ void gst_libcamera_framerate_to_caps(Gst
 	gst_structure_set(s, "framerate", GST_TYPE_FRACTION, fps_caps_n, fps_caps_d, nullptr);
 }
 
+void gst_libcamera_gvalue_set_point(GValue *value, const Point &point)
+{
+	GValue x = G_VALUE_INIT;
+	g_value_init(&x, G_TYPE_INT);
+	g_value_set_int(&x, point.x);
+	gst_value_array_append_and_take_value(value, &x);
+
+	GValue y = G_VALUE_INIT;
+	g_value_init(&y, G_TYPE_INT);
+	g_value_set_int(&y, point.y);
+	gst_value_array_append_and_take_value(value, &y);
+}
+
+void gst_libcamera_gvalue_set_size(GValue *value, const Size &size)
+{
+	GValue width = G_VALUE_INIT;
+	g_value_init(&width, G_TYPE_INT);
+	g_value_set_int(&width, size.width);
+	gst_value_array_append_and_take_value(value, &width);
+
+	GValue height = G_VALUE_INIT;
+	g_value_init(&height, G_TYPE_INT);
+	g_value_set_int(&height, size.height);
+	gst_value_array_append_and_take_value(value, &height);
+}
+
+void gst_libcamera_gvalue_set_rectangle(GValue *value, const Rectangle &rect)
+{
+	gst_libcamera_gvalue_set_point(value, rect.topLeft());
+	gst_libcamera_gvalue_set_size(value, rect.size());
+}
+
+Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value)
+{
+	const GValue *r;
+	r = gst_value_array_get_value(value, 0);
+	int x = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 1);
+	int y = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 2);
+	int w = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 3);
+	int h = g_value_get_int(r);
+
+	return Rectangle(x, y, w, h);
+}
+
 #if !GST_CHECK_VERSION(1, 17, 1)
 gboolean
 gst_task_resume(GstTask *task)
@@ -596,6 +670,43 @@ gst_task_resume(GstTask *task)
 }
 #endif
 
+#if !GST_CHECK_VERSION(1, 22, 0)
+/*
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
+ */
+/*
+ * This function has been imported directly from the gstreamer project to
+ * support backwards compatibility and should be removed when the older version
+ * is no longer supported.
+ */
+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
+{
+	gint estride;
+	gint comp[GST_VIDEO_MAX_COMPONENTS];
+	gint i;
+
+	/* There is nothing to extrapolate on first plane. */
+	if (plane == 0)
+		return stride;
+
+	gst_video_format_info_component(finfo, plane, comp);
+
+	/*
+	 * For now, all planar formats have a single component on first plane, but
+	 * if there was a planar format with more, we'd have to make a ratio of the
+	 * number of component on the first plane against the number of component on
+	 * the current plane.
+	 */
+	estride = 0;
+	for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
+		estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
+
+	return estride;
+}
+#endif
+
 G_LOCK_DEFINE_STATIC(cm_singleton_lock);
 static std::weak_ptr<CameraManager> cm_singleton_ptr;
 
@@ -619,3 +730,171 @@ gst_libcamera_get_camera_manager(int &re
 
 	return cm;
 }
+
+int gst_libcamera_set_structure_field(GstStructure *structure, const ControlId *id,
+				      const ControlValue &value)
+{
+	std::string prop = "api.libcamera." + id->name();
+	g_auto(GValue) v = G_VALUE_INIT;
+	g_auto(GValue) x = G_VALUE_INIT;
+	gboolean is_array = value.isArray();
+
+	GType type = control_type_to_gtype(value.type());
+	if (type == G_TYPE_INVALID)
+		return -EINVAL;
+
+	if (is_array || type == GST_TYPE_ARRAY)
+		g_value_init(&v, GST_TYPE_ARRAY);
+
+	switch (value.type()) {
+	case ControlTypeBool:
+		if (is_array) {
+			Span<const bool> data = value.get<Span<const bool>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_boolean(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_BOOLEAN,
+					  value.get<const bool>(), nullptr);
+		}
+		break;
+	case ControlTypeByte:
+		if (is_array) {
+			Span<const uint8_t> data = value.get<Span<const uint8_t>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_uint(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_UINT,
+					  value.get<const uint8_t>(), nullptr);
+		}
+		break;
+	case ControlTypeUnsigned16:
+		if (is_array) {
+			Span<const uint16_t> data = value.get<Span<const uint16_t>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_uint(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_UINT,
+					  value.get<const uint16_t>(), nullptr);
+		}
+		break;
+	case ControlTypeUnsigned32:
+		if (is_array) {
+			Span<const uint32_t> data = value.get<Span<const uint32_t>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_uint(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_UINT,
+					  value.get<const uint32_t>(), nullptr);
+		}
+		break;
+	case ControlTypeInteger32:
+		if (is_array) {
+			Span<const int32_t> data = value.get<Span<const int32_t>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_int(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			if (!id->enumerators().empty()) {
+				int32_t val = value.get<int32_t>();
+				const auto &iter = id->enumerators().find(val);
+				if (iter != id->enumerators().end()) {
+					gst_structure_set(structure, prop.c_str(),
+							  G_TYPE_STRING,
+							  iter->second.c_str(),
+							  nullptr);
+				} else {
+					return -EINVAL;
+				}
+			} else {
+				gst_structure_set(structure, prop.c_str(), G_TYPE_INT,
+						  value.get<const int32_t>(), nullptr);
+			}
+		}
+		break;
+	case ControlTypeInteger64:
+		if (is_array) {
+			Span<const int64_t> data = value.get<Span<const int64_t>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_int64(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_INT64,
+					  value.get<const int64_t>(), nullptr);
+		}
+		break;
+	case ControlTypeFloat:
+		if (is_array) {
+			Span<const float> data = value.get<Span<const float>>();
+			for (auto it = data.begin(); it != data.end(); ++it) {
+				g_value_init(&x, type);
+				g_value_set_float(&x, *it);
+				gst_value_array_append_and_take_value(&v, &x);
+			}
+		} else {
+			gst_structure_set(structure, prop.c_str(), G_TYPE_FLOAT,
+					  value.get<const float>(), nullptr);
+		}
+		break;
+	case ControlTypeString:
+		/*
+		 * isArray() is always true for strings hence, unset the GValue
+		 * array because we are going to the toString() helper directly.
+		 */
+		g_value_unset(&v);
+		gst_structure_set(structure, prop.c_str(), G_TYPE_STRING,
+				  value.toString().c_str(), nullptr);
+		break;
+	case ControlTypeSize:
+		if (is_array) {
+			Span<const Size> data = value.get<Span<const Size>>();
+			for (auto it = data.begin(); it != data.end(); ++it)
+				gst_libcamera_gvalue_set_size(&v, *it);
+		} else {
+			gst_libcamera_gvalue_set_size(&v, value.get<const Size>());
+		}
+		break;
+	case ControlTypePoint:
+		if (is_array) {
+			Span<const Point> data = value.get<Span<const Point>>();
+			for (auto it = data.begin(); it != data.end(); ++it)
+				gst_libcamera_gvalue_set_point(&v, *it);
+		} else {
+			gst_libcamera_gvalue_set_point(&v, value.get<const Point>());
+		}
+		break;
+	case ControlTypeRectangle:
+		if (is_array) {
+			Span<const Rectangle> data = value.get<Span<const Rectangle>>();
+			for (auto it = data.begin(); it != data.end(); ++it)
+				gst_libcamera_gvalue_set_rectangle(&v, *it);
+		} else {
+			gst_libcamera_gvalue_set_rectangle(&v, value.get<const Rectangle>());
+		}
+		break;
+	case ControlTypeNone:
+		[[fallthrough]];
+	default:
+		return -EINVAL;
+	}
+
+	if (GST_VALUE_HOLDS_ARRAY(&v))
+		gst_structure_set_value(structure, prop.c_str(), &v);
+
+	return 0;
+}
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamera-utils.h 0.5.2-2/src/gstreamer/gstlibcamera-utils.h
--- 0.5.0-1/src/gstreamer/gstlibcamera-utils.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamera-utils.h	2025-08-07 13:46:17.000000000 +0000
@@ -25,6 +25,13 @@ void gst_libcamera_clamp_and_set_framedu
 					       const libcamera::ControlInfoMap &camera_controls,
 					       GstStructure *element_caps);
 void gst_libcamera_framerate_to_caps(GstCaps *caps, const GstStructure *element_caps);
+void gst_libcamera_gvalue_set_point(GValue *value, const libcamera::Point &point);
+void gst_libcamera_gvalue_set_size(GValue *value, const libcamera::Size &size);
+void gst_libcamera_gvalue_set_rectangle(GValue *value, const libcamera::Rectangle &rect);
+libcamera::Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value);
+int gst_libcamera_set_structure_field(GstStructure *structure,
+				      const libcamera::ControlId *id,
+				      const libcamera::ControlValue &value);
 
 #if !GST_CHECK_VERSION(1, 16, 0)
 static inline void gst_clear_event(GstEvent **event_ptr)
@@ -36,6 +43,11 @@ static inline void gst_clear_event(GstEv
 #if !GST_CHECK_VERSION(1, 17, 1)
 gboolean gst_task_resume(GstTask *task);
 #endif
+
+#if !GST_CHECK_VERSION(1, 22, 0)
+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
+#endif
+
 std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
 
 /**
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamerapad.cpp 0.5.2-2/src/gstreamer/gstlibcamerapad.cpp
--- 0.5.0-1/src/gstreamer/gstlibcamerapad.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamerapad.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -18,6 +18,8 @@ struct _GstLibcameraPad {
 	GstPad parent;
 	StreamRole role;
 	GstLibcameraPool *pool;
+	GstBufferPool *video_pool;
+	GstVideoInfo info;
 	GstClockTime latency;
 };
 
@@ -70,6 +72,10 @@ gst_libcamera_pad_query(GstPad *pad, Gst
 	if (query->type != GST_QUERY_LATENCY)
 		return gst_pad_query_default(pad, parent, query);
 
+	GLibLocker lock(GST_OBJECT(self));
+	if (self->latency == GST_CLOCK_TIME_NONE)
+		return FALSE;
+
 	/* TRUE here means live, we assumes that max latency is the same as min
 	 * as we have no idea that duration of frames. */
 	gst_query_set_latency(query, TRUE, self->latency, self->latency);
@@ -79,6 +85,7 @@ gst_libcamera_pad_query(GstPad *pad, Gst
 static void
 gst_libcamera_pad_init(GstLibcameraPad *self)
 {
+	self->latency = GST_CLOCK_TIME_NONE;
 	GST_PAD_QUERYFUNC(self) = gst_libcamera_pad_query;
 }
 
@@ -100,7 +107,7 @@ gst_libcamera_stream_role_get_type()
 			"libcamera::Viewfinder",
 			"view-finder",
 		},
-		{ 0, NULL, NULL }
+		{ 0, nullptr, nullptr }
 	};
 
 	if (!type)
@@ -153,6 +160,35 @@ gst_libcamera_pad_set_pool(GstPad *pad,
 	self->pool = pool;
 }
 
+GstBufferPool *
+gst_libcamera_pad_get_video_pool(GstPad *pad)
+{
+	auto *self = GST_LIBCAMERA_PAD(pad);
+	return self->video_pool;
+}
+
+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool)
+{
+	auto *self = GST_LIBCAMERA_PAD(pad);
+
+	if (self->video_pool)
+		g_object_unref(self->video_pool);
+	self->video_pool = video_pool;
+}
+
+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad)
+{
+	auto *self = GST_LIBCAMERA_PAD(pad);
+	return self->info;
+}
+
+void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info)
+{
+	auto *self = GST_LIBCAMERA_PAD(pad);
+
+	self->info = *info;
+}
+
 Stream *
 gst_libcamera_pad_get_stream(GstPad *pad)
 {
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamerapad.h 0.5.2-2/src/gstreamer/gstlibcamerapad.h
--- 0.5.0-1/src/gstreamer/gstlibcamerapad.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamerapad.h	2025-08-07 13:46:17.000000000 +0000
@@ -23,6 +23,14 @@ GstLibcameraPool *gst_libcamera_pad_get_
 
 void gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool);
 
+GstBufferPool *gst_libcamera_pad_get_video_pool(GstPad *pad);
+
+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool);
+
+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad);
+
+void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info);
+
 libcamera::Stream *gst_libcamera_pad_get_stream(GstPad *pad);
 
 void gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency);
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamerapool.cpp 0.5.2-2/src/gstreamer/gstlibcamerapool.cpp
--- 0.5.0-1/src/gstreamer/gstlibcamerapool.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamerapool.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -134,8 +134,20 @@ gst_libcamera_pool_class_init(GstLibcame
 						     G_TYPE_NONE, 0);
 }
 
+static void
+gst_libcamera_buffer_add_video_meta(GstBuffer *buffer, GstVideoInfo *info)
+{
+	GstVideoMeta *vmeta;
+	vmeta = gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
+					       GST_VIDEO_INFO_FORMAT(info), GST_VIDEO_INFO_WIDTH(info),
+					       GST_VIDEO_INFO_HEIGHT(info), GST_VIDEO_INFO_N_PLANES(info),
+					       info->offset, info->stride);
+	GST_META_FLAGS(vmeta) = (GstMetaFlags)(GST_META_FLAGS(vmeta) | GST_META_FLAG_POOLED);
+}
+
 GstLibcameraPool *
-gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
+gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream,
+		       GstVideoInfo *info)
 {
 	auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
 
@@ -145,6 +157,7 @@ gst_libcamera_pool_new(GstLibcameraAlloc
 	gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
 	for (gsize i = 0; i < pool_size; i++) {
 		GstBuffer *buffer = gst_buffer_new();
+		gst_libcamera_buffer_add_video_meta(buffer, info);
 		pool->queue->push_back(buffer);
 	}
 
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamerapool.h 0.5.2-2/src/gstreamer/gstlibcamerapool.h
--- 0.5.0-1/src/gstreamer/gstlibcamerapool.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamerapool.h	2025-08-07 13:46:17.000000000 +0000
@@ -14,6 +14,7 @@
 #include "gstlibcameraallocator.h"
 
 #include <gst/gst.h>
+#include <gst/video/video.h>
 
 #include <libcamera/stream.h>
 
@@ -21,7 +22,7 @@
 G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
 
 GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
-					 libcamera::Stream *stream);
+					 libcamera::Stream *stream, GstVideoInfo *info);
 
 libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
 
diff -pruN 0.5.0-1/src/gstreamer/gstlibcameraprovider.cpp 0.5.2-2/src/gstreamer/gstlibcameraprovider.cpp
--- 0.5.0-1/src/gstreamer/gstlibcameraprovider.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcameraprovider.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -12,6 +12,7 @@
 
 #include <libcamera/camera.h>
 #include <libcamera/camera_manager.h>
+#include <libcamera/property_ids.h>
 
 #include "gstlibcamerasrc.h"
 #include "gstlibcamera-utils.h"
@@ -144,12 +145,24 @@ gst_libcamera_device_new(const std::shar
 			gst_caps_append(caps, sub_caps);
 	}
 
+	g_autoptr(GstStructure) props = gst_structure_new_empty("camera-properties");
+	for (const auto &[key, value] : camera->properties()) {
+		const ControlId *id = properties::properties.at(key);
+
+		int ret = gst_libcamera_set_structure_field(props, id, value);
+		if (ret < 0) {
+			GST_ERROR("Failed to retrieve value for %s property", id->name().c_str());
+			return nullptr;
+		}
+	}
+
 	return GST_DEVICE(g_object_new(GST_TYPE_LIBCAMERA_DEVICE,
 				       /* \todo Use a unique identifier instead of camera name. */
 				       "name", name,
 				       "display-name", name,
 				       "caps", caps,
 				       "device-class", "Source/Video",
+				       "properties", props,
 				       nullptr));
 }
 
diff -pruN 0.5.0-1/src/gstreamer/gstlibcamerasrc.cpp 0.5.2-2/src/gstreamer/gstlibcamerasrc.cpp
--- 0.5.0-1/src/gstreamer/gstlibcamerasrc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/gstlibcamerasrc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -29,6 +29,8 @@
 
 #include <atomic>
 #include <queue>
+#include <tuple>
+#include <utility>
 #include <vector>
 
 #include <libcamera/camera.h>
@@ -268,6 +270,69 @@ GstLibcameraSrcState::requestCompleted(R
 	gst_task_resume(src_->task);
 }
 
+static void
+gst_libcamera_extrapolate_info(GstVideoInfo *info, guint32 stride)
+{
+	guint i, estride;
+	gsize offset = 0;
+
+	/* This should be updated if tiled formats get added in the future. */
+	for (i = 0; i < GST_VIDEO_INFO_N_PLANES(info); i++) {
+		estride = gst_video_format_info_extrapolate_stride(info->finfo, i, stride);
+		info->stride[i] = estride;
+		info->offset[i] = offset;
+		offset += estride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info->finfo, i,
+								       GST_VIDEO_INFO_HEIGHT(info));
+	}
+}
+
+static GstFlowReturn
+gst_libcamera_video_frame_copy(GstBuffer *src, GstBuffer *dest,
+			       const GstVideoInfo *dest_info, guint32 stride)
+{
+	/*
+	 * When dropping support for versions earlier than v1.22.0, use
+	 *
+	 * g_auto (GstVideoFrame) src_frame = GST_VIDEO_FRAME_INIT;
+	 * g_auto (GstVideoFrame) dest_frame = GST_VIDEO_FRAME_INIT;
+	 *
+	 * and drop the gst_video_frame_unmap() calls.
+	 */
+	GstVideoFrame src_frame, dest_frame;
+	GstVideoInfo src_info = *dest_info;
+
+	gst_libcamera_extrapolate_info(&src_info, stride);
+	src_info.size = gst_buffer_get_size(src);
+
+	if (!gst_video_frame_map(&src_frame, &src_info, src, GST_MAP_READ)) {
+		GST_ERROR("Could not map src buffer");
+		return GST_FLOW_ERROR;
+	}
+
+	/*
+	 * When dropping support for versions earlier than 1.20.0, drop the
+	 * const_cast<>().
+	 */
+	if (!gst_video_frame_map(&dest_frame, const_cast<GstVideoInfo *>(dest_info),
+				 dest, GST_MAP_WRITE)) {
+		GST_ERROR("Could not map dest buffer");
+		gst_video_frame_unmap(&src_frame);
+		return GST_FLOW_ERROR;
+	}
+
+	if (!gst_video_frame_copy(&dest_frame, &src_frame)) {
+		GST_ERROR("Could not copy frame");
+		gst_video_frame_unmap(&src_frame);
+		gst_video_frame_unmap(&dest_frame);
+		return GST_FLOW_ERROR;
+	}
+
+	gst_video_frame_unmap(&src_frame);
+	gst_video_frame_unmap(&dest_frame);
+
+	return GST_FLOW_OK;
+}
+
 /* Must be called with stream_lock held. */
 int GstLibcameraSrcState::processRequest()
 {
@@ -292,11 +357,41 @@ int GstLibcameraSrcState::processRequest
 	GstFlowReturn ret = GST_FLOW_OK;
 	gst_flow_combiner_reset(src_->flow_combiner);
 
-	for (GstPad *srcpad : srcpads_) {
+	for (gsize i = 0; i < srcpads_.size(); i++) {
+		GstPad *srcpad = srcpads_[i];
 		Stream *stream = gst_libcamera_pad_get_stream(srcpad);
 		GstBuffer *buffer = wrap->detachBuffer(stream);
 
 		FrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer);
+		const StreamConfiguration &stream_cfg = config_->at(i);
+		GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad);
+
+		if (video_pool) {
+			/* Only set video pool when a copy is needed. */
+			GstBuffer *copy = nullptr;
+			const GstVideoInfo info = gst_libcamera_pad_get_video_info(srcpad);
+
+			ret = gst_buffer_pool_acquire_buffer(video_pool, &copy, nullptr);
+			if (ret != GST_FLOW_OK) {
+				gst_buffer_unref(buffer);
+				GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+						  ("Failed to acquire buffer"),
+						  ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+				return -EPIPE;
+			}
+
+			ret = gst_libcamera_video_frame_copy(buffer, copy, &info, stream_cfg.stride);
+			gst_buffer_unref(buffer);
+			if (ret != GST_FLOW_OK) {
+				gst_buffer_unref(copy);
+				GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+						  ("Failed to copy buffer"),
+						  ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+				return -EPIPE;
+			}
+
+			buffer = copy;
+		}
 
 		if (GST_CLOCK_TIME_IS_VALID(wrap->pts_)) {
 			GST_BUFFER_PTS(buffer) = wrap->pts_;
@@ -428,6 +523,73 @@ gst_libcamera_src_open(GstLibcameraSrc *
 	return true;
 }
 
+/**
+ * \brief Create a video pool for a pad
+ * \param[in] self The libcamerasrc instance
+ * \param[in] srcpad The pad
+ * \param[in] caps The pad caps
+ * \param[in] info The video info for the pad
+ *
+ * This function creates and returns a video buffer pool for the given pad if
+ * needed to accommodate stride mismatch. If the peer element supports stride
+ * negotiation through the meta API, no pool is needed and the function will
+ * return a null pool.
+ *
+ * \return A tuple containing the video buffers pool pointer and an error code
+ */
+static std::tuple<GstBufferPool *, int>
+gst_libcamera_create_video_pool(GstLibcameraSrc *self, GstPad *srcpad,
+				GstCaps *caps, const GstVideoInfo *info)
+{
+	g_autoptr(GstQuery) query = nullptr;
+	g_autoptr(GstBufferPool) pool = nullptr;
+	const gboolean need_pool = true;
+
+	/*
+	 * Get the peer allocation hints to check if it supports the meta API.
+	 * If so, the stride will be negotiated, and there's no need to create a
+	 * video pool.
+	 */
+	query = gst_query_new_allocation(caps, need_pool);
+
+	if (!gst_pad_peer_query(srcpad, query))
+		GST_DEBUG_OBJECT(self, "Didn't get downstream ALLOCATION hints");
+	else if (gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr))
+		return { nullptr, 0 };
+
+	GST_WARNING_OBJECT(self, "Downstream doesn't support video meta, need to copy frame.");
+
+	/*
+	 * If the allocation query has pools, use the first one. Otherwise,
+	 * create a new pool.
+	 */
+	if (gst_query_get_n_allocation_pools(query) > 0)
+		gst_query_parse_nth_allocation_pool(query, 0, &pool, nullptr,
+						    nullptr, nullptr);
+
+	if (!pool) {
+		GstStructure *config;
+		guint min_buffers = 3;
+
+		pool = gst_video_buffer_pool_new();
+		config = gst_buffer_pool_get_config(pool);
+		gst_buffer_pool_config_set_params(config, caps, info->size, min_buffers, 0);
+
+		GST_DEBUG_OBJECT(self, "Own pool config is %" GST_PTR_FORMAT, config);
+
+		gst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(pool), config);
+	}
+
+	if (!gst_buffer_pool_set_active(pool, true)) {
+		GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
+				  ("Failed to active buffer pool"),
+				  ("gst_libcamera_src_negotiate() failed."));
+		return { nullptr, -EINVAL };
+	}
+
+	return { std::exchange(pool, nullptr), 0 };
+}
+
 /* Must be called with stream_lock held. */
 static bool
 gst_libcamera_src_negotiate(GstLibcameraSrc *self)
@@ -455,8 +617,13 @@ gst_libcamera_src_negotiate(GstLibcamera
 	}
 
 	/* Validate the configuration. */
-	if (state->config_->validate() == CameraConfiguration::Invalid)
+	CameraConfiguration::Status status = state->config_->validate();
+	if (status == CameraConfiguration::Invalid)
 		return false;
+	else if (status == CameraConfiguration::Adjusted)
+		GST_ELEMENT_INFO(self, RESOURCE, SETTINGS,
+				 ("Configuration was adjusted"),
+				 ("CameraConfiguration::validate() returned CameraConfiguration::Adjusted"));
 
 	int ret = state->cam_->configure(state->config_.get());
 	if (ret) {
@@ -481,6 +648,10 @@ gst_libcamera_src_negotiate(GstLibcamera
 		g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
 		gst_libcamera_framerate_to_caps(caps, element_caps);
 
+		if (status == CameraConfiguration::Adjusted &&
+		    !gst_pad_peer_query_accept_caps(srcpad, caps))
+			return false;
+
 		if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps)))
 			return false;
 	}
@@ -499,13 +670,33 @@ gst_libcamera_src_negotiate(GstLibcamera
 	for (gsize i = 0; i < state->srcpads_.size(); i++) {
 		GstPad *srcpad = state->srcpads_[i];
 		const StreamConfiguration &stream_cfg = state->config_->at(i);
+		GstBufferPool *video_pool = nullptr;
+		GstVideoInfo info;
+
+		g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
+
+		gst_video_info_from_caps(&info, caps);
+		gst_libcamera_pad_set_video_info(srcpad, &info);
+
+		/* Stride mismatch between camera stride and that calculated by video-info. */
+		if (static_cast<unsigned int>(info.stride[0]) != stream_cfg.stride &&
+		    GST_VIDEO_INFO_FORMAT(&info) != GST_VIDEO_FORMAT_ENCODED) {
+			gst_libcamera_extrapolate_info(&info, stream_cfg.stride);
+
+			std::tie(video_pool, ret) =
+				gst_libcamera_create_video_pool(self, srcpad,
+								caps, &info);
+			if (ret)
+				return false;
+		}
 
 		GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
-								stream_cfg.stream());
+								stream_cfg.stream(), &info);
 		g_signal_connect_swapped(pool, "buffer-notify",
 					 G_CALLBACK(gst_task_resume), self->task);
 
 		gst_libcamera_pad_set_pool(srcpad, pool);
+		gst_libcamera_pad_set_video_pool(srcpad, video_pool);
 
 		/* Clear all reconfigure flags. */
 		gst_pad_check_reconfigure(srcpad);
@@ -548,7 +739,8 @@ gst_libcamera_src_task_run(gpointer user
 		if (gst_pad_check_reconfigure(srcpad)) {
 			/* Check if the caps even need changing. */
 			g_autoptr(GstCaps) caps = gst_pad_get_current_caps(srcpad);
-			if (!gst_pad_peer_query_accept_caps(srcpad, caps)) {
+			g_autoptr(GstCaps) peercaps = gst_pad_peer_query_caps(srcpad, caps);
+			if (gst_caps_is_empty(peercaps)) {
 				reconfigure = true;
 				break;
 			}
@@ -699,8 +891,10 @@ gst_libcamera_src_task_leave([[maybe_unu
 
 	{
 		GLibRecLocker locker(&self->stream_lock);
-		for (GstPad *srcpad : state->srcpads_)
+		for (GstPad *srcpad : state->srcpads_) {
+			gst_libcamera_pad_set_latency(srcpad, GST_CLOCK_TIME_NONE);
 			gst_libcamera_pad_set_pool(srcpad, nullptr);
+		}
 	}
 
 	g_clear_object(&self->allocator);
@@ -884,7 +1078,7 @@ gst_libcamera_src_request_new_pad(GstEle
 				  const gchar *name, [[maybe_unused]] const GstCaps *caps)
 {
 	GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element);
-	g_autoptr(GstPad) pad = NULL;
+	g_autoptr(GstPad) pad = nullptr;
 
 	GST_DEBUG_OBJECT(self, "new request pad created");
 
@@ -898,12 +1092,12 @@ gst_libcamera_src_request_new_pad(GstEle
 		GST_ELEMENT_ERROR(element, STREAM, FAILED,
 				  ("Internal data stream error."),
 				  ("Could not add pad to element"));
-		return NULL;
+		return nullptr;
 	}
 
 	gst_child_proxy_child_added(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
 
-	return reinterpret_cast<GstPad *>(g_steal_pointer(&pad));
+	return std::exchange(pad, nullptr);
 }
 
 static void
@@ -922,6 +1116,12 @@ gst_libcamera_src_release_pad(GstElement
 		auto end_iterator = pads.end();
 		auto pad_iterator = std::find(begin_iterator, end_iterator, pad);
 
+		GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(pad);
+		if (video_pool) {
+			gst_buffer_pool_set_active(video_pool, false);
+			gst_object_unref(video_pool);
+		}
+
 		if (pad_iterator != end_iterator) {
 			g_object_unref(*pad_iterator);
 			pads.erase(pad_iterator);
diff -pruN 0.5.0-1/src/gstreamer/meson.build 0.5.2-2/src/gstreamer/meson.build
--- 0.5.0-1/src/gstreamer/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/gstreamer/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -33,6 +33,7 @@ libcamera_gst_sources += custom_target('
                                        output : 'gstlibcamera-controls.cpp',
                                        command : [gen_gst_controls, '-o', '@OUTPUT@',
                                                   '-t', gen_gst_controls_template, '@INPUT@'],
+                                       depend_files : [py_mod_controls],
                                        env : py_build_env)
 
 libcamera_gst_cpp_args = [
diff -pruN 0.5.0-1/src/ipa/libipa/agc_mean_luminance.cpp 0.5.2-2/src/ipa/libipa/agc_mean_luminance.cpp
--- 0.5.0-1/src/ipa/libipa/agc_mean_luminance.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/agc_mean_luminance.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -44,6 +44,15 @@ static constexpr uint32_t kNumStartupFra
  */
 static constexpr double kDefaultRelativeLuminanceTarget = 0.16;
 
+/*
+ * Maximum relative luminance target
+ *
+ * This value limits the relative luminance target after applying the exposure
+ * compensation. Targeting a value above this limit results in saturation
+ * and the inability to regulate properly.
+ */
+static constexpr double kMaxRelativeLuminanceTarget = 0.95;
+
 /**
  * \struct AgcMeanLuminance::AgcConstraint
  * \brief The boundaries and target for an AeConstraintMode constraint
@@ -134,7 +143,8 @@ static constexpr double kDefaultRelative
  */
 
 AgcMeanLuminance::AgcMeanLuminance()
-	: frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0)
+	: exposureCompensation_(1.0), frameCount_(0), filteredExposure_(0s),
+	  relativeLuminanceTarget_(0)
 {
 }
 
@@ -218,8 +228,7 @@ int AgcMeanLuminance::parseConstraintMod
 		constraintModes_[controls::ConstraintNormal].insert(
 			constraintModes_[controls::ConstraintNormal].begin(),
 			constraint);
-		availableConstraintModes.push_back(
-			AeConstraintModeNameValueMap.at("ConstraintNormal"));
+		availableConstraintModes.push_back(controls::ConstraintNormal);
 	}
 
 	controls_[&controls::AeConstraintMode] = ControlInfo(availableConstraintModes);
@@ -287,7 +296,7 @@ int AgcMeanLuminance::parseExposureModes
 	 * possible before touching gain.
 	 */
 	if (availableExposureModes.empty()) {
-		int32_t exposureModeId = AeExposureModeNameValueMap.at("ExposureNormal");
+		int32_t exposureModeId = controls::ExposureNormal;
 		std::vector<std::pair<utils::Duration, double>> stages = { };
 
 		std::shared_ptr<ExposureModeHelper> helper =
@@ -370,6 +379,15 @@ int AgcMeanLuminance::parseTuningData(co
 }
 
 /**
+ * \fn AgcMeanLuminance::setExposureCompensation()
+ * \brief Set the exposure compensation value
+ * \param[in] gain The exposure compensation gain
+ *
+ * This function sets the exposure compensation value to be used in the
+ * AGC calculations. It is expressed as gain instead of EV.
+ */
+
+/**
  * \brief Set the ExposureModeHelper limits for this class
  * \param[in] minExposureTime Minimum exposure time to allow
  * \param[in] maxExposureTime Maximum ewposure time to allow
@@ -425,7 +443,8 @@ void AgcMeanLuminance::setLimits(utils::
  */
 double AgcMeanLuminance::estimateInitialGain() const
 {
-	double yTarget = relativeLuminanceTarget_;
+	double yTarget = std::min(relativeLuminanceTarget_ * exposureCompensation_,
+				  kMaxRelativeLuminanceTarget);
 	double yGain = 1.0;
 
 	/*
@@ -468,12 +487,20 @@ double AgcMeanLuminance::constraintClamp
 				 hist.interQuantileMean(constraint.qLo, constraint.qHi);
 
 		if (constraint.bound == AgcConstraint::Bound::Lower &&
-		    newGain > gain)
+		    newGain > gain) {
 			gain = newGain;
+			LOG(AgcMeanLuminance, Debug)
+				<< "Apply lower bound: " << gain << " to "
+				<< newGain;
+		}
 
 		if (constraint.bound == AgcConstraint::Bound::Upper &&
-		    newGain < gain)
+		    newGain < gain) {
 			gain = newGain;
+			LOG(AgcMeanLuminance, Debug)
+				<< "Apply upper bound: " << gain << " to "
+				<< newGain;
+		}
 	}
 
 	return gain;
diff -pruN 0.5.0-1/src/ipa/libipa/agc_mean_luminance.h 0.5.2-2/src/ipa/libipa/agc_mean_luminance.h
--- 0.5.0-1/src/ipa/libipa/agc_mean_luminance.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/agc_mean_luminance.h	2025-08-07 13:46:17.000000000 +0000
@@ -44,6 +44,11 @@ public:
 
 	int parseTuningData(const YamlObject &tuningData);
 
+	void setExposureCompensation(double gain)
+	{
+		exposureCompensation_ = gain;
+	}
+
 	void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime,
 		       double minGain, double maxGain);
 
@@ -84,6 +89,7 @@ private:
 				   double gain);
 	utils::Duration filterExposure(utils::Duration exposureValue);
 
+	double exposureCompensation_;
 	uint64_t frameCount_;
 	utils::Duration filteredExposure_;
 	double relativeLuminanceTarget_;
diff -pruN 0.5.0-1/src/ipa/libipa/awb.cpp 0.5.2-2/src/ipa/libipa/awb.cpp
--- 0.5.0-1/src/ipa/libipa/awb.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -114,7 +114,7 @@ namespace ipa {
  * does not take any statistics into account. It is used to compute the colour
  * gains when the user manually specifies a colour temperature.
  *
- * \return The colour gains
+ * \return The colour gains or std::nullopt if the conversion is not possible
  */
 
 /**
diff -pruN 0.5.0-1/src/ipa/libipa/awb.h 0.5.2-2/src/ipa/libipa/awb.h
--- 0.5.0-1/src/ipa/libipa/awb.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb.h	2025-08-07 13:46:17.000000000 +0000
@@ -8,6 +8,7 @@
 #pragma once
 
 #include <map>
+#include <optional>
 
 #include <libcamera/control_ids.h>
 #include <libcamera/controls.h>
@@ -39,7 +40,7 @@ public:
 
 	virtual int init(const YamlObject &tuningData) = 0;
 	virtual AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) = 0;
-	virtual RGB<double> gainsFromColourTemperature(double colourTemperature) = 0;
+	virtual std::optional<RGB<double>> gainsFromColourTemperature(double colourTemperature) = 0;
 
 	const ControlInfoMap::Map &controls() const
 	{
diff -pruN 0.5.0-1/src/ipa/libipa/awb_bayes.cpp 0.5.2-2/src/ipa/libipa/awb_bayes.cpp
--- 0.5.0-1/src/ipa/libipa/awb_bayes.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb_bayes.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -270,7 +270,7 @@ void AwbBayes::handleControls(const Cont
 	}
 }
 
-RGB<double> AwbBayes::gainsFromColourTemperature(double colourTemperature)
+std::optional<RGB<double>> AwbBayes::gainsFromColourTemperature(double colourTemperature)
 {
 	/*
 	 * \todo In the RaspberryPi code, the ct curve was interpolated in
@@ -278,7 +278,7 @@ RGB<double> AwbBayes::gainsFromColourTem
 	 * intuitive, as the gains are in linear space. But I can't prove it.
 	 */
 	const auto &gains = colourGainCurve_.getInterpolated(colourTemperature);
-	return { { gains[0], 1.0, gains[1] } };
+	return RGB<double>{ { gains[0], 1.0, gains[1] } };
 }
 
 AwbResult AwbBayes::calculateAwb(const AwbStats &stats, unsigned int lux)
diff -pruN 0.5.0-1/src/ipa/libipa/awb_bayes.h 0.5.2-2/src/ipa/libipa/awb_bayes.h
--- 0.5.0-1/src/ipa/libipa/awb_bayes.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb_bayes.h	2025-08-07 13:46:17.000000000 +0000
@@ -27,7 +27,7 @@ public:
 
 	int init(const YamlObject &tuningData) override;
 	AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override;
-	RGB<double> gainsFromColourTemperature(double temperatureK) override;
+	std::optional<RGB<double>> gainsFromColourTemperature(double temperatureK) override;
 	void handleControls(const ControlList &controls) override;
 
 private:
diff -pruN 0.5.0-1/src/ipa/libipa/awb_grey.cpp 0.5.2-2/src/ipa/libipa/awb_grey.cpp
--- 0.5.0-1/src/ipa/libipa/awb_grey.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb_grey.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -98,15 +98,15 @@ AwbResult AwbGrey::calculateAwb(const Aw
  * \return The colour gains if a colour temperature curve is available,
  * [1, 1, 1] otherwise.
  */
-RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)
+std::optional<RGB<double>> AwbGrey::gainsFromColourTemperature(double colourTemperature)
 {
 	if (!colourGainCurve_) {
 		LOG(Awb, Error) << "No gains defined";
-		return RGB<double>({ 1.0, 1.0, 1.0 });
+		return std::nullopt;
 	}
 
 	auto gains = colourGainCurve_->getInterpolated(colourTemperature);
-	return { { gains[0], 1.0, gains[1] } };
+	return RGB<double>{ { gains[0], 1.0, gains[1] } };
 }
 
 } /* namespace ipa */
diff -pruN 0.5.0-1/src/ipa/libipa/awb_grey.h 0.5.2-2/src/ipa/libipa/awb_grey.h
--- 0.5.0-1/src/ipa/libipa/awb_grey.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/awb_grey.h	2025-08-07 13:46:17.000000000 +0000
@@ -25,7 +25,7 @@ public:
 
 	int init(const YamlObject &tuningData) override;
 	AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override;
-	RGB<double> gainsFromColourTemperature(double colourTemperature) override;
+	std::optional<RGB<double>> gainsFromColourTemperature(double colourTemperature) override;
 
 private:
 	std::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;
diff -pruN 0.5.0-1/src/ipa/libipa/camera_sensor_helper.cpp 0.5.2-2/src/ipa/libipa/camera_sensor_helper.cpp
--- 0.5.0-1/src/ipa/libipa/camera_sensor_helper.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/camera_sensor_helper.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -745,6 +745,18 @@ public:
 };
 REGISTER_CAMERA_SENSOR_HELPER("ov13858", CameraSensorHelperOv13858)
 
+class CameraSensorHelperVd56g3 : public CameraSensorHelper
+{
+public:
+	CameraSensorHelperVd56g3()
+	{
+		/* From datasheet: 0x40 at 10bits. */
+		blackLevel_ = 4096;
+		gain_ = AnalogueGainLinear{ 0, 32, -1, 32 };
+	}
+};
+REGISTER_CAMERA_SENSOR_HELPER("vd56g3", CameraSensorHelperVd56g3)
+
 #endif /* __DOXYGEN__ */
 
 } /* namespace ipa */
diff -pruN 0.5.0-1/src/ipa/libipa/pwl.cpp 0.5.2-2/src/ipa/libipa/pwl.cpp
--- 0.5.0-1/src/ipa/libipa/pwl.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/libipa/pwl.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -35,14 +35,13 @@ namespace ipa {
  *
  * https://en.wikipedia.org/wiki/Piecewise_linear_function
  *
- * A consequence of the Pwl class being defined by knots instead of linear
- * functions is that the values of the piecewise linear function past the ends
- * of the function are constants as opposed to linear functions. In a
- * mathematical piecewise linear function that is defined by multiple linear
- * functions, the ends of the function are also linear functions and hence grow
- * to infinity (or negative infinity). However, since this Pwl class is defined
- * by knots, the y-value of the leftmost and rightmost knots will hold for all
- * x values to negative infinity and positive infinity, respectively.
+ * Outside the domain of the piecewise linear function the closest segment is
+ * extrapolated linearly. If one wants to ensure that the returned values stay
+ * within the range of the pwl, the input can be clamped:
+ *
+ * \code{.cpp}
+ * pwl.eval(pwl.domain().clip(x))
+ * \endcode
  */
 
 /**
@@ -211,6 +210,10 @@ double Pwl::eval(double x, int *span, bo
 					: points_.size() / 2 - 1);
 	if (span && updateSpan)
 		*span = index;
+
+	if (points_.size() == 1)
+		return points_[0].y();
+
 	return points_[index].y() +
 	       (x - points_[index].x()) * (points_[index + 1].y() - points_[index].y()) /
 		       (points_[index + 1].x() - points_[index].x());
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/agc.cpp 0.5.2-2/src/ipa/mali-c55/algorithms/agc.cpp
--- 0.5.0-1/src/ipa/mali-c55/algorithms/agc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/agc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board Oy
  *
- * agc.cpp - AGC/AEC mean-based control algorithm
+ * AGC/AEC mean-based control algorithm
  */
 
 #include "agc.h"
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/agc.h 0.5.2-2/src/ipa/mali-c55/algorithms/agc.h
--- 0.5.0-1/src/ipa/mali-c55/algorithms/agc.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/agc.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2023, Ideas on Board Oy
  *
- * agc.h - Mali C55 AGC/AEC mean-based control algorithm
+ * Mali C55 AGC/AEC mean-based control algorithm
  */
 
 #pragma once
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/algorithm.h 0.5.2-2/src/ipa/mali-c55/algorithms/algorithm.h
--- 0.5.0-1/src/ipa/mali-c55/algorithms/algorithm.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/algorithm.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * algorithm.h - Mali-C55 control algorithm interface
+ * Mali-C55 control algorithm interface
  */
 
 #pragma once
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/awb.cpp 0.5.2-2/src/ipa/mali-c55/algorithms/awb.cpp
--- 0.5.0-1/src/ipa/mali-c55/algorithms/awb.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/awb.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board Oy
  *
- * awb.cpp - Mali C55 grey world auto white balance algorithm
+ * Mali C55 grey world auto white balance algorithm
  */
 
 #include "awb.h"
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/awb.h 0.5.2-2/src/ipa/mali-c55/algorithms/awb.h
--- 0.5.0-1/src/ipa/mali-c55/algorithms/awb.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/awb.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas on Board Oy
  *
- * awb.h - Mali C55 grey world auto white balance algorithm
+ * Mali C55 grey world auto white balance algorithm
  */
 
 #include "algorithm.h"
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/lsc.cpp 0.5.2-2/src/ipa/mali-c55/algorithms/lsc.cpp
--- 0.5.0-1/src/ipa/mali-c55/algorithms/lsc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/lsc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board Oy
  *
- * lsc.cpp - Mali-C55 Lens shading correction algorithm
+ * Mali-C55 Lens shading correction algorithm
  */
 
 #include "lsc.h"
diff -pruN 0.5.0-1/src/ipa/mali-c55/algorithms/lsc.h 0.5.2-2/src/ipa/mali-c55/algorithms/lsc.h
--- 0.5.0-1/src/ipa/mali-c55/algorithms/lsc.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/algorithms/lsc.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board Oy
  *
- * lsc.h - Mali-C55 Lens shading correction algorithm
+ * Mali-C55 Lens shading correction algorithm
  */
 
 #include <map>
diff -pruN 0.5.0-1/src/ipa/mali-c55/ipa_context.cpp 0.5.2-2/src/ipa/mali-c55/ipa_context.cpp
--- 0.5.0-1/src/ipa/mali-c55/ipa_context.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/ipa_context.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * ipa_context.cpp - MaliC55 IPA Context
+ * Mali-C55 IPA Context
  */
 
 #include "ipa_context.h"
diff -pruN 0.5.0-1/src/ipa/mali-c55/ipa_context.h 0.5.2-2/src/ipa/mali-c55/ipa_context.h
--- 0.5.0-1/src/ipa/mali-c55/ipa_context.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/ipa_context.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * ipa_context.h - Mali-C55 IPA Context
+ * Mali-C55 IPA Context
  */
 
 #pragma once
diff -pruN 0.5.0-1/src/ipa/mali-c55/mali-c55.cpp 0.5.2-2/src/ipa/mali-c55/mali-c55.cpp
--- 0.5.0-1/src/ipa/mali-c55/mali-c55.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/mali-c55.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2023, Ideas on Board Oy
  *
- * mali-c55.cpp - Mali-C55 ISP image processing algorithms
+ * Mali-C55 ISP image processing algorithms
  */
 
 #include <map>
@@ -346,7 +346,8 @@ void IPAMaliC55::fillParams(unsigned int
 		ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE);
 	}
 
-	paramsComputed.emit(request);
+	size_t bytesused = offsetof(struct mali_c55_params_buffer, data) + params->total_size;
+	paramsComputed.emit(request, bytesused);
 }
 
 void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,
diff -pruN 0.5.0-1/src/ipa/mali-c55/module.h 0.5.2-2/src/ipa/mali-c55/module.h
--- 0.5.0-1/src/ipa/mali-c55/module.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/mali-c55/module.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * module.h - Mali-C55 IPA Module
+ * Mali-C55 IPA Module
  */
 
 #pragma once
diff -pruN 0.5.0-1/src/ipa/meson.build 0.5.2-2/src/ipa/meson.build
--- 0.5.0-1/src/ipa/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -4,7 +4,7 @@ ipa_includes = [
     libcamera_includes,
 ]
 
-ipa_install_dir = libcamera_libdir
+ipa_install_dir = libcamera_libdir / 'ipa'
 ipa_data_dir = libcamera_datadir / 'ipa'
 ipa_sysconf_dir = libcamera_sysconfdir / 'ipa'
 
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/agc.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/agc.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/agc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/agc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -68,10 +68,9 @@ int Agc::parseMeteringModes(IPAContext &
 	if (meteringModes_.empty()) {
 		LOG(RkISP1Agc, Warning)
 			<< "No metering modes read from tuning file; defaulting to matrix";
-		int32_t meteringModeId = controls::AeMeteringModeNameValueMap.at("MeteringMatrix");
 		std::vector<uint8_t> weights(context.hw->numHistogramWeights, 1);
 
-		meteringModes_[meteringModeId] = weights;
+		meteringModes_[controls::MeteringMatrix] = weights;
 	}
 
 	std::vector<ControlValue> meteringModes;
@@ -158,6 +157,7 @@ int Agc::init(IPAContext &context, const
 			    ControlValue(controls::AnalogueGainModeAuto));
 	/* \todo Move this to the Camera class */
 	context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true, true);
+	context.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);
 	context.ctrlMap.merge(controls());
 
 	return 0;
@@ -180,6 +180,7 @@ int Agc::configure(IPAContext &context,
 	context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure;
 	context.activeState.agc.autoExposureEnabled = !context.configuration.raw;
 	context.activeState.agc.autoGainEnabled = !context.configuration.raw;
+	context.activeState.agc.exposureValue = 0.0;
 
 	context.activeState.agc.constraintMode =
 		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
@@ -302,6 +303,11 @@ void Agc::queueRequest(IPAContext &conte
 			static_cast<controls::AeConstraintModeEnum>(*constraintMode);
 	frameContext.agc.constraintMode = agc.constraintMode;
 
+	const auto &exposureValue = controls.get(controls::ExposureValue);
+	if (exposureValue)
+		agc.exposureValue = *exposureValue;
+	frameContext.agc.exposureValue = agc.exposureValue;
+
 	const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);
 	if (frameDurationLimits) {
 		/* Limit the control value to the limits in ControlInfo */
@@ -408,6 +414,7 @@ void Agc::fillMetadata(IPAContext &conte
 	metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode);
 	metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode);
 	metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode);
+	metadata.set(controls::ExposureValue, frameContext.agc.exposureValue);
 }
 
 /**
@@ -557,6 +564,8 @@ void Agc::process(IPAContext &context, [
 	double analogueGain = frameContext.sensor.gain;
 	utils::Duration effectiveExposureValue = exposureTime * analogueGain;
 
+	setExposureCompensation(pow(2.0, frameContext.agc.exposureValue));
+
 	utils::Duration newExposureTime;
 	double aGain, dGain;
 	std::tie(newExposureTime, aGain, dGain) =
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/awb.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/awb.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/awb.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/awb.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -90,6 +90,8 @@ int Awb::init(IPAContext &context, const
 	cmap[&controls::ColourTemperature] = ControlInfo(kMinColourTemperature,
 							 kMaxColourTemperature,
 							 kDefaultColourTemperature);
+	cmap[&controls::AwbEnable] = ControlInfo(false, true);
+	cmap[&controls::ColourGains] = ControlInfo(0.0f, 3.996f, 1.0f);
 
 	if (!tuningData.contains("algorithm"))
 		LOG(RkISP1Awb, Info) << "No AWB algorithm specified."
@@ -124,11 +126,16 @@ int Awb::init(IPAContext &context, const
 int Awb::configure(IPAContext &context,
 		   const IPACameraSensorInfo &configInfo)
 {
-	context.activeState.awb.gains.manual = RGB<double>{ 1.0 };
-	context.activeState.awb.gains.automatic =
-		awbAlgo_->gainsFromColourTemperature(kDefaultColourTemperature);
+	context.activeState.awb.manual.gains = RGB<double>{ 1.0 };
+	auto gains = awbAlgo_->gainsFromColourTemperature(kDefaultColourTemperature);
+	if (gains)
+		context.activeState.awb.automatic.gains = *gains;
+	else
+		context.activeState.awb.automatic.gains = RGB<double>{ 1.0 };
+
 	context.activeState.awb.autoEnabled = true;
-	context.activeState.awb.temperatureK = kDefaultColourTemperature;
+	context.activeState.awb.manual.temperatureK = kDefaultColourTemperature;
+	context.activeState.awb.automatic.temperatureK = kDefaultColourTemperature;
 
 	/*
 	 * Define the measurement window for AWB as a centered rectangle
@@ -173,8 +180,8 @@ void Awb::queueRequest(IPAContext &conte
 	const auto &colourTemperature = controls.get(controls::ColourTemperature);
 	bool update = false;
 	if (colourGains) {
-		awb.gains.manual.r() = (*colourGains)[0];
-		awb.gains.manual.b() = (*colourGains)[1];
+		awb.manual.gains.r() = (*colourGains)[0];
+		awb.manual.gains.b() = (*colourGains)[1];
 		/*
 		 * \todo Colour temperature reported in metadata is now
 		 * incorrect, as we can't deduce the temperature from the gains.
@@ -182,19 +189,21 @@ void Awb::queueRequest(IPAContext &conte
 		 */
 		update = true;
 	} else if (colourTemperature) {
+		awb.manual.temperatureK = *colourTemperature;
 		const auto &gains = awbAlgo_->gainsFromColourTemperature(*colourTemperature);
-		awb.gains.manual.r() = gains.r();
-		awb.gains.manual.b() = gains.b();
-		awb.temperatureK = *colourTemperature;
-		update = true;
+		if (gains) {
+			awb.manual.gains.r() = gains->r();
+			awb.manual.gains.b() = gains->b();
+			update = true;
+		}
 	}
 
 	if (update)
 		LOG(RkISP1Awb, Debug)
-			<< "Set colour gains to " << awb.gains.manual;
+			<< "Set colour gains to " << awb.manual.gains;
 
-	frameContext.awb.gains = awb.gains.manual;
-	frameContext.awb.temperatureK = awb.temperatureK;
+	frameContext.awb.gains = awb.manual.gains;
+	frameContext.awb.temperatureK = awb.manual.temperatureK;
 }
 
 /**
@@ -208,8 +217,9 @@ void Awb::prepare(IPAContext &context, c
 	 * most up-to-date automatic values we can read.
 	 */
 	if (frameContext.awb.autoEnabled) {
-		frameContext.awb.gains = context.activeState.awb.gains.automatic;
-		frameContext.awb.temperatureK = context.activeState.awb.temperatureK;
+		const auto &awb = context.activeState.awb;
+		frameContext.awb.gains = awb.automatic.gains;
+		frameContext.awb.temperatureK = awb.automatic.temperatureK;
 	}
 
 	auto gainConfig = params->block<BlockType::AwbGain>();
@@ -296,6 +306,11 @@ void Awb::process(IPAContext &context,
 	const rkisp1_cif_isp_stat *params = &stats->params;
 	const rkisp1_cif_isp_awb_stat *awb = &params->awb;
 
+	if (awb->awb_mean[0].cnt == 0) {
+		LOG(RkISP1Awb, Debug) << "AWB statistics are empty";
+		return;
+	}
+
 	RGB<double> rgbMeans = calculateRgbMeans(frameContext, awb);
 
 	/*
@@ -309,11 +324,6 @@ void Awb::process(IPAContext &context,
 	RkISP1AwbStats awbStats{ rgbMeans };
 	AwbResult awbResult = awbAlgo_->calculateAwb(awbStats, frameContext.lux.lux);
 
-	activeState.awb.temperatureK = awbResult.colourTemperature;
-
-	/* Metadata shall contain the up to date measurement */
-	metadata.set(controls::ColourTemperature, activeState.awb.temperatureK);
-
 	/*
 	 * Clamp the gain values to the hardware, which expresses gains as Q2.8
 	 * unsigned integer values. Set the minimum just above zero to avoid
@@ -324,16 +334,19 @@ void Awb::process(IPAContext &context,
 
 	/* Filter the values to avoid oscillations. */
 	double speed = 0.2;
+	double ct = awbResult.colourTemperature;
+	ct = ct * speed + activeState.awb.automatic.temperatureK * (1 - speed);
 	awbResult.gains = awbResult.gains * speed +
-			  activeState.awb.gains.automatic * (1 - speed);
+			  activeState.awb.automatic.gains * (1 - speed);
 
-	activeState.awb.gains.automatic = awbResult.gains;
+	activeState.awb.automatic.temperatureK = static_cast<unsigned int>(ct);
+	activeState.awb.automatic.gains = awbResult.gains;
 
 	LOG(RkISP1Awb, Debug)
 		<< std::showpoint
 		<< "Means " << rgbMeans << ", gains "
-		<< activeState.awb.gains.automatic << ", temp "
-		<< activeState.awb.temperatureK << "K";
+		<< activeState.awb.automatic.gains << ", temp "
+		<< activeState.awb.automatic.temperatureK << "K";
 }
 
 RGB<double> Awb::calculateRgbMeans(const IPAFrameContext &frameContext, const rkisp1_cif_isp_awb_stat *awb) const
@@ -392,11 +405,17 @@ RGB<double> Awb::calculateRgbMeans(const
 	}
 
 	/*
+	 * The ISP computes the AWB means after applying the CCM. Apply the
+	 * inverse as we want to get the raw means before the colour gains.
+	 */
+	rgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;
+
+	/*
 	 * The ISP computes the AWB means after applying the colour gains,
 	 * divide by the gains that were used to get the raw means from the
-	 * sensor.
+	 * sensor. Apply a minimum value to avoid divisions by near-zero.
 	 */
-	rgbMeans /= frameContext.awb.gains;
+	rgbMeans /= frameContext.awb.gains.max(0.01);
 
 	return rgbMeans;
 }
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/awb.h 0.5.2-2/src/ipa/rkisp1/algorithms/awb.h
--- 0.5.0-1/src/ipa/rkisp1/algorithms/awb.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/awb.h	2025-08-07 13:46:17.000000000 +0000
@@ -7,8 +7,6 @@
 
 #pragma once
 
-#include <optional>
-
 #include "libcamera/internal/vector.h"
 
 #include "libipa/awb.h"
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/ccm.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/ccm.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/ccm.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/ccm.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -36,17 +36,25 @@ namespace ipa::rkisp1::algorithms {
 
 LOG_DEFINE_CATEGORY(RkISP1Ccm)
 
+constexpr Matrix<float, 3, 3> kIdentity3x3 = Matrix<float, 3, 3>::identity();
+
 /**
  * \copydoc libcamera::ipa::Algorithm::init
  */
 int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
 {
+	auto &cmap = context.ctrlMap;
+	cmap[&controls::ColourCorrectionMatrix] = ControlInfo(
+		ControlValue(-8.0f),
+		ControlValue(7.993f),
+		ControlValue(kIdentity3x3.data()));
+
 	int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
 	if (ret < 0) {
 		LOG(RkISP1Ccm, Warning)
 			<< "Failed to parse 'ccm' "
 			<< "parameter from tuning file; falling back to unit matrix";
-		ccm_.setData({ { 0, Matrix<float, 3, 3>::identity() } });
+		ccm_.setData({ { 0, kIdentity3x3 } });
 	}
 
 	ret = offsets_.readYaml(tuningData["ccms"], "ct", "offsets");
@@ -61,13 +69,51 @@ int Ccm::init([[maybe_unused]] IPAContex
 	return 0;
 }
 
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int Ccm::configure(IPAContext &context,
+		   [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+	auto &as = context.activeState;
+	as.ccm.manual = kIdentity3x3;
+	as.ccm.automatic = ccm_.getInterpolated(as.awb.automatic.temperatureK);
+	return 0;
+}
+
+void Ccm::queueRequest(IPAContext &context,
+		       [[maybe_unused]] const uint32_t frame,
+		       IPAFrameContext &frameContext,
+		       const ControlList &controls)
+{
+	/* Nothing to do here, the ccm will be calculated in prepare() */
+	if (frameContext.awb.autoEnabled)
+		return;
+
+	auto &ccm = context.activeState.ccm;
+
+	const auto &colourTemperature = controls.get(controls::ColourTemperature);
+	const auto &ccmMatrix = controls.get(controls::ColourCorrectionMatrix);
+	if (ccmMatrix) {
+		ccm.manual = Matrix<float, 3, 3>(*ccmMatrix);
+		LOG(RkISP1Ccm, Debug)
+			<< "Setting manual CCM from CCM control to " << ccm.manual;
+	} else if (colourTemperature) {
+		ccm.manual = ccm_.getInterpolated(*colourTemperature);
+		LOG(RkISP1Ccm, Debug)
+			<< "Setting manual CCM from CT control to " << ccm.manual;
+	}
+
+	frameContext.ccm.ccm = ccm.manual;
+}
+
 void Ccm::setParameters(struct rkisp1_cif_isp_ctk_config &config,
 			const Matrix<float, 3, 3> &matrix,
 			const Matrix<int16_t, 3, 1> &offsets)
 {
 	/*
 	 * 4 bit integer and 7 bit fractional, ranging from -8 (0x400) to
-	 * +7.992 (0x3ff)
+	 * +7.9921875 (0x3ff)
 	 */
 	for (unsigned int i = 0; i < 3; i++) {
 		for (unsigned int j = 0; j < 3; j++)
@@ -88,14 +134,16 @@ void Ccm::setParameters(struct rkisp1_ci
 void Ccm::prepare(IPAContext &context, const uint32_t frame,
 		  IPAFrameContext &frameContext, RkISP1Params *params)
 {
-	uint32_t ct = context.activeState.awb.temperatureK;
+	if (!frameContext.awb.autoEnabled) {
+		auto config = params->block<BlockType::Ctk>();
+		config.setEnabled(true);
+		setParameters(*config, frameContext.ccm.ccm, Matrix<int16_t, 3, 1>());
+		return;
+	}
 
-	/*
-	 * \todo The colour temperature will likely be noisy, add filtering to
-	 * avoid updating the CCM matrix all the time.
-	 */
+	uint32_t ct = frameContext.awb.temperatureK;
 	if (frame > 0 && ct == ct_) {
-		frameContext.ccm.ccm = context.activeState.ccm.ccm;
+		frameContext.ccm.ccm = context.activeState.ccm.automatic;
 		return;
 	}
 
@@ -103,7 +151,7 @@ void Ccm::prepare(IPAContext &context, c
 	Matrix<float, 3, 3> ccm = ccm_.getInterpolated(ct);
 	Matrix<int16_t, 3, 1> offsets = offsets_.getInterpolated(ct);
 
-	context.activeState.ccm.ccm = ccm;
+	context.activeState.ccm.automatic = ccm;
 	frameContext.ccm.ccm = ccm;
 
 	auto config = params->block<BlockType::Ctk>();
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/ccm.h 0.5.2-2/src/ipa/rkisp1/algorithms/ccm.h
--- 0.5.0-1/src/ipa/rkisp1/algorithms/ccm.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/ccm.h	2025-08-07 13:46:17.000000000 +0000
@@ -26,6 +26,12 @@ public:
 	~Ccm() = default;
 
 	int init(IPAContext &context, const YamlObject &tuningData) override;
+	int configure(IPAContext &context,
+		      const IPACameraSensorInfo &configInfo) override;
+	void queueRequest(IPAContext &context,
+			  const uint32_t frame,
+			  IPAFrameContext &frameContext,
+			  const ControlList &controls) override;
 	void prepare(IPAContext &context, const uint32_t frame,
 		     IPAFrameContext &frameContext,
 		     RkISP1Params *params) override;
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/filter.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/filter.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/filter.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/filter.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -40,6 +40,17 @@ static constexpr uint32_t kFiltLumWeight
 static constexpr uint32_t kFiltModeDefault = 0x000004f2;
 
 /**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Filter::init(IPAContext &context,
+		 [[maybe_unused]] const YamlObject &tuningData)
+{
+	auto &cmap = context.ctrlMap;
+	cmap[&controls::Sharpness] = ControlInfo(0.0f, 10.0f, 1.0f);
+
+	return 0;
+}
+/**
  * \copydoc libcamera::ipa::Algorithm::queueRequest
  */
 void Filter::queueRequest(IPAContext &context,
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/filter.h 0.5.2-2/src/ipa/rkisp1/algorithms/filter.h
--- 0.5.0-1/src/ipa/rkisp1/algorithms/filter.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/filter.h	2025-08-07 13:46:17.000000000 +0000
@@ -21,6 +21,7 @@ public:
 	Filter() = default;
 	~Filter() = default;
 
+	int init(IPAContext &context, const YamlObject &tuningData) override;
 	void queueRequest(IPAContext &context, const uint32_t frame,
 			  IPAFrameContext &frameContext,
 			  const ControlList &controls) override;
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/lsc.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/lsc.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/lsc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/lsc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -404,12 +404,12 @@ void LensShadingCorrection::copyTable(rk
 /**
  * \copydoc libcamera::ipa::Algorithm::prepare
  */
-void LensShadingCorrection::prepare(IPAContext &context,
+void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,
 				    [[maybe_unused]] const uint32_t frame,
-				    [[maybe_unused]] IPAFrameContext &frameContext,
+				    IPAFrameContext &frameContext,
 				    RkISP1Params *params)
 {
-	uint32_t ct = context.activeState.awb.temperatureK;
+	uint32_t ct = frameContext.awb.temperatureK;
 	if (std::abs(static_cast<int>(ct) - static_cast<int>(lastAppliedCt_)) <
 	    kColourTemperatureChangeThreshhold)
 		return;
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/lux.cpp 0.5.2-2/src/ipa/rkisp1/algorithms/lux.cpp
--- 0.5.0-1/src/ipa/rkisp1/algorithms/lux.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/lux.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * lux.cpp - RkISP1 Lux control
+ * RkISP1 Lux control
  */
 
 #include "lux.h"
diff -pruN 0.5.0-1/src/ipa/rkisp1/algorithms/lux.h 0.5.2-2/src/ipa/rkisp1/algorithms/lux.h
--- 0.5.0-1/src/ipa/rkisp1/algorithms/lux.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/algorithms/lux.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas On Board
  *
- * lux.h - RkISP1 Lux control
+ * RkISP1 Lux control
  */
 
 #pragma once
diff -pruN 0.5.0-1/src/ipa/rkisp1/ipa_context.cpp 0.5.2-2/src/ipa/rkisp1/ipa_context.cpp
--- 0.5.0-1/src/ipa/rkisp1/ipa_context.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/ipa_context.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -191,23 +191,37 @@ namespace libcamera::ipa::rkisp1 {
  * \var IPAActiveState::awb
  * \brief State for the Automatic White Balance algorithm
  *
- * \struct IPAActiveState::awb.gains
+ * \struct IPAActiveState::awb::AwbState
+ * \brief Struct for the AWB regulation state
+ *
+ * \var IPAActiveState::awb::AwbState.gains
  * \brief White balance gains
  *
- * \var IPAActiveState::awb.gains.manual
- * \brief Manual white balance gains (set through requests)
+ * \var IPAActiveState::awb::AwbState.temperatureK
+ * \brief Color temperature
  *
- * \var IPAActiveState::awb.gains.automatic
- * \brief Automatic white balance gains (computed by the algorithm)
+ * \var IPAActiveState::awb.manual
+ * \brief Manual regulation state (set through requests)
  *
- * \var IPAActiveState::awb.temperatureK
- * \brief Estimated color temperature
+ * \var IPAActiveState::awb.automatic
+ * \brief Automatic regulation state (computed by the algorithm)
  *
  * \var IPAActiveState::awb.autoEnabled
  * \brief Whether the Auto White Balance algorithm is enabled
  */
 
 /**
+ * \var IPAActiveState::ccm
+ * \brief State for the Colour Correction Matrix algorithm
+ *
+ * \var IPAActiveState::ccm.manual
+ * \brief Manual CCM (set through requests)
+ *
+ * \var IPAActiveState::awb.automatic
+ * \brief Automatic CCM (computed by the algorithm)
+ */
+
+/**
  * \var IPAActiveState::cproc
  * \brief State for the Color Processing algorithm
  *
@@ -346,13 +360,24 @@ namespace libcamera::ipa::rkisp1 {
  * \brief White balance gains
  *
  * \var IPAFrameContext::awb.temperatureK
- * \brief Estimated color temperature
+ * \brief Color temperature used for processing this frame
+ *
+ * This does not match the color temperature estimated for this frame as the
+ * measurements were taken on a previous frame.
  *
  * \var IPAFrameContext::awb.autoEnabled
  * \brief Whether the Auto White Balance algorithm is enabled
  */
 
 /**
+ * \var IPAFrameContext::ccm
+ * \brief Colour Correction Matrix parameters for this frame
+ *
+ * \struct IPAFrameContext::ccm.ccm
+ * \brief Colour Correction Matrix
+ */
+
+/**
  * \var IPAFrameContext::cproc
  * \brief Color Processing parameters for this frame
  *
diff -pruN 0.5.0-1/src/ipa/rkisp1/ipa_context.h 0.5.2-2/src/ipa/rkisp1/ipa_context.h
--- 0.5.0-1/src/ipa/rkisp1/ipa_context.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/ipa_context.h	2025-08-07 13:46:17.000000000 +0000
@@ -81,6 +81,7 @@ struct IPAActiveState {
 
 		bool autoExposureEnabled;
 		bool autoGainEnabled;
+		double exposureValue;
 		controls::AeConstraintModeEnum constraintMode;
 		controls::AeExposureModeEnum exposureMode;
 		controls::AeMeteringModeEnum meteringMode;
@@ -89,17 +90,20 @@ struct IPAActiveState {
 	} agc;
 
 	struct {
-		struct {
-			RGB<double> manual;
-			RGB<double> automatic;
-		} gains;
+		struct AwbState {
+			RGB<double> gains;
+			unsigned int temperatureK;
+		};
+
+		AwbState manual;
+		AwbState automatic;
 
-		unsigned int temperatureK;
 		bool autoEnabled;
 	} awb;
 
 	struct {
-		Matrix<float, 3, 3> ccm;
+		Matrix<float, 3, 3> manual;
+		Matrix<float, 3, 3> automatic;
 	} ccm;
 
 	struct {
@@ -126,6 +130,7 @@ struct IPAFrameContext : public FrameCon
 	struct {
 		uint32_t exposure;
 		double gain;
+		double exposureValue;
 		uint32_t vblank;
 		bool autoExposureEnabled;
 		bool autoGainEnabled;
diff -pruN 0.5.0-1/src/ipa/rkisp1/rkisp1.cpp 0.5.2-2/src/ipa/rkisp1/rkisp1.cpp
--- 0.5.0-1/src/ipa/rkisp1/rkisp1.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rkisp1/rkisp1.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -115,10 +115,7 @@ const IPAHwSettings ipaHwSettingsV12{
 
 /* List of controls handled by the RkISP1 IPA */
 const ControlInfoMap::Map rkisp1Controls{
-	{ &controls::AwbEnable, ControlInfo(false, true) },
-	{ &controls::ColourGains, ControlInfo(0.0f, 3.996f, 1.0f) },
 	{ &controls::DebugMetadataEnable, ControlInfo(false, true, false) },
-	{ &controls::Sharpness, ControlInfo(0.0f, 10.0f, 1.0f) },
 	{ &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) },
 };
 
diff -pruN 0.5.0-1/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp 0.5.2-2/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp
--- 0.5.0-1/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Raspberry Pi Ltd
  *
- * cam_helper_Imx283.cpp - camera information for Imx283 sensor
+ * camera information for Imx283 sensor
  */
 
 #include <assert.h>
diff -pruN 0.5.0-1/src/ipa/rpi/cam_helper/cam_helper_vd56g3.cpp 0.5.2-2/src/ipa/rpi/cam_helper/cam_helper_vd56g3.cpp
--- 0.5.0-1/src/ipa/rpi/cam_helper/cam_helper_vd56g3.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/cam_helper/cam_helper_vd56g3.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) STMicroelectronics SA 2025
+ *
+ * Camera information for vd56g3 sensor
+ */
+
+#include <assert.h>
+
+#include "cam_helper.h"
+
+using namespace RPiController;
+
+class CamHelperVd56g3 : public CamHelper
+{
+public:
+	CamHelperVd56g3();
+	uint32_t gainCode(double gain) const override;
+	double gain(uint32_t gainCode) const override;
+
+private:
+	/*
+	 * Smallest difference between the frame length and integration time,
+	 * in units of lines.
+	 */
+	static constexpr int frameIntegrationDiff = 61;
+};
+
+CamHelperVd56g3::CamHelperVd56g3()
+	: CamHelper({}, frameIntegrationDiff)
+{
+}
+
+uint32_t CamHelperVd56g3::gainCode(double gain) const
+{
+	return static_cast<uint32_t>(32.0 - 32.0 / gain);
+}
+
+double CamHelperVd56g3::gain(uint32_t gainCode) const
+{
+	return static_cast<double>(32.0 / (32 - gainCode));
+}
+
+static CamHelper *create()
+{
+	return new CamHelperVd56g3();
+}
+
+static RegisterCamHelper reg("vd56g3", &create);
diff -pruN 0.5.0-1/src/ipa/rpi/cam_helper/meson.build 0.5.2-2/src/ipa/rpi/cam_helper/meson.build
--- 0.5.0-1/src/ipa/rpi/cam_helper/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/cam_helper/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -14,6 +14,7 @@ rpi_ipa_cam_helper_sources = files([
     'cam_helper_ov64a40.cpp',
     'cam_helper_ov7251.cpp',
     'cam_helper_ov9281.cpp',
+    'cam_helper_vd56g3.cpp',
     'md_parser_smia.cpp',
 ])
 
diff -pruN 0.5.0-1/src/ipa/rpi/common/ipa_base.cpp 0.5.2-2/src/ipa/rpi/common/ipa_base.cpp
--- 0.5.0-1/src/ipa/rpi/common/ipa_base.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/common/ipa_base.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -58,23 +58,24 @@ const ControlInfoMap::Map ipaControls{
 	/* \todo Move this to the Camera class */
 	{ &controls::AeEnable, ControlInfo(false, true, true) },
 	{ &controls::ExposureTimeMode,
-	  ControlInfo(static_cast<int32_t>(controls::ExposureTimeModeAuto),
-		      static_cast<int32_t>(controls::ExposureTimeModeManual),
-		      static_cast<int32_t>(controls::ExposureTimeModeAuto)) },
+	  ControlInfo({ { ControlValue(controls::ExposureTimeModeAuto),
+			  ControlValue(controls::ExposureTimeModeManual) } },
+		      ControlValue(controls::ExposureTimeModeAuto)) },
 	{ &controls::ExposureTime,
 	  ControlInfo(1, 66666, static_cast<int32_t>(defaultExposureTime.get<std::micro>())) },
 	{ &controls::AnalogueGainMode,
-	  ControlInfo(static_cast<int32_t>(controls::AnalogueGainModeAuto),
-		      static_cast<int32_t>(controls::AnalogueGainModeManual),
-		      static_cast<int32_t>(controls::AnalogueGainModeAuto)) },
+	  ControlInfo({ { ControlValue(controls::AnalogueGainModeAuto),
+			  ControlValue(controls::AnalogueGainModeManual) } },
+		      ControlValue(controls::AnalogueGainModeAuto)) },
 	{ &controls::AnalogueGain, ControlInfo(1.0f, 16.0f, 1.0f) },
 	{ &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) },
 	{ &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
 	{ &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
 	{ &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
-	{ &controls::AeFlickerMode, ControlInfo(static_cast<int>(controls::FlickerOff),
-						static_cast<int>(controls::FlickerManual),
-						static_cast<int>(controls::FlickerOff)) },
+	{ &controls::AeFlickerMode,
+	  ControlInfo({ { ControlValue(controls::FlickerOff),
+			  ControlValue(controls::FlickerManual) } },
+		      ControlValue(controls::FlickerOff)) },
 	{ &controls::AeFlickerPeriod, ControlInfo(100, 1000000) },
 	{ &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
 	{ &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
@@ -232,25 +233,6 @@ int32_t IpaBase::configure(const IPACame
 		agcStatus.analogueGain = defaultAnalogueGain;
 		applyAGC(&agcStatus, ctrls);
 
-		/*
-		 * Set the lens to the default (typically hyperfocal) position
-		 * on first start.
-		 */
-		if (lensPresent_) {
-			RPiController::AfAlgorithm *af =
-				dynamic_cast<RPiController::AfAlgorithm *>(controller_.getAlgorithm("af"));
-
-			if (af) {
-				float defaultPos =
-					ipaAfControls.at(&controls::LensPosition).def().get<float>();
-				ControlList lensCtrl(lensCtrls_);
-				int32_t hwpos;
-
-				af->setLensPosition(defaultPos, &hwpos);
-				lensCtrl.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos);
-				result->lensControls = std::move(lensCtrl);
-			}
-		}
 	}
 
 	result->sensorControls = std::move(ctrls);
@@ -280,8 +262,20 @@ int32_t IpaBase::configure(const IPACame
 		ctrlMap.merge(ControlInfoMap::Map(ipaColourControls));
 
 	/* Declare Autofocus controls, only if we have a controllable lens */
-	if (lensPresent_)
+	if (lensPresent_) {
 		ctrlMap.merge(ControlInfoMap::Map(ipaAfControls));
+		RPiController::AfAlgorithm *af =
+			dynamic_cast<RPiController::AfAlgorithm *>(controller_.getAlgorithm("af"));
+		if (af) {
+			double min, max, dflt;
+			af->getLensLimits(min, max);
+			dflt = af->getDefaultLensPosition();
+			ctrlMap[&controls::LensPosition] =
+				ControlInfo(static_cast<float>(min),
+					    static_cast<float>(max),
+					    static_cast<float>(dflt));
+		}
+	}
 
 	result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
 
@@ -304,29 +298,53 @@ void IpaBase::start(const ControlList &c
 	frameLengths_.clear();
 	frameLengths_.resize(FrameLengthsQueueSize, 0s);
 
-	/* SwitchMode may supply updated exposure/gain values to use. */
-	AgcStatus agcStatus;
-	agcStatus.exposureTime = 0.0s;
-	agcStatus.analogueGain = 0.0;
+	/*
+	 * SwitchMode may supply updated exposure/gain values to use.
+	 * agcStatus_ will store these values for us to use until delayed_status values
+	 * start to appear.
+	 */
+	agcStatus_.exposureTime = 0.0s;
+	agcStatus_.analogueGain = 0.0;
 
-	metadata.get("agc.status", agcStatus);
-	if (agcStatus.exposureTime && agcStatus.analogueGain) {
+	metadata.get("agc.status", agcStatus_);
+	if (agcStatus_.exposureTime && agcStatus_.analogueGain) {
 		ControlList ctrls(sensorCtrls_);
-		applyAGC(&agcStatus, ctrls);
+		applyAGC(&agcStatus_, ctrls);
 		result->controls = std::move(ctrls);
 		setCameraTimeoutValue();
 	}
 	/* Make a note of this as it tells us the HDR status of the first few frames. */
-	hdrStatus_ = agcStatus.hdr;
+	hdrStatus_ = agcStatus_.hdr;
+
+	/*
+	 * AF: If no lens position was specified, drive lens to a default position.
+	 * This had to be deferred (not initialised by a constructor) until here
+	 * to ensure that exactly ONE starting position is sent to the lens driver.
+	 * It should be the static API default, not dependent on AF range or mode.
+	 */
+	if (firstStart_ && lensPresent_) {
+		RPiController::AfAlgorithm *af = dynamic_cast<RPiController::AfAlgorithm *>(
+			controller_.getAlgorithm("af"));
+		if (af && !af->getLensPosition()) {
+			int32_t hwpos;
+			double pos = af->getDefaultLensPosition();
+			if (af->setLensPosition(pos, &hwpos, true)) {
+				ControlList lensCtrls(lensCtrls_);
+				lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos);
+				setLensControls.emit(lensCtrls);
+			}
+		}
+	}
 
 	/*
 	 * Initialise frame counts, and decide how many frames must be hidden or
 	 * "mistrusted", which depends on whether this is a startup from cold,
 	 * or merely a mode switch in a running system.
 	 */
+	unsigned int agcConvergenceFrames = 0, awbConvergenceFrames = 0;
 	frameCount_ = 0;
 	if (firstStart_) {
-		dropFrameCount_ = helper_->hideFramesStartup();
+		invalidCount_ = helper_->hideFramesStartup();
 		mistrustCount_ = helper_->mistrustFramesStartup();
 
 		/*
@@ -336,7 +354,6 @@ void IpaBase::start(const ControlList &c
 		 * (mistrustCount_) that they won't see. But if zero (i.e.
 		 * no convergence necessary), no frames need to be dropped.
 		 */
-		unsigned int agcConvergenceFrames = 0;
 		RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
 			controller_.getAlgorithm("agc"));
 		if (agc) {
@@ -345,7 +362,6 @@ void IpaBase::start(const ControlList &c
 				agcConvergenceFrames += mistrustCount_;
 		}
 
-		unsigned int awbConvergenceFrames = 0;
 		RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
 			controller_.getAlgorithm("awb"));
 		if (awb) {
@@ -353,15 +369,18 @@ void IpaBase::start(const ControlList &c
 			if (awbConvergenceFrames)
 				awbConvergenceFrames += mistrustCount_;
 		}
-
-		dropFrameCount_ = std::max({ dropFrameCount_, agcConvergenceFrames, awbConvergenceFrames });
-		LOG(IPARPI, Debug) << "Drop " << dropFrameCount_ << " frames on startup";
 	} else {
-		dropFrameCount_ = helper_->hideFramesModeSwitch();
+		invalidCount_ = helper_->hideFramesModeSwitch();
 		mistrustCount_ = helper_->mistrustFramesModeSwitch();
 	}
 
-	result->dropFrameCount = dropFrameCount_;
+	result->startupFrameCount = std::max({ agcConvergenceFrames, awbConvergenceFrames });
+	result->invalidFrameCount = invalidCount_;
+
+	invalidCount_ = std::max({ invalidCount_, agcConvergenceFrames, awbConvergenceFrames });
+
+	LOG(IPARPI, Debug) << "Startup frames: " << result->startupFrameCount
+			   << " Invalid frames: " << result->invalidFrameCount;
 
 	firstStart_ = false;
 	lastRunTimestamp_ = 0;
@@ -441,7 +460,7 @@ void IpaBase::prepareIsp(const PreparePa
 
 	/* Allow a 10% margin on the comparison below. */
 	Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;
-	if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ &&
+	if (lastRunTimestamp_ && frameCount_ > invalidCount_ &&
 	    delta < controllerMinFrameDuration * 0.9 && !hdrChange) {
 		/*
 		 * Ensure we merge the previous frame's metadata with the current
@@ -470,7 +489,9 @@ void IpaBase::prepareIsp(const PreparePa
 		controller_.prepare(&rpiMetadata);
 		/* Actually prepare the ISP parameters for the frame. */
 		platformPrepareIsp(params, rpiMetadata);
-	}
+		platformPrepareAgc(rpiMetadata);
+	} else
+		platformPrepareAgc(rpiMetadata);
 
 	frameCount_++;
 
@@ -485,10 +506,9 @@ void IpaBase::prepareIsp(const PreparePa
 void IpaBase::processStats(const ProcessParams &params)
 {
 	unsigned int ipaContext = params.ipaContext % rpiMetadata_.size();
+	RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
 
 	if (processPending_ && frameCount_ >= mistrustCount_) {
-		RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
-
 		auto it = buffers_.find(params.buffers.stats);
 		if (it == buffers_.end()) {
 			LOG(IPARPI, Error) << "Could not find stats buffer!";
@@ -502,14 +522,15 @@ void IpaBase::processStats(const Process
 
 		helper_->process(statistics, rpiMetadata);
 		controller_.process(statistics, &rpiMetadata);
+	}
 
-		struct AgcStatus agcStatus;
-		if (rpiMetadata.get("agc.status", agcStatus) == 0) {
-			ControlList ctrls(sensorCtrls_);
-			applyAGC(&agcStatus, ctrls);
-			setDelayedControls.emit(ctrls, ipaContext);
-			setCameraTimeoutValue();
-		}
+	struct AgcStatus agcStatus;
+	if (rpiMetadata.get("agc.status", agcStatus) == 0) {
+		ControlList ctrls(sensorCtrls_);
+		applyAGC(&agcStatus, ctrls);
+		rpiMetadata.set("agc.status", agcStatus);
+		setDelayedControls.emit(ctrls, ipaContext);
+		setCameraTimeoutValue();
 	}
 
 	/*
@@ -852,7 +873,7 @@ void IpaBase::applyControls(const Contro
 			if (agc->autoGainEnabled())
 				break;
 
-			agc->setFixedAnalogueGain(0, ctrl.second.get<float>());
+			agc->setFixedGain(0, ctrl.second.get<float>());
 
 			libcameraMetadata_.set(controls::AnalogueGain,
 					       ctrl.second.get<float>());
@@ -946,6 +967,17 @@ void IpaBase::applyControls(const Contro
 			break;
 		}
 
+		case controls::AE_ENABLE: {
+			/*
+			 * The AeEnable control is now just a wrapper that will already have been
+			 * converted to ExposureTimeMode and AnalogueGainMode equivalents, so there
+			 * would be nothing to do here. Nonetheless, "handle" the control so as to
+			 * avoid warnings from the "default:" clause of the switch statement.
+			 */
+
+			break;
+		}
+
 		case controls::AE_FLICKER_MODE: {
 			RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
 				controller_.getAlgorithm("agc"));
@@ -1394,9 +1426,6 @@ void IpaBase::reportMetadata(unsigned in
 	}
 
 	AgcPrepareStatus *agcPrepareStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status");
-	if (agcPrepareStatus)
-		libcameraMetadata_.set(controls::DigitalGain, agcPrepareStatus->digitalGain);
-
 	RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
 		controller_.getAlgorithm("agc"));
 	if (agc) {
@@ -1409,6 +1438,13 @@ void IpaBase::reportMetadata(unsigned in
 						       : controls::AeStateSearching);
 	}
 
+	const AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
+	if (agcStatus)
+		libcameraMetadata_.set(controls::DigitalGain, agcStatus->digitalGain);
+	else
+		libcameraMetadata_.set(controls::DigitalGain, agcStatus_.digitalGain);
+	/* The HDR metadata reporting will use this agcStatus too. */
+
 	LuxStatus *luxStatus = rpiMetadata.getLocked<LuxStatus>("lux.status");
 	if (luxStatus)
 		libcameraMetadata_.set(controls::Lux, luxStatus->lux);
@@ -1498,7 +1534,6 @@ void IpaBase::reportMetadata(unsigned in
 	 * delayed_status to be available, we use the HDR status that came out of the
 	 * switchMode call.
 	 */
-	const AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
 	const HdrStatus &hdrStatus = agcStatus ? agcStatus->hdr : hdrStatus_;
 	if (!hdrStatus.mode.empty() && hdrStatus.mode != "Off") {
 		int32_t hdrMode = controls::HdrModeOff;
@@ -1552,10 +1587,11 @@ void IpaBase::applyFrameDurations(Durati
 
 	RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
 		controller_.getAlgorithm("agc"));
-	agc->setMaxExposureTime(maxExposureTime);
+	if (agc)
+		agc->setMaxExposureTime(maxExposureTime);
 }
 
-void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
+void IpaBase::applyAGC(struct AgcStatus *agcStatus, ControlList &ctrls)
 {
 	const int32_t minGainCode = helper_->gainCode(mode_.minAnalogueGain);
 	const int32_t maxGainCode = helper_->gainCode(mode_.maxAnalogueGain);
@@ -1585,6 +1621,19 @@ void IpaBase::applyAGC(const struct AgcS
 	ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);
 
 	/*
+	 * We must update the digital gain to make up for any quantisation that happens, and
+	 * communicate that back into the metadata so that it will appear as the "delayed" status.
+	 * (Note that "exposure" is already the "actual" exposure.)
+	 */
+	double actualGain = helper_->gain(gainCode);
+	double ratio = agcStatus->analogueGain / actualGain;
+	ratio *= agcStatus->exposureTime / exposure;
+	double newDigitalGain = agcStatus->digitalGain * ratio;
+	agcStatus->digitalGain = newDigitalGain;
+	agcStatus->analogueGain = actualGain;
+	agcStatus->exposureTime = exposure;
+
+	/*
 	 * At present, there is no way of knowing if a control is read-only.
 	 * As a workaround, assume that if the minimum and maximum values of
 	 * the V4L2_CID_HBLANK control are the same, it implies the control
diff -pruN 0.5.0-1/src/ipa/rpi/common/ipa_base.h 0.5.2-2/src/ipa/rpi/common/ipa_base.h
--- 0.5.0-1/src/ipa/rpi/common/ipa_base.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/common/ipa_base.h	2025-08-07 13:46:17.000000000 +0000
@@ -73,6 +73,9 @@ protected:
 	/* Remember the HDR status after a mode switch. */
 	HdrStatus hdrStatus_;
 
+	/* Remember the AGC status after a mode switch. */
+	AgcStatus agcStatus_;
+
 	/* Whether the stitch block (if available) needs to swap buffers. */
 	bool stitchSwapBuffers_;
 
@@ -86,6 +89,7 @@ private:
 
 	virtual void platformPrepareIsp(const PrepareParams &params,
 					RPiController::Metadata &rpiMetadata) = 0;
+	virtual void platformPrepareAgc(RPiController::Metadata &rpiMetadata) = 0;
 	virtual RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) = 0;
 
 	void setMode(const IPACameraSensorInfo &sensorInfo);
@@ -97,7 +101,7 @@ private:
 	void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
 	void reportMetadata(unsigned int ipaContext);
 	void applyFrameDurations(utils::Duration minFrameDuration, utils::Duration maxFrameDuration);
-	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
+	void applyAGC(struct AgcStatus *agcStatus, ControlList &ctrls);
 
 	std::map<unsigned int, MappedFrameBuffer> buffers_;
 
@@ -115,8 +119,8 @@ private:
 	/* How many frames we should avoid running control algos on. */
 	unsigned int mistrustCount_;
 
-	/* Number of frames that need to be dropped on startup. */
-	unsigned int dropFrameCount_;
+	/* Number of frames that need to be marked as dropped on startup. */
+	unsigned int invalidCount_;
 
 	/* Frame timestamp for the last run of the controller. */
 	uint64_t lastRunTimestamp_;
diff -pruN 0.5.0-1/src/ipa/rpi/controller/af_algorithm.h 0.5.2-2/src/ipa/rpi/controller/af_algorithm.h
--- 0.5.0-1/src/ipa/rpi/controller/af_algorithm.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/af_algorithm.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2022, Raspberry Pi Ltd
  *
- * af_algorithm.hpp - auto focus algorithm interface
+ * Auto focus algorithm interface
  */
 #pragma once
 
@@ -33,6 +33,10 @@ public:
 	 *
 	 * getMode() is provided mainly for validating controls.
 	 * getLensPosition() is provided for populating DeviceStatus.
+	 *
+	 * getDefaultlensPosition() and getLensLimits() were added for
+	 * populating ControlInfoMap. They return the static API limits
+	 * which should be independent of the current range or mode.
 	 */
 
 	enum AfRange { AfRangeNormal = 0,
@@ -66,7 +70,9 @@ public:
 	}
 	virtual void setMode(AfMode mode) = 0;
 	virtual AfMode getMode() const = 0;
-	virtual bool setLensPosition(double dioptres, int32_t *hwpos) = 0;
+	virtual double getDefaultLensPosition() const = 0;
+	virtual void getLensLimits(double &min, double &max) const = 0;
+	virtual bool setLensPosition(double dioptres, int32_t *hwpos, bool force = false) = 0;
 	virtual std::optional<double> getLensPosition() const = 0;
 	virtual void triggerScan() = 0;
 	virtual void cancelScan() = 0;
diff -pruN 0.5.0-1/src/ipa/rpi/controller/agc_algorithm.h 0.5.2-2/src/ipa/rpi/controller/agc_algorithm.h
--- 0.5.0-1/src/ipa/rpi/controller/agc_algorithm.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/agc_algorithm.h	2025-08-07 13:46:17.000000000 +0000
@@ -26,7 +26,7 @@ public:
 	virtual void setFixedExposureTime(unsigned int channel,
 					  libcamera::utils::Duration fixedExposureTime) = 0;
 	virtual void setMaxExposureTime(libcamera::utils::Duration maxExposureTime) = 0;
-	virtual void setFixedAnalogueGain(unsigned int channel, double fixedAnalogueGain) = 0;
+	virtual void setFixedGain(unsigned int channel, double fixedGain) = 0;
 	virtual void setMeteringMode(std::string const &meteringModeName) = 0;
 	virtual void setExposureMode(std::string const &exposureModeName) = 0;
 	virtual void setConstraintMode(std::string const &contraintModeName) = 0;
diff -pruN 0.5.0-1/src/ipa/rpi/controller/agc_status.h 0.5.2-2/src/ipa/rpi/controller/agc_status.h
--- 0.5.0-1/src/ipa/rpi/controller/agc_status.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/agc_status.h	2025-08-07 13:46:17.000000000 +0000
@@ -30,6 +30,7 @@ struct AgcStatus {
 	libcamera::utils::Duration targetExposureValue; /* (unfiltered) target total exposure AGC is aiming for */
 	libcamera::utils::Duration exposureTime;
 	double analogueGain;
+	double digitalGain;
 	std::string exposureMode;
 	std::string constraintMode;
 	std::string meteringMode;
@@ -37,12 +38,11 @@ struct AgcStatus {
 	libcamera::utils::Duration flickerPeriod;
 	int floatingRegionEnable;
 	libcamera::utils::Duration fixedExposureTime;
-	double fixedAnalogueGain;
+	double fixedGain;
 	unsigned int channel;
 	HdrStatus hdr;
 };
 
 struct AgcPrepareStatus {
-	double digitalGain;
 	int locked;
 };
diff -pruN 0.5.0-1/src/ipa/rpi/controller/controller.cpp 0.5.2-2/src/ipa/rpi/controller/controller.cpp
--- 0.5.0-1/src/ipa/rpi/controller/controller.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/controller.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -21,61 +21,70 @@ using namespace std::literals::chrono_li
 
 LOG_DEFINE_CATEGORY(RPiController)
 
-static const std::map<std::string, Controller::HardwareConfig> HardwareConfigMap = {
-	{
-		"bcm2835",
+namespace {
+
+const std::map<std::string, Controller::HardwareConfig> &hardwareConfigMap()
+{
+	static const std::map<std::string, Controller::HardwareConfig> map = {
 		{
-			/*
-			 * There are only ever 15 AGC regions computed by the firmware
-			 * due to zoning, but the HW defines AGC_REGIONS == 16!
-			 */
-			.agcRegions = { 15 , 1 },
-			.agcZoneWeights = { 15 , 1 },
-			.awbRegions = { 16, 12 },
-			.cacRegions = { 0, 0 },
-			.focusRegions = { 4, 3 },
-			.numHistogramBins = 128,
-			.numGammaPoints = 33,
-			.pipelineWidth = 13,
-			.statsInline = false,
-			.minPixelProcessingTime = 0s,
-			.dataBufferStrided = true,
-		}
-	},
-	{
-		"pisp",
+			"bcm2835",
+			{
+				/*
+				* There are only ever 15 AGC regions computed by the firmware
+				* due to zoning, but the HW defines AGC_REGIONS == 16!
+				*/
+				.agcRegions = { 15 , 1 },
+				.agcZoneWeights = { 15 , 1 },
+				.awbRegions = { 16, 12 },
+				.cacRegions = { 0, 0 },
+				.focusRegions = { 4, 3 },
+				.numHistogramBins = 128,
+				.numGammaPoints = 33,
+				.pipelineWidth = 13,
+				.statsInline = false,
+				.minPixelProcessingTime = 0s,
+				.dataBufferStrided = true,
+			}
+		},
 		{
-			.agcRegions = { 0, 0 },
-			.agcZoneWeights = { 15, 15 },
-			.awbRegions = { 32, 32 },
-			.cacRegions = { 8, 8 },
-			.focusRegions = { 8, 8 },
-			.numHistogramBins = 1024,
-			.numGammaPoints = 64,
-			.pipelineWidth = 16,
-			.statsInline = true,
-
-			/*
-			 * The constraint below is on the rate of pixels going
-			 * from CSI2 peripheral to ISP-FE (400Mpix/s, plus tiny
-			 * overheads per scanline, for which 380Mpix/s is a
-			 * conservative bound).
-			 *
-			 * There is a 64kbit data FIFO before the bottleneck,
-			 * which means that in all reasonable cases the
-			 * constraint applies at a timescale >= 1 scanline, so
-			 * adding horizontal blanking can prevent loss.
-			 *
-			 * If the backlog were to grow beyond 64kbit during a
-			 * single scanline, there could still be loss. This
-			 * could happen using 4 lanes at 1.5Gbps at 10bpp with
-			 * frames wider than ~16,000 pixels.
-			 */
-			.minPixelProcessingTime = 1.0us / 380,
-			.dataBufferStrided = false,
-		}
-	},
-};
+			"pisp",
+			{
+				.agcRegions = { 0, 0 },
+				.agcZoneWeights = { 15, 15 },
+				.awbRegions = { 32, 32 },
+				.cacRegions = { 8, 8 },
+				.focusRegions = { 8, 8 },
+				.numHistogramBins = 1024,
+				.numGammaPoints = 64,
+				.pipelineWidth = 16,
+				.statsInline = true,
+
+				/*
+				* The constraint below is on the rate of pixels going
+				* from CSI2 peripheral to ISP-FE (400Mpix/s, plus tiny
+				* overheads per scanline, for which 380Mpix/s is a
+				* conservative bound).
+				*
+				* There is a 64kbit data FIFO before the bottleneck,
+				* which means that in all reasonable cases the
+				* constraint applies at a timescale >= 1 scanline, so
+				* adding horizontal blanking can prevent loss.
+				*
+				* If the backlog were to grow beyond 64kbit during a
+				* single scanline, there could still be loss. This
+				* could happen using 4 lanes at 1.5Gbps at 10bpp with
+				* frames wider than ~16,000 pixels.
+				*/
+				.minPixelProcessingTime = 1.0us / 380,
+				.dataBufferStrided = false,
+			}
+		},
+	};
+
+	return map;
+}
+
+} /* namespace */
 
 Controller::Controller()
 	: switchModeCalled_(false)
@@ -211,12 +220,12 @@ const std::string &Controller::getTarget
 
 const Controller::HardwareConfig &Controller::getHardwareConfig() const
 {
-	auto cfg = HardwareConfigMap.find(getTarget());
+	auto cfg = hardwareConfigMap().find(getTarget());
 
 	/*
 	 * This really should not happen, the IPA ought to validate the target
 	 * on initialisation.
 	 */
-	ASSERT(cfg != HardwareConfigMap.end());
+	ASSERT(cfg != hardwareConfigMap().end());
 	return cfg->second;
 }
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/af.cpp 0.5.2-2/src/ipa/rpi/controller/rpi/af.cpp
--- 0.5.0-1/src/ipa/rpi/controller/rpi/af.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/af.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -46,6 +46,8 @@ Af::SpeedDependentParams::SpeedDependent
 	: stepCoarse(1.0),
 	  stepFine(0.25),
 	  contrastRatio(0.75),
+	  retriggerRatio(0.75),
+	  retriggerDelay(10),
 	  pdafGain(-0.02),
 	  pdafSquelch(0.125),
 	  maxSlew(2.0),
@@ -60,6 +62,7 @@ Af::CfgParams::CfgParams()
 	  confThresh(16),
 	  confClip(512),
 	  skipFrames(5),
+	  checkForIR(false),
 	  map()
 {
 }
@@ -87,6 +90,8 @@ void Af::SpeedDependentParams::read(cons
 	readNumber<double>(stepCoarse, params, "step_coarse");
 	readNumber<double>(stepFine, params, "step_fine");
 	readNumber<double>(contrastRatio, params, "contrast_ratio");
+	readNumber<double>(retriggerRatio, params, "retrigger_ratio");
+	readNumber<uint32_t>(retriggerDelay, params, "retrigger_delay");
 	readNumber<double>(pdafGain, params, "pdaf_gain");
 	readNumber<double>(pdafSquelch, params, "pdaf_squelch");
 	readNumber<double>(maxSlew, params, "max_slew");
@@ -137,6 +142,7 @@ int Af::CfgParams::read(const libcamera:
 	readNumber<uint32_t>(confThresh, params, "conf_thresh");
 	readNumber<uint32_t>(confClip, params, "conf_clip");
 	readNumber<uint32_t>(skipFrames, params, "skip_frames");
+	readNumber<bool>(checkForIR, params, "check_for_ir");
 
 	if (params.contains("map"))
 		map = params["map"].get<ipa::Pwl>(ipa::Pwl{});
@@ -176,27 +182,38 @@ Af::Af(Controller *controller)
 	  useWindows_(false),
 	  phaseWeights_(),
 	  contrastWeights_(),
+	  awbWeights_(),
 	  scanState_(ScanState::Idle),
 	  initted_(false),
+	  irFlag_(false),
 	  ftarget_(-1.0),
 	  fsmooth_(-1.0),
 	  prevContrast_(0.0),
+	  oldSceneContrast_(0.0),
+	  prevAverage_{ 0.0, 0.0, 0.0 },
+	  oldSceneAverage_{ 0.0, 0.0, 0.0 },
+	  prevPhase_(0.0),
 	  skipCount_(0),
 	  stepCount_(0),
 	  dropCount_(0),
+	  sameSignCount_(0),
+	  sceneChangeCount_(0),
 	  scanMaxContrast_(0.0),
 	  scanMinContrast_(1.0e9),
+	  scanStep_(0.0),
 	  scanData_(),
 	  reportState_(AfState::Idle)
 {
 	/*
-	 * Reserve space for data, to reduce memory fragmentation. It's too early
-	 * to query the size of the PDAF (from camera) and Contrast (from ISP)
-	 * statistics, but these are plausible upper bounds.
+	 * Reserve space for data structures, to reduce memory fragmentation.
+	 * It's too early to query the size of the PDAF sensor data, so guess.
 	 */
+	windows_.reserve(1);
 	phaseWeights_.w.reserve(16 * 12);
 	contrastWeights_.w.reserve(getHardwareConfig().focusRegions.width *
 				   getHardwareConfig().focusRegions.height);
+	contrastWeights_.w.reserve(getHardwareConfig().awbRegions.width *
+				   getHardwareConfig().awbRegions.height);
 	scanData_.reserve(32);
 }
 
@@ -235,13 +252,14 @@ void Af::switchMode(CameraMode const &ca
 			  << statsRegion_.height;
 	invalidateWeights();
 
-	if (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) {
+	if (scanState_ >= ScanState::Coarse1 && scanState_ < ScanState::Settle) {
 		/*
 		 * If a scan was in progress, re-start it, as CDAF statistics
 		 * may have changed. Though if the application is just about
 		 * to take a still picture, this will not help...
 		 */
 		startProgrammedScan();
+		updateLensPosition();
 	}
 	skipCount_ = cfg_.skipFrames;
 }
@@ -307,6 +325,7 @@ void Af::invalidateWeights()
 {
 	phaseWeights_.sum = 0;
 	contrastWeights_.sum = 0;
+	awbWeights_.sum = 0;
 }
 
 bool Af::getPhase(PdafRegions const &regions, double &phase, double &conf)
@@ -328,9 +347,8 @@ bool Af::getPhase(PdafRegions const &reg
 			if (c >= cfg_.confThresh) {
 				if (c > cfg_.confClip)
 					c = cfg_.confClip;
-				c -= (cfg_.confThresh >> 2);
+				c -= (cfg_.confThresh >> 1);
 				sumWc += w * c;
-				c -= (cfg_.confThresh >> 2);
 				sumWcp += (int64_t)(w * c) * (int64_t)data.phase;
 			}
 		}
@@ -364,6 +382,54 @@ double Af::getContrast(const FocusRegion
 	return (contrastWeights_.sum > 0) ? ((double)sumWc / (double)contrastWeights_.sum) : 0.0;
 }
 
+/*
+ * Get the average R, G, B values in AF window[s] (from AWB statistics).
+ * Optionally, check if all of {R,G,B} are within 4:5 of each other
+ * across more than 50% of the counted area and within the AF window:
+ * for an RGB sensor this strongly suggests that IR lighting is in use.
+ */
+
+bool Af::getAverageAndTestIr(const RgbyRegions &awbStats, double rgb[3])
+{
+	libcamera::Size size = awbStats.size();
+	if (size.height != awbWeights_.rows ||
+	    size.width != awbWeights_.cols || awbWeights_.sum == 0) {
+		LOG(RPiAf, Debug) << "Recompute RGB weights " << size.width << 'x' << size.height;
+		computeWeights(&awbWeights_, size.height, size.width);
+	}
+
+	uint64_t sr = 0, sg = 0, sb = 0, sw = 1;
+	uint64_t greyCount = 0, allCount = 0;
+	for (unsigned i = 0; i < awbStats.numRegions(); ++i) {
+		uint64_t r = awbStats.get(i).val.rSum;
+		uint64_t g = awbStats.get(i).val.gSum;
+		uint64_t b = awbStats.get(i).val.bSum;
+		uint64_t w = awbWeights_.w[i];
+		if (w) {
+			sw += w;
+			sr += w * r;
+			sg += w * g;
+			sb += w * b;
+		}
+		if (cfg_.checkForIR) {
+			if (4 * r < 5 * b && 4 * b < 5 * r &&
+			    4 * r < 5 * g && 4 * g < 5 * r &&
+			    4 * b < 5 * g && 4 * g < 5 * b)
+				greyCount += awbStats.get(i).counted;
+			allCount += awbStats.get(i).counted;
+		}
+	}
+
+	rgb[0] = sr / (double)sw;
+	rgb[1] = sg / (double)sw;
+	rgb[2] = sb / (double)sw;
+
+	return (cfg_.checkForIR && 2 * greyCount > allCount &&
+		4 * sr < 5 * sb && 4 * sb < 5 * sr &&
+		4 * sr < 5 * sg && 4 * sg < 5 * sr &&
+		4 * sb < 5 * sg && 4 * sg < 5 * sb);
+}
+
 void Af::doPDAF(double phase, double conf)
 {
 	/* Apply loop gain */
@@ -410,7 +476,7 @@ void Af::doPDAF(double phase, double con
 bool Af::earlyTerminationByPhase(double phase)
 {
 	if (scanData_.size() > 0 &&
-	    scanData_[scanData_.size() - 1].conf >= cfg_.confEpsilon) {
+	    scanData_[scanData_.size() - 1].conf >= cfg_.confThresh) {
 		double oldFocus = scanData_[scanData_.size() - 1].focus;
 		double oldPhase = scanData_[scanData_.size() - 1].phase;
 
@@ -419,11 +485,12 @@ bool Af::earlyTerminationByPhase(double
 		 * Interpolate/extrapolate the lens position for zero phase.
 		 * Check that the extrapolation is well-conditioned.
 		 */
-		if ((ftarget_ - oldFocus) * (phase - oldPhase) > 0.0) {
+		if ((ftarget_ - oldFocus) * (phase - oldPhase) * cfg_.speeds[speed_].pdafGain < 0.0) {
 			double param = phase / (phase - oldPhase);
-			if (-3.0 <= param && param <= 3.5) {
-				ftarget_ += param * (oldFocus - ftarget_);
+			if ((-2.5 <= param || mode_ == AfModeContinuous) && param <= 3.0) {
 				LOG(RPiAf, Debug) << "ETBP: param=" << param;
+				param = std::max(param, -2.5);
+				ftarget_ += param * (oldFocus - ftarget_);
 				return true;
 			}
 		}
@@ -436,15 +503,28 @@ double Af::findPeak(unsigned i) const
 {
 	double f = scanData_[i].focus;
 
-	if (i > 0 && i + 1 < scanData_.size()) {
-		double dropLo = scanData_[i].contrast - scanData_[i - 1].contrast;
-		double dropHi = scanData_[i].contrast - scanData_[i + 1].contrast;
-		if (0.0 <= dropLo && dropLo < dropHi) {
-			double param = 0.3125 * (1.0 - dropLo / dropHi) * (1.6 - dropLo / dropHi);
-			f += param * (scanData_[i - 1].focus - f);
-		} else if (0.0 <= dropHi && dropHi < dropLo) {
-			double param = 0.3125 * (1.0 - dropHi / dropLo) * (1.6 - dropHi / dropLo);
-			f += param * (scanData_[i + 1].focus - f);
+	if (scanData_.size() >= 3) {
+		/*
+		 * Given the sample with the highest contrast score and its two
+		 * neighbours either side (or same side if at the end of a scan),
+		 * solve for the best lens position by fitting a parabola.
+		 * Adapted from awb.cpp: interpolateQaudaratic()
+		 */
+
+		if (i == 0)
+			i++;
+		else if (i + 1 >= scanData_.size())
+			i--;
+
+		double abx = scanData_[i - 1].focus - scanData_[i].focus;
+		double aby = scanData_[i - 1].contrast - scanData_[i].contrast;
+		double cbx = scanData_[i + 1].focus - scanData_[i].focus;
+		double cby = scanData_[i + 1].contrast - scanData_[i].contrast;
+		double denom = 2.0 * (aby * cbx - cby * abx);
+		if (std::abs(denom) >= (1.0 / 64.0) && denom * abx > 0.0) {
+			f = (aby * cbx * cbx - cby * abx * abx) / denom;
+			f = std::clamp(f, std::min(abx, cbx), std::max(abx, cbx));
+			f += scanData_[i].focus;
 		}
 	}
 
@@ -458,36 +538,49 @@ void Af::doScan(double contrast, double
 	if (scanData_.empty() || contrast > scanMaxContrast_) {
 		scanMaxContrast_ = contrast;
 		scanMaxIndex_ = scanData_.size();
+		if (scanState_ != ScanState::Fine)
+			std::copy(prevAverage_, prevAverage_ + 3, oldSceneAverage_);
 	}
 	if (contrast < scanMinContrast_)
 		scanMinContrast_ = contrast;
 	scanData_.emplace_back(ScanRecord{ ftarget_, contrast, phase, conf });
 
-	if (scanState_ == ScanState::Coarse) {
-		if (ftarget_ >= cfg_.ranges[range_].focusMax ||
-		    contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
-			/*
-			 * Finished course scan, or termination based on contrast.
-			 * Jump to just after max contrast and start fine scan.
-			 */
-			ftarget_ = std::min(ftarget_, findPeak(scanMaxIndex_) +
-					2.0 * cfg_.speeds[speed_].stepFine);
-			scanState_ = ScanState::Fine;
-			scanData_.clear();
-		} else
-			ftarget_ += cfg_.speeds[speed_].stepCoarse;
-	} else { /* ScanState::Fine */
-		if (ftarget_ <= cfg_.ranges[range_].focusMin || scanData_.size() >= 5 ||
-		    contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
-			/*
-			 * Finished fine scan, or termination based on contrast.
-			 * Use quadratic peak-finding to find best contrast position.
-			 */
-			ftarget_ = findPeak(scanMaxIndex_);
+	if ((scanStep_ >= 0.0 && ftarget_ >= cfg_.ranges[range_].focusMax) ||
+	    (scanStep_ <= 0.0 && ftarget_ <= cfg_.ranges[range_].focusMin) ||
+	    (scanState_ == ScanState::Fine && scanData_.size() >= 3) ||
+	    contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
+		double pk = findPeak(scanMaxIndex_);
+		/*
+		 * Finished a scan, by hitting a limit or due to constrast dropping off.
+		 * If this is a first coarse scan and we didn't bracket the peak, reverse!
+		 * If this is a fine scan, or no fine step was defined, we've finished.
+		 * Otherwise, start fine scan in opposite direction.
+		 */
+		if (scanState_ == ScanState::Coarse1 &&
+		    scanData_[0].contrast >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
+			scanStep_ = -scanStep_;
+			scanState_ = ScanState::Coarse2;
+		} else if (scanState_ == ScanState::Fine || cfg_.speeds[speed_].stepFine <= 0.0) {
+			ftarget_ = pk;
 			scanState_ = ScanState::Settle;
-		} else
-			ftarget_ -= cfg_.speeds[speed_].stepFine;
-	}
+		} else if (scanState_ == ScanState::Coarse1 &&
+			   scanData_[0].contrast >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
+			scanStep_ = -scanStep_;
+			scanState_ = ScanState::Coarse2;
+		} else if (scanStep_ >= 0.0) {
+			ftarget_ = std::min(pk + cfg_.speeds[speed_].stepFine,
+					    cfg_.ranges[range_].focusMax);
+			scanStep_ = -cfg_.speeds[speed_].stepFine;
+			scanState_ = ScanState::Fine;
+		} else {
+			ftarget_ = std::max(pk - cfg_.speeds[speed_].stepFine,
+					    cfg_.ranges[range_].focusMin);
+			scanStep_ = cfg_.speeds[speed_].stepFine;
+			scanState_ = ScanState::Fine;
+		}
+		scanData_.clear();
+	} else
+		ftarget_ += scanStep_;
 
 	stepCount_ = (ftarget_ == fsmooth_) ? 0 : cfg_.speeds[speed_].stepFrames;
 }
@@ -501,26 +594,70 @@ void Af::doAF(double contrast, double ph
 		return;
 	}
 
+	/* Count frames for which PDAF phase has had same sign */
+	if (phase * prevPhase_ <= 0.0)
+		sameSignCount_ = 0;
+	else
+		sameSignCount_++;
+	prevPhase_ = phase;
+
+	if (mode_ == AfModeManual)
+		return; /* nothing to do */
+
 	if (scanState_ == ScanState::Pdaf) {
 		/*
 		 * Use PDAF closed-loop control whenever available, in both CAF
 		 * mode and (for a limited number of iterations) when triggered.
-		 * If PDAF fails (due to poor contrast, noise or large defocus),
-		 * fall back to a CDAF-based scan. To avoid "nuisance" scans,
-		 * scan only after a number of frames with low PDAF confidence.
+		 * If PDAF fails (due to poor contrast, noise or large defocus)
+		 * for at least dropoutFrames, fall back to a CDAF-based scan
+		 * immediately (in triggered-auto) or on scene change (in CAF).
 		 */
-		if (conf > (dropCount_ ? 1.0 : 0.25) * cfg_.confEpsilon) {
-			doPDAF(phase, conf);
+		if (conf >= cfg_.confEpsilon) {
+			if (mode_ == AfModeAuto || sameSignCount_ >= 3)
+				doPDAF(phase, conf);
 			if (stepCount_ > 0)
 				stepCount_--;
 			else if (mode_ != AfModeContinuous)
 				scanState_ = ScanState::Idle;
+			oldSceneContrast_ = contrast;
+			std::copy(prevAverage_, prevAverage_ + 3, oldSceneAverage_);
+			sceneChangeCount_ = 0;
 			dropCount_ = 0;
-		} else if (++dropCount_ == cfg_.speeds[speed_].dropoutFrames)
+			return;
+		} else {
+			dropCount_++;
+			if (dropCount_ < cfg_.speeds[speed_].dropoutFrames)
+				return;
+			if (mode_ != AfModeContinuous) {
+				startProgrammedScan();
+				return;
+			}
+			/* else fall through to waiting for a scene change */
+		}
+	}
+	if (scanState_ < ScanState::Coarse1 && mode_ == AfModeContinuous) {
+		/*
+		 * In CAF mode, not in a scan, and PDAF is unavailable.
+		 * Wait for a scene change, followed by stability.
+		 */
+		if (contrast + 1.0 < cfg_.speeds[speed_].retriggerRatio * oldSceneContrast_ ||
+		    oldSceneContrast_ + 1.0 < cfg_.speeds[speed_].retriggerRatio * contrast ||
+		    prevAverage_[0] + 1.0 < cfg_.speeds[speed_].retriggerRatio * oldSceneAverage_[0] ||
+		    oldSceneAverage_[0] + 1.0 < cfg_.speeds[speed_].retriggerRatio * prevAverage_[0] ||
+		    prevAverage_[1] + 1.0 < cfg_.speeds[speed_].retriggerRatio * oldSceneAverage_[1] ||
+		    oldSceneAverage_[1] + 1.0 < cfg_.speeds[speed_].retriggerRatio * prevAverage_[1] ||
+		    prevAverage_[2] + 1.0 < cfg_.speeds[speed_].retriggerRatio * oldSceneAverage_[2] ||
+		    oldSceneAverage_[2] + 1.0 < cfg_.speeds[speed_].retriggerRatio * prevAverage_[2]) {
+			oldSceneContrast_ = contrast;
+			std::copy(prevAverage_, prevAverage_ + 3, oldSceneAverage_);
+			sceneChangeCount_ = 1;
+		} else if (sceneChangeCount_)
+			sceneChangeCount_++;
+		if (sceneChangeCount_ >= cfg_.speeds[speed_].retriggerDelay)
 			startProgrammedScan();
-	} else if (scanState_ >= ScanState::Coarse && fsmooth_ == ftarget_) {
+	} else if (scanState_ >= ScanState::Coarse1 && fsmooth_ == ftarget_) {
 		/*
-		 * Scanning sequence. This means PDAF has become unavailable.
+		 * CDAF-based scanning sequence.
 		 * Allow a delay between steps for CDAF FoM statistics to be
 		 * updated, and a "settling time" at the end of the sequence.
 		 * [A coarse or fine scan can be abandoned if two PDAF samples
@@ -539,11 +676,14 @@ void Af::doAF(double contrast, double ph
 				scanState_ = ScanState::Pdaf;
 			else
 				scanState_ = ScanState::Idle;
+			dropCount_ = 0;
+			sceneChangeCount_ = 0;
+			oldSceneContrast_ = std::max(scanMaxContrast_, prevContrast_);
 			scanData_.clear();
-		} else if (conf >= cfg_.confEpsilon && earlyTerminationByPhase(phase)) {
+		} else if (conf >= cfg_.confThresh && earlyTerminationByPhase(phase)) {
+			std::copy(prevAverage_, prevAverage_ + 3, oldSceneAverage_);
 			scanState_ = ScanState::Settle;
-			stepCount_ = (mode_ == AfModeContinuous) ? 0
-								 : cfg_.speeds[speed_].stepFrames;
+			stepCount_ = (mode_ == AfModeContinuous) ? 0 : cfg_.speeds[speed_].stepFrames;
 		} else
 			doScan(contrast, phase, conf);
 	}
@@ -573,7 +713,8 @@ void Af::updateLensPosition()
 void Af::startAF()
 {
 	/* Use PDAF if the tuning file allows it; else CDAF. */
-	if (cfg_.speeds[speed_].dropoutFrames > 0 &&
+	if (cfg_.speeds[speed_].pdafGain != 0.0 &&
+	    cfg_.speeds[speed_].dropoutFrames > 0 &&
 	    (mode_ == AfModeContinuous || cfg_.speeds[speed_].pdafFrames > 0)) {
 		if (!initted_) {
 			ftarget_ = cfg_.ranges[range_].focusDefault;
@@ -583,16 +724,30 @@ void Af::startAF()
 		scanState_ = ScanState::Pdaf;
 		scanData_.clear();
 		dropCount_ = 0;
+		oldSceneContrast_ = 0.0;
+		sceneChangeCount_ = 0;
 		reportState_ = AfState::Scanning;
-	} else
+	} else {
 		startProgrammedScan();
+		updateLensPosition();
+	}
 }
 
 void Af::startProgrammedScan()
 {
-	ftarget_ = cfg_.ranges[range_].focusMin;
-	updateLensPosition();
-	scanState_ = ScanState::Coarse;
+	if (!initted_ || mode_ != AfModeContinuous ||
+	    fsmooth_ <= cfg_.ranges[range_].focusMin + 2.0 * cfg_.speeds[speed_].stepCoarse) {
+		ftarget_ = cfg_.ranges[range_].focusMin;
+		scanStep_ = cfg_.speeds[speed_].stepCoarse;
+		scanState_ = ScanState::Coarse2;
+	} else if (fsmooth_ >= cfg_.ranges[range_].focusMax - 2.0 * cfg_.speeds[speed_].stepCoarse) {
+		ftarget_ = cfg_.ranges[range_].focusMax;
+		scanStep_ = -cfg_.speeds[speed_].stepCoarse;
+		scanState_ = ScanState::Coarse2;
+	} else {
+		scanStep_ = -cfg_.speeds[speed_].stepCoarse;
+		scanState_ = ScanState::Coarse1;
+	}
 	scanMaxContrast_ = 0.0;
 	scanMinContrast_ = 1.0e9;
 	scanMaxIndex_ = 0;
@@ -633,7 +788,7 @@ void Af::prepare(Metadata *imageMetadata
 		uint32_t oldSt = stepCount_;
 		if (imageMetadata->get("pdaf.regions", regions) == 0)
 			getPhase(regions, phase, conf);
-		doAF(prevContrast_, phase, conf);
+		doAF(prevContrast_, phase, irFlag_ ? 0 : conf);
 		updateLensPosition();
 		LOG(RPiAf, Debug) << std::fixed << std::setprecision(2)
 				  << static_cast<unsigned int>(reportState_)
@@ -643,7 +798,8 @@ void Af::prepare(Metadata *imageMetadata
 				  << " ft" << oldFt << "->" << ftarget_
 				  << " fs" << oldFs << "->" << fsmooth_
 				  << " cont=" << (int)prevContrast_
-				  << " phase=" << (int)phase << " conf=" << (int)conf;
+				  << " phase=" << (int)phase << " conf=" << (int)conf
+				  << (irFlag_ ? " IR" : "");
 	}
 
 	/* Report status and produce new lens setting */
@@ -656,6 +812,8 @@ void Af::prepare(Metadata *imageMetadata
 
 	if (mode_ == AfModeAuto && scanState_ != ScanState::Idle)
 		status.state = AfState::Scanning;
+	else if (mode_ == AfModeManual)
+		status.state = AfState::Idle;
 	else
 		status.state = reportState_;
 	status.lensSetting = initted_ ? std::optional<int>(cfg_.map.eval(fsmooth_))
@@ -667,6 +825,7 @@ void Af::process(StatisticsPtr &stats, [
 {
 	(void)imageMetadata;
 	prevContrast_ = getContrast(stats->focusRegions);
+	irFlag_ = getAverageAndTestIr(stats->awbRegions, prevAverage_);
 }
 
 /* Controls */
@@ -715,11 +874,23 @@ void Af::setWindows(libcamera::Span<libc
 		invalidateWeights();
 }
 
-bool Af::setLensPosition(double dioptres, int *hwpos)
+double Af::getDefaultLensPosition() const
+{
+	return cfg_.ranges[AfRangeNormal].focusDefault;
+}
+
+void Af::getLensLimits(double &min, double &max) const
+{
+	/* Limits for manual focus are set by map, not by ranges */
+	min = cfg_.map.domain().start;
+	max = cfg_.map.domain().end;
+}
+
+bool Af::setLensPosition(double dioptres, int *hwpos, bool force)
 {
 	bool changed = false;
 
-	if (mode_ == AfModeManual) {
+	if (mode_ == AfModeManual || force) {
 		LOG(RPiAf, Debug) << "setLensPosition: " << dioptres;
 		ftarget_ = cfg_.map.domain().clamp(dioptres);
 		changed = !(initted_ && fsmooth_ == ftarget_);
@@ -763,7 +934,7 @@ void Af::setMode(AfAlgorithm::AfMode mod
 		pauseFlag_ = false;
 		if (mode == AfModeContinuous)
 			scanState_ = ScanState::Trigger;
-		else if (mode != AfModeAuto || scanState_ < ScanState::Coarse)
+		else if (mode != AfModeAuto || scanState_ < ScanState::Coarse1)
 			goIdle();
 	}
 }
@@ -779,12 +950,14 @@ void Af::pause(AfAlgorithm::AfPause paus
 	if (mode_ == AfModeContinuous) {
 		if (pause == AfPauseResume && pauseFlag_) {
 			pauseFlag_ = false;
-			if (scanState_ < ScanState::Coarse)
+			if (scanState_ < ScanState::Coarse1)
 				scanState_ = ScanState::Trigger;
 		} else if (pause != AfPauseResume && !pauseFlag_) {
 			pauseFlag_ = true;
-			if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse)
-				goIdle();
+			if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse1) {
+				scanState_ = ScanState::Idle;
+				scanData_.clear();
+			}
 		}
 	}
 }
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/af.h 0.5.2-2/src/ipa/rpi/controller/rpi/af.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/af.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/af.h	2025-08-07 13:46:17.000000000 +0000
@@ -15,20 +15,28 @@
 /*
  * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.
  *
- * Whenever PDAF is available, it is used in a continuous feedback loop.
- * When triggered in auto mode, we simply enable AF for a limited number
- * of frames (it may terminate early if the delta becomes small enough).
+ * Whenever PDAF is available (and reports sufficiently high confidence),
+ * it is used for continuous feedback control of the lens position. When
+ * triggered in Auto mode, we enable the loop for a limited number of frames
+ * (it may terminate sooner if the phase becomes small). In CAF mode, the
+ * PDAF loop runs continuously. Very small lens movements are suppressed.
  *
  * When PDAF confidence is low (due e.g. to low contrast or extreme defocus)
  * or PDAF data are absent, fall back to CDAF with a programmed scan pattern.
- * A coarse and fine scan are performed, using ISP's CDAF focus FoM to
- * estimate the lens position with peak contrast. This is slower due to
- * extra latency in the ISP, and requires a settling time between steps.
+ * A coarse and fine scan are performed, using the ISP's CDAF contrast FoM
+ * to estimate the lens position with peak contrast. (This is slower due to
+ * extra latency in the ISP, and requires a settling time between steps.)
+ * The scan may terminate early if PDAF recovers and allows the zero-phase
+ * lens position to be interpolated.
  *
- * Some hysteresis is applied to the switch between PDAF and CDAF, to avoid
- * "nuisance" scans. During each interval where PDAF is not working, only
- * ONE scan will be performed; CAF cannot track objects using CDAF alone.
+ * In CAF mode, the fallback to a CDAF scan is triggered when PDAF fails to
+ * report high confidence and a configurable number of frames have elapsed
+ * since the last image change since either PDAF was working or a previous
+ * scan found peak contrast. Image changes are detected using both contrast
+ * and AWB statistics (within the AF window[s]).
  *
+ * IR lighting can interfere with the correct operation of PDAF, so we
+ * optionally try to detect it (from AWB statistics).
  */
 
 namespace RPiController {
@@ -54,7 +62,9 @@ public:
 	void setWindows(libcamera::Span<libcamera::Rectangle const> const &wins) override;
 	void setMode(AfMode mode) override;
 	AfMode getMode() const override;
-	bool setLensPosition(double dioptres, int32_t *hwpos) override;
+	double getDefaultLensPosition() const override;
+	void getLensLimits(double &min, double &max) const override;
+	bool setLensPosition(double dioptres, int32_t *hwpos, bool force) override;
 	std::optional<double> getLensPosition() const override;
 	void triggerScan() override;
 	void cancelScan() override;
@@ -65,7 +75,8 @@ private:
 		Idle = 0,
 		Trigger,
 		Pdaf,
-		Coarse,
+		Coarse1,
+		Coarse2,
 		Fine,
 		Settle
 	};
@@ -80,9 +91,11 @@ private:
 	};
 
 	struct SpeedDependentParams {
-		double stepCoarse;		/* used for scans */
-		double stepFine;		/* used for scans */
+		double stepCoarse;		/* in dioptres; used for scans */
+		double stepFine;		/* in dioptres; used for scans */
 		double contrastRatio;		/* used for scan termination and reporting */
+		double retriggerRatio;          /* contrast and RGB ratio for re-triggering */
+		uint32_t retriggerDelay;        /* frames of stability before re-triggering */
 		double pdafGain;		/* coefficient for PDAF feedback loop */
 		double pdafSquelch;		/* PDAF stability parameter (device-specific) */
 		double maxSlew;			/* limit for lens movement per frame */
@@ -101,6 +114,7 @@ private:
 		uint32_t confThresh;	       	/* PDAF confidence cell min (sensor-specific) */
 		uint32_t confClip;	       	/* PDAF confidence cell max (sensor-specific) */
 		uint32_t skipFrames;	       	/* frames to skip at start or modeswitch */
+		bool checkForIR;                /* Set this if PDAF is unreliable in IR light */
 		libcamera::ipa::Pwl map;       	/* converts dioptres -> lens driver position */
 
 		CfgParams();
@@ -129,6 +143,7 @@ private:
 	void invalidateWeights();
 	bool getPhase(PdafRegions const &regions, double &phase, double &conf);
 	double getContrast(const FocusRegions &focusStats);
+	bool getAverageAndTestIr(const RgbyRegions &awbStats, double rgb[3]);
 	void doPDAF(double phase, double conf);
 	bool earlyTerminationByPhase(double phase);
 	double findPeak(unsigned index) const;
@@ -150,15 +165,20 @@ private:
 	bool useWindows_;
 	RegionWeights phaseWeights_;
 	RegionWeights contrastWeights_;
+	RegionWeights awbWeights_;
 
 	/* Working state. */
 	ScanState scanState_;
-	bool initted_;
+	bool initted_, irFlag_;
 	double ftarget_, fsmooth_;
-	double prevContrast_;
+	double prevContrast_, oldSceneContrast_;
+	double prevAverage_[3], oldSceneAverage_[3];
+	double prevPhase_;
 	unsigned skipCount_, stepCount_, dropCount_;
+	unsigned sameSignCount_;
+	unsigned sceneChangeCount_;
 	unsigned scanMaxIndex_;
-	double scanMaxContrast_, scanMinContrast_;
+	double scanMaxContrast_, scanMinContrast_, scanStep_;
 	std::vector<ScanRecord> scanData_;
 	AfState reportState_;
 };
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/agc.cpp 0.5.2-2/src/ipa/rpi/controller/rpi/agc.cpp
--- 0.5.0-1/src/ipa/rpi/controller/rpi/agc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/agc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -184,14 +184,14 @@ void Agc::setFixedExposureTime(unsigned
 	channelData_[channelIndex].channel.setFixedExposureTime(fixedExposureTime);
 }
 
-void Agc::setFixedAnalogueGain(unsigned int channelIndex, double fixedAnalogueGain)
+void Agc::setFixedGain(unsigned int channelIndex, double fixedGain)
 {
 	if (checkChannel(channelIndex))
 		return;
 
-	LOG(RPiAgc, Debug) << "setFixedAnalogueGain " << fixedAnalogueGain
+	LOG(RPiAgc, Debug) << "setFixedGain " << fixedGain
 			   << " for channel " << channelIndex;
-	channelData_[channelIndex].channel.setFixedAnalogueGain(fixedAnalogueGain);
+	channelData_[channelIndex].channel.setFixedGain(fixedGain);
 }
 
 void Agc::setMeteringMode(std::string const &meteringModeName)
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/agc.h 0.5.2-2/src/ipa/rpi/controller/rpi/agc.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/agc.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/agc.h	2025-08-07 13:46:17.000000000 +0000
@@ -35,8 +35,8 @@ public:
 	void setMaxExposureTime(libcamera::utils::Duration maxExposureTime) override;
 	void setFixedExposureTime(unsigned int channelIndex,
 				  libcamera::utils::Duration fixedExposureTime) override;
-	void setFixedAnalogueGain(unsigned int channelIndex,
-				  double fixedAnalogueGain) override;
+	void setFixedGain(unsigned int channelIndex,
+			  double fixedGain) override;
 	void setMeteringMode(std::string const &meteringModeName) override;
 	void setExposureMode(std::string const &exposureModeName) override;
 	void setConstraintMode(std::string const &contraintModeName) override;
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/agc_channel.cpp 0.5.2-2/src/ipa/rpi/controller/rpi/agc_channel.cpp
--- 0.5.0-1/src/ipa/rpi/controller/rpi/agc_channel.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/agc_channel.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -260,11 +260,13 @@ int AgcConfig::read(const libcamera::Yam
 
 	desaturate = params["desaturate"].get<int>(1);
 
+	maxDigitalGain = params["max_digital_gain"].get<double>(4.0);
+
 	return 0;
 }
 
 AgcChannel::ExposureValues::ExposureValues()
-	: exposureTime(0s), analogueGain(0),
+	: exposureTime(0s), analogueGain(0), digitalGain(0),
 	  totalExposure(0s), totalExposureNoDG(0s)
 {
 }
@@ -273,7 +275,7 @@ AgcChannel::AgcChannel()
 	: meteringMode_(nullptr), exposureMode_(nullptr), constraintMode_(nullptr),
 	  frameCount_(0), lockCount_(0),
 	  lastTargetExposure_(0s), ev_(1.0), flickerPeriod_(0s),
-	  maxExposureTime_(0s), fixedExposureTime_(0s), fixedAnalogueGain_(0.0)
+	  maxExposureTime_(0s), fixedExposureTime_(0s), fixedGain_(0.0)
 {
 	/* Set AWB default values in case early frames have no updates in metadata. */
 	awb_.gainR = 1.0;
@@ -337,17 +339,17 @@ bool AgcChannel::autoExposureEnabled() c
 
 void AgcChannel::disableAutoGain()
 {
-	fixedAnalogueGain_ = status_.analogueGain;
+	fixedGain_ = status_.analogueGain;
 }
 
 void AgcChannel::enableAutoGain()
 {
-	fixedAnalogueGain_ = 0;
+	fixedGain_ = 0;
 }
 
 bool AgcChannel::autoGainEnabled() const
 {
-	return fixedAnalogueGain_ == 0;
+	return fixedGain_ == 0;
 }
 
 unsigned int AgcChannel::getConvergenceFrames() const
@@ -356,7 +358,7 @@ unsigned int AgcChannel::getConvergenceF
 	 * If exposure time and gain have been explicitly set, there is no
 	 * convergence to happen, so no need to drop any frames - return zero.
 	 */
-	if (fixedExposureTime_ && fixedAnalogueGain_)
+	if (fixedExposureTime_ && fixedGain_)
 		return 0;
 	else
 		return config_.convergenceFrames;
@@ -396,11 +398,9 @@ void AgcChannel::setFixedExposureTime(Du
 	status_.exposureTime = limitExposureTime(fixedExposureTime_);
 }
 
-void AgcChannel::setFixedAnalogueGain(double fixedAnalogueGain)
+void AgcChannel::setFixedGain(double fixedGain)
 {
-	fixedAnalogueGain_ = fixedAnalogueGain;
-	/* Set this in case someone calls disableAuto() straight after. */
-	status_.analogueGain = limitGain(fixedAnalogueGain);
+	fixedGain_ = fixedGain;
 }
 
 void AgcChannel::setMeteringMode(std::string const &meteringModeName)
@@ -434,22 +434,10 @@ void AgcChannel::switchMode(CameraMode c
 	mode_ = cameraMode;
 
 	Duration fixedExposureTime = limitExposureTime(fixedExposureTime_);
-	if (fixedExposureTime && fixedAnalogueGain_) {
-		/* We're going to reset the algorithm here with these fixed values. */
-		fetchAwbStatus(metadata);
-		double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
-		ASSERT(minColourGain != 0.0);
-
-		/* This is the equivalent of computeTargetExposure and applyDigitalGain. */
-		target_.totalExposureNoDG = fixedExposureTime_ * fixedAnalogueGain_;
-		target_.totalExposure = target_.totalExposureNoDG / minColourGain;
-
-		/* Equivalent of filterExposure. This resets any "history". */
-		filtered_ = target_;
-
-		/* Equivalent of divideUpExposure. */
-		filtered_.exposureTime = fixedExposureTime;
-		filtered_.analogueGain = fixedAnalogueGain_;
+	double fixedGain = limitGain(fixedGain_);
+	if (fixedExposureTime && fixedGain_) {
+		filtered_.totalExposureNoDG = fixedExposureTime * fixedGain;
+		filtered_.totalExposure = filtered_.totalExposureNoDG;
 	} else if (status_.totalExposureValue) {
 		/*
 		 * On a mode switch, various things could happen:
@@ -462,12 +450,8 @@ void AgcChannel::switchMode(CameraMode c
 		 */
 
 		double ratio = lastSensitivity / cameraMode.sensitivity;
-		target_.totalExposureNoDG *= ratio;
-		target_.totalExposure *= ratio;
-		filtered_.totalExposureNoDG *= ratio;
 		filtered_.totalExposure *= ratio;
-
-		divideUpExposure();
+		filtered_.totalExposureNoDG = filtered_.totalExposure;
 	} else {
 		/*
 		 * We come through here on startup, when at least one of the
@@ -477,54 +461,30 @@ void AgcChannel::switchMode(CameraMode c
 		 * weren't set.
 		 */
 
-		/* Equivalent of divideUpExposure. */
-		filtered_.exposureTime = fixedExposureTime ? fixedExposureTime : config_.defaultExposureTime;
-		filtered_.analogueGain = fixedAnalogueGain_ ? fixedAnalogueGain_ : config_.defaultAnalogueGain;
+		Duration exposureTime = fixedExposureTime ? fixedExposureTime : config_.defaultExposureTime;
+		double gain = fixedGain ? fixedGain : config_.defaultAnalogueGain;
+		filtered_.totalExposure = exposureTime * gain;
+		filtered_.totalExposureNoDG = filtered_.totalExposure;
 	}
 
+	/* Setting target_ to filtered_ removes any history from before the mode switch. */
+	target_ = filtered_;
+	divideUpExposure();
+
 	writeAndFinish(metadata, false);
 }
 
 void AgcChannel::prepare(Metadata *imageMetadata)
 {
-	Duration totalExposureValue = status_.totalExposureValue;
-	AgcStatus delayedStatus;
+	DeviceStatus deviceStatus;
 	AgcPrepareStatus prepareStatus;
 
-	/* Fetch the AWB status now because AWB also sets it in the prepare method. */
-	fetchAwbStatus(imageMetadata);
-
-	if (!imageMetadata->get("agc.delayed_status", delayedStatus))
-		totalExposureValue = delayedStatus.totalExposureValue;
-
-	prepareStatus.digitalGain = 1.0;
 	prepareStatus.locked = false;
 
-	if (status_.totalExposureValue) {
-		/* Process has run, so we have meaningful values. */
-		DeviceStatus deviceStatus;
-		if (imageMetadata->get("device.status", deviceStatus) == 0) {
-			Duration actualExposure = deviceStatus.exposureTime *
-						  deviceStatus.analogueGain;
-			if (actualExposure) {
-				double digitalGain = totalExposureValue / actualExposure;
-				LOG(RPiAgc, Debug) << "Want total exposure " << totalExposureValue;
-				/*
-				 * Never ask for a gain < 1.0, and also impose
-				 * some upper limit. Make it customisable?
-				 */
-				prepareStatus.digitalGain = std::max(1.0, std::min(digitalGain, 4.0));
-				LOG(RPiAgc, Debug) << "Actual exposure " << actualExposure;
-				LOG(RPiAgc, Debug) << "Use digitalGain " << prepareStatus.digitalGain;
-				LOG(RPiAgc, Debug) << "Effective exposure "
-						   << actualExposure * prepareStatus.digitalGain;
-				/* Decide whether AEC/AGC has converged. */
-				prepareStatus.locked = updateLockStatus(deviceStatus);
-			}
-		} else
-			LOG(RPiAgc, Warning) << "AgcChannel: no device metadata";
-		imageMetadata->set("agc.prepare_status", prepareStatus);
-	}
+	if (!imageMetadata->get("device.status", deviceStatus))
+		prepareStatus.locked = updateLockStatus(deviceStatus);
+
+	imageMetadata->set("agc.prepare_status", prepareStatus);
 }
 
 void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus,
@@ -610,11 +570,11 @@ void AgcChannel::housekeepConfig()
 	/* First fetch all the up-to-date settings, so no one else has to do it. */
 	status_.ev = ev_;
 	status_.fixedExposureTime = limitExposureTime(fixedExposureTime_);
-	status_.fixedAnalogueGain = fixedAnalogueGain_;
+	status_.fixedGain = limitGain(fixedGain_);
 	status_.flickerPeriod = flickerPeriod_;
 	LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedExposureTime "
-			   << status_.fixedExposureTime << " fixedAnalogueGain "
-			   << status_.fixedAnalogueGain;
+			   << status_.fixedExposureTime << " fixedGain "
+			   << status_.fixedGain;
 	/*
 	 * Make sure the "mode" pointers point to the up-to-date things, if
 	 * they've changed.
@@ -661,6 +621,9 @@ void AgcChannel::fetchCurrentExposure(De
 	current_.analogueGain = deviceStatus.analogueGain;
 	current_.totalExposure = 0s; /* this value is unused */
 	current_.totalExposureNoDG = current_.exposureTime * current_.analogueGain;
+	LOG(RPiAgc, Debug) << "Current frame: exposure time " << current_.exposureTime
+			   << " ag " << current_.analogueGain
+			   << " (total " << current_.totalExposureNoDG << ")";
 }
 
 void AgcChannel::fetchAwbStatus(Metadata *imageMetadata)
@@ -716,8 +679,13 @@ static double computeInitialY(Statistics
 	}
 
 	/* Factor in the AWB correction if needed. */
-	if (stats->agcStatsPos == Statistics::AgcStatsPos::PreWb)
-		sum *= RGB<double>{ { awb.gainR, awb.gainR, awb.gainB } };
+	if (stats->agcStatsPos == Statistics::AgcStatsPos::PreWb) {
+		double minColourGain = std::min({ awb.gainR, awb.gainG, awb.gainB, 1.0 });
+		minColourGain = std::max(minColourGain, 1.0);
+		RGB<double> colourGains{ { awb.gainR, awb.gainG, awb.gainB } };
+		colourGains /= minColourGain;
+		sum *= colourGains;
+	}
 
 	double ySum = ipa::rec601LuminanceFromRGB(sum);
 
@@ -796,17 +764,9 @@ void AgcChannel::computeGain(StatisticsP
 
 void AgcChannel::computeTargetExposure(double gain)
 {
-	if (status_.fixedExposureTime && status_.fixedAnalogueGain) {
-		/*
-		 * When analogue gain and exposure time are both fixed, we need
-		 * to drive the total exposure so that we end up with a digital
-		 * gain of at least 1/minColourGain. Otherwise we'd desaturate
-		 * channels causing white to go cyan or magenta.
-		 */
-		double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
-		ASSERT(minColourGain != 0.0);
+	if (status_.fixedExposureTime && status_.fixedGain) {
 		target_.totalExposure =
-			status_.fixedExposureTime * status_.fixedAnalogueGain / minColourGain;
+			status_.fixedExposureTime * status_.fixedGain;
 	} else {
 		/*
 		 * The statistics reflect the image without digital gain, so the final
@@ -815,14 +775,12 @@ void AgcChannel::computeTargetExposure(d
 		target_.totalExposure = current_.totalExposureNoDG * gain;
 		/* The final target exposure is also limited to what the exposure mode allows. */
 		Duration maxExposureTime = status_.fixedExposureTime
-					 ? status_.fixedExposureTime
-					 : exposureMode_->exposureTime.back();
+					      ? status_.fixedExposureTime
+					      : exposureMode_->exposureTime.back();
 		maxExposureTime = limitExposureTime(maxExposureTime);
-		Duration maxTotalExposure =
-			maxExposureTime *
-			(status_.fixedAnalogueGain != 0.0
-				 ? status_.fixedAnalogueGain
-				 : exposureMode_->gain.back());
+		double maxGain = status_.fixedGain ? status_.fixedGain : exposureMode_->gain.back();
+		maxGain = limitGain(maxGain);
+		Duration maxTotalExposure = maxExposureTime * maxGain;
 		target_.totalExposure = std::min(target_.totalExposure, maxTotalExposure);
 	}
 	LOG(RPiAgc, Debug) << "Target totalExposure " << target_.totalExposure;
@@ -831,8 +789,6 @@ void AgcChannel::computeTargetExposure(d
 bool AgcChannel::applyChannelConstraints(const AgcChannelTotalExposures &channelTotalExposures)
 {
 	bool channelBound = false;
-	LOG(RPiAgc, Debug)
-		<< "Total exposure before channel constraints " << filtered_.totalExposure;
 
 	for (const auto &constraint : config_.channelConstraints) {
 		LOG(RPiAgc, Debug)
@@ -867,15 +823,8 @@ bool AgcChannel::applyChannelConstraints
 
 bool AgcChannel::applyDigitalGain(double gain, double targetY, bool channelBound)
 {
-	double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
-	ASSERT(minColourGain != 0.0);
-	double dg = 1.0 / minColourGain;
-	/*
-	 * I think this pipeline subtracts black level and rescales before we
-	 * get the stats, so no need to worry about it.
-	 */
-	LOG(RPiAgc, Debug) << "after AWB, target dg " << dg << " gain " << gain
-			   << " target_Y " << targetY;
+	filtered_.totalExposureNoDG = filtered_.totalExposure;
+
 	/*
 	 * Finally, if we're trying to reduce exposure but the target_Y is
 	 * "close" to 1.0, then the gain computed for that constraint will be
@@ -885,15 +834,14 @@ bool AgcChannel::applyDigitalGain(double
 	 * quickly (and we then approach the correct value more quickly from
 	 * below).
 	 */
-	bool desaturate = false;
-	if (config_.desaturate)
-		desaturate = !channelBound &&
-			     targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
-	if (desaturate)
-		dg /= config_.fastReduceThreshold;
-	LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate;
-	filtered_.totalExposureNoDG = filtered_.totalExposure / dg;
-	LOG(RPiAgc, Debug) << "Target totalExposureNoDG " << filtered_.totalExposureNoDG;
+	bool desaturate = config_.desaturate && !channelBound &&
+			  targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
+
+	if (desaturate) {
+		filtered_.totalExposureNoDG *= config_.fastReduceThreshold;
+		LOG(RPiAgc, Debug) << "Desaturating, exposure no dg " << filtered_.totalExposureNoDG;
+	}
+
 	return desaturate;
 }
 
@@ -908,7 +856,7 @@ void AgcChannel::filterExposure()
 	 * region, because we want to reflect any user exposure/gain updates,
 	 * however small.
 	 */
-	if ((status_.fixedExposureTime && status_.fixedAnalogueGain) ||
+	if ((status_.fixedExposureTime && status_.fixedGain) ||
 	    frameCount_ <= config_.startupFrames) {
 		speed = 1.0;
 		stableRegion = 0.0;
@@ -929,8 +877,7 @@ void AgcChannel::filterExposure()
 		filtered_.totalExposure = speed * target_.totalExposure +
 					  filtered_.totalExposure * (1.0 - speed);
 	}
-	LOG(RPiAgc, Debug) << "After filtering, totalExposure " << filtered_.totalExposure
-			   << " no dg " << filtered_.totalExposureNoDG;
+	LOG(RPiAgc, Debug) << "After filtering, totalExposure " << filtered_.totalExposure;
 }
 
 void AgcChannel::divideUpExposure()
@@ -942,63 +889,70 @@ void AgcChannel::divideUpExposure()
 	 */
 	Duration exposureValue = filtered_.totalExposureNoDG;
 	Duration exposureTime;
-	double analogueGain;
+	double gain;
 	exposureTime = status_.fixedExposureTime ? status_.fixedExposureTime
 						 : exposureMode_->exposureTime[0];
 	exposureTime = limitExposureTime(exposureTime);
-	analogueGain = status_.fixedAnalogueGain != 0.0 ? status_.fixedAnalogueGain
-							: exposureMode_->gain[0];
-	analogueGain = limitGain(analogueGain);
-	if (exposureTime * analogueGain < exposureValue) {
+	gain = status_.fixedGain != 0.0 ? status_.fixedGain
+		: exposureMode_->gain[0];
+	gain = limitGain(gain);
+	if (exposureTime * gain < exposureValue) {
 		for (unsigned int stage = 1;
 		     stage < exposureMode_->gain.size(); stage++) {
 			if (!status_.fixedExposureTime) {
 				Duration stageExposureTime =
 					limitExposureTime(exposureMode_->exposureTime[stage]);
-				if (stageExposureTime * analogueGain >= exposureValue) {
-					exposureTime = exposureValue / analogueGain;
+				if (stageExposureTime * gain >= exposureValue) {
+					exposureTime = exposureValue / gain;
 					break;
 				}
 				exposureTime = stageExposureTime;
 			}
-			if (status_.fixedAnalogueGain == 0.0) {
+			if (status_.fixedGain == 0.0) {
 				if (exposureMode_->gain[stage] * exposureTime >= exposureValue) {
-					analogueGain = exposureValue / exposureTime;
+					gain = exposureValue / exposureTime;
 					break;
 				}
-				analogueGain = exposureMode_->gain[stage];
-				analogueGain = limitGain(analogueGain);
+				gain = exposureMode_->gain[stage];
+				gain = limitGain(gain);
 			}
 		}
 	}
-	LOG(RPiAgc, Debug)
-		<< "Divided up exposure time and gain are " << exposureTime
-		<< " and " << analogueGain;
+
 	/*
 	 * Finally adjust exposure time for flicker avoidance (require both
 	 * exposure time and gain not to be fixed).
 	 */
-	if (!status_.fixedExposureTime && !status_.fixedAnalogueGain &&
+	if (!status_.fixedExposureTime && !status_.fixedGain &&
 	    status_.flickerPeriod) {
 		int flickerPeriods = exposureTime / status_.flickerPeriod;
 		if (flickerPeriods) {
 			Duration newExposureTime = flickerPeriods * status_.flickerPeriod;
-			analogueGain *= exposureTime / newExposureTime;
-			/*
-			 * We should still not allow the ag to go over the
-			 * largest value in the exposure mode. Note that this
-			 * may force more of the total exposure into the digital
-			 * gain as a side-effect.
-			 */
-			analogueGain = std::min(analogueGain, exposureMode_->gain.back());
-			analogueGain = limitGain(analogueGain);
+			gain *= exposureTime / newExposureTime;
 			exposureTime = newExposureTime;
 		}
 		LOG(RPiAgc, Debug) << "After flicker avoidance, exposure time "
-				   << exposureTime << " gain " << analogueGain;
+				   << exposureTime << " gain " << gain;
 	}
+
+	/* Limit analogue gain to maximum allowed. */
+	double analogueGain = std::min(gain, mode_.maxAnalogueGain);
+
+	/* Finally work out the digital gain that we will need. */
+	filtered_.totalExposureNoDG = analogueGain * exposureTime;
+	double digitalGain = filtered_.totalExposure / filtered_.totalExposureNoDG;
+	/* Limit dg by what is allowed. */
+	digitalGain = std::min(digitalGain, config_.maxDigitalGain);
+	/* Update total exposure, in case the dg went down. */
+	filtered_.totalExposure = filtered_.totalExposureNoDG * digitalGain;
+
 	filtered_.exposureTime = exposureTime;
 	filtered_.analogueGain = analogueGain;
+	filtered_.digitalGain = digitalGain;
+	LOG(RPiAgc, Debug) << "DivideUpExposure: total " << filtered_.totalExposure
+			   << " no dg " << filtered_.totalExposureNoDG;
+	LOG(RPiAgc, Debug) << "DivideUpExposure: exp " << exposureTime
+			   << " ag " << gain << " dg " << digitalGain;
 }
 
 void AgcChannel::writeAndFinish(Metadata *imageMetadata, bool desaturate)
@@ -1007,6 +961,7 @@ void AgcChannel::writeAndFinish(Metadata
 	status_.targetExposureValue = desaturate ? 0s : target_.totalExposure;
 	status_.exposureTime = filtered_.exposureTime;
 	status_.analogueGain = filtered_.analogueGain;
+	status_.digitalGain = filtered_.digitalGain;
 	/*
 	 * Write to metadata as well, in case anyone wants to update the camera
 	 * immediately.
@@ -1014,8 +969,6 @@ void AgcChannel::writeAndFinish(Metadata
 	imageMetadata->set("agc.status", status_);
 	LOG(RPiAgc, Debug) << "Output written, total exposure requested is "
 			   << filtered_.totalExposure;
-	LOG(RPiAgc, Debug) << "Camera exposure update: exposure time " << filtered_.exposureTime
-			   << " analogue gain " << filtered_.analogueGain;
 }
 
 Duration AgcChannel::limitExposureTime(Duration exposureTime)
@@ -1044,6 +997,7 @@ double AgcChannel::limitGain(double gain
 	if (!gain)
 		return gain;
 
-	gain = std::max(gain, mode_.minAnalogueGain);
+	gain = std::clamp(gain, mode_.minAnalogueGain,
+			  mode_.maxAnalogueGain * config_.maxDigitalGain);
 	return gain;
 }
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/agc_channel.h 0.5.2-2/src/ipa/rpi/controller/rpi/agc_channel.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/agc_channel.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/agc_channel.h	2025-08-07 13:46:17.000000000 +0000
@@ -78,6 +78,7 @@ struct AgcConfig {
 	double defaultAnalogueGain;
 	double stableRegion;
 	bool desaturate;
+	double maxDigitalGain;
 };
 
 class AgcChannel
@@ -92,7 +93,7 @@ public:
 	void setFlickerPeriod(libcamera::utils::Duration flickerPeriod);
 	void setMaxExposureTime(libcamera::utils::Duration maxExposureTime);
 	void setFixedExposureTime(libcamera::utils::Duration fixedExposureTime);
-	void setFixedAnalogueGain(double fixedAnalogueGain);
+	void setFixedGain(double fixedGain);
 	void setMeteringMode(std::string const &meteringModeName);
 	void setExposureMode(std::string const &exposureModeName);
 	void setConstraintMode(std::string const &contraintModeName);
@@ -134,6 +135,7 @@ private:
 
 		libcamera::utils::Duration exposureTime;
 		double analogueGain;
+		double digitalGain;
 		libcamera::utils::Duration totalExposure;
 		libcamera::utils::Duration totalExposureNoDG; /* without digital gain */
 	};
@@ -152,7 +154,7 @@ private:
 	libcamera::utils::Duration flickerPeriod_;
 	libcamera::utils::Duration maxExposureTime_;
 	libcamera::utils::Duration fixedExposureTime_;
-	double fixedAnalogueGain_;
+	double fixedGain_;
 };
 
 } /* namespace RPiController */
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/awb.cpp 0.5.2-2/src/ipa/rpi/controller/rpi/awb.cpp
--- 0.5.0-1/src/ipa/rpi/controller/rpi/awb.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/awb.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -165,7 +165,6 @@ int AwbConfig::read(const libcamera::Yam
 			bayes = false;
 		}
 	}
-	fast = params[fast].get<int>(bayes); /* default to fast for Bayesian, otherwise slow */
 	whitepointR = params["whitepoint_r"].get<double>(0.0);
 	whitepointB = params["whitepoint_b"].get<double>(0.0);
 	if (bayes == false)
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/awb.h 0.5.2-2/src/ipa/rpi/controller/rpi/awb.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/awb.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/awb.h	2025-08-07 13:46:17.000000000 +0000
@@ -43,7 +43,6 @@ struct AwbConfig {
 	uint16_t startupFrames;
 	unsigned int convergenceFrames; /* approx number of frames to converge */
 	double speed; /* IIR filter speed applied to algorithm results */
-	bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */
 	libcamera::ipa::Pwl ctR; /* function maps CT to r (= R/G) */
 	libcamera::ipa::Pwl ctB; /* function maps CT to b (= B/G) */
 	libcamera::ipa::Pwl ctRInverse; /* inverse of ctR */
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/cac.h 0.5.2-2/src/ipa/rpi/controller/rpi/cac.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/cac.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/cac.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2023, Raspberry Pi Ltd
  *
- * cac.hpp - CAC control algorithm
+ * CAC control algorithm
  */
 #pragma once
 
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/denoise.h 0.5.2-2/src/ipa/rpi/controller/rpi/denoise.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/denoise.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/denoise.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2022, Raspberry Pi Ltd
  *
- * denoise.hpp - Denoise (spatial, colour, temporal) control algorithm
+ * Denoise (spatial, colour, temporal) control algorithm
  */
 #pragma once
 
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/saturation.h 0.5.2-2/src/ipa/rpi/controller/rpi/saturation.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/saturation.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/saturation.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2022, Raspberry Pi Ltd
  *
- * saturation.hpp - Saturation control algorithm
+ * Saturation control algorithm
  */
 #pragma once
 
diff -pruN 0.5.0-1/src/ipa/rpi/controller/rpi/tonemap.h 0.5.2-2/src/ipa/rpi/controller/rpi/tonemap.h
--- 0.5.0-1/src/ipa/rpi/controller/rpi/tonemap.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/controller/rpi/tonemap.h	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2022, Raspberry Pi Ltd
  *
- * tonemap.hpp - Tonemap control algorithm
+ * Tonemap control algorithm
  */
 #pragma once
 
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/imx283.json 0.5.2-2/src/ipa/rpi/pisp/data/imx283.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/imx283.json	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/imx283.json	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,1334 @@
+{
+    "version": 2.0,
+    "target": "pisp",
+    "algorithms": [
+        {
+            "rpi.black_level":
+            {
+                "black_level": 3200
+            }
+        },
+        {
+            "rpi.lux":
+            {
+                "reference_shutter_speed": 10857,
+                "reference_gain": 1.49,
+                "reference_aperture": 1.0,
+                "reference_lux": 1050,
+                "reference_Y": 13959
+            }
+        },
+        {
+            "rpi.dpc":
+            {
+                "strength": 1
+            }
+        },
+        {
+            "rpi.noise":
+            {
+                "reference_constant": 0,
+                "reference_slope": 2.147
+            }
+        },
+        {
+            "rpi.geq":
+            {
+                "offset": 249,
+                "slope": 0.02036
+            }
+        },
+        {
+            "rpi.denoise":
+            {
+                "normal":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 0.8,
+                        "threshold": 0.05
+                    }
+                },
+                "hdr":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                },
+                "night":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                }
+            }
+        },
+        {
+            "rpi.awb":
+            {
+                "priors": [
+                    {
+                        "lux": 0,
+                        "prior":
+                        [
+                            2000, 1.0,
+                            3000, 0.0,
+                            13000, 0.0
+                        ]
+                    },
+                    {
+                        "lux": 800,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            6000, 2.0,
+                            13000, 2.0
+                        ]
+                    },
+                    {
+                        "lux": 1500,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            4000, 1.0,
+                            6000, 6.0,
+                            6500, 7.0,
+                            7000, 1.0,
+                            13000, 1.0
+                        ]
+                    }
+                ],
+                "modes":
+                {
+                    "auto":
+                    {
+                        "lo": 2500,
+                        "hi": 7700
+                    },
+                    "incandescent":
+                    {
+                        "lo": 2500,
+                        "hi": 3000
+                    },
+                    "tungsten":
+                    {
+                        "lo": 3000,
+                        "hi": 3500
+                    },
+                    "fluorescent":
+                    {
+                        "lo": 4000,
+                        "hi": 4700
+                    },
+                    "indoor":
+                    {
+                        "lo": 3000,
+                        "hi": 5000
+                    },
+                    "daylight":
+                    {
+                        "lo": 5500,
+                        "hi": 6500
+                    },
+                    "cloudy":
+                    {
+                        "lo": 6000,
+                        "hi": 6800,
+                    }
+                },
+                "bayes": 1,
+                "ct_curve":
+                [
+                    2500.0, 0.9437, 0.2866,
+                    2820.0, 0.8496, 0.3541,
+                    2830.0, 0.8309, 0.3681,
+                    2885.0, 0.8183, 0.3778,
+                    3601.0, 0.6946, 0.4786,
+                    3615.0, 0.6929, 0.4801,
+                    3622.0, 0.6905, 0.4821,
+                    4345.0, 0.6012, 0.5628,
+                    4410.0, 0.5956, 0.5682,
+                    4486.0, 0.5892, 0.5743,
+                    4576.0, 0.5794, 0.5837,
+                    5672.0, 0.5232, 0.6392,
+                    5710.0, 0.5188, 0.6436,
+                    6850.0, 0.4862, 0.6773
+                ],
+                "sensitivity_r": 1.0,
+                "sensitivity_b": 1.0,
+                "transverse_pos": 0.02634,
+                "transverse_neg": 0.02491
+            }
+        },
+        {
+            "rpi.agc":
+            {
+                "channels": [
+                    {
+                        "comment": "Channel 0 is normal AGC",
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 66666 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0, 12.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.5,
+                                    "y_target":
+                                    [
+                                        0, 0.17,
+                                        1000, 0.17
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 1 is the HDR short channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 2 is the HDR long channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [  ],
+                            "highlight": [  ],
+                            "shadows": [  ]
+                        },
+                        "channel_constraints": [
+                            {
+                                "bound": "UPPER",
+                                "channel": 4,
+                                "factor": 8
+                            },
+                            {
+                                "bound": "LOWER",
+                                "channel": 4,
+                                "factor": 2
+                            }
+                        ],
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 3 is the night mode channel",
+                        "base_ev": 0.33,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 66666 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 33333 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 66666, 120000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 4.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.16,
+                            10000, 0.17
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.alsc":
+            {
+                "omega": 1.3,
+                "n_iter": 100,
+                "luminance_strength": 0.8,
+                "calibrations_Cr": [
+                    {
+                        "ct": 2940,
+                        "table":
+                        [
+                            1.023, 1.024, 1.028, 1.032, 1.034, 1.036, 1.037, 1.038, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.036, 1.035, 1.033, 1.031, 1.026, 1.022, 1.019, 1.014, 1.012,
+                            1.021, 1.023, 1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.027, 1.025, 1.021, 1.016, 1.012, 1.009,
+                            1.019, 1.022, 1.024, 1.026, 1.028, 1.029, 1.029, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.027, 1.026, 1.024, 1.021, 1.016, 1.011, 1.009,
+                            1.021, 1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.024, 1.022, 1.018, 1.012, 1.011,
+                            1.021, 1.024, 1.025, 1.027, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.025, 1.023, 1.019, 1.014, 1.011,
+                            1.023, 1.025, 1.026, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.027, 1.025, 1.023, 1.021, 1.015, 1.011,
+                            1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.028, 1.026, 1.023, 1.021, 1.015, 1.012,
+                            1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.028, 1.026, 1.024, 1.021, 1.016, 1.012,
+                            1.023, 1.025, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.028, 1.027, 1.025, 1.022, 1.017, 1.014,
+                            1.024, 1.025, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.025, 1.023, 1.018, 1.015,
+                            1.024, 1.026, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.025, 1.025, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+                            1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+                            1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+                            1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.028, 1.026, 1.023, 1.018, 1.016,
+                            1.025, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+                            1.025, 1.027, 1.028, 1.028, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.016,
+                            1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.015,
+                            1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.025, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.016,
+                            1.025, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.028, 1.027, 1.025, 1.022, 1.017, 1.015,
+                            1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.027, 1.025, 1.022, 1.017, 1.015,
+                            1.025, 1.027, 1.028, 1.029, 1.029, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.025, 1.022, 1.017, 1.014,
+                            1.025, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.028, 1.028, 1.029, 1.028, 1.026, 1.024, 1.021, 1.016, 1.014,
+                            1.025, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.026, 1.023, 1.021, 1.014, 1.012,
+                            1.024, 1.027, 1.028, 1.029, 1.031, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.019, 1.014, 1.011,
+                            1.024, 1.027, 1.028, 1.029, 1.031, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.025, 1.022, 1.018, 1.012, 1.009,
+                            1.024, 1.026, 1.028, 1.029, 1.032, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.028, 1.027, 1.026, 1.025, 1.022, 1.018, 1.011, 1.009,
+                            1.023, 1.026, 1.028, 1.029, 1.032, 1.033, 1.033, 1.033, 1.033, 1.032, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.025, 1.024, 1.021, 1.016, 1.011, 1.008,
+                            1.022, 1.026, 1.028, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.033, 1.032, 1.032, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.028, 1.027, 1.025, 1.023, 1.021, 1.015, 1.009, 1.007,
+                            1.022, 1.025, 1.028, 1.031, 1.032, 1.033, 1.034, 1.034, 1.035, 1.034, 1.034, 1.033, 1.032, 1.032, 1.032, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.028, 1.027, 1.025, 1.023, 1.019, 1.014, 1.008, 1.006,
+                            1.021, 1.024, 1.027, 1.029, 1.031, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.032, 1.032, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.031, 1.029, 1.027, 1.026, 1.024, 1.021, 1.017, 1.013, 1.007, 1.004,
+                            1.019, 1.022, 1.026, 1.028, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.033, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.028, 1.026, 1.024, 1.021, 1.018, 1.015, 1.011, 1.004, 1.002,
+                            1.018, 1.021, 1.025, 1.027, 1.029, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.027, 1.025, 1.023, 1.019, 1.017, 1.013, 1.009, 1.002, 1.001
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.027, 1.032, 1.036, 1.039, 1.044, 1.047, 1.049, 1.051, 1.051, 1.052, 1.052, 1.051, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.053, 1.053, 1.053, 1.053, 1.052, 1.051, 1.049, 1.047, 1.043, 1.039, 1.036, 1.031, 1.021, 1.015,
+                            1.027, 1.029, 1.035, 1.038, 1.041, 1.042, 1.042, 1.042, 1.043, 1.043, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.045, 1.045, 1.044, 1.044, 1.043, 1.041, 1.037, 1.033, 1.026, 1.018, 1.015,
+                            1.025, 1.029, 1.033, 1.036, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.044, 1.043, 1.042, 1.039, 1.037, 1.033, 1.026, 1.018, 1.014,
+                            1.026, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.028, 1.021, 1.017,
+                            1.028, 1.032, 1.035, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.041, 1.039, 1.036, 1.031, 1.023, 1.019,
+                            1.029, 1.033, 1.036, 1.038, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.041, 1.039, 1.036, 1.032, 1.024, 1.019,
+                            1.029, 1.034, 1.036, 1.038, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.042, 1.039, 1.036, 1.033, 1.024, 1.021,
+                            1.029, 1.034, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.043, 1.043, 1.043, 1.042, 1.041, 1.037, 1.033, 1.026, 1.022,
+                            1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.027, 1.024,
+                            1.031, 1.033, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.028, 1.025,
+                            1.031, 1.034, 1.036, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.039, 1.035, 1.028, 1.025,
+                            1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+                            1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+                            1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+                            1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.025,
+                            1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+                            1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+                            1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+                            1.031, 1.034, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.027, 1.024,
+                            1.031, 1.034, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.027, 1.023,
+                            1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.026, 1.022,
+                            1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.038, 1.036, 1.032, 1.025, 1.021,
+                            1.029, 1.033, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.035, 1.031, 1.023, 1.019,
+                            1.029, 1.033, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.037, 1.034, 1.029, 1.021, 1.018,
+                            1.028, 1.032, 1.035, 1.038, 1.039, 1.039, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.037, 1.033, 1.028, 1.019, 1.017,
+                            1.028, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.036, 1.032, 1.027, 1.018, 1.015,
+                            1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.039, 1.038, 1.036, 1.035, 1.031, 1.025, 1.016, 1.013,
+                            1.025, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.034, 1.029, 1.022, 1.014, 1.011,
+                            1.023, 1.029, 1.034, 1.037, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.035, 1.033, 1.028, 1.021, 1.012, 1.009,
+                            1.022, 1.027, 1.032, 1.036, 1.038, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.035, 1.033, 1.031, 1.025, 1.018, 1.009, 1.005,
+                            1.019, 1.024, 1.029, 1.034, 1.036, 1.039, 1.039, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.033, 1.031, 1.026, 1.021, 1.014, 1.005, 1.003,
+                            1.017, 1.022, 1.028, 1.032, 1.036, 1.038, 1.039, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.034, 1.032, 1.028, 1.024, 1.019, 1.012, 1.003, 1.001
+                        ]
+                    },
+                    {
+                        "ct": 6000,
+                        "table":
+                        [
+                            1.021, 1.033, 1.041, 1.046, 1.051, 1.054, 1.057, 1.061, 1.064, 1.066, 1.068, 1.068, 1.068, 1.068, 1.068, 1.069, 1.069, 1.069, 1.069, 1.069, 1.068, 1.067, 1.064, 1.062, 1.058, 1.056, 1.052, 1.047, 1.041, 1.031, 1.019, 1.012,
+                            1.021, 1.029, 1.037, 1.043, 1.048, 1.053, 1.056, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.058, 1.054, 1.049, 1.044, 1.039, 1.029, 1.018, 1.012,
+                            1.023, 1.029, 1.037, 1.043, 1.048, 1.052, 1.055, 1.057, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.061, 1.059, 1.057, 1.054, 1.049, 1.044, 1.039, 1.029, 1.018, 1.015,
+                            1.025, 1.032, 1.039, 1.045, 1.049, 1.054, 1.057, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.058, 1.055, 1.051, 1.046, 1.041, 1.033, 1.022, 1.018,
+                            1.027, 1.035, 1.041, 1.046, 1.051, 1.054, 1.057, 1.058, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.061, 1.059, 1.056, 1.053, 1.048, 1.043, 1.036, 1.025, 1.019,
+                            1.029, 1.036, 1.043, 1.049, 1.052, 1.055, 1.057, 1.058, 1.059, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.061, 1.058, 1.054, 1.051, 1.045, 1.039, 1.027, 1.021,
+                            1.031, 1.039, 1.045, 1.049, 1.053, 1.056, 1.058, 1.059, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.046, 1.039, 1.028, 1.023,
+                            1.032, 1.039, 1.045, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.061, 1.057, 1.053, 1.048, 1.041, 1.031, 1.025,
+                            1.033, 1.039, 1.046, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.061, 1.062, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.058, 1.054, 1.049, 1.043, 1.032, 1.027,
+                            1.034, 1.041, 1.046, 1.051, 1.054, 1.056, 1.057, 1.059, 1.059, 1.061, 1.062, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.054, 1.049, 1.044, 1.034, 1.029,
+                            1.034, 1.041, 1.046, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.055, 1.051, 1.044, 1.035, 1.029,
+                            1.035, 1.041, 1.047, 1.051, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.045, 1.035, 1.029,
+                            1.036, 1.042, 1.047, 1.052, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+                            1.036, 1.043, 1.048, 1.052, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.056, 1.052, 1.046, 1.036, 1.031,
+                            1.037, 1.043, 1.048, 1.052, 1.055, 1.057, 1.059, 1.061, 1.061, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+                            1.037, 1.044, 1.048, 1.053, 1.055, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+                            1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+                            1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.059, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.065, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.045, 1.036, 1.031,
+                            1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.065, 1.065, 1.065, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.044, 1.035, 1.031,
+                            1.037, 1.043, 1.049, 1.053, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.055, 1.051, 1.044, 1.034, 1.029,
+                            1.035, 1.042, 1.048, 1.053, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.059, 1.055, 1.051, 1.043, 1.034, 1.027,
+                            1.034, 1.042, 1.048, 1.053, 1.056, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.058, 1.054, 1.049, 1.042, 1.032, 1.026,
+                            1.034, 1.041, 1.047, 1.053, 1.056, 1.058, 1.061, 1.061, 1.062, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.059, 1.057, 1.053, 1.048, 1.041, 1.031, 1.026,
+                            1.034, 1.041, 1.047, 1.052, 1.056, 1.059, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.058, 1.056, 1.052, 1.046, 1.038, 1.028, 1.024,
+                            1.032, 1.039, 1.045, 1.052, 1.056, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.057, 1.055, 1.051, 1.045, 1.037, 1.026, 1.022,
+                            1.032, 1.038, 1.044, 1.049, 1.054, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.057, 1.054, 1.049, 1.044, 1.036, 1.024, 1.019,
+                            1.029, 1.037, 1.044, 1.049, 1.054, 1.058, 1.059, 1.062, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.058, 1.055, 1.052, 1.048, 1.042, 1.033, 1.022, 1.017,
+                            1.027, 1.035, 1.042, 1.049, 1.054, 1.057, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.059, 1.057, 1.054, 1.051, 1.047, 1.039, 1.031, 1.019, 1.014,
+                            1.025, 1.033, 1.041, 1.047, 1.052, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.059, 1.056, 1.053, 1.049, 1.044, 1.037, 1.028, 1.016, 1.012,
+                            1.022, 1.031, 1.038, 1.045, 1.051, 1.053, 1.056, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.059, 1.057, 1.054, 1.051, 1.047, 1.041, 1.034, 1.025, 1.013, 1.007,
+                            1.019, 1.026, 1.035, 1.042, 1.047, 1.051, 1.054, 1.057, 1.059, 1.061, 1.062, 1.062, 1.062, 1.063, 1.063, 1.063, 1.062, 1.063, 1.062, 1.062, 1.061, 1.059, 1.057, 1.054, 1.051, 1.047, 1.042, 1.036, 1.029, 1.019, 1.007, 1.005,
+                            1.016, 1.024, 1.033, 1.041, 1.046, 1.049, 1.053, 1.055, 1.057, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.057, 1.055, 1.052, 1.049, 1.045, 1.039, 1.033, 1.027, 1.016, 1.005, 1.001
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 2940,
+                        "table":
+                        [
+                            1.001, 1.001, 1.014, 1.027, 1.038, 1.047, 1.051, 1.054, 1.058, 1.061, 1.062, 1.064, 1.066, 1.066, 1.066, 1.067, 1.067, 1.067, 1.067, 1.067, 1.067, 1.066, 1.064, 1.063, 1.061, 1.057, 1.055, 1.049, 1.043, 1.032, 1.026, 1.018,
+                            1.001, 1.013, 1.023, 1.032, 1.042, 1.049, 1.056, 1.063, 1.066, 1.069, 1.073, 1.076, 1.077, 1.078, 1.079, 1.079, 1.081, 1.081, 1.079, 1.079, 1.078, 1.077, 1.076, 1.073, 1.071, 1.066, 1.061, 1.053, 1.046, 1.037, 1.031, 1.026,
+                            1.008, 1.019, 1.031, 1.042, 1.049, 1.056, 1.063, 1.066, 1.069, 1.073, 1.076, 1.078, 1.078, 1.081, 1.082, 1.082, 1.082, 1.081, 1.081, 1.081, 1.079, 1.079, 1.077, 1.076, 1.073, 1.071, 1.066, 1.061, 1.053, 1.046, 1.035, 1.032,
+                            1.012, 1.023, 1.036, 1.045, 1.053, 1.059, 1.065, 1.068, 1.072, 1.075, 1.078, 1.079, 1.081, 1.083, 1.084, 1.084, 1.084, 1.084, 1.083, 1.083, 1.081, 1.081, 1.079, 1.078, 1.076, 1.073, 1.069, 1.063, 1.056, 1.049, 1.039, 1.034,
+                            1.015, 1.027, 1.038, 1.048, 1.056, 1.061, 1.067, 1.071, 1.074, 1.076, 1.079, 1.082, 1.084, 1.085, 1.086, 1.086, 1.086, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.075, 1.072, 1.066, 1.059, 1.051, 1.041, 1.038,
+                            1.018, 1.031, 1.041, 1.051, 1.058, 1.064, 1.069, 1.073, 1.075, 1.079, 1.082, 1.084, 1.085, 1.087, 1.088, 1.088, 1.088, 1.088, 1.087, 1.087, 1.086, 1.084, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.061, 1.052, 1.043, 1.038,
+                            1.021, 1.033, 1.045, 1.053, 1.059, 1.066, 1.071, 1.074, 1.078, 1.081, 1.084, 1.085, 1.087, 1.088, 1.089, 1.089, 1.089, 1.089, 1.089, 1.088, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.075, 1.071, 1.062, 1.054, 1.044, 1.039,
+                            1.023, 1.035, 1.046, 1.055, 1.062, 1.067, 1.072, 1.075, 1.079, 1.083, 1.085, 1.087, 1.088, 1.089, 1.091, 1.091, 1.091, 1.091, 1.091, 1.091, 1.089, 1.087, 1.086, 1.084, 1.082, 1.081, 1.076, 1.072, 1.065, 1.055, 1.045, 1.039,
+                            1.025, 1.036, 1.048, 1.056, 1.063, 1.069, 1.073, 1.077, 1.081, 1.085, 1.087, 1.088, 1.089, 1.091, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.088, 1.087, 1.085, 1.084, 1.081, 1.078, 1.074, 1.066, 1.057, 1.047, 1.042,
+                            1.028, 1.039, 1.051, 1.058, 1.065, 1.071, 1.075, 1.079, 1.083, 1.086, 1.088, 1.089, 1.091, 1.092, 1.093, 1.093, 1.093, 1.093, 1.093, 1.093, 1.092, 1.091, 1.088, 1.086, 1.084, 1.082, 1.079, 1.074, 1.067, 1.058, 1.047, 1.043,
+                            1.029, 1.041, 1.051, 1.059, 1.067, 1.071, 1.076, 1.081, 1.083, 1.087, 1.088, 1.091, 1.092, 1.093, 1.093, 1.094, 1.094, 1.094, 1.094, 1.094, 1.093, 1.092, 1.089, 1.087, 1.085, 1.083, 1.079, 1.074, 1.068, 1.059, 1.048, 1.044,
+                            1.031, 1.042, 1.053, 1.061, 1.068, 1.072, 1.077, 1.081, 1.084, 1.087, 1.089, 1.091, 1.093, 1.093, 1.094, 1.095, 1.095, 1.095, 1.095, 1.095, 1.094, 1.092, 1.089, 1.088, 1.086, 1.083, 1.081, 1.075, 1.069, 1.061, 1.049, 1.044,
+                            1.032, 1.043, 1.054, 1.062, 1.068, 1.073, 1.078, 1.081, 1.085, 1.088, 1.089, 1.092, 1.093, 1.094, 1.095, 1.095, 1.096, 1.096, 1.096, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.083, 1.081, 1.075, 1.069, 1.061, 1.049, 1.044,
+                            1.033, 1.044, 1.055, 1.062, 1.069, 1.073, 1.079, 1.082, 1.085, 1.088, 1.091, 1.093, 1.094, 1.095, 1.096, 1.096, 1.096, 1.097, 1.096, 1.096, 1.095, 1.093, 1.092, 1.089, 1.087, 1.084, 1.081, 1.076, 1.071, 1.062, 1.049, 1.046,
+                            1.034, 1.045, 1.056, 1.064, 1.069, 1.074, 1.079, 1.082, 1.086, 1.089, 1.091, 1.093, 1.094, 1.095, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.084, 1.081, 1.077, 1.071, 1.062, 1.052, 1.046,
+                            1.034, 1.046, 1.057, 1.064, 1.069, 1.074, 1.079, 1.083, 1.086, 1.089, 1.091, 1.093, 1.094, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.084, 1.081, 1.077, 1.071, 1.063, 1.052, 1.046,
+                            1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.089, 1.091, 1.093, 1.094, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.085, 1.081, 1.077, 1.071, 1.063, 1.052, 1.047,
+                            1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.088, 1.091, 1.092, 1.094, 1.095, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.091, 1.089, 1.087, 1.085, 1.082, 1.077, 1.071, 1.063, 1.052, 1.046,
+                            1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.088, 1.091, 1.092, 1.093, 1.095, 1.096, 1.096, 1.097, 1.097, 1.096, 1.095, 1.095, 1.093, 1.091, 1.089, 1.087, 1.084, 1.082, 1.077, 1.071, 1.062, 1.052, 1.047,
+                            1.035, 1.046, 1.056, 1.063, 1.069, 1.074, 1.077, 1.082, 1.085, 1.087, 1.091, 1.091, 1.093, 1.094, 1.095, 1.096, 1.096, 1.096, 1.095, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.085, 1.081, 1.077, 1.071, 1.062, 1.052, 1.047,
+                            1.033, 1.045, 1.055, 1.063, 1.068, 1.073, 1.077, 1.082, 1.084, 1.087, 1.089, 1.091, 1.093, 1.093, 1.094, 1.095, 1.095, 1.095, 1.095, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.084, 1.081, 1.076, 1.069, 1.062, 1.051, 1.047,
+                            1.032, 1.045, 1.054, 1.062, 1.067, 1.072, 1.077, 1.081, 1.083, 1.087, 1.089, 1.091, 1.092, 1.093, 1.093, 1.094, 1.094, 1.094, 1.094, 1.094, 1.093, 1.092, 1.091, 1.088, 1.086, 1.083, 1.079, 1.076, 1.069, 1.061, 1.051, 1.047,
+                            1.031, 1.044, 1.053, 1.061, 1.066, 1.072, 1.077, 1.081, 1.083, 1.087, 1.088, 1.091, 1.091, 1.093, 1.093, 1.093, 1.094, 1.094, 1.094, 1.093, 1.092, 1.091, 1.089, 1.088, 1.086, 1.083, 1.079, 1.075, 1.069, 1.059, 1.051, 1.046,
+                            1.031, 1.043, 1.051, 1.061, 1.065, 1.072, 1.076, 1.079, 1.083, 1.086, 1.087, 1.091, 1.091, 1.091, 1.092, 1.093, 1.093, 1.093, 1.093, 1.092, 1.091, 1.091, 1.089, 1.086, 1.085, 1.083, 1.079, 1.074, 1.066, 1.059, 1.051, 1.045,
+                            1.029, 1.039, 1.051, 1.059, 1.065, 1.071, 1.074, 1.077, 1.082, 1.085, 1.087, 1.088, 1.091, 1.091, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.089, 1.088, 1.085, 1.084, 1.082, 1.077, 1.071, 1.065, 1.058, 1.048, 1.043,
+                            1.028, 1.037, 1.049, 1.057, 1.063, 1.068, 1.073, 1.076, 1.081, 1.082, 1.085, 1.087, 1.089, 1.089, 1.091, 1.091, 1.091, 1.091, 1.091, 1.091, 1.089, 1.088, 1.086, 1.085, 1.083, 1.079, 1.076, 1.071, 1.064, 1.056, 1.046, 1.041,
+                            1.025, 1.035, 1.047, 1.055, 1.061, 1.067, 1.071, 1.075, 1.078, 1.081, 1.084, 1.086, 1.087, 1.088, 1.089, 1.089, 1.089, 1.089, 1.089, 1.089, 1.088, 1.087, 1.085, 1.084, 1.081, 1.078, 1.075, 1.069, 1.062, 1.054, 1.044, 1.039,
+                            1.023, 1.032, 1.044, 1.052, 1.059, 1.065, 1.069, 1.073, 1.076, 1.079, 1.081, 1.084, 1.085, 1.086, 1.087, 1.087, 1.088, 1.088, 1.088, 1.087, 1.087, 1.085, 1.084, 1.081, 1.079, 1.076, 1.072, 1.067, 1.059, 1.052, 1.041, 1.037,
+                            1.019, 1.028, 1.039, 1.048, 1.057, 1.062, 1.067, 1.071, 1.074, 1.077, 1.079, 1.081, 1.083, 1.084, 1.085, 1.085, 1.086, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.073, 1.069, 1.063, 1.056, 1.048, 1.038, 1.034,
+                            1.016, 1.025, 1.035, 1.045, 1.053, 1.059, 1.064, 1.069, 1.072, 1.076, 1.077, 1.079, 1.081, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.083, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.065, 1.059, 1.053, 1.044, 1.034, 1.029,
+                            1.011, 1.022, 1.032, 1.042, 1.051, 1.057, 1.062, 1.067, 1.069, 1.074, 1.076, 1.077, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.082, 1.081, 1.081, 1.078, 1.077, 1.075, 1.071, 1.066, 1.061, 1.056, 1.049, 1.041, 1.029, 1.028,
+                            1.008, 1.019, 1.031, 1.039, 1.048, 1.056, 1.061, 1.065, 1.069, 1.073, 1.075, 1.076, 1.078, 1.079, 1.079, 1.079, 1.081, 1.081, 1.081, 1.081, 1.078, 1.078, 1.076, 1.072, 1.069, 1.064, 1.058, 1.054, 1.047, 1.038, 1.029, 1.026
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.001, 1.005, 1.011, 1.015, 1.019, 1.021, 1.021, 1.022, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.022, 1.021, 1.018, 1.015, 1.011,
+                            1.005, 1.008, 1.014, 1.021, 1.025, 1.028, 1.031, 1.032, 1.034, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.038, 1.038, 1.038, 1.038, 1.037, 1.037, 1.036, 1.036, 1.035, 1.035, 1.034, 1.033, 1.029, 1.028, 1.023, 1.021, 1.021,
+                            1.009, 1.014, 1.021, 1.025, 1.028, 1.031, 1.032, 1.034, 1.035, 1.037, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.037, 1.036, 1.036, 1.036, 1.036, 1.034, 1.033, 1.029, 1.028, 1.023, 1.022,
+                            1.011, 1.016, 1.023, 1.027, 1.029, 1.031, 1.034, 1.035, 1.037, 1.038, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.037, 1.036, 1.034, 1.032, 1.029, 1.025, 1.022,
+                            1.013, 1.018, 1.024, 1.027, 1.031, 1.033, 1.035, 1.037, 1.038, 1.039, 1.041, 1.042, 1.042, 1.043, 1.043, 1.043, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.037, 1.035, 1.034, 1.029, 1.025, 1.023,
+                            1.014, 1.019, 1.025, 1.029, 1.031, 1.034, 1.037, 1.038, 1.039, 1.041, 1.042, 1.043, 1.043, 1.044, 1.044, 1.044, 1.043, 1.043, 1.043, 1.043, 1.042, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.037, 1.034, 1.029, 1.026, 1.024,
+                            1.016, 1.021, 1.027, 1.031, 1.033, 1.035, 1.038, 1.039, 1.041, 1.042, 1.043, 1.044, 1.044, 1.045, 1.045, 1.045, 1.044, 1.044, 1.044, 1.044, 1.043, 1.042, 1.042, 1.041, 1.041, 1.039, 1.038, 1.038, 1.035, 1.031, 1.027, 1.025,
+                            1.017, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.042, 1.043, 1.044, 1.045, 1.046, 1.046, 1.046, 1.046, 1.046, 1.046, 1.045, 1.045, 1.044, 1.043, 1.043, 1.042, 1.041, 1.041, 1.039, 1.038, 1.036, 1.032, 1.028, 1.025,
+                            1.018, 1.023, 1.028, 1.032, 1.035, 1.038, 1.041, 1.041, 1.043, 1.045, 1.046, 1.046, 1.046, 1.047, 1.047, 1.047, 1.047, 1.047, 1.047, 1.046, 1.045, 1.044, 1.044, 1.043, 1.042, 1.041, 1.041, 1.039, 1.037, 1.032, 1.028, 1.026,
+                            1.019, 1.024, 1.029, 1.033, 1.036, 1.039, 1.041, 1.042, 1.044, 1.046, 1.046, 1.047, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.047, 1.047, 1.045, 1.044, 1.044, 1.043, 1.042, 1.041, 1.039, 1.037, 1.033, 1.029, 1.027,
+                            1.021, 1.026, 1.029, 1.034, 1.037, 1.039, 1.042, 1.043, 1.045, 1.046, 1.047, 1.047, 1.048, 1.048, 1.049, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.047, 1.046, 1.044, 1.044, 1.043, 1.041, 1.039, 1.037, 1.034, 1.029, 1.027,
+                            1.022, 1.026, 1.032, 1.035, 1.038, 1.039, 1.043, 1.044, 1.045, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.044, 1.043, 1.041, 1.039, 1.038, 1.034, 1.029, 1.027,
+                            1.023, 1.027, 1.032, 1.035, 1.039, 1.041, 1.043, 1.044, 1.045, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.041, 1.039, 1.035, 1.029, 1.027,
+                            1.024, 1.028, 1.033, 1.036, 1.039, 1.041, 1.043, 1.045, 1.046, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.047, 1.046, 1.045, 1.043, 1.042, 1.041, 1.039, 1.035, 1.029, 1.029,
+                            1.024, 1.029, 1.034, 1.037, 1.039, 1.041, 1.044, 1.045, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.045, 1.044, 1.042, 1.041, 1.039, 1.036, 1.031, 1.029,
+                            1.024, 1.029, 1.034, 1.037, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.039, 1.037, 1.031, 1.029,
+                            1.025, 1.031, 1.035, 1.037, 1.039, 1.042, 1.045, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.045, 1.043, 1.042, 1.041, 1.037, 1.032, 1.031,
+                            1.026, 1.031, 1.035, 1.037, 1.039, 1.043, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.045, 1.044, 1.042, 1.041, 1.037, 1.032, 1.031,
+                            1.026, 1.031, 1.035, 1.038, 1.039, 1.043, 1.044, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.043, 1.041, 1.037, 1.032, 1.031,
+                            1.025, 1.031, 1.035, 1.038, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.045, 1.044, 1.043, 1.041, 1.037, 1.032, 1.031,
+                            1.024, 1.031, 1.035, 1.038, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.044, 1.043, 1.041, 1.037, 1.033, 1.031,
+                            1.025, 1.031, 1.035, 1.037, 1.039, 1.041, 1.044, 1.046, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.037, 1.033, 1.031,
+                            1.024, 1.031, 1.034, 1.037, 1.038, 1.041, 1.044, 1.045, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.037, 1.034, 1.032,
+                            1.024, 1.031, 1.034, 1.037, 1.038, 1.041, 1.043, 1.045, 1.046, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.038, 1.034, 1.032,
+                            1.024, 1.029, 1.034, 1.037, 1.038, 1.041, 1.043, 1.044, 1.046, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.048, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.038, 1.034, 1.031,
+                            1.024, 1.028, 1.034, 1.037, 1.038, 1.041, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.047, 1.045, 1.044, 1.043, 1.039, 1.038, 1.033, 1.031,
+                            1.022, 1.027, 1.034, 1.035, 1.038, 1.039, 1.041, 1.043, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.047, 1.047, 1.046, 1.045, 1.044, 1.043, 1.039, 1.037, 1.032, 1.029,
+                            1.022, 1.026, 1.032, 1.034, 1.037, 1.039, 1.041, 1.042, 1.044, 1.045, 1.046, 1.047, 1.048, 1.048, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.047, 1.047, 1.046, 1.045, 1.045, 1.044, 1.041, 1.039, 1.036, 1.031, 1.029,
+                            1.021, 1.025, 1.029, 1.033, 1.036, 1.038, 1.039, 1.041, 1.042, 1.044, 1.045, 1.046, 1.047, 1.047, 1.047, 1.047, 1.048, 1.048, 1.048, 1.047, 1.047, 1.047, 1.045, 1.045, 1.045, 1.044, 1.042, 1.039, 1.038, 1.034, 1.029, 1.028,
+                            1.019, 1.023, 1.028, 1.032, 1.035, 1.038, 1.039, 1.041, 1.042, 1.044, 1.044, 1.045, 1.045, 1.046, 1.046, 1.046, 1.047, 1.047, 1.047, 1.046, 1.046, 1.045, 1.045, 1.045, 1.044, 1.042, 1.041, 1.038, 1.037, 1.033, 1.029, 1.027,
+                            1.017, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.043, 1.044, 1.045, 1.045, 1.045, 1.045, 1.046, 1.046, 1.046, 1.046, 1.046, 1.045, 1.045, 1.045, 1.044, 1.043, 1.041, 1.039, 1.038, 1.035, 1.031, 1.029, 1.027,
+                            1.016, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.043, 1.043, 1.044, 1.045, 1.045, 1.045, 1.045, 1.046, 1.046, 1.046, 1.045, 1.045, 1.045, 1.045, 1.044, 1.043, 1.041, 1.038, 1.038, 1.035, 1.031, 1.029, 1.028
+                        ]
+                    },
+                    {
+                        "ct": 6000,
+                        "table":
+                        [
+                            1.001, 1.003, 1.005, 1.008, 1.009, 1.011, 1.011, 1.011, 1.011, 1.012, 1.012, 1.012, 1.013, 1.013, 1.013, 1.013, 1.013, 1.013, 1.013, 1.012, 1.012, 1.013, 1.013, 1.013, 1.015, 1.015, 1.015, 1.014, 1.014, 1.014, 1.013, 1.012,
+                            1.007, 1.008, 1.011, 1.015, 1.017, 1.019, 1.019, 1.021, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.022, 1.022, 1.022, 1.022, 1.023, 1.023, 1.023, 1.024, 1.024, 1.023, 1.023, 1.022, 1.022, 1.021,
+                            1.008, 1.011, 1.015, 1.017, 1.019, 1.021, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.023, 1.024, 1.024, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.024, 1.024, 1.024, 1.024, 1.023, 1.023, 1.022, 1.022,
+                            1.009, 1.012, 1.016, 1.018, 1.019, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.024, 1.024, 1.025, 1.024, 1.024, 1.024, 1.024, 1.023, 1.023, 1.023, 1.024, 1.024, 1.024, 1.025, 1.025, 1.024, 1.024, 1.023, 1.022, 1.021,
+                            1.009, 1.013, 1.016, 1.018, 1.019, 1.021, 1.021, 1.022, 1.023, 1.023, 1.024, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.025, 1.025, 1.025, 1.024, 1.023, 1.022, 1.021,
+                            1.011, 1.014, 1.017, 1.019, 1.019, 1.021, 1.022, 1.023, 1.023, 1.024, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.024, 1.022, 1.022, 1.022,
+                            1.011, 1.014, 1.018, 1.019, 1.021, 1.022, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.027, 1.026, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.022, 1.022, 1.022,
+                            1.012, 1.015, 1.018, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.023, 1.022, 1.021,
+                            1.012, 1.015, 1.018, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.023, 1.022, 1.021,
+                            1.013, 1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.021, 1.021,
+                            1.014, 1.017, 1.019, 1.021, 1.022, 1.023, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.021, 1.021,
+                            1.014, 1.017, 1.021, 1.021, 1.022, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.024, 1.021, 1.021,
+                            1.014, 1.017, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.024, 1.021, 1.021,
+                            1.015, 1.017, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.024, 1.022, 1.021,
+                            1.015, 1.018, 1.021, 1.022, 1.023, 1.024, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.025, 1.022, 1.021,
+                            1.016, 1.019, 1.022, 1.022, 1.023, 1.024, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.023, 1.022,
+                            1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.022,
+                            1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.022,
+                            1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.026, 1.024, 1.023,
+                            1.016, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.026, 1.024, 1.023,
+                            1.015, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.025, 1.024,
+                            1.016, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.025, 1.025,
+                            1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.026, 1.025,
+                            1.016, 1.019, 1.021, 1.023, 1.023, 1.024, 1.025, 1.025, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026,
+                            1.016, 1.019, 1.021, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026,
+                            1.016, 1.018, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026,
+                            1.016, 1.018, 1.021, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.026, 1.026,
+                            1.016, 1.017, 1.021, 1.022, 1.022, 1.023, 1.024, 1.024, 1.024, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026,
+                            1.015, 1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026,
+                            1.014, 1.016, 1.019, 1.021, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026,
+                            1.015, 1.016, 1.019, 1.021, 1.021, 1.023, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.028, 1.027, 1.027, 1.027,
+                            1.015, 1.017, 1.019, 1.019, 1.021, 1.023, 1.024, 1.025, 1.025, 1.025, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.029
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    1.319, 1.289, 1.238, 1.198, 1.169, 1.139, 1.116, 1.094, 1.082, 1.074, 1.068, 1.064, 1.062, 1.061, 1.061, 1.061, 1.061, 1.062, 1.064, 1.065, 1.068, 1.074, 1.084, 1.101, 1.123, 1.148, 1.181, 1.221, 1.277, 1.356, 1.468, 1.477,
+                    1.292, 1.255, 1.208, 1.171, 1.145, 1.121, 1.099, 1.086, 1.073, 1.064, 1.058, 1.053, 1.049, 1.047, 1.046, 1.045, 1.048, 1.049, 1.051, 1.053, 1.059, 1.065, 1.074, 1.089, 1.105, 1.125, 1.152, 1.189, 1.238, 1.309, 1.408, 1.468,
+                    1.269, 1.234, 1.189, 1.155, 1.136, 1.112, 1.086, 1.073, 1.064, 1.057, 1.052, 1.047, 1.043, 1.041, 1.041, 1.041, 1.043, 1.047, 1.048, 1.049, 1.053, 1.059, 1.065, 1.074, 1.089, 1.109, 1.136, 1.171, 1.216, 1.278, 1.369, 1.408,
+                    1.249, 1.216, 1.173, 1.142, 1.125, 1.105, 1.078, 1.066, 1.058, 1.053, 1.047, 1.043, 1.038, 1.036, 1.035, 1.035, 1.039, 1.042, 1.043, 1.044, 1.048, 1.053, 1.059, 1.066, 1.079, 1.098, 1.122, 1.155, 1.197, 1.252, 1.337, 1.369,
+                    1.233, 1.199, 1.159, 1.129, 1.111, 1.095, 1.073, 1.061, 1.054, 1.049, 1.044, 1.038, 1.033, 1.031, 1.029, 1.029, 1.033, 1.037, 1.037, 1.037, 1.042, 1.047, 1.052, 1.059, 1.071, 1.087, 1.111, 1.142, 1.181, 1.232, 1.307, 1.337,
+                    1.217, 1.185, 1.146, 1.116, 1.097, 1.083, 1.069, 1.055, 1.049, 1.045, 1.039, 1.033, 1.029, 1.027, 1.025, 1.024, 1.025, 1.028, 1.029, 1.031, 1.035, 1.041, 1.047, 1.053, 1.063, 1.079, 1.101, 1.131, 1.168, 1.215, 1.282, 1.308,
+                    1.206, 1.172, 1.134, 1.106, 1.088, 1.076, 1.064, 1.051, 1.045, 1.041, 1.034, 1.029, 1.026, 1.023, 1.021, 1.021, 1.021, 1.021, 1.024, 1.027, 1.031, 1.035, 1.041, 1.048, 1.058, 1.072, 1.092, 1.121, 1.157, 1.201, 1.262, 1.288,
+                    1.196, 1.161, 1.125, 1.099, 1.081, 1.071, 1.059, 1.047, 1.041, 1.035, 1.029, 1.025, 1.022, 1.019, 1.018, 1.017, 1.017, 1.018, 1.021, 1.023, 1.027, 1.031, 1.036, 1.044, 1.053, 1.066, 1.085, 1.113, 1.148, 1.191, 1.247, 1.273,
+                    1.188, 1.152, 1.118, 1.093, 1.076, 1.064, 1.052, 1.044, 1.037, 1.031, 1.026, 1.022, 1.019, 1.017, 1.015, 1.015, 1.014, 1.015, 1.017, 1.019, 1.023, 1.027, 1.033, 1.041, 1.049, 1.061, 1.078, 1.105, 1.139, 1.181, 1.235, 1.261,
+                    1.182, 1.145, 1.112, 1.088, 1.072, 1.059, 1.049, 1.041, 1.035, 1.028, 1.023, 1.019, 1.016, 1.014, 1.012, 1.012, 1.011, 1.012, 1.015, 1.017, 1.021, 1.024, 1.029, 1.037, 1.045, 1.056, 1.074, 1.099, 1.133, 1.173, 1.224, 1.251,
+                    1.178, 1.139, 1.106, 1.083, 1.068, 1.056, 1.046, 1.039, 1.032, 1.026, 1.021, 1.017, 1.014, 1.011, 1.009, 1.009, 1.009, 1.009, 1.012, 1.015, 1.018, 1.022, 1.028, 1.035, 1.042, 1.054, 1.069, 1.095, 1.127, 1.168, 1.217, 1.244,
+                    1.174, 1.134, 1.102, 1.081, 1.065, 1.053, 1.044, 1.037, 1.029, 1.024, 1.019, 1.015, 1.012, 1.009, 1.007, 1.006, 1.006, 1.007, 1.009, 1.012, 1.016, 1.019, 1.027, 1.034, 1.041, 1.051, 1.067, 1.092, 1.123, 1.161, 1.211, 1.239,
+                    1.173, 1.131, 1.099, 1.078, 1.062, 1.051, 1.042, 1.035, 1.028, 1.023, 1.018, 1.013, 1.009, 1.007, 1.005, 1.004, 1.004, 1.005, 1.007, 1.011, 1.014, 1.018, 1.025, 1.032, 1.039, 1.049, 1.064, 1.089, 1.119, 1.158, 1.207, 1.236,
+                    1.171, 1.129, 1.097, 1.075, 1.061, 1.049, 1.041, 1.033, 1.027, 1.021, 1.017, 1.012, 1.008, 1.005, 1.004, 1.003, 1.003, 1.003, 1.005, 1.009, 1.013, 1.017, 1.025, 1.031, 1.037, 1.047, 1.062, 1.087, 1.117, 1.155, 1.205, 1.233,
+                    1.169, 1.128, 1.097, 1.074, 1.059, 1.049, 1.041, 1.033, 1.026, 1.021, 1.015, 1.011, 1.007, 1.005, 1.004, 1.002, 1.002, 1.002, 1.004, 1.008, 1.012, 1.017, 1.024, 1.031, 1.036, 1.046, 1.061, 1.085, 1.116, 1.155, 1.203, 1.233,
+                    1.169, 1.128, 1.097, 1.074, 1.059, 1.048, 1.039, 1.031, 1.025, 1.021, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.001, 1.002, 1.004, 1.008, 1.012, 1.017, 1.024, 1.031, 1.036, 1.046, 1.061, 1.084, 1.115, 1.155, 1.203, 1.233,
+                    1.169, 1.129, 1.098, 1.075, 1.059, 1.048, 1.039, 1.031, 1.025, 1.019, 1.015, 1.011, 1.006, 1.004, 1.002, 1.001, 1.001, 1.002, 1.004, 1.008, 1.012, 1.017, 1.025, 1.031, 1.036, 1.046, 1.061, 1.084, 1.116, 1.155, 1.204, 1.234,
+                    1.169, 1.131, 1.099, 1.076, 1.061, 1.049, 1.039, 1.032, 1.025, 1.021, 1.015, 1.011, 1.007, 1.005, 1.003, 1.001, 1.001, 1.002, 1.005, 1.009, 1.013, 1.017, 1.024, 1.031, 1.037, 1.047, 1.061, 1.085, 1.118, 1.158, 1.208, 1.237,
+                    1.171, 1.134, 1.102, 1.078, 1.062, 1.051, 1.041, 1.033, 1.026, 1.021, 1.016, 1.011, 1.009, 1.006, 1.004, 1.002, 1.002, 1.003, 1.006, 1.012, 1.015, 1.018, 1.023, 1.031, 1.038, 1.048, 1.064, 1.088, 1.121, 1.162, 1.212, 1.241,
+                    1.174, 1.139, 1.106, 1.081, 1.064, 1.052, 1.043, 1.034, 1.027, 1.022, 1.017, 1.013, 1.009, 1.008, 1.005, 1.004, 1.004, 1.005, 1.008, 1.013, 1.017, 1.019, 1.024, 1.031, 1.039, 1.051, 1.067, 1.092, 1.125, 1.167, 1.219, 1.246,
+                    1.179, 1.145, 1.112, 1.085, 1.067, 1.054, 1.045, 1.036, 1.029, 1.023, 1.018, 1.014, 1.011, 1.008, 1.007, 1.006, 1.007, 1.008, 1.009, 1.014, 1.018, 1.019, 1.025, 1.032, 1.041, 1.054, 1.071, 1.096, 1.131, 1.174, 1.228, 1.255,
+                    1.186, 1.152, 1.117, 1.089, 1.069, 1.057, 1.046, 1.038, 1.031, 1.025, 1.019, 1.016, 1.013, 1.009, 1.009, 1.008, 1.009, 1.009, 1.011, 1.014, 1.018, 1.021, 1.027, 1.034, 1.044, 1.056, 1.075, 1.102, 1.137, 1.182, 1.239, 1.268,
+                    1.194, 1.159, 1.123, 1.095, 1.075, 1.061, 1.049, 1.041, 1.033, 1.027, 1.022, 1.018, 1.015, 1.013, 1.011, 1.011, 1.011, 1.011, 1.013, 1.016, 1.019, 1.024, 1.031, 1.037, 1.047, 1.061, 1.082, 1.109, 1.147, 1.194, 1.256, 1.281,
+                    1.203, 1.169, 1.132, 1.102, 1.081, 1.065, 1.053, 1.044, 1.036, 1.029, 1.026, 1.021, 1.018, 1.015, 1.013, 1.013, 1.013, 1.014, 1.015, 1.018, 1.022, 1.028, 1.034, 1.041, 1.052, 1.067, 1.091, 1.121, 1.157, 1.207, 1.273, 1.299,
+                    1.214, 1.179, 1.142, 1.111, 1.088, 1.069, 1.057, 1.047, 1.039, 1.033, 1.028, 1.024, 1.021, 1.018, 1.016, 1.015, 1.015, 1.016, 1.018, 1.021, 1.026, 1.031, 1.038, 1.046, 1.058, 1.075, 1.101, 1.133, 1.171, 1.222, 1.293, 1.319,
+                    1.227, 1.194, 1.153, 1.121, 1.096, 1.076, 1.062, 1.052, 1.043, 1.036, 1.031, 1.027, 1.023, 1.021, 1.019, 1.019, 1.018, 1.019, 1.021, 1.025, 1.029, 1.035, 1.042, 1.051, 1.065, 1.085, 1.112, 1.145, 1.185, 1.239, 1.318, 1.346,
+                    1.244, 1.211, 1.167, 1.133, 1.107, 1.088, 1.069, 1.057, 1.047, 1.041, 1.034, 1.031, 1.026, 1.024, 1.022, 1.022, 1.022, 1.023, 1.025, 1.029, 1.034, 1.039, 1.047, 1.059, 1.075, 1.096, 1.124, 1.158, 1.202, 1.261, 1.346, 1.381,
+                    1.264, 1.229, 1.183, 1.147, 1.119, 1.098, 1.077, 1.063, 1.053, 1.045, 1.039, 1.034, 1.031, 1.027, 1.026, 1.026, 1.026, 1.027, 1.029, 1.034, 1.039, 1.045, 1.054, 1.068, 1.086, 1.109, 1.138, 1.175, 1.222, 1.288, 1.381, 1.423,
+                    1.287, 1.251, 1.201, 1.163, 1.134, 1.109, 1.086, 1.071, 1.059, 1.051, 1.044, 1.039, 1.034, 1.032, 1.031, 1.031, 1.031, 1.032, 1.036, 1.039, 1.045, 1.053, 1.064, 1.079, 1.101, 1.125, 1.156, 1.195, 1.246, 1.321, 1.423, 1.479,
+                    1.314, 1.275, 1.222, 1.182, 1.151, 1.123, 1.098, 1.082, 1.068, 1.058, 1.051, 1.045, 1.041, 1.037, 1.036, 1.036, 1.037, 1.039, 1.041, 1.047, 1.053, 1.063, 1.076, 1.093, 1.115, 1.141, 1.174, 1.216, 1.275, 1.361, 1.479, 1.556,
+                    1.349, 1.307, 1.246, 1.203, 1.169, 1.138, 1.114, 1.095, 1.079, 1.067, 1.058, 1.052, 1.048, 1.045, 1.043, 1.043, 1.044, 1.046, 1.049, 1.055, 1.063, 1.075, 1.091, 1.111, 1.134, 1.161, 1.196, 1.242, 1.311, 1.407, 1.556, 1.617,
+                    1.389, 1.333, 1.266, 1.219, 1.184, 1.152, 1.127, 1.106, 1.089, 1.076, 1.066, 1.058, 1.053, 1.049, 1.049, 1.049, 1.049, 1.051, 1.055, 1.062, 1.072, 1.085, 1.102, 1.127, 1.151, 1.177, 1.213, 1.264, 1.339, 1.445, 1.617, 1.667
+                ],
+                "sigma": 0.00092,
+                "sigma_Cb": 0.00149
+            }
+        },
+        {
+            "rpi.contrast":
+            {
+                "ce_enable": 1,
+                "gamma_curve":
+                [
+                    0, 0,
+                    1024, 5040,
+                    2048, 9338,
+                    3072, 12356,
+                    4096, 15312,
+                    5120, 18051,
+                    6144, 20790,
+                    7168, 23193,
+                    8192, 25744,
+                    9216, 27942,
+                    10240, 30035,
+                    11264, 32005,
+                    12288, 33975,
+                    13312, 35815,
+                    14336, 37600,
+                    15360, 39168,
+                    16384, 40642,
+                    18432, 43379,
+                    20480, 45749,
+                    22528, 47753,
+                    24576, 49621,
+                    26624, 51253,
+                    28672, 52698,
+                    30720, 53796,
+                    32768, 54876,
+                    36864, 57012,
+                    40960, 58656,
+                    45056, 59954,
+                    49152, 61183,
+                    53248, 62355,
+                    57344, 63419,
+                    61440, 64476,
+                    65535, 65535
+                ]
+            }
+        },
+        {
+            "rpi.ccm":
+            {
+                "ccms": [
+                    {
+                        "ct": 2500,
+                        "ccm":
+                        [
+                            1.82604, -0.41219, -0.41384,
+                            -0.51919, 1.83221, -0.31302,
+                            0.23201, -1.42044, 2.18842
+                        ]
+                    },
+                    {
+                        "ct": 2820,
+                        "ccm":
+                        [
+                            1.80891, -0.47916, -0.32974,
+                            -0.47311, 1.83395, -0.36084,
+                            0.21814, -1.22973, 2.01158
+                        ]
+                    },
+                    {
+                        "ct": 2830,
+                        "ccm":
+                        [
+                            1.80397, -0.51779, -0.28617,
+                            -0.64069, 2.16622, -0.52553,
+                            0.12013, -0.95702, 1.83689
+                        ]
+                    },
+                    {
+                        "ct": 2885,
+                        "ccm":
+                        [
+                            1.78861, -0.50175, -0.28685,
+                            -0.63703, 2.14176, -0.50473,
+                            0.08715, -0.86455, 1.77741
+                        ]
+                    },
+                    {
+                        "ct": 3601,
+                        "ccm":
+                        [
+                            1.85135, -0.56992, -0.28143,
+                            -0.56285, 2.08651, -0.52366,
+                            0.03737, -0.70813, 1.67076
+                        ]
+                    },
+                    {
+                        "ct": 3615,
+                        "ccm":
+                        [
+                            1.87447, -0.60511, -0.26936,
+                            -0.55592, 2.07733, -0.52141,
+                            0.04105, -0.70347, 1.66242
+                        ]
+                    },
+                    {
+                        "ct": 3622,
+                        "ccm":
+                        [
+                            1.85269, -0.58224, -0.27044,
+                            -0.55133, 2.06403, -0.51271,
+                            0.03952, -0.69055, 1.65103
+                        ]
+                    },
+                    {
+                        "ct": 4345,
+                        "ccm":
+                        [
+                            1.81525, -0.56996, -0.24529,
+                            -0.49203, 2.16996, -0.67793,
+                            0.02513, -0.67456, 1.64943
+                        ]
+                    },
+                    {
+                        "ct": 4410,
+                        "ccm":
+                        [
+                            1.83312, -0.59611, -0.23701,
+                            -0.48332, 2.15123, -0.66791,
+                            0.02629, -0.67203, 1.64574
+                        ]
+                    },
+                    {
+                        "ct": 4486,
+                        "ccm":
+                        [
+                            1.84759, -0.60181, -0.24578,
+                            -0.47792, 2.13471, -0.65679,
+                            0.02056, -0.65959, 1.63903
+                        ]
+                    },
+                    {
+                        "ct": 4576,
+                        "ccm":
+                        [
+                            1.83733, -0.58859, -0.24874,
+                            -0.47443, 2.13699, -0.66255,
+                            0.01842, -0.65402, 1.63561
+                        ]
+                    },
+                    {
+                        "ct": 5657,
+                        "ccm":
+                        [
+                            1.84437, -0.57638, -0.26799,
+                            -0.44569, 2.04163, -0.59593,
+                            -0.01759, -0.52787, 1.54546
+                        ]
+                    },
+                    {
+                        "ct": 5672,
+                        "ccm":
+                        [
+                            1.83986, -0.57025, -0.26962,
+                            -0.44974, 2.04763, -0.59789,
+                            -0.03246, -0.51626, 1.54872
+                        ]
+                    },
+                    {
+                        "ct": 5710,
+                        "ccm":
+                        [
+                            1.83822, -0.57688, -0.26134,
+                            -0.44263, 2.03779, -0.59516,
+                            -0.02552, -0.52605, 1.55157
+                        ]
+                    },
+                    {
+                        "ct": 6850,
+                        "ccm":
+                        [
+                            1.80507, -0.22489, -0.58017,
+                            -0.48609, 2.48778, -1.00168,
+                            -0.10995, -0.63701, 1.74696
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.cac": { }
+        },
+        {
+            "rpi.sharpen":
+            {
+                "threshold": 0.25,
+                "limit": 1.0,
+                "strength": 1.0
+            }
+        },
+        {
+            "rpi.hdr":
+            {
+                "Off":
+                {
+                    "cadence": [ 0 ]
+                },
+                "MultiExposureUnmerged":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    }
+                },
+                "SingleExposure":
+                {
+                    "cadence": [ 1 ],
+                    "channel_map":
+                    {
+                        "short": 1
+                    },
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "MultiExposure":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    },
+                    "stitch_enable": 1,
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "Night":
+                {
+                    "cadence": [ 3 ],
+                    "channel_map":
+                    {
+                        "night": 3
+                    },
+                    "tonemap_enable": 1,
+                    "tonemap":
+                    [
+                        0, 0,
+                        5000, 20000,
+                        10000, 30000,
+                        20000, 47000,
+                        30000, 55000,
+                        65535, 65535
+                    ]
+                }
+            }
+        }
+    ]
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/imx708.json 0.5.2-2/src/ipa/rpi/pisp/data/imx708.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/imx708.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/imx708.json	2025-08-07 13:46:17.000000000 +0000
@@ -1139,11 +1139,27 @@
                         "step_coarse": 1.0,
                         "step_fine": 0.25,
                         "contrast_ratio": 0.75,
-                        "pdaf_gain": -0.02,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
+                        "pdaf_gain": -0.016,
                         "pdaf_squelch": 0.125,
-                        "max_slew": 2.0,
+                        "max_slew": 1.5,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
+                        "step_frames": 5
+                    },
+                    "fast":
+                    {
+                        "step_coarse": 1.25,
+                        "step_fine": 0.0,
+                        "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
+                        "pdaf_gain": -0.02,
+                        "pdaf_squelch": 0.125,
+                        "max_slew": 2.0,
+                        "pdaf_frames": 16,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -1151,6 +1167,7 @@
                 "conf_thresh": 16,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": false,
                 "map": [ 0.0, 445, 15.0, 925 ]
             }
         },
@@ -1267,4 +1284,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/imx708_noir.json 0.5.2-2/src/ipa/rpi/pisp/data/imx708_noir.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/imx708_noir.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/imx708_noir.json	2025-08-07 13:46:17.000000000 +0000
@@ -1156,11 +1156,27 @@
                         "step_coarse": 1.0,
                         "step_fine": 0.25,
                         "contrast_ratio": 0.75,
-                        "pdaf_gain": -0.02,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
+                        "pdaf_gain": -0.016,
                         "pdaf_squelch": 0.125,
-                        "max_slew": 2.0,
+                        "max_slew": 1.5,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
+                        "step_frames": 5
+                    },
+                    "fast":
+                    {
+                        "step_coarse": 1.25,
+                        "step_fine": 0.0,
+                        "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
+                        "pdaf_gain": -0.02,
+                        "pdaf_squelch": 0.125,
+                        "max_slew": 2.0,
+                        "pdaf_frames": 16,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -1168,6 +1184,7 @@
                 "conf_thresh": 16,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": true,
                 "map": [ 0.0, 445, 15.0, 925 ]
             }
         },
@@ -1230,4 +1247,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/imx708_wide.json 0.5.2-2/src/ipa/rpi/pisp/data/imx708_wide.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/imx708_wide.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/imx708_wide.json	2025-08-07 13:46:17.000000000 +0000
@@ -1148,23 +1148,27 @@
                         "step_coarse": 2.0,
                         "step_fine": 0.5,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio" : 0.8,
+                        "retrigger_delay" : 10,
                         "pdaf_gain": -0.03,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 4.0,
+                        "max_slew": 3.0,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
-                        "step_frames": 4
+                        "step_frames": 5
                     },
                     "fast":
                     {
-                        "step_coarse": 2.0,
-                        "step_fine": 0.5,
+                        "step_coarse": 2.5,
+                        "step_fine": 0.0,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio" : 0.8,
+                        "retrigger_delay" : 8,
                         "pdaf_gain": -0.05,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 5.0,
+                        "max_slew": 4.0,
                         "pdaf_frames": 16,
-                        "dropout_frames": 6,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -1172,6 +1176,7 @@
                 "conf_thresh": 12,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": false,
                 "map": [ 0.0, 420, 35.0, 920 ]
             }
         },
@@ -1290,4 +1295,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/imx708_wide_noir.json 0.5.2-2/src/ipa/rpi/pisp/data/imx708_wide_noir.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/imx708_wide_noir.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/imx708_wide_noir.json	2025-08-07 13:46:17.000000000 +0000
@@ -1057,23 +1057,27 @@
                         "step_coarse": 2.0,
                         "step_fine": 0.5,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio" : 0.8,
+                        "retrigger_delay" : 10,
                         "pdaf_gain": -0.03,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 4.0,
+                        "max_slew": 3.0,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
-                        "step_frames": 4
+                        "step_frames": 5
                     },
                     "fast":
                     {
-                        "step_coarse": 2.0,
-                        "step_fine": 0.5,
+                        "step_coarse": 2.5,
+                        "step_fine": 0.0,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio" : 0.8,
+                        "retrigger_delay" : 8,
                         "pdaf_gain": -0.05,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 5.0,
+                        "max_slew": 4.0,
                         "pdaf_frames": 16,
-                        "dropout_frames": 6,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -1081,6 +1085,7 @@
                 "conf_thresh": 12,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": true,
                 "map": [ 0.0, 420, 35.0, 920 ]
             }
         },
@@ -1145,4 +1150,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/meson.build 0.5.2-2/src/ipa/rpi/pisp/data/meson.build
--- 0.5.0-1/src/ipa/rpi/pisp/data/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -3,6 +3,7 @@
 conf_files = files([
     'imx219.json',
     'imx219_noir.json',
+    'imx283.json',
     'imx290.json',
     'imx296.json',
     'imx296_mono.json',
@@ -23,6 +24,8 @@ conf_files = files([
     'ov9281_mono.json',
     'se327m12.json',
     'uncalibrated.json',
+    'vd56g3.json',
+    'vd56g3_mono.json',
 ])
 
 install_data(conf_files,
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/vd56g3.json 0.5.2-2/src/ipa/rpi/pisp/data/vd56g3.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/vd56g3.json	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/vd56g3.json	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,1293 @@
+{
+    "version": 2.0,
+    "target": "pisp",
+    "algorithms": [
+        {
+            "rpi.black_level":
+            {
+                "black_level": 4096
+            }
+        },
+        {
+            "rpi.lux":
+            {
+                "reference_shutter_speed": 5971,
+                "reference_gain": 1.0,
+                "reference_aperture": 1.0,
+                "reference_lux": 950,
+                "reference_Y": 23748
+            }
+        },
+        {
+            "rpi.noise":
+            {
+                "reference_constant": 0,
+                "reference_slope": 2.732
+            }
+        },
+        {
+            "rpi.geq":
+            {
+                "offset": 108,
+                "slope": 0.05756
+            }
+        },
+        {
+            "rpi.denoise":
+            {
+                "normal":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 0.8,
+                        "threshold": 0.05
+                    }
+                },
+                "hdr":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                },
+                "night":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                }
+            }
+        },
+        {
+            "rpi.awb":
+            {
+                "priors": [
+                    {
+                        "lux": 0,
+                        "prior":
+                        [
+                            2000, 1.0,
+                            3000, 0.0,
+                            13000, 0.0
+                        ]
+                    },
+                    {
+                        "lux": 800,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            6000, 2.0,
+                            13000, 2.0
+                        ]
+                    },
+                    {
+                        "lux": 1500,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            4000, 1.0,
+                            6000, 6.0,
+                            6500, 7.0,
+                            7000, 1.0,
+                            13000, 1.0
+                        ]
+                    }
+                ],
+                "modes":
+                {
+                    "auto":
+                    {
+                        "lo": 2500,
+                        "hi": 7700
+                    },
+                    "incandescent":
+                    {
+                        "lo": 2500,
+                        "hi": 3000
+                    },
+                    "tungsten":
+                    {
+                        "lo": 3000,
+                        "hi": 3500
+                    },
+                    "fluorescent":
+                    {
+                        "lo": 4000,
+                        "hi": 4700
+                    },
+                    "indoor":
+                    {
+                        "lo": 3000,
+                        "hi": 5000
+                    },
+                    "daylight":
+                    {
+                        "lo": 5500,
+                        "hi": 6500
+                    },
+                    "cloudy":
+                    {
+                        "lo": 7000,
+                        "hi": 8000
+                    }
+                },
+                "bayes": 1,
+                "ct_curve":
+                [
+                    3000.0, 1.2153, 0.4578,
+                    6500.0, 0.6724, 0.7526
+                ],
+                "sensitivity_r": 1.0,
+                "sensitivity_b": 1.0,
+                "transverse_pos": 0.01428,
+                "transverse_neg": 0.01963
+            }
+        },
+        {
+            "rpi.agc":
+            {
+                "channels": [
+                    {
+                        "comment": "Channel 0 is normal AGC",
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 66666 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0, 12.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.5,
+                                    "y_target":
+                                    [
+                                        0, 0.17,
+                                        1000, 0.17
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 1 is the HDR short channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 2 is the HDR long channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [  ],
+                            "highlight": [  ],
+                            "shadows": [  ]
+                        },
+                        "channel_constraints": [
+                            {
+                                "bound": "UPPER",
+                                "channel": 4,
+                                "factor": 8
+                            },
+                            {
+                                "bound": "LOWER",
+                                "channel": 4,
+                                "factor": 2
+                            }
+                        ],
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 3 is the night mode channel",
+                        "base_ev": 0.33,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 66666 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 33333 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 66666, 120000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 4.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.16,
+                            10000, 0.17
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.alsc":
+            {
+                "omega": 1.3,
+                "n_iter": 100,
+                "luminance_strength": 0.8,
+                "calibrations_Cr": [
+                    {
+                        "ct": 2400,
+                        "table":
+                        [
+                            1.075, 1.079, 1.081, 1.087, 1.087, 1.086, 1.086, 1.086, 1.085, 1.085, 1.085, 1.087, 1.087, 1.087, 1.087, 1.088, 1.085, 1.085, 1.083, 1.079, 1.074, 1.071, 1.065, 1.059, 1.054, 1.047, 1.039, 1.032, 1.023, 1.012, 1.011, 1.011,
+                            1.075, 1.077, 1.078, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.078, 1.079, 1.081, 1.081, 1.083, 1.084, 1.085, 1.084, 1.083, 1.082, 1.078, 1.074, 1.071, 1.063, 1.059, 1.053, 1.047, 1.039, 1.032, 1.023, 1.018, 1.012, 1.012,
+                            1.074, 1.077, 1.078, 1.079, 1.081, 1.081, 1.079, 1.079, 1.078, 1.078, 1.079, 1.079, 1.081, 1.082, 1.084, 1.085, 1.084, 1.083, 1.082, 1.078, 1.074, 1.069, 1.065, 1.059, 1.054, 1.049, 1.043, 1.034, 1.027, 1.021, 1.021, 1.019,
+                            1.075, 1.077, 1.081, 1.081, 1.081, 1.081, 1.079, 1.079, 1.079, 1.079, 1.079, 1.082, 1.082, 1.084, 1.085, 1.086, 1.086, 1.084, 1.082, 1.079, 1.076, 1.072, 1.068, 1.064, 1.058, 1.052, 1.047, 1.041, 1.031, 1.029, 1.028, 1.027,
+                            1.076, 1.079, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.082, 1.084, 1.085, 1.086, 1.087, 1.087, 1.085, 1.083, 1.081, 1.077, 1.075, 1.071, 1.068, 1.063, 1.056, 1.051, 1.045, 1.035, 1.032, 1.032, 1.033,
+                            1.077, 1.079, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.083, 1.084, 1.085, 1.087, 1.088, 1.087, 1.086, 1.084, 1.082, 1.079, 1.077, 1.074, 1.069, 1.065, 1.061, 1.053, 1.047, 1.038, 1.036, 1.037, 1.039,
+                            1.077, 1.078, 1.081, 1.081, 1.082, 1.082, 1.081, 1.082, 1.082, 1.082, 1.082, 1.083, 1.085, 1.086, 1.087, 1.088, 1.088, 1.086, 1.084, 1.083, 1.082, 1.078, 1.076, 1.072, 1.067, 1.063, 1.058, 1.051, 1.043, 1.041, 1.041, 1.042,
+                            1.077, 1.079, 1.081, 1.082, 1.082, 1.082, 1.082, 1.083, 1.082, 1.082, 1.083, 1.084, 1.086, 1.087, 1.089, 1.089, 1.088, 1.087, 1.085, 1.083, 1.082, 1.079, 1.078, 1.074, 1.069, 1.066, 1.061, 1.055, 1.047, 1.045, 1.045, 1.048,
+                            1.076, 1.079, 1.081, 1.083, 1.083, 1.084, 1.083, 1.083, 1.083, 1.084, 1.084, 1.086, 1.087, 1.088, 1.089, 1.089, 1.088, 1.087, 1.085, 1.084, 1.082, 1.079, 1.078, 1.076, 1.072, 1.068, 1.063, 1.058, 1.049, 1.048, 1.049, 1.051,
+                            1.076, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.083, 1.083, 1.084, 1.085, 1.086, 1.087, 1.089, 1.089, 1.089, 1.087, 1.086, 1.086, 1.084, 1.082, 1.081, 1.079, 1.076, 1.074, 1.071, 1.065, 1.061, 1.054, 1.051, 1.051, 1.055,
+                            1.078, 1.081, 1.083, 1.084, 1.085, 1.085, 1.084, 1.083, 1.084, 1.084, 1.085, 1.085, 1.087, 1.088, 1.088, 1.088, 1.086, 1.086, 1.086, 1.084, 1.083, 1.082, 1.081, 1.078, 1.075, 1.071, 1.066, 1.063, 1.056, 1.055, 1.055, 1.057,
+                            1.081, 1.083, 1.084, 1.085, 1.086, 1.085, 1.084, 1.084, 1.084, 1.084, 1.085, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.086, 1.085, 1.084, 1.084, 1.082, 1.081, 1.079, 1.076, 1.072, 1.067, 1.064, 1.057, 1.056, 1.057, 1.058,
+                            1.081, 1.084, 1.085, 1.086, 1.086, 1.086, 1.084, 1.084, 1.084, 1.084, 1.084, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.076, 1.072, 1.069, 1.065, 1.058, 1.057, 1.058, 1.061,
+                            1.081, 1.084, 1.085, 1.086, 1.086, 1.086, 1.084, 1.083, 1.083, 1.083, 1.084, 1.084, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.073, 1.069, 1.066, 1.059, 1.059, 1.061, 1.063,
+                            1.081, 1.084, 1.086, 1.086, 1.086, 1.086, 1.083, 1.082, 1.082, 1.083, 1.083, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.066, 1.061, 1.061, 1.062, 1.065,
+                            1.082, 1.085, 1.086, 1.086, 1.086, 1.084, 1.082, 1.081, 1.082, 1.082, 1.082, 1.083, 1.084, 1.086, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.082, 1.081, 1.079, 1.077, 1.074, 1.071, 1.066, 1.061, 1.061, 1.064, 1.066,
+                            1.082, 1.084, 1.085, 1.085, 1.083, 1.083, 1.081, 1.081, 1.081, 1.082, 1.082, 1.083, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.077, 1.074, 1.071, 1.067, 1.061, 1.061, 1.064, 1.065,
+                            1.081, 1.082, 1.083, 1.082, 1.081, 1.079, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.085, 1.086, 1.086, 1.086, 1.086, 1.085, 1.084, 1.083, 1.082, 1.079, 1.076, 1.074, 1.071, 1.067, 1.061, 1.061, 1.064, 1.066,
+                            1.079, 1.078, 1.078, 1.077, 1.077, 1.077, 1.078, 1.078, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.084, 1.084, 1.083, 1.082, 1.081, 1.079, 1.076, 1.073, 1.071, 1.067, 1.061, 1.061, 1.065, 1.067,
+                            1.073, 1.073, 1.073, 1.074, 1.074, 1.074, 1.074, 1.075, 1.076, 1.077, 1.077, 1.077, 1.077, 1.078, 1.079, 1.079, 1.081, 1.083, 1.083, 1.083, 1.081, 1.081, 1.079, 1.077, 1.075, 1.072, 1.069, 1.066, 1.061, 1.061, 1.064, 1.066,
+                            1.064, 1.064, 1.066, 1.067, 1.067, 1.071, 1.071, 1.072, 1.073, 1.074, 1.074, 1.073, 1.074, 1.075, 1.076, 1.077, 1.078, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.076, 1.074, 1.071, 1.068, 1.064, 1.059, 1.059, 1.062, 1.064,
+                            1.056, 1.058, 1.059, 1.061, 1.062, 1.065, 1.066, 1.067, 1.068, 1.068, 1.069, 1.068, 1.068, 1.069, 1.071, 1.074, 1.076, 1.078, 1.079, 1.079, 1.079, 1.078, 1.076, 1.074, 1.072, 1.069, 1.065, 1.061, 1.057, 1.057, 1.059, 1.061,
+                            1.048, 1.053, 1.054, 1.057, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.065, 1.066, 1.068, 1.071, 1.074, 1.077, 1.078, 1.078, 1.078, 1.076, 1.074, 1.071, 1.069, 1.066, 1.063, 1.058, 1.055, 1.054, 1.056, 1.059,
+                            1.044, 1.047, 1.049, 1.052, 1.054, 1.056, 1.057, 1.059, 1.059, 1.059, 1.061, 1.063, 1.064, 1.065, 1.067, 1.071, 1.073, 1.075, 1.077, 1.076, 1.076, 1.073, 1.071, 1.069, 1.066, 1.064, 1.059, 1.056, 1.051, 1.051, 1.053, 1.055,
+                            1.039, 1.042, 1.045, 1.048, 1.049, 1.051, 1.053, 1.055, 1.056, 1.057, 1.059, 1.061, 1.063, 1.065, 1.068, 1.071, 1.073, 1.074, 1.074, 1.074, 1.072, 1.071, 1.069, 1.066, 1.064, 1.062, 1.057, 1.054, 1.048, 1.048, 1.049, 1.053,
+                            1.036, 1.038, 1.042, 1.045, 1.047, 1.049, 1.051, 1.052, 1.054, 1.056, 1.058, 1.061, 1.063, 1.065, 1.068, 1.069, 1.072, 1.073, 1.073, 1.072, 1.071, 1.069, 1.066, 1.065, 1.062, 1.059, 1.055, 1.052, 1.047, 1.047, 1.047, 1.049,
+                            1.032, 1.036, 1.038, 1.042, 1.044, 1.046, 1.049, 1.051, 1.053, 1.055, 1.058, 1.061, 1.062, 1.066, 1.067, 1.069, 1.071, 1.071, 1.071, 1.071, 1.069, 1.067, 1.065, 1.063, 1.061, 1.057, 1.054, 1.051, 1.045, 1.045, 1.046, 1.048,
+                            1.028, 1.032, 1.036, 1.038, 1.042, 1.044, 1.045, 1.049, 1.051, 1.054, 1.057, 1.059, 1.061, 1.065, 1.066, 1.067, 1.068, 1.069, 1.069, 1.069, 1.067, 1.066, 1.064, 1.063, 1.061, 1.055, 1.052, 1.049, 1.044, 1.044, 1.046, 1.048,
+                            1.025, 1.027, 1.032, 1.035, 1.036, 1.041, 1.043, 1.045, 1.049, 1.051, 1.054, 1.057, 1.059, 1.062, 1.065, 1.066, 1.066, 1.066, 1.067, 1.066, 1.065, 1.065, 1.063, 1.061, 1.059, 1.056, 1.052, 1.047, 1.043, 1.042, 1.045, 1.046,
+                            1.017, 1.021, 1.025, 1.029, 1.034, 1.036, 1.041, 1.042, 1.044, 1.047, 1.049, 1.053, 1.055, 1.057, 1.059, 1.061, 1.063, 1.063, 1.063, 1.064, 1.063, 1.062, 1.061, 1.059, 1.057, 1.054, 1.051, 1.046, 1.039, 1.039, 1.039, 1.044,
+                            1.009, 1.015, 1.021, 1.023, 1.027, 1.031, 1.036, 1.037, 1.039, 1.042, 1.043, 1.045, 1.048, 1.051, 1.053, 1.055, 1.057, 1.058, 1.058, 1.059, 1.058, 1.058, 1.057, 1.055, 1.054, 1.051, 1.046, 1.041, 1.037, 1.037, 1.036, 1.038,
+                            1.004, 1.008, 1.014, 1.019, 1.022, 1.024, 1.025, 1.028, 1.029, 1.029, 1.031, 1.036, 1.039, 1.043, 1.046, 1.048, 1.049, 1.049, 1.052, 1.052, 1.052, 1.051, 1.049, 1.048, 1.045, 1.044, 1.041, 1.038, 1.033, 1.029, 1.031, 1.031
+                        ]
+                    },
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            1.058, 1.066, 1.068, 1.072, 1.073, 1.075, 1.076, 1.074, 1.073, 1.072, 1.071, 1.071, 1.071, 1.071, 1.071, 1.072, 1.069, 1.068, 1.066, 1.064, 1.061, 1.058, 1.053, 1.049, 1.044, 1.037, 1.031, 1.024, 1.014, 1.009, 1.007, 1.006,
+                            1.061, 1.065, 1.068, 1.072, 1.073, 1.074, 1.074, 1.074, 1.072, 1.069, 1.069, 1.071, 1.071, 1.072, 1.073, 1.072, 1.072, 1.069, 1.067, 1.066, 1.062, 1.058, 1.054, 1.051, 1.045, 1.041, 1.034, 1.026, 1.016, 1.011, 1.009, 1.011,
+                            1.062, 1.066, 1.071, 1.073, 1.073, 1.074, 1.074, 1.074, 1.073, 1.069, 1.069, 1.072, 1.074, 1.074, 1.075, 1.075, 1.076, 1.074, 1.072, 1.071, 1.068, 1.065, 1.059, 1.055, 1.052, 1.046, 1.039, 1.032, 1.026, 1.019, 1.019, 1.022,
+                            1.066, 1.071, 1.074, 1.076, 1.078, 1.079, 1.078, 1.078, 1.077, 1.077, 1.077, 1.078, 1.079, 1.081, 1.082, 1.084, 1.083, 1.081, 1.079, 1.076, 1.075, 1.071, 1.067, 1.063, 1.058, 1.053, 1.047, 1.041, 1.032, 1.032, 1.029, 1.029,
+                            1.069, 1.073, 1.077, 1.079, 1.081, 1.082, 1.081, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.086, 1.086, 1.087, 1.086, 1.086, 1.084, 1.082, 1.079, 1.076, 1.072, 1.069, 1.064, 1.059, 1.053, 1.047, 1.039, 1.037, 1.036, 1.039,
+                            1.071, 1.075, 1.078, 1.081, 1.082, 1.084, 1.084, 1.085, 1.084, 1.084, 1.084, 1.086, 1.087, 1.089, 1.091, 1.091, 1.091, 1.089, 1.088, 1.086, 1.083, 1.081, 1.078, 1.073, 1.069, 1.064, 1.058, 1.052, 1.044, 1.042, 1.042, 1.042,
+                            1.072, 1.078, 1.079, 1.082, 1.085, 1.086, 1.086, 1.087, 1.086, 1.087, 1.088, 1.089, 1.091, 1.093, 1.094, 1.095, 1.095, 1.093, 1.092, 1.091, 1.088, 1.086, 1.083, 1.079, 1.074, 1.071, 1.064, 1.057, 1.049, 1.047, 1.047, 1.051,
+                            1.072, 1.079, 1.082, 1.085, 1.087, 1.089, 1.089, 1.091, 1.091, 1.092, 1.092, 1.094, 1.095, 1.097, 1.099, 1.098, 1.098, 1.096, 1.095, 1.094, 1.093, 1.089, 1.088, 1.084, 1.079, 1.075, 1.069, 1.063, 1.054, 1.053, 1.053, 1.058,
+                            1.073, 1.081, 1.085, 1.088, 1.091, 1.092, 1.093, 1.093, 1.093, 1.094, 1.096, 1.096, 1.099, 1.101, 1.102, 1.101, 1.099, 1.099, 1.098, 1.097, 1.095, 1.093, 1.089, 1.088, 1.084, 1.078, 1.075, 1.068, 1.059, 1.058, 1.059, 1.059,
+                            1.076, 1.083, 1.086, 1.089, 1.093, 1.094, 1.095, 1.095, 1.095, 1.096, 1.098, 1.099, 1.101, 1.103, 1.103, 1.103, 1.102, 1.101, 1.099, 1.099, 1.098, 1.095, 1.093, 1.091, 1.088, 1.084, 1.077, 1.073, 1.066, 1.063, 1.062, 1.066,
+                            1.081, 1.085, 1.089, 1.093, 1.095, 1.096, 1.096, 1.096, 1.097, 1.098, 1.099, 1.101, 1.103, 1.103, 1.104, 1.103, 1.103, 1.102, 1.102, 1.101, 1.099, 1.098, 1.096, 1.093, 1.091, 1.087, 1.082, 1.076, 1.067, 1.066, 1.068, 1.071,
+                            1.085, 1.088, 1.093, 1.095, 1.097, 1.098, 1.097, 1.098, 1.098, 1.099, 1.101, 1.102, 1.103, 1.104, 1.104, 1.104, 1.104, 1.104, 1.103, 1.103, 1.102, 1.099, 1.098, 1.096, 1.093, 1.089, 1.084, 1.078, 1.069, 1.069, 1.071, 1.073,
+                            1.085, 1.092, 1.094, 1.096, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.105, 1.104, 1.104, 1.103, 1.103, 1.102, 1.099, 1.097, 1.095, 1.091, 1.086, 1.081, 1.075, 1.072, 1.072, 1.074,
+                            1.087, 1.093, 1.096, 1.098, 1.101, 1.101, 1.101, 1.099, 1.101, 1.101, 1.101, 1.101, 1.103, 1.104, 1.105, 1.106, 1.106, 1.105, 1.105, 1.104, 1.104, 1.103, 1.101, 1.099, 1.096, 1.092, 1.087, 1.082, 1.075, 1.074, 1.075, 1.077,
+                            1.088, 1.094, 1.097, 1.099, 1.101, 1.101, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.104, 1.105, 1.106, 1.106, 1.106, 1.105, 1.105, 1.105, 1.103, 1.102, 1.099, 1.096, 1.093, 1.089, 1.083, 1.076, 1.076, 1.076, 1.077,
+                            1.089, 1.094, 1.098, 1.099, 1.101, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.107, 1.107, 1.107, 1.105, 1.104, 1.102, 1.099, 1.097, 1.094, 1.091, 1.083, 1.076, 1.076, 1.077, 1.078,
+                            1.091, 1.096, 1.098, 1.099, 1.099, 1.098, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.106, 1.107, 1.107, 1.105, 1.103, 1.102, 1.099, 1.097, 1.094, 1.089, 1.084, 1.077, 1.077, 1.077, 1.078,
+                            1.091, 1.094, 1.096, 1.097, 1.096, 1.096, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.102, 1.103, 1.103, 1.104, 1.105, 1.106, 1.106, 1.104, 1.103, 1.102, 1.099, 1.097, 1.094, 1.091, 1.084, 1.077, 1.077, 1.078, 1.079,
+                            1.091, 1.091, 1.093, 1.094, 1.093, 1.093, 1.094, 1.095, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.101, 1.101, 1.103, 1.104, 1.105, 1.104, 1.103, 1.101, 1.099, 1.097, 1.094, 1.089, 1.084, 1.077, 1.077, 1.078, 1.079,
+                            1.083, 1.087, 1.088, 1.089, 1.089, 1.091, 1.092, 1.094, 1.095, 1.095, 1.096, 1.096, 1.096, 1.097, 1.098, 1.098, 1.098, 1.101, 1.102, 1.103, 1.102, 1.102, 1.101, 1.098, 1.096, 1.092, 1.088, 1.083, 1.076, 1.076, 1.077, 1.079,
+                            1.077, 1.081, 1.082, 1.084, 1.086, 1.087, 1.089, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.092, 1.092, 1.094, 1.096, 1.098, 1.099, 1.101, 1.102, 1.099, 1.099, 1.097, 1.093, 1.089, 1.087, 1.081, 1.074, 1.073, 1.075, 1.077,
+                            1.067, 1.072, 1.075, 1.078, 1.082, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.087, 1.089, 1.091, 1.094, 1.097, 1.098, 1.099, 1.099, 1.098, 1.097, 1.094, 1.091, 1.087, 1.083, 1.079, 1.071, 1.071, 1.072, 1.075,
+                            1.061, 1.065, 1.069, 1.072, 1.075, 1.079, 1.081, 1.082, 1.082, 1.083, 1.083, 1.083, 1.083, 1.084, 1.087, 1.089, 1.092, 1.095, 1.097, 1.097, 1.097, 1.096, 1.094, 1.091, 1.087, 1.084, 1.079, 1.074, 1.068, 1.067, 1.068, 1.069,
+                            1.053, 1.058, 1.063, 1.067, 1.071, 1.073, 1.075, 1.076, 1.078, 1.078, 1.079, 1.079, 1.081, 1.083, 1.085, 1.088, 1.091, 1.093, 1.095, 1.095, 1.093, 1.092, 1.089, 1.086, 1.084, 1.081, 1.075, 1.071, 1.064, 1.061, 1.063, 1.065,
+                            1.049, 1.053, 1.059, 1.062, 1.065, 1.068, 1.071, 1.073, 1.074, 1.075, 1.077, 1.078, 1.079, 1.082, 1.085, 1.088, 1.089, 1.091, 1.091, 1.091, 1.089, 1.088, 1.085, 1.082, 1.079, 1.075, 1.071, 1.066, 1.059, 1.059, 1.061, 1.063,
+                            1.044, 1.048, 1.053, 1.057, 1.061, 1.065, 1.066, 1.069, 1.071, 1.073, 1.074, 1.076, 1.078, 1.082, 1.084, 1.086, 1.088, 1.089, 1.089, 1.088, 1.087, 1.084, 1.081, 1.079, 1.076, 1.071, 1.067, 1.062, 1.055, 1.055, 1.056, 1.059,
+                            1.039, 1.046, 1.049, 1.054, 1.057, 1.059, 1.062, 1.064, 1.067, 1.071, 1.073, 1.075, 1.077, 1.081, 1.082, 1.084, 1.084, 1.085, 1.085, 1.084, 1.083, 1.081, 1.077, 1.075, 1.071, 1.068, 1.063, 1.057, 1.052, 1.051, 1.054, 1.055,
+                            1.034, 1.041, 1.046, 1.049, 1.053, 1.055, 1.058, 1.062, 1.064, 1.067, 1.069, 1.072, 1.075, 1.078, 1.079, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.076, 1.074, 1.071, 1.068, 1.064, 1.061, 1.054, 1.049, 1.049, 1.052, 1.054,
+                            1.029, 1.034, 1.041, 1.043, 1.048, 1.051, 1.054, 1.056, 1.061, 1.063, 1.066, 1.067, 1.071, 1.073, 1.075, 1.075, 1.075, 1.076, 1.075, 1.075, 1.074, 1.072, 1.071, 1.068, 1.064, 1.061, 1.057, 1.051, 1.047, 1.046, 1.048, 1.054,
+                            1.019, 1.027, 1.033, 1.036, 1.042, 1.046, 1.049, 1.051, 1.053, 1.056, 1.058, 1.061, 1.064, 1.065, 1.066, 1.069, 1.071, 1.071, 1.069, 1.068, 1.068, 1.067, 1.064, 1.062, 1.059, 1.057, 1.052, 1.047, 1.044, 1.043, 1.048, 1.067,
+                            1.009, 1.018, 1.025, 1.028, 1.033, 1.039, 1.042, 1.043, 1.044, 1.046, 1.047, 1.049, 1.052, 1.055, 1.057, 1.059, 1.061, 1.061, 1.061, 1.062, 1.061, 1.058, 1.057, 1.056, 1.052, 1.051, 1.046, 1.042, 1.038, 1.038, 1.059, 1.067,
+                            1.005, 1.009, 1.017, 1.023, 1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.032, 1.036, 1.041, 1.044, 1.046, 1.047, 1.047, 1.048, 1.049, 1.049, 1.049, 1.048, 1.046, 1.044, 1.042, 1.039, 1.039, 1.034, 1.033, 1.048, 1.062, 1.062
+                        ]
+                    },
+                    {
+                        "ct": 5000,
+                        "table":
+                        [
+                            1.051, 1.052, 1.057, 1.061, 1.061, 1.061, 1.061, 1.061, 1.061, 1.059, 1.059, 1.059, 1.062, 1.063, 1.065, 1.067, 1.066, 1.064, 1.063, 1.062, 1.058, 1.055, 1.049, 1.047, 1.043, 1.039, 1.037, 1.029, 1.028, 1.024, 1.026, 1.027,
+                            1.048, 1.049, 1.052, 1.053, 1.053, 1.051, 1.051, 1.051, 1.049, 1.048, 1.049, 1.051, 1.052, 1.055, 1.055, 1.056, 1.057, 1.058, 1.058, 1.055, 1.053, 1.051, 1.047, 1.044, 1.041, 1.038, 1.034, 1.029, 1.025, 1.024, 1.025, 1.029,
+                            1.047, 1.048, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.048, 1.048, 1.048, 1.051, 1.051, 1.053, 1.055, 1.056, 1.056, 1.056, 1.055, 1.054, 1.051, 1.049, 1.046, 1.043, 1.041, 1.037, 1.035, 1.029, 1.026, 1.026, 1.029, 1.031,
+                            1.046, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.049, 1.051, 1.052, 1.055, 1.056, 1.056, 1.057, 1.057, 1.056, 1.054, 1.052, 1.049, 1.047, 1.045, 1.041, 1.039, 1.035, 1.032, 1.029, 1.031, 1.032, 1.038,
+                            1.047, 1.049, 1.051, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.051, 1.053, 1.055, 1.056, 1.058, 1.058, 1.056, 1.056, 1.053, 1.051, 1.049, 1.048, 1.045, 1.044, 1.041, 1.038, 1.034, 1.031, 1.031, 1.036, 1.041,
+                            1.046, 1.048, 1.048, 1.048, 1.048, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.051, 1.052, 1.054, 1.055, 1.057, 1.057, 1.057, 1.056, 1.054, 1.051, 1.049, 1.049, 1.047, 1.044, 1.042, 1.039, 1.035, 1.032, 1.033, 1.039, 1.043,
+                            1.046, 1.047, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.049, 1.049, 1.051, 1.053, 1.054, 1.055, 1.057, 1.056, 1.055, 1.055, 1.054, 1.053, 1.051, 1.049, 1.048, 1.046, 1.043, 1.042, 1.038, 1.033, 1.034, 1.042, 1.047,
+                            1.045, 1.046, 1.048, 1.049, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.049, 1.051, 1.053, 1.055, 1.055, 1.056, 1.055, 1.055, 1.054, 1.054, 1.053, 1.052, 1.051, 1.049, 1.047, 1.045, 1.043, 1.041, 1.036, 1.037, 1.044, 1.047,
+                            1.044, 1.047, 1.048, 1.049, 1.051, 1.049, 1.048, 1.048, 1.048, 1.048, 1.049, 1.051, 1.052, 1.055, 1.056, 1.056, 1.055, 1.054, 1.054, 1.053, 1.053, 1.052, 1.049, 1.049, 1.048, 1.046, 1.044, 1.042, 1.037, 1.037, 1.045, 1.049,
+                            1.045, 1.047, 1.048, 1.051, 1.051, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.051, 1.051, 1.053, 1.055, 1.055, 1.054, 1.053, 1.053, 1.053, 1.053, 1.051, 1.051, 1.051, 1.048, 1.047, 1.045, 1.042, 1.039, 1.039, 1.048, 1.051,
+                            1.046, 1.047, 1.049, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.053, 1.054, 1.054, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.046, 1.043, 1.039, 1.039, 1.051, 1.055,
+                            1.046, 1.048, 1.049, 1.051, 1.051, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.051, 1.051, 1.049, 1.048, 1.047, 1.043, 1.041, 1.041, 1.053, 1.056,
+                            1.047, 1.049, 1.051, 1.051, 1.051, 1.049, 1.049, 1.047, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.044, 1.042, 1.042, 1.054, 1.058,
+                            1.048, 1.049, 1.051, 1.052, 1.051, 1.049, 1.048, 1.047, 1.046, 1.046, 1.047, 1.047, 1.048, 1.051, 1.051, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.045, 1.043, 1.043, 1.055, 1.057,
+                            1.048, 1.051, 1.052, 1.051, 1.051, 1.049, 1.047, 1.046, 1.046, 1.046, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.044, 1.056, 1.058,
+                            1.049, 1.051, 1.052, 1.051, 1.049, 1.048, 1.046, 1.046, 1.044, 1.046, 1.046, 1.047, 1.047, 1.049, 1.051, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.044, 1.056, 1.059,
+                            1.051, 1.052, 1.053, 1.051, 1.048, 1.047, 1.045, 1.043, 1.043, 1.044, 1.045, 1.046, 1.047, 1.048, 1.051, 1.051, 1.051, 1.053, 1.053, 1.053, 1.052, 1.051, 1.049, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.046, 1.057, 1.061,
+                            1.051, 1.051, 1.049, 1.048, 1.047, 1.045, 1.043, 1.043, 1.044, 1.044, 1.045, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.046, 1.055, 1.083, 1.092,
+                            1.048, 1.048, 1.047, 1.045, 1.045, 1.043, 1.042, 1.042, 1.043, 1.043, 1.044, 1.044, 1.044, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.055, 1.073, 1.098, 1.099,
+                            1.044, 1.044, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.041, 1.042, 1.041, 1.041, 1.041, 1.042, 1.044, 1.045, 1.047, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.048, 1.047, 1.048, 1.048, 1.072, 1.087, 1.097, 1.099,
+                            1.038, 1.037, 1.036, 1.036, 1.036, 1.037, 1.037, 1.038, 1.038, 1.038, 1.039, 1.038, 1.038, 1.038, 1.039, 1.041, 1.043, 1.045, 1.047, 1.048, 1.049, 1.048, 1.048, 1.047, 1.047, 1.045, 1.045, 1.048, 1.071, 1.087, 1.096, 1.098,
+                            1.029, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.035, 1.035, 1.034, 1.033, 1.033, 1.034, 1.034, 1.036, 1.039, 1.041, 1.044, 1.045, 1.047, 1.048, 1.046, 1.046, 1.046, 1.044, 1.044, 1.043, 1.042, 1.049, 1.081, 1.095, 1.096,
+                            1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.033, 1.035, 1.038, 1.039, 1.043, 1.044, 1.045, 1.045, 1.044, 1.044, 1.043, 1.043, 1.041, 1.041, 1.039, 1.039, 1.049, 1.087, 1.095,
+                            1.021, 1.023, 1.025, 1.026, 1.027, 1.028, 1.029, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.032, 1.035, 1.038, 1.041, 1.043, 1.045, 1.044, 1.044, 1.042, 1.041, 1.041, 1.039, 1.039, 1.039, 1.037, 1.035, 1.036, 1.054, 1.071,
+                            1.017, 1.021, 1.023, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.027, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.041, 1.042, 1.044, 1.044, 1.043, 1.041, 1.039, 1.039, 1.038, 1.037, 1.036, 1.034, 1.034, 1.034, 1.046, 1.051,
+                            1.014, 1.019, 1.022, 1.024, 1.024, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.031, 1.033, 1.035, 1.038, 1.041, 1.042, 1.042, 1.043, 1.042, 1.041, 1.039, 1.038, 1.038, 1.037, 1.036, 1.034, 1.033, 1.032, 1.033, 1.045, 1.047,
+                            1.014, 1.019, 1.021, 1.023, 1.024, 1.025, 1.025, 1.026, 1.027, 1.029, 1.031, 1.032, 1.035, 1.037, 1.039, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.039, 1.038, 1.037, 1.037, 1.035, 1.034, 1.033, 1.033, 1.033, 1.044, 1.047,
+                            1.015, 1.019, 1.019, 1.022, 1.024, 1.025, 1.025, 1.027, 1.029, 1.031, 1.032, 1.033, 1.036, 1.038, 1.039, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.036, 1.034, 1.034, 1.033, 1.034, 1.043, 1.046,
+                            1.014, 1.016, 1.019, 1.019, 1.022, 1.023, 1.025, 1.026, 1.029, 1.031, 1.032, 1.033, 1.036, 1.037, 1.039, 1.039, 1.039, 1.041, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.037, 1.036, 1.034, 1.034, 1.033, 1.034, 1.042, 1.045,
+                            1.011, 1.013, 1.016, 1.018, 1.021, 1.023, 1.024, 1.024, 1.026, 1.028, 1.029, 1.031, 1.033, 1.035, 1.036, 1.036, 1.037, 1.038, 1.038, 1.038, 1.038, 1.038, 1.037, 1.037, 1.036, 1.036, 1.034, 1.033, 1.032, 1.033, 1.039, 1.044,
+                            1.003, 1.007, 1.012, 1.014, 1.016, 1.019, 1.022, 1.022, 1.022, 1.022, 1.024, 1.025, 1.026, 1.028, 1.029, 1.031, 1.033, 1.034, 1.034, 1.036, 1.036, 1.035, 1.033, 1.034, 1.033, 1.033, 1.032, 1.031, 1.029, 1.029, 1.034, 1.038,
+                            1.001, 1.002, 1.008, 1.011, 1.013, 1.015, 1.014, 1.014, 1.013, 1.012, 1.013, 1.015, 1.019, 1.022, 1.023, 1.025, 1.026, 1.026, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.025, 1.025, 1.026, 1.031, 1.035
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.179, 1.187, 1.194, 1.197, 1.199, 1.201, 1.199, 1.199, 1.198, 1.198, 1.196, 1.195, 1.192, 1.192, 1.191, 1.191, 1.187, 1.182, 1.176, 1.171, 1.162, 1.156, 1.143, 1.132, 1.114, 1.097, 1.081, 1.061, 1.041, 1.019, 1.006, 1.001,
+                            1.187, 1.189, 1.194, 1.197, 1.199, 1.201, 1.201, 1.201, 1.199, 1.199, 1.196, 1.195, 1.195, 1.194, 1.193, 1.192, 1.189, 1.187, 1.181, 1.175, 1.168, 1.157, 1.152, 1.135, 1.122, 1.104, 1.086, 1.069, 1.047, 1.027, 1.012, 1.005,
+                            1.188, 1.189, 1.195, 1.199, 1.201, 1.202, 1.203, 1.204, 1.202, 1.201, 1.201, 1.199, 1.199, 1.201, 1.199, 1.196, 1.195, 1.192, 1.189, 1.183, 1.175, 1.166, 1.155, 1.146, 1.129, 1.117, 1.099, 1.082, 1.061, 1.041, 1.025, 1.012,
+                            1.189, 1.197, 1.199, 1.203, 1.205, 1.207, 1.208, 1.208, 1.208, 1.208, 1.207, 1.207, 1.206, 1.207, 1.207, 1.205, 1.203, 1.202, 1.195, 1.189, 1.183, 1.174, 1.165, 1.154, 1.138, 1.125, 1.108, 1.088, 1.071, 1.049, 1.041, 1.029,
+                            1.198, 1.199, 1.204, 1.207, 1.209, 1.209, 1.209, 1.209, 1.211, 1.211, 1.209, 1.209, 1.209, 1.212, 1.212, 1.212, 1.209, 1.206, 1.202, 1.196, 1.189, 1.182, 1.172, 1.162, 1.151, 1.134, 1.118, 1.099, 1.079, 1.063, 1.049, 1.041,
+                            1.199, 1.204, 1.206, 1.209, 1.211, 1.211, 1.212, 1.213, 1.214, 1.214, 1.214, 1.213, 1.215, 1.216, 1.216, 1.216, 1.213, 1.209, 1.208, 1.202, 1.196, 1.188, 1.179, 1.169, 1.157, 1.142, 1.125, 1.111, 1.088, 1.073, 1.062, 1.054,
+                            1.202, 1.205, 1.208, 1.211, 1.213, 1.215, 1.215, 1.216, 1.216, 1.216, 1.218, 1.219, 1.219, 1.221, 1.221, 1.221, 1.218, 1.215, 1.211, 1.207, 1.202, 1.194, 1.186, 1.177, 1.162, 1.149, 1.137, 1.116, 1.098, 1.082, 1.073, 1.063,
+                            1.202, 1.205, 1.209, 1.213, 1.215, 1.217, 1.218, 1.219, 1.219, 1.221, 1.222, 1.223, 1.223, 1.224, 1.223, 1.223, 1.221, 1.218, 1.215, 1.211, 1.207, 1.201, 1.189, 1.181, 1.171, 1.158, 1.143, 1.129, 1.107, 1.089, 1.081, 1.074,
+                            1.203, 1.207, 1.209, 1.215, 1.217, 1.219, 1.221, 1.222, 1.222, 1.224, 1.225, 1.225, 1.226, 1.227, 1.227, 1.225, 1.223, 1.221, 1.218, 1.215, 1.209, 1.202, 1.195, 1.186, 1.178, 1.164, 1.151, 1.133, 1.115, 1.098, 1.088, 1.083,
+                            1.202, 1.209, 1.213, 1.217, 1.219, 1.221, 1.222, 1.223, 1.224, 1.225, 1.226, 1.228, 1.228, 1.228, 1.228, 1.226, 1.225, 1.223, 1.219, 1.216, 1.212, 1.205, 1.198, 1.192, 1.183, 1.169, 1.154, 1.142, 1.122, 1.106, 1.097, 1.089,
+                            1.206, 1.211, 1.215, 1.218, 1.221, 1.223, 1.224, 1.224, 1.225, 1.226, 1.227, 1.229, 1.229, 1.229, 1.229, 1.228, 1.226, 1.224, 1.222, 1.218, 1.215, 1.209, 1.203, 1.196, 1.188, 1.175, 1.161, 1.149, 1.127, 1.112, 1.103, 1.097,
+                            1.209, 1.213, 1.217, 1.218, 1.222, 1.224, 1.225, 1.225, 1.226, 1.227, 1.229, 1.229, 1.229, 1.229, 1.229, 1.229, 1.227, 1.225, 1.223, 1.221, 1.217, 1.213, 1.207, 1.199, 1.191, 1.179, 1.164, 1.149, 1.133, 1.117, 1.109, 1.103,
+                            1.211, 1.216, 1.218, 1.219, 1.224, 1.225, 1.226, 1.226, 1.227, 1.228, 1.229, 1.229, 1.229, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.222, 1.219, 1.215, 1.209, 1.202, 1.194, 1.182, 1.169, 1.153, 1.137, 1.122, 1.115, 1.108,
+                            1.211, 1.215, 1.219, 1.219, 1.224, 1.225, 1.227, 1.227, 1.228, 1.229, 1.229, 1.229, 1.229, 1.231, 1.231, 1.231, 1.229, 1.227, 1.226, 1.223, 1.221, 1.217, 1.211, 1.203, 1.195, 1.183, 1.171, 1.156, 1.141, 1.123, 1.116, 1.113,
+                            1.209, 1.215, 1.219, 1.222, 1.225, 1.225, 1.226, 1.226, 1.227, 1.228, 1.229, 1.229, 1.231, 1.231, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.222, 1.217, 1.211, 1.204, 1.196, 1.184, 1.174, 1.159, 1.142, 1.128, 1.121, 1.114,
+                            1.208, 1.215, 1.218, 1.221, 1.223, 1.223, 1.224, 1.224, 1.226, 1.227, 1.227, 1.228, 1.229, 1.231, 1.231, 1.231, 1.231, 1.229, 1.229, 1.226, 1.223, 1.218, 1.213, 1.207, 1.197, 1.188, 1.176, 1.161, 1.143, 1.129, 1.121, 1.114,
+                            1.208, 1.213, 1.216, 1.219, 1.221, 1.221, 1.222, 1.223, 1.224, 1.225, 1.226, 1.227, 1.228, 1.231, 1.231, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.218, 1.214, 1.207, 1.197, 1.188, 1.177, 1.161, 1.145, 1.131, 1.122, 1.115,
+                            1.207, 1.208, 1.214, 1.214, 1.217, 1.218, 1.219, 1.221, 1.223, 1.224, 1.225, 1.225, 1.226, 1.227, 1.228, 1.228, 1.229, 1.229, 1.228, 1.226, 1.223, 1.219, 1.215, 1.207, 1.198, 1.188, 1.177, 1.161, 1.147, 1.131, 1.123, 1.116,
+                            1.199, 1.204, 1.208, 1.208, 1.211, 1.212, 1.215, 1.218, 1.221, 1.221, 1.222, 1.223, 1.223, 1.225, 1.225, 1.227, 1.226, 1.227, 1.226, 1.225, 1.223, 1.219, 1.212, 1.207, 1.198, 1.187, 1.177, 1.161, 1.146, 1.131, 1.123, 1.116,
+                            1.193, 1.196, 1.199, 1.201, 1.204, 1.206, 1.209, 1.213, 1.215, 1.217, 1.218, 1.219, 1.221, 1.221, 1.221, 1.222, 1.223, 1.224, 1.224, 1.223, 1.222, 1.218, 1.211, 1.207, 1.197, 1.187, 1.175, 1.161, 1.146, 1.129, 1.122, 1.113,
+                            1.177, 1.182, 1.188, 1.192, 1.195, 1.198, 1.203, 1.208, 1.211, 1.212, 1.214, 1.214, 1.214, 1.215, 1.216, 1.217, 1.219, 1.221, 1.223, 1.222, 1.221, 1.216, 1.212, 1.204, 1.197, 1.186, 1.175, 1.159, 1.143, 1.129, 1.119, 1.114,
+                            1.162, 1.171, 1.177, 1.183, 1.187, 1.192, 1.196, 1.201, 1.204, 1.205, 1.206, 1.207, 1.208, 1.209, 1.212, 1.215, 1.216, 1.219, 1.221, 1.219, 1.218, 1.214, 1.211, 1.203, 1.195, 1.184, 1.173, 1.157, 1.139, 1.128, 1.117, 1.113,
+                            1.149, 1.158, 1.167, 1.172, 1.178, 1.184, 1.189, 1.193, 1.195, 1.196, 1.201, 1.201, 1.201, 1.206, 1.209, 1.212, 1.214, 1.216, 1.218, 1.218, 1.216, 1.212, 1.207, 1.201, 1.192, 1.181, 1.167, 1.155, 1.136, 1.121, 1.114, 1.107,
+                            1.137, 1.147, 1.155, 1.161, 1.169, 1.174, 1.179, 1.183, 1.188, 1.189, 1.193, 1.196, 1.199, 1.201, 1.206, 1.209, 1.213, 1.214, 1.215, 1.214, 1.211, 1.208, 1.202, 1.194, 1.186, 1.177, 1.163, 1.149, 1.132, 1.117, 1.109, 1.104,
+                            1.126, 1.136, 1.144, 1.151, 1.157, 1.163, 1.171, 1.177, 1.181, 1.183, 1.186, 1.191, 1.196, 1.201, 1.204, 1.207, 1.211, 1.213, 1.213, 1.211, 1.209, 1.204, 1.199, 1.191, 1.183, 1.172, 1.158, 1.144, 1.127, 1.112, 1.105, 1.098,
+                            1.114, 1.125, 1.134, 1.142, 1.147, 1.155, 1.161, 1.166, 1.171, 1.177, 1.181, 1.186, 1.191, 1.197, 1.202, 1.204, 1.207, 1.209, 1.209, 1.209, 1.205, 1.201, 1.195, 1.188, 1.178, 1.168, 1.154, 1.139, 1.122, 1.106, 1.099, 1.095,
+                            1.107, 1.114, 1.123, 1.132, 1.137, 1.144, 1.152, 1.157, 1.162, 1.169, 1.176, 1.181, 1.187, 1.193, 1.198, 1.202, 1.204, 1.205, 1.206, 1.204, 1.201, 1.197, 1.191, 1.183, 1.175, 1.162, 1.149, 1.133, 1.117, 1.103, 1.095, 1.088,
+                            1.094, 1.101, 1.112, 1.117, 1.129, 1.133, 1.141, 1.149, 1.156, 1.161, 1.169, 1.175, 1.182, 1.187, 1.192, 1.196, 1.199, 1.201, 1.201, 1.199, 1.196, 1.192, 1.187, 1.181, 1.171, 1.157, 1.145, 1.128, 1.114, 1.099, 1.088, 1.085,
+                            1.083, 1.091, 1.099, 1.104, 1.112, 1.121, 1.128, 1.139, 1.146, 1.154, 1.159, 1.168, 1.174, 1.182, 1.187, 1.188, 1.193, 1.195, 1.195, 1.194, 1.191, 1.188, 1.182, 1.172, 1.163, 1.154, 1.138, 1.123, 1.109, 1.096, 1.085, 1.079,
+                            1.065, 1.074, 1.082, 1.092, 1.101, 1.109, 1.118, 1.126, 1.133, 1.143, 1.149, 1.158, 1.164, 1.171, 1.176, 1.179, 1.185, 1.186, 1.186, 1.185, 1.184, 1.181, 1.172, 1.165, 1.157, 1.148, 1.133, 1.118, 1.101, 1.086, 1.079, 1.071,
+                            1.045, 1.059, 1.067, 1.076, 1.084, 1.097, 1.106, 1.113, 1.123, 1.129, 1.134, 1.141, 1.149, 1.155, 1.161, 1.165, 1.172, 1.175, 1.177, 1.176, 1.174, 1.172, 1.164, 1.156, 1.148, 1.135, 1.121, 1.108, 1.091, 1.079, 1.067, 1.057,
+                            1.038, 1.045, 1.057, 1.067, 1.075, 1.083, 1.094, 1.102, 1.109, 1.115, 1.122, 1.131, 1.136, 1.144, 1.151, 1.155, 1.161, 1.161, 1.164, 1.164, 1.164, 1.161, 1.158, 1.147, 1.138, 1.125, 1.111, 1.095, 1.079, 1.067, 1.057, 1.051
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 2400,
+                        "table":
+                        [
+                            1.261, 1.265, 1.267, 1.273, 1.276, 1.283, 1.283, 1.291, 1.294, 1.299, 1.301, 1.304, 1.304, 1.305, 1.297, 1.301, 1.304, 1.303, 1.301, 1.298, 1.285, 1.271, 1.252, 1.234, 1.204, 1.177, 1.148, 1.115, 1.083, 1.048, 1.004, 1.001,
+                            1.274, 1.283, 1.289, 1.292, 1.299, 1.302, 1.309, 1.314, 1.317, 1.321, 1.322, 1.326, 1.329, 1.332, 1.335, 1.335, 1.335, 1.329, 1.322, 1.311, 1.299, 1.286, 1.265, 1.249, 1.224, 1.201, 1.173, 1.141, 1.111, 1.077, 1.031, 1.004,
+                            1.287, 1.292, 1.299, 1.303, 1.306, 1.312, 1.317, 1.322, 1.327, 1.331, 1.334, 1.338, 1.341, 1.344, 1.345, 1.346, 1.344, 1.339, 1.335, 1.324, 1.312, 1.299, 1.286, 1.265, 1.245, 1.218, 1.193, 1.159, 1.126, 1.094, 1.051, 1.028,
+                            1.297, 1.299, 1.307, 1.311, 1.314, 1.321, 1.325, 1.329, 1.335, 1.339, 1.344, 1.348, 1.349, 1.353, 1.353, 1.355, 1.354, 1.351, 1.345, 1.337, 1.324, 1.311, 1.299, 1.279, 1.256, 1.232, 1.202, 1.173, 1.141, 1.109, 1.066, 1.042,
+                            1.303, 1.309, 1.315, 1.318, 1.324, 1.327, 1.334, 1.339, 1.343, 1.347, 1.352, 1.354, 1.361, 1.364, 1.366, 1.365, 1.365, 1.361, 1.358, 1.349, 1.338, 1.323, 1.309, 1.292, 1.269, 1.244, 1.217, 1.189, 1.157, 1.121, 1.079, 1.057,
+                            1.312, 1.316, 1.322, 1.326, 1.329, 1.337, 1.341, 1.346, 1.351, 1.353, 1.358, 1.365, 1.368, 1.371, 1.373, 1.373, 1.373, 1.367, 1.363, 1.358, 1.349, 1.336, 1.319, 1.301, 1.285, 1.257, 1.231, 1.201, 1.168, 1.137, 1.094, 1.065,
+                            1.317, 1.323, 1.329, 1.332, 1.338, 1.342, 1.349, 1.353, 1.356, 1.361, 1.367, 1.372, 1.375, 1.379, 1.381, 1.381, 1.379, 1.377, 1.371, 1.364, 1.357, 1.345, 1.333, 1.312, 1.292, 1.267, 1.239, 1.211, 1.181, 1.142, 1.105, 1.078,
+                            1.321, 1.329, 1.334, 1.341, 1.343, 1.351, 1.355, 1.359, 1.361, 1.368, 1.373, 1.377, 1.381, 1.385, 1.387, 1.387, 1.385, 1.381, 1.376, 1.371, 1.362, 1.351, 1.338, 1.319, 1.299, 1.276, 1.252, 1.221, 1.189, 1.156, 1.113, 1.086,
+                            1.328, 1.334, 1.341, 1.344, 1.351, 1.353, 1.359, 1.363, 1.368, 1.374, 1.377, 1.382, 1.385, 1.389, 1.392, 1.391, 1.389, 1.387, 1.381, 1.375, 1.367, 1.359, 1.345, 1.327, 1.311, 1.284, 1.261, 1.231, 1.201, 1.169, 1.121, 1.091,
+                            1.331, 1.338, 1.343, 1.347, 1.352, 1.358, 1.362, 1.367, 1.371, 1.375, 1.379, 1.385, 1.389, 1.393, 1.395, 1.396, 1.393, 1.391, 1.385, 1.378, 1.373, 1.362, 1.349, 1.335, 1.313, 1.291, 1.265, 1.238, 1.209, 1.175, 1.129, 1.098,
+                            1.331, 1.341, 1.345, 1.349, 1.355, 1.359, 1.364, 1.368, 1.372, 1.378, 1.381, 1.388, 1.392, 1.394, 1.397, 1.397, 1.396, 1.392, 1.388, 1.382, 1.374, 1.363, 1.353, 1.337, 1.317, 1.295, 1.274, 1.244, 1.216, 1.183, 1.138, 1.101,
+                            1.329, 1.341, 1.345, 1.351, 1.355, 1.361, 1.365, 1.368, 1.373, 1.377, 1.381, 1.387, 1.391, 1.394, 1.396, 1.397, 1.397, 1.393, 1.389, 1.383, 1.375, 1.366, 1.357, 1.339, 1.321, 1.299, 1.276, 1.247, 1.219, 1.187, 1.141, 1.108,
+                            1.331, 1.342, 1.345, 1.351, 1.357, 1.361, 1.365, 1.369, 1.372, 1.377, 1.381, 1.385, 1.391, 1.393, 1.396, 1.396, 1.396, 1.394, 1.389, 1.384, 1.375, 1.368, 1.358, 1.339, 1.324, 1.302, 1.278, 1.251, 1.221, 1.192, 1.146, 1.113,
+                            1.332, 1.341, 1.346, 1.352, 1.356, 1.361, 1.365, 1.369, 1.372, 1.376, 1.379, 1.384, 1.389, 1.392, 1.395, 1.396, 1.396, 1.394, 1.391, 1.386, 1.381, 1.368, 1.358, 1.341, 1.325, 1.303, 1.279, 1.252, 1.224, 1.194, 1.148, 1.117,
+                            1.332, 1.339, 1.346, 1.351, 1.355, 1.358, 1.363, 1.367, 1.371, 1.375, 1.378, 1.384, 1.387, 1.392, 1.395, 1.396, 1.395, 1.393, 1.389, 1.385, 1.381, 1.368, 1.357, 1.342, 1.325, 1.302, 1.279, 1.252, 1.224, 1.195, 1.151, 1.119,
+                            1.332, 1.338, 1.344, 1.349, 1.354, 1.357, 1.361, 1.366, 1.369, 1.373, 1.377, 1.383, 1.388, 1.391, 1.392, 1.395, 1.393, 1.393, 1.391, 1.386, 1.379, 1.367, 1.356, 1.342, 1.324, 1.302, 1.279, 1.253, 1.224, 1.195, 1.152, 1.118,
+                            1.331, 1.335, 1.339, 1.346, 1.351, 1.355, 1.357, 1.363, 1.367, 1.372, 1.377, 1.381, 1.386, 1.388, 1.392, 1.393, 1.393, 1.392, 1.389, 1.385, 1.377, 1.367, 1.356, 1.341, 1.325, 1.303, 1.279, 1.252, 1.224, 1.195, 1.152, 1.118,
+                            1.324, 1.329, 1.335, 1.339, 1.347, 1.351, 1.356, 1.359, 1.364, 1.369, 1.375, 1.378, 1.383, 1.388, 1.389, 1.392, 1.392, 1.391, 1.387, 1.383, 1.376, 1.366, 1.355, 1.342, 1.324, 1.302, 1.279, 1.253, 1.225, 1.196, 1.153, 1.116,
+                            1.314, 1.318, 1.328, 1.333, 1.339, 1.345, 1.349, 1.356, 1.361, 1.366, 1.371, 1.376, 1.381, 1.383, 1.387, 1.388, 1.388, 1.387, 1.385, 1.381, 1.376, 1.367, 1.356, 1.341, 1.323, 1.303, 1.279, 1.252, 1.226, 1.196, 1.153, 1.117,
+                            1.303, 1.312, 1.318, 1.322, 1.328, 1.338, 1.345, 1.348, 1.357, 1.362, 1.367, 1.371, 1.375, 1.379, 1.382, 1.384, 1.384, 1.384, 1.382, 1.379, 1.375, 1.365, 1.356, 1.339, 1.322, 1.301, 1.278, 1.251, 1.224, 1.195, 1.152, 1.117,
+                            1.288, 1.299, 1.305, 1.313, 1.321, 1.328, 1.339, 1.345, 1.348, 1.355, 1.362, 1.365, 1.369, 1.373, 1.377, 1.379, 1.381, 1.381, 1.381, 1.377, 1.372, 1.363, 1.353, 1.338, 1.319, 1.299, 1.277, 1.249, 1.219, 1.194, 1.149, 1.116,
+                            1.277, 1.286, 1.293, 1.302, 1.309, 1.319, 1.327, 1.336, 1.342, 1.346, 1.351, 1.357, 1.362, 1.367, 1.371, 1.374, 1.375, 1.377, 1.377, 1.374, 1.368, 1.359, 1.349, 1.335, 1.318, 1.296, 1.271, 1.245, 1.217, 1.193, 1.147, 1.112,
+                            1.256, 1.271, 1.277, 1.288, 1.293, 1.305, 1.316, 1.322, 1.331, 1.336, 1.343, 1.348, 1.354, 1.359, 1.366, 1.368, 1.371, 1.373, 1.372, 1.368, 1.362, 1.356, 1.343, 1.329, 1.311, 1.291, 1.266, 1.238, 1.214, 1.184, 1.142, 1.111,
+                            1.234, 1.251, 1.261, 1.269, 1.282, 1.291, 1.302, 1.313, 1.319, 1.327, 1.334, 1.341, 1.348, 1.353, 1.359, 1.365, 1.367, 1.367, 1.366, 1.362, 1.356, 1.349, 1.339, 1.322, 1.304, 1.284, 1.261, 1.235, 1.208, 1.178, 1.136, 1.104,
+                            1.218, 1.231, 1.244, 1.253, 1.265, 1.278, 1.289, 1.297, 1.304, 1.316, 1.325, 1.333, 1.339, 1.346, 1.351, 1.356, 1.361, 1.361, 1.361, 1.355, 1.349, 1.341, 1.328, 1.314, 1.299, 1.278, 1.255, 1.229, 1.203, 1.175, 1.133, 1.099,
+                            1.199, 1.214, 1.227, 1.235, 1.247, 1.262, 1.271, 1.283, 1.292, 1.301, 1.311, 1.322, 1.331, 1.338, 1.342, 1.349, 1.353, 1.353, 1.351, 1.347, 1.341, 1.331, 1.321, 1.308, 1.289, 1.269, 1.246, 1.219, 1.195, 1.167, 1.127, 1.096,
+                            1.179, 1.195, 1.207, 1.218, 1.231, 1.241, 1.257, 1.267, 1.278, 1.288, 1.297, 1.309, 1.321, 1.328, 1.333, 1.338, 1.344, 1.346, 1.342, 1.336, 1.331, 1.323, 1.312, 1.298, 1.284, 1.262, 1.241, 1.214, 1.191, 1.162, 1.118, 1.092,
+                            1.159, 1.174, 1.189, 1.198, 1.209, 1.225, 1.238, 1.252, 1.261, 1.272, 1.286, 1.295, 1.306, 1.314, 1.324, 1.327, 1.332, 1.333, 1.333, 1.329, 1.322, 1.314, 1.303, 1.293, 1.275, 1.253, 1.231, 1.207, 1.181, 1.151, 1.112, 1.081,
+                            1.142, 1.156, 1.168, 1.179, 1.192, 1.206, 1.221, 1.232, 1.243, 1.255, 1.269, 1.278, 1.291, 1.303, 1.311, 1.313, 1.316, 1.319, 1.321, 1.318, 1.314, 1.303, 1.295, 1.281, 1.268, 1.248, 1.225, 1.197, 1.174, 1.147, 1.106, 1.077,
+                            1.119, 1.134, 1.145, 1.159, 1.171, 1.186, 1.199, 1.212, 1.223, 1.236, 1.247, 1.259, 1.272, 1.281, 1.291, 1.297, 1.299, 1.307, 1.308, 1.306, 1.302, 1.294, 1.284, 1.272, 1.257, 1.239, 1.215, 1.188, 1.163, 1.136, 1.099, 1.069,
+                            1.101, 1.114, 1.126, 1.134, 1.151, 1.163, 1.181, 1.189, 1.202, 1.216, 1.229, 1.239, 1.252, 1.262, 1.269, 1.283, 1.288, 1.293, 1.294, 1.292, 1.289, 1.284, 1.272, 1.261, 1.244, 1.228, 1.204, 1.178, 1.154, 1.131, 1.089, 1.062,
+                            1.087, 1.098, 1.112, 1.124, 1.134, 1.148, 1.161, 1.175, 1.185, 1.201, 1.212, 1.224, 1.236, 1.248, 1.259, 1.268, 1.274, 1.275, 1.275, 1.276, 1.276, 1.272, 1.263, 1.247, 1.232, 1.212, 1.192, 1.166, 1.143, 1.115, 1.078, 1.051
+                        ]
+                    },
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            1.333, 1.336, 1.336, 1.343, 1.347, 1.356, 1.361, 1.364, 1.366, 1.371, 1.371, 1.377, 1.382, 1.385, 1.385, 1.385, 1.381, 1.381, 1.375, 1.369, 1.363, 1.348, 1.326, 1.298, 1.269, 1.241, 1.207, 1.169, 1.134, 1.096, 1.044, 1.038,
+                            1.345, 1.351, 1.361, 1.365, 1.369, 1.376, 1.384, 1.389, 1.389, 1.395, 1.398, 1.403, 1.406, 1.409, 1.411, 1.409, 1.409, 1.408, 1.401, 1.389, 1.379, 1.359, 1.339, 1.319, 1.294, 1.264, 1.235, 1.199, 1.163, 1.127, 1.074, 1.044,
+                            1.359, 1.362, 1.375, 1.377, 1.381, 1.388, 1.394, 1.397, 1.401, 1.403, 1.407, 1.413, 1.417, 1.419, 1.422, 1.425, 1.425, 1.419, 1.413, 1.403, 1.391, 1.379, 1.359, 1.339, 1.315, 1.287, 1.254, 1.219, 1.184, 1.151, 1.098, 1.071,
+                            1.366, 1.376, 1.382, 1.386, 1.391, 1.397, 1.401, 1.404, 1.408, 1.411, 1.419, 1.421, 1.426, 1.429, 1.433, 1.433, 1.431, 1.429, 1.423, 1.415, 1.403, 1.391, 1.373, 1.351, 1.329, 1.301, 1.269, 1.231, 1.198, 1.164, 1.118, 1.089,
+                            1.378, 1.383, 1.393, 1.396, 1.399, 1.403, 1.411, 1.414, 1.419, 1.423, 1.426, 1.432, 1.439, 1.444, 1.445, 1.444, 1.442, 1.439, 1.432, 1.424, 1.416, 1.399, 1.385, 1.361, 1.343, 1.314, 1.283, 1.249, 1.215, 1.179, 1.131, 1.098,
+                            1.385, 1.394, 1.399, 1.401, 1.406, 1.412, 1.418, 1.423, 1.427, 1.431, 1.435, 1.442, 1.446, 1.451, 1.452, 1.451, 1.448, 1.445, 1.443, 1.434, 1.424, 1.411, 1.393, 1.377, 1.352, 1.329, 1.296, 1.264, 1.228, 1.195, 1.145, 1.114,
+                            1.391, 1.401, 1.404, 1.408, 1.413, 1.419, 1.425, 1.428, 1.432, 1.437, 1.442, 1.451, 1.453, 1.456, 1.459, 1.461, 1.459, 1.453, 1.449, 1.443, 1.434, 1.419, 1.404, 1.387, 1.362, 1.338, 1.306, 1.272, 1.238, 1.207, 1.153, 1.126,
+                            1.399, 1.405, 1.412, 1.415, 1.419, 1.425, 1.429, 1.436, 1.441, 1.444, 1.451, 1.454, 1.457, 1.463, 1.466, 1.466, 1.465, 1.461, 1.454, 1.449, 1.441, 1.427, 1.414, 1.393, 1.376, 1.347, 1.321, 1.286, 1.251, 1.216, 1.169, 1.134,
+                            1.399, 1.412, 1.416, 1.419, 1.424, 1.429, 1.436, 1.441, 1.445, 1.449, 1.455, 1.461, 1.463, 1.468, 1.472, 1.471, 1.471, 1.468, 1.459, 1.454, 1.445, 1.435, 1.419, 1.402, 1.382, 1.354, 1.329, 1.296, 1.259, 1.225, 1.175, 1.143,
+                            1.403, 1.416, 1.419, 1.423, 1.427, 1.434, 1.439, 1.443, 1.449, 1.452, 1.459, 1.463, 1.468, 1.472, 1.473, 1.472, 1.471, 1.469, 1.466, 1.455, 1.449, 1.438, 1.425, 1.408, 1.389, 1.362, 1.337, 1.304, 1.271, 1.233, 1.184, 1.148,
+                            1.404, 1.418, 1.421, 1.425, 1.429, 1.436, 1.441, 1.444, 1.449, 1.453, 1.461, 1.465, 1.471, 1.472, 1.474, 1.474, 1.472, 1.471, 1.467, 1.459, 1.451, 1.441, 1.428, 1.411, 1.393, 1.368, 1.341, 1.309, 1.277, 1.239, 1.189, 1.152,
+                            1.404, 1.417, 1.421, 1.425, 1.431, 1.437, 1.441, 1.445, 1.449, 1.453, 1.461, 1.465, 1.469, 1.471, 1.472, 1.475, 1.474, 1.471, 1.466, 1.461, 1.452, 1.442, 1.431, 1.411, 1.395, 1.371, 1.345, 1.313, 1.279, 1.242, 1.194, 1.156,
+                            1.405, 1.417, 1.421, 1.428, 1.433, 1.437, 1.441, 1.445, 1.449, 1.453, 1.459, 1.462, 1.467, 1.469, 1.472, 1.475, 1.475, 1.471, 1.467, 1.461, 1.452, 1.443, 1.431, 1.414, 1.396, 1.372, 1.346, 1.315, 1.284, 1.249, 1.199, 1.161,
+                            1.409, 1.418, 1.422, 1.426, 1.432, 1.436, 1.439, 1.444, 1.447, 1.452, 1.457, 1.461, 1.464, 1.469, 1.471, 1.474, 1.474, 1.471, 1.467, 1.462, 1.452, 1.445, 1.431, 1.415, 1.395, 1.374, 1.349, 1.314, 1.285, 1.253, 1.201, 1.165,
+                            1.409, 1.418, 1.421, 1.425, 1.429, 1.433, 1.437, 1.441, 1.446, 1.451, 1.454, 1.459, 1.463, 1.467, 1.469, 1.473, 1.473, 1.469, 1.466, 1.461, 1.455, 1.445, 1.431, 1.416, 1.396, 1.373, 1.349, 1.316, 1.286, 1.254, 1.205, 1.165,
+                            1.409, 1.412, 1.421, 1.422, 1.426, 1.429, 1.436, 1.439, 1.443, 1.449, 1.452, 1.457, 1.462, 1.465, 1.469, 1.471, 1.471, 1.469, 1.467, 1.461, 1.455, 1.445, 1.431, 1.416, 1.396, 1.372, 1.349, 1.317, 1.286, 1.254, 1.205, 1.165,
+                            1.403, 1.409, 1.413, 1.419, 1.423, 1.429, 1.432, 1.437, 1.441, 1.445, 1.451, 1.455, 1.462, 1.464, 1.468, 1.471, 1.471, 1.469, 1.465, 1.461, 1.455, 1.443, 1.429, 1.414, 1.397, 1.372, 1.349, 1.316, 1.286, 1.254, 1.205, 1.163,
+                            1.396, 1.402, 1.409, 1.414, 1.419, 1.423, 1.429, 1.434, 1.439, 1.444, 1.448, 1.453, 1.456, 1.462, 1.463, 1.468, 1.469, 1.468, 1.465, 1.459, 1.452, 1.442, 1.429, 1.412, 1.396, 1.373, 1.349, 1.315, 1.287, 1.253, 1.206, 1.163,
+                            1.389, 1.393, 1.402, 1.406, 1.414, 1.418, 1.424, 1.431, 1.435, 1.441, 1.447, 1.449, 1.455, 1.457, 1.461, 1.462, 1.466, 1.465, 1.461, 1.458, 1.451, 1.442, 1.428, 1.412, 1.395, 1.372, 1.347, 1.315, 1.287, 1.252, 1.205, 1.164,
+                            1.373, 1.385, 1.388, 1.394, 1.403, 1.408, 1.418, 1.424, 1.431, 1.437, 1.441, 1.447, 1.449, 1.454, 1.456, 1.459, 1.461, 1.461, 1.459, 1.457, 1.449, 1.439, 1.427, 1.412, 1.394, 1.371, 1.346, 1.315, 1.284, 1.249, 1.202, 1.162,
+                            1.359, 1.371, 1.377, 1.383, 1.392, 1.403, 1.408, 1.416, 1.423, 1.431, 1.437, 1.439, 1.444, 1.447, 1.451, 1.455, 1.458, 1.459, 1.457, 1.453, 1.447, 1.435, 1.425, 1.409, 1.391, 1.367, 1.341, 1.312, 1.281, 1.246, 1.199, 1.161,
+                            1.345, 1.356, 1.363, 1.371, 1.379, 1.389, 1.401, 1.408, 1.415, 1.421, 1.428, 1.431, 1.436, 1.441, 1.446, 1.449, 1.453, 1.453, 1.453, 1.449, 1.443, 1.433, 1.421, 1.406, 1.389, 1.364, 1.337, 1.306, 1.274, 1.244, 1.197, 1.158,
+                            1.321, 1.337, 1.344, 1.355, 1.362, 1.376, 1.387, 1.396, 1.403, 1.409, 1.416, 1.423, 1.428, 1.433, 1.438, 1.444, 1.447, 1.449, 1.449, 1.443, 1.438, 1.428, 1.417, 1.397, 1.381, 1.359, 1.331, 1.301, 1.271, 1.236, 1.188, 1.157,
+                            1.298, 1.315, 1.325, 1.332, 1.344, 1.357, 1.368, 1.383, 1.391, 1.398, 1.404, 1.413, 1.422, 1.427, 1.434, 1.438, 1.442, 1.443, 1.443, 1.439, 1.431, 1.419, 1.409, 1.394, 1.372, 1.353, 1.325, 1.296, 1.261, 1.231, 1.183, 1.148,
+                            1.278, 1.294, 1.304, 1.316, 1.328, 1.341, 1.353, 1.362, 1.375, 1.386, 1.392, 1.402, 1.411, 1.421, 1.426, 1.431, 1.436, 1.436, 1.436, 1.431, 1.421, 1.414, 1.401, 1.387, 1.365, 1.344, 1.319, 1.289, 1.258, 1.226, 1.178, 1.142,
+                            1.259, 1.273, 1.287, 1.296, 1.311, 1.322, 1.335, 1.349, 1.358, 1.371, 1.381, 1.392, 1.399, 1.411, 1.417, 1.423, 1.425, 1.425, 1.424, 1.419, 1.414, 1.404, 1.392, 1.373, 1.359, 1.336, 1.311, 1.282, 1.249, 1.216, 1.175, 1.139,
+                            1.234, 1.253, 1.266, 1.276, 1.291, 1.301, 1.315, 1.328, 1.344, 1.355, 1.364, 1.377, 1.386, 1.397, 1.406, 1.412, 1.416, 1.419, 1.417, 1.409, 1.404, 1.394, 1.383, 1.368, 1.351, 1.329, 1.301, 1.271, 1.242, 1.208, 1.162, 1.131,
+                            1.213, 1.229, 1.245, 1.254, 1.267, 1.282, 1.297, 1.311, 1.325, 1.337, 1.351, 1.362, 1.374, 1.381, 1.393, 1.399, 1.402, 1.404, 1.404, 1.402, 1.394, 1.384, 1.373, 1.361, 1.342, 1.319, 1.293, 1.266, 1.234, 1.204, 1.157, 1.125,
+                            1.188, 1.208, 1.221, 1.231, 1.245, 1.263, 1.276, 1.291, 1.302, 1.317, 1.333, 1.341, 1.358, 1.366, 1.373, 1.382, 1.386, 1.388, 1.391, 1.388, 1.382, 1.375, 1.365, 1.351, 1.333, 1.311, 1.284, 1.254, 1.225, 1.198, 1.152, 1.121,
+                            1.165, 1.182, 1.195, 1.209, 1.221, 1.239, 1.254, 1.268, 1.278, 1.296, 1.309, 1.322, 1.337, 1.348, 1.355, 1.365, 1.371, 1.374, 1.375, 1.373, 1.372, 1.365, 1.352, 1.341, 1.321, 1.301, 1.273, 1.242, 1.212, 1.183, 1.141, 1.111,
+                            1.141, 1.159, 1.173, 1.183, 1.198, 1.215, 1.229, 1.245, 1.258, 1.271, 1.286, 1.299, 1.317, 1.326, 1.334, 1.347, 1.355, 1.359, 1.362, 1.362, 1.358, 1.351, 1.341, 1.325, 1.307, 1.289, 1.259, 1.233, 1.203, 1.175, 1.119, 1.062,
+                            1.126, 1.139, 1.155, 1.171, 1.182, 1.197, 1.211, 1.225, 1.241, 1.255, 1.267, 1.281, 1.295, 1.309, 1.321, 1.331, 1.337, 1.341, 1.342, 1.343, 1.342, 1.341, 1.329, 1.311, 1.292, 1.271, 1.245, 1.217, 1.189, 1.152, 1.075, 1.049
+                        ]
+                    },
+                    {
+                        "ct": 5000,
+                        "table":
+                        [
+                            1.413, 1.419, 1.423, 1.434, 1.444, 1.447, 1.455, 1.456, 1.459, 1.462, 1.466, 1.469, 1.478, 1.481, 1.482, 1.479, 1.477, 1.474, 1.463, 1.457, 1.445, 1.427, 1.396, 1.368, 1.329, 1.287, 1.247, 1.212, 1.165, 1.123, 1.064, 1.049,
+                            1.434, 1.446, 1.456, 1.464, 1.473, 1.478, 1.491, 1.492, 1.495, 1.502, 1.507, 1.509, 1.512, 1.522, 1.523, 1.519, 1.512, 1.505, 1.499, 1.487, 1.468, 1.451, 1.422, 1.389, 1.354, 1.321, 1.281, 1.242, 1.199, 1.157, 1.105, 1.064,
+                            1.454, 1.459, 1.476, 1.484, 1.489, 1.496, 1.505, 1.515, 1.514, 1.522, 1.529, 1.533, 1.535, 1.542, 1.544, 1.543, 1.539, 1.536, 1.527, 1.513, 1.495, 1.469, 1.451, 1.422, 1.387, 1.352, 1.313, 1.272, 1.227, 1.189, 1.131, 1.097,
+                            1.467, 1.479, 1.488, 1.495, 1.502, 1.512, 1.519, 1.529, 1.534, 1.539, 1.546, 1.551, 1.553, 1.558, 1.562, 1.561, 1.558, 1.554, 1.542, 1.532, 1.514, 1.496, 1.469, 1.446, 1.407, 1.379, 1.335, 1.296, 1.249, 1.211, 1.149, 1.123,
+                            1.485, 1.495, 1.504, 1.509, 1.517, 1.525, 1.539, 1.543, 1.553, 1.558, 1.559, 1.565, 1.568, 1.573, 1.582, 1.582, 1.577, 1.573, 1.563, 1.549, 1.537, 1.514, 1.489, 1.461, 1.434, 1.399, 1.356, 1.316, 1.271, 1.231, 1.168, 1.135,
+                            1.499, 1.504, 1.514, 1.522, 1.531, 1.543, 1.549, 1.561, 1.563, 1.568, 1.579, 1.585, 1.591, 1.595, 1.595, 1.596, 1.595, 1.589, 1.583, 1.569, 1.552, 1.537, 1.511, 1.486, 1.457, 1.418, 1.379, 1.334, 1.291, 1.254, 1.189, 1.149,
+                            1.506, 1.514, 1.527, 1.535, 1.543, 1.553, 1.562, 1.569, 1.577, 1.583, 1.594, 1.601, 1.606, 1.611, 1.612, 1.612, 1.609, 1.604, 1.594, 1.585, 1.569, 1.551, 1.531, 1.502, 1.472, 1.429, 1.396, 1.352, 1.311, 1.264, 1.201, 1.167,
+                            1.513, 1.527, 1.537, 1.546, 1.553, 1.563, 1.576, 1.584, 1.588, 1.595, 1.605, 1.611, 1.617, 1.623, 1.625, 1.623, 1.622, 1.616, 1.607, 1.597, 1.583, 1.563, 1.541, 1.518, 1.489, 1.452, 1.409, 1.372, 1.324, 1.281, 1.221, 1.181,
+                            1.525, 1.537, 1.547, 1.554, 1.561, 1.575, 1.584, 1.591, 1.596, 1.605, 1.613, 1.621, 1.627, 1.633, 1.634, 1.635, 1.631, 1.626, 1.618, 1.608, 1.594, 1.577, 1.555, 1.533, 1.501, 1.467, 1.431, 1.387, 1.341, 1.295, 1.232, 1.189,
+                            1.529, 1.546, 1.552, 1.561, 1.569, 1.581, 1.588, 1.595, 1.604, 1.613, 1.621, 1.626, 1.634, 1.639, 1.639, 1.643, 1.638, 1.634, 1.626, 1.616, 1.605, 1.586, 1.567, 1.548, 1.512, 1.478, 1.445, 1.398, 1.357, 1.313, 1.244, 1.209,
+                            1.529, 1.549, 1.558, 1.565, 1.571, 1.583, 1.593, 1.601, 1.608, 1.618, 1.624, 1.631, 1.639, 1.643, 1.644, 1.644, 1.644, 1.641, 1.633, 1.621, 1.613, 1.594, 1.578, 1.551, 1.524, 1.486, 1.449, 1.408, 1.363, 1.321, 1.255, 1.214,
+                            1.529, 1.552, 1.561, 1.567, 1.577, 1.587, 1.595, 1.604, 1.611, 1.619, 1.626, 1.633, 1.642, 1.648, 1.649, 1.648, 1.645, 1.643, 1.637, 1.627, 1.617, 1.601, 1.584, 1.555, 1.529, 1.493, 1.462, 1.418, 1.373, 1.328, 1.266, 1.225,
+                            1.534, 1.551, 1.562, 1.568, 1.581, 1.591, 1.596, 1.605, 1.612, 1.619, 1.628, 1.633, 1.642, 1.648, 1.651, 1.652, 1.649, 1.643, 1.639, 1.632, 1.619, 1.604, 1.586, 1.561, 1.536, 1.499, 1.466, 1.423, 1.379, 1.335, 1.272, 1.233,
+                            1.535, 1.551, 1.562, 1.569, 1.581, 1.591, 1.598, 1.604, 1.612, 1.619, 1.629, 1.634, 1.639, 1.647, 1.649, 1.652, 1.649, 1.646, 1.643, 1.634, 1.622, 1.606, 1.588, 1.564, 1.538, 1.502, 1.469, 1.425, 1.382, 1.341, 1.275, 1.236,
+                            1.535, 1.549, 1.561, 1.569, 1.578, 1.587, 1.598, 1.604, 1.609, 1.619, 1.629, 1.633, 1.638, 1.644, 1.649, 1.651, 1.649, 1.647, 1.642, 1.634, 1.622, 1.607, 1.588, 1.564, 1.538, 1.505, 1.471, 1.431, 1.385, 1.346, 1.281, 1.236,
+                            1.534, 1.548, 1.559, 1.565, 1.574, 1.585, 1.593, 1.599, 1.607, 1.618, 1.626, 1.631, 1.637, 1.644, 1.648, 1.651, 1.649, 1.647, 1.642, 1.634, 1.625, 1.608, 1.589, 1.566, 1.539, 1.506, 1.472, 1.432, 1.388, 1.347, 1.284, 1.241,
+                            1.532, 1.543, 1.554, 1.562, 1.569, 1.581, 1.592, 1.598, 1.603, 1.614, 1.623, 1.628, 1.634, 1.641, 1.645, 1.647, 1.648, 1.645, 1.641, 1.633, 1.625, 1.606, 1.589, 1.565, 1.538, 1.505, 1.472, 1.431, 1.392, 1.347, 1.287, 1.239,
+                            1.519, 1.531, 1.544, 1.557, 1.565, 1.578, 1.586, 1.594, 1.601, 1.609, 1.619, 1.626, 1.632, 1.641, 1.644, 1.646, 1.647, 1.644, 1.639, 1.631, 1.622, 1.605, 1.589, 1.566, 1.538, 1.505, 1.472, 1.431, 1.392, 1.347, 1.283, 1.227,
+                            1.509, 1.517, 1.531, 1.545, 1.559, 1.567, 1.579, 1.586, 1.596, 1.606, 1.612, 1.621, 1.629, 1.634, 1.637, 1.643, 1.643, 1.641, 1.634, 1.629, 1.621, 1.604, 1.586, 1.566, 1.538, 1.506, 1.471, 1.431, 1.391, 1.336, 1.263, 1.171,
+                            1.492, 1.506, 1.517, 1.528, 1.541, 1.557, 1.568, 1.578, 1.589, 1.598, 1.606, 1.612, 1.621, 1.629, 1.632, 1.634, 1.633, 1.633, 1.631, 1.625, 1.617, 1.601, 1.583, 1.564, 1.535, 1.504, 1.468, 1.431, 1.384, 1.306, 1.205, 1.159,
+                            1.471, 1.486, 1.503, 1.511, 1.525, 1.541, 1.554, 1.565, 1.577, 1.589, 1.597, 1.602, 1.611, 1.617, 1.621, 1.625, 1.625, 1.629, 1.626, 1.622, 1.612, 1.595, 1.578, 1.556, 1.532, 1.499, 1.466, 1.423, 1.379, 1.306, 1.201, 1.145,
+                            1.446, 1.464, 1.481, 1.493, 1.508, 1.523, 1.539, 1.551, 1.561, 1.575, 1.586, 1.592, 1.598, 1.606, 1.612, 1.617, 1.618, 1.622, 1.619, 1.613, 1.604, 1.588, 1.575, 1.551, 1.528, 1.493, 1.457, 1.416, 1.375, 1.326, 1.212, 1.159,
+                            1.419, 1.443, 1.453, 1.468, 1.486, 1.501, 1.519, 1.534, 1.544, 1.558, 1.568, 1.577, 1.585, 1.594, 1.602, 1.607, 1.612, 1.611, 1.609, 1.605, 1.592, 1.581, 1.565, 1.543, 1.516, 1.481, 1.444, 1.405, 1.366, 1.324, 1.254, 1.172,
+                            1.389, 1.415, 1.426, 1.441, 1.463, 1.479, 1.494, 1.515, 1.525, 1.538, 1.555, 1.562, 1.571, 1.579, 1.589, 1.595, 1.601, 1.601, 1.598, 1.592, 1.581, 1.571, 1.553, 1.526, 1.501, 1.472, 1.437, 1.397, 1.355, 1.316, 1.252, 1.212,
+                            1.364, 1.386, 1.405, 1.419, 1.436, 1.456, 1.474, 1.491, 1.504, 1.519, 1.533, 1.541, 1.557, 1.565, 1.573, 1.584, 1.587, 1.588, 1.587, 1.578, 1.571, 1.555, 1.534, 1.514, 1.491, 1.457, 1.425, 1.386, 1.347, 1.304, 1.248, 1.209,
+                            1.335, 1.358, 1.376, 1.395, 1.412, 1.431, 1.449, 1.466, 1.482, 1.495, 1.509, 1.527, 1.538, 1.551, 1.561, 1.566, 1.573, 1.573, 1.569, 1.561, 1.554, 1.541, 1.525, 1.501, 1.476, 1.446, 1.413, 1.372, 1.328, 1.295, 1.235, 1.206,
+                            1.309, 1.333, 1.353, 1.367, 1.386, 1.401, 1.424, 1.441, 1.459, 1.474, 1.489, 1.505, 1.517, 1.533, 1.544, 1.548, 1.553, 1.555, 1.555, 1.546, 1.541, 1.525, 1.507, 1.485, 1.464, 1.435, 1.396, 1.351, 1.317, 1.279, 1.224, 1.188,
+                            1.279, 1.304, 1.326, 1.337, 1.353, 1.379, 1.395, 1.415, 1.432, 1.447, 1.469, 1.482, 1.498, 1.514, 1.524, 1.532, 1.537, 1.537, 1.536, 1.532, 1.522, 1.507, 1.488, 1.472, 1.452, 1.413, 1.378, 1.342, 1.308, 1.271, 1.213, 1.171,
+                            1.253, 1.275, 1.294, 1.304, 1.326, 1.349, 1.368, 1.386, 1.403, 1.421, 1.439, 1.457, 1.473, 1.489, 1.497, 1.504, 1.513, 1.515, 1.515, 1.511, 1.504, 1.489, 1.474, 1.455, 1.431, 1.402, 1.368, 1.329, 1.298, 1.255, 1.201, 1.167,
+                            1.221, 1.238, 1.263, 1.277, 1.296, 1.316, 1.334, 1.353, 1.372, 1.392, 1.411, 1.422, 1.448, 1.458, 1.466, 1.481, 1.489, 1.492, 1.493, 1.489, 1.486, 1.474, 1.455, 1.437, 1.415, 1.381, 1.348, 1.314, 1.275, 1.238, 1.184, 1.155,
+                            1.193, 1.213, 1.227, 1.243, 1.264, 1.287, 1.304, 1.322, 1.339, 1.355, 1.377, 1.395, 1.409, 1.425, 1.438, 1.455, 1.461, 1.466, 1.469, 1.468, 1.464, 1.451, 1.434, 1.415, 1.386, 1.362, 1.331, 1.296, 1.261, 1.225, 1.172, 1.136,
+                            1.172, 1.189, 1.209, 1.225, 1.241, 1.261, 1.279, 1.297, 1.317, 1.334, 1.351, 1.368, 1.387, 1.399, 1.418, 1.426, 1.434, 1.437, 1.437, 1.438, 1.437, 1.433, 1.418, 1.397, 1.365, 1.337, 1.306, 1.269, 1.238, 1.204, 1.156, 1.127
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.096, 1.097, 1.099, 1.101, 1.102, 1.103, 1.106, 1.106, 1.107, 1.106, 1.106, 1.108, 1.109, 1.111, 1.114, 1.115, 1.116, 1.117, 1.114, 1.112, 1.109, 1.106, 1.101, 1.092, 1.083, 1.076, 1.066, 1.054, 1.043, 1.027, 1.008, 1.001,
+                            1.098, 1.099, 1.104, 1.104, 1.103, 1.105, 1.107, 1.108, 1.108, 1.107, 1.108, 1.109, 1.111, 1.113, 1.115, 1.116, 1.117, 1.118, 1.116, 1.115, 1.111, 1.108, 1.102, 1.095, 1.091, 1.078, 1.069, 1.062, 1.049, 1.036, 1.021, 1.006,
+                            1.101, 1.105, 1.105, 1.105, 1.105, 1.107, 1.109, 1.109, 1.109, 1.109, 1.111, 1.111, 1.114, 1.115, 1.117, 1.117, 1.118, 1.119, 1.117, 1.115, 1.112, 1.109, 1.103, 1.098, 1.093, 1.084, 1.075, 1.065, 1.053, 1.041, 1.025, 1.011,
+                            1.101, 1.106, 1.107, 1.106, 1.107, 1.108, 1.111, 1.111, 1.111, 1.111, 1.112, 1.114, 1.116, 1.117, 1.119, 1.119, 1.121, 1.119, 1.119, 1.116, 1.113, 1.111, 1.105, 1.101, 1.094, 1.087, 1.077, 1.069, 1.057, 1.046, 1.031, 1.017,
+                            1.105, 1.108, 1.108, 1.108, 1.108, 1.109, 1.112, 1.111, 1.112, 1.112, 1.112, 1.113, 1.116, 1.117, 1.119, 1.121, 1.121, 1.121, 1.119, 1.116, 1.114, 1.111, 1.106, 1.102, 1.097, 1.089, 1.081, 1.072, 1.059, 1.048, 1.034, 1.021,
+                            1.106, 1.109, 1.111, 1.109, 1.109, 1.111, 1.113, 1.112, 1.112, 1.112, 1.113, 1.114, 1.117, 1.118, 1.119, 1.121, 1.121, 1.119, 1.119, 1.116, 1.115, 1.111, 1.107, 1.104, 1.098, 1.091, 1.083, 1.074, 1.064, 1.052, 1.037, 1.022,
+                            1.107, 1.111, 1.111, 1.111, 1.111, 1.112, 1.113, 1.112, 1.112, 1.113, 1.113, 1.115, 1.116, 1.118, 1.119, 1.119, 1.119, 1.119, 1.117, 1.116, 1.114, 1.111, 1.108, 1.105, 1.099, 1.093, 1.085, 1.077, 1.066, 1.054, 1.041, 1.027,
+                            1.106, 1.111, 1.111, 1.112, 1.112, 1.113, 1.113, 1.112, 1.112, 1.112, 1.113, 1.114, 1.116, 1.116, 1.117, 1.118, 1.118, 1.117, 1.116, 1.115, 1.113, 1.111, 1.108, 1.104, 1.099, 1.093, 1.086, 1.077, 1.068, 1.057, 1.042, 1.029,
+                            1.108, 1.112, 1.112, 1.112, 1.112, 1.113, 1.113, 1.112, 1.112, 1.112, 1.111, 1.113, 1.115, 1.116, 1.116, 1.116, 1.116, 1.115, 1.114, 1.113, 1.112, 1.111, 1.106, 1.103, 1.099, 1.092, 1.085, 1.077, 1.069, 1.058, 1.042, 1.029,
+                            1.109, 1.111, 1.112, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.113, 1.114, 1.114, 1.114, 1.114, 1.113, 1.112, 1.111, 1.109, 1.107, 1.105, 1.102, 1.098, 1.091, 1.085, 1.077, 1.068, 1.059, 1.045, 1.031,
+                            1.109, 1.111, 1.111, 1.111, 1.109, 1.111, 1.109, 1.109, 1.108, 1.108, 1.109, 1.109, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.108, 1.108, 1.107, 1.105, 1.103, 1.099, 1.096, 1.089, 1.083, 1.077, 1.068, 1.058, 1.045, 1.029,
+                            1.108, 1.109, 1.109, 1.109, 1.109, 1.109, 1.107, 1.106, 1.105, 1.105, 1.106, 1.107, 1.107, 1.107, 1.108, 1.108, 1.107, 1.107, 1.106, 1.105, 1.104, 1.102, 1.101, 1.097, 1.092, 1.088, 1.082, 1.074, 1.067, 1.057, 1.046, 1.031,
+                            1.106, 1.108, 1.109, 1.107, 1.107, 1.106, 1.105, 1.104, 1.104, 1.103, 1.102, 1.102, 1.104, 1.104, 1.104, 1.105, 1.105, 1.105, 1.104, 1.103, 1.101, 1.099, 1.098, 1.095, 1.091, 1.085, 1.081, 1.072, 1.065, 1.057, 1.044, 1.031,
+                            1.104, 1.106, 1.107, 1.106, 1.105, 1.104, 1.103, 1.102, 1.101, 1.101, 1.101, 1.101, 1.101, 1.102, 1.103, 1.103, 1.104, 1.103, 1.102, 1.101, 1.099, 1.098, 1.095, 1.092, 1.089, 1.084, 1.079, 1.071, 1.063, 1.055, 1.044, 1.031,
+                            1.105, 1.106, 1.106, 1.105, 1.104, 1.102, 1.101, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.102, 1.101, 1.099, 1.097, 1.096, 1.093, 1.091, 1.087, 1.082, 1.076, 1.069, 1.062, 1.054, 1.043, 1.028,
+                            1.105, 1.106, 1.106, 1.104, 1.103, 1.101, 1.099, 1.099, 1.098, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.101, 1.101, 1.099, 1.098, 1.096, 1.095, 1.091, 1.089, 1.086, 1.081, 1.075, 1.071, 1.061, 1.054, 1.043, 1.028,
+                            1.105, 1.105, 1.105, 1.104, 1.102, 1.101, 1.099, 1.098, 1.097, 1.096, 1.096, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.099, 1.098, 1.097, 1.095, 1.093, 1.091, 1.088, 1.085, 1.079, 1.076, 1.069, 1.061, 1.053, 1.043, 1.027,
+                            1.105, 1.105, 1.104, 1.102, 1.101, 1.099, 1.099, 1.097, 1.097, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.097, 1.098, 1.098, 1.097, 1.096, 1.094, 1.092, 1.091, 1.088, 1.085, 1.079, 1.076, 1.068, 1.062, 1.054, 1.043, 1.027,
+                            1.104, 1.103, 1.103, 1.099, 1.098, 1.098, 1.098, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.096, 1.097, 1.097, 1.097, 1.096, 1.094, 1.093, 1.091, 1.089, 1.085, 1.081, 1.076, 1.068, 1.063, 1.054, 1.043, 1.027,
+                            1.099, 1.099, 1.098, 1.098, 1.097, 1.097, 1.097, 1.097, 1.097, 1.096, 1.096, 1.096, 1.095, 1.095, 1.095, 1.095, 1.096, 1.096, 1.097, 1.097, 1.096, 1.094, 1.091, 1.089, 1.085, 1.081, 1.076, 1.068, 1.062, 1.055, 1.044, 1.028,
+                            1.097, 1.096, 1.096, 1.095, 1.095, 1.095, 1.095, 1.097, 1.097, 1.096, 1.095, 1.095, 1.094, 1.094, 1.094, 1.095, 1.096, 1.096, 1.097, 1.098, 1.097, 1.095, 1.092, 1.088, 1.086, 1.083, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.091, 1.092, 1.093, 1.092, 1.092, 1.093, 1.095, 1.095, 1.095, 1.093, 1.092, 1.092, 1.092, 1.093, 1.094, 1.095, 1.096, 1.097, 1.098, 1.098, 1.097, 1.095, 1.093, 1.088, 1.086, 1.082, 1.076, 1.069, 1.062, 1.056, 1.046, 1.031,
+                            1.085, 1.088, 1.088, 1.089, 1.089, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.092, 1.093, 1.094, 1.096, 1.097, 1.099, 1.098, 1.098, 1.097, 1.095, 1.093, 1.089, 1.085, 1.081, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.081, 1.082, 1.084, 1.084, 1.085, 1.087, 1.089, 1.091, 1.091, 1.091, 1.091, 1.092, 1.092, 1.094, 1.096, 1.096, 1.099, 1.099, 1.099, 1.098, 1.097, 1.096, 1.093, 1.089, 1.086, 1.082, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.073, 1.078, 1.081, 1.082, 1.083, 1.084, 1.088, 1.089, 1.089, 1.089, 1.091, 1.091, 1.093, 1.095, 1.096, 1.098, 1.099, 1.101, 1.099, 1.099, 1.096, 1.095, 1.093, 1.089, 1.086, 1.081, 1.077, 1.069, 1.062, 1.055, 1.043, 1.032,
+                            1.068, 1.072, 1.076, 1.079, 1.081, 1.083, 1.084, 1.087, 1.088, 1.088, 1.089, 1.092, 1.093, 1.096, 1.097, 1.099, 1.099, 1.099, 1.099, 1.097, 1.096, 1.095, 1.092, 1.089, 1.086, 1.081, 1.077, 1.071, 1.062, 1.055, 1.045, 1.034,
+                            1.064, 1.066, 1.072, 1.073, 1.077, 1.079, 1.082, 1.084, 1.086, 1.088, 1.089, 1.091, 1.093, 1.095, 1.097, 1.099, 1.099, 1.099, 1.098, 1.097, 1.096, 1.095, 1.092, 1.089, 1.086, 1.081, 1.077, 1.069, 1.064, 1.055, 1.043, 1.035,
+                            1.057, 1.062, 1.065, 1.068, 1.071, 1.075, 1.077, 1.081, 1.084, 1.086, 1.088, 1.089, 1.092, 1.094, 1.096, 1.098, 1.098, 1.098, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.086, 1.083, 1.077, 1.069, 1.064, 1.055, 1.043, 1.033,
+                            1.051, 1.056, 1.059, 1.062, 1.066, 1.068, 1.074, 1.077, 1.079, 1.083, 1.086, 1.088, 1.089, 1.092, 1.094, 1.096, 1.096, 1.096, 1.096, 1.095, 1.094, 1.093, 1.092, 1.089, 1.086, 1.083, 1.077, 1.068, 1.063, 1.055, 1.043, 1.033,
+                            1.043, 1.048, 1.052, 1.056, 1.059, 1.065, 1.068, 1.071, 1.074, 1.078, 1.081, 1.083, 1.088, 1.089, 1.091, 1.092, 1.094, 1.095, 1.094, 1.094, 1.094, 1.094, 1.092, 1.089, 1.086, 1.083, 1.077, 1.069, 1.062, 1.055, 1.044, 1.031,
+                            1.036, 1.041, 1.045, 1.049, 1.052, 1.059, 1.062, 1.067, 1.069, 1.072, 1.074, 1.077, 1.082, 1.083, 1.086, 1.089, 1.091, 1.092, 1.093, 1.092, 1.092, 1.091, 1.091, 1.088, 1.085, 1.081, 1.076, 1.069, 1.061, 1.054, 1.043, 1.031,
+                            1.029, 1.036, 1.041, 1.045, 1.049, 1.052, 1.056, 1.058, 1.064, 1.064, 1.067, 1.071, 1.075, 1.079, 1.083, 1.085, 1.089, 1.089, 1.089, 1.091, 1.091, 1.091, 1.089, 1.087, 1.083, 1.079, 1.074, 1.066, 1.062, 1.052, 1.041, 1.029
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    3.174, 3.091, 2.978, 2.891, 2.829, 2.779, 2.739, 2.708, 2.683, 2.659, 2.641, 2.623, 2.616, 2.622, 2.629, 2.644, 2.669, 2.691, 2.731, 2.784, 2.843, 2.894, 2.947, 3.004, 3.065, 3.133, 3.214, 3.303, 3.416, 3.541, 3.674, 3.765,
+                    3.093, 2.968, 2.861, 2.778, 2.702, 2.651, 2.599, 2.563, 2.533, 2.509, 2.487, 2.472, 2.466, 2.462, 2.466, 2.481, 2.501, 2.531, 2.568, 2.612, 2.663, 2.712, 2.764, 2.821, 2.881, 2.954, 3.041, 3.137, 3.265, 3.396, 3.482, 3.547,
+                    2.897, 2.851, 2.743, 2.608, 2.521, 2.464, 2.417, 2.381, 2.348, 2.323, 2.302, 2.287, 2.275, 2.264, 2.269, 2.279, 2.297, 2.322, 2.351, 2.392, 2.436, 2.479, 2.526, 2.577, 2.631, 2.701, 2.778, 2.871, 2.985, 3.105, 3.191, 3.254,
+                    2.764, 2.689, 2.586, 2.452, 2.361, 2.303, 2.254, 2.218, 2.189, 2.166, 2.141, 2.123, 2.108, 2.099, 2.098, 2.105, 2.121, 2.144, 2.172, 2.203, 2.238, 2.276, 2.317, 2.358, 2.411, 2.471, 2.541, 2.626, 2.733, 2.844, 2.924, 2.979,
+                    2.581, 2.499, 2.405, 2.296, 2.223, 2.171, 2.126, 2.088, 2.056, 2.031, 2.011, 1.993, 1.975, 1.968, 1.964, 1.968, 1.982, 2.003, 2.027, 2.052, 2.079, 2.109, 2.149, 2.184, 2.231, 2.285, 2.346, 2.427, 2.521, 2.622, 2.691, 2.744,
+                    2.437, 2.343, 2.261, 2.184, 2.116, 2.058, 2.017, 1.979, 1.949, 1.924, 1.899, 1.883, 1.868, 1.859, 1.855, 1.857, 1.866, 1.883, 1.901, 1.923, 1.951, 1.978, 2.011, 2.042, 2.081, 2.133, 2.189, 2.261, 2.349, 2.433, 2.491, 2.541,
+                    2.332, 2.251, 2.166, 2.092, 2.024, 1.969, 1.925, 1.889, 1.856, 1.832, 1.809, 1.791, 1.774, 1.768, 1.762, 1.762, 1.771, 1.779, 1.799, 1.815, 1.837, 1.861, 1.892, 1.924, 1.961, 2.006, 2.059, 2.126, 2.201, 2.279, 2.329, 2.367,
+                    2.249, 2.168, 2.083, 2.005, 1.941, 1.891, 1.845, 1.808, 1.775, 1.749, 1.726, 1.711, 1.696, 1.686, 1.681, 1.681, 1.687, 1.697, 1.712, 1.726, 1.743, 1.765, 1.792, 1.824, 1.859, 1.901, 1.951, 2.009, 2.079, 2.149, 2.194, 2.229,
+                    2.173, 2.094, 2.009, 1.936, 1.871, 1.819, 1.771, 1.736, 1.705, 1.679, 1.656, 1.638, 1.623, 1.612, 1.608, 1.609, 1.613, 1.622, 1.634, 1.647, 1.664, 1.685, 1.709, 1.738, 1.772, 1.813, 1.858, 1.912, 1.979, 2.046, 2.091, 2.121,
+                    2.105, 2.033, 1.947, 1.875, 1.811, 1.756, 1.714, 1.677, 1.643, 1.616, 1.596, 1.577, 1.561, 1.551, 1.544, 1.544, 1.548, 1.556, 1.568, 1.582, 1.596, 1.616, 1.639, 1.665, 1.698, 1.739, 1.783, 1.836, 1.896, 1.959, 1.999, 2.032,
+                    2.045, 1.975, 1.892, 1.819, 1.759, 1.706, 1.661, 1.622, 1.592, 1.563, 1.543, 1.523, 1.509, 1.499, 1.491, 1.491, 1.494, 1.501, 1.512, 1.524, 1.539, 1.558, 1.578, 1.605, 1.638, 1.676, 1.721, 1.769, 1.829, 1.887, 1.923, 1.952,
+                    1.988, 1.923, 1.841, 1.773, 1.711, 1.661, 1.617, 1.579, 1.547, 1.521, 1.496, 1.476, 1.462, 1.452, 1.445, 1.445, 1.448, 1.454, 1.464, 1.476, 1.489, 1.507, 1.529, 1.557, 1.589, 1.626, 1.667, 1.719, 1.774, 1.829, 1.863, 1.893,
+                    1.943, 1.881, 1.803, 1.734, 1.673, 1.621, 1.579, 1.543, 1.508, 1.479, 1.457, 1.436, 1.421, 1.412, 1.405, 1.402, 1.405, 1.412, 1.423, 1.434, 1.449, 1.466, 1.489, 1.516, 1.546, 1.582, 1.625, 1.671, 1.726, 1.777, 1.816, 1.838,
+                    1.913, 1.848, 1.769, 1.701, 1.641, 1.591, 1.548, 1.511, 1.478, 1.449, 1.422, 1.401, 1.387, 1.377, 1.369, 1.368, 1.371, 1.379, 1.389, 1.402, 1.415, 1.432, 1.454, 1.481, 1.513, 1.548, 1.587, 1.633, 1.686, 1.736, 1.772, 1.793,
+                    1.891, 1.819, 1.742, 1.674, 1.614, 1.566, 1.523, 1.485, 1.451, 1.422, 1.397, 1.376, 1.359, 1.347, 1.339, 1.339, 1.343, 1.351, 1.361, 1.375, 1.388, 1.406, 1.429, 1.453, 1.484, 1.517, 1.555, 1.599, 1.652, 1.699, 1.733, 1.755,
+                    1.874, 1.801, 1.721, 1.654, 1.602, 1.552, 1.506, 1.465, 1.429, 1.399, 1.374, 1.352, 1.336, 1.323, 1.315, 1.316, 1.321, 1.329, 1.338, 1.352, 1.369, 1.387, 1.409, 1.435, 1.462, 1.494, 1.529, 1.572, 1.622, 1.669, 1.698, 1.718,
+                    1.868, 1.791, 1.712, 1.647, 1.592, 1.542, 1.497, 1.454, 1.416, 1.385, 1.356, 1.335, 1.319, 1.307, 1.301, 1.299, 1.305, 1.314, 1.324, 1.341, 1.357, 1.376, 1.397, 1.422, 1.449, 1.478, 1.511, 1.551, 1.599, 1.644, 1.671, 1.688,
+                    1.867, 1.789, 1.715, 1.648, 1.591, 1.539, 1.494, 1.451, 1.412, 1.378, 1.352, 1.329, 1.311, 1.301, 1.294, 1.294, 1.298, 1.306, 1.321, 1.335, 1.353, 1.369, 1.391, 1.413, 1.439, 1.469, 1.502, 1.539, 1.583, 1.623, 1.648, 1.666,
+                    1.873, 1.799, 1.724, 1.657, 1.599, 1.546, 1.498, 1.454, 1.416, 1.384, 1.356, 1.334, 1.317, 1.306, 1.299, 1.299, 1.303, 1.311, 1.323, 1.339, 1.354, 1.373, 1.393, 1.414, 1.438, 1.466, 1.495, 1.532, 1.578, 1.617, 1.639, 1.656,
+                    1.884, 1.816, 1.743, 1.675, 1.615, 1.559, 1.509, 1.466, 1.429, 1.397, 1.371, 1.351, 1.332, 1.321, 1.315, 1.313, 1.316, 1.325, 1.337, 1.351, 1.365, 1.383, 1.403, 1.424, 1.449, 1.475, 1.501, 1.537, 1.574, 1.617, 1.639, 1.654,
+                    1.906, 1.845, 1.772, 1.699, 1.636, 1.579, 1.531, 1.489, 1.453, 1.419, 1.395, 1.376, 1.364, 1.354, 1.344, 1.339, 1.338, 1.345, 1.356, 1.368, 1.383, 1.402, 1.422, 1.443, 1.469, 1.492, 1.518, 1.548, 1.583, 1.622, 1.648, 1.658,
+                    1.941, 1.882, 1.804, 1.731, 1.667, 1.611, 1.562, 1.519, 1.484, 1.453, 1.427, 1.412, 1.401, 1.389, 1.381, 1.369, 1.367, 1.372, 1.381, 1.392, 1.409, 1.429, 1.449, 1.473, 1.496, 1.521, 1.546, 1.574, 1.609, 1.637, 1.657, 1.673,
+                    1.987, 1.929, 1.845, 1.773, 1.708, 1.651, 1.603, 1.561, 1.524, 1.495, 1.467, 1.449, 1.441, 1.431, 1.417, 1.404, 1.401, 1.406, 1.415, 1.427, 1.445, 1.463, 1.485, 1.509, 1.536, 1.562, 1.587, 1.612, 1.642, 1.672, 1.678, 1.692,
+                    2.041, 1.978, 1.897, 1.824, 1.757, 1.699, 1.649, 1.606, 1.569, 1.539, 1.513, 1.489, 1.473, 1.461, 1.451, 1.445, 1.445, 1.447, 1.459, 1.472, 1.491, 1.509, 1.533, 1.558, 1.584, 1.607, 1.632, 1.661, 1.692, 1.716, 1.721, 1.728,
+                    2.111, 2.041, 1.957, 1.879, 1.813, 1.755, 1.702, 1.657, 1.622, 1.591, 1.566, 1.543, 1.525, 1.511, 1.501, 1.495, 1.492, 1.497, 1.509, 1.524, 1.543, 1.564, 1.589, 1.615, 1.638, 1.663, 1.688, 1.719, 1.746, 1.774, 1.776, 1.785,
+                    2.186, 2.115, 2.023, 1.943, 1.875, 1.816, 1.762, 1.718, 1.681, 1.649, 1.624, 1.601, 1.583, 1.571, 1.559, 1.552, 1.551, 1.557, 1.571, 1.588, 1.607, 1.628, 1.654, 1.678, 1.704, 1.729, 1.756, 1.783, 1.813, 1.841, 1.849, 1.854,
+                    2.271, 2.188, 2.097, 2.017, 1.948, 1.886, 1.829, 1.784, 1.747, 1.718, 1.693, 1.671, 1.651, 1.639, 1.629, 1.624, 1.625, 1.633, 1.647, 1.663, 1.682, 1.703, 1.728, 1.754, 1.777, 1.805, 1.834, 1.861, 1.892, 1.918, 1.926, 1.939,
+                    2.369, 2.278, 2.182, 2.101, 2.028, 1.967, 1.912, 1.863, 1.827, 1.797, 1.773, 1.751, 1.734, 1.722, 1.713, 1.711, 1.712, 1.719, 1.734, 1.749, 1.768, 1.791, 1.815, 1.839, 1.864, 1.892, 1.919, 1.949, 1.981, 2.011, 2.021, 2.035,
+                    2.479, 2.382, 2.284, 2.202, 2.126, 2.062, 2.005, 1.961, 1.921, 1.891, 1.867, 1.847, 1.832, 1.822, 1.815, 1.814, 1.817, 1.822, 1.837, 1.853, 1.871, 1.891, 1.915, 1.939, 1.964, 1.992, 2.019, 2.053, 2.089, 2.122, 2.133, 2.153,
+                    2.619, 2.509, 2.411, 2.319, 2.239, 2.173, 2.114, 2.072, 2.037, 2.006, 1.982, 1.963, 1.949, 1.941, 1.937, 1.934, 1.937, 1.947, 1.961, 1.977, 1.987, 2.009, 2.034, 2.058, 2.087, 2.112, 2.139, 2.172, 2.211, 2.253, 2.269, 2.297,
+                    2.783, 2.662, 2.554, 2.457, 2.374, 2.304, 2.246, 2.203, 2.166, 2.139, 2.118, 2.099, 2.087, 2.081, 2.077, 2.073, 2.081, 2.092, 2.106, 2.118, 2.133, 2.153, 2.174, 2.197, 2.228, 2.253, 2.281, 2.318, 2.362, 2.407, 2.434, 2.478,
+                    2.878, 2.792, 2.675, 2.577, 2.499, 2.438, 2.396, 2.357, 2.331, 2.309, 2.293, 2.278, 2.267, 2.259, 2.256, 2.258, 2.261, 2.267, 2.278, 2.293, 2.304, 2.319, 2.343, 2.369, 2.389, 2.411, 2.437, 2.469, 2.509, 2.552, 2.621, 2.683
+                ],
+                "sigma": 0.00476,
+                "sigma_Cb": 0.01242
+            }
+        },
+        {
+            "rpi.contrast":
+            {
+                "ce_enable": 1,
+                "gamma_curve":
+                [
+                    0, 0,
+                    1024, 5040,
+                    2048, 9338,
+                    3072, 12356,
+                    4096, 15312,
+                    5120, 18051,
+                    6144, 20790,
+                    7168, 23193,
+                    8192, 25744,
+                    9216, 27942,
+                    10240, 30035,
+                    11264, 32005,
+                    12288, 33975,
+                    13312, 35815,
+                    14336, 37600,
+                    15360, 39168,
+                    16384, 40642,
+                    18432, 43379,
+                    20480, 45749,
+                    22528, 47753,
+                    24576, 49621,
+                    26624, 51253,
+                    28672, 52698,
+                    30720, 53796,
+                    32768, 54876,
+                    36864, 57012,
+                    40960, 58656,
+                    45056, 59954,
+                    49152, 61183,
+                    53248, 62355,
+                    57344, 63419,
+                    61440, 64476,
+                    65535, 65535
+                ]
+            }
+        },
+        {
+            "rpi.ccm":
+            {
+                "ccms": [
+                    {
+                        "ct": 2400,
+                        "ccm":
+                        [
+                            1.78571, -0.47464, -0.31106,
+                            -0.35019, 1.75738, -0.40719,
+                            0.21137, -1.36874, 2.15737
+                        ]
+                    },
+                    {
+                        "ct": 3000,
+                        "ccm":
+                        [
+                            1.71274, -0.29097, -0.42177,
+                            -0.51279, 1.96739, -0.45461,
+                            0.07392, -1.05557, 1.98165
+                        ]
+                    },
+                    {
+                        "ct": 5000,
+                        "ccm":
+                        [
+                            1.85104, -0.46872, -0.38232,
+                            -0.37898, 1.69668, -0.31771,
+                            0.13397, -1.75721, 2.62323
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "ccm":
+                        [
+                            1.76986, -0.57663, -0.19323,
+                            -0.23588, 1.74842, -0.51254,
+                            0.03796, -0.64141, 1.60345
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.cac": { }
+        },
+        {
+            "rpi.sharpen":
+            {
+                "threshold": 0.25,
+                "limit": 1.0,
+                "strength": 1.0
+            }
+        },
+        {
+            "rpi.hdr":
+            {
+                "Off":
+                {
+                    "cadence": [ 0 ]
+                },
+                "MultiExposureUnmerged":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    }
+                },
+                "SingleExposure":
+                {
+                    "cadence": [ 1 ],
+                    "channel_map":
+                    {
+                        "short": 1
+                    },
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "MultiExposure":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    },
+                    "stitch_enable": 1,
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "Night":
+                {
+                    "cadence": [ 3 ],
+                    "channel_map":
+                    {
+                        "night": 3
+                    },
+                    "tonemap_enable": 1,
+                    "tonemap":
+                    [
+                        0, 0,
+                        5000, 20000,
+                        10000, 30000,
+                        20000, 47000,
+                        30000, 55000,
+                        65535, 65535
+                    ]
+                }
+            }
+        }
+    ]
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/data/vd56g3_mono.json 0.5.2-2/src/ipa/rpi/pisp/data/vd56g3_mono.json
--- 0.5.0-1/src/ipa/rpi/pisp/data/vd56g3_mono.json	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/data/vd56g3_mono.json	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,1155 @@
+{
+    "version": 2.0,
+    "target": "pisp",
+    "algorithms": [
+        {
+            "rpi.black_level":
+            {
+                "black_level": 4096
+            }
+        },
+        {
+            "rpi.lux":
+            {
+                "reference_shutter_speed": 5971,
+                "reference_gain": 1.0,
+                "reference_aperture": 1.0,
+                "reference_lux": 950,
+                "reference_Y": 23748
+            }
+        },
+        {
+            "rpi.noise":
+            {
+                "reference_constant": 0,
+                "reference_slope": 2.732
+            }
+        },
+        {
+            "rpi.denoise":
+            {
+                "normal":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 0.8,
+                        "threshold": 0.05
+                    }
+                },
+                "hdr":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                },
+                "night":
+                {
+                    "sdn":
+                    {
+                        "deviation": 1.6,
+                        "strength": 0.5,
+                        "deviation2": 3.2,
+                        "deviation_no_tdn": 3.2,
+                        "strength_no_tdn": 0.75
+                    },
+                    "cdn":
+                    {
+                        "deviation": 200,
+                        "strength": 0.3
+                    },
+                    "tdn":
+                    {
+                        "deviation": 1.3,
+                        "threshold": 0.1
+                    }
+                }
+            }
+        },
+        {
+            "rpi.agc":
+            {
+                "channels": [
+                    {
+                        "comment": "Channel 0 is normal AGC",
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 66666 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+                                "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0, 12.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.5,
+                                    "y_target":
+                                    [
+                                        0, 0.17,
+                                        1000, 0.17
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 1 is the HDR short channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 60000 ],
+                                "gain": [ 1.0, 1.0, 1.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.95,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.7,
+                                        1000, 0.7
+                                    ]
+                                },
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.0,
+                                    "q_hi": 0.2,
+                                    "y_target":
+                                    [
+                                        0, 0.002,
+                                        1000, 0.002
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 2 is the HDR long channel",
+                        "desaturate": 0,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 30000, 60000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [  ],
+                            "highlight": [  ],
+                            "shadows": [  ]
+                        },
+                        "channel_constraints": [
+                            {
+                                "bound": "UPPER",
+                                "channel": 4,
+                                "factor": 8
+                            },
+                            {
+                                "bound": "LOWER",
+                                "channel": 4,
+                                "factor": 2
+                            }
+                        ],
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.165,
+                            10000, 0.17
+                        ]
+                    },
+                    {
+                        "comment": "Channel 3 is the night mode channel",
+                        "base_ev": 0.33,
+                        "metering_modes":
+                        {
+                            "centre-weighted":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+                                    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+                                    0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+                                    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+                                ]
+                            },
+                            "spot":
+                            {
+                                "weights":
+                                [
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+                                ]
+                            },
+                            "matrix":
+                            {
+                                "weights":
+                                [
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+                                ]
+                            }
+                        },
+                        "exposure_modes":
+                        {
+                            "normal":
+                            {
+                                "shutter": [ 100, 20000, 66666 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "short":
+                            {
+                                "shutter": [ 100, 20000, 33333 ],
+                                "gain": [ 1.0, 2.0, 4.0 ]
+                            },
+                            "long":
+                            {
+                                "shutter": [ 100, 20000, 66666, 120000 ],
+                                "gain": [ 1.0, 2.0, 4.0, 4.0 ]
+                            }
+                        },
+                        "constraint_modes":
+                        {
+                            "normal": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ],
+                            "highlight": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                },
+                                {
+                                    "bound": "UPPER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.8,
+                                        1000, 0.8
+                                    ]
+                                }
+                            ],
+                            "shadows": [
+                                {
+                                    "bound": "LOWER",
+                                    "q_lo": 0.98,
+                                    "q_hi": 1.0,
+                                    "y_target":
+                                    [
+                                        0, 0.5,
+                                        1000, 0.5
+                                    ]
+                                }
+                            ]
+                        },
+                        "y_target":
+                        [
+                            0, 0.16,
+                            1000, 0.16,
+                            10000, 0.17
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.alsc":
+            {
+                "omega": 1.3,
+                "n_iter": 100,
+                "luminance_strength": 0.8,
+                "calibrations_Cr": [
+                    {
+                        "ct": 2400,
+                        "table":
+                        [
+                            1.075, 1.079, 1.081, 1.087, 1.087, 1.086, 1.086, 1.086, 1.085, 1.085, 1.085, 1.087, 1.087, 1.087, 1.087, 1.088, 1.085, 1.085, 1.083, 1.079, 1.074, 1.071, 1.065, 1.059, 1.054, 1.047, 1.039, 1.032, 1.023, 1.012, 1.011, 1.011,
+                            1.075, 1.077, 1.078, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.078, 1.079, 1.081, 1.081, 1.083, 1.084, 1.085, 1.084, 1.083, 1.082, 1.078, 1.074, 1.071, 1.063, 1.059, 1.053, 1.047, 1.039, 1.032, 1.023, 1.018, 1.012, 1.012,
+                            1.074, 1.077, 1.078, 1.079, 1.081, 1.081, 1.079, 1.079, 1.078, 1.078, 1.079, 1.079, 1.081, 1.082, 1.084, 1.085, 1.084, 1.083, 1.082, 1.078, 1.074, 1.069, 1.065, 1.059, 1.054, 1.049, 1.043, 1.034, 1.027, 1.021, 1.021, 1.019,
+                            1.075, 1.077, 1.081, 1.081, 1.081, 1.081, 1.079, 1.079, 1.079, 1.079, 1.079, 1.082, 1.082, 1.084, 1.085, 1.086, 1.086, 1.084, 1.082, 1.079, 1.076, 1.072, 1.068, 1.064, 1.058, 1.052, 1.047, 1.041, 1.031, 1.029, 1.028, 1.027,
+                            1.076, 1.079, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.082, 1.084, 1.085, 1.086, 1.087, 1.087, 1.085, 1.083, 1.081, 1.077, 1.075, 1.071, 1.068, 1.063, 1.056, 1.051, 1.045, 1.035, 1.032, 1.032, 1.033,
+                            1.077, 1.079, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.081, 1.083, 1.084, 1.085, 1.087, 1.088, 1.087, 1.086, 1.084, 1.082, 1.079, 1.077, 1.074, 1.069, 1.065, 1.061, 1.053, 1.047, 1.038, 1.036, 1.037, 1.039,
+                            1.077, 1.078, 1.081, 1.081, 1.082, 1.082, 1.081, 1.082, 1.082, 1.082, 1.082, 1.083, 1.085, 1.086, 1.087, 1.088, 1.088, 1.086, 1.084, 1.083, 1.082, 1.078, 1.076, 1.072, 1.067, 1.063, 1.058, 1.051, 1.043, 1.041, 1.041, 1.042,
+                            1.077, 1.079, 1.081, 1.082, 1.082, 1.082, 1.082, 1.083, 1.082, 1.082, 1.083, 1.084, 1.086, 1.087, 1.089, 1.089, 1.088, 1.087, 1.085, 1.083, 1.082, 1.079, 1.078, 1.074, 1.069, 1.066, 1.061, 1.055, 1.047, 1.045, 1.045, 1.048,
+                            1.076, 1.079, 1.081, 1.083, 1.083, 1.084, 1.083, 1.083, 1.083, 1.084, 1.084, 1.086, 1.087, 1.088, 1.089, 1.089, 1.088, 1.087, 1.085, 1.084, 1.082, 1.079, 1.078, 1.076, 1.072, 1.068, 1.063, 1.058, 1.049, 1.048, 1.049, 1.051,
+                            1.076, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.083, 1.083, 1.084, 1.085, 1.086, 1.087, 1.089, 1.089, 1.089, 1.087, 1.086, 1.086, 1.084, 1.082, 1.081, 1.079, 1.076, 1.074, 1.071, 1.065, 1.061, 1.054, 1.051, 1.051, 1.055,
+                            1.078, 1.081, 1.083, 1.084, 1.085, 1.085, 1.084, 1.083, 1.084, 1.084, 1.085, 1.085, 1.087, 1.088, 1.088, 1.088, 1.086, 1.086, 1.086, 1.084, 1.083, 1.082, 1.081, 1.078, 1.075, 1.071, 1.066, 1.063, 1.056, 1.055, 1.055, 1.057,
+                            1.081, 1.083, 1.084, 1.085, 1.086, 1.085, 1.084, 1.084, 1.084, 1.084, 1.085, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.086, 1.085, 1.084, 1.084, 1.082, 1.081, 1.079, 1.076, 1.072, 1.067, 1.064, 1.057, 1.056, 1.057, 1.058,
+                            1.081, 1.084, 1.085, 1.086, 1.086, 1.086, 1.084, 1.084, 1.084, 1.084, 1.084, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.076, 1.072, 1.069, 1.065, 1.058, 1.057, 1.058, 1.061,
+                            1.081, 1.084, 1.085, 1.086, 1.086, 1.086, 1.084, 1.083, 1.083, 1.083, 1.084, 1.084, 1.085, 1.086, 1.087, 1.088, 1.087, 1.087, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.073, 1.069, 1.066, 1.059, 1.059, 1.061, 1.063,
+                            1.081, 1.084, 1.086, 1.086, 1.086, 1.086, 1.083, 1.082, 1.082, 1.083, 1.083, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.066, 1.061, 1.061, 1.062, 1.065,
+                            1.082, 1.085, 1.086, 1.086, 1.086, 1.084, 1.082, 1.081, 1.082, 1.082, 1.082, 1.083, 1.084, 1.086, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.082, 1.081, 1.079, 1.077, 1.074, 1.071, 1.066, 1.061, 1.061, 1.064, 1.066,
+                            1.082, 1.084, 1.085, 1.085, 1.083, 1.083, 1.081, 1.081, 1.081, 1.082, 1.082, 1.083, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.077, 1.074, 1.071, 1.067, 1.061, 1.061, 1.064, 1.065,
+                            1.081, 1.082, 1.083, 1.082, 1.081, 1.079, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.085, 1.086, 1.086, 1.086, 1.086, 1.085, 1.084, 1.083, 1.082, 1.079, 1.076, 1.074, 1.071, 1.067, 1.061, 1.061, 1.064, 1.066,
+                            1.079, 1.078, 1.078, 1.077, 1.077, 1.077, 1.078, 1.078, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.084, 1.084, 1.083, 1.082, 1.081, 1.079, 1.076, 1.073, 1.071, 1.067, 1.061, 1.061, 1.065, 1.067,
+                            1.073, 1.073, 1.073, 1.074, 1.074, 1.074, 1.074, 1.075, 1.076, 1.077, 1.077, 1.077, 1.077, 1.078, 1.079, 1.079, 1.081, 1.083, 1.083, 1.083, 1.081, 1.081, 1.079, 1.077, 1.075, 1.072, 1.069, 1.066, 1.061, 1.061, 1.064, 1.066,
+                            1.064, 1.064, 1.066, 1.067, 1.067, 1.071, 1.071, 1.072, 1.073, 1.074, 1.074, 1.073, 1.074, 1.075, 1.076, 1.077, 1.078, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.076, 1.074, 1.071, 1.068, 1.064, 1.059, 1.059, 1.062, 1.064,
+                            1.056, 1.058, 1.059, 1.061, 1.062, 1.065, 1.066, 1.067, 1.068, 1.068, 1.069, 1.068, 1.068, 1.069, 1.071, 1.074, 1.076, 1.078, 1.079, 1.079, 1.079, 1.078, 1.076, 1.074, 1.072, 1.069, 1.065, 1.061, 1.057, 1.057, 1.059, 1.061,
+                            1.048, 1.053, 1.054, 1.057, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.065, 1.066, 1.068, 1.071, 1.074, 1.077, 1.078, 1.078, 1.078, 1.076, 1.074, 1.071, 1.069, 1.066, 1.063, 1.058, 1.055, 1.054, 1.056, 1.059,
+                            1.044, 1.047, 1.049, 1.052, 1.054, 1.056, 1.057, 1.059, 1.059, 1.059, 1.061, 1.063, 1.064, 1.065, 1.067, 1.071, 1.073, 1.075, 1.077, 1.076, 1.076, 1.073, 1.071, 1.069, 1.066, 1.064, 1.059, 1.056, 1.051, 1.051, 1.053, 1.055,
+                            1.039, 1.042, 1.045, 1.048, 1.049, 1.051, 1.053, 1.055, 1.056, 1.057, 1.059, 1.061, 1.063, 1.065, 1.068, 1.071, 1.073, 1.074, 1.074, 1.074, 1.072, 1.071, 1.069, 1.066, 1.064, 1.062, 1.057, 1.054, 1.048, 1.048, 1.049, 1.053,
+                            1.036, 1.038, 1.042, 1.045, 1.047, 1.049, 1.051, 1.052, 1.054, 1.056, 1.058, 1.061, 1.063, 1.065, 1.068, 1.069, 1.072, 1.073, 1.073, 1.072, 1.071, 1.069, 1.066, 1.065, 1.062, 1.059, 1.055, 1.052, 1.047, 1.047, 1.047, 1.049,
+                            1.032, 1.036, 1.038, 1.042, 1.044, 1.046, 1.049, 1.051, 1.053, 1.055, 1.058, 1.061, 1.062, 1.066, 1.067, 1.069, 1.071, 1.071, 1.071, 1.071, 1.069, 1.067, 1.065, 1.063, 1.061, 1.057, 1.054, 1.051, 1.045, 1.045, 1.046, 1.048,
+                            1.028, 1.032, 1.036, 1.038, 1.042, 1.044, 1.045, 1.049, 1.051, 1.054, 1.057, 1.059, 1.061, 1.065, 1.066, 1.067, 1.068, 1.069, 1.069, 1.069, 1.067, 1.066, 1.064, 1.063, 1.061, 1.055, 1.052, 1.049, 1.044, 1.044, 1.046, 1.048,
+                            1.025, 1.027, 1.032, 1.035, 1.036, 1.041, 1.043, 1.045, 1.049, 1.051, 1.054, 1.057, 1.059, 1.062, 1.065, 1.066, 1.066, 1.066, 1.067, 1.066, 1.065, 1.065, 1.063, 1.061, 1.059, 1.056, 1.052, 1.047, 1.043, 1.042, 1.045, 1.046,
+                            1.017, 1.021, 1.025, 1.029, 1.034, 1.036, 1.041, 1.042, 1.044, 1.047, 1.049, 1.053, 1.055, 1.057, 1.059, 1.061, 1.063, 1.063, 1.063, 1.064, 1.063, 1.062, 1.061, 1.059, 1.057, 1.054, 1.051, 1.046, 1.039, 1.039, 1.039, 1.044,
+                            1.009, 1.015, 1.021, 1.023, 1.027, 1.031, 1.036, 1.037, 1.039, 1.042, 1.043, 1.045, 1.048, 1.051, 1.053, 1.055, 1.057, 1.058, 1.058, 1.059, 1.058, 1.058, 1.057, 1.055, 1.054, 1.051, 1.046, 1.041, 1.037, 1.037, 1.036, 1.038,
+                            1.004, 1.008, 1.014, 1.019, 1.022, 1.024, 1.025, 1.028, 1.029, 1.029, 1.031, 1.036, 1.039, 1.043, 1.046, 1.048, 1.049, 1.049, 1.052, 1.052, 1.052, 1.051, 1.049, 1.048, 1.045, 1.044, 1.041, 1.038, 1.033, 1.029, 1.031, 1.031
+                        ]
+                    },
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            1.058, 1.066, 1.068, 1.072, 1.073, 1.075, 1.076, 1.074, 1.073, 1.072, 1.071, 1.071, 1.071, 1.071, 1.071, 1.072, 1.069, 1.068, 1.066, 1.064, 1.061, 1.058, 1.053, 1.049, 1.044, 1.037, 1.031, 1.024, 1.014, 1.009, 1.007, 1.006,
+                            1.061, 1.065, 1.068, 1.072, 1.073, 1.074, 1.074, 1.074, 1.072, 1.069, 1.069, 1.071, 1.071, 1.072, 1.073, 1.072, 1.072, 1.069, 1.067, 1.066, 1.062, 1.058, 1.054, 1.051, 1.045, 1.041, 1.034, 1.026, 1.016, 1.011, 1.009, 1.011,
+                            1.062, 1.066, 1.071, 1.073, 1.073, 1.074, 1.074, 1.074, 1.073, 1.069, 1.069, 1.072, 1.074, 1.074, 1.075, 1.075, 1.076, 1.074, 1.072, 1.071, 1.068, 1.065, 1.059, 1.055, 1.052, 1.046, 1.039, 1.032, 1.026, 1.019, 1.019, 1.022,
+                            1.066, 1.071, 1.074, 1.076, 1.078, 1.079, 1.078, 1.078, 1.077, 1.077, 1.077, 1.078, 1.079, 1.081, 1.082, 1.084, 1.083, 1.081, 1.079, 1.076, 1.075, 1.071, 1.067, 1.063, 1.058, 1.053, 1.047, 1.041, 1.032, 1.032, 1.029, 1.029,
+                            1.069, 1.073, 1.077, 1.079, 1.081, 1.082, 1.081, 1.081, 1.081, 1.081, 1.082, 1.083, 1.084, 1.086, 1.086, 1.087, 1.086, 1.086, 1.084, 1.082, 1.079, 1.076, 1.072, 1.069, 1.064, 1.059, 1.053, 1.047, 1.039, 1.037, 1.036, 1.039,
+                            1.071, 1.075, 1.078, 1.081, 1.082, 1.084, 1.084, 1.085, 1.084, 1.084, 1.084, 1.086, 1.087, 1.089, 1.091, 1.091, 1.091, 1.089, 1.088, 1.086, 1.083, 1.081, 1.078, 1.073, 1.069, 1.064, 1.058, 1.052, 1.044, 1.042, 1.042, 1.042,
+                            1.072, 1.078, 1.079, 1.082, 1.085, 1.086, 1.086, 1.087, 1.086, 1.087, 1.088, 1.089, 1.091, 1.093, 1.094, 1.095, 1.095, 1.093, 1.092, 1.091, 1.088, 1.086, 1.083, 1.079, 1.074, 1.071, 1.064, 1.057, 1.049, 1.047, 1.047, 1.051,
+                            1.072, 1.079, 1.082, 1.085, 1.087, 1.089, 1.089, 1.091, 1.091, 1.092, 1.092, 1.094, 1.095, 1.097, 1.099, 1.098, 1.098, 1.096, 1.095, 1.094, 1.093, 1.089, 1.088, 1.084, 1.079, 1.075, 1.069, 1.063, 1.054, 1.053, 1.053, 1.058,
+                            1.073, 1.081, 1.085, 1.088, 1.091, 1.092, 1.093, 1.093, 1.093, 1.094, 1.096, 1.096, 1.099, 1.101, 1.102, 1.101, 1.099, 1.099, 1.098, 1.097, 1.095, 1.093, 1.089, 1.088, 1.084, 1.078, 1.075, 1.068, 1.059, 1.058, 1.059, 1.059,
+                            1.076, 1.083, 1.086, 1.089, 1.093, 1.094, 1.095, 1.095, 1.095, 1.096, 1.098, 1.099, 1.101, 1.103, 1.103, 1.103, 1.102, 1.101, 1.099, 1.099, 1.098, 1.095, 1.093, 1.091, 1.088, 1.084, 1.077, 1.073, 1.066, 1.063, 1.062, 1.066,
+                            1.081, 1.085, 1.089, 1.093, 1.095, 1.096, 1.096, 1.096, 1.097, 1.098, 1.099, 1.101, 1.103, 1.103, 1.104, 1.103, 1.103, 1.102, 1.102, 1.101, 1.099, 1.098, 1.096, 1.093, 1.091, 1.087, 1.082, 1.076, 1.067, 1.066, 1.068, 1.071,
+                            1.085, 1.088, 1.093, 1.095, 1.097, 1.098, 1.097, 1.098, 1.098, 1.099, 1.101, 1.102, 1.103, 1.104, 1.104, 1.104, 1.104, 1.104, 1.103, 1.103, 1.102, 1.099, 1.098, 1.096, 1.093, 1.089, 1.084, 1.078, 1.069, 1.069, 1.071, 1.073,
+                            1.085, 1.092, 1.094, 1.096, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.105, 1.104, 1.104, 1.103, 1.103, 1.102, 1.099, 1.097, 1.095, 1.091, 1.086, 1.081, 1.075, 1.072, 1.072, 1.074,
+                            1.087, 1.093, 1.096, 1.098, 1.101, 1.101, 1.101, 1.099, 1.101, 1.101, 1.101, 1.101, 1.103, 1.104, 1.105, 1.106, 1.106, 1.105, 1.105, 1.104, 1.104, 1.103, 1.101, 1.099, 1.096, 1.092, 1.087, 1.082, 1.075, 1.074, 1.075, 1.077,
+                            1.088, 1.094, 1.097, 1.099, 1.101, 1.101, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.104, 1.105, 1.106, 1.106, 1.106, 1.105, 1.105, 1.105, 1.103, 1.102, 1.099, 1.096, 1.093, 1.089, 1.083, 1.076, 1.076, 1.076, 1.077,
+                            1.089, 1.094, 1.098, 1.099, 1.101, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.107, 1.107, 1.107, 1.105, 1.104, 1.102, 1.099, 1.097, 1.094, 1.091, 1.083, 1.076, 1.076, 1.077, 1.078,
+                            1.091, 1.096, 1.098, 1.099, 1.099, 1.098, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.102, 1.103, 1.104, 1.105, 1.105, 1.106, 1.107, 1.107, 1.105, 1.103, 1.102, 1.099, 1.097, 1.094, 1.089, 1.084, 1.077, 1.077, 1.077, 1.078,
+                            1.091, 1.094, 1.096, 1.097, 1.096, 1.096, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.102, 1.103, 1.103, 1.104, 1.105, 1.106, 1.106, 1.104, 1.103, 1.102, 1.099, 1.097, 1.094, 1.091, 1.084, 1.077, 1.077, 1.078, 1.079,
+                            1.091, 1.091, 1.093, 1.094, 1.093, 1.093, 1.094, 1.095, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.101, 1.101, 1.103, 1.104, 1.105, 1.104, 1.103, 1.101, 1.099, 1.097, 1.094, 1.089, 1.084, 1.077, 1.077, 1.078, 1.079,
+                            1.083, 1.087, 1.088, 1.089, 1.089, 1.091, 1.092, 1.094, 1.095, 1.095, 1.096, 1.096, 1.096, 1.097, 1.098, 1.098, 1.098, 1.101, 1.102, 1.103, 1.102, 1.102, 1.101, 1.098, 1.096, 1.092, 1.088, 1.083, 1.076, 1.076, 1.077, 1.079,
+                            1.077, 1.081, 1.082, 1.084, 1.086, 1.087, 1.089, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.092, 1.092, 1.094, 1.096, 1.098, 1.099, 1.101, 1.102, 1.099, 1.099, 1.097, 1.093, 1.089, 1.087, 1.081, 1.074, 1.073, 1.075, 1.077,
+                            1.067, 1.072, 1.075, 1.078, 1.082, 1.084, 1.085, 1.086, 1.087, 1.087, 1.087, 1.087, 1.086, 1.087, 1.089, 1.091, 1.094, 1.097, 1.098, 1.099, 1.099, 1.098, 1.097, 1.094, 1.091, 1.087, 1.083, 1.079, 1.071, 1.071, 1.072, 1.075,
+                            1.061, 1.065, 1.069, 1.072, 1.075, 1.079, 1.081, 1.082, 1.082, 1.083, 1.083, 1.083, 1.083, 1.084, 1.087, 1.089, 1.092, 1.095, 1.097, 1.097, 1.097, 1.096, 1.094, 1.091, 1.087, 1.084, 1.079, 1.074, 1.068, 1.067, 1.068, 1.069,
+                            1.053, 1.058, 1.063, 1.067, 1.071, 1.073, 1.075, 1.076, 1.078, 1.078, 1.079, 1.079, 1.081, 1.083, 1.085, 1.088, 1.091, 1.093, 1.095, 1.095, 1.093, 1.092, 1.089, 1.086, 1.084, 1.081, 1.075, 1.071, 1.064, 1.061, 1.063, 1.065,
+                            1.049, 1.053, 1.059, 1.062, 1.065, 1.068, 1.071, 1.073, 1.074, 1.075, 1.077, 1.078, 1.079, 1.082, 1.085, 1.088, 1.089, 1.091, 1.091, 1.091, 1.089, 1.088, 1.085, 1.082, 1.079, 1.075, 1.071, 1.066, 1.059, 1.059, 1.061, 1.063,
+                            1.044, 1.048, 1.053, 1.057, 1.061, 1.065, 1.066, 1.069, 1.071, 1.073, 1.074, 1.076, 1.078, 1.082, 1.084, 1.086, 1.088, 1.089, 1.089, 1.088, 1.087, 1.084, 1.081, 1.079, 1.076, 1.071, 1.067, 1.062, 1.055, 1.055, 1.056, 1.059,
+                            1.039, 1.046, 1.049, 1.054, 1.057, 1.059, 1.062, 1.064, 1.067, 1.071, 1.073, 1.075, 1.077, 1.081, 1.082, 1.084, 1.084, 1.085, 1.085, 1.084, 1.083, 1.081, 1.077, 1.075, 1.071, 1.068, 1.063, 1.057, 1.052, 1.051, 1.054, 1.055,
+                            1.034, 1.041, 1.046, 1.049, 1.053, 1.055, 1.058, 1.062, 1.064, 1.067, 1.069, 1.072, 1.075, 1.078, 1.079, 1.081, 1.081, 1.081, 1.081, 1.079, 1.078, 1.076, 1.074, 1.071, 1.068, 1.064, 1.061, 1.054, 1.049, 1.049, 1.052, 1.054,
+                            1.029, 1.034, 1.041, 1.043, 1.048, 1.051, 1.054, 1.056, 1.061, 1.063, 1.066, 1.067, 1.071, 1.073, 1.075, 1.075, 1.075, 1.076, 1.075, 1.075, 1.074, 1.072, 1.071, 1.068, 1.064, 1.061, 1.057, 1.051, 1.047, 1.046, 1.048, 1.054,
+                            1.019, 1.027, 1.033, 1.036, 1.042, 1.046, 1.049, 1.051, 1.053, 1.056, 1.058, 1.061, 1.064, 1.065, 1.066, 1.069, 1.071, 1.071, 1.069, 1.068, 1.068, 1.067, 1.064, 1.062, 1.059, 1.057, 1.052, 1.047, 1.044, 1.043, 1.048, 1.067,
+                            1.009, 1.018, 1.025, 1.028, 1.033, 1.039, 1.042, 1.043, 1.044, 1.046, 1.047, 1.049, 1.052, 1.055, 1.057, 1.059, 1.061, 1.061, 1.061, 1.062, 1.061, 1.058, 1.057, 1.056, 1.052, 1.051, 1.046, 1.042, 1.038, 1.038, 1.059, 1.067,
+                            1.005, 1.009, 1.017, 1.023, 1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.032, 1.036, 1.041, 1.044, 1.046, 1.047, 1.047, 1.048, 1.049, 1.049, 1.049, 1.048, 1.046, 1.044, 1.042, 1.039, 1.039, 1.034, 1.033, 1.048, 1.062, 1.062
+                        ]
+                    },
+                    {
+                        "ct": 5000,
+                        "table":
+                        [
+                            1.051, 1.052, 1.057, 1.061, 1.061, 1.061, 1.061, 1.061, 1.061, 1.059, 1.059, 1.059, 1.062, 1.063, 1.065, 1.067, 1.066, 1.064, 1.063, 1.062, 1.058, 1.055, 1.049, 1.047, 1.043, 1.039, 1.037, 1.029, 1.028, 1.024, 1.026, 1.027,
+                            1.048, 1.049, 1.052, 1.053, 1.053, 1.051, 1.051, 1.051, 1.049, 1.048, 1.049, 1.051, 1.052, 1.055, 1.055, 1.056, 1.057, 1.058, 1.058, 1.055, 1.053, 1.051, 1.047, 1.044, 1.041, 1.038, 1.034, 1.029, 1.025, 1.024, 1.025, 1.029,
+                            1.047, 1.048, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.048, 1.048, 1.048, 1.051, 1.051, 1.053, 1.055, 1.056, 1.056, 1.056, 1.055, 1.054, 1.051, 1.049, 1.046, 1.043, 1.041, 1.037, 1.035, 1.029, 1.026, 1.026, 1.029, 1.031,
+                            1.046, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.049, 1.051, 1.052, 1.055, 1.056, 1.056, 1.057, 1.057, 1.056, 1.054, 1.052, 1.049, 1.047, 1.045, 1.041, 1.039, 1.035, 1.032, 1.029, 1.031, 1.032, 1.038,
+                            1.047, 1.049, 1.051, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.051, 1.053, 1.055, 1.056, 1.058, 1.058, 1.056, 1.056, 1.053, 1.051, 1.049, 1.048, 1.045, 1.044, 1.041, 1.038, 1.034, 1.031, 1.031, 1.036, 1.041,
+                            1.046, 1.048, 1.048, 1.048, 1.048, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.051, 1.052, 1.054, 1.055, 1.057, 1.057, 1.057, 1.056, 1.054, 1.051, 1.049, 1.049, 1.047, 1.044, 1.042, 1.039, 1.035, 1.032, 1.033, 1.039, 1.043,
+                            1.046, 1.047, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.049, 1.049, 1.051, 1.053, 1.054, 1.055, 1.057, 1.056, 1.055, 1.055, 1.054, 1.053, 1.051, 1.049, 1.048, 1.046, 1.043, 1.042, 1.038, 1.033, 1.034, 1.042, 1.047,
+                            1.045, 1.046, 1.048, 1.049, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.049, 1.051, 1.053, 1.055, 1.055, 1.056, 1.055, 1.055, 1.054, 1.054, 1.053, 1.052, 1.051, 1.049, 1.047, 1.045, 1.043, 1.041, 1.036, 1.037, 1.044, 1.047,
+                            1.044, 1.047, 1.048, 1.049, 1.051, 1.049, 1.048, 1.048, 1.048, 1.048, 1.049, 1.051, 1.052, 1.055, 1.056, 1.056, 1.055, 1.054, 1.054, 1.053, 1.053, 1.052, 1.049, 1.049, 1.048, 1.046, 1.044, 1.042, 1.037, 1.037, 1.045, 1.049,
+                            1.045, 1.047, 1.048, 1.051, 1.051, 1.049, 1.049, 1.048, 1.048, 1.048, 1.049, 1.051, 1.051, 1.053, 1.055, 1.055, 1.054, 1.053, 1.053, 1.053, 1.053, 1.051, 1.051, 1.051, 1.048, 1.047, 1.045, 1.042, 1.039, 1.039, 1.048, 1.051,
+                            1.046, 1.047, 1.049, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.053, 1.054, 1.054, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.046, 1.043, 1.039, 1.039, 1.051, 1.055,
+                            1.046, 1.048, 1.049, 1.051, 1.051, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.051, 1.051, 1.049, 1.048, 1.047, 1.043, 1.041, 1.041, 1.053, 1.056,
+                            1.047, 1.049, 1.051, 1.051, 1.051, 1.049, 1.049, 1.047, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.044, 1.042, 1.042, 1.054, 1.058,
+                            1.048, 1.049, 1.051, 1.052, 1.051, 1.049, 1.048, 1.047, 1.046, 1.046, 1.047, 1.047, 1.048, 1.051, 1.051, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.045, 1.043, 1.043, 1.055, 1.057,
+                            1.048, 1.051, 1.052, 1.051, 1.051, 1.049, 1.047, 1.046, 1.046, 1.046, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.052, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.044, 1.056, 1.058,
+                            1.049, 1.051, 1.052, 1.051, 1.049, 1.048, 1.046, 1.046, 1.044, 1.046, 1.046, 1.047, 1.047, 1.049, 1.051, 1.051, 1.052, 1.053, 1.053, 1.053, 1.052, 1.052, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.044, 1.056, 1.059,
+                            1.051, 1.052, 1.053, 1.051, 1.048, 1.047, 1.045, 1.043, 1.043, 1.044, 1.045, 1.046, 1.047, 1.048, 1.051, 1.051, 1.051, 1.053, 1.053, 1.053, 1.052, 1.051, 1.049, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.046, 1.057, 1.061,
+                            1.051, 1.051, 1.049, 1.048, 1.047, 1.045, 1.043, 1.043, 1.044, 1.044, 1.045, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.046, 1.055, 1.083, 1.092,
+                            1.048, 1.048, 1.047, 1.045, 1.045, 1.043, 1.042, 1.042, 1.043, 1.043, 1.044, 1.044, 1.044, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.055, 1.073, 1.098, 1.099,
+                            1.044, 1.044, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.041, 1.042, 1.041, 1.041, 1.041, 1.042, 1.044, 1.045, 1.047, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.048, 1.047, 1.048, 1.048, 1.072, 1.087, 1.097, 1.099,
+                            1.038, 1.037, 1.036, 1.036, 1.036, 1.037, 1.037, 1.038, 1.038, 1.038, 1.039, 1.038, 1.038, 1.038, 1.039, 1.041, 1.043, 1.045, 1.047, 1.048, 1.049, 1.048, 1.048, 1.047, 1.047, 1.045, 1.045, 1.048, 1.071, 1.087, 1.096, 1.098,
+                            1.029, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.035, 1.035, 1.034, 1.033, 1.033, 1.034, 1.034, 1.036, 1.039, 1.041, 1.044, 1.045, 1.047, 1.048, 1.046, 1.046, 1.046, 1.044, 1.044, 1.043, 1.042, 1.049, 1.081, 1.095, 1.096,
+                            1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.033, 1.035, 1.038, 1.039, 1.043, 1.044, 1.045, 1.045, 1.044, 1.044, 1.043, 1.043, 1.041, 1.041, 1.039, 1.039, 1.049, 1.087, 1.095,
+                            1.021, 1.023, 1.025, 1.026, 1.027, 1.028, 1.029, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.032, 1.035, 1.038, 1.041, 1.043, 1.045, 1.044, 1.044, 1.042, 1.041, 1.041, 1.039, 1.039, 1.039, 1.037, 1.035, 1.036, 1.054, 1.071,
+                            1.017, 1.021, 1.023, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.027, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.041, 1.042, 1.044, 1.044, 1.043, 1.041, 1.039, 1.039, 1.038, 1.037, 1.036, 1.034, 1.034, 1.034, 1.046, 1.051,
+                            1.014, 1.019, 1.022, 1.024, 1.024, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.031, 1.033, 1.035, 1.038, 1.041, 1.042, 1.042, 1.043, 1.042, 1.041, 1.039, 1.038, 1.038, 1.037, 1.036, 1.034, 1.033, 1.032, 1.033, 1.045, 1.047,
+                            1.014, 1.019, 1.021, 1.023, 1.024, 1.025, 1.025, 1.026, 1.027, 1.029, 1.031, 1.032, 1.035, 1.037, 1.039, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.039, 1.038, 1.037, 1.037, 1.035, 1.034, 1.033, 1.033, 1.033, 1.044, 1.047,
+                            1.015, 1.019, 1.019, 1.022, 1.024, 1.025, 1.025, 1.027, 1.029, 1.031, 1.032, 1.033, 1.036, 1.038, 1.039, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.036, 1.034, 1.034, 1.033, 1.034, 1.043, 1.046,
+                            1.014, 1.016, 1.019, 1.019, 1.022, 1.023, 1.025, 1.026, 1.029, 1.031, 1.032, 1.033, 1.036, 1.037, 1.039, 1.039, 1.039, 1.041, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.037, 1.036, 1.034, 1.034, 1.033, 1.034, 1.042, 1.045,
+                            1.011, 1.013, 1.016, 1.018, 1.021, 1.023, 1.024, 1.024, 1.026, 1.028, 1.029, 1.031, 1.033, 1.035, 1.036, 1.036, 1.037, 1.038, 1.038, 1.038, 1.038, 1.038, 1.037, 1.037, 1.036, 1.036, 1.034, 1.033, 1.032, 1.033, 1.039, 1.044,
+                            1.003, 1.007, 1.012, 1.014, 1.016, 1.019, 1.022, 1.022, 1.022, 1.022, 1.024, 1.025, 1.026, 1.028, 1.029, 1.031, 1.033, 1.034, 1.034, 1.036, 1.036, 1.035, 1.033, 1.034, 1.033, 1.033, 1.032, 1.031, 1.029, 1.029, 1.034, 1.038,
+                            1.001, 1.002, 1.008, 1.011, 1.013, 1.015, 1.014, 1.014, 1.013, 1.012, 1.013, 1.015, 1.019, 1.022, 1.023, 1.025, 1.026, 1.026, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.025, 1.025, 1.026, 1.031, 1.035
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.179, 1.187, 1.194, 1.197, 1.199, 1.201, 1.199, 1.199, 1.198, 1.198, 1.196, 1.195, 1.192, 1.192, 1.191, 1.191, 1.187, 1.182, 1.176, 1.171, 1.162, 1.156, 1.143, 1.132, 1.114, 1.097, 1.081, 1.061, 1.041, 1.019, 1.006, 1.001,
+                            1.187, 1.189, 1.194, 1.197, 1.199, 1.201, 1.201, 1.201, 1.199, 1.199, 1.196, 1.195, 1.195, 1.194, 1.193, 1.192, 1.189, 1.187, 1.181, 1.175, 1.168, 1.157, 1.152, 1.135, 1.122, 1.104, 1.086, 1.069, 1.047, 1.027, 1.012, 1.005,
+                            1.188, 1.189, 1.195, 1.199, 1.201, 1.202, 1.203, 1.204, 1.202, 1.201, 1.201, 1.199, 1.199, 1.201, 1.199, 1.196, 1.195, 1.192, 1.189, 1.183, 1.175, 1.166, 1.155, 1.146, 1.129, 1.117, 1.099, 1.082, 1.061, 1.041, 1.025, 1.012,
+                            1.189, 1.197, 1.199, 1.203, 1.205, 1.207, 1.208, 1.208, 1.208, 1.208, 1.207, 1.207, 1.206, 1.207, 1.207, 1.205, 1.203, 1.202, 1.195, 1.189, 1.183, 1.174, 1.165, 1.154, 1.138, 1.125, 1.108, 1.088, 1.071, 1.049, 1.041, 1.029,
+                            1.198, 1.199, 1.204, 1.207, 1.209, 1.209, 1.209, 1.209, 1.211, 1.211, 1.209, 1.209, 1.209, 1.212, 1.212, 1.212, 1.209, 1.206, 1.202, 1.196, 1.189, 1.182, 1.172, 1.162, 1.151, 1.134, 1.118, 1.099, 1.079, 1.063, 1.049, 1.041,
+                            1.199, 1.204, 1.206, 1.209, 1.211, 1.211, 1.212, 1.213, 1.214, 1.214, 1.214, 1.213, 1.215, 1.216, 1.216, 1.216, 1.213, 1.209, 1.208, 1.202, 1.196, 1.188, 1.179, 1.169, 1.157, 1.142, 1.125, 1.111, 1.088, 1.073, 1.062, 1.054,
+                            1.202, 1.205, 1.208, 1.211, 1.213, 1.215, 1.215, 1.216, 1.216, 1.216, 1.218, 1.219, 1.219, 1.221, 1.221, 1.221, 1.218, 1.215, 1.211, 1.207, 1.202, 1.194, 1.186, 1.177, 1.162, 1.149, 1.137, 1.116, 1.098, 1.082, 1.073, 1.063,
+                            1.202, 1.205, 1.209, 1.213, 1.215, 1.217, 1.218, 1.219, 1.219, 1.221, 1.222, 1.223, 1.223, 1.224, 1.223, 1.223, 1.221, 1.218, 1.215, 1.211, 1.207, 1.201, 1.189, 1.181, 1.171, 1.158, 1.143, 1.129, 1.107, 1.089, 1.081, 1.074,
+                            1.203, 1.207, 1.209, 1.215, 1.217, 1.219, 1.221, 1.222, 1.222, 1.224, 1.225, 1.225, 1.226, 1.227, 1.227, 1.225, 1.223, 1.221, 1.218, 1.215, 1.209, 1.202, 1.195, 1.186, 1.178, 1.164, 1.151, 1.133, 1.115, 1.098, 1.088, 1.083,
+                            1.202, 1.209, 1.213, 1.217, 1.219, 1.221, 1.222, 1.223, 1.224, 1.225, 1.226, 1.228, 1.228, 1.228, 1.228, 1.226, 1.225, 1.223, 1.219, 1.216, 1.212, 1.205, 1.198, 1.192, 1.183, 1.169, 1.154, 1.142, 1.122, 1.106, 1.097, 1.089,
+                            1.206, 1.211, 1.215, 1.218, 1.221, 1.223, 1.224, 1.224, 1.225, 1.226, 1.227, 1.229, 1.229, 1.229, 1.229, 1.228, 1.226, 1.224, 1.222, 1.218, 1.215, 1.209, 1.203, 1.196, 1.188, 1.175, 1.161, 1.149, 1.127, 1.112, 1.103, 1.097,
+                            1.209, 1.213, 1.217, 1.218, 1.222, 1.224, 1.225, 1.225, 1.226, 1.227, 1.229, 1.229, 1.229, 1.229, 1.229, 1.229, 1.227, 1.225, 1.223, 1.221, 1.217, 1.213, 1.207, 1.199, 1.191, 1.179, 1.164, 1.149, 1.133, 1.117, 1.109, 1.103,
+                            1.211, 1.216, 1.218, 1.219, 1.224, 1.225, 1.226, 1.226, 1.227, 1.228, 1.229, 1.229, 1.229, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.222, 1.219, 1.215, 1.209, 1.202, 1.194, 1.182, 1.169, 1.153, 1.137, 1.122, 1.115, 1.108,
+                            1.211, 1.215, 1.219, 1.219, 1.224, 1.225, 1.227, 1.227, 1.228, 1.229, 1.229, 1.229, 1.229, 1.231, 1.231, 1.231, 1.229, 1.227, 1.226, 1.223, 1.221, 1.217, 1.211, 1.203, 1.195, 1.183, 1.171, 1.156, 1.141, 1.123, 1.116, 1.113,
+                            1.209, 1.215, 1.219, 1.222, 1.225, 1.225, 1.226, 1.226, 1.227, 1.228, 1.229, 1.229, 1.231, 1.231, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.222, 1.217, 1.211, 1.204, 1.196, 1.184, 1.174, 1.159, 1.142, 1.128, 1.121, 1.114,
+                            1.208, 1.215, 1.218, 1.221, 1.223, 1.223, 1.224, 1.224, 1.226, 1.227, 1.227, 1.228, 1.229, 1.231, 1.231, 1.231, 1.231, 1.229, 1.229, 1.226, 1.223, 1.218, 1.213, 1.207, 1.197, 1.188, 1.176, 1.161, 1.143, 1.129, 1.121, 1.114,
+                            1.208, 1.213, 1.216, 1.219, 1.221, 1.221, 1.222, 1.223, 1.224, 1.225, 1.226, 1.227, 1.228, 1.231, 1.231, 1.231, 1.231, 1.231, 1.229, 1.227, 1.224, 1.218, 1.214, 1.207, 1.197, 1.188, 1.177, 1.161, 1.145, 1.131, 1.122, 1.115,
+                            1.207, 1.208, 1.214, 1.214, 1.217, 1.218, 1.219, 1.221, 1.223, 1.224, 1.225, 1.225, 1.226, 1.227, 1.228, 1.228, 1.229, 1.229, 1.228, 1.226, 1.223, 1.219, 1.215, 1.207, 1.198, 1.188, 1.177, 1.161, 1.147, 1.131, 1.123, 1.116,
+                            1.199, 1.204, 1.208, 1.208, 1.211, 1.212, 1.215, 1.218, 1.221, 1.221, 1.222, 1.223, 1.223, 1.225, 1.225, 1.227, 1.226, 1.227, 1.226, 1.225, 1.223, 1.219, 1.212, 1.207, 1.198, 1.187, 1.177, 1.161, 1.146, 1.131, 1.123, 1.116,
+                            1.193, 1.196, 1.199, 1.201, 1.204, 1.206, 1.209, 1.213, 1.215, 1.217, 1.218, 1.219, 1.221, 1.221, 1.221, 1.222, 1.223, 1.224, 1.224, 1.223, 1.222, 1.218, 1.211, 1.207, 1.197, 1.187, 1.175, 1.161, 1.146, 1.129, 1.122, 1.113,
+                            1.177, 1.182, 1.188, 1.192, 1.195, 1.198, 1.203, 1.208, 1.211, 1.212, 1.214, 1.214, 1.214, 1.215, 1.216, 1.217, 1.219, 1.221, 1.223, 1.222, 1.221, 1.216, 1.212, 1.204, 1.197, 1.186, 1.175, 1.159, 1.143, 1.129, 1.119, 1.114,
+                            1.162, 1.171, 1.177, 1.183, 1.187, 1.192, 1.196, 1.201, 1.204, 1.205, 1.206, 1.207, 1.208, 1.209, 1.212, 1.215, 1.216, 1.219, 1.221, 1.219, 1.218, 1.214, 1.211, 1.203, 1.195, 1.184, 1.173, 1.157, 1.139, 1.128, 1.117, 1.113,
+                            1.149, 1.158, 1.167, 1.172, 1.178, 1.184, 1.189, 1.193, 1.195, 1.196, 1.201, 1.201, 1.201, 1.206, 1.209, 1.212, 1.214, 1.216, 1.218, 1.218, 1.216, 1.212, 1.207, 1.201, 1.192, 1.181, 1.167, 1.155, 1.136, 1.121, 1.114, 1.107,
+                            1.137, 1.147, 1.155, 1.161, 1.169, 1.174, 1.179, 1.183, 1.188, 1.189, 1.193, 1.196, 1.199, 1.201, 1.206, 1.209, 1.213, 1.214, 1.215, 1.214, 1.211, 1.208, 1.202, 1.194, 1.186, 1.177, 1.163, 1.149, 1.132, 1.117, 1.109, 1.104,
+                            1.126, 1.136, 1.144, 1.151, 1.157, 1.163, 1.171, 1.177, 1.181, 1.183, 1.186, 1.191, 1.196, 1.201, 1.204, 1.207, 1.211, 1.213, 1.213, 1.211, 1.209, 1.204, 1.199, 1.191, 1.183, 1.172, 1.158, 1.144, 1.127, 1.112, 1.105, 1.098,
+                            1.114, 1.125, 1.134, 1.142, 1.147, 1.155, 1.161, 1.166, 1.171, 1.177, 1.181, 1.186, 1.191, 1.197, 1.202, 1.204, 1.207, 1.209, 1.209, 1.209, 1.205, 1.201, 1.195, 1.188, 1.178, 1.168, 1.154, 1.139, 1.122, 1.106, 1.099, 1.095,
+                            1.107, 1.114, 1.123, 1.132, 1.137, 1.144, 1.152, 1.157, 1.162, 1.169, 1.176, 1.181, 1.187, 1.193, 1.198, 1.202, 1.204, 1.205, 1.206, 1.204, 1.201, 1.197, 1.191, 1.183, 1.175, 1.162, 1.149, 1.133, 1.117, 1.103, 1.095, 1.088,
+                            1.094, 1.101, 1.112, 1.117, 1.129, 1.133, 1.141, 1.149, 1.156, 1.161, 1.169, 1.175, 1.182, 1.187, 1.192, 1.196, 1.199, 1.201, 1.201, 1.199, 1.196, 1.192, 1.187, 1.181, 1.171, 1.157, 1.145, 1.128, 1.114, 1.099, 1.088, 1.085,
+                            1.083, 1.091, 1.099, 1.104, 1.112, 1.121, 1.128, 1.139, 1.146, 1.154, 1.159, 1.168, 1.174, 1.182, 1.187, 1.188, 1.193, 1.195, 1.195, 1.194, 1.191, 1.188, 1.182, 1.172, 1.163, 1.154, 1.138, 1.123, 1.109, 1.096, 1.085, 1.079,
+                            1.065, 1.074, 1.082, 1.092, 1.101, 1.109, 1.118, 1.126, 1.133, 1.143, 1.149, 1.158, 1.164, 1.171, 1.176, 1.179, 1.185, 1.186, 1.186, 1.185, 1.184, 1.181, 1.172, 1.165, 1.157, 1.148, 1.133, 1.118, 1.101, 1.086, 1.079, 1.071,
+                            1.045, 1.059, 1.067, 1.076, 1.084, 1.097, 1.106, 1.113, 1.123, 1.129, 1.134, 1.141, 1.149, 1.155, 1.161, 1.165, 1.172, 1.175, 1.177, 1.176, 1.174, 1.172, 1.164, 1.156, 1.148, 1.135, 1.121, 1.108, 1.091, 1.079, 1.067, 1.057,
+                            1.038, 1.045, 1.057, 1.067, 1.075, 1.083, 1.094, 1.102, 1.109, 1.115, 1.122, 1.131, 1.136, 1.144, 1.151, 1.155, 1.161, 1.161, 1.164, 1.164, 1.164, 1.161, 1.158, 1.147, 1.138, 1.125, 1.111, 1.095, 1.079, 1.067, 1.057, 1.051
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 2400,
+                        "table":
+                        [
+                            1.261, 1.265, 1.267, 1.273, 1.276, 1.283, 1.283, 1.291, 1.294, 1.299, 1.301, 1.304, 1.304, 1.305, 1.297, 1.301, 1.304, 1.303, 1.301, 1.298, 1.285, 1.271, 1.252, 1.234, 1.204, 1.177, 1.148, 1.115, 1.083, 1.048, 1.004, 1.001,
+                            1.274, 1.283, 1.289, 1.292, 1.299, 1.302, 1.309, 1.314, 1.317, 1.321, 1.322, 1.326, 1.329, 1.332, 1.335, 1.335, 1.335, 1.329, 1.322, 1.311, 1.299, 1.286, 1.265, 1.249, 1.224, 1.201, 1.173, 1.141, 1.111, 1.077, 1.031, 1.004,
+                            1.287, 1.292, 1.299, 1.303, 1.306, 1.312, 1.317, 1.322, 1.327, 1.331, 1.334, 1.338, 1.341, 1.344, 1.345, 1.346, 1.344, 1.339, 1.335, 1.324, 1.312, 1.299, 1.286, 1.265, 1.245, 1.218, 1.193, 1.159, 1.126, 1.094, 1.051, 1.028,
+                            1.297, 1.299, 1.307, 1.311, 1.314, 1.321, 1.325, 1.329, 1.335, 1.339, 1.344, 1.348, 1.349, 1.353, 1.353, 1.355, 1.354, 1.351, 1.345, 1.337, 1.324, 1.311, 1.299, 1.279, 1.256, 1.232, 1.202, 1.173, 1.141, 1.109, 1.066, 1.042,
+                            1.303, 1.309, 1.315, 1.318, 1.324, 1.327, 1.334, 1.339, 1.343, 1.347, 1.352, 1.354, 1.361, 1.364, 1.366, 1.365, 1.365, 1.361, 1.358, 1.349, 1.338, 1.323, 1.309, 1.292, 1.269, 1.244, 1.217, 1.189, 1.157, 1.121, 1.079, 1.057,
+                            1.312, 1.316, 1.322, 1.326, 1.329, 1.337, 1.341, 1.346, 1.351, 1.353, 1.358, 1.365, 1.368, 1.371, 1.373, 1.373, 1.373, 1.367, 1.363, 1.358, 1.349, 1.336, 1.319, 1.301, 1.285, 1.257, 1.231, 1.201, 1.168, 1.137, 1.094, 1.065,
+                            1.317, 1.323, 1.329, 1.332, 1.338, 1.342, 1.349, 1.353, 1.356, 1.361, 1.367, 1.372, 1.375, 1.379, 1.381, 1.381, 1.379, 1.377, 1.371, 1.364, 1.357, 1.345, 1.333, 1.312, 1.292, 1.267, 1.239, 1.211, 1.181, 1.142, 1.105, 1.078,
+                            1.321, 1.329, 1.334, 1.341, 1.343, 1.351, 1.355, 1.359, 1.361, 1.368, 1.373, 1.377, 1.381, 1.385, 1.387, 1.387, 1.385, 1.381, 1.376, 1.371, 1.362, 1.351, 1.338, 1.319, 1.299, 1.276, 1.252, 1.221, 1.189, 1.156, 1.113, 1.086,
+                            1.328, 1.334, 1.341, 1.344, 1.351, 1.353, 1.359, 1.363, 1.368, 1.374, 1.377, 1.382, 1.385, 1.389, 1.392, 1.391, 1.389, 1.387, 1.381, 1.375, 1.367, 1.359, 1.345, 1.327, 1.311, 1.284, 1.261, 1.231, 1.201, 1.169, 1.121, 1.091,
+                            1.331, 1.338, 1.343, 1.347, 1.352, 1.358, 1.362, 1.367, 1.371, 1.375, 1.379, 1.385, 1.389, 1.393, 1.395, 1.396, 1.393, 1.391, 1.385, 1.378, 1.373, 1.362, 1.349, 1.335, 1.313, 1.291, 1.265, 1.238, 1.209, 1.175, 1.129, 1.098,
+                            1.331, 1.341, 1.345, 1.349, 1.355, 1.359, 1.364, 1.368, 1.372, 1.378, 1.381, 1.388, 1.392, 1.394, 1.397, 1.397, 1.396, 1.392, 1.388, 1.382, 1.374, 1.363, 1.353, 1.337, 1.317, 1.295, 1.274, 1.244, 1.216, 1.183, 1.138, 1.101,
+                            1.329, 1.341, 1.345, 1.351, 1.355, 1.361, 1.365, 1.368, 1.373, 1.377, 1.381, 1.387, 1.391, 1.394, 1.396, 1.397, 1.397, 1.393, 1.389, 1.383, 1.375, 1.366, 1.357, 1.339, 1.321, 1.299, 1.276, 1.247, 1.219, 1.187, 1.141, 1.108,
+                            1.331, 1.342, 1.345, 1.351, 1.357, 1.361, 1.365, 1.369, 1.372, 1.377, 1.381, 1.385, 1.391, 1.393, 1.396, 1.396, 1.396, 1.394, 1.389, 1.384, 1.375, 1.368, 1.358, 1.339, 1.324, 1.302, 1.278, 1.251, 1.221, 1.192, 1.146, 1.113,
+                            1.332, 1.341, 1.346, 1.352, 1.356, 1.361, 1.365, 1.369, 1.372, 1.376, 1.379, 1.384, 1.389, 1.392, 1.395, 1.396, 1.396, 1.394, 1.391, 1.386, 1.381, 1.368, 1.358, 1.341, 1.325, 1.303, 1.279, 1.252, 1.224, 1.194, 1.148, 1.117,
+                            1.332, 1.339, 1.346, 1.351, 1.355, 1.358, 1.363, 1.367, 1.371, 1.375, 1.378, 1.384, 1.387, 1.392, 1.395, 1.396, 1.395, 1.393, 1.389, 1.385, 1.381, 1.368, 1.357, 1.342, 1.325, 1.302, 1.279, 1.252, 1.224, 1.195, 1.151, 1.119,
+                            1.332, 1.338, 1.344, 1.349, 1.354, 1.357, 1.361, 1.366, 1.369, 1.373, 1.377, 1.383, 1.388, 1.391, 1.392, 1.395, 1.393, 1.393, 1.391, 1.386, 1.379, 1.367, 1.356, 1.342, 1.324, 1.302, 1.279, 1.253, 1.224, 1.195, 1.152, 1.118,
+                            1.331, 1.335, 1.339, 1.346, 1.351, 1.355, 1.357, 1.363, 1.367, 1.372, 1.377, 1.381, 1.386, 1.388, 1.392, 1.393, 1.393, 1.392, 1.389, 1.385, 1.377, 1.367, 1.356, 1.341, 1.325, 1.303, 1.279, 1.252, 1.224, 1.195, 1.152, 1.118,
+                            1.324, 1.329, 1.335, 1.339, 1.347, 1.351, 1.356, 1.359, 1.364, 1.369, 1.375, 1.378, 1.383, 1.388, 1.389, 1.392, 1.392, 1.391, 1.387, 1.383, 1.376, 1.366, 1.355, 1.342, 1.324, 1.302, 1.279, 1.253, 1.225, 1.196, 1.153, 1.116,
+                            1.314, 1.318, 1.328, 1.333, 1.339, 1.345, 1.349, 1.356, 1.361, 1.366, 1.371, 1.376, 1.381, 1.383, 1.387, 1.388, 1.388, 1.387, 1.385, 1.381, 1.376, 1.367, 1.356, 1.341, 1.323, 1.303, 1.279, 1.252, 1.226, 1.196, 1.153, 1.117,
+                            1.303, 1.312, 1.318, 1.322, 1.328, 1.338, 1.345, 1.348, 1.357, 1.362, 1.367, 1.371, 1.375, 1.379, 1.382, 1.384, 1.384, 1.384, 1.382, 1.379, 1.375, 1.365, 1.356, 1.339, 1.322, 1.301, 1.278, 1.251, 1.224, 1.195, 1.152, 1.117,
+                            1.288, 1.299, 1.305, 1.313, 1.321, 1.328, 1.339, 1.345, 1.348, 1.355, 1.362, 1.365, 1.369, 1.373, 1.377, 1.379, 1.381, 1.381, 1.381, 1.377, 1.372, 1.363, 1.353, 1.338, 1.319, 1.299, 1.277, 1.249, 1.219, 1.194, 1.149, 1.116,
+                            1.277, 1.286, 1.293, 1.302, 1.309, 1.319, 1.327, 1.336, 1.342, 1.346, 1.351, 1.357, 1.362, 1.367, 1.371, 1.374, 1.375, 1.377, 1.377, 1.374, 1.368, 1.359, 1.349, 1.335, 1.318, 1.296, 1.271, 1.245, 1.217, 1.193, 1.147, 1.112,
+                            1.256, 1.271, 1.277, 1.288, 1.293, 1.305, 1.316, 1.322, 1.331, 1.336, 1.343, 1.348, 1.354, 1.359, 1.366, 1.368, 1.371, 1.373, 1.372, 1.368, 1.362, 1.356, 1.343, 1.329, 1.311, 1.291, 1.266, 1.238, 1.214, 1.184, 1.142, 1.111,
+                            1.234, 1.251, 1.261, 1.269, 1.282, 1.291, 1.302, 1.313, 1.319, 1.327, 1.334, 1.341, 1.348, 1.353, 1.359, 1.365, 1.367, 1.367, 1.366, 1.362, 1.356, 1.349, 1.339, 1.322, 1.304, 1.284, 1.261, 1.235, 1.208, 1.178, 1.136, 1.104,
+                            1.218, 1.231, 1.244, 1.253, 1.265, 1.278, 1.289, 1.297, 1.304, 1.316, 1.325, 1.333, 1.339, 1.346, 1.351, 1.356, 1.361, 1.361, 1.361, 1.355, 1.349, 1.341, 1.328, 1.314, 1.299, 1.278, 1.255, 1.229, 1.203, 1.175, 1.133, 1.099,
+                            1.199, 1.214, 1.227, 1.235, 1.247, 1.262, 1.271, 1.283, 1.292, 1.301, 1.311, 1.322, 1.331, 1.338, 1.342, 1.349, 1.353, 1.353, 1.351, 1.347, 1.341, 1.331, 1.321, 1.308, 1.289, 1.269, 1.246, 1.219, 1.195, 1.167, 1.127, 1.096,
+                            1.179, 1.195, 1.207, 1.218, 1.231, 1.241, 1.257, 1.267, 1.278, 1.288, 1.297, 1.309, 1.321, 1.328, 1.333, 1.338, 1.344, 1.346, 1.342, 1.336, 1.331, 1.323, 1.312, 1.298, 1.284, 1.262, 1.241, 1.214, 1.191, 1.162, 1.118, 1.092,
+                            1.159, 1.174, 1.189, 1.198, 1.209, 1.225, 1.238, 1.252, 1.261, 1.272, 1.286, 1.295, 1.306, 1.314, 1.324, 1.327, 1.332, 1.333, 1.333, 1.329, 1.322, 1.314, 1.303, 1.293, 1.275, 1.253, 1.231, 1.207, 1.181, 1.151, 1.112, 1.081,
+                            1.142, 1.156, 1.168, 1.179, 1.192, 1.206, 1.221, 1.232, 1.243, 1.255, 1.269, 1.278, 1.291, 1.303, 1.311, 1.313, 1.316, 1.319, 1.321, 1.318, 1.314, 1.303, 1.295, 1.281, 1.268, 1.248, 1.225, 1.197, 1.174, 1.147, 1.106, 1.077,
+                            1.119, 1.134, 1.145, 1.159, 1.171, 1.186, 1.199, 1.212, 1.223, 1.236, 1.247, 1.259, 1.272, 1.281, 1.291, 1.297, 1.299, 1.307, 1.308, 1.306, 1.302, 1.294, 1.284, 1.272, 1.257, 1.239, 1.215, 1.188, 1.163, 1.136, 1.099, 1.069,
+                            1.101, 1.114, 1.126, 1.134, 1.151, 1.163, 1.181, 1.189, 1.202, 1.216, 1.229, 1.239, 1.252, 1.262, 1.269, 1.283, 1.288, 1.293, 1.294, 1.292, 1.289, 1.284, 1.272, 1.261, 1.244, 1.228, 1.204, 1.178, 1.154, 1.131, 1.089, 1.062,
+                            1.087, 1.098, 1.112, 1.124, 1.134, 1.148, 1.161, 1.175, 1.185, 1.201, 1.212, 1.224, 1.236, 1.248, 1.259, 1.268, 1.274, 1.275, 1.275, 1.276, 1.276, 1.272, 1.263, 1.247, 1.232, 1.212, 1.192, 1.166, 1.143, 1.115, 1.078, 1.051
+                        ]
+                    },
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            1.333, 1.336, 1.336, 1.343, 1.347, 1.356, 1.361, 1.364, 1.366, 1.371, 1.371, 1.377, 1.382, 1.385, 1.385, 1.385, 1.381, 1.381, 1.375, 1.369, 1.363, 1.348, 1.326, 1.298, 1.269, 1.241, 1.207, 1.169, 1.134, 1.096, 1.044, 1.038,
+                            1.345, 1.351, 1.361, 1.365, 1.369, 1.376, 1.384, 1.389, 1.389, 1.395, 1.398, 1.403, 1.406, 1.409, 1.411, 1.409, 1.409, 1.408, 1.401, 1.389, 1.379, 1.359, 1.339, 1.319, 1.294, 1.264, 1.235, 1.199, 1.163, 1.127, 1.074, 1.044,
+                            1.359, 1.362, 1.375, 1.377, 1.381, 1.388, 1.394, 1.397, 1.401, 1.403, 1.407, 1.413, 1.417, 1.419, 1.422, 1.425, 1.425, 1.419, 1.413, 1.403, 1.391, 1.379, 1.359, 1.339, 1.315, 1.287, 1.254, 1.219, 1.184, 1.151, 1.098, 1.071,
+                            1.366, 1.376, 1.382, 1.386, 1.391, 1.397, 1.401, 1.404, 1.408, 1.411, 1.419, 1.421, 1.426, 1.429, 1.433, 1.433, 1.431, 1.429, 1.423, 1.415, 1.403, 1.391, 1.373, 1.351, 1.329, 1.301, 1.269, 1.231, 1.198, 1.164, 1.118, 1.089,
+                            1.378, 1.383, 1.393, 1.396, 1.399, 1.403, 1.411, 1.414, 1.419, 1.423, 1.426, 1.432, 1.439, 1.444, 1.445, 1.444, 1.442, 1.439, 1.432, 1.424, 1.416, 1.399, 1.385, 1.361, 1.343, 1.314, 1.283, 1.249, 1.215, 1.179, 1.131, 1.098,
+                            1.385, 1.394, 1.399, 1.401, 1.406, 1.412, 1.418, 1.423, 1.427, 1.431, 1.435, 1.442, 1.446, 1.451, 1.452, 1.451, 1.448, 1.445, 1.443, 1.434, 1.424, 1.411, 1.393, 1.377, 1.352, 1.329, 1.296, 1.264, 1.228, 1.195, 1.145, 1.114,
+                            1.391, 1.401, 1.404, 1.408, 1.413, 1.419, 1.425, 1.428, 1.432, 1.437, 1.442, 1.451, 1.453, 1.456, 1.459, 1.461, 1.459, 1.453, 1.449, 1.443, 1.434, 1.419, 1.404, 1.387, 1.362, 1.338, 1.306, 1.272, 1.238, 1.207, 1.153, 1.126,
+                            1.399, 1.405, 1.412, 1.415, 1.419, 1.425, 1.429, 1.436, 1.441, 1.444, 1.451, 1.454, 1.457, 1.463, 1.466, 1.466, 1.465, 1.461, 1.454, 1.449, 1.441, 1.427, 1.414, 1.393, 1.376, 1.347, 1.321, 1.286, 1.251, 1.216, 1.169, 1.134,
+                            1.399, 1.412, 1.416, 1.419, 1.424, 1.429, 1.436, 1.441, 1.445, 1.449, 1.455, 1.461, 1.463, 1.468, 1.472, 1.471, 1.471, 1.468, 1.459, 1.454, 1.445, 1.435, 1.419, 1.402, 1.382, 1.354, 1.329, 1.296, 1.259, 1.225, 1.175, 1.143,
+                            1.403, 1.416, 1.419, 1.423, 1.427, 1.434, 1.439, 1.443, 1.449, 1.452, 1.459, 1.463, 1.468, 1.472, 1.473, 1.472, 1.471, 1.469, 1.466, 1.455, 1.449, 1.438, 1.425, 1.408, 1.389, 1.362, 1.337, 1.304, 1.271, 1.233, 1.184, 1.148,
+                            1.404, 1.418, 1.421, 1.425, 1.429, 1.436, 1.441, 1.444, 1.449, 1.453, 1.461, 1.465, 1.471, 1.472, 1.474, 1.474, 1.472, 1.471, 1.467, 1.459, 1.451, 1.441, 1.428, 1.411, 1.393, 1.368, 1.341, 1.309, 1.277, 1.239, 1.189, 1.152,
+                            1.404, 1.417, 1.421, 1.425, 1.431, 1.437, 1.441, 1.445, 1.449, 1.453, 1.461, 1.465, 1.469, 1.471, 1.472, 1.475, 1.474, 1.471, 1.466, 1.461, 1.452, 1.442, 1.431, 1.411, 1.395, 1.371, 1.345, 1.313, 1.279, 1.242, 1.194, 1.156,
+                            1.405, 1.417, 1.421, 1.428, 1.433, 1.437, 1.441, 1.445, 1.449, 1.453, 1.459, 1.462, 1.467, 1.469, 1.472, 1.475, 1.475, 1.471, 1.467, 1.461, 1.452, 1.443, 1.431, 1.414, 1.396, 1.372, 1.346, 1.315, 1.284, 1.249, 1.199, 1.161,
+                            1.409, 1.418, 1.422, 1.426, 1.432, 1.436, 1.439, 1.444, 1.447, 1.452, 1.457, 1.461, 1.464, 1.469, 1.471, 1.474, 1.474, 1.471, 1.467, 1.462, 1.452, 1.445, 1.431, 1.415, 1.395, 1.374, 1.349, 1.314, 1.285, 1.253, 1.201, 1.165,
+                            1.409, 1.418, 1.421, 1.425, 1.429, 1.433, 1.437, 1.441, 1.446, 1.451, 1.454, 1.459, 1.463, 1.467, 1.469, 1.473, 1.473, 1.469, 1.466, 1.461, 1.455, 1.445, 1.431, 1.416, 1.396, 1.373, 1.349, 1.316, 1.286, 1.254, 1.205, 1.165,
+                            1.409, 1.412, 1.421, 1.422, 1.426, 1.429, 1.436, 1.439, 1.443, 1.449, 1.452, 1.457, 1.462, 1.465, 1.469, 1.471, 1.471, 1.469, 1.467, 1.461, 1.455, 1.445, 1.431, 1.416, 1.396, 1.372, 1.349, 1.317, 1.286, 1.254, 1.205, 1.165,
+                            1.403, 1.409, 1.413, 1.419, 1.423, 1.429, 1.432, 1.437, 1.441, 1.445, 1.451, 1.455, 1.462, 1.464, 1.468, 1.471, 1.471, 1.469, 1.465, 1.461, 1.455, 1.443, 1.429, 1.414, 1.397, 1.372, 1.349, 1.316, 1.286, 1.254, 1.205, 1.163,
+                            1.396, 1.402, 1.409, 1.414, 1.419, 1.423, 1.429, 1.434, 1.439, 1.444, 1.448, 1.453, 1.456, 1.462, 1.463, 1.468, 1.469, 1.468, 1.465, 1.459, 1.452, 1.442, 1.429, 1.412, 1.396, 1.373, 1.349, 1.315, 1.287, 1.253, 1.206, 1.163,
+                            1.389, 1.393, 1.402, 1.406, 1.414, 1.418, 1.424, 1.431, 1.435, 1.441, 1.447, 1.449, 1.455, 1.457, 1.461, 1.462, 1.466, 1.465, 1.461, 1.458, 1.451, 1.442, 1.428, 1.412, 1.395, 1.372, 1.347, 1.315, 1.287, 1.252, 1.205, 1.164,
+                            1.373, 1.385, 1.388, 1.394, 1.403, 1.408, 1.418, 1.424, 1.431, 1.437, 1.441, 1.447, 1.449, 1.454, 1.456, 1.459, 1.461, 1.461, 1.459, 1.457, 1.449, 1.439, 1.427, 1.412, 1.394, 1.371, 1.346, 1.315, 1.284, 1.249, 1.202, 1.162,
+                            1.359, 1.371, 1.377, 1.383, 1.392, 1.403, 1.408, 1.416, 1.423, 1.431, 1.437, 1.439, 1.444, 1.447, 1.451, 1.455, 1.458, 1.459, 1.457, 1.453, 1.447, 1.435, 1.425, 1.409, 1.391, 1.367, 1.341, 1.312, 1.281, 1.246, 1.199, 1.161,
+                            1.345, 1.356, 1.363, 1.371, 1.379, 1.389, 1.401, 1.408, 1.415, 1.421, 1.428, 1.431, 1.436, 1.441, 1.446, 1.449, 1.453, 1.453, 1.453, 1.449, 1.443, 1.433, 1.421, 1.406, 1.389, 1.364, 1.337, 1.306, 1.274, 1.244, 1.197, 1.158,
+                            1.321, 1.337, 1.344, 1.355, 1.362, 1.376, 1.387, 1.396, 1.403, 1.409, 1.416, 1.423, 1.428, 1.433, 1.438, 1.444, 1.447, 1.449, 1.449, 1.443, 1.438, 1.428, 1.417, 1.397, 1.381, 1.359, 1.331, 1.301, 1.271, 1.236, 1.188, 1.157,
+                            1.298, 1.315, 1.325, 1.332, 1.344, 1.357, 1.368, 1.383, 1.391, 1.398, 1.404, 1.413, 1.422, 1.427, 1.434, 1.438, 1.442, 1.443, 1.443, 1.439, 1.431, 1.419, 1.409, 1.394, 1.372, 1.353, 1.325, 1.296, 1.261, 1.231, 1.183, 1.148,
+                            1.278, 1.294, 1.304, 1.316, 1.328, 1.341, 1.353, 1.362, 1.375, 1.386, 1.392, 1.402, 1.411, 1.421, 1.426, 1.431, 1.436, 1.436, 1.436, 1.431, 1.421, 1.414, 1.401, 1.387, 1.365, 1.344, 1.319, 1.289, 1.258, 1.226, 1.178, 1.142,
+                            1.259, 1.273, 1.287, 1.296, 1.311, 1.322, 1.335, 1.349, 1.358, 1.371, 1.381, 1.392, 1.399, 1.411, 1.417, 1.423, 1.425, 1.425, 1.424, 1.419, 1.414, 1.404, 1.392, 1.373, 1.359, 1.336, 1.311, 1.282, 1.249, 1.216, 1.175, 1.139,
+                            1.234, 1.253, 1.266, 1.276, 1.291, 1.301, 1.315, 1.328, 1.344, 1.355, 1.364, 1.377, 1.386, 1.397, 1.406, 1.412, 1.416, 1.419, 1.417, 1.409, 1.404, 1.394, 1.383, 1.368, 1.351, 1.329, 1.301, 1.271, 1.242, 1.208, 1.162, 1.131,
+                            1.213, 1.229, 1.245, 1.254, 1.267, 1.282, 1.297, 1.311, 1.325, 1.337, 1.351, 1.362, 1.374, 1.381, 1.393, 1.399, 1.402, 1.404, 1.404, 1.402, 1.394, 1.384, 1.373, 1.361, 1.342, 1.319, 1.293, 1.266, 1.234, 1.204, 1.157, 1.125,
+                            1.188, 1.208, 1.221, 1.231, 1.245, 1.263, 1.276, 1.291, 1.302, 1.317, 1.333, 1.341, 1.358, 1.366, 1.373, 1.382, 1.386, 1.388, 1.391, 1.388, 1.382, 1.375, 1.365, 1.351, 1.333, 1.311, 1.284, 1.254, 1.225, 1.198, 1.152, 1.121,
+                            1.165, 1.182, 1.195, 1.209, 1.221, 1.239, 1.254, 1.268, 1.278, 1.296, 1.309, 1.322, 1.337, 1.348, 1.355, 1.365, 1.371, 1.374, 1.375, 1.373, 1.372, 1.365, 1.352, 1.341, 1.321, 1.301, 1.273, 1.242, 1.212, 1.183, 1.141, 1.111,
+                            1.141, 1.159, 1.173, 1.183, 1.198, 1.215, 1.229, 1.245, 1.258, 1.271, 1.286, 1.299, 1.317, 1.326, 1.334, 1.347, 1.355, 1.359, 1.362, 1.362, 1.358, 1.351, 1.341, 1.325, 1.307, 1.289, 1.259, 1.233, 1.203, 1.175, 1.119, 1.062,
+                            1.126, 1.139, 1.155, 1.171, 1.182, 1.197, 1.211, 1.225, 1.241, 1.255, 1.267, 1.281, 1.295, 1.309, 1.321, 1.331, 1.337, 1.341, 1.342, 1.343, 1.342, 1.341, 1.329, 1.311, 1.292, 1.271, 1.245, 1.217, 1.189, 1.152, 1.075, 1.049
+                        ]
+                    },
+                    {
+                        "ct": 5000,
+                        "table":
+                        [
+                            1.413, 1.419, 1.423, 1.434, 1.444, 1.447, 1.455, 1.456, 1.459, 1.462, 1.466, 1.469, 1.478, 1.481, 1.482, 1.479, 1.477, 1.474, 1.463, 1.457, 1.445, 1.427, 1.396, 1.368, 1.329, 1.287, 1.247, 1.212, 1.165, 1.123, 1.064, 1.049,
+                            1.434, 1.446, 1.456, 1.464, 1.473, 1.478, 1.491, 1.492, 1.495, 1.502, 1.507, 1.509, 1.512, 1.522, 1.523, 1.519, 1.512, 1.505, 1.499, 1.487, 1.468, 1.451, 1.422, 1.389, 1.354, 1.321, 1.281, 1.242, 1.199, 1.157, 1.105, 1.064,
+                            1.454, 1.459, 1.476, 1.484, 1.489, 1.496, 1.505, 1.515, 1.514, 1.522, 1.529, 1.533, 1.535, 1.542, 1.544, 1.543, 1.539, 1.536, 1.527, 1.513, 1.495, 1.469, 1.451, 1.422, 1.387, 1.352, 1.313, 1.272, 1.227, 1.189, 1.131, 1.097,
+                            1.467, 1.479, 1.488, 1.495, 1.502, 1.512, 1.519, 1.529, 1.534, 1.539, 1.546, 1.551, 1.553, 1.558, 1.562, 1.561, 1.558, 1.554, 1.542, 1.532, 1.514, 1.496, 1.469, 1.446, 1.407, 1.379, 1.335, 1.296, 1.249, 1.211, 1.149, 1.123,
+                            1.485, 1.495, 1.504, 1.509, 1.517, 1.525, 1.539, 1.543, 1.553, 1.558, 1.559, 1.565, 1.568, 1.573, 1.582, 1.582, 1.577, 1.573, 1.563, 1.549, 1.537, 1.514, 1.489, 1.461, 1.434, 1.399, 1.356, 1.316, 1.271, 1.231, 1.168, 1.135,
+                            1.499, 1.504, 1.514, 1.522, 1.531, 1.543, 1.549, 1.561, 1.563, 1.568, 1.579, 1.585, 1.591, 1.595, 1.595, 1.596, 1.595, 1.589, 1.583, 1.569, 1.552, 1.537, 1.511, 1.486, 1.457, 1.418, 1.379, 1.334, 1.291, 1.254, 1.189, 1.149,
+                            1.506, 1.514, 1.527, 1.535, 1.543, 1.553, 1.562, 1.569, 1.577, 1.583, 1.594, 1.601, 1.606, 1.611, 1.612, 1.612, 1.609, 1.604, 1.594, 1.585, 1.569, 1.551, 1.531, 1.502, 1.472, 1.429, 1.396, 1.352, 1.311, 1.264, 1.201, 1.167,
+                            1.513, 1.527, 1.537, 1.546, 1.553, 1.563, 1.576, 1.584, 1.588, 1.595, 1.605, 1.611, 1.617, 1.623, 1.625, 1.623, 1.622, 1.616, 1.607, 1.597, 1.583, 1.563, 1.541, 1.518, 1.489, 1.452, 1.409, 1.372, 1.324, 1.281, 1.221, 1.181,
+                            1.525, 1.537, 1.547, 1.554, 1.561, 1.575, 1.584, 1.591, 1.596, 1.605, 1.613, 1.621, 1.627, 1.633, 1.634, 1.635, 1.631, 1.626, 1.618, 1.608, 1.594, 1.577, 1.555, 1.533, 1.501, 1.467, 1.431, 1.387, 1.341, 1.295, 1.232, 1.189,
+                            1.529, 1.546, 1.552, 1.561, 1.569, 1.581, 1.588, 1.595, 1.604, 1.613, 1.621, 1.626, 1.634, 1.639, 1.639, 1.643, 1.638, 1.634, 1.626, 1.616, 1.605, 1.586, 1.567, 1.548, 1.512, 1.478, 1.445, 1.398, 1.357, 1.313, 1.244, 1.209,
+                            1.529, 1.549, 1.558, 1.565, 1.571, 1.583, 1.593, 1.601, 1.608, 1.618, 1.624, 1.631, 1.639, 1.643, 1.644, 1.644, 1.644, 1.641, 1.633, 1.621, 1.613, 1.594, 1.578, 1.551, 1.524, 1.486, 1.449, 1.408, 1.363, 1.321, 1.255, 1.214,
+                            1.529, 1.552, 1.561, 1.567, 1.577, 1.587, 1.595, 1.604, 1.611, 1.619, 1.626, 1.633, 1.642, 1.648, 1.649, 1.648, 1.645, 1.643, 1.637, 1.627, 1.617, 1.601, 1.584, 1.555, 1.529, 1.493, 1.462, 1.418, 1.373, 1.328, 1.266, 1.225,
+                            1.534, 1.551, 1.562, 1.568, 1.581, 1.591, 1.596, 1.605, 1.612, 1.619, 1.628, 1.633, 1.642, 1.648, 1.651, 1.652, 1.649, 1.643, 1.639, 1.632, 1.619, 1.604, 1.586, 1.561, 1.536, 1.499, 1.466, 1.423, 1.379, 1.335, 1.272, 1.233,
+                            1.535, 1.551, 1.562, 1.569, 1.581, 1.591, 1.598, 1.604, 1.612, 1.619, 1.629, 1.634, 1.639, 1.647, 1.649, 1.652, 1.649, 1.646, 1.643, 1.634, 1.622, 1.606, 1.588, 1.564, 1.538, 1.502, 1.469, 1.425, 1.382, 1.341, 1.275, 1.236,
+                            1.535, 1.549, 1.561, 1.569, 1.578, 1.587, 1.598, 1.604, 1.609, 1.619, 1.629, 1.633, 1.638, 1.644, 1.649, 1.651, 1.649, 1.647, 1.642, 1.634, 1.622, 1.607, 1.588, 1.564, 1.538, 1.505, 1.471, 1.431, 1.385, 1.346, 1.281, 1.236,
+                            1.534, 1.548, 1.559, 1.565, 1.574, 1.585, 1.593, 1.599, 1.607, 1.618, 1.626, 1.631, 1.637, 1.644, 1.648, 1.651, 1.649, 1.647, 1.642, 1.634, 1.625, 1.608, 1.589, 1.566, 1.539, 1.506, 1.472, 1.432, 1.388, 1.347, 1.284, 1.241,
+                            1.532, 1.543, 1.554, 1.562, 1.569, 1.581, 1.592, 1.598, 1.603, 1.614, 1.623, 1.628, 1.634, 1.641, 1.645, 1.647, 1.648, 1.645, 1.641, 1.633, 1.625, 1.606, 1.589, 1.565, 1.538, 1.505, 1.472, 1.431, 1.392, 1.347, 1.287, 1.239,
+                            1.519, 1.531, 1.544, 1.557, 1.565, 1.578, 1.586, 1.594, 1.601, 1.609, 1.619, 1.626, 1.632, 1.641, 1.644, 1.646, 1.647, 1.644, 1.639, 1.631, 1.622, 1.605, 1.589, 1.566, 1.538, 1.505, 1.472, 1.431, 1.392, 1.347, 1.283, 1.227,
+                            1.509, 1.517, 1.531, 1.545, 1.559, 1.567, 1.579, 1.586, 1.596, 1.606, 1.612, 1.621, 1.629, 1.634, 1.637, 1.643, 1.643, 1.641, 1.634, 1.629, 1.621, 1.604, 1.586, 1.566, 1.538, 1.506, 1.471, 1.431, 1.391, 1.336, 1.263, 1.171,
+                            1.492, 1.506, 1.517, 1.528, 1.541, 1.557, 1.568, 1.578, 1.589, 1.598, 1.606, 1.612, 1.621, 1.629, 1.632, 1.634, 1.633, 1.633, 1.631, 1.625, 1.617, 1.601, 1.583, 1.564, 1.535, 1.504, 1.468, 1.431, 1.384, 1.306, 1.205, 1.159,
+                            1.471, 1.486, 1.503, 1.511, 1.525, 1.541, 1.554, 1.565, 1.577, 1.589, 1.597, 1.602, 1.611, 1.617, 1.621, 1.625, 1.625, 1.629, 1.626, 1.622, 1.612, 1.595, 1.578, 1.556, 1.532, 1.499, 1.466, 1.423, 1.379, 1.306, 1.201, 1.145,
+                            1.446, 1.464, 1.481, 1.493, 1.508, 1.523, 1.539, 1.551, 1.561, 1.575, 1.586, 1.592, 1.598, 1.606, 1.612, 1.617, 1.618, 1.622, 1.619, 1.613, 1.604, 1.588, 1.575, 1.551, 1.528, 1.493, 1.457, 1.416, 1.375, 1.326, 1.212, 1.159,
+                            1.419, 1.443, 1.453, 1.468, 1.486, 1.501, 1.519, 1.534, 1.544, 1.558, 1.568, 1.577, 1.585, 1.594, 1.602, 1.607, 1.612, 1.611, 1.609, 1.605, 1.592, 1.581, 1.565, 1.543, 1.516, 1.481, 1.444, 1.405, 1.366, 1.324, 1.254, 1.172,
+                            1.389, 1.415, 1.426, 1.441, 1.463, 1.479, 1.494, 1.515, 1.525, 1.538, 1.555, 1.562, 1.571, 1.579, 1.589, 1.595, 1.601, 1.601, 1.598, 1.592, 1.581, 1.571, 1.553, 1.526, 1.501, 1.472, 1.437, 1.397, 1.355, 1.316, 1.252, 1.212,
+                            1.364, 1.386, 1.405, 1.419, 1.436, 1.456, 1.474, 1.491, 1.504, 1.519, 1.533, 1.541, 1.557, 1.565, 1.573, 1.584, 1.587, 1.588, 1.587, 1.578, 1.571, 1.555, 1.534, 1.514, 1.491, 1.457, 1.425, 1.386, 1.347, 1.304, 1.248, 1.209,
+                            1.335, 1.358, 1.376, 1.395, 1.412, 1.431, 1.449, 1.466, 1.482, 1.495, 1.509, 1.527, 1.538, 1.551, 1.561, 1.566, 1.573, 1.573, 1.569, 1.561, 1.554, 1.541, 1.525, 1.501, 1.476, 1.446, 1.413, 1.372, 1.328, 1.295, 1.235, 1.206,
+                            1.309, 1.333, 1.353, 1.367, 1.386, 1.401, 1.424, 1.441, 1.459, 1.474, 1.489, 1.505, 1.517, 1.533, 1.544, 1.548, 1.553, 1.555, 1.555, 1.546, 1.541, 1.525, 1.507, 1.485, 1.464, 1.435, 1.396, 1.351, 1.317, 1.279, 1.224, 1.188,
+                            1.279, 1.304, 1.326, 1.337, 1.353, 1.379, 1.395, 1.415, 1.432, 1.447, 1.469, 1.482, 1.498, 1.514, 1.524, 1.532, 1.537, 1.537, 1.536, 1.532, 1.522, 1.507, 1.488, 1.472, 1.452, 1.413, 1.378, 1.342, 1.308, 1.271, 1.213, 1.171,
+                            1.253, 1.275, 1.294, 1.304, 1.326, 1.349, 1.368, 1.386, 1.403, 1.421, 1.439, 1.457, 1.473, 1.489, 1.497, 1.504, 1.513, 1.515, 1.515, 1.511, 1.504, 1.489, 1.474, 1.455, 1.431, 1.402, 1.368, 1.329, 1.298, 1.255, 1.201, 1.167,
+                            1.221, 1.238, 1.263, 1.277, 1.296, 1.316, 1.334, 1.353, 1.372, 1.392, 1.411, 1.422, 1.448, 1.458, 1.466, 1.481, 1.489, 1.492, 1.493, 1.489, 1.486, 1.474, 1.455, 1.437, 1.415, 1.381, 1.348, 1.314, 1.275, 1.238, 1.184, 1.155,
+                            1.193, 1.213, 1.227, 1.243, 1.264, 1.287, 1.304, 1.322, 1.339, 1.355, 1.377, 1.395, 1.409, 1.425, 1.438, 1.455, 1.461, 1.466, 1.469, 1.468, 1.464, 1.451, 1.434, 1.415, 1.386, 1.362, 1.331, 1.296, 1.261, 1.225, 1.172, 1.136,
+                            1.172, 1.189, 1.209, 1.225, 1.241, 1.261, 1.279, 1.297, 1.317, 1.334, 1.351, 1.368, 1.387, 1.399, 1.418, 1.426, 1.434, 1.437, 1.437, 1.438, 1.437, 1.433, 1.418, 1.397, 1.365, 1.337, 1.306, 1.269, 1.238, 1.204, 1.156, 1.127
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.096, 1.097, 1.099, 1.101, 1.102, 1.103, 1.106, 1.106, 1.107, 1.106, 1.106, 1.108, 1.109, 1.111, 1.114, 1.115, 1.116, 1.117, 1.114, 1.112, 1.109, 1.106, 1.101, 1.092, 1.083, 1.076, 1.066, 1.054, 1.043, 1.027, 1.008, 1.001,
+                            1.098, 1.099, 1.104, 1.104, 1.103, 1.105, 1.107, 1.108, 1.108, 1.107, 1.108, 1.109, 1.111, 1.113, 1.115, 1.116, 1.117, 1.118, 1.116, 1.115, 1.111, 1.108, 1.102, 1.095, 1.091, 1.078, 1.069, 1.062, 1.049, 1.036, 1.021, 1.006,
+                            1.101, 1.105, 1.105, 1.105, 1.105, 1.107, 1.109, 1.109, 1.109, 1.109, 1.111, 1.111, 1.114, 1.115, 1.117, 1.117, 1.118, 1.119, 1.117, 1.115, 1.112, 1.109, 1.103, 1.098, 1.093, 1.084, 1.075, 1.065, 1.053, 1.041, 1.025, 1.011,
+                            1.101, 1.106, 1.107, 1.106, 1.107, 1.108, 1.111, 1.111, 1.111, 1.111, 1.112, 1.114, 1.116, 1.117, 1.119, 1.119, 1.121, 1.119, 1.119, 1.116, 1.113, 1.111, 1.105, 1.101, 1.094, 1.087, 1.077, 1.069, 1.057, 1.046, 1.031, 1.017,
+                            1.105, 1.108, 1.108, 1.108, 1.108, 1.109, 1.112, 1.111, 1.112, 1.112, 1.112, 1.113, 1.116, 1.117, 1.119, 1.121, 1.121, 1.121, 1.119, 1.116, 1.114, 1.111, 1.106, 1.102, 1.097, 1.089, 1.081, 1.072, 1.059, 1.048, 1.034, 1.021,
+                            1.106, 1.109, 1.111, 1.109, 1.109, 1.111, 1.113, 1.112, 1.112, 1.112, 1.113, 1.114, 1.117, 1.118, 1.119, 1.121, 1.121, 1.119, 1.119, 1.116, 1.115, 1.111, 1.107, 1.104, 1.098, 1.091, 1.083, 1.074, 1.064, 1.052, 1.037, 1.022,
+                            1.107, 1.111, 1.111, 1.111, 1.111, 1.112, 1.113, 1.112, 1.112, 1.113, 1.113, 1.115, 1.116, 1.118, 1.119, 1.119, 1.119, 1.119, 1.117, 1.116, 1.114, 1.111, 1.108, 1.105, 1.099, 1.093, 1.085, 1.077, 1.066, 1.054, 1.041, 1.027,
+                            1.106, 1.111, 1.111, 1.112, 1.112, 1.113, 1.113, 1.112, 1.112, 1.112, 1.113, 1.114, 1.116, 1.116, 1.117, 1.118, 1.118, 1.117, 1.116, 1.115, 1.113, 1.111, 1.108, 1.104, 1.099, 1.093, 1.086, 1.077, 1.068, 1.057, 1.042, 1.029,
+                            1.108, 1.112, 1.112, 1.112, 1.112, 1.113, 1.113, 1.112, 1.112, 1.112, 1.111, 1.113, 1.115, 1.116, 1.116, 1.116, 1.116, 1.115, 1.114, 1.113, 1.112, 1.111, 1.106, 1.103, 1.099, 1.092, 1.085, 1.077, 1.069, 1.058, 1.042, 1.029,
+                            1.109, 1.111, 1.112, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.113, 1.114, 1.114, 1.114, 1.114, 1.113, 1.112, 1.111, 1.109, 1.107, 1.105, 1.102, 1.098, 1.091, 1.085, 1.077, 1.068, 1.059, 1.045, 1.031,
+                            1.109, 1.111, 1.111, 1.111, 1.109, 1.111, 1.109, 1.109, 1.108, 1.108, 1.109, 1.109, 1.111, 1.111, 1.111, 1.111, 1.111, 1.111, 1.108, 1.108, 1.107, 1.105, 1.103, 1.099, 1.096, 1.089, 1.083, 1.077, 1.068, 1.058, 1.045, 1.029,
+                            1.108, 1.109, 1.109, 1.109, 1.109, 1.109, 1.107, 1.106, 1.105, 1.105, 1.106, 1.107, 1.107, 1.107, 1.108, 1.108, 1.107, 1.107, 1.106, 1.105, 1.104, 1.102, 1.101, 1.097, 1.092, 1.088, 1.082, 1.074, 1.067, 1.057, 1.046, 1.031,
+                            1.106, 1.108, 1.109, 1.107, 1.107, 1.106, 1.105, 1.104, 1.104, 1.103, 1.102, 1.102, 1.104, 1.104, 1.104, 1.105, 1.105, 1.105, 1.104, 1.103, 1.101, 1.099, 1.098, 1.095, 1.091, 1.085, 1.081, 1.072, 1.065, 1.057, 1.044, 1.031,
+                            1.104, 1.106, 1.107, 1.106, 1.105, 1.104, 1.103, 1.102, 1.101, 1.101, 1.101, 1.101, 1.101, 1.102, 1.103, 1.103, 1.104, 1.103, 1.102, 1.101, 1.099, 1.098, 1.095, 1.092, 1.089, 1.084, 1.079, 1.071, 1.063, 1.055, 1.044, 1.031,
+                            1.105, 1.106, 1.106, 1.105, 1.104, 1.102, 1.101, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.099, 1.101, 1.101, 1.102, 1.102, 1.101, 1.099, 1.097, 1.096, 1.093, 1.091, 1.087, 1.082, 1.076, 1.069, 1.062, 1.054, 1.043, 1.028,
+                            1.105, 1.106, 1.106, 1.104, 1.103, 1.101, 1.099, 1.099, 1.098, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.101, 1.101, 1.101, 1.099, 1.098, 1.096, 1.095, 1.091, 1.089, 1.086, 1.081, 1.075, 1.071, 1.061, 1.054, 1.043, 1.028,
+                            1.105, 1.105, 1.105, 1.104, 1.102, 1.101, 1.099, 1.098, 1.097, 1.096, 1.096, 1.097, 1.097, 1.098, 1.098, 1.099, 1.099, 1.099, 1.098, 1.097, 1.095, 1.093, 1.091, 1.088, 1.085, 1.079, 1.076, 1.069, 1.061, 1.053, 1.043, 1.027,
+                            1.105, 1.105, 1.104, 1.102, 1.101, 1.099, 1.099, 1.097, 1.097, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.097, 1.098, 1.098, 1.097, 1.096, 1.094, 1.092, 1.091, 1.088, 1.085, 1.079, 1.076, 1.068, 1.062, 1.054, 1.043, 1.027,
+                            1.104, 1.103, 1.103, 1.099, 1.098, 1.098, 1.098, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.097, 1.096, 1.097, 1.097, 1.097, 1.096, 1.094, 1.093, 1.091, 1.089, 1.085, 1.081, 1.076, 1.068, 1.063, 1.054, 1.043, 1.027,
+                            1.099, 1.099, 1.098, 1.098, 1.097, 1.097, 1.097, 1.097, 1.097, 1.096, 1.096, 1.096, 1.095, 1.095, 1.095, 1.095, 1.096, 1.096, 1.097, 1.097, 1.096, 1.094, 1.091, 1.089, 1.085, 1.081, 1.076, 1.068, 1.062, 1.055, 1.044, 1.028,
+                            1.097, 1.096, 1.096, 1.095, 1.095, 1.095, 1.095, 1.097, 1.097, 1.096, 1.095, 1.095, 1.094, 1.094, 1.094, 1.095, 1.096, 1.096, 1.097, 1.098, 1.097, 1.095, 1.092, 1.088, 1.086, 1.083, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.091, 1.092, 1.093, 1.092, 1.092, 1.093, 1.095, 1.095, 1.095, 1.093, 1.092, 1.092, 1.092, 1.093, 1.094, 1.095, 1.096, 1.097, 1.098, 1.098, 1.097, 1.095, 1.093, 1.088, 1.086, 1.082, 1.076, 1.069, 1.062, 1.056, 1.046, 1.031,
+                            1.085, 1.088, 1.088, 1.089, 1.089, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.092, 1.093, 1.094, 1.096, 1.097, 1.099, 1.098, 1.098, 1.097, 1.095, 1.093, 1.089, 1.085, 1.081, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.081, 1.082, 1.084, 1.084, 1.085, 1.087, 1.089, 1.091, 1.091, 1.091, 1.091, 1.092, 1.092, 1.094, 1.096, 1.096, 1.099, 1.099, 1.099, 1.098, 1.097, 1.096, 1.093, 1.089, 1.086, 1.082, 1.076, 1.069, 1.062, 1.056, 1.045, 1.031,
+                            1.073, 1.078, 1.081, 1.082, 1.083, 1.084, 1.088, 1.089, 1.089, 1.089, 1.091, 1.091, 1.093, 1.095, 1.096, 1.098, 1.099, 1.101, 1.099, 1.099, 1.096, 1.095, 1.093, 1.089, 1.086, 1.081, 1.077, 1.069, 1.062, 1.055, 1.043, 1.032,
+                            1.068, 1.072, 1.076, 1.079, 1.081, 1.083, 1.084, 1.087, 1.088, 1.088, 1.089, 1.092, 1.093, 1.096, 1.097, 1.099, 1.099, 1.099, 1.099, 1.097, 1.096, 1.095, 1.092, 1.089, 1.086, 1.081, 1.077, 1.071, 1.062, 1.055, 1.045, 1.034,
+                            1.064, 1.066, 1.072, 1.073, 1.077, 1.079, 1.082, 1.084, 1.086, 1.088, 1.089, 1.091, 1.093, 1.095, 1.097, 1.099, 1.099, 1.099, 1.098, 1.097, 1.096, 1.095, 1.092, 1.089, 1.086, 1.081, 1.077, 1.069, 1.064, 1.055, 1.043, 1.035,
+                            1.057, 1.062, 1.065, 1.068, 1.071, 1.075, 1.077, 1.081, 1.084, 1.086, 1.088, 1.089, 1.092, 1.094, 1.096, 1.098, 1.098, 1.098, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.086, 1.083, 1.077, 1.069, 1.064, 1.055, 1.043, 1.033,
+                            1.051, 1.056, 1.059, 1.062, 1.066, 1.068, 1.074, 1.077, 1.079, 1.083, 1.086, 1.088, 1.089, 1.092, 1.094, 1.096, 1.096, 1.096, 1.096, 1.095, 1.094, 1.093, 1.092, 1.089, 1.086, 1.083, 1.077, 1.068, 1.063, 1.055, 1.043, 1.033,
+                            1.043, 1.048, 1.052, 1.056, 1.059, 1.065, 1.068, 1.071, 1.074, 1.078, 1.081, 1.083, 1.088, 1.089, 1.091, 1.092, 1.094, 1.095, 1.094, 1.094, 1.094, 1.094, 1.092, 1.089, 1.086, 1.083, 1.077, 1.069, 1.062, 1.055, 1.044, 1.031,
+                            1.036, 1.041, 1.045, 1.049, 1.052, 1.059, 1.062, 1.067, 1.069, 1.072, 1.074, 1.077, 1.082, 1.083, 1.086, 1.089, 1.091, 1.092, 1.093, 1.092, 1.092, 1.091, 1.091, 1.088, 1.085, 1.081, 1.076, 1.069, 1.061, 1.054, 1.043, 1.031,
+                            1.029, 1.036, 1.041, 1.045, 1.049, 1.052, 1.056, 1.058, 1.064, 1.064, 1.067, 1.071, 1.075, 1.079, 1.083, 1.085, 1.089, 1.089, 1.089, 1.091, 1.091, 1.091, 1.089, 1.087, 1.083, 1.079, 1.074, 1.066, 1.062, 1.052, 1.041, 1.029
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    3.174, 3.091, 2.978, 2.891, 2.829, 2.779, 2.739, 2.708, 2.683, 2.659, 2.641, 2.623, 2.616, 2.622, 2.629, 2.644, 2.669, 2.691, 2.731, 2.784, 2.843, 2.894, 2.947, 3.004, 3.065, 3.133, 3.214, 3.303, 3.416, 3.541, 3.674, 3.765,
+                    3.093, 2.968, 2.861, 2.778, 2.702, 2.651, 2.599, 2.563, 2.533, 2.509, 2.487, 2.472, 2.466, 2.462, 2.466, 2.481, 2.501, 2.531, 2.568, 2.612, 2.663, 2.712, 2.764, 2.821, 2.881, 2.954, 3.041, 3.137, 3.265, 3.396, 3.482, 3.547,
+                    2.897, 2.851, 2.743, 2.608, 2.521, 2.464, 2.417, 2.381, 2.348, 2.323, 2.302, 2.287, 2.275, 2.264, 2.269, 2.279, 2.297, 2.322, 2.351, 2.392, 2.436, 2.479, 2.526, 2.577, 2.631, 2.701, 2.778, 2.871, 2.985, 3.105, 3.191, 3.254,
+                    2.764, 2.689, 2.586, 2.452, 2.361, 2.303, 2.254, 2.218, 2.189, 2.166, 2.141, 2.123, 2.108, 2.099, 2.098, 2.105, 2.121, 2.144, 2.172, 2.203, 2.238, 2.276, 2.317, 2.358, 2.411, 2.471, 2.541, 2.626, 2.733, 2.844, 2.924, 2.979,
+                    2.581, 2.499, 2.405, 2.296, 2.223, 2.171, 2.126, 2.088, 2.056, 2.031, 2.011, 1.993, 1.975, 1.968, 1.964, 1.968, 1.982, 2.003, 2.027, 2.052, 2.079, 2.109, 2.149, 2.184, 2.231, 2.285, 2.346, 2.427, 2.521, 2.622, 2.691, 2.744,
+                    2.437, 2.343, 2.261, 2.184, 2.116, 2.058, 2.017, 1.979, 1.949, 1.924, 1.899, 1.883, 1.868, 1.859, 1.855, 1.857, 1.866, 1.883, 1.901, 1.923, 1.951, 1.978, 2.011, 2.042, 2.081, 2.133, 2.189, 2.261, 2.349, 2.433, 2.491, 2.541,
+                    2.332, 2.251, 2.166, 2.092, 2.024, 1.969, 1.925, 1.889, 1.856, 1.832, 1.809, 1.791, 1.774, 1.768, 1.762, 1.762, 1.771, 1.779, 1.799, 1.815, 1.837, 1.861, 1.892, 1.924, 1.961, 2.006, 2.059, 2.126, 2.201, 2.279, 2.329, 2.367,
+                    2.249, 2.168, 2.083, 2.005, 1.941, 1.891, 1.845, 1.808, 1.775, 1.749, 1.726, 1.711, 1.696, 1.686, 1.681, 1.681, 1.687, 1.697, 1.712, 1.726, 1.743, 1.765, 1.792, 1.824, 1.859, 1.901, 1.951, 2.009, 2.079, 2.149, 2.194, 2.229,
+                    2.173, 2.094, 2.009, 1.936, 1.871, 1.819, 1.771, 1.736, 1.705, 1.679, 1.656, 1.638, 1.623, 1.612, 1.608, 1.609, 1.613, 1.622, 1.634, 1.647, 1.664, 1.685, 1.709, 1.738, 1.772, 1.813, 1.858, 1.912, 1.979, 2.046, 2.091, 2.121,
+                    2.105, 2.033, 1.947, 1.875, 1.811, 1.756, 1.714, 1.677, 1.643, 1.616, 1.596, 1.577, 1.561, 1.551, 1.544, 1.544, 1.548, 1.556, 1.568, 1.582, 1.596, 1.616, 1.639, 1.665, 1.698, 1.739, 1.783, 1.836, 1.896, 1.959, 1.999, 2.032,
+                    2.045, 1.975, 1.892, 1.819, 1.759, 1.706, 1.661, 1.622, 1.592, 1.563, 1.543, 1.523, 1.509, 1.499, 1.491, 1.491, 1.494, 1.501, 1.512, 1.524, 1.539, 1.558, 1.578, 1.605, 1.638, 1.676, 1.721, 1.769, 1.829, 1.887, 1.923, 1.952,
+                    1.988, 1.923, 1.841, 1.773, 1.711, 1.661, 1.617, 1.579, 1.547, 1.521, 1.496, 1.476, 1.462, 1.452, 1.445, 1.445, 1.448, 1.454, 1.464, 1.476, 1.489, 1.507, 1.529, 1.557, 1.589, 1.626, 1.667, 1.719, 1.774, 1.829, 1.863, 1.893,
+                    1.943, 1.881, 1.803, 1.734, 1.673, 1.621, 1.579, 1.543, 1.508, 1.479, 1.457, 1.436, 1.421, 1.412, 1.405, 1.402, 1.405, 1.412, 1.423, 1.434, 1.449, 1.466, 1.489, 1.516, 1.546, 1.582, 1.625, 1.671, 1.726, 1.777, 1.816, 1.838,
+                    1.913, 1.848, 1.769, 1.701, 1.641, 1.591, 1.548, 1.511, 1.478, 1.449, 1.422, 1.401, 1.387, 1.377, 1.369, 1.368, 1.371, 1.379, 1.389, 1.402, 1.415, 1.432, 1.454, 1.481, 1.513, 1.548, 1.587, 1.633, 1.686, 1.736, 1.772, 1.793,
+                    1.891, 1.819, 1.742, 1.674, 1.614, 1.566, 1.523, 1.485, 1.451, 1.422, 1.397, 1.376, 1.359, 1.347, 1.339, 1.339, 1.343, 1.351, 1.361, 1.375, 1.388, 1.406, 1.429, 1.453, 1.484, 1.517, 1.555, 1.599, 1.652, 1.699, 1.733, 1.755,
+                    1.874, 1.801, 1.721, 1.654, 1.602, 1.552, 1.506, 1.465, 1.429, 1.399, 1.374, 1.352, 1.336, 1.323, 1.315, 1.316, 1.321, 1.329, 1.338, 1.352, 1.369, 1.387, 1.409, 1.435, 1.462, 1.494, 1.529, 1.572, 1.622, 1.669, 1.698, 1.718,
+                    1.868, 1.791, 1.712, 1.647, 1.592, 1.542, 1.497, 1.454, 1.416, 1.385, 1.356, 1.335, 1.319, 1.307, 1.301, 1.299, 1.305, 1.314, 1.324, 1.341, 1.357, 1.376, 1.397, 1.422, 1.449, 1.478, 1.511, 1.551, 1.599, 1.644, 1.671, 1.688,
+                    1.867, 1.789, 1.715, 1.648, 1.591, 1.539, 1.494, 1.451, 1.412, 1.378, 1.352, 1.329, 1.311, 1.301, 1.294, 1.294, 1.298, 1.306, 1.321, 1.335, 1.353, 1.369, 1.391, 1.413, 1.439, 1.469, 1.502, 1.539, 1.583, 1.623, 1.648, 1.666,
+                    1.873, 1.799, 1.724, 1.657, 1.599, 1.546, 1.498, 1.454, 1.416, 1.384, 1.356, 1.334, 1.317, 1.306, 1.299, 1.299, 1.303, 1.311, 1.323, 1.339, 1.354, 1.373, 1.393, 1.414, 1.438, 1.466, 1.495, 1.532, 1.578, 1.617, 1.639, 1.656,
+                    1.884, 1.816, 1.743, 1.675, 1.615, 1.559, 1.509, 1.466, 1.429, 1.397, 1.371, 1.351, 1.332, 1.321, 1.315, 1.313, 1.316, 1.325, 1.337, 1.351, 1.365, 1.383, 1.403, 1.424, 1.449, 1.475, 1.501, 1.537, 1.574, 1.617, 1.639, 1.654,
+                    1.906, 1.845, 1.772, 1.699, 1.636, 1.579, 1.531, 1.489, 1.453, 1.419, 1.395, 1.376, 1.364, 1.354, 1.344, 1.339, 1.338, 1.345, 1.356, 1.368, 1.383, 1.402, 1.422, 1.443, 1.469, 1.492, 1.518, 1.548, 1.583, 1.622, 1.648, 1.658,
+                    1.941, 1.882, 1.804, 1.731, 1.667, 1.611, 1.562, 1.519, 1.484, 1.453, 1.427, 1.412, 1.401, 1.389, 1.381, 1.369, 1.367, 1.372, 1.381, 1.392, 1.409, 1.429, 1.449, 1.473, 1.496, 1.521, 1.546, 1.574, 1.609, 1.637, 1.657, 1.673,
+                    1.987, 1.929, 1.845, 1.773, 1.708, 1.651, 1.603, 1.561, 1.524, 1.495, 1.467, 1.449, 1.441, 1.431, 1.417, 1.404, 1.401, 1.406, 1.415, 1.427, 1.445, 1.463, 1.485, 1.509, 1.536, 1.562, 1.587, 1.612, 1.642, 1.672, 1.678, 1.692,
+                    2.041, 1.978, 1.897, 1.824, 1.757, 1.699, 1.649, 1.606, 1.569, 1.539, 1.513, 1.489, 1.473, 1.461, 1.451, 1.445, 1.445, 1.447, 1.459, 1.472, 1.491, 1.509, 1.533, 1.558, 1.584, 1.607, 1.632, 1.661, 1.692, 1.716, 1.721, 1.728,
+                    2.111, 2.041, 1.957, 1.879, 1.813, 1.755, 1.702, 1.657, 1.622, 1.591, 1.566, 1.543, 1.525, 1.511, 1.501, 1.495, 1.492, 1.497, 1.509, 1.524, 1.543, 1.564, 1.589, 1.615, 1.638, 1.663, 1.688, 1.719, 1.746, 1.774, 1.776, 1.785,
+                    2.186, 2.115, 2.023, 1.943, 1.875, 1.816, 1.762, 1.718, 1.681, 1.649, 1.624, 1.601, 1.583, 1.571, 1.559, 1.552, 1.551, 1.557, 1.571, 1.588, 1.607, 1.628, 1.654, 1.678, 1.704, 1.729, 1.756, 1.783, 1.813, 1.841, 1.849, 1.854,
+                    2.271, 2.188, 2.097, 2.017, 1.948, 1.886, 1.829, 1.784, 1.747, 1.718, 1.693, 1.671, 1.651, 1.639, 1.629, 1.624, 1.625, 1.633, 1.647, 1.663, 1.682, 1.703, 1.728, 1.754, 1.777, 1.805, 1.834, 1.861, 1.892, 1.918, 1.926, 1.939,
+                    2.369, 2.278, 2.182, 2.101, 2.028, 1.967, 1.912, 1.863, 1.827, 1.797, 1.773, 1.751, 1.734, 1.722, 1.713, 1.711, 1.712, 1.719, 1.734, 1.749, 1.768, 1.791, 1.815, 1.839, 1.864, 1.892, 1.919, 1.949, 1.981, 2.011, 2.021, 2.035,
+                    2.479, 2.382, 2.284, 2.202, 2.126, 2.062, 2.005, 1.961, 1.921, 1.891, 1.867, 1.847, 1.832, 1.822, 1.815, 1.814, 1.817, 1.822, 1.837, 1.853, 1.871, 1.891, 1.915, 1.939, 1.964, 1.992, 2.019, 2.053, 2.089, 2.122, 2.133, 2.153,
+                    2.619, 2.509, 2.411, 2.319, 2.239, 2.173, 2.114, 2.072, 2.037, 2.006, 1.982, 1.963, 1.949, 1.941, 1.937, 1.934, 1.937, 1.947, 1.961, 1.977, 1.987, 2.009, 2.034, 2.058, 2.087, 2.112, 2.139, 2.172, 2.211, 2.253, 2.269, 2.297,
+                    2.783, 2.662, 2.554, 2.457, 2.374, 2.304, 2.246, 2.203, 2.166, 2.139, 2.118, 2.099, 2.087, 2.081, 2.077, 2.073, 2.081, 2.092, 2.106, 2.118, 2.133, 2.153, 2.174, 2.197, 2.228, 2.253, 2.281, 2.318, 2.362, 2.407, 2.434, 2.478,
+                    2.878, 2.792, 2.675, 2.577, 2.499, 2.438, 2.396, 2.357, 2.331, 2.309, 2.293, 2.278, 2.267, 2.259, 2.256, 2.258, 2.261, 2.267, 2.278, 2.293, 2.304, 2.319, 2.343, 2.369, 2.389, 2.411, 2.437, 2.469, 2.509, 2.552, 2.621, 2.683
+                ],
+                "sigma": 0.00476,
+                "sigma_Cb": 0.01242
+            }
+        },
+        {
+            "rpi.contrast":
+            {
+                "ce_enable": 1,
+                "gamma_curve":
+                [
+                    0, 0,
+                    1024, 5040,
+                    2048, 9338,
+                    3072, 12356,
+                    4096, 15312,
+                    5120, 18051,
+                    6144, 20790,
+                    7168, 23193,
+                    8192, 25744,
+                    9216, 27942,
+                    10240, 30035,
+                    11264, 32005,
+                    12288, 33975,
+                    13312, 35815,
+                    14336, 37600,
+                    15360, 39168,
+                    16384, 40642,
+                    18432, 43379,
+                    20480, 45749,
+                    22528, 47753,
+                    24576, 49621,
+                    26624, 51253,
+                    28672, 52698,
+                    30720, 53796,
+                    32768, 54876,
+                    36864, 57012,
+                    40960, 58656,
+                    45056, 59954,
+                    49152, 61183,
+                    53248, 62355,
+                    57344, 63419,
+                    61440, 64476,
+                    65535, 65535
+                ]
+            }
+        },
+        {
+            "rpi.sharpen":
+            {
+                "threshold": 0.25,
+                "limit": 1.0,
+                "strength": 1.0
+            }
+        },
+        {
+            "rpi.hdr":
+            {
+                "Off":
+                {
+                    "cadence": [ 0 ]
+                },
+                "MultiExposureUnmerged":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    }
+                },
+                "SingleExposure":
+                {
+                    "cadence": [ 1 ],
+                    "channel_map":
+                    {
+                        "short": 1
+                    },
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "MultiExposure":
+                {
+                    "cadence": [ 1, 2 ],
+                    "channel_map":
+                    {
+                        "short": 1,
+                        "long": 2
+                    },
+                    "stitch_enable": 1,
+                    "spatial_gain": 2.0,
+                    "tonemap_enable": 1
+                },
+                "Night":
+                {
+                    "cadence": [ 3 ],
+                    "channel_map":
+                    {
+                        "night": 3
+                    },
+                    "tonemap_enable": 1,
+                    "tonemap":
+                    [
+                        0, 0,
+                        5000, 20000,
+                        10000, 30000,
+                        20000, 47000,
+                        30000, 55000,
+                        65535, 65535
+                    ]
+                }
+            }
+        }
+    ]
+}
diff -pruN 0.5.0-1/src/ipa/rpi/pisp/pisp.cpp 0.5.2-2/src/ipa/rpi/pisp/pisp.cpp
--- 0.5.0-1/src/ipa/rpi/pisp/pisp.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/pisp/pisp.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,8 +2,9 @@
 /*
  * Copyright (C) 2023, Raspberry Pi Ltd
  *
- * pisp.cpp - Raspberry Pi PiSP IPA
+ * Raspberry Pi PiSP IPA
  */
+
 #include <algorithm>
 #include <cmath>
 #include <mutex>
@@ -218,13 +219,14 @@ private:
 
 	void platformPrepareIsp(const PrepareParams &params,
 				RPiController::Metadata &rpiMetadata) override;
+	void platformPrepareAgc(RPiController::Metadata &rpiMetadata) override;
 	RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
 
 	void handleControls(const ControlList &controls) override;
 
-	void applyWBG(const AwbStatus *awbStatus, const AgcPrepareStatus *agcStatus,
+	void applyWBG(const AwbStatus *awbStatus, double digitalGain,
 		      pisp_be_global_config &global);
-	void applyDgOnly(const AgcPrepareStatus *agcPrepareStatus, pisp_be_global_config &global);
+	void applyDgOnly(double digitalGain, pisp_be_global_config &global);
 	void applyCAC(const CacStatus *cacStatus, pisp_be_global_config &global);
 	void applyContrast(const ContrastStatus *contrastStatus,
 			   pisp_be_global_config &global);
@@ -341,7 +343,6 @@ void IpaPiSP::platformPrepareIsp([[maybe
 				PISP_BE_RGB_ENABLE_SHARPEN + PISP_BE_RGB_ENABLE_SAT_CONTROL);
 
 	NoiseStatus *noiseStatus = rpiMetadata.getLocked<NoiseStatus>("noise.status");
-	AgcPrepareStatus *agcPrepareStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status");
 
 	{
 		/* All Frontend config goes first, we do not want to hold the FE lock for long! */
@@ -355,14 +356,6 @@ void IpaPiSP::platformPrepareIsp([[maybe
 		if (blackLevelStatus)
 			applyBlackLevel(blackLevelStatus, global);
 
-		AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
-		if (awbStatus && agcPrepareStatus) {
-			/* Applies digital gain as well. */
-			applyWBG(awbStatus, agcPrepareStatus, global);
-		} else if (agcPrepareStatus) {
-			/* Mono sensor fallback for digital gain. */
-			applyDgOnly(agcPrepareStatus, global);
-		}
 	}
 
 	CacStatus *cacStatus = rpiMetadata.getLocked<CacStatus>("cac.status");
@@ -443,6 +436,34 @@ void IpaPiSP::platformPrepareIsp([[maybe
 	}
 }
 
+void IpaPiSP::platformPrepareAgc(RPiController::Metadata &rpiMetadata)
+{
+	std::scoped_lock<RPiController::Metadata> l(rpiMetadata);
+
+	AgcStatus *delayedAgcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
+	/* If no delayed status, use the gain from the last mode switch. */
+	double digitalGain = delayedAgcStatus ? delayedAgcStatus->digitalGain : agcStatus_.digitalGain;
+	AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
+
+	pisp_be_global_config global;
+	be_->GetGlobal(global);
+
+	{
+		/* All Frontend config goes first, we do not want to hold the FE lock for long! */
+		std::scoped_lock<FrontEnd> lf(*fe_);
+
+		if (awbStatus) {
+			/* Applies digital gain as well. */
+			applyWBG(awbStatus, digitalGain, global);
+		} else {
+			/* Mono sensor fallback for digital gain. */
+			applyDgOnly(digitalGain, global);
+		}
+	}
+
+	be_->SetGlobal(global);
+}
+
 RPiController::StatisticsPtr IpaPiSP::platformProcessStats(Span<uint8_t> mem)
 {
 	using namespace RPiController;
@@ -515,16 +536,29 @@ void IpaPiSP::handleControls(const Contr
 	}
 }
 
-void IpaPiSP::applyWBG(const AwbStatus *awbStatus, const AgcPrepareStatus *agcPrepareStatus,
+void IpaPiSP::applyWBG(const AwbStatus *awbStatus, double digitalGain,
 		       pisp_be_global_config &global)
 {
 	pisp_wbg_config wbg;
 	pisp_fe_rgby_config rgby = {};
-	double dg = agcPrepareStatus ? agcPrepareStatus->digitalGain : 1.0;
+	double minColourGain = std::min({ awbStatus->gainR, awbStatus->gainG, awbStatus->gainB, 1.0 });
+	/* The 0.1 here doesn't mean much, but just stops arithmetic errors and extreme behaviour. */
+	double extraGain = 1.0 / std::max({ minColourGain, 0.1 });
 
-	wbg.gain_r = clampField(dg * awbStatus->gainR, 14, 10);
-	wbg.gain_g = clampField(dg * awbStatus->gainG, 14, 10);
-	wbg.gain_b = clampField(dg * awbStatus->gainB, 14, 10);
+	/*
+	 * Apply an extra gain of 1 / minColourGain so as not to apply < 1 gains to any
+	 * channels (which would cause saturated pixels to go cyan or magenta).
+	 * Doing this doesn't really apply more gain than necessary, because one of the
+	 * channels is always getting the minimum gain possible. For this reason we also
+	 * don't change the values that we report externally.
+	 */
+	double gainR = awbStatus->gainR * extraGain;
+	double gainG = awbStatus->gainG * extraGain;
+	double gainB = awbStatus->gainB * extraGain;
+
+	wbg.gain_r = clampField(digitalGain * gainR, 14, 10);
+	wbg.gain_g = clampField(digitalGain * gainG, 14, 10);
+	wbg.gain_b = clampField(digitalGain * gainB, 14, 10);
 
 	/*
 	 * The YCbCr conversion block should contain the appropriate YCbCr
@@ -535,9 +569,9 @@ void IpaPiSP::applyWBG(const AwbStatus *
 	be_->GetYcbcr(csc);
 
 	/* The CSC coefficients already have the << 10 scaling applied. */
-	rgby.gain_r = clampField(csc.coeffs[0] * awbStatus->gainR, 14);
-	rgby.gain_g = clampField(csc.coeffs[1] * awbStatus->gainG, 14);
-	rgby.gain_b = clampField(csc.coeffs[2] * awbStatus->gainB, 14);
+	rgby.gain_r = clampField(csc.coeffs[0] * gainR, 14);
+	rgby.gain_g = clampField(csc.coeffs[1] * gainG, 14);
+	rgby.gain_b = clampField(csc.coeffs[2] * gainB, 14);
 
 	LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
 			   << awbStatus->gainB;
@@ -547,15 +581,15 @@ void IpaPiSP::applyWBG(const AwbStatus *
 	global.bayer_enables |= PISP_BE_BAYER_ENABLE_WBG;
 }
 
-void IpaPiSP::applyDgOnly(const AgcPrepareStatus *agcPrepareStatus, pisp_be_global_config &global)
+void IpaPiSP::applyDgOnly(double digitalGain, pisp_be_global_config &global)
 {
 	pisp_wbg_config wbg;
 
-	wbg.gain_r = clampField(agcPrepareStatus->digitalGain, 14, 10);
-	wbg.gain_g = clampField(agcPrepareStatus->digitalGain, 14, 10);
-	wbg.gain_b = clampField(agcPrepareStatus->digitalGain, 14, 10);
+	wbg.gain_r = clampField(digitalGain, 14, 10);
+	wbg.gain_g = clampField(digitalGain, 14, 10);
+	wbg.gain_b = clampField(digitalGain, 14, 10);
 
-	LOG(IPARPI, Debug) << "Applying DG (only) : " << agcPrepareStatus->digitalGain;
+	LOG(IPARPI, Debug) << "Applying DG (only) : " << digitalGain;
 
 	be_->SetWbg(wbg);
 	global.bayer_enables |= PISP_BE_BAYER_ENABLE_WBG;
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/imx283.json 0.5.2-2/src/ipa/rpi/vc4/data/imx283.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/imx283.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/imx283.json	2025-08-07 13:46:17.000000000 +0000
@@ -14,25 +14,25 @@
         {
             "rpi.lux":
             {
-                "reference_shutter_speed": 2461,
-                "reference_gain": 1.0,
+                "reference_shutter_speed": 10857,
+                "reference_gain": 1.49,
                 "reference_aperture": 1.0,
-                "reference_lux": 1148,
-                "reference_Y": 13314
+                "reference_lux": 1050,
+                "reference_Y": 13959
             }
         },
         {
             "rpi.noise":
             {
                 "reference_constant": 0,
-                "reference_slope": 2.204
+                "reference_slope": 2.147
             }
         },
         {
             "rpi.geq":
             {
-                "offset": 199,
-                "slope": 0.01947
+                "offset": 249,
+                "slope": 0.02036
             }
         },
         {
@@ -104,19 +104,35 @@
                     {
                         "lo": 5500,
                         "hi": 6500
+                    },
+                    "cloudy":
+                    {
+                        "lo": 6000,
+                        "hi": 6800
                     }
                 },
                 "bayes": 1,
                 "ct_curve":
                 [
-                    2213.0, 0.9607, 0.2593,
-                    5313.0, 0.4822, 0.5909,
-                    6237.0, 0.4739, 0.6308
+                    2500.0, 0.9429, 0.2809,
+                    2820.0, 0.8488, 0.3472,
+                    2830.0, 0.8303, 0.3609,
+                    2885.0, 0.8177, 0.3703,
+                    3601.0, 0.6935, 0.4705,
+                    3615.0, 0.6918, 0.4719,
+                    3622.0, 0.6894, 0.4741,
+                    4345.0, 0.5999, 0.5546,
+                    4410.0, 0.5942, 0.5601,
+                    4486.0, 0.5878, 0.5661,
+                    4576.0, 0.5779, 0.5756,
+                    5672.0, 0.5211, 0.6318,
+                    5710.0, 0.5168, 0.6362,
+                    6850.0, 0.4841, 0.6702
                 ],
                 "sensitivity_r": 1.0,
                 "sensitivity_b": 1.0,
-                "transverse_pos": 0.0144,
-                "transverse_neg": 0.01
+                "transverse_pos": 0.02601,
+                "transverse_neg": 0.0246
             }
         },
         {
@@ -209,7 +225,136 @@
             {
                 "omega": 1.3,
                 "n_iter": 100,
-                "luminance_strength": 0.7
+                "luminance_strength": 0.8,
+                "calibrations_Cr": [
+                    {
+                        "ct": 2940,
+                        "table":
+                        [
+                            1.021, 1.026, 1.028, 1.029, 1.031, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.028, 1.027, 1.022, 1.013, 1.008,
+                            1.022, 1.026, 1.027, 1.028, 1.027, 1.026, 1.026, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.022, 1.014, 1.009,
+                            1.023, 1.026, 1.026, 1.027, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.026, 1.027, 1.026, 1.023, 1.017, 1.012,
+                            1.024, 1.026, 1.026, 1.026, 1.025, 1.024, 1.024, 1.023, 1.023, 1.024, 1.025, 1.026, 1.026, 1.024, 1.018, 1.013,
+                            1.024, 1.026, 1.026, 1.026, 1.025, 1.024, 1.023, 1.023, 1.023, 1.023, 1.024, 1.026, 1.026, 1.025, 1.019, 1.013,
+                            1.025, 1.026, 1.026, 1.026, 1.025, 1.024, 1.023, 1.023, 1.023, 1.023, 1.024, 1.026, 1.026, 1.025, 1.018, 1.013,
+                            1.025, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.023, 1.023, 1.024, 1.024, 1.026, 1.026, 1.024, 1.018, 1.013,
+                            1.025, 1.027, 1.028, 1.028, 1.027, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.024, 1.017, 1.012,
+                            1.024, 1.027, 1.029, 1.029, 1.028, 1.027, 1.026, 1.026, 1.025, 1.025, 1.026, 1.026, 1.025, 1.022, 1.014, 1.009,
+                            1.024, 1.027, 1.029, 1.031, 1.031, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.026, 1.025, 1.021, 1.011, 1.007,
+                            1.022, 1.026, 1.031, 1.031, 1.031, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.026, 1.022, 1.017, 1.007, 1.003,
+                            1.019, 1.024, 1.029, 1.031, 1.032, 1.032, 1.032, 1.031, 1.029, 1.029, 1.027, 1.024, 1.019, 1.013, 1.003, 1.001
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.027, 1.035, 1.039, 1.041, 1.043, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.041, 1.041, 1.034, 1.021, 1.014,
+                            1.029, 1.035, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.035, 1.024, 1.017,
+                            1.029, 1.034, 1.036, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.036, 1.027, 1.021,
+                            1.031, 1.034, 1.036, 1.036, 1.037, 1.037, 1.038, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.037, 1.029, 1.021,
+                            1.031, 1.034, 1.035, 1.036, 1.037, 1.037, 1.037, 1.037, 1.037, 1.037, 1.038, 1.038, 1.038, 1.037, 1.029, 1.022,
+                            1.031, 1.034, 1.035, 1.036, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.038, 1.038, 1.037, 1.029, 1.022,
+                            1.031, 1.035, 1.036, 1.037, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.038, 1.038, 1.036, 1.028, 1.021,
+                            1.031, 1.034, 1.036, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.036, 1.036, 1.037, 1.037, 1.035, 1.026, 1.019,
+                            1.028, 1.034, 1.037, 1.037, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.037, 1.037, 1.033, 1.022, 1.016,
+                            1.028, 1.034, 1.037, 1.038, 1.039, 1.038, 1.037, 1.037, 1.037, 1.037, 1.037, 1.037, 1.035, 1.031, 1.017, 1.011,
+                            1.025, 1.031, 1.036, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.036, 1.031, 1.024, 1.011, 1.006,
+                            1.021, 1.028, 1.034, 1.037, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.036, 1.033, 1.027, 1.019, 1.006, 1.001
+                        ]
+                    },
+                    {
+                        "ct": 6000,
+                        "table":
+                        [
+                            1.026, 1.037, 1.048, 1.054, 1.057, 1.058, 1.059, 1.059, 1.061, 1.059, 1.059, 1.056, 1.049, 1.038, 1.019, 1.013,
+                            1.031, 1.039, 1.049, 1.054, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.056, 1.051, 1.042, 1.026, 1.018,
+                            1.033, 1.044, 1.051, 1.054, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.058, 1.058, 1.055, 1.046, 1.031, 1.023,
+                            1.035, 1.045, 1.051, 1.055, 1.057, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.056, 1.049, 1.035, 1.026,
+                            1.037, 1.046, 1.052, 1.055, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.051, 1.037, 1.027,
+                            1.037, 1.047, 1.053, 1.056, 1.059, 1.059, 1.061, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.051, 1.037, 1.027,
+                            1.037, 1.047, 1.053, 1.057, 1.059, 1.059, 1.061, 1.061, 1.059, 1.059, 1.059, 1.058, 1.056, 1.049, 1.036, 1.026,
+                            1.037, 1.047, 1.054, 1.057, 1.059, 1.059, 1.061, 1.061, 1.059, 1.059, 1.059, 1.058, 1.056, 1.048, 1.034, 1.025,
+                            1.034, 1.045, 1.054, 1.057, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.053, 1.045, 1.029, 1.021,
+                            1.032, 1.043, 1.052, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.055, 1.049, 1.041, 1.022, 1.013,
+                            1.028, 1.037, 1.048, 1.053, 1.057, 1.059, 1.059, 1.059, 1.059, 1.058, 1.056, 1.051, 1.044, 1.032, 1.013, 1.007,
+                            1.021, 1.033, 1.044, 1.051, 1.055, 1.058, 1.059, 1.059, 1.058, 1.057, 1.052, 1.047, 1.039, 1.026, 1.007, 1.001
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 2940,
+                        "table":
+                        [
+                            1.002, 1.012, 1.031, 1.042, 1.051, 1.056, 1.058, 1.058, 1.058, 1.058, 1.057, 1.055, 1.045, 1.033, 1.017, 1.016,
+                            1.011, 1.026, 1.041, 1.048, 1.056, 1.063, 1.066, 1.067, 1.067, 1.066, 1.064, 1.061, 1.051, 1.045, 1.028, 1.017,
+                            1.016, 1.033, 1.047, 1.056, 1.063, 1.067, 1.071, 1.072, 1.072, 1.071, 1.068, 1.064, 1.061, 1.051, 1.033, 1.024,
+                            1.021, 1.038, 1.051, 1.061, 1.067, 1.071, 1.073, 1.075, 1.075, 1.074, 1.071, 1.068, 1.063, 1.054, 1.036, 1.025,
+                            1.023, 1.041, 1.054, 1.063, 1.069, 1.073, 1.075, 1.077, 1.077, 1.076, 1.074, 1.069, 1.064, 1.055, 1.038, 1.027,
+                            1.023, 1.043, 1.055, 1.063, 1.069, 1.074, 1.076, 1.078, 1.078, 1.077, 1.075, 1.071, 1.065, 1.056, 1.039, 1.028,
+                            1.023, 1.043, 1.055, 1.063, 1.069, 1.074, 1.076, 1.077, 1.078, 1.076, 1.074, 1.071, 1.065, 1.056, 1.039, 1.028,
+                            1.023, 1.041, 1.052, 1.062, 1.068, 1.072, 1.074, 1.076, 1.076, 1.075, 1.073, 1.069, 1.064, 1.055, 1.038, 1.028,
+                            1.021, 1.038, 1.051, 1.059, 1.066, 1.069, 1.072, 1.074, 1.074, 1.073, 1.069, 1.067, 1.062, 1.052, 1.036, 1.027,
+                            1.018, 1.032, 1.046, 1.055, 1.061, 1.066, 1.069, 1.069, 1.069, 1.069, 1.067, 1.062, 1.057, 1.047, 1.031, 1.021,
+                            1.011, 1.023, 1.039, 1.049, 1.056, 1.061, 1.062, 1.064, 1.065, 1.064, 1.062, 1.058, 1.049, 1.038, 1.021, 1.016,
+                            1.001, 1.019, 1.035, 1.046, 1.053, 1.058, 1.061, 1.062, 1.062, 1.062, 1.059, 1.053, 1.043, 1.033, 1.016, 1.011
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.001, 1.003, 1.011, 1.016, 1.019, 1.019, 1.021, 1.021, 1.019, 1.019, 1.019, 1.017, 1.017, 1.013, 1.007, 1.006,
+                            1.003, 1.011, 1.015, 1.021, 1.024, 1.026, 1.027, 1.027, 1.027, 1.026, 1.025, 1.023, 1.022, 1.016, 1.012, 1.007,
+                            1.007, 1.015, 1.021, 1.024, 1.027, 1.029, 1.031, 1.031, 1.031, 1.029, 1.028, 1.026, 1.024, 1.022, 1.015, 1.011,
+                            1.011, 1.017, 1.023, 1.027, 1.029, 1.032, 1.033, 1.033, 1.033, 1.033, 1.031, 1.028, 1.026, 1.024, 1.016, 1.011,
+                            1.012, 1.019, 1.025, 1.029, 1.032, 1.033, 1.034, 1.035, 1.035, 1.034, 1.033, 1.031, 1.028, 1.025, 1.018, 1.014,
+                            1.013, 1.021, 1.026, 1.031, 1.033, 1.034, 1.036, 1.036, 1.036, 1.035, 1.034, 1.032, 1.029, 1.026, 1.019, 1.015,
+                            1.013, 1.021, 1.026, 1.031, 1.033, 1.035, 1.036, 1.037, 1.037, 1.036, 1.034, 1.032, 1.029, 1.027, 1.019, 1.016,
+                            1.013, 1.021, 1.026, 1.031, 1.033, 1.035, 1.036, 1.036, 1.036, 1.036, 1.035, 1.033, 1.031, 1.027, 1.021, 1.016,
+                            1.013, 1.021, 1.025, 1.029, 1.032, 1.034, 1.035, 1.035, 1.036, 1.035, 1.034, 1.032, 1.031, 1.027, 1.021, 1.015,
+                            1.012, 1.019, 1.024, 1.027, 1.029, 1.032, 1.034, 1.034, 1.034, 1.034, 1.032, 1.031, 1.029, 1.026, 1.019, 1.015,
+                            1.009, 1.015, 1.022, 1.025, 1.028, 1.029, 1.031, 1.032, 1.032, 1.031, 1.031, 1.029, 1.026, 1.023, 1.017, 1.015,
+                            1.005, 1.014, 1.021, 1.025, 1.027, 1.029, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.024, 1.021, 1.016, 1.015
+                        ]
+                    },
+                    {
+                        "ct": 6000,
+                        "table":
+                        [
+                            1.001, 1.001, 1.006, 1.007, 1.008, 1.009, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.011, 1.011, 1.009, 1.008,
+                            1.001, 1.005, 1.008, 1.011, 1.012, 1.013, 1.014, 1.014, 1.014, 1.013, 1.013, 1.014, 1.014, 1.012, 1.011, 1.009,
+                            1.004, 1.008, 1.011, 1.012, 1.014, 1.016, 1.016, 1.016, 1.016, 1.016, 1.015, 1.015, 1.015, 1.014, 1.012, 1.011,
+                            1.005, 1.009, 1.012, 1.014, 1.016, 1.017, 1.018, 1.018, 1.018, 1.018, 1.017, 1.016, 1.016, 1.015, 1.012, 1.011,
+                            1.006, 1.011, 1.013, 1.015, 1.017, 1.018, 1.018, 1.019, 1.019, 1.019, 1.018, 1.017, 1.016, 1.015, 1.012, 1.011,
+                            1.007, 1.011, 1.013, 1.015, 1.017, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.017, 1.016, 1.013, 1.011,
+                            1.007, 1.012, 1.013, 1.015, 1.017, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.018, 1.017, 1.014, 1.013,
+                            1.007, 1.012, 1.013, 1.015, 1.016, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.018, 1.017, 1.015, 1.014,
+                            1.007, 1.011, 1.012, 1.014, 1.016, 1.017, 1.018, 1.018, 1.019, 1.019, 1.019, 1.018, 1.018, 1.018, 1.016, 1.015,
+                            1.007, 1.011, 1.012, 1.013, 1.015, 1.016, 1.017, 1.017, 1.018, 1.018, 1.018, 1.018, 1.018, 1.017, 1.016, 1.015,
+                            1.006, 1.009, 1.012, 1.013, 1.014, 1.015, 1.015, 1.016, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.016,
+                            1.005, 1.009, 1.012, 1.013, 1.015, 1.015, 1.015, 1.015, 1.016, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    1.223, 1.187, 1.129, 1.085, 1.061, 1.049, 1.046, 1.046, 1.046, 1.051, 1.061, 1.089, 1.134, 1.212, 1.359, 1.367,
+                    1.188, 1.141, 1.098, 1.065, 1.048, 1.037, 1.029, 1.029, 1.034, 1.036, 1.046, 1.066, 1.095, 1.158, 1.269, 1.359,
+                    1.158, 1.109, 1.073, 1.049, 1.035, 1.025, 1.019, 1.016, 1.017, 1.022, 1.033, 1.047, 1.072, 1.127, 1.219, 1.269,
+                    1.147, 1.092, 1.058, 1.039, 1.026, 1.017, 1.011, 1.007, 1.009, 1.015, 1.022, 1.035, 1.058, 1.107, 1.191, 1.236,
+                    1.144, 1.082, 1.051, 1.033, 1.021, 1.011, 1.005, 1.002, 1.004, 1.009, 1.017, 1.031, 1.051, 1.097, 1.177, 1.232,
+                    1.144, 1.081, 1.049, 1.031, 1.018, 1.008, 1.002, 1.001, 1.001, 1.006, 1.015, 1.029, 1.048, 1.096, 1.177, 1.232,
+                    1.144, 1.084, 1.051, 1.032, 1.018, 1.009, 1.004, 1.001, 1.002, 1.009, 1.016, 1.029, 1.051, 1.098, 1.183, 1.232,
+                    1.149, 1.096, 1.057, 1.037, 1.022, 1.014, 1.008, 1.005, 1.007, 1.012, 1.019, 1.033, 1.059, 1.113, 1.205, 1.248,
+                    1.166, 1.117, 1.071, 1.046, 1.031, 1.021, 1.014, 1.012, 1.014, 1.019, 1.029, 1.045, 1.078, 1.141, 1.247, 1.314,
+                    1.202, 1.151, 1.096, 1.061, 1.044, 1.031, 1.023, 1.021, 1.022, 1.029, 1.044, 1.067, 1.109, 1.182, 1.314, 1.424,
+                    1.242, 1.202, 1.134, 1.088, 1.061, 1.045, 1.038, 1.036, 1.039, 1.048, 1.066, 1.103, 1.157, 1.248, 1.424, 1.532,
+                    1.318, 1.238, 1.162, 1.111, 1.078, 1.059, 1.048, 1.048, 1.049, 1.063, 1.089, 1.133, 1.189, 1.296, 1.532, 1.606
+                ],
+                "sigma": 0.00175,
+                "sigma_Cb": 0.00268
             }
         },
         {
@@ -259,48 +404,138 @@
             {
                 "ccms": [
                     {
-                        "ct": 2213,
+                        "ct": 2500,
+                        "ccm":
+                        [
+                            1.82257, -0.40941, -0.41316,
+                            -0.52091, 1.83005, -0.30915,
+                            0.22503, -1.41259, 2.18757
+                        ]
+                    },
+                    {
+                        "ct": 2820,
+                        "ccm":
+                        [
+                            1.80564, -0.47587, -0.32977,
+                            -0.47385, 1.83075, -0.35691,
+                            0.21369, -1.22609, 2.01239
+                        ]
+                    },
+                    {
+                        "ct": 2830,
+                        "ccm":
+                        [
+                            1.80057, -0.51479, -0.28578,
+                            -0.64031, 2.16074, -0.52044,
+                            0.11794, -0.95667, 1.83873
+                        ]
+                    },
+                    {
+                        "ct": 2885,
+                        "ccm":
+                        [
+                            1.78452, -0.49769, -0.28683,
+                            -0.63651, 2.13634, -0.49983,
+                            0.08547, -0.86501, 1.77954
+                        ]
+                    },
+                    {
+                        "ct": 3601,
+                        "ccm":
+                        [
+                            1.85165, -0.57008, -0.28156,
+                            -0.56249, 2.08321, -0.52072,
+                            0.03724, -0.70964, 1.67239
+                        ]
+                    },
+                    {
+                        "ct": 3615,
+                        "ccm":
+                        [
+                            1.87611, -0.60772, -0.26839,
+                            -0.55497, 2.07257, -0.51761,
+                            0.04151, -0.70635, 1.66485
+                        ]
+                    },
+                    {
+                        "ct": 3622,
+                        "ccm":
+                        [
+                            1.85505, -0.58542, -0.26963,
+                            -0.55053, 2.05981, -0.50928,
+                            0.04005, -0.69302, 1.65297
+                        ]
+                    },
+                    {
+                        "ct": 4345,
+                        "ccm":
+                        [
+                            1.81872, -0.57511, -0.24361,
+                            -0.49071, 2.16621, -0.67551,
+                            0.02641, -0.67838, 1.65196
+                        ]
+                    },
+                    {
+                        "ct": 4410,
+                        "ccm":
+                        [
+                            1.83689, -0.60178, -0.23512,
+                            -0.48204, 2.14729, -0.66525,
+                            0.02773, -0.67615, 1.64841
+                        ]
+                    },
+                    {
+                        "ct": 4486,
+                        "ccm":
+                        [
+                            1.85101, -0.60733, -0.24368,
+                            -0.47635, 2.13101, -0.65465,
+                            0.02229, -0.66412, 1.64183
+                        ]
+                    },
+                    {
+                        "ct": 4576,
                         "ccm":
                         [
-                            1.91264, -0.27609, -0.63655,
-                            -0.65708, 2.11718, -0.46009,
-                            0.03629, -1.38441, 2.34811
+                            1.84076, -0.59449, -0.24626,
+                            -0.47307, 2.13369, -0.66062,
+                            0.01984, -0.65788, 1.63804
                         ]
                     },
                     {
-                        "ct": 2255,
+                        "ct": 5657,
                         "ccm":
                         [
-                            1.90369, -0.29309, -0.61059,
-                            -0.64693, 2.08169, -0.43476,
-                            0.04086, -1.29999, 2.25914
+                            1.84536, -0.57827, -0.26709,
+                            -0.44532, 2.04086, -0.59554,
+                            -0.01738, -0.52806, 1.54544
                         ]
                     },
                     {
-                        "ct": 2259,
+                        "ct": 5672,
                         "ccm":
                         [
-                            1.92762, -0.35134, -0.57628,
-                            -0.63523, 2.08481, -0.44958,
-                            0.06754, -1.32953, 2.26199
+                            1.84251, -0.57486, -0.26765,
+                            -0.44925, 2.04615, -0.59689,
+                            -0.03179, -0.51748, 1.54928
                         ]
                     },
                     {
-                        "ct": 5313,
+                        "ct": 5710,
                         "ccm":
                         [
-                            1.75924, -0.54053, -0.21871,
-                            -0.38159, 1.88671, -0.50511,
-                            -0.00747, -0.53492, 1.54239
+                            1.84081, -0.58127, -0.25953,
+                            -0.44169, 2.03593, -0.59424,
+                            -0.02503, -0.52696, 1.55199
                         ]
                     },
                     {
-                        "ct": 6237,
+                        "ct": 6850,
                         "ccm":
                         [
-                            2.19299, -0.74764, -0.44536,
-                            -0.51678, 2.27651, -0.75972,
-                            -0.06498, -0.74269, 1.80767
+                            1.80426, -0.22567, -0.57859,
+                            -0.48629, 2.49024, -1.00395,
+                            -0.10865, -0.63841, 1.74705
                         ]
                     }
                 ]
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/imx708.json 0.5.2-2/src/ipa/rpi/vc4/data/imx708.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/imx708.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/imx708.json	2025-08-07 13:46:17.000000000 +0000
@@ -638,11 +638,27 @@
                         "step_coarse": 1.0,
                         "step_fine": 0.25,
                         "contrast_ratio": 0.75,
-                        "pdaf_gain": -0.02,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
+                        "pdaf_gain": -0.016,
                         "pdaf_squelch": 0.125,
-                        "max_slew": 2.0,
+                        "max_slew": 1.5,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
+                        "step_frames": 5
+                    },
+                    "fast":
+                    {
+                        "step_coarse": 1.25,
+                        "step_fine": 0.0,
+                        "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
+                        "pdaf_gain": -0.02,
+                        "pdaf_squelch": 0.125,
+                        "max_slew": 2.0,
+                        "pdaf_frames": 16,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -650,6 +666,7 @@
                 "conf_thresh": 16,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": false,
                 "map": [ 0.0, 445, 15.0, 925 ]
             }
         },
@@ -668,4 +685,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/imx708_noir.json 0.5.2-2/src/ipa/rpi/vc4/data/imx708_noir.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/imx708_noir.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/imx708_noir.json	2025-08-07 13:46:17.000000000 +0000
@@ -737,11 +737,27 @@
                         "step_coarse": 1.0,
                         "step_fine": 0.25,
                         "contrast_ratio": 0.75,
-                        "pdaf_gain": -0.02,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
+                        "pdaf_gain": -0.016,
                         "pdaf_squelch": 0.125,
-                        "max_slew": 2.0,
+                        "max_slew": 1.5,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
+                        "step_frames": 5
+                    },
+                    "fast":
+                    {
+                        "step_coarse": 1.25,
+                        "step_fine": 0.0,
+                        "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
+                        "pdaf_gain": -0.02,
+                        "pdaf_squelch": 0.125,
+                        "max_slew": 2.0,
+                        "pdaf_frames": 16,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -749,6 +765,7 @@
                 "conf_thresh": 16,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": true,
                 "map": [ 0.0, 445, 15.0, 925 ]
             }
         },
@@ -767,4 +784,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/imx708_wide.json 0.5.2-2/src/ipa/rpi/vc4/data/imx708_wide.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/imx708_wide.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/imx708_wide.json	2025-08-07 13:46:17.000000000 +0000
@@ -637,23 +637,27 @@
                         "step_coarse": 2.0,
                         "step_fine": 0.5,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
                         "pdaf_gain": -0.03,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 4.0,
+                        "max_slew": 3.0,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
-                        "step_frames": 4
+                        "step_frames": 5
                     },
                     "fast":
                     {
-                        "step_coarse": 2.0,
-                        "step_fine": 0.5,
+                        "step_coarse": 2.5,
+                        "step_fine": 0.0,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
                         "pdaf_gain": -0.05,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 5.0,
+                        "max_slew": 4.0,
                         "pdaf_frames": 16,
-                        "dropout_frames": 6,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -661,6 +665,7 @@
                 "conf_thresh": 12,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": false,
                 "map": [ 0.0, 420, 35.0, 920 ]
             }
         },
@@ -679,4 +684,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/imx708_wide_noir.json 0.5.2-2/src/ipa/rpi/vc4/data/imx708_wide_noir.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/imx708_wide_noir.json	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/imx708_wide_noir.json	2025-08-07 13:46:17.000000000 +0000
@@ -628,23 +628,27 @@
                         "step_coarse": 2.0,
                         "step_fine": 0.5,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 10,
                         "pdaf_gain": -0.03,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 4.0,
+                        "max_slew": 3.0,
                         "pdaf_frames": 20,
                         "dropout_frames": 6,
-                        "step_frames": 4
+                        "step_frames": 5
                     },
                     "fast":
                     {
-                        "step_coarse": 2.0,
-                        "step_fine": 0.5,
+                        "step_coarse": 2.5,
+                        "step_fine": 0.0,
                         "contrast_ratio": 0.75,
+                        "retrigger_ratio": 0.8,
+                        "retrigger_delay": 8,
                         "pdaf_gain": -0.05,
                         "pdaf_squelch": 0.2,
-                        "max_slew": 5.0,
+                        "max_slew": 4.0,
                         "pdaf_frames": 16,
-                        "dropout_frames": 6,
+                        "dropout_frames": 4,
                         "step_frames": 4
                     }
                 },
@@ -652,6 +656,7 @@
                 "conf_thresh": 12,
                 "conf_clip": 512,
                 "skip_frames": 5,
+                "check_for_ir": true,
                 "map": [ 0.0, 420, 35.0, 920 ]
             }
         },
@@ -670,4 +675,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/meson.build 0.5.2-2/src/ipa/rpi/vc4/data/meson.build
--- 0.5.0-1/src/ipa/rpi/vc4/data/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -26,6 +26,8 @@ conf_files = files([
     'ov9281_mono.json',
     'se327m12.json',
     'uncalibrated.json',
+    'vd56g3.json',
+    'vd56g3_mono.json',
 ])
 
 install_data(conf_files,
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/vd56g3.json 0.5.2-2/src/ipa/rpi/vc4/data/vd56g3.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/vd56g3.json	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/vd56g3.json	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,417 @@
+{
+    "version": 2.0,
+    "target": "bcm2835",
+    "algorithms": [
+        {
+            "rpi.black_level":
+            {
+                "black_level": 4096
+            }
+        },
+        {
+            "rpi.lux":
+            {
+                "reference_shutter_speed": 17247,
+                "reference_gain": 1.0,
+                "reference_aperture": 1.0,
+                "reference_lux": 1090,
+                "reference_Y": 24531
+            }
+        },
+        {
+            "rpi.noise":
+            {
+                "reference_constant": 0,
+                "reference_slope": 3.289
+            }
+        },
+        {
+            "rpi.geq":
+            {
+                "offset": 762,
+                "slope": 0.02394
+            }
+        },
+        {
+            "rpi.sdn": { }
+        },
+        {
+            "rpi.awb":
+            {
+                "priors": [
+                    {
+                        "lux": 0,
+                        "prior":
+                        [
+                            2000, 1.0,
+                            3000, 0.0,
+                            13000, 0.0
+                        ]
+                    },
+                    {
+                        "lux": 800,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            6000, 2.0,
+                            13000, 2.0
+                        ]
+                    },
+                    {
+                        "lux": 1500,
+                        "prior":
+                        [
+                            2000, 0.0,
+                            4000, 1.0,
+                            6000, 6.0,
+                            6500, 7.0,
+                            7000, 1.0,
+                            13000, 1.0
+                        ]
+                    }
+                ],
+                "modes":
+                {
+                    "auto":
+                    {
+                        "lo": 2500,
+                        "hi": 8000
+                    },
+                    "incandescent":
+                    {
+                        "lo": 2500,
+                        "hi": 3000
+                    },
+                    "tungsten":
+                    {
+                        "lo": 3000,
+                        "hi": 3500
+                    },
+                    "fluorescent":
+                    {
+                        "lo": 4000,
+                        "hi": 4700
+                    },
+                    "indoor":
+                    {
+                        "lo": 3000,
+                        "hi": 5000
+                    },
+                    "daylight":
+                    {
+                        "lo": 5500,
+                        "hi": 6500
+                    },
+                    "cloudy":
+                    {
+                        "lo": 7000,
+                        "hi": 8600
+                    }
+                },
+                "bayes": 1,
+                "ct_curve":
+                [
+                    3000.0, 1.1702, 0.4424,
+                    4000.0, 0.9551, 0.5528,
+                    6500.0, 0.7007, 0.7375
+                ],
+                "sensitivity_r": 1.0,
+                "sensitivity_b": 1.0,
+                "transverse_pos": 0.01,
+                "transverse_neg": 0.01
+            }
+        },
+        {
+            "rpi.agc":
+            {
+                "metering_modes":
+                {
+                    "centre-weighted":
+                    {
+                        "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+                    },
+                    "spot":
+                    {
+                        "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
+                    },
+                    "matrix":
+                    {
+                        "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
+                    }
+                },
+                "exposure_modes":
+                {
+                    "normal":
+                    {
+                        "shutter": [ 100, 10000, 30000, 60000, 120000 ],
+                        "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+                    },
+                    "short":
+                    {
+                        "shutter": [ 100, 5000, 10000, 20000, 120000 ],
+                        "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+                    }
+                },
+                "constraint_modes":
+                {
+                    "normal": [
+                        {
+                            "bound": "LOWER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.5,
+                                1000, 0.5
+                            ]
+                        }
+                    ],
+                    "highlight": [
+                        {
+                            "bound": "LOWER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.5,
+                                1000, 0.5
+                            ]
+                        },
+                        {
+                            "bound": "UPPER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.8,
+                                1000, 0.8
+                            ]
+                        }
+                    ]
+                },
+                "y_target":
+                [
+                    0, 0.16,
+                    1000, 0.165,
+                    10000, 0.17
+                ]
+            }
+        },
+        {
+            "rpi.alsc":
+            {
+                "omega": 1.3,
+                "n_iter": 100,
+                "luminance_strength": 0.5,
+                "calibrations_Cr": [
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            0.792, 0.798, 0.799, 0.803, 0.806, 0.808, 0.809, 0.809, 0.807, 0.803, 0.801, 0.798, 0.796, 0.795, 0.794, 0.792,
+                            0.801, 0.801, 0.802, 0.809, 0.812, 0.816, 0.818, 0.818, 0.818, 0.816, 0.814, 0.813, 0.811, 0.809, 0.806, 0.803,
+                            0.806, 0.806, 0.809, 0.813, 0.817, 0.823, 0.824, 0.826, 0.826, 0.825, 0.822, 0.819, 0.818, 0.817, 0.816, 0.814,
+                            0.808, 0.809, 0.813, 0.817, 0.823, 0.825, 0.828, 0.831, 0.831, 0.829, 0.827, 0.825, 0.824, 0.825, 0.825, 0.825,
+                            0.809, 0.811, 0.815, 0.821, 0.825, 0.828, 0.831, 0.832, 0.832, 0.832, 0.831, 0.831, 0.831, 0.832, 0.834, 0.835,
+                            0.809, 0.811, 0.816, 0.821, 0.826, 0.829, 0.832, 0.832, 0.835, 0.834, 0.833, 0.832, 0.833, 0.834, 0.836, 0.837,
+                            0.809, 0.809, 0.816, 0.821, 0.826, 0.829, 0.832, 0.834, 0.835, 0.834, 0.833, 0.832, 0.833, 0.835, 0.836, 0.836,
+                            0.804, 0.805, 0.811, 0.817, 0.821, 0.826, 0.829, 0.832, 0.833, 0.832, 0.831, 0.831, 0.831, 0.832, 0.834, 0.833,
+                            0.796, 0.797, 0.804, 0.811, 0.817, 0.821, 0.824, 0.829, 0.829, 0.827, 0.827, 0.826, 0.827, 0.829, 0.829, 0.829,
+                            0.788, 0.788, 0.796, 0.803, 0.809, 0.814, 0.817, 0.821, 0.821, 0.821, 0.819, 0.819, 0.821, 0.825, 0.827, 0.827,
+                            0.775, 0.777, 0.784, 0.791, 0.797, 0.802, 0.806, 0.811, 0.813, 0.813, 0.812, 0.813, 0.816, 0.818, 0.819, 0.819,
+                            0.767, 0.774, 0.777, 0.784, 0.791, 0.797, 0.802, 0.806, 0.809, 0.811, 0.811, 0.812, 0.812, 0.816, 0.816, 0.816
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.009, 1.013, 1.018, 1.027, 1.034, 1.038, 1.038, 1.038, 1.037, 1.032, 1.026, 1.018, 1.011, 1.005, 0.999, 0.991,
+                            1.012, 1.015, 1.023, 1.032, 1.041, 1.045, 1.051, 1.052, 1.052, 1.047, 1.042, 1.035, 1.029, 1.023, 1.016, 1.006,
+                            1.015, 1.018, 1.027, 1.037, 1.044, 1.051, 1.056, 1.058, 1.058, 1.055, 1.051, 1.044, 1.041, 1.036, 1.032, 1.028,
+                            1.014, 1.021, 1.029, 1.037, 1.045, 1.052, 1.057, 1.061, 1.061, 1.058, 1.054, 1.051, 1.049, 1.047, 1.045, 1.042,
+                            1.012, 1.018, 1.029, 1.039, 1.046, 1.053, 1.056, 1.059, 1.061, 1.059, 1.058, 1.055, 1.055, 1.054, 1.055, 1.055,
+                            1.011, 1.014, 1.027, 1.036, 1.046, 1.052, 1.057, 1.059, 1.061, 1.061, 1.059, 1.057, 1.057, 1.058, 1.058, 1.058,
+                            1.006, 1.011, 1.024, 1.035, 1.044, 1.051, 1.056, 1.058, 1.061, 1.061, 1.058, 1.057, 1.057, 1.057, 1.058, 1.058,
+                            0.998, 1.006, 1.017, 1.028, 1.038, 1.046, 1.051, 1.056, 1.058, 1.057, 1.055, 1.055, 1.055, 1.056, 1.056, 1.054,
+                            0.988, 0.994, 1.006, 1.018, 1.028, 1.038, 1.046, 1.051, 1.051, 1.051, 1.049, 1.048, 1.049, 1.049, 1.051, 1.051,
+                            0.972, 0.979, 0.993, 1.006, 1.018, 1.027, 1.033, 1.039, 1.041, 1.041, 1.039, 1.039, 1.041, 1.044, 1.046, 1.046,
+                            0.954, 0.962, 0.977, 0.989, 1.001, 1.011, 1.018, 1.024, 1.029, 1.032, 1.032, 1.032, 1.033, 1.035, 1.038, 1.039,
+                            0.944, 0.954, 0.962, 0.977, 0.989, 1.001, 1.011, 1.018, 1.024, 1.024, 1.026, 1.027, 1.029, 1.029, 1.029, 1.029
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.288, 1.292, 1.304, 1.324, 1.339, 1.348, 1.349, 1.348, 1.347, 1.339, 1.326, 1.314, 1.302, 1.287, 1.274, 1.261,
+                            1.292, 1.304, 1.322, 1.334, 1.349, 1.359, 1.368, 1.368, 1.364, 1.361, 1.352, 1.337, 1.327, 1.315, 1.303, 1.289,
+                            1.305, 1.312, 1.332, 1.348, 1.359, 1.371, 1.379, 1.385, 1.385, 1.379, 1.373, 1.363, 1.354, 1.347, 1.335, 1.327,
+                            1.312, 1.321, 1.337, 1.357, 1.369, 1.379, 1.387, 1.396, 1.397, 1.395, 1.389, 1.381, 1.376, 1.373, 1.371, 1.365,
+                            1.319, 1.328, 1.343, 1.361, 1.377, 1.387, 1.396, 1.399, 1.403, 1.403, 1.399, 1.395, 1.394, 1.394, 1.395, 1.397,
+                            1.319, 1.328, 1.345, 1.365, 1.379, 1.391, 1.399, 1.403, 1.411, 1.411, 1.409, 1.406, 1.406, 1.408, 1.411, 1.409,
+                            1.317, 1.326, 1.345, 1.365, 1.379, 1.391, 1.401, 1.408, 1.411, 1.412, 1.411, 1.409, 1.409, 1.413, 1.416, 1.419,
+                            1.308, 1.319, 1.339, 1.359, 1.375, 1.389, 1.399, 1.405, 1.409, 1.411, 1.409, 1.409, 1.409, 1.415, 1.418, 1.419,
+                            1.291, 1.308, 1.328, 1.351, 1.366, 1.381, 1.391, 1.401, 1.405, 1.405, 1.405, 1.406, 1.408, 1.412, 1.417, 1.419,
+                            1.272, 1.291, 1.311, 1.331, 1.353, 1.369, 1.381, 1.391, 1.396, 1.397, 1.398, 1.402, 1.404, 1.411, 1.415, 1.417,
+                            1.247, 1.272, 1.291, 1.316, 1.338, 1.354, 1.369, 1.381, 1.388, 1.391, 1.394, 1.395, 1.399, 1.404, 1.411, 1.415,
+                            1.247, 1.247, 1.274, 1.296, 1.319, 1.341, 1.355, 1.369, 1.381, 1.389, 1.389, 1.394, 1.395, 1.401, 1.405, 1.406
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            2.614, 2.679, 2.791, 2.891, 2.984, 3.058, 3.058, 3.058, 3.056, 3.005, 2.933, 2.848, 2.764, 2.672, 2.575, 2.484,
+                            2.659, 2.754, 2.869, 2.975, 3.053, 3.142, 3.178, 3.178, 3.159, 3.106, 3.037, 2.963, 2.874, 2.784, 2.686, 2.605,
+                            2.687, 2.805, 2.933, 3.053, 3.142, 3.192, 3.267, 3.267, 3.264, 3.231, 3.179, 3.116, 3.042, 2.963, 2.878, 2.808,
+                            2.718, 2.847, 2.969, 3.091, 3.192, 3.267, 3.295, 3.341, 3.341, 3.331, 3.278, 3.238, 3.187, 3.127, 3.064, 3.012,
+                            2.722, 2.877, 2.993, 3.117, 3.216, 3.295, 3.341, 3.369, 3.386, 3.381, 3.352, 3.324, 3.281, 3.249, 3.207, 3.157,
+                            2.722, 2.877, 3.007, 3.128, 3.237, 3.314, 3.369, 3.396, 3.417, 3.401, 3.381, 3.358, 3.331, 3.303, 3.278, 3.255,
+                            2.722, 2.872, 3.007, 3.128, 3.237, 3.316, 3.378, 3.416, 3.419, 3.418, 3.396, 3.374, 3.357, 3.329, 3.309, 3.295,
+                            2.708, 2.855, 2.989, 3.117, 3.225, 3.312, 3.378, 3.401, 3.418, 3.403, 3.395, 3.374, 3.358, 3.335, 3.309, 3.301,
+                            2.686, 2.807, 2.941, 3.083, 3.193, 3.281, 3.333, 3.378, 3.395, 3.395, 3.381, 3.362, 3.345, 3.323, 3.305, 3.288,
+                            2.636, 2.731, 2.877, 3.011, 3.129, 3.221, 3.281, 3.333, 3.352, 3.352, 3.333, 3.323, 3.301, 3.295, 3.272, 3.253,
+                            2.547, 2.651, 2.781, 2.931, 3.039, 3.129, 3.221, 3.271, 3.284, 3.284, 3.275, 3.261, 3.255, 3.229, 3.199, 3.185,
+                            2.483, 2.547, 2.671, 2.809, 2.942, 3.041, 3.138, 3.191, 3.207, 3.207, 3.207, 3.192, 3.172, 3.156, 3.149, 3.118
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            2.129, 2.159, 2.221, 2.281, 2.333, 2.368, 2.368, 2.368, 2.358, 2.331, 2.293, 2.243, 2.193, 2.144, 2.087, 2.036,
+                            2.151, 2.211, 2.274, 2.326, 2.372, 2.416, 2.435, 2.435, 2.419, 2.391, 2.356, 2.313, 2.267, 2.217, 2.166, 2.113,
+                            2.183, 2.242, 2.306, 2.368, 2.423, 2.456, 2.481, 2.487, 2.487, 2.471, 2.441, 2.406, 2.373, 2.329, 2.286, 2.248,
+                            2.201, 2.273, 2.338, 2.404, 2.456, 2.492, 2.519, 2.537, 2.537, 2.529, 2.508, 2.484, 2.456, 2.429, 2.396, 2.371,
+                            2.206, 2.291, 2.359, 2.425, 2.481, 2.518, 2.547, 2.561, 2.566, 2.564, 2.551, 2.534, 2.517, 2.497, 2.478, 2.459,
+                            2.206, 2.294, 2.367, 2.435, 2.489, 2.533, 2.561, 2.576, 2.588, 2.581, 2.571, 2.559, 2.548, 2.533, 2.528, 2.509,
+                            2.206, 2.295, 2.367, 2.439, 2.498, 2.539, 2.573, 2.591, 2.594, 2.591, 2.581, 2.571, 2.561, 2.552, 2.543, 2.534,
+                            2.203, 2.289, 2.366, 2.439, 2.498, 2.539, 2.573, 2.591, 2.594, 2.592, 2.581, 2.575, 2.566, 2.559, 2.549, 2.544,
+                            2.193, 2.271, 2.344, 2.419, 2.485, 2.531, 2.565, 2.577, 2.591, 2.586, 2.577, 2.573, 2.563, 2.554, 2.545, 2.543,
+                            2.171, 2.229, 2.312, 2.393, 2.459, 2.505, 2.533, 2.565, 2.569, 2.569, 2.564, 2.553, 2.548, 2.535, 2.531, 2.527,
+                            2.117, 2.179, 2.262, 2.343, 2.414, 2.461, 2.505, 2.532, 2.539, 2.539, 2.532, 2.524, 2.515, 2.511, 2.501, 2.493,
+                            2.077, 2.117, 2.201, 2.287, 2.365, 2.417, 2.468, 2.499, 2.503, 2.503, 2.503, 2.497, 2.491, 2.479, 2.468, 2.456
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.576, 1.602, 1.624, 1.648, 1.664, 1.674, 1.674, 1.673, 1.663, 1.651, 1.633, 1.611, 1.593, 1.568, 1.544, 1.521,
+                            1.583, 1.609, 1.635, 1.659, 1.671, 1.676, 1.684, 1.687, 1.676, 1.664, 1.651, 1.634, 1.614, 1.594, 1.568, 1.551,
+                            1.583, 1.611, 1.637, 1.661, 1.676, 1.684, 1.689, 1.692, 1.691, 1.679, 1.669, 1.661, 1.648, 1.633, 1.617, 1.597,
+                            1.583, 1.611, 1.636, 1.659, 1.674, 1.684, 1.692, 1.695, 1.694, 1.691, 1.682, 1.677, 1.668, 1.661, 1.652, 1.639,
+                            1.578, 1.607, 1.631, 1.653, 1.669, 1.683, 1.693, 1.695, 1.696, 1.692, 1.687, 1.683, 1.678, 1.676, 1.672, 1.668,
+                            1.574, 1.604, 1.628, 1.651, 1.669, 1.683, 1.693, 1.697, 1.697, 1.694, 1.689, 1.686, 1.685, 1.683, 1.682, 1.681,
+                            1.568, 1.602, 1.629, 1.653, 1.671, 1.683, 1.694, 1.698, 1.698, 1.696, 1.689, 1.687, 1.687, 1.687, 1.687, 1.684,
+                            1.567, 1.599, 1.628, 1.654, 1.671, 1.687, 1.697, 1.699, 1.699, 1.697, 1.691, 1.688, 1.688, 1.689, 1.689, 1.687,
+                            1.564, 1.598, 1.628, 1.654, 1.671, 1.687, 1.697, 1.699, 1.699, 1.695, 1.691, 1.689, 1.689, 1.688, 1.687, 1.686,
+                            1.556, 1.582, 1.619, 1.646, 1.667, 1.682, 1.689, 1.694, 1.695, 1.691, 1.689, 1.687, 1.684, 1.684, 1.683, 1.684,
+                            1.539, 1.567, 1.597, 1.629, 1.653, 1.667, 1.682, 1.687, 1.687, 1.684, 1.681, 1.679, 1.679, 1.674, 1.674, 1.673,
+                            1.523, 1.539, 1.581, 1.612, 1.639, 1.656, 1.671, 1.681, 1.683, 1.681, 1.679, 1.675, 1.671, 1.666, 1.665, 1.661
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    2.049, 1.919, 1.821, 1.752, 1.701, 1.665, 1.647, 1.645, 1.645, 1.646, 1.651, 1.668, 1.697, 1.741, 1.802, 1.887,
+                    1.873, 1.746, 1.626, 1.534, 1.468, 1.423, 1.393, 1.377, 1.375, 1.378, 1.396, 1.424, 1.465, 1.523, 1.605, 1.711,
+                    1.653, 1.538, 1.427, 1.349, 1.291, 1.251, 1.225, 1.207, 1.206, 1.211, 1.231, 1.261, 1.299, 1.351, 1.423, 1.517,
+                    1.518, 1.433, 1.328, 1.235, 1.179, 1.141, 1.113, 1.098, 1.098, 1.102, 1.125, 1.155, 1.195, 1.245, 1.321, 1.399,
+                    1.426, 1.347, 1.249, 1.191, 1.139, 1.085, 1.047, 1.029, 1.029, 1.034, 1.058, 1.097, 1.147, 1.205, 1.259, 1.323,
+                    1.429, 1.339, 1.237, 1.161, 1.102, 1.058, 1.033, 1.015, 1.001, 1.023, 1.039, 1.069, 1.111, 1.164, 1.232, 1.309,
+                    1.442, 1.347, 1.244, 1.168, 1.119, 1.083, 1.049, 1.017, 1.004, 1.025, 1.062, 1.092, 1.125, 1.169, 1.238, 1.317,
+                    1.533, 1.422, 1.335, 1.244, 1.168, 1.119, 1.086, 1.067, 1.067, 1.071, 1.094, 1.125, 1.169, 1.238, 1.301, 1.371,
+                    1.669, 1.539, 1.422, 1.337, 1.274, 1.228, 1.196, 1.174, 1.172, 1.172, 1.191, 1.218, 1.252, 1.301, 1.377, 1.468,
+                    1.891, 1.748, 1.616, 1.524, 1.453, 1.401, 1.365, 1.341, 1.329, 1.329, 1.341, 1.364, 1.399, 1.446, 1.514, 1.608,
+                    2.245, 2.074, 1.925, 1.815, 1.733, 1.673, 1.626, 1.595, 1.573, 1.571, 1.571, 1.589, 1.624, 1.677, 1.736, 1.841,
+                    2.532, 2.348, 2.221, 2.124, 2.051, 1.994, 1.946, 1.912, 1.892, 1.889, 1.889, 1.889, 1.902, 1.929, 1.981, 2.058
+                ],
+                "sigma": 0.0041,
+                "sigma_Cb": 0.01178
+            }
+        },
+        {
+            "rpi.contrast":
+            {
+                "ce_enable": 1,
+                "gamma_curve":
+                [
+                    0, 0,
+                    1024, 5040,
+                    2048, 9338,
+                    3072, 12356,
+                    4096, 15312,
+                    5120, 18051,
+                    6144, 20790,
+                    7168, 23193,
+                    8192, 25744,
+                    9216, 27942,
+                    10240, 30035,
+                    11264, 32005,
+                    12288, 33975,
+                    13312, 35815,
+                    14336, 37600,
+                    15360, 39168,
+                    16384, 40642,
+                    18432, 43379,
+                    20480, 45749,
+                    22528, 47753,
+                    24576, 49621,
+                    26624, 51253,
+                    28672, 52698,
+                    30720, 53796,
+                    32768, 54876,
+                    36864, 57012,
+                    40960, 58656,
+                    45056, 59954,
+                    49152, 61183,
+                    53248, 62355,
+                    57344, 63419,
+                    61440, 64476,
+                    65535, 65535
+                ]
+            }
+        },
+        {
+            "rpi.ccm":
+            {
+                "ccms": [
+                    {
+                        "ct": 3000,
+                        "ccm":
+                        [
+                            1.65876, -0.24897, -0.40979,
+                            -0.49453, 1.89553, -0.40099,
+                            0.03049, -1.01729, 1.98679
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "ccm":
+                        [
+                            1.64934, -0.37359, -0.27574,
+                            -0.39441, 1.73642, -0.34201,
+                            -0.00897, -0.68344, 1.69241
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "ccm":
+                        [
+                            1.64354, -0.39984, -0.24369,
+                            -0.23773, 1.66846, -0.43072,
+                            0.01321, -0.65019, 1.63698
+                        ]
+                    }
+                ]
+            }
+        },
+        {
+            "rpi.sharpen": { }
+        }
+    ]
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/data/vd56g3_mono.json 0.5.2-2/src/ipa/rpi/vc4/data/vd56g3_mono.json
--- 0.5.0-1/src/ipa/rpi/vc4/data/vd56g3_mono.json	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/data/vd56g3_mono.json	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,290 @@
+{
+    "version": 2.0,
+    "target": "bcm2835",
+    "algorithms": [
+        {
+            "rpi.black_level":
+            {
+                "black_level": 4096
+            }
+        },
+        {
+            "rpi.lux":
+            {
+                "reference_shutter_speed": 17247,
+                "reference_gain": 1.0,
+                "reference_aperture": 1.0,
+                "reference_lux": 1090,
+                "reference_Y": 24531
+            }
+        },
+        {
+            "rpi.noise":
+            {
+                "reference_constant": 0,
+                "reference_slope": 3.289
+            }
+        },
+        {
+            "rpi.sdn": { }
+        },
+        {
+            "rpi.agc":
+            {
+                "metering_modes":
+                {
+                    "centre-weighted":
+                    {
+                        "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+                    },
+                    "spot":
+                    {
+                        "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
+                    },
+                    "matrix":
+                    {
+                        "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
+                    }
+                },
+                "exposure_modes":
+                {
+                    "normal":
+                    {
+                        "shutter": [ 100, 10000, 30000, 60000, 120000 ],
+                        "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+                    },
+                    "short":
+                    {
+                        "shutter": [ 100, 5000, 10000, 20000, 120000 ],
+                        "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+                    }
+                },
+                "constraint_modes":
+                {
+                    "normal": [
+                        {
+                            "bound": "LOWER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.5,
+                                1000, 0.5
+                            ]
+                        }
+                    ],
+                    "highlight": [
+                        {
+                            "bound": "LOWER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.5,
+                                1000, 0.5
+                            ]
+                        },
+                        {
+                            "bound": "UPPER",
+                            "q_lo": 0.98,
+                            "q_hi": 1.0,
+                            "y_target":
+                            [
+                                0, 0.8,
+                                1000, 0.8
+                            ]
+                        }
+                    ]
+                },
+                "y_target":
+                [
+                    0, 0.16,
+                    1000, 0.165,
+                    10000, 0.17
+                ]
+            }
+        },
+        {
+            "rpi.alsc":
+            {
+                "omega": 1.3,
+                "n_iter": 100,
+                "luminance_strength": 0.5,
+                "calibrations_Cr": [
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            0.792, 0.798, 0.799, 0.803, 0.806, 0.808, 0.809, 0.809, 0.807, 0.803, 0.801, 0.798, 0.796, 0.795, 0.794, 0.792,
+                            0.801, 0.801, 0.802, 0.809, 0.812, 0.816, 0.818, 0.818, 0.818, 0.816, 0.814, 0.813, 0.811, 0.809, 0.806, 0.803,
+                            0.806, 0.806, 0.809, 0.813, 0.817, 0.823, 0.824, 0.826, 0.826, 0.825, 0.822, 0.819, 0.818, 0.817, 0.816, 0.814,
+                            0.808, 0.809, 0.813, 0.817, 0.823, 0.825, 0.828, 0.831, 0.831, 0.829, 0.827, 0.825, 0.824, 0.825, 0.825, 0.825,
+                            0.809, 0.811, 0.815, 0.821, 0.825, 0.828, 0.831, 0.832, 0.832, 0.832, 0.831, 0.831, 0.831, 0.832, 0.834, 0.835,
+                            0.809, 0.811, 0.816, 0.821, 0.826, 0.829, 0.832, 0.832, 0.835, 0.834, 0.833, 0.832, 0.833, 0.834, 0.836, 0.837,
+                            0.809, 0.809, 0.816, 0.821, 0.826, 0.829, 0.832, 0.834, 0.835, 0.834, 0.833, 0.832, 0.833, 0.835, 0.836, 0.836,
+                            0.804, 0.805, 0.811, 0.817, 0.821, 0.826, 0.829, 0.832, 0.833, 0.832, 0.831, 0.831, 0.831, 0.832, 0.834, 0.833,
+                            0.796, 0.797, 0.804, 0.811, 0.817, 0.821, 0.824, 0.829, 0.829, 0.827, 0.827, 0.826, 0.827, 0.829, 0.829, 0.829,
+                            0.788, 0.788, 0.796, 0.803, 0.809, 0.814, 0.817, 0.821, 0.821, 0.821, 0.819, 0.819, 0.821, 0.825, 0.827, 0.827,
+                            0.775, 0.777, 0.784, 0.791, 0.797, 0.802, 0.806, 0.811, 0.813, 0.813, 0.812, 0.813, 0.816, 0.818, 0.819, 0.819,
+                            0.767, 0.774, 0.777, 0.784, 0.791, 0.797, 0.802, 0.806, 0.809, 0.811, 0.811, 0.812, 0.812, 0.816, 0.816, 0.816
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            1.009, 1.013, 1.018, 1.027, 1.034, 1.038, 1.038, 1.038, 1.037, 1.032, 1.026, 1.018, 1.011, 1.005, 0.999, 0.991,
+                            1.012, 1.015, 1.023, 1.032, 1.041, 1.045, 1.051, 1.052, 1.052, 1.047, 1.042, 1.035, 1.029, 1.023, 1.016, 1.006,
+                            1.015, 1.018, 1.027, 1.037, 1.044, 1.051, 1.056, 1.058, 1.058, 1.055, 1.051, 1.044, 1.041, 1.036, 1.032, 1.028,
+                            1.014, 1.021, 1.029, 1.037, 1.045, 1.052, 1.057, 1.061, 1.061, 1.058, 1.054, 1.051, 1.049, 1.047, 1.045, 1.042,
+                            1.012, 1.018, 1.029, 1.039, 1.046, 1.053, 1.056, 1.059, 1.061, 1.059, 1.058, 1.055, 1.055, 1.054, 1.055, 1.055,
+                            1.011, 1.014, 1.027, 1.036, 1.046, 1.052, 1.057, 1.059, 1.061, 1.061, 1.059, 1.057, 1.057, 1.058, 1.058, 1.058,
+                            1.006, 1.011, 1.024, 1.035, 1.044, 1.051, 1.056, 1.058, 1.061, 1.061, 1.058, 1.057, 1.057, 1.057, 1.058, 1.058,
+                            0.998, 1.006, 1.017, 1.028, 1.038, 1.046, 1.051, 1.056, 1.058, 1.057, 1.055, 1.055, 1.055, 1.056, 1.056, 1.054,
+                            0.988, 0.994, 1.006, 1.018, 1.028, 1.038, 1.046, 1.051, 1.051, 1.051, 1.049, 1.048, 1.049, 1.049, 1.051, 1.051,
+                            0.972, 0.979, 0.993, 1.006, 1.018, 1.027, 1.033, 1.039, 1.041, 1.041, 1.039, 1.039, 1.041, 1.044, 1.046, 1.046,
+                            0.954, 0.962, 0.977, 0.989, 1.001, 1.011, 1.018, 1.024, 1.029, 1.032, 1.032, 1.032, 1.033, 1.035, 1.038, 1.039,
+                            0.944, 0.954, 0.962, 0.977, 0.989, 1.001, 1.011, 1.018, 1.024, 1.024, 1.026, 1.027, 1.029, 1.029, 1.029, 1.029
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.288, 1.292, 1.304, 1.324, 1.339, 1.348, 1.349, 1.348, 1.347, 1.339, 1.326, 1.314, 1.302, 1.287, 1.274, 1.261,
+                            1.292, 1.304, 1.322, 1.334, 1.349, 1.359, 1.368, 1.368, 1.364, 1.361, 1.352, 1.337, 1.327, 1.315, 1.303, 1.289,
+                            1.305, 1.312, 1.332, 1.348, 1.359, 1.371, 1.379, 1.385, 1.385, 1.379, 1.373, 1.363, 1.354, 1.347, 1.335, 1.327,
+                            1.312, 1.321, 1.337, 1.357, 1.369, 1.379, 1.387, 1.396, 1.397, 1.395, 1.389, 1.381, 1.376, 1.373, 1.371, 1.365,
+                            1.319, 1.328, 1.343, 1.361, 1.377, 1.387, 1.396, 1.399, 1.403, 1.403, 1.399, 1.395, 1.394, 1.394, 1.395, 1.397,
+                            1.319, 1.328, 1.345, 1.365, 1.379, 1.391, 1.399, 1.403, 1.411, 1.411, 1.409, 1.406, 1.406, 1.408, 1.411, 1.409,
+                            1.317, 1.326, 1.345, 1.365, 1.379, 1.391, 1.401, 1.408, 1.411, 1.412, 1.411, 1.409, 1.409, 1.413, 1.416, 1.419,
+                            1.308, 1.319, 1.339, 1.359, 1.375, 1.389, 1.399, 1.405, 1.409, 1.411, 1.409, 1.409, 1.409, 1.415, 1.418, 1.419,
+                            1.291, 1.308, 1.328, 1.351, 1.366, 1.381, 1.391, 1.401, 1.405, 1.405, 1.405, 1.406, 1.408, 1.412, 1.417, 1.419,
+                            1.272, 1.291, 1.311, 1.331, 1.353, 1.369, 1.381, 1.391, 1.396, 1.397, 1.398, 1.402, 1.404, 1.411, 1.415, 1.417,
+                            1.247, 1.272, 1.291, 1.316, 1.338, 1.354, 1.369, 1.381, 1.388, 1.391, 1.394, 1.395, 1.399, 1.404, 1.411, 1.415,
+                            1.247, 1.247, 1.274, 1.296, 1.319, 1.341, 1.355, 1.369, 1.381, 1.389, 1.389, 1.394, 1.395, 1.401, 1.405, 1.406
+                        ]
+                    }
+                ],
+                "calibrations_Cb": [
+                    {
+                        "ct": 3000,
+                        "table":
+                        [
+                            2.614, 2.679, 2.791, 2.891, 2.984, 3.058, 3.058, 3.058, 3.056, 3.005, 2.933, 2.848, 2.764, 2.672, 2.575, 2.484,
+                            2.659, 2.754, 2.869, 2.975, 3.053, 3.142, 3.178, 3.178, 3.159, 3.106, 3.037, 2.963, 2.874, 2.784, 2.686, 2.605,
+                            2.687, 2.805, 2.933, 3.053, 3.142, 3.192, 3.267, 3.267, 3.264, 3.231, 3.179, 3.116, 3.042, 2.963, 2.878, 2.808,
+                            2.718, 2.847, 2.969, 3.091, 3.192, 3.267, 3.295, 3.341, 3.341, 3.331, 3.278, 3.238, 3.187, 3.127, 3.064, 3.012,
+                            2.722, 2.877, 2.993, 3.117, 3.216, 3.295, 3.341, 3.369, 3.386, 3.381, 3.352, 3.324, 3.281, 3.249, 3.207, 3.157,
+                            2.722, 2.877, 3.007, 3.128, 3.237, 3.314, 3.369, 3.396, 3.417, 3.401, 3.381, 3.358, 3.331, 3.303, 3.278, 3.255,
+                            2.722, 2.872, 3.007, 3.128, 3.237, 3.316, 3.378, 3.416, 3.419, 3.418, 3.396, 3.374, 3.357, 3.329, 3.309, 3.295,
+                            2.708, 2.855, 2.989, 3.117, 3.225, 3.312, 3.378, 3.401, 3.418, 3.403, 3.395, 3.374, 3.358, 3.335, 3.309, 3.301,
+                            2.686, 2.807, 2.941, 3.083, 3.193, 3.281, 3.333, 3.378, 3.395, 3.395, 3.381, 3.362, 3.345, 3.323, 3.305, 3.288,
+                            2.636, 2.731, 2.877, 3.011, 3.129, 3.221, 3.281, 3.333, 3.352, 3.352, 3.333, 3.323, 3.301, 3.295, 3.272, 3.253,
+                            2.547, 2.651, 2.781, 2.931, 3.039, 3.129, 3.221, 3.271, 3.284, 3.284, 3.275, 3.261, 3.255, 3.229, 3.199, 3.185,
+                            2.483, 2.547, 2.671, 2.809, 2.942, 3.041, 3.138, 3.191, 3.207, 3.207, 3.207, 3.192, 3.172, 3.156, 3.149, 3.118
+                        ]
+                    },
+                    {
+                        "ct": 4000,
+                        "table":
+                        [
+                            2.129, 2.159, 2.221, 2.281, 2.333, 2.368, 2.368, 2.368, 2.358, 2.331, 2.293, 2.243, 2.193, 2.144, 2.087, 2.036,
+                            2.151, 2.211, 2.274, 2.326, 2.372, 2.416, 2.435, 2.435, 2.419, 2.391, 2.356, 2.313, 2.267, 2.217, 2.166, 2.113,
+                            2.183, 2.242, 2.306, 2.368, 2.423, 2.456, 2.481, 2.487, 2.487, 2.471, 2.441, 2.406, 2.373, 2.329, 2.286, 2.248,
+                            2.201, 2.273, 2.338, 2.404, 2.456, 2.492, 2.519, 2.537, 2.537, 2.529, 2.508, 2.484, 2.456, 2.429, 2.396, 2.371,
+                            2.206, 2.291, 2.359, 2.425, 2.481, 2.518, 2.547, 2.561, 2.566, 2.564, 2.551, 2.534, 2.517, 2.497, 2.478, 2.459,
+                            2.206, 2.294, 2.367, 2.435, 2.489, 2.533, 2.561, 2.576, 2.588, 2.581, 2.571, 2.559, 2.548, 2.533, 2.528, 2.509,
+                            2.206, 2.295, 2.367, 2.439, 2.498, 2.539, 2.573, 2.591, 2.594, 2.591, 2.581, 2.571, 2.561, 2.552, 2.543, 2.534,
+                            2.203, 2.289, 2.366, 2.439, 2.498, 2.539, 2.573, 2.591, 2.594, 2.592, 2.581, 2.575, 2.566, 2.559, 2.549, 2.544,
+                            2.193, 2.271, 2.344, 2.419, 2.485, 2.531, 2.565, 2.577, 2.591, 2.586, 2.577, 2.573, 2.563, 2.554, 2.545, 2.543,
+                            2.171, 2.229, 2.312, 2.393, 2.459, 2.505, 2.533, 2.565, 2.569, 2.569, 2.564, 2.553, 2.548, 2.535, 2.531, 2.527,
+                            2.117, 2.179, 2.262, 2.343, 2.414, 2.461, 2.505, 2.532, 2.539, 2.539, 2.532, 2.524, 2.515, 2.511, 2.501, 2.493,
+                            2.077, 2.117, 2.201, 2.287, 2.365, 2.417, 2.468, 2.499, 2.503, 2.503, 2.503, 2.497, 2.491, 2.479, 2.468, 2.456
+                        ]
+                    },
+                    {
+                        "ct": 6500,
+                        "table":
+                        [
+                            1.576, 1.602, 1.624, 1.648, 1.664, 1.674, 1.674, 1.673, 1.663, 1.651, 1.633, 1.611, 1.593, 1.568, 1.544, 1.521,
+                            1.583, 1.609, 1.635, 1.659, 1.671, 1.676, 1.684, 1.687, 1.676, 1.664, 1.651, 1.634, 1.614, 1.594, 1.568, 1.551,
+                            1.583, 1.611, 1.637, 1.661, 1.676, 1.684, 1.689, 1.692, 1.691, 1.679, 1.669, 1.661, 1.648, 1.633, 1.617, 1.597,
+                            1.583, 1.611, 1.636, 1.659, 1.674, 1.684, 1.692, 1.695, 1.694, 1.691, 1.682, 1.677, 1.668, 1.661, 1.652, 1.639,
+                            1.578, 1.607, 1.631, 1.653, 1.669, 1.683, 1.693, 1.695, 1.696, 1.692, 1.687, 1.683, 1.678, 1.676, 1.672, 1.668,
+                            1.574, 1.604, 1.628, 1.651, 1.669, 1.683, 1.693, 1.697, 1.697, 1.694, 1.689, 1.686, 1.685, 1.683, 1.682, 1.681,
+                            1.568, 1.602, 1.629, 1.653, 1.671, 1.683, 1.694, 1.698, 1.698, 1.696, 1.689, 1.687, 1.687, 1.687, 1.687, 1.684,
+                            1.567, 1.599, 1.628, 1.654, 1.671, 1.687, 1.697, 1.699, 1.699, 1.697, 1.691, 1.688, 1.688, 1.689, 1.689, 1.687,
+                            1.564, 1.598, 1.628, 1.654, 1.671, 1.687, 1.697, 1.699, 1.699, 1.695, 1.691, 1.689, 1.689, 1.688, 1.687, 1.686,
+                            1.556, 1.582, 1.619, 1.646, 1.667, 1.682, 1.689, 1.694, 1.695, 1.691, 1.689, 1.687, 1.684, 1.684, 1.683, 1.684,
+                            1.539, 1.567, 1.597, 1.629, 1.653, 1.667, 1.682, 1.687, 1.687, 1.684, 1.681, 1.679, 1.679, 1.674, 1.674, 1.673,
+                            1.523, 1.539, 1.581, 1.612, 1.639, 1.656, 1.671, 1.681, 1.683, 1.681, 1.679, 1.675, 1.671, 1.666, 1.665, 1.661
+                        ]
+                    }
+                ],
+                "luminance_lut":
+                [
+                    2.049, 1.919, 1.821, 1.752, 1.701, 1.665, 1.647, 1.645, 1.645, 1.646, 1.651, 1.668, 1.697, 1.741, 1.802, 1.887,
+                    1.873, 1.746, 1.626, 1.534, 1.468, 1.423, 1.393, 1.377, 1.375, 1.378, 1.396, 1.424, 1.465, 1.523, 1.605, 1.711,
+                    1.653, 1.538, 1.427, 1.349, 1.291, 1.251, 1.225, 1.207, 1.206, 1.211, 1.231, 1.261, 1.299, 1.351, 1.423, 1.517,
+                    1.518, 1.433, 1.328, 1.235, 1.179, 1.141, 1.113, 1.098, 1.098, 1.102, 1.125, 1.155, 1.195, 1.245, 1.321, 1.399,
+                    1.426, 1.347, 1.249, 1.191, 1.139, 1.085, 1.047, 1.029, 1.029, 1.034, 1.058, 1.097, 1.147, 1.205, 1.259, 1.323,
+                    1.429, 1.339, 1.237, 1.161, 1.102, 1.058, 1.033, 1.015, 1.001, 1.023, 1.039, 1.069, 1.111, 1.164, 1.232, 1.309,
+                    1.442, 1.347, 1.244, 1.168, 1.119, 1.083, 1.049, 1.017, 1.004, 1.025, 1.062, 1.092, 1.125, 1.169, 1.238, 1.317,
+                    1.533, 1.422, 1.335, 1.244, 1.168, 1.119, 1.086, 1.067, 1.067, 1.071, 1.094, 1.125, 1.169, 1.238, 1.301, 1.371,
+                    1.669, 1.539, 1.422, 1.337, 1.274, 1.228, 1.196, 1.174, 1.172, 1.172, 1.191, 1.218, 1.252, 1.301, 1.377, 1.468,
+                    1.891, 1.748, 1.616, 1.524, 1.453, 1.401, 1.365, 1.341, 1.329, 1.329, 1.341, 1.364, 1.399, 1.446, 1.514, 1.608,
+                    2.245, 2.074, 1.925, 1.815, 1.733, 1.673, 1.626, 1.595, 1.573, 1.571, 1.571, 1.589, 1.624, 1.677, 1.736, 1.841,
+                    2.532, 2.348, 2.221, 2.124, 2.051, 1.994, 1.946, 1.912, 1.892, 1.889, 1.889, 1.889, 1.902, 1.929, 1.981, 2.058
+                ],
+                "sigma": 0.0041,
+                "sigma_Cb": 0.01178
+            }
+        },
+        {
+            "rpi.contrast":
+            {
+                "ce_enable": 1,
+                "gamma_curve":
+                [
+                    0, 0,
+                    1024, 5040,
+                    2048, 9338,
+                    3072, 12356,
+                    4096, 15312,
+                    5120, 18051,
+                    6144, 20790,
+                    7168, 23193,
+                    8192, 25744,
+                    9216, 27942,
+                    10240, 30035,
+                    11264, 32005,
+                    12288, 33975,
+                    13312, 35815,
+                    14336, 37600,
+                    15360, 39168,
+                    16384, 40642,
+                    18432, 43379,
+                    20480, 45749,
+                    22528, 47753,
+                    24576, 49621,
+                    26624, 51253,
+                    28672, 52698,
+                    30720, 53796,
+                    32768, 54876,
+                    36864, 57012,
+                    40960, 58656,
+                    45056, 59954,
+                    49152, 61183,
+                    53248, 62355,
+                    57344, 63419,
+                    61440, 64476,
+                    65535, 65535
+                ]
+            }
+        },
+        {
+            "rpi.sharpen": { }
+        }
+    ]
+}
diff -pruN 0.5.0-1/src/ipa/rpi/vc4/vc4.cpp 0.5.2-2/src/ipa/rpi/vc4/vc4.cpp
--- 0.5.0-1/src/ipa/rpi/vc4/vc4.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/rpi/vc4/vc4.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -57,13 +57,14 @@ private:
 	int32_t platformConfigure(const ConfigParams &params, ConfigResult *result) override;
 
 	void platformPrepareIsp(const PrepareParams &params, RPiController::Metadata &rpiMetadata) override;
+	void platformPrepareAgc([[maybe_unused]] RPiController::Metadata &rpiMetadata) override;
 	RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
 
 	void handleControls(const ControlList &controls) override;
 	bool validateIspControls();
 
 	void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
-	void applyDG(const struct AgcPrepareStatus *dgStatus, ControlList &ctrls);
+	void applyDG(double digitalGain, const struct AwbStatus *awbStatus, ControlList &ctrls);
 	void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);
 	void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);
 	void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);
@@ -77,6 +78,7 @@ private:
 
 	/* VC4 ISP controls. */
 	ControlInfoMap ispCtrls_;
+	ControlList ctrls_;
 
 	/* LS table allocation passed in from the pipeline handler. */
 	SharedFD lsTableHandle_;
@@ -106,6 +108,7 @@ int32_t IpaVc4::platformStart([[maybe_un
 int32_t IpaVc4::platformConfigure(const ConfigParams &params, [[maybe_unused]] ConfigResult *result)
 {
 	ispCtrls_ = params.ispControls;
+	ctrls_ = ControlList(ispCtrls_);
 	if (!validateIspControls()) {
 		LOG(IPARPI, Error) << "ISP control validation failed.";
 		return -1;
@@ -138,7 +141,7 @@ int32_t IpaVc4::platformConfigure(const
 void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams &params,
 				RPiController::Metadata &rpiMetadata)
 {
-	ControlList ctrls(ispCtrls_);
+	ControlList &ctrls = ctrls_;
 
 	/* Lock the metadata buffer to avoid constant locks/unlocks. */
 	std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
@@ -151,10 +154,6 @@ void IpaVc4::platformPrepareIsp([[maybe_
 	if (ccmStatus)
 		applyCCM(ccmStatus, ctrls);
 
-	AgcPrepareStatus *dgStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status");
-	if (dgStatus)
-		applyDG(dgStatus, ctrls);
-
 	AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
 	if (lsStatus)
 		applyLS(lsStatus, ctrls);
@@ -190,9 +189,18 @@ void IpaVc4::platformPrepareIsp([[maybe_
 		if (!lensctrls.empty())
 			setLensControls.emit(lensctrls);
 	}
+}
+
+void IpaVc4::platformPrepareAgc(RPiController::Metadata &rpiMetadata)
+{
+	AgcStatus *delayedAgcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
+	double digitalGain = delayedAgcStatus ? delayedAgcStatus->digitalGain : agcStatus_.digitalGain;
+	AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
+
+	applyDG(digitalGain, awbStatus, ctrls_);
 
-	if (!ctrls.empty())
-		setIspControls.emit(ctrls);
+	setIspControls.emit(ctrls_);
+	ctrls_ = ControlList(ispCtrls_);
 }
 
 RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
@@ -329,10 +337,24 @@ void IpaVc4::applyAWB(const struct AwbSt
 		  static_cast<int32_t>(awbStatus->gainB * 1000));
 }
 
-void IpaVc4::applyDG(const struct AgcPrepareStatus *dgStatus, ControlList &ctrls)
+void IpaVc4::applyDG(double digitalGain,
+		     const struct AwbStatus *awbStatus, ControlList &ctrls)
 {
+	if (awbStatus) {
+		/*
+		 * We must apply sufficient extra digital gain to stop any of the channel gains being
+		 * less than 1, which would cause saturation artifacts. Note that one of the colour
+		 * channels is still getting the minimum possible gain, so it's not a "real" gain
+		 * increase.
+		 */
+		double minColourGain = std::min({ awbStatus->gainR, awbStatus->gainG, awbStatus->gainB, 1.0 });
+		/* The 0.1 here doesn't mean much, but just stops arithmetic errors and extreme behaviour. */
+		double extraGain = 1.0 / std::max({ minColourGain, 0.1 });
+		digitalGain *= extraGain;
+	}
+
 	ctrls.set(V4L2_CID_DIGITAL_GAIN,
-		  static_cast<int32_t>(dgStatus->digitalGain * 1000));
+		  static_cast<int32_t>(digitalGain * 1000));
 }
 
 void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
diff -pruN 0.5.0-1/src/ipa/simple/algorithms/awb.cpp 0.5.2-2/src/ipa/simple/algorithms/awb.cpp
--- 0.5.0-1/src/ipa/simple/algorithms/awb.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/simple/algorithms/awb.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -40,6 +40,7 @@ void Awb::prepare(IPAContext &context,
 		  [[maybe_unused]] DebayerParams *params)
 {
 	auto &gains = context.activeState.awb.gains;
+	/* Just report, the gains are applied in LUT algorithm. */
 	frameContext.gains.red = gains.r();
 	frameContext.gains.blue = gains.b();
 }
diff -pruN 0.5.0-1/src/ipa/simple/algorithms/ccm.cpp 0.5.2-2/src/ipa/simple/algorithms/ccm.cpp
--- 0.5.0-1/src/ipa/simple/algorithms/ccm.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/simple/algorithms/ccm.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -3,7 +3,7 @@
  * Copyright (C) 2024, Ideas On Board
  * Copyright (C) 2024-2025, Red Hat Inc.
  *
- * Color correction matrix
+ * Color correction matrix + saturation
  */
 
 #include "ccm.h"
@@ -13,6 +13,8 @@
 
 #include <libcamera/control_ids.h>
 
+#include "libcamera/internal/matrix.h"
+
 namespace {
 
 constexpr unsigned int kTemperatureThreshold = 100;
@@ -35,28 +37,77 @@ int Ccm::init([[maybe_unused]] IPAContex
 	}
 
 	context.ccmEnabled = true;
+	context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
+
+	return 0;
+}
+
+int Ccm::configure(IPAContext &context,
+		   [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+	context.activeState.knobs.saturation = std::optional<double>();
 
 	return 0;
 }
 
+void Ccm::queueRequest(typename Module::Context &context,
+		       [[maybe_unused]] const uint32_t frame,
+		       [[maybe_unused]] typename Module::FrameContext &frameContext,
+		       const ControlList &controls)
+{
+	const auto &saturation = controls.get(controls::Saturation);
+	if (saturation.has_value()) {
+		context.activeState.knobs.saturation = saturation;
+		LOG(IPASoftCcm, Debug) << "Setting saturation to " << saturation.value();
+	}
+}
+
+void Ccm::applySaturation(Matrix<float, 3, 3> &ccm, float saturation)
+{
+	/* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */
+	const Matrix<float, 3, 3> rgb2ycbcr{
+		{ 0.256788235294, 0.504129411765, 0.0979058823529,
+		  -0.148223529412, -0.290992156863, 0.439215686275,
+		  0.439215686275, -0.367788235294, -0.0714274509804 }
+	};
+	const Matrix<float, 3, 3> ycbcr2rgb{
+		{ 1.16438356164, 0, 1.59602678571,
+		  1.16438356164, -0.391762290094, -0.812967647235,
+		  1.16438356164, 2.01723214285, 0 }
+	};
+	const Matrix<float, 3, 3> saturationMatrix{
+		{ 1, 0, 0,
+		  0, saturation, 0,
+		  0, 0, saturation }
+	};
+	ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm;
+}
+
 void Ccm::prepare(IPAContext &context, const uint32_t frame,
 		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
 {
+	auto &saturation = context.activeState.knobs.saturation;
+
 	const unsigned int ct = context.activeState.awb.temperatureK;
 
-	/* Change CCM only on bigger temperature changes. */
+	/* Change CCM only on saturation or bigger temperature changes. */
 	if (frame > 0 &&
-	    utils::abs_diff(ct, lastCt_) < kTemperatureThreshold) {
+	    utils::abs_diff(ct, lastCt_) < kTemperatureThreshold &&
+	    saturation == lastSaturation_) {
 		frameContext.ccm.ccm = context.activeState.ccm.ccm;
 		context.activeState.ccm.changed = false;
 		return;
 	}
 
 	lastCt_ = ct;
+	lastSaturation_ = saturation;
 	Matrix<float, 3, 3> ccm = ccm_.getInterpolated(ct);
+	if (saturation)
+		applySaturation(ccm, saturation.value());
 
 	context.activeState.ccm.ccm = ccm;
 	frameContext.ccm.ccm = ccm;
+	frameContext.saturation = saturation;
 	context.activeState.ccm.changed = true;
 }
 
@@ -67,6 +118,9 @@ void Ccm::process([[maybe_unused]] IPACo
 		  ControlList &metadata)
 {
 	metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data());
+
+	const auto &saturation = frameContext.saturation;
+	metadata.set(controls::Saturation, saturation.value_or(1.0));
 }
 
 REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
diff -pruN 0.5.0-1/src/ipa/simple/algorithms/ccm.h 0.5.2-2/src/ipa/simple/algorithms/ccm.h
--- 0.5.0-1/src/ipa/simple/algorithms/ccm.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/simple/algorithms/ccm.h	2025-08-07 13:46:17.000000000 +0000
@@ -7,6 +7,8 @@
 
 #pragma once
 
+#include <optional>
+
 #include "libcamera/internal/matrix.h"
 
 #include <libipa/interpolator.h>
@@ -24,6 +26,12 @@ public:
 	~Ccm() = default;
 
 	int init(IPAContext &context, const YamlObject &tuningData) override;
+	int configure(IPAContext &context,
+		      const IPAConfigInfo &configInfo) override;
+	void queueRequest(typename Module::Context &context,
+			  const uint32_t frame,
+			  typename Module::FrameContext &frameContext,
+			  const ControlList &controls) override;
 	void prepare(IPAContext &context,
 		     const uint32_t frame,
 		     IPAFrameContext &frameContext,
@@ -34,7 +42,10 @@ public:
 		     ControlList &metadata) override;
 
 private:
+	void applySaturation(Matrix<float, 3, 3> &ccm, float saturation);
+
 	unsigned int lastCt_;
+	std::optional<float> lastSaturation_;
 	Interpolator<Matrix<float, 3, 3>> ccm_;
 };
 
diff -pruN 0.5.0-1/src/ipa/simple/algorithms/lut.cpp 0.5.2-2/src/ipa/simple/algorithms/lut.cpp
--- 0.5.0-1/src/ipa/simple/algorithms/lut.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/simple/algorithms/lut.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -122,7 +122,7 @@ void Lut::prepare(IPAContext &context,
 		Matrix<float, 3, 3> gainCcm = { { gains.r(), 0, 0,
 						  0, gains.g(), 0,
 						  0, 0, gains.b() } };
-		auto ccm = gainCcm * context.activeState.ccm.ccm;
+		auto ccm = context.activeState.ccm.ccm * gainCcm;
 		auto &red = params->redCcm;
 		auto &green = params->greenCcm;
 		auto &blue = params->blueCcm;
diff -pruN 0.5.0-1/src/ipa/simple/ipa_context.h 0.5.2-2/src/ipa/simple/ipa_context.h
--- 0.5.0-1/src/ipa/simple/ipa_context.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/ipa/simple/ipa_context.h	2025-08-07 13:46:17.000000000 +0000
@@ -63,6 +63,7 @@ struct IPAActiveState {
 	struct {
 		/* 0..2 range, 1.0 = normal */
 		std::optional<double> contrast;
+		std::optional<float> saturation;
 	} knobs;
 };
 
@@ -75,11 +76,14 @@ struct IPAFrameContext : public FrameCon
 		int32_t exposure;
 		double gain;
 	} sensor;
+
 	struct {
 		double red;
 		double blue;
 	} gains;
+
 	std::optional<double> contrast;
+	std::optional<float> saturation;
 };
 
 struct IPAContext {
diff -pruN 0.5.0-1/src/libcamera/base/log.cpp 0.5.2-2/src/libcamera/base/log.cpp
--- 0.5.0-1/src/libcamera/base/log.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/base/log.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -690,8 +690,9 @@ LogSeverity Logger::parseLogLevel(std::s
 	unsigned int severity = LogInvalid;
 
 	if (std::isdigit(level[0])) {
-		auto [end, ec] = std::from_chars(level.data(), level.data() + level.size(), severity);
-		if (ec != std::errc() || *end != '\0' || severity > LogFatal)
+		const char *levelEnd = level.data() + level.size();
+		auto [end, ec] = std::from_chars(level.data(), levelEnd, severity);
+		if (ec != std::errc() || end != levelEnd || severity > LogFatal)
 			severity = LogInvalid;
 	} else {
 		for (unsigned int i = 0; i < std::size(names); ++i) {
@@ -951,12 +952,10 @@ Loggable::~Loggable()
  *
  * \return A log message
  */
-LogMessage Loggable::_log(const LogCategory *category, LogSeverity severity,
+LogMessage Loggable::_log(const LogCategory &category, LogSeverity severity,
 			  const char *fileName, unsigned int line) const
 {
-	return LogMessage(fileName, line,
-			  category ? *category : LogCategory::defaultCategory(),
-			  severity, logPrefix());
+	return LogMessage(fileName, line, category, severity, logPrefix());
 }
 
 /**
@@ -971,12 +970,10 @@ LogMessage Loggable::_log(const LogCateg
  *
  * \return A log message
  */
-LogMessage _log(const LogCategory *category, LogSeverity severity,
+LogMessage _log(const LogCategory &category, LogSeverity severity,
 		const char *fileName, unsigned int line)
 {
-	return LogMessage(fileName, line,
-			  category ? *category : LogCategory::defaultCategory(),
-			  severity);
+	return LogMessage(fileName, line, category, severity);
 }
 
 /**
diff -pruN 0.5.0-1/src/libcamera/camera.cpp 0.5.2-2/src/libcamera/camera.cpp
--- 0.5.0-1/src/libcamera/camera.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/camera.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -488,7 +488,7 @@ std::size_t CameraConfiguration::size()
  *
  * \return A CameraConfiguration::Status value that describes the validation
  * status.
- * \retval CameraConfigutation::Adjusted The configuration has been adjusted
+ * \retval CameraConfiguration::Adjusted The configuration has been adjusted
  * and is now valid. The color space of some or all of the streams may have
  * been changed. The caller shall check the color spaces carefully.
  * \retval CameraConfiguration::Valid The configuration was already valid and
@@ -629,6 +629,15 @@ Camera::Private::~Private()
  */
 
 /**
+ * \var Camera::Private::waitingRequests_
+ * \brief The queue of waiting requests
+ *
+ * This queue tracks all requests that can not yet be queued to the device.
+ * Either because they are not yet prepared or because the maximum number of
+ * queued requests was reached.
+ */
+
+/**
  * \var Camera::Private::controlInfo_
  * \brief The set of controls supported by the camera
  *
@@ -1122,7 +1131,8 @@ std::unique_ptr<CameraConfiguration> Cam
 		return nullptr;
 
 	std::unique_ptr<CameraConfiguration> config =
-		d->pipe_->generateConfiguration(this, roles);
+		d->pipe_->invokeMethod(&PipelineHandler::generateConfiguration,
+				       ConnectionTypeBlocking, this, roles);
 	if (!config) {
 		LOG(Camera, Debug)
 			<< "Pipeline handler failed to generate configuration";
@@ -1267,6 +1277,33 @@ std::unique_ptr<Request> Camera::createR
 }
 
 /**
+ * \brief Patch a control list that contains the AeEnable control
+ * \param[inout] controls The control list to be patched
+ *
+ * The control list is patched in place, turning the AeEnable control into
+ * the equivalent ExposureTimeMode/AnalogueGainMode controls.
+ */
+void Camera::patchControlList(ControlList &controls)
+{
+	const auto &aeEnable = controls.get(controls::AeEnable);
+	if (aeEnable) {
+		if (_d()->controlInfo_.count(controls::AnalogueGainMode.id()) &&
+		    !controls.contains(controls::AnalogueGainMode.id())) {
+			controls.set(controls::AnalogueGainMode,
+				     *aeEnable ? controls::AnalogueGainModeAuto
+					       : controls::AnalogueGainModeManual);
+		}
+
+		if (_d()->controlInfo_.count(controls::ExposureTimeMode.id()) &&
+		    !controls.contains(controls::ExposureTimeMode.id())) {
+			controls.set(controls::ExposureTimeMode,
+				     *aeEnable ? controls::ExposureTimeModeAuto
+					       : controls::ExposureTimeModeManual);
+		}
+	}
+}
+
+/**
  * \brief Queue a request to the camera
  * \param[in] request The request to queue to the camera
  *
@@ -1329,23 +1366,7 @@ int Camera::queueRequest(Request *reques
 	}
 
 	/* Pre-process AeEnable. */
-	ControlList &controls = request->controls();
-	const auto &aeEnable = controls.get(controls::AeEnable);
-	if (aeEnable) {
-		if (_d()->controlInfo_.count(controls::AnalogueGainMode.id()) &&
-		    !controls.contains(controls::AnalogueGainMode.id())) {
-			controls.set(controls::AnalogueGainMode,
-				     *aeEnable ? controls::AnalogueGainModeAuto
-					       : controls::AnalogueGainModeManual);
-		}
-
-		if (_d()->controlInfo_.count(controls::ExposureTimeMode.id()) &&
-		    !controls.contains(controls::ExposureTimeMode.id())) {
-			controls.set(controls::ExposureTimeMode,
-				     *aeEnable ? controls::ExposureTimeModeAuto
-					       : controls::ExposureTimeModeManual);
-		}
-	}
+	patchControlList(request->controls());
 
 	d->pipe_->invokeMethod(&PipelineHandler::queueRequest,
 			       ConnectionTypeQueued, request);
@@ -1383,8 +1404,16 @@ int Camera::start(const ControlList *con
 
 	ASSERT(d->requestSequence_ == 0);
 
-	ret = d->pipe_->invokeMethod(&PipelineHandler::start,
-				     ConnectionTypeBlocking, this, controls);
+	if (controls) {
+		ControlList copy(*controls);
+		patchControlList(copy);
+		ret = d->pipe_->invokeMethod(&PipelineHandler::start,
+					     ConnectionTypeBlocking, this, &copy);
+	} else {
+		ret = d->pipe_->invokeMethod(&PipelineHandler::start,
+					     ConnectionTypeBlocking, this, nullptr);
+	}
+
 	if (ret)
 		return ret;
 
diff -pruN 0.5.0-1/src/libcamera/camera_manager.cpp 0.5.2-2/src/libcamera/camera_manager.cpp
--- 0.5.0-1/src/libcamera/camera_manager.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/camera_manager.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -217,6 +217,10 @@ void CameraManager::Private::addCamera(s
 		cameras_.push_back(camera);
 	}
 
+	LOG(Camera, Info)
+		<< "Adding camera '" << camera->id() << "' for pipeline handler "
+		<< camera->_d()->pipe()->name();
+
 	/* Report the addition to the public signal */
 	CameraManager *const o = LIBCAMERA_O_PTR();
 	o->cameraAdded.emit(camera);
diff -pruN 0.5.0-1/src/libcamera/clock_recovery.cpp 0.5.2-2/src/libcamera/clock_recovery.cpp
--- 0.5.0-1/src/libcamera/clock_recovery.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/libcamera/clock_recovery.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,230 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Raspberry Pi Ltd
+ *
+ * Clock recovery algorithm
+ */
+
+#include "libcamera/internal/clock_recovery.h"
+
+#include <time.h>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file clock_recovery.h
+ * \brief Clock recovery - deriving one clock from another independent clock
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(ClockRec)
+
+/**
+ * \class ClockRecovery
+ * \brief Recover an output clock from an input clock
+ *
+ * The ClockRecovery class derives an output clock from an input clock,
+ * modelling the output clock as being linearly related to the input clock.
+ * For example, we may use it to derive wall clock timestamps from timestamps
+ * measured by the internal system clock which counts local time since boot.
+ *
+ * When pairs of corresponding input and output timestamps are available,
+ * they should be submitted to the model with addSample(). The model will
+ * update, and output clock values for known input clock values can be
+ * obtained using getOutput().
+ *
+ * As a convenience, if the input clock is indeed the time since boot, and the
+ * output clock represents a real wallclock time, then addSample() can be
+ * called with no arguments, and a pair of timestamps will be captured at
+ * that moment.
+ *
+ * The configure() function accepts some configuration parameters to control
+ * the linear fitting process.
+ */
+
+/**
+ * \brief Construct a ClockRecovery
+ */
+ClockRecovery::ClockRecovery()
+{
+	configure();
+	reset();
+}
+
+/**
+ * \brief Set configuration parameters
+ * \param[in] numSamples The approximate duration for which the state of the model
+ * is persistent
+ * \param[in] maxJitter New output samples are clamped to no more than this
+ * amount of jitter, to prevent sudden swings from having a large effect
+ * \param[in] minSamples The fitted clock model is not used to generate outputs
+ * until this many samples have been received
+ * \param[in] errorThreshold If the accumulated differences between input and
+ * output clocks reaches this amount over a few frames, the model is reset
+ */
+void ClockRecovery::configure(unsigned int numSamples, unsigned int maxJitter,
+			      unsigned int minSamples, unsigned int errorThreshold)
+{
+	LOG(ClockRec, Debug)
+		<< "configure " << numSamples << " " << maxJitter << " " << minSamples << " " << errorThreshold;
+
+	numSamples_ = numSamples;
+	maxJitter_ = maxJitter;
+	minSamples_ = minSamples;
+	errorThreshold_ = errorThreshold;
+}
+
+/**
+ * \brief Reset the clock recovery model and start again from scratch
+ */
+void ClockRecovery::reset()
+{
+	LOG(ClockRec, Debug) << "reset";
+
+	lastInput_ = 0;
+	lastOutput_ = 0;
+	xAve_ = 0;
+	yAve_ = 0;
+	x2Ave_ = 0;
+	xyAve_ = 0;
+	count_ = 0;
+	error_ = 0.0;
+	/*
+	 * Setting slope_ and offset_ to zero initially means that the clocks
+	 * advance at exactly the same rate.
+	 */
+	slope_ = 0.0;
+	offset_ = 0.0;
+}
+
+/**
+ * \brief Add a sample point to the clock recovery model, for recovering a wall
+ * clock value from the internal system time since boot
+ *
+ * This is a convenience function to make it easy to derive a wall clock value
+ * (using the Linux CLOCK_REALTIME) from the time since the system started
+ * (measured by CLOCK_BOOTTIME).
+ */
+void ClockRecovery::addSample()
+{
+	LOG(ClockRec, Debug) << "addSample";
+
+	struct timespec bootTime1;
+	struct timespec bootTime2;
+	struct timespec wallTime;
+
+	/* Get boot and wall clocks in microseconds. */
+	clock_gettime(CLOCK_BOOTTIME, &bootTime1);
+	clock_gettime(CLOCK_REALTIME, &wallTime);
+	clock_gettime(CLOCK_BOOTTIME, &bootTime2);
+	uint64_t boot1 = bootTime1.tv_sec * 1000000ULL + bootTime1.tv_nsec / 1000;
+	uint64_t boot2 = bootTime2.tv_sec * 1000000ULL + bootTime2.tv_nsec / 1000;
+	uint64_t boot = (boot1 + boot2) / 2;
+	uint64_t wall = wallTime.tv_sec * 1000000ULL + wallTime.tv_nsec / 1000;
+
+	addSample(boot, wall);
+}
+
+/**
+ * \brief Add a sample point to the clock recovery model, specifying the exact
+ * input and output clock values
+ * \param[in] input The input clock value
+ * \param[in] output The value of the output clock at the same moment, as far
+ * as possible, that the input clock was sampled
+ *
+ * This function should be used for corresponding clocks other than the Linux
+ * BOOTTIME and REALTIME clocks.
+ */
+void ClockRecovery::addSample(uint64_t input, uint64_t output)
+{
+	LOG(ClockRec, Debug) << "addSample " << input << " " << output;
+
+	if (count_ == 0) {
+		inputBase_ = input;
+		outputBase_ = output;
+	}
+
+	/*
+	 * We keep an eye on cumulative drift over the last several frames. If this exceeds a
+	 * threshold, then probably the system clock has been updated and we're going to have to
+	 * reset everything and start over.
+	 */
+	if (lastOutput_) {
+		int64_t inputDiff = getOutput(input) - getOutput(lastInput_);
+		int64_t outputDiff = output - lastOutput_;
+		error_ = error_ * 0.95 + (outputDiff - inputDiff);
+		if (std::abs(error_) > errorThreshold_) {
+			reset();
+			inputBase_ = input;
+			outputBase_ = output;
+		}
+	}
+	lastInput_ = input;
+	lastOutput_ = output;
+
+	/*
+	 * Never let the new output value be more than maxJitter_ away from what
+	 * we would have expected.  This is just to reduce the effect of sudden
+	 * large delays in the measured output.
+	 */
+	uint64_t expectedOutput = getOutput(input);
+	output = std::clamp(output, expectedOutput - maxJitter_, expectedOutput + maxJitter_);
+
+	/*
+	 * We use x, y, x^2 and x*y sums to calculate the best fit line. Here we
+	 * update them by pretending we have count_ samples at the previous fit,
+	 * and now one new one. Gradually the effect of the older values gets
+	 * lost. This is a very simple way of updating the fit (there are much
+	 * more complicated ones!), but it works well enough. Using averages
+	 * instead of sums makes the relative effect of old values and the new
+	 * sample clearer.
+	 */
+	double x = static_cast<int64_t>(input - inputBase_);
+	double y = static_cast<int64_t>(output - outputBase_) - x;
+	unsigned int count1 = count_ + 1;
+	xAve_ = (count_ * xAve_ + x) / count1;
+	yAve_ = (count_ * yAve_ + y) / count1;
+	x2Ave_ = (count_ * x2Ave_ + x * x) / count1;
+	xyAve_ = (count_ * xyAve_ + x * y) / count1;
+
+	/*
+	 * Don't update slope and offset until we've seen "enough" sample
+	 * points.  Note that the initial settings for slope_ and offset_
+	 * ensures that the wallclock advances at the same rate as the realtime
+	 * clock (but with their respective initial offsets).
+	 */
+	if (count_ > minSamples_) {
+		/* These are the standard equations for least squares linear regression. */
+		slope_ = (count1 * count1 * xyAve_ - count1 * xAve_ * count1 * yAve_) /
+			 (count1 * count1 * x2Ave_ - count1 * xAve_ * count1 * xAve_);
+		offset_ = yAve_ - slope_ * xAve_;
+	}
+
+	/*
+	 * Don't increase count_ above numSamples_, as this controls the long-term
+	 * amount of the residual fit.
+	 */
+	if (count1 < numSamples_)
+		count_++;
+}
+
+/**
+ * \brief Calculate the output clock value according to the model from an input
+ * clock value
+ * \param[in] input The input clock value
+ *
+ * \return Output clock value
+ */
+uint64_t ClockRecovery::getOutput(uint64_t input)
+{
+	double x = static_cast<int64_t>(input - inputBase_);
+	double y = slope_ * x + offset_;
+	uint64_t output = y + x + outputBase_;
+
+	LOG(ClockRec, Debug) << "getOutput " << input << " " << output;
+
+	return output;
+}
+
+} /* namespace libcamera */
diff -pruN 0.5.0-1/src/libcamera/control_ids_core.yaml 0.5.2-2/src/libcamera/control_ids_core.yaml
--- 0.5.0-1/src/libcamera/control_ids_core.yaml	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/control_ids_core.yaml	2025-08-07 13:46:17.000000000 +0000
@@ -212,7 +212,7 @@ controls:
       description: |
         Exposure time for the frame applied in the sensor device.
 
-        This value is specified in micro-seconds.
+        This value is specified in microseconds.
 
         This control will only take effect if ExposureTimeMode is Manual. If
         this control is set when ExposureTimeMode is Auto, the value will be
@@ -1268,4 +1268,20 @@ controls:
       description: |
         Enable or disable the debug metadata.
 
+  - FrameWallClock:
+      type: int64_t
+      direction: out
+      description: |
+        This timestamp corresponds to the same moment in time as the
+        SensorTimestamp, but is represented as a wall clock time as measured by
+        the CLOCK_REALTIME clock. Like SensorTimestamp, the timestamp value is
+        expressed in nanoseconds.
+
+        Being a wall clock measurement, it can be used to synchronise timing
+        across different devices.
+
+        \sa SensorTimestamp
+
+        The FrameWallClock control can only be returned in metadata.
+
 ...
diff -pruN 0.5.0-1/src/libcamera/control_ids_rpi.yaml 0.5.2-2/src/libcamera/control_ids_rpi.yaml
--- 0.5.0-1/src/libcamera/control_ids_rpi.yaml	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/control_ids_rpi.yaml	2025-08-07 13:46:17.000000000 +0000
@@ -71,4 +71,116 @@ controls:
 
         \sa StatsOutputEnable
 
+  - SyncMode:
+      type: int32_t
+      direction: in
+      description: |
+        Enable or disable camera synchronisation ("sync") mode.
+
+        When sync mode is enabled, a camera will synchronise frames temporally
+        with other cameras, either attached to the same device or a different
+        one. There should be one "server" device, which broadcasts timing
+        information to one or more "clients". Communication is one-way, from
+        server to clients only, and it is only clients that adjust their frame
+        timings to match the server.
+
+        Sync mode requires all cameras to be running at (as far as possible) the
+        same fixed framerate. Clients may continue to make adjustments to keep
+        their cameras synchronised with the server for the duration of the
+        session, though any updates after the initial ones should remain small.
+
+        \sa SyncReady
+        \sa SyncTimer
+        \sa SyncFrames
+
+      enum:
+        - name: SyncModeOff
+          value: 0
+          description: Disable sync mode.
+        - name: SyncModeServer
+          value: 1
+          description: |
+            Enable sync mode, act as server. The server broadcasts timing
+            messages to any clients that are listening, so that the clients can
+            synchronise their camera frames with the server's.
+        - name: SyncModeClient
+          value: 2
+          description: |
+            Enable sync mode, act as client. A client listens for any server
+            messages, and arranges for its camera frames to synchronise as
+            closely as possible with the server's. Many clients can listen out
+            for the same server. Clients can also be started ahead of any
+            servers, causing them merely to wait for the server to start.
+
+  - SyncReady:
+      type: bool
+      direction: out
+      description: |
+        When using the camera synchronisation algorithm, the server broadcasts
+        timing information to the clients. This also includes the time (some
+        number of frames in the future, called the "ready time") at which the
+        server will signal its controlling application, using this control, to
+        start using the image frames.
+
+        The client receives the "ready time" from the server, and will signal
+        its application to start using the frames at this same moment.
+
+        While this control value is false, applications (on both client and
+        server) should continue to wait, and not use the frames.
+
+        Once this value becomes true, it means that this is the first frame
+        where the server and its clients have agreed that they will both be
+        synchronised and that applications should begin consuming frames.
+        Thereafter, this control will continue to signal the value true for
+        the rest of the session.
+
+        \sa SyncMode
+        \sa SyncTimer
+        \sa SyncFrames
+
+  - SyncTimer:
+      type: int64_t
+      direction: out
+      description: |
+        This reports the amount of time, in microseconds, until the "ready
+        time", at which the server and client will signal their controlling
+        applications that the frames are now synchronised and should be
+        used. The value may be refined slightly over time, becoming more precise
+        as the "ready time" approaches.
+
+        Servers always report this value, whereas clients will omit this control
+        until they have received a message from the server that enables them to
+        calculate it.
+
+        Normally the value will start positive (the "ready time" is in the
+        future), and decrease towards zero, before becoming negative (the "ready
+        time" has elapsed). So there should be just one frame where the timer
+        value is, or is very close to, zero - the one for which the SyncReady
+        control becomes true. At this moment, the value indicates how closely
+        synchronised the client believes it is with the server.
+
+        But note that if frames are being dropped, then the "near zero" valued
+        frame, or indeed any other, could be skipped. In these cases the timer
+        value allows an application to deduce that this has happened.
+
+        \sa SyncMode
+        \sa SyncReady
+        \sa SyncFrames
+
+  - SyncFrames:
+      type: int32_t
+      direction: in
+      description: |
+        The number of frames the server should wait, after enabling
+        SyncModeServer, before signalling (via the SyncReady control) that
+        frames should be used. This therefore determines the "ready time" for
+        all synchronised cameras.
+
+        This control value should be set only for the device that is to act as
+        the server, before or at the same moment at which SyncModeServer is
+        enabled.
+
+        \sa SyncMode
+        \sa SyncReady
+        \sa SyncTimer
 ...
diff -pruN 0.5.0-1/src/libcamera/control_serializer.cpp 0.5.2-2/src/libcamera/control_serializer.cpp
--- 0.5.0-1/src/libcamera/control_serializer.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/control_serializer.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -601,8 +601,10 @@ ControlList ControlSerializer::deseriali
 
 		case IPA_CONTROL_ID_MAP_V4L2:
 		default:
-			LOG(Serializer, Fatal)
-				<< "A list of V4L2 controls requires an ControlInfoMap";
+			if (hdr->entries > 0)
+				LOG(Serializer, Fatal)
+					<< "A list of V4L2 controls requires a ControlInfoMap";
+
 			return {};
 		}
 	}
diff -pruN 0.5.0-1/src/libcamera/controls.cpp 0.5.2-2/src/libcamera/controls.cpp
--- 0.5.0-1/src/libcamera/controls.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/controls.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -857,15 +857,7 @@ const ControlInfoMap::mapped_type &Contr
  */
 ControlInfoMap::size_type ControlInfoMap::count(unsigned int id) const
 {
-	if (!idmap_)
-		return 0;
-
-	/*
-	 * The ControlInfoMap and its idmap have a 1:1 mapping between their
-	 * entries, we can thus just count the matching entries in idmap to
-	 * avoid an additional lookup.
-	 */
-	return idmap_->count(id);
+	return find(id) != end();
 }
 
 /**
diff -pruN 0.5.0-1/src/libcamera/framebuffer.cpp 0.5.2-2/src/libcamera/framebuffer.cpp
--- 0.5.0-1/src/libcamera/framebuffer.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/framebuffer.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -43,12 +43,19 @@ LOG_DEFINE_CATEGORY(Buffer)
  * The frame has been captured with success and contains valid data. All fields
  * of the FrameMetadata structure are valid.
  * \var FrameMetadata::FrameError
- * An error occurred during capture of the frame. The frame data may be partly
- * or fully invalid. The sequence and timestamp fields of the FrameMetadata
- * structure is valid, the other fields may be invalid.
+ * The frame data is partly or fully corrupted, missing or otherwise invalid.
+ * This can for instance indicate a hardware transmission error, or invalid data
+ * produced by the sensor during its startup phase. The sequence and timestamp
+ * fields of the FrameMetadata structure is valid, all the other fields may be
+ * invalid.
  * \var FrameMetadata::FrameCancelled
  * Capture stopped before the frame completed. The frame data is not valid. All
  * fields of the FrameMetadata structure but the status field are invalid.
+ * \var FrameMetadata::FrameStartup
+ * The frame has been successfully captured. However, the IPA is in a
+ * cold-start or reset phase and will result in image quality parameters
+ * producing unusable images. Applications are recommended to not consume these
+ * frames. All other fields of the FrameMetadata structure are valid.
  */
 
 /**
diff -pruN 0.5.0-1/src/libcamera/ipa_data_serializer.cpp 0.5.2-2/src/libcamera/ipa_data_serializer.cpp
--- 0.5.0-1/src/libcamera/ipa_data_serializer.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/ipa_data_serializer.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -196,7 +196,6 @@ IPADataSerializer<type>::serialize(const
 				  [[maybe_unused]] ControlSerializer *cs) \
 {									\
 	std::vector<uint8_t> dataVec;					\
-	dataVec.reserve(sizeof(type));					\
 	appendPOD<type>(dataVec, data);					\
 									\
 	return { dataVec, {} };						\
diff -pruN 0.5.0-1/src/libcamera/ipa_module.cpp 0.5.2-2/src/libcamera/ipa_module.cpp
--- 0.5.0-1/src/libcamera/ipa_module.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/ipa_module.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -373,7 +373,7 @@ const struct IPAModuleInfo &IPAModule::i
  *
  * \return The IPA module signature
  */
-const std::vector<uint8_t> IPAModule::signature() const
+const std::vector<uint8_t> &IPAModule::signature() const
 {
 	return signature_;
 }
diff -pruN 0.5.0-1/src/libcamera/ipc_pipe_unixsocket.cpp 0.5.2-2/src/libcamera/ipc_pipe_unixsocket.cpp
--- 0.5.0-1/src/libcamera/ipc_pipe_unixsocket.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/ipc_pipe_unixsocket.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -28,10 +28,6 @@ IPCPipeUnixSocket::IPCPipeUnixSocket(con
 				     const char *ipaProxyWorkerPath)
 	: IPCPipe()
 {
-	std::vector<int> fds;
-	std::vector<std::string> args;
-	args.push_back(ipaModulePath);
-
 	socket_ = std::make_unique<IPCUnixSocket>();
 	UniqueFD fd = socket_->create();
 	if (!fd.isValid()) {
@@ -39,8 +35,9 @@ IPCPipeUnixSocket::IPCPipeUnixSocket(con
 		return;
 	}
 	socket_->readyRead.connect(this, &IPCPipeUnixSocket::readyRead);
-	args.push_back(std::to_string(fd.get()));
-	fds.push_back(fd.get());
+
+	std::array args{ std::string(ipaModulePath), std::to_string(fd.get()) };
+	std::array fds{ fd.get() };
 
 	proc_ = std::make_unique<Process>();
 	int ret = proc_->start(ipaProxyWorkerPath, args, fds);
diff -pruN 0.5.0-1/src/libcamera/matrix.cpp 0.5.2-2/src/libcamera/matrix.cpp
--- 0.5.0-1/src/libcamera/matrix.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/matrix.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -7,6 +7,12 @@
 
 #include "libcamera/internal/matrix.h"
 
+#include <algorithm>
+#include <assert.h>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
 #include <libcamera/base/log.h>
 
 /**
@@ -42,6 +48,16 @@ LOG_DEFINE_CATEGORY(Matrix)
  */
 
 /**
+ * \fn Matrix::Matrix(const Span<const T, Rows * Cols> data)
+ * \brief Construct a matrix from supplied data
+ * \param[in] data Data from which to construct a matrix
+ *
+ * \a data is a one-dimensional Span and will be turned into a matrix in
+ * row-major order. The size of \a data must be equal to the product of the
+ * number of rows and columns of the matrix (Rows x Cols).
+ */
+
+/**
  * \fn Matrix::identity()
  * \brief Construct an identity matrix
  */
@@ -78,6 +94,20 @@ LOG_DEFINE_CATEGORY(Matrix)
  */
 
 /**
+ * \fn Matrix::inverse(bool *ok) const
+ * \param[out] ok Indicate if the matrix was successfully inverted
+ * \brief Compute the inverse of the matrix
+ *
+ * This function computes the inverse of the matrix. It is only implemented for
+ * matrices of float and double types. If \a ok is provided it will be set to a
+ * boolean value to indicate of the inversion was successful. This can be used
+ * to check if the matrix is singular, in which case the function will return
+ * an identity matrix.
+ *
+ * \return The inverse of the matrix
+ */
+
+/**
  * \fn Matrix::operator[](size_t i)
  * \copydoc Matrix::operator[](size_t i) const
  */
@@ -108,11 +138,12 @@ LOG_DEFINE_CATEGORY(Matrix)
  */
 
 /**
- * \fn Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)
+ * \fn operator*(const Matrix<T1, R1, C1> &m1, const Matrix<T2, R2, C2> &m2)
  * \brief Matrix multiplication
- * \tparam T Type of numerical values in the matrices
+ * \tparam T1 Type of numerical values in the first matrix
  * \tparam R1 Number of rows in the first matrix
  * \tparam C1 Number of columns in the first matrix
+ * \tparam T2 Type of numerical values in the secont matrix
  * \tparam R2 Number of rows in the second matrix
  * \tparam C2 Number of columns in the second matrix
  * \param m1 Multiplicand matrix
@@ -132,6 +163,152 @@ LOG_DEFINE_CATEGORY(Matrix)
  */
 
 #ifndef __DOXYGEN__
+template<typename T>
+bool matrixInvert(Span<const T> dataIn, Span<T> dataOut, unsigned int dim,
+		  Span<T> scratchBuffer, Span<unsigned int> swapBuffer)
+{
+	/*
+	 * Convenience class to access matrix data, providing a row-major (i,j)
+	 * element accessor through the call operator, and the ability to swap
+	 * rows without modifying the backing storage.
+	 */
+	class MatrixAccessor
+	{
+	public:
+		MatrixAccessor(Span<T> data, Span<unsigned int> swapBuffer, unsigned int rows, unsigned int cols)
+			: data_(data), swap_(swapBuffer), rows_(rows), cols_(cols)
+		{
+			ASSERT(swap_.size() == rows);
+			std::iota(swap_.begin(), swap_.end(), T{ 0 });
+		}
+
+		T &operator()(unsigned int row, unsigned int col)
+		{
+			assert(row < rows_ && col < cols_);
+			return data_[index(row, col)];
+		}
+
+		void swap(unsigned int a, unsigned int b)
+		{
+			assert(a < rows_ && a < cols_);
+			std::swap(swap_[a], swap_[b]);
+		}
+
+	private:
+		unsigned int index(unsigned int row, unsigned int col) const
+		{
+			return swap_[row] * cols_ + col;
+		}
+
+		Span<T> data_;
+		Span<unsigned int> swap_;
+		unsigned int rows_;
+		unsigned int cols_;
+	};
+
+	/*
+	 * Matrix inversion using Gaussian elimination.
+	 *
+	 * Start by augmenting the original matrix with an identiy matrix of
+	 * the same size.
+	 */
+	ASSERT(scratchBuffer.size() == dim * dim * 2);
+	MatrixAccessor matrix(scratchBuffer, swapBuffer, dim, dim * 2);
+
+	for (unsigned int i = 0; i < dim; ++i) {
+		for (unsigned int j = 0; j < dim; ++j) {
+			matrix(i, j) = dataIn[i * dim + j];
+			matrix(i, j + dim) = T{ 0 };
+		}
+		matrix(i, i + dim) = T{ 1 };
+	}
+
+	/* Start by triangularizing the input . */
+	for (unsigned int pivot = 0; pivot < dim; ++pivot) {
+		/*
+		 * Locate the next pivot. To improve numerical stability, use
+		 * the row with the largest value in the pivot's column.
+		 */
+		unsigned int row = pivot;
+		T maxValue{ 0 };
+
+		for (unsigned int i = pivot; i < dim; ++i) {
+			T value = std::abs(matrix(i, pivot));
+			if (maxValue < value) {
+				maxValue = value;
+				row = i;
+			}
+		}
+
+		/*
+		 * If no pivot is found in the column, the matrix is not
+		 * invertible. Return an identity matrix.
+		 */
+		if (maxValue == 0) {
+			std::fill(dataOut.begin(), dataOut.end(), T{ 0 });
+			for (unsigned int i = 0; i < dim; ++i)
+				dataOut[i * dim + i] = T{ 1 };
+			return false;
+		}
+
+		/* Swap rows to bring the pivot in the right location. */
+		matrix.swap(pivot, row);
+
+		/* Process all rows below the pivot to zero the pivot column. */
+		const T pivotValue = matrix(pivot, pivot);
+
+		for (unsigned int i = pivot + 1; i < dim; ++i) {
+			const T factor = matrix(i, pivot) / pivotValue;
+
+			/*
+			 * We know the element in the pivot column will be 0,
+			 * hardcode it instead of computing it.
+			 */
+			matrix(i, pivot) = T{ 0 };
+
+			for (unsigned int j = pivot + 1; j < dim * 2; ++j)
+				matrix(i, j) -= matrix(pivot, j) * factor;
+		}
+	}
+
+	/*
+	 * Then diagonalize the input, walking the diagonal backwards. There's
+	 * no need to update the input matrix, as all the values we would write
+	 * in the top-right triangle aren't used in further calculations (and
+	 * would all by definition be zero).
+	 */
+	for (unsigned int pivot = dim - 1; pivot > 0; --pivot) {
+		const T pivotValue = matrix(pivot, pivot);
+
+		for (unsigned int i = 0; i < pivot; ++i) {
+			const T factor = matrix(i, pivot) / pivotValue;
+
+			for (unsigned int j = dim; j < dim * 2; ++j)
+				matrix(i, j) -= matrix(pivot, j) * factor;
+		}
+	}
+
+	/*
+	 * Finally, normalize the diagonal and store the result in the output
+	 * data.
+	 */
+	for (unsigned int i = 0; i < dim; ++i) {
+		const T factor = matrix(i, i);
+
+		for (unsigned int j = 0; j < dim; ++j)
+			dataOut[i * dim + j] = matrix(i, j + dim) / factor;
+	}
+
+	return true;
+}
+
+template bool matrixInvert<float>(Span<const float> dataIn, Span<float> dataOut,
+				  unsigned int dim, Span<float> scratchBuffer,
+				  Span<unsigned int> swapBuffer);
+template bool matrixInvert<double>(Span<const double> data, Span<double> dataOut,
+				   unsigned int dim, Span<double> scratchBuffer,
+				   Span<unsigned int> swapBuffer);
+
 /*
  * The YAML data shall be a list of numerical values. Its size shall be equal
  * to the product of the number of rows and columns of the matrix (Rows x
diff -pruN 0.5.0-1/src/libcamera/media_device.cpp 0.5.2-2/src/libcamera/media_device.cpp
--- 0.5.0-1/src/libcamera/media_device.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/media_device.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -794,7 +794,7 @@ void MediaDevice::fixupEntityFlags(struc
  * low-level link setup as it performs no checks on the validity of the \a
  * flags, and assumes that the supplied \a flags are valid for the link (e.g.
  * immutable links cannot be disabled).
-*
+ *
  * \sa MediaLink::setEnabled(bool enable)
  *
  * \return 0 on success or a negative error code otherwise
@@ -829,4 +829,26 @@ int MediaDevice::setupLink(const MediaLi
 	return 0;
 }
 
+/**
+ * \brief Identify all entities of a common function in the MediaDevice
+ * \param[in] function The entity function to search for
+ *
+ * Search all entities within the graph of the MediaDevice and return
+ * a vector of those which match the given function.
+ *
+ * \return A vector of matching entities
+ */
+std::vector<MediaEntity *> MediaDevice::locateEntities(unsigned int function)
+{
+	std::vector<MediaEntity *> found;
+
+	/* Gather all the entities matching the function they expose. */
+	for (MediaEntity *entity : entities()) {
+		if (entity->function() == function)
+			found.push_back(entity);
+	}
+
+	return found;
+}
+
 } /* namespace libcamera */
diff -pruN 0.5.0-1/src/libcamera/media_pipeline.cpp 0.5.2-2/src/libcamera/media_pipeline.cpp
--- 0.5.0-1/src/libcamera/media_pipeline.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/src/libcamera/media_pipeline.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Media pipeline support
+ */
+
+#include "libcamera/internal/media_pipeline.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <queue>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <linux/media.h>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/media_device.h"
+#include "libcamera/internal/media_object.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+
+/**
+ * \file media_pipeline.h
+ * \brief Provide a representation of a pipeline of devices using the Media
+ * Controller.
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(MediaPipeline)
+
+/**
+ * \class MediaPipeline
+ * \brief The MediaPipeline represents a set of entities that together form a
+ * data path for stream data.
+ *
+ * A MediaPipeline instance is constructed from a sink and a source between
+ * two entities in a media graph.
+ */
+
+/**
+ * \brief Retrieve all source pads connected to a sink pad through active routes
+ *
+ * Examine the entity using the V4L2 Subdevice Routing API to collect all the
+ * source pads which are connected with an active route to the sink pad.
+ *
+ * \return A vector of source MediaPads
+ */
+static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink)
+{
+	MediaEntity *entity = sink->entity();
+	std::unique_ptr<V4L2Subdevice> subdev =
+		std::make_unique<V4L2Subdevice>(entity);
+
+	int ret = subdev->open();
+	if (ret < 0)
+		return {};
+
+	V4L2Subdevice::Routing routing = {};
+	ret = subdev->getRouting(&routing, V4L2Subdevice::ActiveFormat);
+	if (ret < 0)
+		return {};
+
+	std::vector<const MediaPad *> pads;
+
+	for (const V4L2Subdevice::Route &route : routing) {
+		if (sink->index() != route.sink.pad ||
+		    !(route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+			continue;
+
+		const MediaPad *pad = entity->getPadByIndex(route.source.pad);
+		if (!pad) {
+			LOG(MediaPipeline, Error)
+				<< "Entity " << entity->name()
+				<< " has invalid route source pad "
+				<< route.source.pad;
+			return {};
+		}
+
+		pads.push_back(pad);
+	}
+
+	return pads;
+}
+
+/**
+ * \brief Find the path from source to sink
+ *
+ * Starting from a source entity, determine the shortest path to the target
+ * described by \a sink.
+ *
+ * If \a sink can not be found, or a route from source to sink can not be
+ * achieved an error of -ENOLINK will be returned.
+ *
+ * When successful, the MediaPipeline will internally store the representation
+ * of entities and links to describe the path between the two entities.
+ *
+ * \return 0 on success, a negative errno otherwise
+ */
+int MediaPipeline::init(MediaEntity *source, std::string_view sink)
+{
+	/*
+	 * Find the shortest path between from the Camera Sensor and the
+	 * target entity.
+	 */
+	std::unordered_set<MediaEntity *> visited;
+	std::queue<std::tuple<MediaEntity *, MediaPad *>> queue;
+
+	/* Remember at each entity where we came from. */
+	std::unordered_map<MediaEntity *, Entity> parents;
+	MediaEntity *entity = nullptr;
+	MediaEntity *target = nullptr;
+	MediaPad *sinkPad;
+
+	queue.push({ source, nullptr });
+
+	while (!queue.empty()) {
+		std::tie(entity, sinkPad) = queue.front();
+		queue.pop();
+
+		/* Found the target device. */
+		if (entity->name() == sink) {
+			LOG(MediaPipeline, Debug)
+				<< "Found Pipeline target " << entity->name();
+			target = entity;
+			break;
+		}
+
+		visited.insert(entity);
+
+		/*
+		 * Add direct downstream entities to the search queue. If the
+		 * current entity supports the subdev internal routing API,
+		 * restrict the search to downstream entities reachable through
+		 * active routes.
+		 */
+
+		std::vector<const MediaPad *> pads;
+		bool supportsRouting = false;
+
+		if (sinkPad) {
+			pads = routedSourcePads(sinkPad);
+			if (!pads.empty())
+				supportsRouting = true;
+		}
+
+		if (pads.empty()) {
+			for (const MediaPad *pad : entity->pads()) {
+				if (!(pad->flags() & MEDIA_PAD_FL_SOURCE))
+					continue;
+				pads.push_back(pad);
+			}
+		}
+
+		for (const MediaPad *pad : pads) {
+			for (MediaLink *link : pad->links()) {
+				MediaEntity *next = link->sink()->entity();
+				if (visited.find(next) == visited.end()) {
+					queue.push({ next, link->sink() });
+
+					Entity e{ entity, supportsRouting, sinkPad, pad, link };
+					parents.insert({ next, e });
+				}
+			}
+		}
+	}
+
+	if (!target) {
+		LOG(MediaPipeline, Error)
+			<< "Failed to connect " << source->name()
+			<< " to " << sink;
+		return -ENOLINK;
+	}
+
+	/*
+	 * With the parents, we can follow back our way from the capture device
+	 * to the sensor. Store all the entities in the pipeline, from the
+	 * camera sensor to the video node, in entities_.
+	 */
+	entities_.push_front({ entity, false, sinkPad, nullptr, nullptr });
+
+	for (auto it = parents.find(entity); it != parents.end();
+	     it = parents.find(entity)) {
+		const Entity &e = it->second;
+		entities_.push_front(e);
+		entity = e.entity;
+	}
+
+	LOG(MediaPipeline, Info)
+		<< "Found pipeline: "
+		<< utils::join(entities_, " -> ",
+			       [](const Entity &e) {
+				       std::string s = "[";
+				       if (e.sink)
+					       s += std::to_string(e.sink->index()) + "|";
+				       s += e.entity->name();
+				       if (e.source)
+					       s += "|" + std::to_string(e.source->index());
+				       s += "]";
+				       return s;
+			       });
+
+	return 0;
+}
+
+/**
+ * \brief Initialise and enable all links through the MediaPipeline
+ * \return 0 on success, or a negative errno otherwise
+ */
+int MediaPipeline::initLinks()
+{
+	int ret = 0;
+
+	MediaLink *sinkLink = nullptr;
+	for (Entity &e : entities_) {
+		/* Sensor entities have no connected sink. */
+		if (!sinkLink) {
+			sinkLink = e.sourceLink;
+			continue;
+		}
+
+		LOG(MediaPipeline, Debug) << "Enabling : " << *sinkLink;
+
+		if (!(sinkLink->flags() & MEDIA_LNK_FL_ENABLED)) {
+			ret = sinkLink->setEnabled(true);
+			if (ret < 0)
+				return ret;
+		}
+
+		sinkLink = e.sourceLink;
+	}
+
+	return ret;
+}
+
+/**
+ * \brief Configure the entities of this MediaPipeline
+ *
+ * Propagate formats through each of the entities of the Pipeline, validating
+ * that each one was not adjusted by the driver from the desired format.
+ *
+ * \return 0 on success or a negative errno otherwise
+ */
+int MediaPipeline::configure(CameraSensor *sensor, V4L2SubdeviceFormat *format)
+{
+	int ret;
+
+	for (const Entity &e : entities_) {
+		/* The sensor is configured through the CameraSensor */
+		if (!e.sourceLink)
+			break;
+
+		MediaLink *link = e.sourceLink;
+		MediaPad *source = link->source();
+		MediaPad *sink = link->sink();
+
+		/* 'format' already contains the sensor configuration */
+		if (source->entity() != sensor->entity()) {
+			/* \todo Add MediaDevice cache to reduce FD pressure */
+			V4L2Subdevice subdev(source->entity());
+			ret = subdev.open();
+			if (ret)
+				return ret;
+
+			ret = subdev.getFormat(source->index(), format);
+			if (ret < 0)
+				return ret;
+		}
+
+		V4L2SubdeviceFormat sourceFormat = *format;
+		/* \todo Add MediaDevice cache to reduce FD pressure */
+		V4L2Subdevice subdev(sink->entity());
+		ret = subdev.open();
+		if (ret)
+			return ret;
+
+		ret = subdev.setFormat(sink->index(), format);
+		if (ret < 0)
+			return ret;
+
+		if (format->code != sourceFormat.code ||
+		    format->size != sourceFormat.size) {
+			LOG(MediaPipeline, Debug)
+				<< "Source '" << *source
+				<< " produces " << sourceFormat
+				<< ", sink '" << *sink
+				<< " requires " << *format;
+			return -EINVAL;
+		}
+
+		LOG(MediaPipeline, Debug)
+			<< "Link " << *link << " configured with format "
+			<< *format;
+	}
+
+	return 0;
+}
+
+} /* namespace libcamera */
diff -pruN 0.5.0-1/src/libcamera/meson.build 0.5.2-2/src/libcamera/meson.build
--- 0.5.0-1/src/libcamera/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -21,6 +21,7 @@ libcamera_internal_sources = files([
     'byte_stream_buffer.cpp',
     'camera_controls.cpp',
     'camera_lens.cpp',
+    'clock_recovery.cpp',
     'control_serializer.cpp',
     'control_validator.cpp',
     'converter.cpp',
@@ -43,6 +44,7 @@ libcamera_internal_sources = files([
     'matrix.cpp',
     'media_device.cpp',
     'media_object.cpp',
+    'media_pipeline.cpp',
     'pipeline_handler.cpp',
     'process.cpp',
     'pub_key.cpp',
@@ -82,7 +84,10 @@ if not cc.has_function('dlopen')
     libdl = cc.find_library('dl')
 endif
 libudev = dependency('libudev', required : get_option('udev'))
-libyaml = dependency('yaml-0.1', required : false)
+libyaml = dependency('yaml-0.1', default_options : [
+    'default_library=static',
+    'werror=false',
+])
 
 # Use one of gnutls or libcrypto (provided by OpenSSL), trying gnutls first.
 libcrypto = dependency('gnutls', required : false)
@@ -118,17 +123,6 @@ if libudev.found()
     ])
 endif
 
-# Fallback to a subproject if libyaml isn't found, as it's not packaged in AOSP.
-if not libyaml.found()
-    cmake = import('cmake')
-
-    libyaml_vars = cmake.subproject_options()
-    libyaml_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'})
-    libyaml_vars.append_compile_args('c', '-Wno-unused-value')
-    libyaml_wrap = cmake.subproject('libyaml', options : libyaml_vars)
-    libyaml = libyaml_wrap.dependency('yaml')
-endif
-
 control_sources = []
 
 controls_mode_files = {
@@ -155,6 +149,7 @@ foreach mode, inout_files : controls_mod
                                      command : [gen_controls, '-o', '@OUTPUT@',
                                                 '--mode', mode, '-t', template_file,
                                                 '-r', ranges_file, '@INPUT@'],
+                                     depend_files : [py_mod_controls],
                                      env : py_build_env)
 endforeach
 
diff -pruN 0.5.0-1/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp 0.5.2-2/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
--- 0.5.0-1/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -40,14 +40,13 @@ class PipelineHandlerISI;
 class ISICameraData : public Camera::Private
 {
 public:
-	ISICameraData(PipelineHandler *ph)
+	/* Maximum amount of streams (i.e. pipes) per camera. */
+	static constexpr unsigned int kNumStreams = 3;
+
+	ISICameraData(PipelineHandler *ph, unsigned int numStreams)
 		: Camera::Private(ph)
 	{
-		/*
-		 * \todo Assume 2 channels only for now, as that's the number of
-		 * available channels on i.MX8MP.
-		 */
-		streams_.resize(2);
+		streams_.resize(std::min(kNumStreams, numStreams));
 	}
 
 	PipelineHandlerISI *pipe();
@@ -56,7 +55,7 @@ public:
 
 	unsigned int pipeIndex(const Stream *stream)
 	{
-		return stream - &*streams_.begin();
+		return stream - &*streams_.begin() + xbarSourceOffset_;
 	}
 
 	unsigned int getRawMediaBusFormat(PixelFormat *pixelFormat) const;
@@ -70,7 +69,8 @@ public:
 
 	std::vector<Stream *> enabledStreams_;
 
-	unsigned int xbarSink_;
+	unsigned int xbarSink_ = 0;
+	unsigned int xbarSourceOffset_ = 0;
 };
 
 class ISICameraConfiguration : public CameraConfiguration
@@ -366,7 +366,6 @@ ISICameraConfiguration::validateRaw(std:
 	 * Make sure the requested RAW format is supported by the
 	 * pipeline, otherwise adjust it.
 	 */
-	std::vector<unsigned int> mbusCodes = data_->sensor_->mbusCodes();
 	StreamConfiguration &rawConfig = config_[0];
 	PixelFormat rawFormat = rawConfig.pixelFormat;
 
@@ -762,30 +761,28 @@ PipelineHandlerISI::generateConfiguratio
 		 */
 		StreamConfiguration cfg;
 
-                switch (role) {
-                case StreamRole::StillCapture:
-                case StreamRole::Viewfinder:
-                case StreamRole::VideoRecording: {
-                        Size size = role == StreamRole::StillCapture
-                                  ? data->sensor_->resolution()
-                                  : PipelineHandlerISI::kPreviewSize;
-                        cfg = generateYUVConfiguration(camera, size);
-                        if (cfg.pixelFormat.isValid())
-                                break;
-
-
-                        /*
-                         * Fallback to use a Bayer format if that's what the
-                         * sensor supports.
-                         */
-                        [[fallthrough]];
-
-		 }
-
-                case StreamRole::Raw: {
-                        cfg = generateRawConfiguration(camera);
-                        break;
-                }
+		switch (role) {
+		case StreamRole::StillCapture:
+		case StreamRole::Viewfinder:
+		case StreamRole::VideoRecording: {
+			Size size = role == StreamRole::StillCapture
+				  ? data->sensor_->resolution()
+				  : PipelineHandlerISI::kPreviewSize;
+			cfg = generateYUVConfiguration(camera, size);
+			if (cfg.pixelFormat.isValid())
+				break;
+
+			/*
+			 * Fallback to use a Bayer format if that's what the
+			 * sensor supports.
+			 */
+			[[fallthrough]];
+		}
+
+		case StreamRole::Raw: {
+			cfg = generateRawConfiguration(camera);
+			break;
+		}
 
 		default:
 			LOG(ISI, Error) << "Requested stream role not supported: " << role;
@@ -811,34 +808,9 @@ int PipelineHandlerISI::configure(Camera
 	ISICameraConfiguration *camConfig = static_cast<ISICameraConfiguration *>(c);
 	ISICameraData *data = cameraData(camera);
 
-	/* All links are immutable except the sensor -> csis link. */
-	const MediaPad *sensorSrc = data->sensor_->entity()->getPadByIndex(0);
-	sensorSrc->links()[0]->setEnabled(true);
-
-	/*
-	 * Reset the crossbar switch routing and enable one route for each
-	 * requested stream configuration.
-	 *
-	 * \todo Handle concurrent usage of multiple cameras by adjusting the
-	 * routing table instead of resetting it.
-	 */
-	V4L2Subdevice::Routing routing = {};
-	unsigned int xbarFirstSource = crossbar_->entity()->pads().size() / 2 + 1;
-
-	for (const auto &[idx, config] : utils::enumerate(*c)) {
-		uint32_t sourcePad = xbarFirstSource + idx;
-		routing.emplace_back(V4L2Subdevice::Stream{ data->xbarSink_, 0 },
-				     V4L2Subdevice::Stream{ sourcePad, 0 },
-				     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
-	}
-
-	int ret = crossbar_->setRouting(&routing, V4L2Subdevice::ActiveFormat);
-	if (ret)
-		return ret;
-
-	/* Apply format to the sensor and CSIS receiver. */
+	/* Apply format to the sensor, CSIS receiver and crossbar sink pad. */
 	V4L2SubdeviceFormat format = camConfig->sensorFormat_;
-	ret = data->sensor_->setFormat(&format);
+	int ret = data->sensor_->setFormat(&format);
 	if (ret)
 		return ret;
 
@@ -850,10 +822,17 @@ int PipelineHandlerISI::configure(Camera
 	if (ret)
 		return ret;
 
-	/* Now configure the ISI and video node instances, one per stream. */
-	data->enabledStreams_.clear();
-	for (const auto &config : *c) {
-		Pipe *pipe = pipeFromStream(camera, config.stream());
+	/*
+	 * As links on the output of the crossbar switch are immutable, the
+	 * routing table configured at match() time creates a media pipeline
+	 * that includes all the ISI pipelines corresponding to streams of this
+	 * camera, regardless of whether or not the streams are used in the
+	 * camera configuration. Set the format on the sink pad of all
+	 * corresponding ISI pipelines to avoid link validation failures when
+	 * starting streaming on the media pipeline.
+	 */
+	for (unsigned i = 0; i < data->streams_.size(); i++) {
+		Pipe *pipe = &pipes_.at(data->xbarSourceOffset_ + i);
 
 		/*
 		 * Set the format on the ISI sink pad: it must match what is
@@ -862,6 +841,15 @@ int PipelineHandlerISI::configure(Camera
 		ret = pipe->isi->setFormat(0, &format);
 		if (ret)
 			return ret;
+	}
+
+	/*
+	 * Now configure the ISI pipeline source pad and video node instances,
+	 * one per enabled stream.
+	 */
+	data->enabledStreams_.clear();
+	for (const auto &config : *c) {
+		Pipe *pipe = pipeFromStream(camera, config.stream());
 
 		/*
 		 * Configure the ISI sink compose rectangle to downscale the
@@ -973,6 +961,20 @@ bool PipelineHandlerISI::match(DeviceEnu
 	if (!isiDev_)
 		return false;
 
+	/* Count the number of sensors, to create one camera per sensor. */
+	unsigned cameraCount = 0;
+	for (MediaEntity *entity : isiDev_->entities()) {
+		if (entity->function() != MEDIA_ENT_F_CAM_SENSOR)
+			continue;
+
+		cameraCount++;
+	}
+
+	if (!cameraCount) {
+		LOG(ISI, Error) << "No camera sensor found";
+		return false;
+	}
+
 	/*
 	 * Acquire the subdevs and video nodes for the crossbar switch and the
 	 * processing pipelines.
@@ -1006,7 +1008,7 @@ bool PipelineHandlerISI::match(DeviceEnu
 
 		ret = capture->open();
 		if (ret)
-			return ret;
+			return false;
 
 		pipes_.push_back({ std::move(isi), std::move(capture) });
 	}
@@ -1016,12 +1018,24 @@ bool PipelineHandlerISI::match(DeviceEnu
 		return false;
 	}
 
+	if (cameraCount > pipes_.size()) {
+		LOG(ISI, Error) << "Too many cameras";
+		return false;
+	}
+
 	/*
 	 * Loop over all the crossbar switch sink pads to find connected CSI-2
 	 * receivers and camera sensors.
+	 *
+	 * In multicamera case, limit maximum amount of streams to allow all
+	 * sensors to get at least one dedicated pipe.
 	 */
 	unsigned int numCameras = 0;
 	unsigned int numSinks = 0;
+	const unsigned int xbarFirstSource = crossbar_->entity()->pads().size() - pipes_.size();
+	const unsigned int maxStreams = pipes_.size() / cameraCount;
+	V4L2Subdevice::Routing routing = {};
+
 	for (MediaPad *pad : crossbar_->entity()->pads()) {
 		unsigned int sink = numSinks;
 
@@ -1052,13 +1066,23 @@ bool PipelineHandlerISI::match(DeviceEnu
 			continue;
 		}
 
+		/* All links are immutable except the sensor -> csis link. */
+		const MediaPad *sensorSrc = sensor->getPadByIndex(0);
+		sensorSrc->links()[0]->setEnabled(true);
+
 		/* Create the camera data. */
 		std::unique_ptr<ISICameraData> data =
-			std::make_unique<ISICameraData>(this);
+			std::make_unique<ISICameraData>(this, maxStreams);
 
 		data->sensor_ = CameraSensorFactoryBase::create(sensor);
 		data->csis_ = std::make_unique<V4L2Subdevice>(csi);
 		data->xbarSink_ = sink;
+		data->xbarSourceOffset_ = numCameras * data->streams_.size();
+
+		LOG(ISI, Debug)
+			<< "cam" << numCameras
+			<< " streams " << data->streams_.size()
+			<< " offset " << data->xbarSourceOffset_;
 
 		ret = data->init();
 		if (ret) {
@@ -1073,6 +1097,14 @@ bool PipelineHandlerISI::match(DeviceEnu
 			       std::inserter(streams, streams.end()),
 			       [](Stream &s) { return &s; });
 
+		/*  Add routes to the crossbar switch routing table. */
+		for (unsigned i = 0; i < data->streams_.size(); i++) {
+			unsigned int sourcePad = xbarFirstSource + data->xbarSourceOffset_ + i;
+			routing.emplace_back(V4L2Subdevice::Stream{ data->xbarSink_, 0 },
+					     V4L2Subdevice::Stream{ sourcePad, 0 },
+					     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+		}
+
 		std::shared_ptr<Camera> camera =
 			Camera::create(std::move(data), id, streams);
 
@@ -1080,6 +1112,10 @@ bool PipelineHandlerISI::match(DeviceEnu
 		numCameras++;
 	}
 
+	ret = crossbar_->setRouting(&routing, V4L2Subdevice::ActiveFormat);
+	if (ret)
+		return false;
+
 	return numCameras > 0;
 }
 
diff -pruN 0.5.0-1/src/libcamera/pipeline/ipu3/cio2.h 0.5.2-2/src/libcamera/pipeline/ipu3/cio2.h
--- 0.5.0-1/src/libcamera/pipeline/ipu3/cio2.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/ipu3/cio2.h	2025-08-07 13:46:17.000000000 +0000
@@ -66,8 +66,6 @@ public:
 private:
 	void freeBuffers();
 
-	void cio2BufferReady(FrameBuffer *buffer);
-
 	std::unique_ptr<CameraSensor> sensor_;
 	std::unique_ptr<V4L2Subdevice> csi2_;
 	std::unique_ptr<V4L2VideoDevice> output_;
diff -pruN 0.5.0-1/src/libcamera/pipeline/mali-c55/mali-c55.cpp 0.5.2-2/src/libcamera/pipeline/mali-c55/mali-c55.cpp
--- 0.5.0-1/src/libcamera/pipeline/mali-c55/mali-c55.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/mali-c55/mali-c55.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -131,8 +131,6 @@ private:
 	void setSensorControls(const ControlList &sensorControls);
 
 	std::string id_;
-	std::vector<unsigned int> tpgCodes_;
-	std::vector<Size> tpgSizes_;
 	Size tpgResolution_;
 };
 
@@ -159,15 +157,16 @@ int MaliC55CameraData::init()
 	 */
 	sensor_ = CameraSensorFactoryBase::create(entity_);
 	if (!sensor_)
-		return ret;
+		return -ENODEV;
 
 	const MediaPad *sourcePad = entity_->getPadByIndex(0);
 	MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity();
 
 	csi_ = std::make_unique<V4L2Subdevice>(csiEntity);
-	if (csi_->open()) {
+	ret = csi_->open();
+	if (ret) {
 		LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice";
-		return false;
+		return ret;
 	}
 
 	return 0;
@@ -180,16 +179,15 @@ void MaliC55CameraData::initTPGData()
 	if (formats.empty())
 		return;
 
-	tpgCodes_ = utils::map_keys(formats);
-	std::sort(tpgCodes_.begin(), tpgCodes_.end());
+	std::vector<Size> tpgSizes;
 
 	for (const auto &format : formats) {
 		const std::vector<SizeRange> &ranges = format.second;
-		std::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes_),
+		std::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes),
 			       [](const SizeRange &range) { return range.max; });
 	}
 
-	tpgResolution_ = tpgSizes_.back();
+	tpgResolution_ = tpgSizes.back();
 }
 
 void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)
@@ -620,7 +618,7 @@ public:
 	void imageBufferReady(FrameBuffer *buffer);
 	void paramsBufferReady(FrameBuffer *buffer);
 	void statsBufferReady(FrameBuffer *buffer);
-	void paramsComputed(unsigned int requestId);
+	void paramsComputed(unsigned int requestId, uint32_t bytesused);
 	void statsProcessed(unsigned int requestId, const ControlList &metadata);
 
 	bool match(DeviceEnumerator *enumerator) override;
@@ -1496,7 +1494,7 @@ void PipelineHandlerMaliC55::statsBuffer
 				 sensorControls);
 }
 
-void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId)
+void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId, uint32_t bytesused)
 {
 	MaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];
 	Request *request = frameInfo.request;
@@ -1507,8 +1505,7 @@ void PipelineHandlerMaliC55::paramsCompu
 	 * video devices.
 	 */
 
-	frameInfo.paramBuffer->_d()->metadata().planes()[0].bytesused =
-		sizeof(struct mali_c55_params_buffer);
+	frameInfo.paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused;
 	params_->queueBuffer(frameInfo.paramBuffer);
 	stats_->queueBuffer(frameInfo.statBuffer);
 
@@ -1712,7 +1709,7 @@ bool PipelineHandlerMaliC55::match(Devic
 	 *
 	 * MEDIA_ENT_F_CAM_SENSOR - The test pattern generator
 	 * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver
-	 * MEDIA_ENT_F_IO_V4L - An input device
+	 * MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER - An input device
 	 *
 	 * The last one will be unsupported for now. The TPG is relatively easy,
 	 * we just register a Camera for it. If we have a CSI-2 receiver we need
@@ -1738,7 +1735,7 @@ bool PipelineHandlerMaliC55::match(Devic
 				return registered;
 
 			break;
-		case MEDIA_ENT_F_IO_V4L:
+		case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER:
 			LOG(MaliC55, Warning) << "Memory input not yet supported";
 			break;
 		default:
diff -pruN 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1.cpp 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1.cpp
--- 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -42,6 +42,7 @@
 #include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/ipa_manager.h"
 #include "libcamera/internal/media_device.h"
+#include "libcamera/internal/media_pipeline.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/v4l2_subdevice.h"
 #include "libcamera/internal/v4l2_videodevice.h"
@@ -116,6 +117,11 @@ public:
 
 	ControlInfoMap ipaControls_;
 
+	/*
+	 * All entities in the pipeline, from the camera sensor to the RKISP1.
+	 */
+	MediaPipeline pipe_;
+
 private:
 	void paramsComputed(unsigned int frame, unsigned int bytesused);
 	void setSensorControls(unsigned int frame,
@@ -149,6 +155,24 @@ private:
 	Transform combinedTransform_;
 };
 
+namespace {
+
+/*
+ * Maximum number of requests that shall be queued into the pipeline to keep
+ * the regulation fast.
+ * \todo This needs revisiting as soon as buffers got decoupled from requests
+ * and/or a fast path for controls was implemented.
+ */
+static constexpr unsigned int kRkISP1MaxQueuedRequests = 4;
+
+/*
+ * This many internal buffers (or rather parameter and statistics buffer
+ * pairs) ensures that the pipeline runs smoothly, without frame drops.
+ */
+static constexpr unsigned int kRkISP1MinBufferCount = 4;
+
+} /* namespace */
+
 class PipelineHandlerRkISP1 : public PipelineHandler
 {
 public:
@@ -180,8 +204,7 @@ private:
 	friend RkISP1CameraConfiguration;
 	friend RkISP1Frames;
 
-	int initLinks(Camera *camera, const CameraSensor *sensor,
-		      const RkISP1CameraConfiguration &config);
+	int initLinks(Camera *camera, const RkISP1CameraConfiguration &config);
 	int createCamera(MediaEntity *sensor);
 	void tryCompleteRequest(RkISP1FrameInfo *info);
 	void imageBufferReady(FrameBuffer *buffer);
@@ -199,7 +222,6 @@ private:
 	std::unique_ptr<V4L2Subdevice> isp_;
 	std::unique_ptr<V4L2VideoDevice> param_;
 	std::unique_ptr<V4L2VideoDevice> stat_;
-	std::unique_ptr<V4L2Subdevice> csi_;
 
 	bool hasSelfPath_;
 	bool isRaw_;
@@ -223,8 +245,6 @@ private:
 	std::queue<FrameBuffer *> availableStatBuffers_;
 
 	Camera *activeCamera_;
-
-	const MediaPad *ispSink_;
 };
 
 RkISP1Frames::RkISP1Frames(PipelineHandler *pipe)
@@ -594,6 +614,12 @@ CameraConfiguration::Status RkISP1Camera
 				return false;
 		}
 
+		if (tryCfg.bufferCount < kRkISP1MinBufferCount) {
+			if (expectedStatus == Valid)
+				return false;
+			tryCfg.bufferCount = kRkISP1MinBufferCount;
+		}
+
 		cfg = tryCfg;
 		cfg.setStream(stream);
 		return true;
@@ -682,7 +708,8 @@ CameraConfiguration::Status RkISP1Camera
  */
 
 PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
-	: PipelineHandler(manager), hasSelfPath_(true), useDewarper_(false)
+	: PipelineHandler(manager, kRkISP1MaxQueuedRequests),
+	  hasSelfPath_(true), useDewarper_(false)
 {
 }
 
@@ -782,6 +809,7 @@ PipelineHandlerRkISP1::generateConfigura
 			return nullptr;
 
 		cfg.colorSpace = colorSpace;
+		cfg.bufferCount = kRkISP1MinBufferCount;
 		config->addConfiguration(cfg);
 	}
 
@@ -798,7 +826,7 @@ int PipelineHandlerRkISP1::configure(Cam
 	CameraSensor *sensor = data->sensor_.get();
 	int ret;
 
-	ret = initLinks(camera, sensor, *config);
+	ret = initLinks(camera, *config);
 	if (ret)
 		return ret;
 
@@ -821,12 +849,12 @@ int PipelineHandlerRkISP1::configure(Cam
 
 	LOG(RkISP1, Debug) << "Sensor configured with " << format;
 
-	if (csi_) {
-		ret = csi_->setFormat(0, &format);
-		if (ret < 0)
-			return ret;
-	}
+	/* Propagate format through the internal media pipeline up to the ISP */
+	ret = data->pipe_.configure(sensor, &format);
+	if (ret < 0)
+		return ret;
 
+	LOG(RkISP1, Debug) << "Configuring ISP with : " << format;
 	ret = isp_->setFormat(0, &format);
 	if (ret < 0)
 		return ret;
@@ -981,24 +1009,19 @@ int PipelineHandlerRkISP1::allocateBuffe
 	unsigned int ipaBufferId = 1;
 	int ret;
 
-	unsigned int maxCount = std::max({
-		data->mainPathStream_.configuration().bufferCount,
-		data->selfPathStream_.configuration().bufferCount,
-	});
-
 	if (!isRaw_) {
-		ret = param_->allocateBuffers(maxCount, &paramBuffers_);
+		ret = param_->allocateBuffers(kRkISP1MinBufferCount, &paramBuffers_);
 		if (ret < 0)
 			goto error;
 
-		ret = stat_->allocateBuffers(maxCount, &statBuffers_);
+		ret = stat_->allocateBuffers(kRkISP1MinBufferCount, &statBuffers_);
 		if (ret < 0)
 			goto error;
 	}
 
 	/* If the dewarper is being used, allocate internal buffers for ISP. */
 	if (useDewarper_) {
-		ret = mainPath_.exportBuffers(maxCount, &mainPathBuffers_);
+		ret = mainPath_.exportBuffers(kRkISP1MinBufferCount, &mainPathBuffers_);
 		if (ret < 0)
 			goto error;
 
@@ -1115,14 +1138,14 @@ int PipelineHandlerRkISP1::start(Camera
 	}
 
 	if (data->mainPath_->isEnabled()) {
-		ret = mainPath_.start();
+		ret = mainPath_.start(data->mainPathStream_.configuration().bufferCount);
 		if (ret)
 			return ret;
 		actions += [&]() { mainPath_.stop(); };
 	}
 
 	if (hasSelfPath_ && data->selfPath_->isEnabled()) {
-		ret = selfPath_.start();
+		ret = selfPath_.start(data->selfPathStream_.configuration().bufferCount);
 		if (ret)
 			return ret;
 	}
@@ -1201,7 +1224,6 @@ int PipelineHandlerRkISP1::queueRequestD
  */
 
 int PipelineHandlerRkISP1::initLinks(Camera *camera,
-				     const CameraSensor *sensor,
 				     const RkISP1CameraConfiguration &config)
 {
 	RkISP1CameraData *data = cameraData(camera);
@@ -1212,31 +1234,16 @@ int PipelineHandlerRkISP1::initLinks(Cam
 		return ret;
 
 	/*
-	 * Configure the sensor links: enable the link corresponding to this
-	 * camera.
+	 * Configure the sensor links: enable the links corresponding to this
+	 * pipeline all the way up to the ISP, through any connected CSI receiver.
 	 */
-	for (MediaLink *link : ispSink_->links()) {
-		if (link->source()->entity() != sensor->entity())
-			continue;
-
-		LOG(RkISP1, Debug)
-			<< "Enabling link from sensor '"
-			<< link->source()->entity()->name()
-			<< "' to ISP";
-
-		ret = link->setEnabled(true);
-		if (ret < 0)
-			return ret;
-	}
-
-	if (csi_) {
-		MediaLink *link = isp_->entity()->getPadByIndex(0)->links().at(0);
-
-		ret = link->setEnabled(true);
-		if (ret < 0)
-			return ret;
+	ret = data->pipe_.initLinks();
+	if (ret) {
+		LOG(RkISP1, Error) << "Failed to set up pipe links";
+		return ret;
 	}
 
+	/* Configure the paths after the ISP */
 	for (const StreamConfiguration &cfg : config) {
 		if (cfg.stream() == &data->mainPathStream_)
 			ret = data->mainPath_->setEnabled(true);
@@ -1312,6 +1319,13 @@ int PipelineHandlerRkISP1::createCamera(
 		std::make_unique<RkISP1CameraData>(this, &mainPath_,
 						   hasSelfPath_ ? &selfPath_ : nullptr);
 
+	/* Identify the pipeline path between the sensor and the rkisp1_isp */
+	ret = data->pipe_.init(sensor, "rkisp1_isp");
+	if (ret) {
+		LOG(RkISP1, Error) << "Failed to identify path from sensor to sink";
+		return ret;
+	}
+
 	data->sensor_ = CameraSensorFactoryBase::create(sensor);
 	if (!data->sensor_)
 		return -ENODEV;
@@ -1325,7 +1339,7 @@ int PipelineHandlerRkISP1::createCamera(
 	std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
 		{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
 		{ V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
-		{ V4L2_CID_VBLANK, { 1, false } },
+		{ V4L2_CID_VBLANK, { delays.vblankDelay, false } },
 	};
 
 	data->delayedCtrls_ =
@@ -1347,6 +1361,7 @@ int PipelineHandlerRkISP1::createCamera(
 	const std::string &id = data->sensor_->id();
 	std::shared_ptr<Camera> camera =
 		Camera::create(std::move(data), id, streams);
+
 	registerCamera(std::move(camera));
 
 	return 0;
@@ -1354,8 +1369,6 @@ int PipelineHandlerRkISP1::createCamera(
 
 bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
 {
-	const MediaPad *pad;
-
 	DeviceMatch dm("rkisp1");
 	dm.add("rkisp1_isp");
 	dm.add("rkisp1_resizer_mainpath");
@@ -1380,22 +1393,6 @@ bool PipelineHandlerRkISP1::match(Device
 	if (isp_->open() < 0)
 		return false;
 
-	/* Locate and open the optional CSI-2 receiver. */
-	ispSink_ = isp_->entity()->getPadByIndex(0);
-	if (!ispSink_ || ispSink_->links().empty())
-		return false;
-
-	pad = ispSink_->links().at(0)->source();
-	if (pad->entity()->function() == MEDIA_ENT_F_VID_IF_BRIDGE) {
-		csi_ = std::make_unique<V4L2Subdevice>(pad->entity());
-		if (csi_->open() < 0)
-			return false;
-
-		ispSink_ = csi_->entity()->getPadByIndex(0);
-		if (!ispSink_)
-			return false;
-	}
-
 	/* Locate and open the stats and params video nodes. */
 	stat_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1_stats");
 	if (stat_->open() < 0)
@@ -1446,8 +1443,10 @@ bool PipelineHandlerRkISP1::match(Device
 	 * camera instance for each of them.
 	 */
 	bool registered = false;
-	for (MediaLink *link : ispSink_->links()) {
-		if (!createCamera(link->source()->entity()))
+
+	for (MediaEntity *entity : media_->locateEntities(MEDIA_ENT_F_CAM_SENSOR)) {
+		LOG(RkISP1, Debug) << "Identified " << entity->name();
+		if (!createCamera(entity))
 			registered = true;
 	}
 
diff -pruN 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
--- 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -54,8 +54,11 @@ const std::map<PixelFormat, uint32_t> fo
 
 } /* namespace */
 
-RkISP1Path::RkISP1Path(const char *name, const Span<const PixelFormat> &formats)
-	: name_(name), running_(false), formats_(formats), link_(nullptr)
+RkISP1Path::RkISP1Path(const char *name, const Span<const PixelFormat> &formats,
+		       const Size &minResolution, const Size &maxResolution)
+	: name_(name), running_(false), formats_(formats),
+	  minResolution_(minResolution), maxResolution_(maxResolution),
+	  link_(nullptr)
 {
 }
 
@@ -246,7 +249,6 @@ RkISP1Path::generateConfiguration(const
 	StreamConfiguration cfg(formats);
 	cfg.pixelFormat = format;
 	cfg.size = streamSize;
-	cfg.bufferCount = RKISP1_BUFFER_COUNT;
 
 	return cfg;
 }
@@ -380,7 +382,6 @@ RkISP1Path::validate(const CameraSensor
 
 	cfg->size.boundTo(maxResolution);
 	cfg->size.expandTo(minResolution);
-	cfg->bufferCount = RKISP1_BUFFER_COUNT;
 
 	V4L2DeviceFormat format;
 	format.fourcc = video_->toV4L2PixelFormat(cfg->pixelFormat);
@@ -477,15 +478,14 @@ int RkISP1Path::configure(const StreamCo
 	return 0;
 }
 
-int RkISP1Path::start()
+int RkISP1Path::start(unsigned int bufferCount)
 {
 	int ret;
 
 	if (running_)
 		return -EBUSY;
 
-	/* \todo Make buffer count user configurable. */
-	ret = video_->importBuffers(RKISP1_BUFFER_COUNT);
+	ret = video_->importBuffers(bufferCount);
 	if (ret)
 		return ret;
 
@@ -517,10 +517,12 @@ void RkISP1Path::stop()
 }
 
 /*
- * \todo Remove the hardcoded formats once all users will have migrated to a
- * recent enough kernel.
+ * \todo Remove the hardcoded resolutions and formats once kernels older than
+ * v6.4 will stop receiving LTS support (scheduled for December 2027 for v6.1).
  */
 namespace {
+constexpr Size RKISP1_RSZ_MP_SRC_MIN{ 32, 16 };
+constexpr Size RKISP1_RSZ_MP_SRC_MAX{ 4416, 3312 };
 constexpr std::array<PixelFormat, 18> RKISP1_RSZ_MP_FORMATS{
 	formats::YUYV,
 	formats::NV16,
@@ -542,6 +544,8 @@ constexpr std::array<PixelFormat, 18> RK
 	formats::SRGGB12,
 };
 
+constexpr Size RKISP1_RSZ_SP_SRC_MIN{ 32, 16 };
+constexpr Size RKISP1_RSZ_SP_SRC_MAX{ 1920, 1920 };
 constexpr std::array<PixelFormat, 8> RKISP1_RSZ_SP_FORMATS{
 	formats::YUYV,
 	formats::NV16,
@@ -555,12 +559,14 @@ constexpr std::array<PixelFormat, 8> RKI
 } /* namespace */
 
 RkISP1MainPath::RkISP1MainPath()
-	: RkISP1Path("main", RKISP1_RSZ_MP_FORMATS)
+	: RkISP1Path("main", RKISP1_RSZ_MP_FORMATS,
+		     RKISP1_RSZ_MP_SRC_MIN, RKISP1_RSZ_MP_SRC_MAX)
 {
 }
 
 RkISP1SelfPath::RkISP1SelfPath()
-	: RkISP1Path("self", RKISP1_RSZ_SP_FORMATS)
+	: RkISP1Path("self", RKISP1_RSZ_SP_FORMATS,
+		     RKISP1_RSZ_SP_SRC_MIN, RKISP1_RSZ_SP_SRC_MAX)
 {
 }
 
diff -pruN 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1_path.h 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1_path.h
--- 0.5.0-1/src/libcamera/pipeline/rkisp1/rkisp1_path.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rkisp1/rkisp1_path.h	2025-08-07 13:46:17.000000000 +0000
@@ -34,7 +34,8 @@ struct V4L2SubdeviceFormat;
 class RkISP1Path
 {
 public:
-	RkISP1Path(const char *name, const Span<const PixelFormat> &formats);
+	RkISP1Path(const char *name, const Span<const PixelFormat> &formats,
+		   const Size &minResolution, const Size &maxResolution);
 
 	bool init(MediaDevice *media);
 
@@ -57,7 +58,7 @@ public:
 		return video_->exportBuffers(bufferCount, buffers);
 	}
 
-	int start();
+	int start(unsigned int bufferCount);
 	void stop();
 
 	int queueBuffer(FrameBuffer *buffer) { return video_->queueBuffer(buffer); }
@@ -68,8 +69,6 @@ private:
 	void populateFormats();
 	Size filterSensorResolution(const CameraSensor *sensor);
 
-	static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
-
 	const char *name_;
 	bool running_;
 
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/common/pipeline_base.cpp 0.5.2-2/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
--- 0.5.0-1/src/libcamera/pipeline/rpi/common/pipeline_base.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/common/pipeline_base.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -659,9 +659,9 @@ int PipelineHandlerBase::start(Camera *c
 	if (!result.controls.empty())
 		data->setSensorControls(result.controls);
 
-	/* Configure the number of dropped frames required on startup. */
-	data->dropFrameCount_ = data->config_.disableStartupFrameDrops
-			      ? 0 : result.dropFrameCount;
+	/* Configure the number of startup and invalid frames reported by the IPA. */
+	data->startupFrameCount_ = result.startupFrameCount;
+	data->invalidFrameCount_ = result.invalidFrameCount;
 
 	for (auto const stream : data->streams_)
 		stream->resetBuffers();
@@ -678,7 +678,6 @@ int PipelineHandlerBase::start(Camera *c
 		data->buffersAllocated_ = true;
 	}
 
-	/* We need to set the dropFrameCount_ before queueing buffers. */
 	ret = queueAllBuffers(camera);
 	if (ret) {
 		LOG(RPI, Error) << "Failed to queue buffers";
@@ -686,6 +685,9 @@ int PipelineHandlerBase::start(Camera *c
 		return ret;
 	}
 
+	/* A good moment to add an initial clock sample. */
+	data->wallClockRecovery_.addSample();
+
 	/*
 	 * Reset the delayed controls with the gain and exposure values set by
 	 * the IPA.
@@ -804,7 +806,8 @@ int PipelineHandlerBase::registerCamera(
 	 * chain. There may be a cascade of devices in this chain!
 	 */
 	MediaLink *link = sensorEntity->getPadByIndex(0)->links()[0];
-	data->enumerateVideoDevices(link, frontendName);
+	if (!data->enumerateVideoDevices(link, frontendName))
+		return -EINVAL;
 
 	ipa::RPi::InitResult result;
 	if (data->loadIPA(&result)) {
@@ -894,28 +897,12 @@ int PipelineHandlerBase::queueAllBuffers
 	int ret;
 
 	for (auto const stream : data->streams_) {
-		if (!(stream->getFlags() & StreamFlag::External)) {
-			ret = stream->queueAllBuffers();
-			if (ret < 0)
-				return ret;
-		} else {
-			/*
-			 * For external streams, we must queue up a set of internal
-			 * buffers to handle the number of drop frames requested by
-			 * the IPA. This is done by passing nullptr in queueBuffer().
-			 *
-			 * The below queueBuffer() call will do nothing if there
-			 * are not enough internal buffers allocated, but this will
-			 * be handled by queuing the request for buffers in the
-			 * RPiStream object.
-			 */
-			unsigned int i;
-			for (i = 0; i < data->dropFrameCount_; i++) {
-				ret = stream->queueBuffer(nullptr);
-				if (ret)
-					return ret;
-			}
-		}
+		if (stream->getFlags() & StreamFlag::External)
+			continue;
+
+		ret = stream->queueAllBuffers();
+		if (ret < 0)
+			return ret;
 	}
 
 	return 0;
@@ -1032,16 +1019,20 @@ void CameraData::freeBuffers()
  *          | Sensor2 |   | Sensor3 |
  *          +---------+   +---------+
  */
-void CameraData::enumerateVideoDevices(MediaLink *link, const std::string &frontend)
+bool CameraData::enumerateVideoDevices(MediaLink *link, const std::string &frontend)
 {
 	const MediaPad *sinkPad = link->sink();
 	const MediaEntity *entity = sinkPad->entity();
 	bool frontendFound = false;
 
+	/* Once we reach the Frontend entity, we are done. */
+	if (link->sink()->entity()->name() == frontend)
+		return true;
+
 	/* We only deal with Video Mux and Bridge devices in cascade. */
 	if (entity->function() != MEDIA_ENT_F_VID_MUX &&
 	    entity->function() != MEDIA_ENT_F_VID_IF_BRIDGE)
-		return;
+		return false;
 
 	/* Find the source pad for this Video Mux or Bridge device. */
 	const MediaPad *sourcePad = nullptr;
@@ -1053,7 +1044,7 @@ void CameraData::enumerateVideoDevices(M
 			 * and this branch in the cascade.
 			 */
 			if (sourcePad)
-				return;
+				return false;
 
 			sourcePad = pad;
 		}
@@ -1070,12 +1061,9 @@ void CameraData::enumerateVideoDevices(M
 	 * other Video Mux and Bridge devices.
 	 */
 	for (MediaLink *l : sourcePad->links()) {
-		enumerateVideoDevices(l, frontend);
-		/* Once we reach the Frontend entity, we are done. */
-		if (l->sink()->entity()->name() == frontend) {
-			frontendFound = true;
+		frontendFound = enumerateVideoDevices(l, frontend);
+		if (frontendFound)
 			break;
-		}
 	}
 
 	/* This identifies the end of our entity enumeration recursion. */
@@ -1090,12 +1078,13 @@ void CameraData::enumerateVideoDevices(M
 			bridgeDevices_.clear();
 		}
 	}
+
+	return frontendFound;
 }
 
 int CameraData::loadPipelineConfiguration()
 {
 	config_ = {
-		.disableStartupFrameDrops = false,
 		.cameraTimeoutValue = 0,
 	};
 
@@ -1132,8 +1121,10 @@ int CameraData::loadPipelineConfiguratio
 
 	const YamlObject &phConfig = (*root)["pipeline_handler"];
 
-	config_.disableStartupFrameDrops =
-		phConfig["disable_startup_frame_drops"].get<bool>(config_.disableStartupFrameDrops);
+	if (phConfig.contains("disable_startup_frame_drops"))
+		LOG(RPI, Warning)
+			<< "The disable_startup_frame_drops key is now deprecated, "
+			<< "startup frames are now identified by the FrameMetadata::Status::FrameStartup flag";
 
 	config_.cameraTimeoutValue =
 		phConfig["camera_timeout_value_ms"].get<unsigned int>(config_.cameraTimeoutValue);
@@ -1412,7 +1403,15 @@ void CameraData::handleStreamBuffer(Fram
 	 * buffer back to the stream.
 	 */
 	Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front();
-	if (!dropFrameCount_ && request && request->findBuffer(stream) == buffer) {
+	if (request && request->findBuffer(stream) == buffer) {
+		FrameMetadata &md = buffer->_d()->metadata();
+
+		/* Mark the non-converged and invalid frames in the metadata. */
+		if (invalidFrameCount_)
+			md.status = FrameMetadata::Status::FrameError;
+		else if (startupFrameCount_)
+			md.status = FrameMetadata::Status::FrameStartup;
+
 		/*
 		 * Tag the buffer as completed, returning it to the
 		 * application.
@@ -1458,49 +1457,40 @@ void CameraData::handleState()
 
 void CameraData::checkRequestCompleted()
 {
-	bool requestCompleted = false;
-	/*
-	 * If we are dropping this frame, do not touch the request, simply
-	 * change the state to IDLE when ready.
-	 */
-	if (!dropFrameCount_) {
-		Request *request = requestQueue_.front();
-		if (request->hasPendingBuffers())
-			return;
+	Request *request = requestQueue_.front();
+	if (request->hasPendingBuffers())
+		return;
+
+	/* Must wait for metadata to be filled in before completing. */
+	if (state_ != State::IpaComplete)
+		return;
 
-		/* Must wait for metadata to be filled in before completing. */
-		if (state_ != State::IpaComplete)
-			return;
+	LOG(RPI, Debug) << "Completing request sequence: "
+			<< request->sequence();
 
-		LOG(RPI, Debug) << "Completing request sequence: "
-				<< request->sequence();
+	pipe()->completeRequest(request);
+	requestQueue_.pop();
 
-		pipe()->completeRequest(request);
-		requestQueue_.pop();
-		requestCompleted = true;
-	}
+	LOG(RPI, Debug) << "Going into Idle state";
+	state_ = State::Idle;
 
-	/*
-	 * Make sure we have three outputs completed in the case of a dropped
-	 * frame.
-	 */
-	if (state_ == State::IpaComplete &&
-	    ((ispOutputCount_ == ispOutputTotal_ && dropFrameCount_) ||
-	     requestCompleted)) {
-		LOG(RPI, Debug) << "Going into Idle state";
-		state_ = State::Idle;
-		if (dropFrameCount_) {
-			dropFrameCount_--;
-			LOG(RPI, Debug) << "Dropping frame at the request of the IPA ("
-					<< dropFrameCount_ << " left)";
-		}
+	if (invalidFrameCount_) {
+		invalidFrameCount_--;
+		LOG(RPI, Debug) << "Decrementing invalid frames to "
+				<< invalidFrameCount_;
+	} else if (startupFrameCount_) {
+		startupFrameCount_--;
+		LOG(RPI, Debug) << "Decrementing startup frames to "
+				<< startupFrameCount_;
 	}
 }
 
 void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request *request)
 {
-	request->metadata().set(controls::SensorTimestamp,
-				bufferControls.get(controls::SensorTimestamp).value_or(0));
+	if (auto x = bufferControls.get(controls::SensorTimestamp))
+		request->metadata().set(controls::SensorTimestamp, *x);
+	if (auto x = bufferControls.get(controls::FrameWallClock))
+		request->metadata().set(controls::FrameWallClock, *x);
 
 	if (cropParams_.size()) {
 		std::vector<Rectangle> crops;
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/common/pipeline_base.h 0.5.2-2/src/libcamera/pipeline/rpi/common/pipeline_base.h
--- 0.5.0-1/src/libcamera/pipeline/rpi/common/pipeline_base.h	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/common/pipeline_base.h	2025-08-07 13:46:17.000000000 +0000
@@ -20,6 +20,7 @@
 #include "libcamera/internal/bayer_format.h"
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/clock_recovery.h"
 #include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/media_object.h"
@@ -48,8 +49,7 @@ class CameraData : public Camera::Privat
 public:
 	CameraData(PipelineHandler *pipe)
 		: Camera::Private(pipe), state_(State::Stopped),
-		  dropFrameCount_(0), buffersAllocated_(false),
-		  ispOutputCount_(0), ispOutputTotal_(0)
+		  startupFrameCount_(0), invalidFrameCount_(0), buffersAllocated_(false)
 	{
 	}
 
@@ -68,7 +68,7 @@ public:
 	void freeBuffers();
 	virtual void platformFreeBuffers() = 0;
 
-	void enumerateVideoDevices(MediaLink *link, const std::string &frontend);
+	bool enumerateVideoDevices(MediaLink *link, const std::string &frontend);
 
 	int loadPipelineConfiguration();
 	int loadIPA(ipa::RPi::InitResult *result);
@@ -151,7 +151,8 @@ public:
 	/* Mapping of CropParams keyed by the output stream order in CameraConfiguration */
 	std::map<unsigned int, CropParams> cropParams_;
 
-	unsigned int dropFrameCount_;
+	unsigned int startupFrameCount_;
+	unsigned int invalidFrameCount_;
 
 	/*
 	 * If set, this stores the value that represets a gain of one for
@@ -164,11 +165,6 @@ public:
 
 	struct Config {
 		/*
-		 * Override any request from the IPA to drop a number of startup
-		 * frames.
-		 */
-		bool disableStartupFrameDrops;
-		/*
 		 * Override the camera timeout value calculated by the IPA based
 		 * on frame durations.
 		 */
@@ -177,15 +173,14 @@ public:
 
 	Config config_;
 
+	ClockRecovery wallClockRecovery_;
+
 protected:
 	void fillRequestMetadata(const ControlList &bufferControls,
 				 Request *request);
 
 	virtual void tryRunPipeline() = 0;
 
-	unsigned int ispOutputCount_;
-	unsigned int ispOutputTotal_;
-
 private:
 	void checkRequestCompleted();
 };
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/pisp/data/example.yaml 0.5.2-2/src/libcamera/pipeline/rpi/pisp/data/example.yaml
--- 0.5.0-1/src/libcamera/pipeline/rpi/pisp/data/example.yaml	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/pisp/data/example.yaml	2025-08-07 13:46:17.000000000 +0000
@@ -16,11 +16,6 @@
                 #
                 # "num_cfe_config_queue": 2,
 
-                # Override any request from the IPA to drop a number of startup
-                # frames.
-                #
-                # "disable_startup_frame_drops": false,
-
                 # Custom timeout value (in ms) for camera to use. This overrides
                 # the value computed by the pipeline handler based on frame
                 # durations.
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/pisp/pisp.cpp 0.5.2-2/src/libcamera/pipeline/rpi/pisp/pisp.cpp
--- 0.5.0-1/src/libcamera/pipeline/rpi/pisp/pisp.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/pisp/pisp.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2023, Raspberry Pi Ltd
  *
- * pisp.cpp - Pipeline handler for PiSP based Raspberry Pi devices
+ * Pipeline handler for PiSP based Raspberry Pi devices
  */
 
 #include <algorithm>
@@ -1755,9 +1755,15 @@ void PiSPCameraData::cfeBufferDequeue(Fr
 		auto [ctrl, delayContext] = delayedCtrls_->get(buffer->metadata().sequence);
 		/*
 		 * Add the frame timestamp to the ControlList for the IPA to use
-		 * as it does not receive the FrameBuffer object.
+		 * as it does not receive the FrameBuffer object. Also derive a
+		 * corresponding wallclock value.
 		 */
-		ctrl.set(controls::SensorTimestamp, buffer->metadata().timestamp);
+		wallClockRecovery_.addSample();
+		uint64_t sensorTimestamp = buffer->metadata().timestamp;
+		uint64_t wallClockTimestamp = wallClockRecovery_.getOutput(sensorTimestamp);
+
+		ctrl.set(controls::SensorTimestamp, sensorTimestamp);
+		ctrl.set(controls::FrameWallClock, wallClockTimestamp);
 		job.sensorControls = std::move(ctrl);
 		job.delayContext = delayContext;
 	} else if (stream == &cfe_[Cfe::Config]) {
@@ -1834,12 +1840,6 @@ void PiSPCameraData::beOutputDequeue(Fra
 		dmabufSyncEnd(buffer->planes()[0].fd);
 
 	handleStreamBuffer(buffer, stream);
-
-	/*
-	 * Increment the number of ISP outputs generated.
-	 * This is needed to track dropped frames.
-	 */
-	ispOutputCount_++;
 	handleState();
 }
 
@@ -1885,7 +1885,6 @@ void PiSPCameraData::prepareIspComplete(
 		 * If there is no need to run the Backend, just signal that the
 		 * input buffer is completed and all Backend outputs are ready.
 		 */
-		ispOutputCount_ = ispOutputTotal_;
 		buffer = cfe_[Cfe::Output0].getBuffers().at(bayerId).buffer;
 		handleStreamBuffer(buffer, &cfe_[Cfe::Output0]);
 	} else
@@ -1994,7 +1993,6 @@ int PiSPCameraData::configureBe(const st
 	global.bayer_enables |= PISP_BE_BAYER_ENABLE_INPUT;
 	global.bayer_order = toPiSPBayerOrder(cfeFormat.fourcc);
 
-	ispOutputTotal_ = 1; /* Config buffer */
 	if (PISP_IMAGE_FORMAT_COMPRESSED(inputFormat.format)) {
 		pisp_decompress_config decompress;
 		decompress.offset = DefaultCompressionOffset;
@@ -2025,7 +2023,6 @@ int PiSPCameraData::configureBe(const st
 		setupOutputClipping(ispFormat0, outputFormat0);
 
 		be_->SetOutputFormat(0, outputFormat0);
-		ispOutputTotal_++;
 	}
 
 	if (global.rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT1) {
@@ -2049,7 +2046,6 @@ int PiSPCameraData::configureBe(const st
 		setupOutputClipping(ispFormat1, outputFormat1);
 
 		be_->SetOutputFormat(1, outputFormat1);
-		ispOutputTotal_++;
 	}
 
 	/* Setup the TDN I/O blocks in case TDN gets turned on later. */
@@ -2256,8 +2252,6 @@ void PiSPCameraData::prepareCfe()
 
 void PiSPCameraData::prepareBe(uint32_t bufferId, bool stitchSwapBuffers)
 {
-	ispOutputCount_ = 0;
-
 	FrameBuffer *buffer = cfe_[Cfe::Output0].getBuffers().at(bufferId).buffer;
 
 	LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bufferId
@@ -2319,16 +2313,11 @@ void PiSPCameraData::tryRunPipeline()
 
 	/* Take the first request from the queue and action the IPA. */
 	Request *request = requestQueue_.front();
+	ASSERT(request->metadata().empty());
 
 	/* See if a new ScalerCrop value needs to be applied. */
 	applyScalerCrop(request->controls());
 
-	/*
-	 * Clear the request metadata and fill it with some initial non-IPA
-	 * related controls. We clear it first because the request metadata
-	 * may have been populated if we have dropped the previous frame.
-	 */
-	request->metadata().clear();
 	fillRequestMetadata(job.sensorControls, request);
 
 	/* Set our state to say the pipeline is active. */
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/vc4/data/example.yaml 0.5.2-2/src/libcamera/pipeline/rpi/vc4/data/example.yaml
--- 0.5.0-1/src/libcamera/pipeline/rpi/vc4/data/example.yaml	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/vc4/data/example.yaml	2025-08-07 13:46:17.000000000 +0000
@@ -29,11 +29,6 @@
                 #
                 # "min_total_unicam_buffers": 4,
 
-                # Override any request from the IPA to drop a number of startup
-                # frames.
-                #
-                # "disable_startup_frame_drops": false,
-
                 # Custom timeout value (in ms) for camera to use. This overrides
                 # the value computed by the pipeline handler based on frame
                 # durations.
diff -pruN 0.5.0-1/src/libcamera/pipeline/rpi/vc4/vc4.cpp 0.5.2-2/src/libcamera/pipeline/rpi/vc4/vc4.cpp
--- 0.5.0-1/src/libcamera/pipeline/rpi/vc4/vc4.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/rpi/vc4/vc4.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -597,8 +597,6 @@ int Vc4CameraData::platformConfigure(con
 		stream->setFlags(StreamFlag::External);
 	}
 
-	ispOutputTotal_ = outStreams.size();
-
 	/*
 	 * If ISP::Output0 stream has not been configured by the application,
 	 * we must allow the hardware to generate an output so that the data
@@ -625,8 +623,6 @@ int Vc4CameraData::platformConfigure(con
 			return -EINVAL;
 		}
 
-		ispOutputTotal_++;
-
 		LOG(RPI, Debug) << "Defaulting ISP Output0 format to "
 				<< format;
 	}
@@ -662,8 +658,6 @@ int Vc4CameraData::platformConfigure(con
 					<< ret;
 			return -EINVAL;
 		}
-
-		ispOutputTotal_++;
 	}
 
 	/* ISP statistics output format. */
@@ -676,8 +670,6 @@ int Vc4CameraData::platformConfigure(con
 		return ret;
 	}
 
-	ispOutputTotal_++;
-
 	/*
 	 * Configure the Unicam embedded data output format only if the sensor
 	 * supports it.
@@ -781,9 +773,15 @@ void Vc4CameraData::unicamBufferDequeue(
 		auto [ctrl, delayContext] = delayedCtrls_->get(buffer->metadata().sequence);
 		/*
 		 * Add the frame timestamp to the ControlList for the IPA to use
-		 * as it does not receive the FrameBuffer object.
+		 * as it does not receive the FrameBuffer object. Also derive a
+		 * corresponding wallclock value.
 		 */
-		ctrl.set(controls::SensorTimestamp, buffer->metadata().timestamp);
+		wallClockRecovery_.addSample();
+		uint64_t sensorTimestamp = buffer->metadata().timestamp;
+		uint64_t wallClockTimestamp = wallClockRecovery_.getOutput(sensorTimestamp);
+
+		ctrl.set(controls::SensorTimestamp, sensorTimestamp);
+		ctrl.set(controls::FrameWallClock, wallClockTimestamp);
 		bayerQueue_.push({ buffer, std::move(ctrl), delayContext });
 	} else {
 		embeddedQueue_.push(buffer);
@@ -843,12 +841,6 @@ void Vc4CameraData::ispOutputDequeue(Fra
 		handleStreamBuffer(buffer, stream);
 	}
 
-	/*
-	 * Increment the number of ISP outputs generated.
-	 * This is needed to track dropped frames.
-	 */
-	ispOutputCount_++;
-
 	handleState();
 }
 
@@ -880,7 +872,6 @@ void Vc4CameraData::prepareIspComplete(c
 			<< ", timestamp: " << buffer->metadata().timestamp;
 
 	isp_[Isp::Input].queueBuffer(buffer);
-	ispOutputCount_ = 0;
 
 	if (sensorMetadata_ && embeddedId) {
 		buffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId & RPi::MaskID).buffer;
@@ -936,16 +927,11 @@ void Vc4CameraData::tryRunPipeline()
 
 	/* Take the first request from the queue and action the IPA. */
 	Request *request = requestQueue_.front();
+	ASSERT(request->metadata().empty());
 
 	/* See if a new ScalerCrop value needs to be applied. */
 	applyScalerCrop(request->controls());
 
-	/*
-	 * Clear the request metadata and fill it with some initial non-IPA
-	 * related controls. We clear it first because the request metadata
-	 * may have been populated if we have dropped the previous frame.
-	 */
-	request->metadata().clear();
 	fillRequestMetadata(bayerFrame.controls, request);
 
 	/* Set our state to say the pipeline is active. */
diff -pruN 0.5.0-1/src/libcamera/pipeline/simple/simple.cpp 0.5.2-2/src/libcamera/pipeline/simple/simple.cpp
--- 0.5.0-1/src/libcamera/pipeline/simple/simple.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/simple/simple.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -427,6 +427,9 @@ private:
 		return static_cast<SimpleCameraData *>(camera->_d());
 	}
 
+	bool matchDevice(MediaDevice *media, const SimplePipelineInfo &info,
+			 DeviceEnumerator *enumerator);
+
 	std::vector<MediaEntity *> locateSensors(MediaDevice *media);
 	static int resetRoutingTable(V4L2Subdevice *subdev);
 
@@ -1660,25 +1663,13 @@ int SimplePipelineHandler::resetRoutingT
 	return 0;
 }
 
-bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
+bool SimplePipelineHandler::matchDevice(MediaDevice *media,
+					const SimplePipelineInfo &info,
+					DeviceEnumerator *enumerator)
 {
-	const SimplePipelineInfo *info = nullptr;
 	unsigned int numStreams = 1;
-	MediaDevice *media;
 
-	for (const SimplePipelineInfo &inf : supportedDevices) {
-		DeviceMatch dm(inf.driver);
-		media = acquireMediaDevice(enumerator, dm);
-		if (media) {
-			info = &inf;
-			break;
-		}
-	}
-
-	if (!media)
-		return false;
-
-	for (const auto &[name, streams] : info->converters) {
+	for (const auto &[name, streams] : info.converters) {
 		DeviceMatch converterMatch(name);
 		converter_ = acquireMediaDevice(enumerator, converterMatch);
 		if (converter_) {
@@ -1687,7 +1678,7 @@ bool SimplePipelineHandler::match(Device
 		}
 	}
 
-	swIspEnabled_ = info->swIspEnabled;
+	swIspEnabled_ = info.swIspEnabled;
 
 	/* Locate the sensors. */
 	std::vector<MediaEntity *> sensors = locateSensors(media);
@@ -1806,6 +1797,43 @@ bool SimplePipelineHandler::match(Device
 	return registered;
 }
 
+bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
+{
+	MediaDevice *media;
+
+	for (const SimplePipelineInfo &inf : supportedDevices) {
+		DeviceMatch dm(inf.driver);
+		while ((media = acquireMediaDevice(enumerator, dm))) {
+			/*
+			 * If match succeeds, return true to let match() be
+			 * called again on a new instance of the pipeline
+			 * handler. Otherwise keep looping until we do
+			 * successfully match one (or run out).
+			 */
+			if (matchDevice(media, inf, enumerator)) {
+				LOG(SimplePipeline, Debug)
+					<< "Matched on device: "
+					<< media->deviceNode();
+				return true;
+			}
+
+			/*
+			 * \todo We need to clear the list of media devices
+			 * that we've already acquired in the event that we
+			 * fail to create a camera. This requires a rework of
+			 * DeviceEnumerator, or even how we create pipelines
+			 * handlers. This is because at the moment acquired
+			 * media devices are only released on pipeline handler
+			 * deconstruction, and if we release them any earlier
+			 * then DeviceEnumerator::search() will keep returning
+			 * the same media devices.
+			 */
+		}
+	}
+
+	return false;
+}
+
 V4L2VideoDevice *SimplePipelineHandler::video(const MediaEntity *entity)
 {
 	auto iter = entities_.find(entity);
diff -pruN 0.5.0-1/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp 0.5.2-2/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
--- 0.5.0-1/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -100,7 +100,7 @@ public:
 private:
 	int processControl(const UVCCameraData *data, ControlList *controls,
 			   unsigned int id, const ControlValue &value);
-	int processControls(UVCCameraData *data, Request *request);
+	int processControls(UVCCameraData *data, const ControlList &reqControls);
 
 	bool acquireDevice(Camera *camera) override;
 	void releaseDevice(Camera *camera) override;
@@ -287,7 +287,7 @@ int PipelineHandlerUVC::exportFrameBuffe
 	return data->video_->exportBuffers(count, buffers);
 }
 
-int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList *controls)
+int PipelineHandlerUVC::start(Camera *camera, const ControlList *controls)
 {
 	UVCCameraData *data = cameraData(camera);
 	unsigned int count = data->stream_.configuration().bufferCount;
@@ -296,13 +296,22 @@ int PipelineHandlerUVC::start(Camera *ca
 	if (ret < 0)
 		return ret;
 
-	ret = data->video_->streamOn();
-	if (ret < 0) {
-		data->video_->releaseBuffers();
-		return ret;
+	if (controls) {
+		ret = processControls(data, *controls);
+		if (ret < 0)
+			goto err_release_buffers;
 	}
 
+	ret = data->video_->streamOn();
+	if (ret < 0)
+		goto err_release_buffers;
+
 	return 0;
+
+err_release_buffers:
+	data->video_->releaseBuffers();
+
+	return ret;
 }
 
 void PipelineHandlerUVC::stopDevice(Camera *camera)
@@ -329,6 +338,10 @@ int PipelineHandlerUVC::processControl(c
 		cid = V4L2_CID_EXPOSURE_ABSOLUTE;
 	else if (id == controls::AnalogueGain)
 		cid = V4L2_CID_GAIN;
+	else if (id == controls::Gamma)
+		cid = V4L2_CID_GAMMA;
+	else if (id == controls::AeEnable)
+		return 0; /* Handled in `Camera::queueRequest()`. */
 	else
 		return -EINVAL;
 
@@ -394,6 +407,10 @@ int PipelineHandlerUVC::processControl(c
 		break;
 	}
 
+	case V4L2_CID_GAMMA:
+		controls->set(cid, static_cast<int32_t>(std::lround(value.get<float>() * 100)));
+		break;
+
 	default: {
 		int32_t ivalue = value.get<int32_t>();
 		controls->set(cid, ivalue);
@@ -404,11 +421,11 @@ int PipelineHandlerUVC::processControl(c
 	return 0;
 }
 
-int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)
+int PipelineHandlerUVC::processControls(UVCCameraData *data, const ControlList &reqControls)
 {
 	ControlList controls(data->video_->controls());
 
-	for (const auto &[id, value] : request->controls())
+	for (const auto &[id, value] : reqControls)
 		processControl(data, &controls, id, value);
 
 	for (const auto &ctrl : controls)
@@ -436,7 +453,7 @@ int PipelineHandlerUVC::queueRequestDevi
 		return -ENOENT;
 	}
 
-	int ret = processControls(data, request);
+	int ret = processControls(data, request->controls());
 	if (ret < 0)
 		return ret;
 
@@ -590,6 +607,11 @@ int UVCCameraData::init(MediaDevice *med
 		addControl(cid, info, &ctrls);
 	}
 
+	if (autoExposureMode_ && manualExposureMode_) {
+		/* \todo Move this to the Camera class */
+		ctrls[&controls::AeEnable] = ControlInfo(false, true, true);
+	}
+
 	controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
 
 	/*
@@ -691,6 +713,9 @@ void UVCCameraData::addControl(uint32_t
 	case V4L2_CID_GAIN:
 		id = &controls::AnalogueGain;
 		break;
+	case V4L2_CID_GAMMA:
+		id = &controls::Gamma;
+		break;
 	default:
 		return;
 	}
@@ -845,6 +870,15 @@ void UVCCameraData::addControl(uint32_t
 		break;
 	}
 
+	case V4L2_CID_GAMMA:
+		/* UVC gamma is in units of 1/100 gamma. */
+		info = ControlInfo{
+			{ min / 100.0f },
+			{ max / 100.0f },
+			{ def / 100.0f }
+		};
+		break;
+
 	default:
 		info = v4l2Info;
 		break;
diff -pruN 0.5.0-1/src/libcamera/pipeline/virtual/config_parser.cpp 0.5.2-2/src/libcamera/pipeline/virtual/config_parser.cpp
--- 0.5.0-1/src/libcamera/pipeline/virtual/config_parser.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline/virtual/config_parser.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -31,7 +31,7 @@ ConfigParser::parseConfigFile(File &file
 
 	std::unique_ptr<YamlObject> cameras = YamlParser::parse(file);
 	if (!cameras) {
-		LOG(Virtual, Error) << "Failed to pass config file.";
+		LOG(Virtual, Error) << "Failed to parse config file.";
 		return configurations;
 	}
 
@@ -233,17 +233,21 @@ int ConfigParser::parseFrameGenerator(co
 
 int ConfigParser::parseLocation(const YamlObject &cameraConfigData, VirtualCameraData *data)
 {
-	std::string location = cameraConfigData["location"].get<std::string>("CameraLocationFront");
-
 	/* Default value is properties::CameraLocationFront */
-	auto it = properties::LocationNameValueMap.find(location);
-	if (it == properties::LocationNameValueMap.end()) {
-		LOG(Virtual, Error)
-			<< "location: " << location << " is not supported";
-		return -EINVAL;
+	int32_t location = properties::CameraLocationFront;
+
+	if (auto l = cameraConfigData["location"].get<std::string>()) {
+		auto it = properties::LocationNameValueMap.find(*l);
+		if (it == properties::LocationNameValueMap.end()) {
+			LOG(Virtual, Error)
+				<< "location: " << *l << " is not supported";
+			return -EINVAL;
+		}
+
+		location = it->second;
 	}
 
-	data->properties_.set(properties::Location, it->second);
+	data->properties_.set(properties::Location, location);
 
 	return 0;
 }
diff -pruN 0.5.0-1/src/libcamera/pipeline_handler.cpp 0.5.2-2/src/libcamera/pipeline_handler.cpp
--- 0.5.0-1/src/libcamera/pipeline_handler.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/pipeline_handler.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -62,13 +62,17 @@ LOG_DEFINE_CATEGORY(Pipeline)
 /**
  * \brief Construct a PipelineHandler instance
  * \param[in] manager The camera manager
+ * \param[in] maxQueuedRequestsDevice The maximum number of requests queued to
+ * the device
  *
  * In order to honour the std::enable_shared_from_this<> contract,
  * PipelineHandler instances shall never be constructed manually, but always
  * through the PipelineHandlerFactoryBase::create() function.
  */
-PipelineHandler::PipelineHandler(CameraManager *manager)
-	: manager_(manager), useCount_(0)
+PipelineHandler::PipelineHandler(CameraManager *manager,
+				 unsigned int maxQueuedRequestsDevice)
+	: manager_(manager), maxQueuedRequestsDevice_(maxQueuedRequestsDevice),
+	  useCount_(0)
 {
 }
 
@@ -360,19 +364,28 @@ void PipelineHandler::unlockMediaDevices
  */
 void PipelineHandler::stop(Camera *camera)
 {
+	/*
+	 * Take all waiting requests so that they are not requeued in response
+	 * to completeRequest() being called inside stopDevice(). Cancel them
+	 * after the device to keep them in order.
+	 */
+	Camera::Private *data = camera->_d();
+	std::queue<Request *> waitingRequests;
+	waitingRequests.swap(data->waitingRequests_);
+
 	/* Stop the pipeline handler and let the queued requests complete. */
 	stopDevice(camera);
 
 	/* Cancel and signal as complete all waiting requests. */
-	while (!waitingRequests_.empty()) {
-		Request *request = waitingRequests_.front();
-		waitingRequests_.pop();
+	while (!waitingRequests.empty()) {
+		Request *request = waitingRequests.front();
+		waitingRequests.pop();
 		cancelRequest(request);
 	}
 
 	/* Make sure no requests are pending. */
-	Camera::Private *data = camera->_d();
 	ASSERT(data->queuedRequests_.empty());
+	ASSERT(data->waitingRequests_.empty());
 
 	data->requestSequence_ = 0;
 }
@@ -414,7 +427,9 @@ void PipelineHandler::registerRequest(Re
 	 * Connect the request prepared signal to notify the pipeline handler
 	 * when a request is ready to be processed.
 	 */
-	request->_d()->prepared.connect(this, &PipelineHandler::doQueueRequests);
+	request->_d()->prepared.connect(this, [this, request]() {
+		doQueueRequests(request->_d()->camera());
+	});
 }
 
 /**
@@ -427,9 +442,9 @@ void PipelineHandler::registerRequest(Re
  * requests which have to be prepared to make sure they are ready for being
  * queued to the pipeline handler.
  *
- * The queue of waiting requests is iterated and all prepared requests are
- * passed to the pipeline handler in the same order they have been queued by
- * calling this function.
+ * The queue of waiting requests is iterated and up to \a
+ * maxQueuedRequestsDevice_ prepared requests are passed to the pipeline handler
+ * in the same order they have been queued by calling this function.
  *
  * If a Request fails during the preparation phase or if the pipeline handler
  * fails in queuing the request to the hardware the request is cancelled.
@@ -444,7 +459,9 @@ void PipelineHandler::queueRequest(Reque
 {
 	LIBCAMERA_TRACEPOINT(request_queue, request);
 
-	waitingRequests_.push(request);
+	Camera *camera = request->_d()->camera();
+	Camera::Private *data = camera->_d();
+	data->waitingRequests_.push(request);
 
 	request->_d()->prepare(300ms);
 }
@@ -478,15 +495,23 @@ void PipelineHandler::doQueueRequest(Req
  * Iterate the list of waiting requests and queue them to the device one
  * by one if they have been prepared.
  */
-void PipelineHandler::doQueueRequests()
+void PipelineHandler::doQueueRequests(Camera *camera)
 {
-	while (!waitingRequests_.empty()) {
-		Request *request = waitingRequests_.front();
+	Camera::Private *data = camera->_d();
+	while (!data->waitingRequests_.empty()) {
+		if (data->queuedRequests_.size() == maxQueuedRequestsDevice_)
+			break;
+
+		Request *request = data->waitingRequests_.front();
 		if (!request->_d()->prepared_)
 			break;
 
+		/*
+		 * Pop the request first, in case doQueueRequests() is called
+		 * recursively from within doQueueRequest()
+		 */
+		data->waitingRequests_.pop();
 		doQueueRequest(request);
-		waitingRequests_.pop();
 	}
 }
 
@@ -562,6 +587,9 @@ void PipelineHandler::completeRequest(Re
 		data->queuedRequests_.pop_front();
 		camera->requestComplete(req);
 	}
+
+	/* Allow any waiting requests to be queued to the pipeline. */
+	doQueueRequests(camera);
 }
 
 /**
@@ -763,6 +791,20 @@ void PipelineHandler::disconnect()
  */
 
 /**
+ * \var PipelineHandler::maxQueuedRequestsDevice_
+ * \brief The maximum number of requests the pipeline handler shall queue to the
+ * device
+ *
+ * maxQueuedRequestsDevice_ limits the number of request that the
+ * pipeline handler shall queue to the underlying hardware, in order to
+ * saturate the pipeline with requests. The application may choose to queue
+ * as many requests as it desires, however only maxQueuedRequestsDevice_
+ * requests will be queued to the hardware at a given point in time. The
+ * remaining requests will be kept waiting in the internal waiting
+ * queue, to be queued at a later stage.
+ */
+
+/**
  * \fn PipelineHandler::name()
  * \brief Retrieve the pipeline handler name
  * \context This function shall be \threadsafe.
diff -pruN 0.5.0-1/src/libcamera/process.cpp 0.5.2-2/src/libcamera/process.cpp
--- 0.5.0-1/src/libcamera/process.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/process.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -10,15 +10,19 @@
 #include <algorithm>
 #include <dirent.h>
 #include <fcntl.h>
-#include <list>
 #include <signal.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <utility>
 #include <vector>
 
+#include <linux/sched.h>
+#include <linux/wait.h> /* glibc only provides `P_PIDFD` in `sys/wait.h` since 2.36 */
+
 #include <libcamera/base/event_notifier.h>
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
@@ -32,161 +36,61 @@ namespace libcamera {
 
 LOG_DEFINE_CATEGORY(Process)
 
-/**
- * \class ProcessManager
- * \brief Manager of processes
- *
- * The ProcessManager singleton keeps track of all created Process instances,
- * and manages the signal handling involved in terminating processes.
- */
-
 namespace {
 
-void sigact(int signal, siginfo_t *info, void *ucontext)
+void closeAllFdsExcept(std::vector<int> v)
 {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
+	sort(v.begin(), v.end());
+
+	ASSERT(v.empty() || v.front() >= 0);
+
+#if HAVE_CLOSE_RANGE
 	/*
-	 * We're in a signal handler so we can't log any message, and we need
-	 * to continue anyway.
+	 * At the moment libcamera does not require at least Linux 5.9,
+	 * which introduced the `close_range()` system call, so a runtime
+	 * check is also needed to make sure that it is supported.
 	 */
-	char data = 0;
-	write(ProcessManager::instance()->writePipe(), &data, sizeof(data));
-#pragma GCC diagnostic pop
-
-	const struct sigaction &oldsa = ProcessManager::instance()->oldsa();
-	if (oldsa.sa_flags & SA_SIGINFO) {
-		oldsa.sa_sigaction(signal, info, ucontext);
-	} else {
-		if (oldsa.sa_handler != SIG_IGN && oldsa.sa_handler != SIG_DFL)
-			oldsa.sa_handler(signal);
-	}
-}
-
-} /* namespace */
+	static const bool hasCloseRange = [] {
+		return close_range(~0u, 0, 0) < 0 && errno == EINVAL;
+	}();
+
+	if (hasCloseRange) {
+		unsigned int prev = 0;
+
+		for (unsigned int curr : v) {
+			ASSERT(prev <= curr + 1);
+			if (prev < curr)
+				close_range(prev, curr - 1, 0);
+			prev = curr + 1;
+		}
 
-void ProcessManager::sighandler()
-{
-	char data;
-	ssize_t ret = read(pipe_[0].get(), &data, sizeof(data));
-	if (ret < 0) {
-		LOG(Process, Error)
-			<< "Failed to read byte from signal handler pipe";
+		close_range(prev, ~0u, 0);
 		return;
 	}
+#endif
 
-	for (auto it = processes_.begin(); it != processes_.end();) {
-		Process *process = *it;
+	DIR *dir = opendir("/proc/self/fd");
+	if (!dir)
+		return;
+
+	int dfd = dirfd(dir);
 
-		int wstatus;
-		pid_t pid = waitpid(process->pid_, &wstatus, WNOHANG);
-		if (process->pid_ != pid) {
-			++it;
+	struct dirent *ent;
+	while ((ent = readdir(dir)) != nullptr) {
+		char *endp;
+		int fd = strtoul(ent->d_name, &endp, 10);
+		if (*endp)
 			continue;
-		}
 
-		it = processes_.erase(it);
-		process->died(wstatus);
+		if (fd >= 0 && fd != dfd &&
+		    !std::binary_search(v.begin(), v.end(), fd))
+			close(fd);
 	}
-}
 
-/**
- * \brief Register process with process manager
- * \param[in] proc Process to register
- *
- * This function registers the \a proc with the process manager. It
- * shall be called by the parent process after successfully forking, in
- * order to let the parent signal process termination.
- */
-void ProcessManager::registerProcess(Process *proc)
-{
-	processes_.push_back(proc);
-}
-
-ProcessManager *ProcessManager::self_ = nullptr;
-
-/**
- * \brief Construct a ProcessManager instance
- *
- * The ProcessManager class is meant to only be instantiated once, by the
- * CameraManager.
- */
-ProcessManager::ProcessManager()
-{
-	if (self_)
-		LOG(Process, Fatal)
-			<< "Multiple ProcessManager objects are not allowed";
-
-	sigaction(SIGCHLD, NULL, &oldsa_);
-
-	struct sigaction sa;
-	memset(&sa, 0, sizeof(sa));
-	sa.sa_sigaction = &sigact;
-	memcpy(&sa.sa_mask, &oldsa_.sa_mask, sizeof(sa.sa_mask));
-	sigaddset(&sa.sa_mask, SIGCHLD);
-	sa.sa_flags = oldsa_.sa_flags | SA_SIGINFO;
-
-	sigaction(SIGCHLD, &sa, NULL);
-
-	int pipe[2];
-	if (pipe2(pipe, O_CLOEXEC | O_DIRECT | O_NONBLOCK))
-		LOG(Process, Fatal)
-			<< "Failed to initialize pipe for signal handling";
-
-	pipe_[0] = UniqueFD(pipe[0]);
-	pipe_[1] = UniqueFD(pipe[1]);
-
-	sigEvent_ = new EventNotifier(pipe_[0].get(), EventNotifier::Read);
-	sigEvent_->activated.connect(this, &ProcessManager::sighandler);
-
-	self_ = this;
-}
-
-ProcessManager::~ProcessManager()
-{
-	sigaction(SIGCHLD, &oldsa_, NULL);
-
-	delete sigEvent_;
-
-	self_ = nullptr;
-}
-
-/**
- * \brief Retrieve the Process manager instance
- *
- * The ProcessManager is constructed by the CameraManager. This function shall
- * be used to retrieve the single instance of the manager.
- *
- * \return The Process manager instance
- */
-ProcessManager *ProcessManager::instance()
-{
-	return self_;
-}
-
-/**
- * \brief Retrieve the Process manager's write pipe
- *
- * This function is meant only to be used by the static signal handler.
- *
- * \return Pipe for writing
- */
-int ProcessManager::writePipe() const
-{
-	return pipe_[1].get();
+	closedir(dir);
 }
 
-/**
- * \brief Retrive the old signal action data
- *
- * This function is meant only to be used by the static signal handler.
- *
- * \return The old signal action data
- */
-const struct sigaction &ProcessManager::oldsa() const
-{
-	return oldsa_;
-}
+} /* namespace */
 
 /**
  * \class Process
@@ -208,7 +112,7 @@ const struct sigaction &ProcessManager::
  */
 
 Process::Process()
-	: pid_(-1), running_(false), exitStatus_(NotExited), exitCode_(0)
+	: pid_(-1), exitStatus_(NotExited), exitCode_(0)
 {
 }
 
@@ -235,102 +139,110 @@ Process::~Process()
  * or a negative error code otherwise
  */
 int Process::start(const std::string &path,
-		   const std::vector<std::string> &args,
-		   const std::vector<int> &fds)
+		   Span<const std::string> args,
+		   Span<const int> fds)
 {
-	int ret;
+	if (pid_ > 0)
+		return -EBUSY;
+
+	for (int fd : fds) {
+		if (fd < 0)
+			return -EINVAL;
+	}
 
-	if (running_)
-		return 0;
+	clone_args cargs = {};
+	int pidfd;
 
-	int childPid = fork();
-	if (childPid == -1) {
-		ret = -errno;
+	cargs.flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWNET;
+	cargs.pidfd = reinterpret_cast<uintptr_t>(&pidfd);
+	cargs.exit_signal = SIGCHLD;
+
+	long childPid = syscall(SYS_clone3, &cargs, sizeof(cargs));
+	if (childPid < 0) {
+		int ret = -errno;
 		LOG(Process, Error) << "Failed to fork: " << strerror(-ret);
 		return ret;
-	} else if (childPid) {
-		pid_ = childPid;
-		ProcessManager::instance()->registerProcess(this);
+	}
 
-		running_ = true;
+	if (childPid) {
+		pid_ = childPid;
+		pidfd_ = UniqueFD(pidfd);
+		pidfdNotify_ = std::make_unique<EventNotifier>(pidfd_.get(), EventNotifier::Type::Read);
+		pidfdNotify_->activated.connect(this, &Process::onPidfdNotify);
 
-		return 0;
+		LOG(Process, Debug) << this << "[" << childPid << ':' << pidfd << "]"
+				    << " forked";
 	} else {
-		if (isolate())
-			_exit(EXIT_FAILURE);
-
-		closeAllFdsExcept(fds);
+		std::vector<int> v(fds.begin(), fds.end());
+		v.push_back(STDERR_FILENO);
+		closeAllFdsExcept(std::move(v));
+
+		const auto tryDevNullLowestFd = [](int expected, int oflag) {
+			int fd = open("/dev/null", oflag);
+			if (fd < 0)
+				_exit(EXIT_FAILURE);
+			if (fd != expected)
+				close(fd);
+		};
+
+		tryDevNullLowestFd(STDIN_FILENO, O_RDONLY);
+		tryDevNullLowestFd(STDOUT_FILENO, O_WRONLY);
+		tryDevNullLowestFd(STDERR_FILENO, O_WRONLY);
 
 		const char *file = utils::secure_getenv("LIBCAMERA_LOG_FILE");
 		if (file && strcmp(file, "syslog"))
 			unsetenv("LIBCAMERA_LOG_FILE");
 
-		const char **argv = new const char *[args.size() + 2];
-		unsigned int len = args.size();
+		const size_t len = args.size();
+		auto argv = std::make_unique<const char *[]>(len + 2);
+
 		argv[0] = path.c_str();
-		for (unsigned int i = 0; i < len; i++)
+		for (size_t i = 0; i < len; i++)
 			argv[i + 1] = args[i].c_str();
 		argv[len + 1] = nullptr;
 
-		execv(path.c_str(), (char **)argv);
+		execv(path.c_str(), const_cast<char **>(argv.get()));
 
-		exit(EXIT_FAILURE);
+		_exit(EXIT_FAILURE);
 	}
+
+	return 0;
 }
 
-void Process::closeAllFdsExcept(const std::vector<int> &fds)
+void Process::onPidfdNotify()
 {
-	std::vector<int> v(fds);
-	sort(v.begin(), v.end());
+	auto pidfdNotify = std::exchange(pidfdNotify_, {});
+	auto pidfd = std::exchange(pidfd_, {});
+	auto pid = std::exchange(pid_, -1);
 
-	DIR *dir = opendir("/proc/self/fd");
-	if (!dir)
-		return;
+	ASSERT(pidfdNotify);
+	ASSERT(pidfd.isValid());
+	ASSERT(pid > 0);
 
-	int dfd = dirfd(dir);
+	siginfo_t info;
 
-	struct dirent *ent;
-	while ((ent = readdir(dir)) != nullptr) {
-		char *endp;
-		int fd = strtoul(ent->d_name, &endp, 10);
-		if (*endp)
-			continue;
+	/*
+	 * `static_cast` is needed because `P_PIDFD` is not defined in `sys/wait.h` if the C standard library
+	 * is old enough. So `P_PIDFD` is taken from `linux/wait.h`, where it is just an integer #define.
+	 */
+	if (waitid(static_cast<idtype_t>(P_PIDFD), pidfd.get(), &info, WNOHANG | WEXITED) >= 0) {
+		ASSERT(info.si_pid == pid);
 
-		if (fd >= 0 && fd != dfd &&
-		    !std::binary_search(v.begin(), v.end(), fd))
-			close(fd);
-	}
+		LOG(Process, Debug)
+			<< this << "[" << pid << ':' << pidfd.get() << "]"
+			<< " code: " << info.si_code
+			<< " status: " << info.si_status;
 
-	closedir(dir);
-}
+		exitStatus_ = info.si_code == CLD_EXITED ? Process::NormalExit : Process::SignalExit;
+		exitCode_ = info.si_code == CLD_EXITED ? info.si_status : -1;
 
-int Process::isolate()
-{
-	int ret = unshare(CLONE_NEWUSER | CLONE_NEWNET);
-	if (ret) {
-		ret = -errno;
-		LOG(Process, Error) << "Failed to unshare execution context: "
-				    << strerror(-ret);
-		return ret;
+		finished.emit(exitStatus_, exitCode_);
+	} else {
+		int err = errno;
+		LOG(Process, Warning)
+			<< this << "[" << pid << ":" << pidfd.get() << "]"
+			<< " waitid() failed: " << strerror(err);
 	}
-
-	return 0;
-}
-
-/**
- * \brief SIGCHLD handler
- * \param[in] wstatus The status as output by waitpid()
- *
- * This function is called when the process associated with Process terminates.
- * It emits the Process::finished signal.
- */
-void Process::died(int wstatus)
-{
-	running_ = false;
-	exitStatus_ = WIFEXITED(wstatus) ? NormalExit : SignalExit;
-	exitCode_ = exitStatus_ == NormalExit ? WEXITSTATUS(wstatus) : -1;
-
-	finished.emit(exitStatus_, exitCode_);
 }
 
 /**
@@ -368,8 +280,8 @@ void Process::died(int wstatus)
  */
 void Process::kill()
 {
-	if (pid_ > 0)
-		::kill(pid_, SIGKILL);
+	if (pidfd_.isValid())
+		syscall(SYS_pidfd_send_signal, pidfd_.get(), SIGKILL, nullptr, 0);
 }
 
 } /* namespace libcamera */
diff -pruN 0.5.0-1/src/libcamera/request.cpp 0.5.2-2/src/libcamera/request.cpp
--- 0.5.0-1/src/libcamera/request.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/request.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -484,15 +484,14 @@ int Request::addBuffer(const Stream *str
 		return -EEXIST;
 	}
 
-	auto it = bufferMap_.find(stream);
-	if (it != bufferMap_.end()) {
+	auto [it, inserted] = bufferMap_.try_emplace(stream, buffer);
+	if (!inserted) {
 		LOG(Request, Error) << "FrameBuffer already set for stream";
 		return -EEXIST;
 	}
 
 	buffer->_d()->setRequest(this);
 	_d()->pending_.insert(buffer);
-	bufferMap_[stream] = buffer;
 
 	if (fence && fence->isValid())
 		buffer->_d()->setFence(std::move(fence));
diff -pruN 0.5.0-1/src/libcamera/sensor/camera_sensor.cpp 0.5.2-2/src/libcamera/sensor/camera_sensor.cpp
--- 0.5.0-1/src/libcamera/sensor/camera_sensor.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/sensor/camera_sensor.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -302,8 +302,9 @@ int CameraSensor::setEmbeddedDataEnabled
  * camera sensor, likely at configure() time.
  *
  * If the requested \a orientation cannot be obtained, the \a orientation
- * parameter is adjusted to report the current image orientation and
- * Transform::Identity is returned.
+ * parameter is adjusted to report the native image orientation (i.e. resulting
+ * from the physical mounting rotation of the camera sensor, without any
+ * transformation) and Transform::Identity is returned.
  *
  * If the requested \a orientation can be obtained, the function computes a
  * Transform and does not adjust \a orientation.
diff -pruN 0.5.0-1/src/libcamera/sensor/camera_sensor_legacy.cpp 0.5.2-2/src/libcamera/sensor/camera_sensor_legacy.cpp
--- 0.5.0-1/src/libcamera/sensor/camera_sensor_legacy.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/sensor/camera_sensor_legacy.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2019, Google Inc.
  *
- * camera_sensor_legacy.cpp - A V4L2-backed camera sensor
+ * A V4L2-backed camera sensor
  */
 
 #include <algorithm>
diff -pruN 0.5.0-1/src/libcamera/sensor/camera_sensor_properties.cpp 0.5.2-2/src/libcamera/sensor/camera_sensor_properties.cpp
--- 0.5.0-1/src/libcamera/sensor/camera_sensor_properties.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/sensor/camera_sensor_properties.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -194,7 +194,7 @@ const CameraSensorProperties *CameraSens
 			.testPatternModes = {},
 			.sensorDelays = {
 				.exposureDelay = 2,
-				.gainDelay = 2,
+				.gainDelay = 1,
 				.vblankDelay = 2,
 				.hblankDelay = 2
 			},
@@ -456,6 +456,27 @@ const CameraSensorProperties *CameraSens
 			},
 			.sensorDelays = { },
 		} },
+		{ "vd56g3", {
+			.unitCellSize = { 2610, 2610 },
+			.testPatternModes = {
+				{ controls::draft::TestPatternModeOff, 0 },
+				{ controls::draft::TestPatternModeSolidColor, 1 },
+				{ controls::draft::TestPatternModePn9, 6 },
+				/*
+				 * No corresponding test pattern mode for:
+				 * 2: "Vertical Color Bars"
+				 * 3: "Horizontal Gray Scale"
+				 * 4: "Vertical Gray Scale"
+				 * 5: "Diagonal Gray Scale"
+				 */
+			},
+			.sensorDelays = {
+				.exposureDelay = 2,
+				.gainDelay = 2,
+				.vblankDelay = 2,
+				.hblankDelay = 2
+			},
+		} },
 	};
 
 	const auto it = sensorProps.find(sensor);
diff -pruN 0.5.0-1/src/libcamera/sensor/camera_sensor_raw.cpp 0.5.2-2/src/libcamera/sensor/camera_sensor_raw.cpp
--- 0.5.0-1/src/libcamera/sensor/camera_sensor_raw.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/sensor/camera_sensor_raw.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Ideas on Board Oy.
  *
- * camera_sensor_raw.cpp - A raw camera sensor using the V4L2 streams API
+ * A raw camera sensor using the V4L2 streams API
  */
 
 #include <algorithm>
diff -pruN 0.5.0-1/src/libcamera/software_isp/swstats_cpu.cpp 0.5.2-2/src/libcamera/software_isp/swstats_cpu.cpp
--- 0.5.0-1/src/libcamera/software_isp/swstats_cpu.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/software_isp/swstats_cpu.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -175,7 +175,7 @@ void SwStatsCpu::statsBGGR8Line0(const u
 		std::swap(src0, src1);
 
 	/* x += 4 sample every other 2x2 block */
-	for (int x = 0; x < (int)window_.width; x += 4) {
+	for (unsigned int x = 0; x < window_.width; x += 4) {
 		b = src0[x];
 		g = src0[x + 1];
 		g2 = src1[x];
@@ -200,7 +200,7 @@ void SwStatsCpu::statsBGGR10Line0(const
 		std::swap(src0, src1);
 
 	/* x += 4 sample every other 2x2 block */
-	for (int x = 0; x < (int)window_.width; x += 4) {
+	for (unsigned int x = 0; x < window_.width; x += 4) {
 		b = src0[x];
 		g = src0[x + 1];
 		g2 = src1[x];
@@ -226,7 +226,7 @@ void SwStatsCpu::statsBGGR12Line0(const
 		std::swap(src0, src1);
 
 	/* x += 4 sample every other 2x2 block */
-	for (int x = 0; x < (int)window_.width; x += 4) {
+	for (unsigned int x = 0; x < window_.width; x += 4) {
 		b = src0[x];
 		g = src0[x + 1];
 		g2 = src1[x];
@@ -245,7 +245,7 @@ void SwStatsCpu::statsBGGR10PLine0(const
 {
 	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
 	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
-	const int widthInBytes = window_.width * 5 / 4;
+	const unsigned int widthInBytes = window_.width * 5 / 4;
 
 	if (swapLines_)
 		std::swap(src0, src1);
@@ -253,7 +253,7 @@ void SwStatsCpu::statsBGGR10PLine0(const
 	SWSTATS_START_LINE_STATS(uint8_t)
 
 	/* x += 5 sample every other 2x2 block */
-	for (int x = 0; x < widthInBytes; x += 5) {
+	for (unsigned int x = 0; x < widthInBytes; x += 5) {
 		/* BGGR */
 		b = src0[x];
 		g = src0[x + 1];
@@ -271,7 +271,7 @@ void SwStatsCpu::statsGBRG10PLine0(const
 {
 	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
 	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
-	const int widthInBytes = window_.width * 5 / 4;
+	const unsigned int widthInBytes = window_.width * 5 / 4;
 
 	if (swapLines_)
 		std::swap(src0, src1);
@@ -279,7 +279,7 @@ void SwStatsCpu::statsGBRG10PLine0(const
 	SWSTATS_START_LINE_STATS(uint8_t)
 
 	/* x += 5 sample every other 2x2 block */
-	for (int x = 0; x < widthInBytes; x += 5) {
+	for (unsigned int x = 0; x < widthInBytes; x += 5) {
 		/* GBRG */
 		g = src0[x];
 		b = src0[x + 1];
diff -pruN 0.5.0-1/src/libcamera/stream.cpp 0.5.2-2/src/libcamera/stream.cpp
--- 0.5.0-1/src/libcamera/stream.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/stream.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -407,7 +407,8 @@ std::string StreamConfiguration::toStrin
  */
 std::ostream &operator<<(std::ostream &out, const StreamConfiguration &cfg)
 {
-	out << cfg.size << "-" << cfg.pixelFormat;
+	out << cfg.size << "-" << cfg.pixelFormat << "/"
+	    << ColorSpace::toString(cfg.colorSpace);
 	return out;
 }
 
diff -pruN 0.5.0-1/src/libcamera/v4l2_subdevice.cpp 0.5.2-2/src/libcamera/v4l2_subdevice.cpp
--- 0.5.0-1/src/libcamera/v4l2_subdevice.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/v4l2_subdevice.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -949,6 +949,8 @@ std::ostream &operator<<(std::ostream &o
 	else
 		out << it->second.name;
 
+	out << "/" << ColorSpace::toString(f.colorSpace);
+
 	return out;
 }
 
diff -pruN 0.5.0-1/src/libcamera/v4l2_videodevice.cpp 0.5.2-2/src/libcamera/v4l2_videodevice.cpp
--- 0.5.0-1/src/libcamera/v4l2_videodevice.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/v4l2_videodevice.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -446,7 +446,8 @@ const std::string V4L2DeviceFormat::toSt
  */
 std::ostream &operator<<(std::ostream &out, const V4L2DeviceFormat &f)
 {
-	out << f.size << "-" << f.fourcc;
+	out << f.size << "-" << f.fourcc << "/"
+	    << ColorSpace::toString(f.colorSpace);
 	return out;
 }
 
@@ -1326,7 +1327,8 @@ int V4L2VideoDevice::requestBuffers(unsi
 
 	if (rb.count < count) {
 		LOG(V4L2, Error)
-			<< "Not enough buffers provided by V4L2VideoDevice";
+			<< "Not enough buffers provided by V4L2VideoDevice. Wanted "
+			<< count << ", got " << rb.count;
 		requestBuffers(0, memoryType);
 		return -ENOMEM;
 	}
@@ -1666,6 +1668,8 @@ int V4L2VideoDevice::queueBuffer(FrameBu
 	if (ret < 0)
 		return ret;
 
+	auto guard = utils::scope_exit{ [&]() { cache_->put(buf.index); } };
+
 	buf.index = ret;
 	buf.type = bufferType_;
 	buf.memory = memoryType_;
@@ -1788,6 +1792,7 @@ int V4L2VideoDevice::queueBuffer(FrameBu
 
 	queuedBuffers_[buf.index] = buffer;
 
+	guard.release();
 	return 0;
 }
 
@@ -2030,10 +2035,9 @@ int V4L2VideoDevice::streamOff()
 	/* Send back all queued buffers. */
 	for (auto it : queuedBuffers_) {
 		FrameBuffer *buffer = it.second;
-		FrameMetadata &metadata = buffer->_d()->metadata();
 
 		cache_->put(it.first);
-		metadata.status = FrameMetadata::FrameCancelled;
+		buffer->_d()->cancel();
 		bufferReady.emit(buffer);
 	}
 
diff -pruN 0.5.0-1/src/libcamera/vector.cpp 0.5.2-2/src/libcamera/vector.cpp
--- 0.5.0-1/src/libcamera/vector.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/libcamera/vector.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -45,6 +45,14 @@ LOG_DEFINE_CATEGORY(Vector)
  */
 
 /**
+ * \fn Vector::Vector(const Span<const T, Rows> data)
+ * \brief Construct vector from supplied data
+ * \param data Data from which to construct a vector
+ *
+ * The size of \a data must be equal to the dimension size Rows of the vector.
+ */
+
+/**
  * \fn T Vector::operator[](size_t i) const
  * \brief Index to an element in the vector
  * \param i Index of element to retrieve
@@ -300,9 +308,10 @@ LOG_DEFINE_CATEGORY(Vector)
  */
 
 /**
- * \fn Vector<T, Rows> operator*(const Matrix<T, Rows, Cols> &m, const Vector<T, Cols> &v)
+ * \fn operator*(const Matrix<T, Rows, Cols> &m, const Vector<U, Cols> &v)
  * \brief Multiply a matrix by a vector
- * \tparam T Numerical type of the contents of the matrix and vector
+ * \tparam T Numerical type of the contents of the matrix
+ * \tparam U Numerical type of the contents of the vector
  * \tparam Rows The number of rows in the matrix
  * \tparam Cols The number of columns in the matrix (= rows in the vector)
  * \param m The matrix
diff -pruN 0.5.0-1/src/py/libcamera/meson.build 0.5.2-2/src/py/libcamera/meson.build
--- 0.5.0-1/src/py/libcamera/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/py/libcamera/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -1,21 +1,5 @@
 # SPDX-License-Identifier: CC0-1.0
 
-py3_dep = dependency('python3', required : get_option('pycamera'))
-
-if not py3_dep.found()
-    pycamera_enabled = false
-    subdir_done()
-endif
-
-pybind11_dep = dependency('pybind11', required : get_option('pycamera'))
-
-if not pybind11_dep.found()
-    pycamera_enabled = false
-    subdir_done()
-endif
-
-pycamera_enabled = true
-
 pycamera_sources = files([
     'py_camera_manager.cpp',
     'py_color_space.cpp',
@@ -36,6 +20,7 @@ pycamera_sources += custom_target('py_ge
                                   output : ['py_controls_generated.cpp'],
                                   command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@',
                                              '-t', gen_py_controls_template, '@INPUT@'],
+                                  depend_files : [py_mod_controls],
                                   env : py_build_env)
 
 pycamera_sources += custom_target('py_gen_properties',
@@ -43,6 +28,7 @@ pycamera_sources += custom_target('py_ge
                                   output : ['py_properties_generated.cpp'],
                                   command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@',
                                              '-t', gen_py_controls_template, '@INPUT@'],
+                                  depend_files : [py_mod_controls],
                                   env : py_build_env)
 
 # Generate formats
diff -pruN 0.5.0-1/src/py/meson.build 0.5.2-2/src/py/meson.build
--- 0.5.0-1/src/py/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/src/py/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -1,3 +1,15 @@
 # SPDX-License-Identifier: CC0-1.0
 
+py3_dep = dependency('python3', required : get_option('pycamera'))
+pybind11_dep = dependency('pybind11', required : get_option('pycamera'))
+
+pycamera_enabled = py3_dep.found() and pybind11_dep.found()
+if not pycamera_enabled
+    subdir_done()
+endif
+
 subdir('libcamera')
+
+pycamera_devenv = environment()
+pycamera_devenv.prepend('PYTHONPATH', meson.current_build_dir())
+meson.add_devenv(pycamera_devenv)
diff -pruN 0.5.0-1/subprojects/.gitignore 0.5.2-2/subprojects/.gitignore
--- 0.5.0-1/subprojects/.gitignore	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/subprojects/.gitignore	2025-08-07 13:46:17.000000000 +0000
@@ -2,6 +2,6 @@
 
 /googletest-release*
 /libpisp
-/libyaml
 /libyuv
 /packagecache
+/yaml-0.2.5
diff -pruN 0.5.0-1/subprojects/libpisp.wrap 0.5.2-2/subprojects/libpisp.wrap
--- 0.5.0-1/subprojects/libpisp.wrap	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/subprojects/libpisp.wrap	2025-08-07 13:46:17.000000000 +0000
@@ -2,5 +2,5 @@
 
 [wrap-git]
 url = https://github.com/raspberrypi/libpisp.git
-revision = v1.2.0
+revision = v1.2.1
 depth = 1
diff -pruN 0.5.0-1/subprojects/libyaml.wrap 0.5.2-2/subprojects/libyaml.wrap
--- 0.5.0-1/subprojects/libyaml.wrap	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/subprojects/libyaml.wrap	2025-08-07 13:46:17.000000000 +0000
@@ -1,7 +1,13 @@
 # SPDX-License-Identifier: CC0-1.0
 
-[wrap-git]
-directory = libyaml
-url = https://github.com/yaml/libyaml
-# tags/0.2.5
-revision = 2c891fc7a770e8ba2fec34fc6b545c672beb37e6
+[wrap-file]
+directory = yaml-0.2.5
+source_url = https://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz
+source_filename = yaml-0.2.5.tar.gz
+source_hash = c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4
+patch_filename = libyaml_0.2.5-1_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/libyaml_0.2.5-1/get_patch
+patch_hash = bf2e9b922be00b6b00c5fce29d9fb8dc83f0431c77239f3b73e8b254d3f3f5b5
+
+[provide]
+yaml-0.1 = yaml_dep
diff -pruN 0.5.0-1/test/gstreamer/gstreamer_memory_lifetime_test.cpp 0.5.2-2/test/gstreamer/gstreamer_memory_lifetime_test.cpp
--- 0.5.0-1/test/gstreamer/gstreamer_memory_lifetime_test.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/gstreamer/gstreamer_memory_lifetime_test.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2024, Nicolas Dufresne
  *
- * gstreamer_memory_lifetime_test.cpp - GStreamer memory lifetime test
+ * GStreamer memory lifetime test
  */
 
 #include <iostream>
diff -pruN 0.5.0-1/test/ipa/libipa/meson.build 0.5.2-2/test/ipa/libipa/meson.build
--- 0.5.0-1/test/ipa/libipa/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/ipa/libipa/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -4,6 +4,7 @@ libipa_test = [
     {'name': 'fixedpoint', 'sources': ['fixedpoint.cpp']},
     {'name': 'histogram', 'sources': ['histogram.cpp']},
     {'name': 'interpolator', 'sources': ['interpolator.cpp']},
+    {'name': 'pwl', 'sources': ['pwl.cpp'] },
 ]
 
 foreach test : libipa_test
diff -pruN 0.5.0-1/test/ipa/libipa/pwl.cpp 0.5.2-2/test/ipa/libipa/pwl.cpp
--- 0.5.0-1/test/ipa/libipa/pwl.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/test/ipa/libipa/pwl.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * PWL tests
+ */
+
+#include "../src/ipa/libipa/pwl.h"
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+#define ASSERT_EQ(a, b)                                                 \
+	if ((a) != (b)) {                                               \
+		std::cout << #a " != " #b " (" << a << " ," << b << ")" \
+			  << std::endl;                                 \
+		return TestFail;                                        \
+	}
+
+class PwlTest : public Test
+{
+protected:
+	int run()
+	{
+		Pwl pwl;
+		pwl.append(0, 0);
+		pwl.append(1, 1);
+
+		ASSERT_EQ(pwl.eval(-1), -1);
+		ASSERT_EQ(pwl.eval(0), 0);
+		ASSERT_EQ(pwl.eval(0.5), 0.5);
+		ASSERT_EQ(pwl.eval(1), 1);
+		ASSERT_EQ(pwl.eval(2), 2);
+
+		ASSERT_EQ(pwl.size(), 2);
+
+		/* Test degenerate PWL. */
+		pwl.clear();
+		pwl.append(1, 1);
+		ASSERT_EQ(pwl.eval(0), 1);
+		ASSERT_EQ(pwl.eval(1), 1);
+		ASSERT_EQ(pwl.eval(2), 1);
+
+		return TestPass;
+	}
+};
+
+TEST_REGISTER(PwlTest)
diff -pruN 0.5.0-1/test/ipc/unixsocket_ipc.cpp 0.5.2-2/test/ipc/unixsocket_ipc.cpp
--- 0.5.0-1/test/ipc/unixsocket_ipc.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/ipc/unixsocket_ipc.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -209,8 +209,6 @@ protected:
 	}
 
 private:
-	ProcessManager processManager_;
-
 	unique_ptr<IPCPipeUnixSocket> ipc_;
 };
 
diff -pruN 0.5.0-1/test/libtest/camera_test.cpp 0.5.2-2/test/libtest/camera_test.cpp
--- 0.5.0-1/test/libtest/camera_test.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/libtest/camera_test.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -15,11 +15,11 @@ using namespace std;
 
 CameraTest::CameraTest(const char *name, bool isolate)
 {
-	cm_ = new CameraManager();
-
 	if (isolate)
 		setenv("LIBCAMERA_IPA_FORCE_ISOLATION", "1", 1);
 
+	cm_ = new CameraManager();
+
 	if (cm_->start()) {
 		cerr << "Failed to start camera manager" << endl;
 		status_ = TestFail;
diff -pruN 0.5.0-1/test/log/log_api.cpp 0.5.2-2/test/log/log_api.cpp
--- 0.5.0-1/test/log/log_api.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/log/log_api.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -26,6 +26,11 @@ using namespace std;
 using namespace libcamera;
 
 LOG_DEFINE_CATEGORY(LogAPITest)
+LOG_DEFINE_CATEGORY(Cat0)
+LOG_DEFINE_CATEGORY(Cat1)
+LOG_DEFINE_CATEGORY(Cat2)
+LOG_DEFINE_CATEGORY(Cat3)
+LOG_DEFINE_CATEGORY(Cat4)
 
 class LogAPITest : public Test
 {
@@ -74,6 +79,34 @@ protected:
 		return TestPass;
 	}
 
+	int testEnvLevels()
+	{
+		setenv("LIBCAMERA_LOG_LEVELS",
+		       "Cat0:0,Cat0:9999,Cat1:INFO,Cat1:INVALID,Cat2:2,Cat2:-1,"
+		       "Cat3:ERROR,Cat3:{[]},Cat4:4,Cat4:rubbish",
+		       true);
+		logSetTarget(libcamera::LoggingTargetNone);
+
+		const std::pair<const LogCategory &, libcamera::LogSeverity> expected[] = {
+			{ _LOG_CATEGORY(Cat0)(), libcamera::LogDebug },
+			{ _LOG_CATEGORY(Cat1)(), libcamera::LogInfo },
+			{ _LOG_CATEGORY(Cat2)(), libcamera::LogWarning },
+			{ _LOG_CATEGORY(Cat3)(), libcamera::LogError },
+			{ _LOG_CATEGORY(Cat4)(), libcamera::LogFatal },
+		};
+		bool ok = true;
+
+		for (const auto &[c, s] : expected) {
+			if (c.severity() != s) {
+				ok = false;
+				cerr << "Severity of " << c.name() << " (" << c.severity() << ") "
+				     << "does not equal " << s << endl;
+			}
+		}
+
+		return ok ? TestPass : TestFail;
+	}
+
 	int testFile()
 	{
 		int fd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
@@ -135,7 +168,11 @@ protected:
 
 	int run() override
 	{
-		int ret = testFile();
+		int ret = testEnvLevels();
+		if (ret != TestPass)
+			return TestFail;
+
+		ret = testFile();
 		if (ret != TestPass)
 			return TestFail;
 
diff -pruN 0.5.0-1/test/log/log_process.cpp 0.5.2-2/test/log/log_process.cpp
--- 0.5.0-1/test/log/log_process.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/log/log_process.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -137,8 +137,6 @@ private:
 		exitCode_ = exitCode;
 	}
 
-	ProcessManager processManager_;
-
 	Process proc_;
 	Process::ExitStatus exitStatus_ = Process::NotExited;
 	string logPath_;
diff -pruN 0.5.0-1/test/log/meson.build 0.5.2-2/test/log/meson.build
--- 0.5.0-1/test/log/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/log/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -11,5 +11,6 @@ foreach test : log_test
                      link_with : test_libraries,
                      include_directories : test_includes_internal)
 
-    test(test['name'], exe, suite : 'log')
+    test(test['name'], exe, suite : 'log',
+         should_fail : test.get('should_fail', false))
 endforeach
diff -pruN 0.5.0-1/test/matrix.cpp 0.5.2-2/test/matrix.cpp
--- 0.5.0-1/test/matrix.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 0.5.2-2/test/matrix.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Matrix tests
+ */
+
+#include "libcamera/internal/matrix.h"
+
+#include <cmath>
+#include <iostream>
+
+#include "test.h"
+
+using namespace libcamera;
+
+#define ASSERT_EQ(a, b)                                                   \
+	if ((a) != (b)) {                                                 \
+		std::cout << #a " != " #b << " (line " << __LINE__ << ")" \
+			  << std::endl;                                   \
+		return TestFail;                                          \
+	}
+
+class MatrixTest : public Test
+{
+protected:
+	int run()
+	{
+		Matrix<double, 3, 3> m1;
+
+		ASSERT_EQ(m1[0][0], 0.0);
+		ASSERT_EQ(m1[0][1], 0.0);
+
+		constexpr Matrix<float, 2, 2> m2 = Matrix<float, 2, 2>().identity();
+		ASSERT_EQ(m2[0][0], 1.0);
+		ASSERT_EQ(m2[0][1], 0.0);
+		ASSERT_EQ(m2[1][0], 0.0);
+		ASSERT_EQ(m2[1][1], 1.0);
+
+		Matrix<float, 2, 2> m3{ { 2.0, 0.0, 0.0, 2.0 } };
+		Matrix<float, 2, 2> m4 = m3.inverse();
+
+		Matrix<float, 2, 2> m5 = m3 * m4;
+		ASSERT_EQ(m5[0][0], 1.0);
+		ASSERT_EQ(m5[0][1], 0.0);
+		ASSERT_EQ(m5[1][0], 0.0);
+		ASSERT_EQ(m5[1][1], 1.0);
+
+		return TestPass;
+	}
+};
+
+TEST_REGISTER(MatrixTest)
diff -pruN 0.5.0-1/test/meson.build 0.5.2-2/test/meson.build
--- 0.5.0-1/test/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -60,6 +60,7 @@ internal_tests = [
     {'name': 'file', 'sources': ['file.cpp']},
     {'name': 'flags', 'sources': ['flags.cpp']},
     {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']},
+    {'name': 'matrix', 'sources': ['matrix.cpp']},
     {'name': 'message', 'sources': ['message.cpp']},
     {'name': 'object', 'sources': ['object.cpp']},
     {'name': 'object-delete', 'sources': ['object-delete.cpp']},
diff -pruN 0.5.0-1/test/object-invoke.cpp 0.5.2-2/test/object-invoke.cpp
--- 0.5.0-1/test/object-invoke.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/object-invoke.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -58,6 +58,19 @@ public:
 		return 42;
 	}
 
+	struct MoveOnly {
+		MoveOnly() = default;
+		MoveOnly(const MoveOnly &) = delete;
+		MoveOnly &operator=(const MoveOnly &) = delete;
+		MoveOnly(MoveOnly &&) = default;
+		MoveOnly &operator=(MoveOnly &&) = default;
+	};
+
+	MoveOnly methodWithReturnMoveOnly()
+	{
+		return {};
+	}
+
 private:
 	Status status_;
 	int value_;
@@ -186,6 +199,10 @@ protected:
 			return TestFail;
 		}
 
+		/* Test invoking a method that returns type with no copy ctor/assignment. */
+		object_.invokeMethod(&InvokedObject::methodWithReturnMoveOnly,
+				     ConnectionTypeBlocking);
+
 		return TestPass;
 	}
 
diff -pruN 0.5.0-1/test/process/process_test.cpp 0.5.2-2/test/process/process_test.cpp
--- 0.5.0-1/test/process/process_test.cpp	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/test/process/process_test.cpp	2025-08-07 13:46:17.000000000 +0000
@@ -5,9 +5,9 @@
  * Process test
  */
 
+#include <array>
 #include <iostream>
 #include <unistd.h>
-#include <vector>
 
 #include <libcamera/base/event_dispatcher.h>
 #include <libcamera/base/thread.h>
@@ -48,8 +48,7 @@ protected:
 		Timer timeout;
 
 		int exitCode = 42;
-		vector<std::string> args;
-		args.push_back(to_string(exitCode));
+		std::array args{ to_string(exitCode) };
 		proc_.finished.connect(this, &ProcessTest::procFinished);
 
 		/* Test that kill() on an unstarted process is safe. */
@@ -87,8 +86,6 @@ private:
 		exitCode_ = exitCode;
 	}
 
-	ProcessManager processManager_;
-
 	Process proc_;
 	enum Process::ExitStatus exitStatus_;
 	int exitCode_;
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl
--- 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl	2025-08-07 13:46:17.000000000 +0000
@@ -31,13 +31,8 @@ template<>
 class IPADataSerializer<{{struct|name}}>
 {
 public:
-{{- serializer.serializer(struct, "")}}
-{%- if struct|has_fd %}
-{{serializer.deserializer_fd(struct, "")}}
-{%- else %}
-{{serializer.deserializer_no_fd(struct, "")}}
-{{serializer.deserializer_fd_simple(struct, "")}}
-{%- endif %}
+{{- serializer.serializer(struct)}}
+{{- serializer.deserializer(struct)}}
 };
 {% endfor %}
 
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
--- 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl	2025-08-07 13:46:17.000000000 +0000
@@ -61,15 +61,16 @@ namespace {{ns}} {
 			return;
 		}
 
-		ipc_ = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),
-							   proxyWorkerPath.c_str());
-		if (!ipc_->isConnected()) {
+		auto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),
+							       proxyWorkerPath.c_str());
+		if (!ipc->isConnected()) {
 			LOG(IPAProxy, Error) << "Failed to create IPCPipe";
 			return;
 		}
 
-		ipc_->recv.connect(this, &{{proxy_name}}::recvMessage);
+		ipc->recv.connect(this, &{{proxy_name}}::recvMessage);
 
+		ipc_ = std::move(ipc);
 		valid_ = true;
 		return;
 	}
@@ -96,7 +97,7 @@ namespace {{ns}} {
 
 {{proxy_name}}::~{{proxy_name}}()
 {
-	if (isolate_) {
+	if (ipc_) {
 		IPCMessage::Header header =
 			{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };
 		IPCMessage msg(header);
@@ -127,13 +128,13 @@ void {{proxy_name}}::recvMessage(const I
 {{proxy_funcs.func_sig(proxy_name, method)}}
 {
 	if (isolate_)
-		{{"return " if method|method_return_value != "void"}}{{method.mojom_name}}IPC(
+		return {{method.mojom_name}}IPC(
 {%- for param in method|method_param_names -%}
 		{{param}}{{- ", " if not loop.last}}
 {%- endfor -%}
 );
 	else
-		{{"return " if method|method_return_value != "void"}}{{method.mojom_name}}Thread(
+		return {{method.mojom_name}}Thread(
 {%- for param in method|method_param_names -%}
 		{{param}}{{- ", " if not loop.last}}
 {%- endfor -%}
@@ -159,16 +160,14 @@ void {{proxy_name}}::recvMessage(const I
 	state_ = ProxyRunning;
 	thread_.start();
 
-	{{ "return " if method|method_return_value != "void" -}}
-	proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking
+	return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking
 	{{- ", " if method|method_param_names}}
 	{%- for param in method|method_param_names -%}
 		{{param}}{{- ", " if not loop.last}}
 	{%- endfor -%}
 );
 {%- elif not method|is_async %}
-	{{ "return " if method|method_return_value != "void" -}}
-	ipa_->{{method.mojom_name}}(
+	return ipa_->{{method.mojom_name}}(
 	{%- for param in method|method_param_names -%}
 		{{param}}{{- ", " if not loop.last}}
 	{%- endfor -%}
@@ -206,7 +205,7 @@ void {{proxy_name}}::recvMessage(const I
 );
 {%- endif %}
 	if (_ret < 0) {
-		LOG(IPAProxy, Error) << "Failed to call {{method.mojom_name}}";
+		LOG(IPAProxy, Error) << "Failed to call {{method.mojom_name}}: " << _ret;
 {%- if method|method_return_value != "void" %}
 		return static_cast<{{method|method_return_value}}>(_ret);
 {%- else %}
@@ -239,10 +238,7 @@ void {{proxy_name}}::{{method.mojom_name
 	[[maybe_unused]] size_t dataSize,
 	[[maybe_unused]] const std::vector<SharedFD> &fds)
 {
-{%- for param in method.parameters %}
-	{{param|name}} {{param.mojom_name}};
-{%- endfor %}
-{{proxy_funcs.deserialize_call(method.parameters, 'data', 'fds', false, false, true, 'dataSize')}}
+{{proxy_funcs.deserialize_call(method.parameters, 'data', 'fds', false, true, true, 'dataSize')}}
 	{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});
 }
 {% endfor %}
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
--- 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl	2025-08-07 13:46:17.000000000 +0000
@@ -44,15 +44,6 @@ public:
 {{proxy_funcs.func_sig(proxy_name, method, "", false, true)|indent(8, true)}};
 {% endfor %}
 
-{%- for method in interface_event.methods %}
-	Signal<
-{%- for param in method.parameters -%}
-		{{"const " if not param|is_pod}}{{param|name}}{{" &" if not param|is_pod and not param|is_enum}}
-		{{- ", " if not loop.last}}
-{%- endfor -%}
-> {{method.mojom_name}};
-{% endfor %}
-
 private:
 	void recvMessage(const IPCMessage &data);
 
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
--- 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl	2025-08-07 13:46:17.000000000 +0000
@@ -32,13 +32,8 @@ template<>
 class IPADataSerializer<{{struct|name_full}}>
 {
 public:
-{{- serializer.serializer(struct, namespace_str)}}
-{%- if struct|has_fd %}
-{{serializer.deserializer_fd(struct, namespace_str)}}
-{%- else %}
-{{serializer.deserializer_no_fd(struct, namespace_str)}}
-{{serializer.deserializer_fd_simple(struct, namespace_str)}}
-{%- endif %}
+{{- serializer.serializer(struct)}}
+{{- serializer.deserializer(struct)}}
 };
 {% endfor %}
 
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl
--- 0.5.0-1/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl	2025-08-07 13:46:17.000000000 +0000
@@ -28,7 +28,7 @@
  #
  # \todo Avoid intermediate vectors
  #}
-{%- macro serializer_field(field, namespace, loop) %}
+{%- macro serializer_field(field, loop) %}
 {%- if field|is_pod or field|is_enum %}
 		std::vector<uint8_t> {{field.mojom_name}};
 		std::tie({{field.mojom_name}}, std::ignore) =
@@ -94,7 +94,7 @@
  # Generate code to deserialize \a field into object ret.
  # This code is meant to be used by the IPADataSerializer specialization.
  #}
-{%- macro deserializer_field(field, namespace, loop) %}
+{%- macro deserializer_field(field, loop) %}
 {% if field|is_pod or field|is_enum %}
 	{%- set field_size = (field|bit_width|int / 8)|int %}
 		{{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
@@ -182,7 +182,7 @@
  # Generate code for IPADataSerializer specialization, for serializing
  # \a struct.
  #}
-{%- macro serializer(struct, namespace) %}
+{%- macro serializer(struct) %}
 	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 	serialize(const {{struct|name_full}} &data,
 {%- if struct|needs_control_serializer %}
@@ -196,7 +196,7 @@
 		std::vector<SharedFD> retFds;
 {%- endif %}
 {%- for field in struct.fields %}
-{{serializer_field(field, namespace, loop)}}
+{{serializer_field(field, loop)}}
 {%- endfor %}
 {% if struct|has_fd %}
 		return {retData, retFds};
@@ -213,7 +213,7 @@
  # Generate code for IPADataSerializer specialization, for deserializing
  # \a struct, in the case that \a struct has file descriptors.
  #}
-{%- macro deserializer_fd(struct, namespace) %}
+{%- macro deserializer_fd(struct) %}
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t> &data,
 		    std::vector<SharedFD> &fds,
@@ -245,7 +245,7 @@
 		size_t dataSize = std::distance(dataBegin, dataEnd);
 		[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
 {%- for field in struct.fields -%}
-{{deserializer_field(field, namespace, loop)}}
+{{deserializer_field(field, loop)}}
 {%- endfor %}
 		return ret;
 	}
@@ -258,7 +258,7 @@
  # \a struct, in the case that \a struct has no file descriptors but requires
  # deserializers with file descriptors.
  #}
-{%- macro deserializer_fd_simple(struct, namespace) %}
+{%- macro deserializer_fd_simple(struct) %}
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t> &data,
 		    [[maybe_unused]] std::vector<SharedFD> &fds,
@@ -285,7 +285,7 @@
  # Generate code for IPADataSerializer specialization, for deserializing
  # \a struct, in the case that \a struct does not have file descriptors.
  #}
-{%- macro deserializer_no_fd(struct, namespace) %}
+{%- macro deserializer_no_fd(struct) %}
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t> &data,
 {%- if struct|needs_control_serializer %}
@@ -312,8 +312,22 @@
 
 		size_t dataSize = std::distance(dataBegin, dataEnd);
 {%- for field in struct.fields -%}
-{{deserializer_field(field, namespace, loop)}}
+{{deserializer_field(field, loop)}}
 {%- endfor %}
 		return ret;
 	}
 {%- endmacro %}
+
+{#
+ # \brief Deserialize a struct
+ #
+ # Generate code for IPADataSerializer specialization, for deserializing \a struct.
+ #}
+{%- macro deserializer(struct) %}
+{%- if struct|has_fd %}
+{{deserializer_fd(struct)}}
+{%- else %}
+{{deserializer_no_fd(struct)}}
+{{deserializer_fd_simple(struct)}}
+{%- endif %}
+{%- endmacro %}
diff -pruN 0.5.0-1/utils/codegen/ipc/generators/mojom_libcamera_generator.py 0.5.2-2/utils/codegen/ipc/generators/mojom_libcamera_generator.py
--- 0.5.0-1/utils/codegen/ipc/generators/mojom_libcamera_generator.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/ipc/generators/mojom_libcamera_generator.py	2025-08-07 13:46:17.000000000 +0000
@@ -166,7 +166,7 @@ def MethodParamOutputs(method):
     return method.response_parameters[1:]
 
 def MethodParamsHaveFd(parameters):
-    return len([x for x in parameters if HasFd(x)]) > 0
+    return any(x for x in parameters if HasFd(x))
 
 def MethodInputHasFd(method):
     return MethodParamsHaveFd(method.parameters)
@@ -465,9 +465,9 @@ class Generator(generator.Generator):
             'cmd_event_enum_name': '_%sEventCmd' % self.module_name,
             'consts': self.module.constants,
             'enums': self.module.enums,
-            'has_array': len([x for x in self.module.kinds.keys() if x[0] == 'a']) > 0,
-            'has_map': len([x for x in self.module.kinds.keys() if x[0] == 'm']) > 0,
-            'has_string': len([x for x in self.module.kinds.keys() if x[0] == 's']) > 0,
+            'has_array': any(x for x in self.module.kinds.keys() if x[0] == 'a'),
+            'has_map': any(x for x in self.module.kinds.keys() if x[0] == 'm'),
+            'has_string': any(x for x in self.module.kinds.keys() if x[0] == 's'),
             'has_namespace': self.module.mojom_namespace != '',
             'interface_event': GetEventInterface(self.module.interfaces),
             'interface_main': GetMainInterface(self.module.interfaces),
@@ -485,9 +485,9 @@ class Generator(generator.Generator):
         return {
             'consts': self.module.constants,
             'enums_gen_header': [x for x in self.module.enums if x.attributes is None or 'skipHeader' not in x.attributes],
-            'has_array': len([x for x in self.module.kinds.keys() if x[0] == 'a']) > 0,
-            'has_map': len([x for x in self.module.kinds.keys() if x[0] == 'm']) > 0,
-            'has_string': len([x for x in self.module.kinds.keys() if x[0] == 's']) > 0,
+            'has_array': any(x for x in self.module.kinds.keys() if x[0] == 'a'),
+            'has_map': any(x for x in self.module.kinds.keys() if x[0] == 'm'),
+            'has_string': any(x for x in self.module.kinds.keys() if x[0] == 's'),
             'structs_gen_header': [x for x in self.module.structs if x.attributes is None or 'skipHeader' not in x.attributes],
             'structs_gen_serializer': [x for x in self.module.structs if x.attributes is None or 'skipSerdes' not in x.attributes],
         }
diff -pruN 0.5.0-1/utils/codegen/meson.build 0.5.2-2/utils/codegen/meson.build
--- 0.5.0-1/utils/codegen/meson.build	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/codegen/meson.build	2025-08-07 13:46:17.000000000 +0000
@@ -16,4 +16,6 @@ gen_header = files('gen-header.sh')
 gen_ipa_pub_key = files('gen-ipa-pub-key.py')
 gen_tracepoints = files('gen-tp-header.py')
 
+py_mod_controls = files('controls.py')
+
 subdir('ipc')
diff -pruN 0.5.0-1/utils/gen-debug-controls.py 0.5.2-2/utils/gen-debug-controls.py
--- 0.5.0-1/utils/gen-debug-controls.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/gen-debug-controls.py	2025-08-07 13:46:17.000000000 +0000
@@ -17,8 +17,13 @@ import sys
 from dataclasses import dataclass
 from pathlib import Path
 
-logger = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
+fmt = '%(levelname)s: %(message)s'
+try:
+    import coloredlogs
+    coloredlogs.install(level=logging.INFO, fmt=fmt)
+except ImportError:
+    logging.basicConfig(level=logging.INFO, format=fmt)
+
 
 try:
     import ruamel.yaml as ruyaml
@@ -27,6 +32,8 @@ except:
         f'Failed to import ruamel.yaml. Please install the ruamel.yaml package.')
     sys.exit(1)
 
+logger = logging.getLogger(__name__)
+
 @dataclass
 class FoundMatch:
     file: os.PathLike
@@ -96,6 +103,7 @@ def main(argv):
             controls_map[k] = v
 
     obsolete_names = list(controls_map.keys())
+    found_by_name = {}
 
     for m in matches:
         if not m.type:
@@ -105,12 +113,19 @@ def main(argv):
             continue
 
         p = m.file.relative_to(root_dir)
+        logger.info(f"Found control {m.name} in {p}")
         desc = {'type': m.type,
                 'direction': 'out',
-                'description': f'Debug control {m.name} found in {p}:{m.line}'}
+                'description': f'Debug control {m.name} found in {p}'}
         if m.size is not None:
             desc['size'] = m.size
 
+        c = found_by_name.setdefault(m.name, m)
+        if c.type != m.type or c.size != m.size:
+            logger.error(
+                f"Found multiple entries for control '{m.name}' with differing type or size")
+            return 1
+
         if m.name in controls_map:
             # Can't use == for modified check because of the special yaml dicts.
             update_needed = False
@@ -127,7 +142,9 @@ def main(argv):
                 controls_map[m.name].clear()
                 controls_map[m.name].update(desc)
 
-            obsolete_names.remove(m.name)
+            # Don't try to remove more than once in case control was found multiple files.
+            if m.name in obsolete_names:
+                obsolete_names.remove(m.name)
         else:
             logger.info(f"Add control '{m.name}'")
             insert_before = len(controls)
@@ -156,6 +173,9 @@ def main(argv):
                  "#\n"))
         yaml.dump(doc, f)
 
+    p = ctrl_file.relative_to(Path.cwd(), walk_up=True)
+    logger.info(f"Sucessfully updated {p}")
+
     return 0
 
 
diff -pruN 0.5.0-1/utils/raspberrypi/ctt/ctt.py 0.5.2-2/utils/raspberrypi/ctt/ctt.py
--- 0.5.0-1/utils/raspberrypi/ctt/ctt.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/raspberrypi/ctt/ctt.py	2025-08-07 13:46:17.000000000 +0000
@@ -198,9 +198,12 @@ class Camera:
         """
         Write output to json
         """
-        self.json['rpi.cac']['cac'] = cacs
-        self.log += '\nCAC calibration written to json file'
-        print('Finished CAC calibration')
+        if cacs:
+            self.json['rpi.cac']['cac'] = cacs
+            self.log += '\nCAC calibration written to json file'
+            print('Finished CAC calibration')
+        else:
+            self.log += "\nCAC calibration failed"
 
 
     """
diff -pruN 0.5.0-1/utils/raspberrypi/ctt/ctt_alsc.py 0.5.2-2/utils/raspberrypi/ctt/ctt_alsc.py
--- 0.5.0-1/utils/raspberrypi/ctt/ctt_alsc.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/raspberrypi/ctt/ctt_alsc.py	2025-08-07 13:46:17.000000000 +0000
@@ -127,11 +127,12 @@ def alsc(Cam, Img, do_alsc_colour, plot=
     channels = [Img.channels[i] for i in Img.order]
     """
     calculate size of single rectangle.
-    -(-(w-1)//32) is a ceiling division. w-1 is to deal robustly with the case
-    where w is a multiple of 32.
+    The divisions here must ensure the final row/column of cells has a non-zero number of
+    pixels.
     """
     w, h = Img.w/2, Img.h/2
-    dx, dy = int(-(-(w-1)//grid_w)), int(-(-(h-1)//grid_h))
+    dx, dy = int((w - 1) // (grid_w - 1)), int((h - 1) // (grid_h - 1))
+
     """
     average the green channels into one
     """
diff -pruN 0.5.0-1/utils/raspberrypi/ctt/ctt_cac.py 0.5.2-2/utils/raspberrypi/ctt/ctt_cac.py
--- 0.5.0-1/utils/raspberrypi/ctt/ctt_cac.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/raspberrypi/ctt/ctt_cac.py	2025-08-07 13:46:17.000000000 +0000
@@ -108,12 +108,29 @@ def shifts_to_yaml(red_shift, blue_shift
         ybsgrid[xgridloc][ygridloc].append(blue_shift[3])
 
     # Now calculate the average pixel shift for each square in the grid
+    grid_incomplete = False
     for x in range(output_grid_size - 1):
         for y in range(output_grid_size - 1):
-            xrgrid[x, y] = np.mean(xrsgrid[x][y])
-            yrgrid[x, y] = np.mean(yrsgrid[x][y])
-            xbgrid[x, y] = np.mean(xbsgrid[x][y])
-            ybgrid[x, y] = np.mean(ybsgrid[x][y])
+            if xrsgrid[x][y]:
+                xrgrid[x, y] = np.mean(xrsgrid[x][y])
+            else:
+                grid_incomplete = True
+            if yrsgrid[x][y]:
+                yrgrid[x, y] = np.mean(yrsgrid[x][y])
+            else:
+                grid_incomplete = True
+            if xbsgrid[x][y]:
+                xbgrid[x, y] = np.mean(xbsgrid[x][y])
+            else:
+                grid_incomplete = True
+            if ybsgrid[x][y]:
+                ybgrid[x, y] = np.mean(ybsgrid[x][y])
+            else:
+                grid_incomplete = True
+
+    if grid_incomplete:
+        raise RuntimeError("\nERROR: CAC measurements do not span the image!"
+                           "\nConsider using improved CAC images, or remove them entirely.\n")
 
     # Next, we start to interpolate the central points of the grid that gets passed to the tuning file
     input_grids = np.array([xrgrid, yrgrid, xbgrid, ybgrid])
@@ -219,7 +236,12 @@ def cac(Cam):
     # tuning file
     print("\nCreating output grid")
     Cam.log += '\nCreating output grid'
-    rx, ry, bx, by = shifts_to_yaml(red_shift, blue_shift, image_size)
+    try:
+        rx, ry, bx, by = shifts_to_yaml(red_shift, blue_shift, image_size)
+    except RuntimeError as e:
+        print(str(e))
+        Cam.log += "\nCAC correction failed! CAC will not be enabled."
+        return {}
 
     print("CAC correction complete!")
     Cam.log += '\nCAC correction complete!'
diff -pruN 0.5.0-1/utils/rkisp1/gen-csc-table.py 0.5.2-2/utils/rkisp1/gen-csc-table.py
--- 0.5.0-1/utils/rkisp1/gen-csc-table.py	2025-04-03 13:38:25.000000000 +0000
+++ 0.5.2-2/utils/rkisp1/gen-csc-table.py	2025-08-07 13:46:17.000000000 +0000
@@ -147,6 +147,8 @@ def main(argv):
         description='Generate color space conversion table coefficients with '
         'configurable fixed-point precision.'
     )
+    parser.add_argument('--format', '-f', choices=['dec', 'hex'], default='hex',
+                        help='Number format')
     parser.add_argument('--invert', '-i', action='store_true',
                         help='Invert the color space conversion (YUV -> RGB)')
     parser.add_argument('--precision', '-p', default='Q1.7',
@@ -190,19 +192,29 @@ def main(argv):
         else:
             line = round_array(line)
 
-        # Convert coefficients to the number of bits selected by the precision.
-        # Negative values will be turned into positive integers using 2's
-        # complement.
-        line = [coeff & ((1 << precision.total) - 1) for coeff in line]
+        if args.format == 'hex':
+            # Convert coefficients to the number of bits selected by the precision.
+            # Negative values will be turned into positive integers using 2's
+            # complement.
+            line = [coeff & ((1 << precision.total) - 1) for coeff in line]
+
         rounded_coeffs.append(line)
 
     # Print the result as C code.
     nbits = 1 << (precision.total - 1).bit_length()
     nbytes = nbits // 4
-    print(f'static const u{nbits} {"yuv2rgb" if args.invert else "rgb2yuv"}_{args.encoding}_{quantization.name.lower()}_coeffs[] = {{')
+
+    if args.format == 'hex':
+        coeff_fmt = '0x{0:0' + str(nbytes) + 'x}'
+        sign = 'u'
+    else:
+        coeff_fmt = '{0}'
+        sign = 's'
+
+    print(f'static const {sign}{nbits} {"yuv2rgb" if args.invert else "rgb2yuv"}_{args.encoding}_{quantization.name.lower()}_coeffs[] = {{')
 
     for line in rounded_coeffs:
-        line = [f'0x{coeff:0{nbytes}x}' for coeff in line]
+        line = [coeff_fmt.format(coeff) for coeff in line]
 
         print(f'\t{", ".join(line)},')
 
