# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (C) 2019-2020, Raspberry Pi Ltd
#
# ctt_image_load.py - camera tuning tool image loading

from ctt_tools import *
from ctt_macbeth_locator import *
import json
import pyexiv2 as pyexif
import rawpy as raw


"""
Image class load image from raw data and extracts metadata.

Once image is extracted from data, it finds 24 16x16 patches for each
channel, centred at the macbeth chart squares
"""
class Image:
    def __init__(self, buf):
        self.buf = buf
        self.patches = None
        self.saturated = False

    '''
    obtain metadata from buffer
    '''
    def get_meta(self):
        self.ver = ba_to_b(self.buf[4:5])
        self.w = ba_to_b(self.buf[0xd0:0xd2])
        self.h = ba_to_b(self.buf[0xd2:0xd4])
        self.pad = ba_to_b(self.buf[0xd4:0xd6])
        self.fmt = self.buf[0xf5]
        self.sigbits = 2*self.fmt + 4
        self.pattern = self.buf[0xf4]
        self.exposure = ba_to_b(self.buf[0x90:0x94])
        self.againQ8 = ba_to_b(self.buf[0x94:0x96])
        self.againQ8_norm = self.againQ8/256
        camName = self.buf[0x10:0x10+128]
        camName_end = camName.find(0x00)
        self.camName = self.buf[0x10:0x10+128][:camName_end].decode()

        """
        Channel order depending on bayer pattern
        """
        bayer_case = {
            0: (0, 1, 2, 3),   # red
            1: (2, 0, 3, 1),   # green next to red
            2: (3, 2, 1, 0),   # green next to blue
            3: (1, 0, 3, 2),   # blue
            128: (0, 1, 2, 3)  # arbitrary order for greyscale casw
        }
        self.order = bayer_case[self.pattern]

        '''
        manual blacklevel - not robust
        '''
        if 'ov5647' in self.camName:
            self.blacklevel = 16
        else:
            self.blacklevel = 64
        self.blacklevel_16 = self.blacklevel << (6)
        return 1

    '''
    print metadata for debug
    '''
    def print_meta(self):
        print('\nData:')
        print('      ver = {}'.format(self.ver))
        print('      w = {}'.format(self.w))
        print('      h = {}'.format(self.h))
        print('      pad = {}'.format(self.pad))
        print('      fmt = {}'.format(self.fmt))
        print('      sigbits = {}'.format(self.sigbits))
        print('      pattern = {}'.format(self.pattern))
        print('      exposure = {}'.format(self.exposure))
        print('      againQ8 = {}'.format(self.againQ8))
        print('      againQ8_norm = {}'.format(self.againQ8_norm))
        print('      camName = {}'.format(self.camName))
        print('      blacklevel = {}'.format(self.blacklevel))
        print('      blacklevel_16 = {}'.format(self.blacklevel_16))

        return 1

    """
    get image from raw scanline data
    """
    def get_image(self, raw):
        self.dptr = []
        """
        check if data is 10 or 12 bits
        """
        if self.sigbits == 10:
            """
            calc length of scanline
            """
            lin_len = ((((((self.w+self.pad+3)>>2)) * 5)+31)>>5) * 32
            """
            stack scan lines into matrix
            """
            raw = np.array(raw).reshape(-1, lin_len).astype(np.int64)[:self.h, ...]
            """
            separate 5 bits in each package, stopping when w is satisfied
            """
            ba0 = raw[..., 0:5*((self.w+3)>>2):5]
            ba1 = raw[..., 1:5*((self.w+3)>>2):5]
            ba2 = raw[..., 2:5*((self.w+3)>>2):5]
            ba3 = raw[..., 3:5*((self.w+3)>>2):5]
            ba4 = raw[..., 4:5*((self.w+3)>>2):5]
            """
            assemble 10 bit numbers
            """
            ch0 = np.left_shift((np.left_shift(ba0, 2) + (ba4 % 4)), 6)
            ch1 = np.left_shift((np.left_shift(ba1, 2) + (np.right_shift(ba4, 2) % 4)), 6)
            ch2 = np.left_shift((np.left_shift(ba2, 2) + (np.right_shift(ba4, 4) % 4)), 6)
            ch3 = np.left_shift((np.left_shift(ba3, 2) + (np.right_shift(ba4, 6) % 4)), 6)
            """
            interleave bits
            """
            mat = np.empty((self.h, self.w), dtype=ch0.dtype)

            mat[..., 0::4] = ch0
            mat[..., 1::4] = ch1
            mat[..., 2::4] = ch2
            mat[..., 3::4] = ch3

            """
            There is som eleaking memory somewhere in the code. This code here
            seemed to make things good enough that the code would run for
            reasonable numbers of images, however this is techincally just a
            workaround. (sorry)
            """
            ba0, ba1, ba2, ba3, ba4 = None, None, None, None, None
            del ba0, ba1, ba2, ba3, ba4
            ch0, ch1, ch2, ch3 = None, None, None, None
            del ch0, ch1, ch2, ch3

            """
        same as before but 12 bit case
        """
        elif self.sigbits == 12:
            lin_len = ((((((self.w+self.pad+1)>>1)) * 3)+31)>>5) * 32
            raw = np.array(raw).reshape(-1, lin_len).astype(np.int64)[:self.h, ...]
            ba0 = raw[..., 0:3*((self.w+1)>>1):3]
            ba1 = raw[..., 1:3*((self.w+1)>>1):3]
            ba2 = raw[..., 2:3*((self.w+1)>>1):3]
            ch0 = np.left_shift((np.left_shift(ba0, 4) + ba2 % 16), 4)
            ch1 = np.left_shift((np.left_shift(ba1, 4) + (np.right_shift(ba2, 4)) % 16), 4)
            mat = np.empty((self.h, self.w), dtype=ch0.dtype)
            mat[..., 0::2] = ch0
            mat[..., 1::2] = ch1

        else:
            """
            data is neither 10 nor 12 or incorrect data
            """
            print('ERROR: wrong bit format, only 10 or 12 bit supported')
            return 0

        """
        separate bayer channels
        """
        c0 = mat[0::2, 0::2]
        c1 = mat[0::2, 1::2]
        c2 = mat[1::2, 0::2]
        c3 = mat[1::2, 1::2]
        self.channels = [c0, c1, c2, c3]
        return 1

    """
    obtain 16x16 patch centred at macbeth square centre for each channel
    """
    def get_patches(self, cen_coords, size=16):
        """
        obtain channel widths and heights
        """
        ch_w, ch_h = self.w, self.h
        cen_coords = list(np.array((cen_coords[0])).astype(np.int32))
        self.cen_coords = cen_coords
        """
        squares are ordered by stacking macbeth chart columns from
        left to right. Some useful patch indices:
            white = 3
            black = 23
            'reds' = 9, 10
            'blues' = 2, 5, 8, 20, 22
            'greens' = 6, 12, 17
            greyscale = 3, 7, 11, 15, 19, 23
        """
        all_patches = []
        for ch in self.channels:
            ch_patches = []
            for cen in cen_coords:
                '''
                macbeth centre is placed at top left of central 2x2 patch
                to account for rounding
                Patch pixels are sorted by pixel brightness so spatial
                information is lost.
                '''
                patch = ch[cen[1]-7:cen[1]+9, cen[0]-7:cen[0]+9].flatten()
                patch.sort()
                if patch[-5] == (2**self.sigbits-1)*2**(16-self.sigbits):
                    self.saturated = True
                ch_patches.append(patch)
                # print('\nNew Patch\n')
            all_patches.append(ch_patches)
            # print('\n\nNew Channel\n\n')
        self.patches = all_patches
        return 1


def brcm_load_image(Cam, im_str):
    """
    Load image where raw data and metadata is in the BRCM format
    """
    try:
        """
        create byte array
        """
        with open(im_str, 'rb') as image:
            f = image.read()
            b = bytearray(f)
        """
        return error if incorrect image address
        """
    except FileNotFoundError:
        print('\nERROR:\nInvalid image address')
        Cam.log += '\nWARNING: Invalid image address'
        return 0

    """
    return error if problem reading file
    """
    if f is None:
        print('\nERROR:\nProblem reading file')
        Cam.log += '\nWARNING: Problem readin file'
        return 0

    # print('\nLooking for EOI and BRCM header')
    """
    find end of image followed by BRCM header by turning
    bytearray into hex string and string matching with regexp
    """
    start = -1
    match = bytearray(b'\xff\xd9@BRCM')
    match_str = binascii.hexlify(match)
    b_str = binascii.hexlify(b)
    """
    note index is divided by two to go from string to hex
    """
    indices = [m.start()//2 for m in re.finditer(match_str, b_str)]
    # print(indices)
    try:
        start = indices[0] + 3
    except IndexError:
        print('\nERROR:\nNo Broadcom header found')
        Cam.log += '\nWARNING: No Broadcom header found!'
        return 0
    """
    extract data after header
    """
    # print('\nExtracting data after header')
    buf = b[start:start+32768]
    Img = Image(buf)
    Img.str = im_str
    # print('Data found successfully')

    """
    obtain metadata
    """
    # print('\nReading metadata')
    Img.get_meta()
    Cam.log += '\nExposure : {} us'.format(Img.exposure)
    Cam.log += '\nNormalised gain : {}'.format(Img.againQ8_norm)
    # print('Metadata read successfully')

    """
    obtain raw image data
    """
    # print('\nObtaining raw image data')
    raw = b[start+32768:]
    Img.get_image(raw)
    """
    delete raw to stop memory errors
    """
    raw = None
    del raw
    # print('Raw image data obtained successfully')

    return Img


def dng_load_image(Cam, im_str):
    try:
        Img = Image(None)

        # RawPy doesn't load all the image tags that we need, so we use py3exiv2
        metadata = pyexif.ImageMetadata(im_str)
        metadata.read()

        Img.ver = 100  # random value
        """
        The DNG and TIFF/EP specifications use different IFDs to store the raw
        image data and the Exif tags. DNG stores them in a SubIFD and in an Exif
        IFD respectively (named "SubImage1" and "Photo" by pyexiv2), while
        TIFF/EP stores them both in IFD0 (name "Image"). Both are used in "DNG"
        files, with libcamera-apps following the DNG recommendation and
        applications based on picamera2 following TIFF/EP.

        This code detects which tags are being used, and therefore extracts the
        correct values.
        """
        try:
            Img.w = metadata['Exif.SubImage1.ImageWidth'].value
            subimage = "SubImage1"
            photo = "Photo"
        except KeyError:
            Img.w = metadata['Exif.Image.ImageWidth'].value
            subimage = "Image"
            photo = "Image"
        Img.pad = 0
        Img.h = metadata[f'Exif.{subimage}.ImageLength'].value
        white = metadata[f'Exif.{subimage}.WhiteLevel'].value
        Img.sigbits = int(white).bit_length()
        Img.fmt = (Img.sigbits - 4) // 2
        Img.exposure = int(metadata[f'Exif.{photo}.ExposureTime'].value * 1000000)
        Img.againQ8 = metadata[f'Exif.{photo}.ISOSpeedRatings'].value * 256 / 100
        Img.againQ8_norm = Img.againQ8 / 256
        Img.camName = metadata['Exif.Image.Model'].value
        Img.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
        Img.blacklevel_16 = Img.blacklevel << (16 - Img.sigbits)
        bayer_case = {
            '0 1 1 2': (0, (0, 1, 2, 3)),
            '1 2 0 1': (1, (2, 0, 3, 1)),
            '2 1 1 0': (2, (3, 2, 1, 0)),
            '1 0 2 1': (3, (1, 0, 3, 2))
        }
        cfa_pattern = metadata[f'Exif.{subimage}.CFAPattern'].value
        Img.pattern = bayer_case[cfa_pattern][0]
        Img.order = bayer_case[cfa_pattern][1]

        # Now use RawPy tp get the raw Bayer pixels
        raw_im = raw.imread(im_str)
        raw_data = raw_im.raw_image
        shift = 16 - Img.sigbits
        c0 = np.left_shift(raw_data[0::2, 0::2].astype(np.int64), shift)
        c1 = np.left_shift(raw_data[0::2, 1::2].astype(np.int64), shift)
        c2 = np.left_shift(raw_data[1::2, 0::2].astype(np.int64), shift)
        c3 = np.left_shift(raw_data[1::2, 1::2].astype(np.int64), shift)
        Img.channels = [c0, c1, c2, c3]

    except Exception:
        print("\nERROR: failed to load DNG file", im_str)
        print("Either file does not exist or is incompatible")
        Cam.log += '\nERROR: DNG file does not exist or is incompatible'
        raise

    return Img


'''
load image from file location and perform calibration
check correct filetype

mac boolean is true if image is expected to contain macbeth chart and false
if not (alsc images don't have macbeth charts)
'''
def load_image(Cam, im_str, mac_config=None, show=False, mac=True, show_meta=False):
    """
    check image is correct filetype
    """
    if '.jpg' in im_str or '.jpeg' in im_str or '.brcm' in im_str or '.dng' in im_str:
        if '.dng' in im_str:
            Img = dng_load_image(Cam, im_str)
        else:
            Img = brcm_load_image(Cam, im_str)
        """
        handle errors smoothly if loading image failed
        """
        if Img == 0:
            return 0
        if show_meta:
            Img.print_meta()

        if mac:
            """
            find macbeth centres, discarding images that are too dark or light
            """
            av_chan = (np.mean(np.array(Img.channels), axis=0)/(2**16))
            av_val = np.mean(av_chan)
            # print(av_val)
            if av_val < Img.blacklevel_16/(2**16)+1/64:
                macbeth = None
                print('\nError: Image too dark!')
                Cam.log += '\nWARNING: Image too dark!'
            else:
                macbeth = find_macbeth(Cam, av_chan, mac_config)

            """
            if no macbeth found return error
            """
            if macbeth is None:
                print('\nERROR: No macbeth chart found')
                return 0
            mac_cen_coords = macbeth[1]
            # print('\nMacbeth centres located successfully')

            """
            obtain image patches
            """
            # print('\nObtaining image patches')
            Img.get_patches(mac_cen_coords)
            if Img.saturated:
                print('\nERROR: Macbeth patches have saturated')
                Cam.log += '\nWARNING: Macbeth patches have saturated!'
                return 0

        """
        clear memory
        """
        Img.buf = None
        del Img.buf

        # print('Image patches obtained successfully')

        """
        optional debug
        """
        if show and __name__ == '__main__':
            copy = sum(Img.channels)/2**18
            copy = np.reshape(copy, (Img.h//2, Img.w//2)).astype(np.float64)
            copy, _ = reshape(copy, 800)
            represent(copy)

        return Img

        """
    return error if incorrect filetype
    """
    else:
        # print('\nERROR:\nInvalid file extension')
        return 0


"""
bytearray splice to number little endian
"""
def ba_to_b(b):
    total = 0
    for i in range(len(b)):
        total += 256**i * b[i]
    return total
id='n335' href='#n335'>335</a>
<a id='n336' href='#n336'>336</a>
<a id='n337' href='#n337'>337</a>
<a id='n338' href='#n338'>338</a>
<a id='n339' href='#n339'>339</a>
<a id='n340' href='#n340'>340</a>
<a id='n341' href='#n341'>341</a>
<a id='n342' href='#n342'>342</a>
<a id='n343' href='#n343'>343</a>
<a id='n344' href='#n344'>344</a>
<a id='n345' href='#n345'>345</a>
<a id='n346' href='#n346'>346</a>
<a id='n347' href='#n347'>347</a>
<a id='n348' href='#n348'>348</a>
<a id='n349' href='#n349'>349</a>
<a id='n350' href='#n350'>350</a>
<a id='n351' href='#n351'>351</a>
<a id='n352' href='#n352'>352</a>
<a id='n353' href='#n353'>353</a>
<a id='n354' href='#n354'>354</a>
<a id='n355' href='#n355'>355</a>
<a id='n356' href='#n356'>356</a>
<a id='n357' href='#n357'>357</a>
<a id='n358' href='#n358'>358</a>
<a id='n359' href='#n359'>359</a>
<a id='n360' href='#n360'>360</a>
<a id='n361' href='#n361'>361</a>
<a id='n362' href='#n362'>362</a>
<a id='n363' href='#n363'>363</a>
<a id='n364' href='#n364'>364</a>
<a id='n365' href='#n365'>365</a>
<a id='n366' href='#n366'>366</a>
<a id='n367' href='#n367'>367</a>
<a id='n368' href='#n368'>368</a>
<a id='n369' href='#n369'>369</a>
<a id='n370' href='#n370'>370</a>
<a id='n371' href='#n371'>371</a>
<a id='n372' href='#n372'>372</a>
<a id='n373' href='#n373'>373</a>
<a id='n374' href='#n374'>374</a>
<a id='n375' href='#n375'>375</a>
<a id='n376' href='#n376'>376</a>
<a id='n377' href='#n377'>377</a>
<a id='n378' href='#n378'>378</a>
<a id='n379' href='#n379'>379</a>
<a id='n380' href='#n380'>380</a>
<a id='n381' href='#n381'>381</a>
<a id='n382' href='#n382'>382</a>
<a id='n383' href='#n383'>383</a>
<a id='n384' href='#n384'>384</a>
<a id='n385' href='#n385'>385</a>
<a id='n386' href='#n386'>386</a>
<a id='n387' href='#n387'>387</a>
<a id='n388' href='#n388'>388</a>
<a id='n389' href='#n389'>389</a>
<a id='n390' href='#n390'>390</a>
<a id='n391' href='#n391'>391</a>
<a id='n392' href='#n392'>392</a>
<a id='n393' href='#n393'>393</a>
<a id='n394' href='#n394'>394</a>
<a id='n395' href='#n395'>395</a>
<a id='n396' href='#n396'>396</a>
<a id='n397' href='#n397'>397</a>
<a id='n398' href='#n398'>398</a>
<a id='n399' href='#n399'>399</a>
<a id='n400' href='#n400'>400</a>
<a id='n401' href='#n401'>401</a>
<a id='n402' href='#n402'>402</a>
<a id='n403' href='#n403'>403</a>
<a id='n404' href='#n404'>404</a>
<a id='n405' href='#n405'>405</a>
<a id='n406' href='#n406'>406</a>
<a id='n407' href='#n407'>407</a>
<a id='n408' href='#n408'>408</a>
<a id='n409' href='#n409'>409</a>
<a id='n410' href='#n410'>410</a>
<a id='n411' href='#n411'>411</a>
<a id='n412' href='#n412'>412</a>
<a id='n413' href='#n413'>413</a>
<a id='n414' href='#n414'>414</a>
<a id='n415' href='#n415'>415</a>
<a id='n416' href='#n416'>416</a>
<a id='n417' href='#n417'>417</a>
<a id='n418' href='#n418'>418</a>
<a id='n419' href='#n419'>419</a>
<a id='n420' href='#n420'>420</a>
<a id='n421' href='#n421'>421</a>
<a id='n422' href='#n422'>422</a>
<a id='n423' href='#n423'>423</a>
<a id='n424' href='#n424'>424</a>
<a id='n425' href='#n425'>425</a>
<a id='n426' href='#n426'>426</a>
<a id='n427' href='#n427'>427</a>
<a id='n428' href='#n428'>428</a>
<a id='n429' href='#n429'>429</a>
<a id='n430' href='#n430'>430</a>
<a id='n431' href='#n431'>431</a>
<a id='n432' href='#n432'>432</a>
<a id='n433' href='#n433'>433</a>
<a id='n434' href='#n434'>434</a>
<a id='n435' href='#n435'>435</a>
<a id='n436' href='#n436'>436</a>
<a id='n437' href='#n437'>437</a>
<a id='n438' href='#n438'>438</a>
<a id='n439' href='#n439'>439</a>
<a id='n440' href='#n440'>440</a>
<a id='n441' href='#n441'>441</a>
<a id='n442' href='#n442'>442</a>
<a id='n443' href='#n443'>443</a>
<a id='n444' href='#n444'>444</a>
<a id='n445' href='#n445'>445</a>
<a id='n446' href='#n446'>446</a>
<a id='n447' href='#n447'>447</a>
<a id='n448' href='#n448'>448</a>
<a id='n449' href='#n449'>449</a>
<a id='n450' href='#n450'>450</a>
<a id='n451' href='#n451'>451</a>
<a id='n452' href='#n452'>452</a>
<a id='n453' href='#n453'>453</a>
<a id='n454' href='#n454'>454</a>
<a id='n455' href='#n455'>455</a>
<a id='n456' href='#n456'>456</a>
<a id='n457' href='#n457'>457</a>
<a id='n458' href='#n458'>458</a>
<a id='n459' href='#n459'>459</a>
<a id='n460' href='#n460'>460</a>
<a id='n461' href='#n461'>461</a>
<a id='n462' href='#n462'>462</a>
<a id='n463' href='#n463'>463</a>
<a id='n464' href='#n464'>464</a>
<a id='n465' href='#n465'>465</a>
<a id='n466' href='#n466'>466</a>
<a id='n467' href='#n467'>467</a>
<a id='n468' href='#n468'>468</a>
<a id='n469' href='#n469'>469</a>
<a id='n470' href='#n470'>470</a>
<a id='n471' href='#n471'>471</a>
<a id='n472' href='#n472'>472</a>
<a id='n473' href='#n473'>473</a>
<a id='n474' href='#n474'>474</a>
<a id='n475' href='#n475'>475</a>
<a id='n476' href='#n476'>476</a>
<a id='n477' href='#n477'>477</a>
<a id='n478' href='#n478'>478</a>
<a id='n479' href='#n479'>479</a>
<a id='n480' href='#n480'>480</a>
<a id='n481' href='#n481'>481</a>
<a id='n482' href='#n482'>482</a>
<a id='n483' href='#n483'>483</a>
<a id='n484' href='#n484'>484</a>
<a id='n485' href='#n485'>485</a>
<a id='n486' href='#n486'>486</a>
<a id='n487' href='#n487'>487</a>
<a id='n488' href='#n488'>488</a>
<a id='n489' href='#n489'>489</a>
<a id='n490' href='#n490'>490</a>
<a id='n491' href='#n491'>491</a>
<a id='n492' href='#n492'>492</a>
<a id='n493' href='#n493'>493</a>
<a id='n494' href='#n494'>494</a>
<a id='n495' href='#n495'>495</a>
<a id='n496' href='#n496'>496</a>
<a id='n497' href='#n497'>497</a>
<a id='n498' href='#n498'>498</a>
<a id='n499' href='#n499'>499</a>
<a id='n500' href='#n500'>500</a>
<a id='n501' href='#n501'>501</a>
<a id='n502' href='#n502'>502</a>
<a id='n503' href='#n503'>503</a>
<a id='n504' href='#n504'>504</a>
<a id='n505' href='#n505'>505</a>
<a id='n506' href='#n506'>506</a>
<a id='n507' href='#n507'>507</a>
<a id='n508' href='#n508'>508</a>
<a id='n509' href='#n509'>509</a>
<a id='n510' href='#n510'>510</a>
<a id='n511' href='#n511'>511</a>
<a id='n512' href='#n512'>512</a>
<a id='n513' href='#n513'>513</a>
<a id='n514' href='#n514'>514</a>
<a id='n515' href='#n515'>515</a>
<a id='n516' href='#n516'>516</a>
<a id='n517' href='#n517'>517</a>
<a id='n518' href='#n518'>518</a>
<a id='n519' href='#n519'>519</a>
<a id='n520' href='#n520'>520</a>
<a id='n521' href='#n521'>521</a>
<a id='n522' href='#n522'>522</a>
<a id='n523' href='#n523'>523</a>
<a id='n524' href='#n524'>524</a>
<a id='n525' href='#n525'>525</a>
<a id='n526' href='#n526'>526</a>
<a id='n527' href='#n527'>527</a>
<a id='n528' href='#n528'>528</a>
<a id='n529' href='#n529'>529</a>
<a id='n530' href='#n530'>530</a>
<a id='n531' href='#n531'>531</a>
<a id='n532' href='#n532'>532</a>
<a id='n533' href='#n533'>533</a>
<a id='n534' href='#n534'>534</a>
<a id='n535' href='#n535'>535</a>
<a id='n536' href='#n536'>536</a>
<a id='n537' href='#n537'>537</a>
<a id='n538' href='#n538'>538</a>
<a id='n539' href='#n539'>539</a>
<a id='n540' href='#n540'>540</a>
<a id='n541' href='#n541'>541</a>
<a id='n542' href='#n542'>542</a>
<a id='n543' href='#n543'>543</a>
<a id='n544' href='#n544'>544</a>
<a id='n545' href='#n545'>545</a>
<a id='n546' href='#n546'>546</a>
<a id='n547' href='#n547'>547</a>
<a id='n548' href='#n548'>548</a>
<a id='n549' href='#n549'>549</a>
<a id='n550' href='#n550'>550</a>
<a id='n551' href='#n551'>551</a>
<a id='n552' href='#n552'>552</a>
<a id='n553' href='#n553'>553</a>
<a id='n554' href='#n554'>554</a>
<a id='n555' href='#n555'>555</a>
<a id='n556' href='#n556'>556</a>
<a id='n557' href='#n557'>557</a>
<a id='n558' href='#n558'>558</a>
<a id='n559' href='#n559'>559</a>
<a id='n560' href='#n560'>560</a>
<a id='n561' href='#n561'>561</a>
<a id='n562' href='#n562'>562</a>
<a id='n563' href='#n563'>563</a>
<a id='n564' href='#n564'>564</a>
<a id='n565' href='#n565'>565</a>
<a id='n566' href='#n566'>566</a>
<a id='n567' href='#n567'>567</a>
<a id='n568' href='#n568'>568</a>
<a id='n569' href='#n569'>569</a>
<a id='n570' href='#n570'>570</a>
<a id='n571' href='#n571'>571</a>
<a id='n572' href='#n572'>572</a>
<a id='n573' href='#n573'>573</a>
<a id='n574' href='#n574'>574</a>
<a id='n575' href='#n575'>575</a>
<a id='n576' href='#n576'>576</a>
<a id='n577' href='#n577'>577</a>
<a id='n578' href='#n578'>578</a>
<a id='n579' href='#n579'>579</a>
<a id='n580' href='#n580'>580</a>
<a id='n581' href='#n581'>581</a>
<a id='n582' href='#n582'>582</a>
<a id='n583' href='#n583'>583</a>
<a id='n584' href='#n584'>584</a>
<a id='n585' href='#n585'>585</a>
<a id='n586' href='#n586'>586</a>
<a id='n587' href='#n587'>587</a>
<a id='n588' href='#n588'>588</a>
<a id='n589' href='#n589'>589</a>
<a id='n590' href='#n590'>590</a>
<a id='n591' href='#n591'>591</a>
<a id='n592' href='#n592'>592</a>
<a id='n593' href='#n593'>593</a>
<a id='n594' href='#n594'>594</a>
<a id='n595' href='#n595'>595</a>
<a id='n596' href='#n596'>596</a>
<a id='n597' href='#n597'>597</a>
<a id='n598' href='#n598'>598</a>
<a id='n599' href='#n599'>599</a>
<a id='n600' href='#n600'>600</a>
<a id='n601' href='#n601'>601</a>
<a id='n602' href='#n602'>602</a>
<a id='n603' href='#n603'>603</a>
<a id='n604' href='#n604'>604</a>
<a id='n605' href='#n605'>605</a>
<a id='n606' href='#n606'>606</a>
<a id='n607' href='#n607'>607</a>
<a id='n608' href='#n608'>608</a>
<a id='n609' href='#n609'>609</a>
<a id='n610' href='#n610'>610</a>
<a id='n611' href='#n611'>611</a>
<a id='n612' href='#n612'>612</a>
<a id='n613' href='#n613'>613</a>
<a id='n614' href='#n614'>614</a>
<a id='n615' href='#n615'>615</a>
<a id='n616' href='#n616'>616</a>
<a id='n617' href='#n617'>617</a>
<a id='n618' href='#n618'>618</a>
<a id='n619' href='#n619'>619</a>
<a id='n620' href='#n620'>620</a>
<a id='n621' href='#n621'>621</a>
<a id='n622' href='#n622'>622</a>
<a id='n623' href='#n623'>623</a>
<a id='n624' href='#n624'>624</a>
<a id='n625' href='#n625'>625</a>
<a id='n626' href='#n626'>626</a>
<a id='n627' href='#n627'>627</a>
<a id='n628' href='#n628'>628</a>
<a id='n629' href='#n629'>629</a>
<a id='n630' href='#n630'>630</a>
<a id='n631' href='#n631'>631</a>
<a id='n632' href='#n632'>632</a>
<a id='n633' href='#n633'>633</a>
<a id='n634' href='#n634'>634</a>
<a id='n635' href='#n635'>635</a>
<a id='n636' href='#n636'>636</a>
<a id='n637' href='#n637'>637</a>
<a id='n638' href='#n638'>638</a>
<a id='n639' href='#n639'>639</a>
<a id='n640' href='#n640'>640</a>
<a id='n641' href='#n641'>641</a>
<a id='n642' href='#n642'>642</a>
<a id='n643' href='#n643'>643</a>
<a id='n644' href='#n644'>644</a>
<a id='n645' href='#n645'>645</a>
<a id='n646' href='#n646'>646</a>
<a id='n647' href='#n647'>647</a>
<a id='n648' href='#n648'>648</a>
<a id='n649' href='#n649'>649</a>
<a id='n650' href='#n650'>650</a>
<a id='n651' href='#n651'>651</a>
<a id='n652' href='#n652'>652</a>
<a id='n653' href='#n653'>653</a>
<a id='n654' href='#n654'>654</a>
<a id='n655' href='#n655'>655</a>
<a id='n656' href='#n656'>656</a>
<a id='n657' href='#n657'>657</a>
<a id='n658' href='#n658'>658</a>
<a id='n659' href='#n659'>659</a>
<a id='n660' href='#n660'>660</a>
<a id='n661' href='#n661'>661</a>
<a id='n662' href='#n662'>662</a>
<a id='n663' href='#n663'>663</a>
<a id='n664' href='#n664'>664</a>
<a id='n665' href='#n665'>665</a>
<a id='n666' href='#n666'>666</a>
<a id='n667' href='#n667'>667</a>
<a id='n668' href='#n668'>668</a>
<a id='n669' href='#n669'>669</a>
<a id='n670' href='#n670'>670</a>
<a id='n671' href='#n671'>671</a>
<a id='n672' href='#n672'>672</a>
<a id='n673' href='#n673'>673</a>
<a id='n674' href='#n674'>674</a>
<a id='n675' href='#n675'>675</a>
<a id='n676' href='#n676'>676</a>
<a id='n677' href='#n677'>677</a>
<a id='n678' href='#n678'>678</a>
<a id='n679' href='#n679'>679</a>
<a id='n680' href='#n680'>680</a>
<a id='n681' href='#n681'>681</a>
<a id='n682' href='#n682'>682</a>
<a id='n683' href='#n683'>683</a>
<a id='n684' href='#n684'>684</a>
<a id='n685' href='#n685'>685</a>
<a id='n686' href='#n686'>686</a>
<a id='n687' href='#n687'>687</a>
<a id='n688' href='#n688'>688</a>
<a id='n689' href='#n689'>689</a>
<a id='n690' href='#n690'>690</a>
<a id='n691' href='#n691'>691</a>
<a id='n692' href='#n692'>692</a>
<a id='n693' href='#n693'>693</a>
<a id='n694' href='#n694'>694</a>
<a id='n695' href='#n695'>695</a>
<a id='n696' href='#n696'>696</a>
<a id='n697' href='#n697'>697</a>
<a id='n698' href='#n698'>698</a>
<a id='n699' href='#n699'>699</a>
<a id='n700' href='#n700'>700</a>
<a id='n701' href='#n701'>701</a>
<a id='n702' href='#n702'>702</a>
<a id='n703' href='#n703'>703</a>
<a id='n704' href='#n704'>704</a>
<a id='n705' href='#n705'>705</a>
<a id='n706' href='#n706'>706</a>
<a id='n707' href='#n707'>707</a>
<a id='n708' href='#n708'>708</a>
</pre></td>
<td class='lines'><pre><code><span class="hl com">/* SPDX-License-Identifier: BSD-2-Clause */</span>
<span class="hl com">/*</span>
<span class="hl com"> * Copyright (C) 2019, Raspberry Pi (Trading) Limited</span>
<span class="hl com"> *</span>
<span class="hl com"> * agc.cpp - AGC/AEC control algorithm</span>
<span class="hl com"> */</span>

<span class="hl ppc">#include &lt;map&gt;</span>

<span class="hl ppc">#include</span> <span class="hl pps">&quot;linux/bcm2835-isp.h&quot;</span><span class="hl ppc"></span>

<span class="hl ppc">#include</span> <span class="hl pps">&quot;libcamera/internal/log.h&quot;</span><span class="hl ppc"></span>

<span class="hl ppc">#include</span> <span class="hl pps">&quot;../awb_status.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;../device_status.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;../histogram.hpp&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;../lux_status.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;../metadata.hpp&quot;</span><span class="hl ppc"></span>

<span class="hl ppc">#include</span> <span class="hl pps">&quot;agc.hpp&quot;</span><span class="hl ppc"></span>

<span class="hl kwa">using namespace</span> RPiController<span class="hl opt">;</span>
<span class="hl kwa">using namespace</span> libcamera<span class="hl opt">;</span>

<span class="hl kwd">LOG_DEFINE_CATEGORY</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">)</span>

<span class="hl ppc">#define NAME</span> <span class="hl pps">&quot;rpi.agc&quot;</span><span class="hl ppc"></span>

<span class="hl ppc">#define PIPELINE_BITS 13</span> <span class="hl slc">// seems to be a 13-bit pipeline</span>
<span class="hl ppc"></span>
<span class="hl kwb">void</span> <span class="hl kwc">AgcMeteringMode</span><span class="hl opt">::</span><span class="hl kwd">Read</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">int</span> num <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;weights&quot;</span><span class="hl opt">)) {</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>num <span class="hl opt">==</span> AGC_STATS_SIZE<span class="hl opt">)</span>
			<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;AgcConfig: too many weights&quot;</span><span class="hl opt">);</span>
		weights<span class="hl opt">[</span>num<span class="hl opt">++] =</span> p<span class="hl opt">.</span>second<span class="hl opt">.</span>get_value<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;();</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>num <span class="hl opt">!=</span> AGC_STATS_SIZE<span class="hl opt">)</span>
		<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;AgcConfig: insufficient weights&quot;</span><span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">static</span> <span class="hl kwc">std</span><span class="hl opt">::</span>string
<span class="hl kwd">read_metering_modes</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>map<span class="hl opt">&lt;</span><span class="hl kwc">std</span><span class="hl opt">::</span>string<span class="hl opt">,</span> AgcMeteringMode<span class="hl opt">&gt; &amp;</span>metering_modes<span class="hl opt">,</span>
		    <span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>string first<span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">) {</span>
		AgcMeteringMode metering_mode<span class="hl opt">;</span>
		metering_mode<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>p<span class="hl opt">.</span>second<span class="hl opt">);</span>
		metering_modes<span class="hl opt">[</span>p<span class="hl opt">.</span>first<span class="hl opt">] =</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">move</span><span class="hl opt">(</span>metering_mode<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>first<span class="hl opt">.</span><span class="hl kwd">empty</span><span class="hl opt">())</span>
			first <span class="hl opt">=</span> p<span class="hl opt">.</span>first<span class="hl opt">;</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">return</span> first<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">static int</span> <span class="hl kwd">read_double_list</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>vector<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt; &amp;</span>list<span class="hl opt">,</span>
			    <span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">)</span>
		list<span class="hl opt">.</span><span class="hl kwd">push_back</span><span class="hl opt">(</span>p<span class="hl opt">.</span>second<span class="hl opt">.</span>get_value<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;());</span>
	<span class="hl kwa">return</span> list<span class="hl opt">.</span><span class="hl kwd">size</span><span class="hl opt">();</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">AgcExposureMode</span><span class="hl opt">::</span><span class="hl kwd">Read</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">int</span> num_shutters <span class="hl opt">=</span>
		<span class="hl kwd">read_double_list</span><span class="hl opt">(</span>shutter<span class="hl opt">,</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;shutter&quot;</span><span class="hl opt">));</span>
	<span class="hl kwb">int</span> num_ags <span class="hl opt">=</span> <span class="hl kwd">read_double_list</span><span class="hl opt">(</span>gain<span class="hl opt">,</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;gain&quot;</span><span class="hl opt">));</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>num_shutters <span class="hl opt">&lt;</span> <span class="hl num">2</span> <span class="hl opt">||</span> num_ags <span class="hl opt">&lt;</span> <span class="hl num">2</span><span class="hl opt">)</span>
		<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span>
			<span class="hl str">&quot;AgcConfig: must have at least two entries in exposure profile&quot;</span><span class="hl opt">);</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>num_shutters <span class="hl opt">!=</span> num_ags<span class="hl opt">)</span>
		<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span>
			<span class="hl str">&quot;AgcConfig: expect same number of exposure and gain entries in exposure profile&quot;</span><span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">static</span> <span class="hl kwc">std</span><span class="hl opt">::</span>string
<span class="hl kwd">read_exposure_modes</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>map<span class="hl opt">&lt;</span><span class="hl kwc">std</span><span class="hl opt">::</span>string<span class="hl opt">,</span> AgcExposureMode<span class="hl opt">&gt; &amp;</span>exposure_modes<span class="hl opt">,</span>
		    <span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>string first<span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">) {</span>
		AgcExposureMode exposure_mode<span class="hl opt">;</span>
		exposure_mode<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>p<span class="hl opt">.</span>second<span class="hl opt">);</span>
		exposure_modes<span class="hl opt">[</span>p<span class="hl opt">.</span>first<span class="hl opt">] =</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">move</span><span class="hl opt">(</span>exposure_mode<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>first<span class="hl opt">.</span><span class="hl kwd">empty</span><span class="hl opt">())</span>
			first <span class="hl opt">=</span> p<span class="hl opt">.</span>first<span class="hl opt">;</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">return</span> first<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">AgcConstraint</span><span class="hl opt">::</span><span class="hl kwd">Read</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>string bound_string <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwc">std</span><span class="hl opt">::</span>string<span class="hl opt">&gt;(</span><span class="hl str">&quot;bound&quot;</span><span class="hl opt">,</span> <span class="hl str">&quot;&quot;</span><span class="hl opt">);</span>
	<span class="hl kwd">transform</span><span class="hl opt">(</span>bound_string<span class="hl opt">.</span><span class="hl kwd">begin</span><span class="hl opt">(),</span> bound_string<span class="hl opt">.</span><span class="hl kwd">end</span><span class="hl opt">(),</span>
		  bound_string<span class="hl opt">.</span><span class="hl kwd">begin</span><span class="hl opt">(), ::</span>toupper<span class="hl opt">);</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>bound_string <span class="hl opt">!=</span> <span class="hl str">&quot;UPPER&quot;</span> <span class="hl opt">&amp;&amp;</span> bound_string <span class="hl opt">!=</span> <span class="hl str">&quot;LOWER&quot;</span><span class="hl opt">)</span>
		<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span>
			<span class="hl str">&quot;AGC constraint type should be UPPER or LOWER&quot;</span><span class="hl opt">);</span>
	bound <span class="hl opt">=</span> bound_string <span class="hl opt">==</span> <span class="hl str">&quot;UPPER&quot;</span> <span class="hl opt">?</span> <span class="hl kwc">Bound</span><span class="hl opt">::</span>UPPER <span class="hl opt">:</span> <span class="hl kwc">Bound</span><span class="hl opt">::</span>LOWER<span class="hl opt">;</span>
	q_lo <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;q_lo&quot;</span><span class="hl opt">);</span>
	q_hi <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;q_hi&quot;</span><span class="hl opt">);</span>
	Y_target<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;y_target&quot;</span><span class="hl opt">));</span>
<span class="hl opt">}</span>

<span class="hl kwb">static</span> AgcConstraintMode
<span class="hl kwd">read_constraint_mode</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	AgcConstraintMode mode<span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">) {</span>
		AgcConstraint constraint<span class="hl opt">;</span>
		constraint<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>p<span class="hl opt">.</span>second<span class="hl opt">);</span>
		mode<span class="hl opt">.</span><span class="hl kwd">push_back</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">move</span><span class="hl opt">(</span>constraint<span class="hl opt">));</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">return</span> mode<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">static</span> <span class="hl kwc">std</span><span class="hl opt">::</span>string <span class="hl kwd">read_constraint_modes</span><span class="hl opt">(</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>map<span class="hl opt">&lt;</span><span class="hl kwc">std</span><span class="hl opt">::</span>string<span class="hl opt">,</span> AgcConstraintMode<span class="hl opt">&gt; &amp;</span>constraint_modes<span class="hl opt">,</span>
	<span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>string first<span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>p <span class="hl opt">:</span> params<span class="hl opt">) {</span>
		constraint_modes<span class="hl opt">[</span>p<span class="hl opt">.</span>first<span class="hl opt">] =</span> <span class="hl kwd">read_constraint_mode</span><span class="hl opt">(</span>p<span class="hl opt">.</span>second<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>first<span class="hl opt">.</span><span class="hl kwd">empty</span><span class="hl opt">())</span>
			first <span class="hl opt">=</span> p<span class="hl opt">.</span>first<span class="hl opt">;</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">return</span> first<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">AgcConfig</span><span class="hl opt">::</span><span class="hl kwd">Read</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;AgcConfig&quot;</span><span class="hl opt">;</span>
	default_metering_mode <span class="hl opt">=</span> <span class="hl kwd">read_metering_modes</span><span class="hl opt">(</span>
		metering_modes<span class="hl opt">,</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;metering_modes&quot;</span><span class="hl opt">));</span>
	default_exposure_mode <span class="hl opt">=</span> <span class="hl kwd">read_exposure_modes</span><span class="hl opt">(</span>
		exposure_modes<span class="hl opt">,</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;exposure_modes&quot;</span><span class="hl opt">));</span>
	default_constraint_mode <span class="hl opt">=</span> <span class="hl kwd">read_constraint_modes</span><span class="hl opt">(</span>
		constraint_modes<span class="hl opt">,</span> params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;constraint_modes&quot;</span><span class="hl opt">));</span>
	Y_target<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>params<span class="hl opt">.</span><span class="hl kwd">get_child</span><span class="hl opt">(</span><span class="hl str">&quot;y_target&quot;</span><span class="hl opt">));</span>
	speed <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;speed&quot;</span><span class="hl opt">,</span> <span class="hl num">0.2</span><span class="hl opt">);</span>
	startup_frames <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">uint16_t</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;startup_frames&quot;</span><span class="hl opt">,</span> <span class="hl num">10</span><span class="hl opt">);</span>
	fast_reduce_threshold <span class="hl opt">=</span>
		params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;fast_reduce_threshold&quot;</span><span class="hl opt">,</span> <span class="hl num">0.4</span><span class="hl opt">);</span>
	base_ev <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;base_ev&quot;</span><span class="hl opt">,</span> <span class="hl num">1.0</span><span class="hl opt">);</span>
	<span class="hl slc">// Start with quite a low value as ramping up is easier than ramping down.</span>
	default_exposure_time <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;default_exposure_time&quot;</span><span class="hl opt">,</span> <span class="hl num">1000</span><span class="hl opt">);</span>
	default_analogue_gain <span class="hl opt">=</span> params<span class="hl opt">.</span>get<span class="hl opt">&lt;</span><span class="hl kwb">double</span><span class="hl opt">&gt;(</span><span class="hl str">&quot;default_analogue_gain&quot;</span><span class="hl opt">,</span> <span class="hl num">1.0</span><span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">Agc</span><span class="hl opt">(</span>Controller <span class="hl opt">*</span>controller<span class="hl opt">)</span>
	<span class="hl opt">:</span> <span class="hl kwd">AgcAlgorithm</span><span class="hl opt">(</span>controller<span class="hl opt">),</span> <span class="hl kwd">metering_mode_</span><span class="hl opt">(</span><span class="hl kwc">nullptr</span><span class="hl opt">),</span>
	  <span class="hl kwd">exposure_mode_</span><span class="hl opt">(</span><span class="hl kwc">nullptr</span><span class="hl opt">),</span> <span class="hl kwd">constraint_mode_</span><span class="hl opt">(</span><span class="hl kwc">nullptr</span><span class="hl opt">),</span>
	  <span class="hl kwd">frame_count_</span><span class="hl opt">(</span><span class="hl num">0</span><span class="hl opt">),</span> <span class="hl kwd">lock_count_</span><span class="hl opt">(</span><span class="hl num">0</span><span class="hl opt">),</span>
	  <span class="hl kwd">last_target_exposure_</span><span class="hl opt">(</span><span class="hl num">0.0</span><span class="hl opt">),</span>
	  <span class="hl kwd">ev_</span><span class="hl opt">(</span><span class="hl num">1.0</span><span class="hl opt">),</span> <span class="hl kwd">flicker_period_</span><span class="hl opt">(</span><span class="hl num">0.0</span><span class="hl opt">),</span>
	  <span class="hl kwd">fixed_shutter_</span><span class="hl opt">(</span><span class="hl num">0</span><span class="hl opt">),</span> <span class="hl kwd">fixed_analogue_gain_</span><span class="hl opt">(</span><span class="hl num">0.0</span><span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwd">memset</span><span class="hl opt">(&amp;</span>awb_<span class="hl opt">,</span> <span class="hl num">0</span><span class="hl opt">,</span> <span class="hl kwa">sizeof</span><span class="hl opt">(</span>awb_<span class="hl opt">));</span>
	<span class="hl slc">// Setting status_.total_exposure_value_ to zero initially tells us</span>
	<span class="hl slc">// it&apos;s not been calculated yet (i.e. Process hasn&apos;t yet run).</span>
	<span class="hl kwd">memset</span><span class="hl opt">(&amp;</span>status_<span class="hl opt">,</span> <span class="hl num">0</span><span class="hl opt">,</span> <span class="hl kwa">sizeof</span><span class="hl opt">(</span>status_<span class="hl opt">));</span>
	status_<span class="hl opt">.</span>ev <span class="hl opt">=</span> ev_<span class="hl opt">;</span>
	<span class="hl kwd">memset</span><span class="hl opt">(&amp;</span>last_device_status_<span class="hl opt">,</span> <span class="hl num">0</span><span class="hl opt">,</span> <span class="hl kwa">sizeof</span><span class="hl opt">(</span>last_device_status_<span class="hl opt">));</span>
<span class="hl opt">}</span>

<span class="hl kwb">char const</span> <span class="hl opt">*</span><span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">Name</span><span class="hl opt">()</span> <span class="hl kwb">const</span>
<span class="hl opt">{</span>
	<span class="hl kwa">return</span> NAME<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">Read</span><span class="hl opt">(</span><span class="hl kwc">boost</span><span class="hl opt">::</span><span class="hl kwc">property_tree</span><span class="hl opt">::</span>ptree <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>params<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Agc&quot;</span><span class="hl opt">;</span>
	config_<span class="hl opt">.</span><span class="hl kwd">Read</span><span class="hl opt">(</span>params<span class="hl opt">);</span>
	<span class="hl slc">// Set the config&apos;s defaults (which are the first ones it read) as our</span>
	<span class="hl slc">// current modes, until someone changes them.  (they&apos;re all known to</span>
	<span class="hl slc">// exist at this point)</span>
	metering_mode_name_ <span class="hl opt">=</span> config_<span class="hl opt">.</span>default_metering_mode<span class="hl opt">;</span>
	metering_mode_ <span class="hl opt">= &amp;</span>config_<span class="hl opt">.</span>metering_modes<span class="hl opt">[</span>metering_mode_name_<span class="hl opt">];</span>
	exposure_mode_name_ <span class="hl opt">=</span> config_<span class="hl opt">.</span>default_exposure_mode<span class="hl opt">;</span>
	exposure_mode_ <span class="hl opt">= &amp;</span>config_<span class="hl opt">.</span>exposure_modes<span class="hl opt">[</span>exposure_mode_name_<span class="hl opt">];</span>
	constraint_mode_name_ <span class="hl opt">=</span> config_<span class="hl opt">.</span>default_constraint_mode<span class="hl opt">;</span>
	constraint_mode_ <span class="hl opt">= &amp;</span>config_<span class="hl opt">.</span>constraint_modes<span class="hl opt">[</span>constraint_mode_name_<span class="hl opt">];</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetEv</span><span class="hl opt">(</span><span class="hl kwb">double</span> ev<span class="hl opt">)</span>
<span class="hl opt">{</span>
	ev_ <span class="hl opt">=</span> ev<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetFlickerPeriod</span><span class="hl opt">(</span><span class="hl kwb">double</span> flicker_period<span class="hl opt">)</span>
<span class="hl opt">{</span>
	flicker_period_ <span class="hl opt">=</span> flicker_period<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetFixedShutter</span><span class="hl opt">(</span><span class="hl kwb">double</span> fixed_shutter<span class="hl opt">)</span>
<span class="hl opt">{</span>
	fixed_shutter_ <span class="hl opt">=</span> fixed_shutter<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetFixedAnalogueGain</span><span class="hl opt">(</span><span class="hl kwb">double</span> fixed_analogue_gain<span class="hl opt">)</span>
<span class="hl opt">{</span>
	fixed_analogue_gain_ <span class="hl opt">=</span> fixed_analogue_gain<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetMeteringMode</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>metering_mode_name<span class="hl opt">)</span>
<span class="hl opt">{</span>
	metering_mode_name_ <span class="hl opt">=</span> metering_mode_name<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetExposureMode</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>exposure_mode_name<span class="hl opt">)</span>
<span class="hl opt">{</span>
	exposure_mode_name_ <span class="hl opt">=</span> exposure_mode_name<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SetConstraintMode</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>constraint_mode_name<span class="hl opt">)</span>
<span class="hl opt">{</span>
	constraint_mode_name_ <span class="hl opt">=</span> constraint_mode_name<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">SwitchMode</span><span class="hl opt">([[</span>maybe_unused<span class="hl opt">]]</span> CameraMode <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>camera_mode<span class="hl opt">,</span>
		     Metadata <span class="hl opt">*</span>metadata<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwd">housekeepConfig</span><span class="hl opt">();</span>

	<span class="hl kwa">if</span> <span class="hl opt">(</span>fixed_shutter_ <span class="hl opt">!=</span> <span class="hl num">0.0</span> <span class="hl opt">&amp;&amp;</span> fixed_analogue_gain_ <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
		<span class="hl slc">// We&apos;re going to reset the algorithm here with these fixed values.</span>

		<span class="hl kwd">fetchAwbStatus</span><span class="hl opt">(</span>metadata<span class="hl opt">);</span>
		<span class="hl kwb">double</span> min_colour_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">({</span> awb_<span class="hl opt">.</span>gain_r<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_g<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_b<span class="hl opt">,</span> <span class="hl num">1.0</span> <span class="hl opt">});</span>
		<span class="hl kwd">ASSERT</span><span class="hl opt">(</span>min_colour_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">);</span>

		<span class="hl slc">// This is the equivalent of computeTargetExposure and applyDigitalGain.</span>
		target_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span> fixed_shutter_ <span class="hl opt">*</span> fixed_analogue_gain_<span class="hl opt">;</span>
		target_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> target_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">/</span> min_colour_gain<span class="hl opt">;</span>

		<span class="hl slc">// Equivalent of filterExposure. This resets any &quot;history&quot;.</span>
		filtered_ <span class="hl opt">=</span> target_<span class="hl opt">;</span>

		<span class="hl slc">// Equivalent of divideUpExposure.</span>
		filtered_<span class="hl opt">.</span>shutter <span class="hl opt">=</span> fixed_shutter_<span class="hl opt">;</span>
		filtered_<span class="hl opt">.</span>analogue_gain <span class="hl opt">=</span> fixed_analogue_gain_<span class="hl opt">;</span>
	<span class="hl opt">}</span> <span class="hl kwa">else if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>total_exposure_value<span class="hl opt">) {</span>
		<span class="hl slc">// On a mode switch, it&apos;s possible the exposure profile could change,</span>
		<span class="hl slc">// or a fixed exposure/gain might be set so we divide up the exposure/</span>
		<span class="hl slc">// gain again, but we don&apos;t change any target values.</span>
		<span class="hl kwd">divideUpExposure</span><span class="hl opt">();</span>
	<span class="hl opt">}</span> <span class="hl kwa">else</span> <span class="hl opt">{</span>
		<span class="hl slc">// We come through here on startup, when at least one of the shutter</span>
		<span class="hl slc">// or gain has not been fixed. We must still write those values out so</span>
		<span class="hl slc">// that they will be applied immediately. We supply some arbitrary defaults</span>
		<span class="hl slc">// for any that weren&apos;t set.</span>

		<span class="hl slc">// Equivalent of divideUpExposure.</span>
		filtered_<span class="hl opt">.</span>shutter <span class="hl opt">=</span> fixed_shutter_ <span class="hl opt">?</span> fixed_shutter_ <span class="hl opt">:</span> config_<span class="hl opt">.</span>default_exposure_time<span class="hl opt">;</span>
		filtered_<span class="hl opt">.</span>analogue_gain <span class="hl opt">=</span> fixed_analogue_gain_ <span class="hl opt">?</span> fixed_analogue_gain_ <span class="hl opt">:</span> config_<span class="hl opt">.</span>default_analogue_gain<span class="hl opt">;</span>
	<span class="hl opt">}</span>

	<span class="hl kwd">writeAndFinish</span><span class="hl opt">(</span>metadata<span class="hl opt">,</span> <span class="hl kwa">false</span><span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">Prepare</span><span class="hl opt">(</span>Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">)</span>
<span class="hl opt">{</span>
	status_<span class="hl opt">.</span>digital_gain <span class="hl opt">=</span> <span class="hl num">1.0</span><span class="hl opt">;</span>
	<span class="hl kwd">fetchAwbStatus</span><span class="hl opt">(</span>image_metadata<span class="hl opt">);</span> <span class="hl slc">// always fetch it so that Process knows it&apos;s been done</span>

	<span class="hl kwa">if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>total_exposure_value<span class="hl opt">) {</span>
		<span class="hl slc">// Process has run, so we have meaningful values.</span>
		DeviceStatus device_status<span class="hl opt">;</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>image_metadata<span class="hl opt">-&gt;</span><span class="hl kwd">Get</span><span class="hl opt">(</span><span class="hl str">&quot;device.status&quot;</span><span class="hl opt">,</span> device_status<span class="hl opt">) ==</span> <span class="hl num">0</span><span class="hl opt">) {</span>
			<span class="hl kwb">double</span> actual_exposure <span class="hl opt">=</span> device_status<span class="hl opt">.</span>shutter_speed <span class="hl opt">*</span>
						 device_status<span class="hl opt">.</span>analogue_gain<span class="hl opt">;</span>
			<span class="hl kwa">if</span> <span class="hl opt">(</span>actual_exposure<span class="hl opt">) {</span>
				status_<span class="hl opt">.</span>digital_gain <span class="hl opt">=</span>
					status_<span class="hl opt">.</span>total_exposure_value <span class="hl opt">/</span>
					actual_exposure<span class="hl opt">;</span>
				<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Want total exposure &quot;</span> <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>total_exposure_value<span class="hl opt">;</span>
				<span class="hl slc">// Never ask for a gain &lt; 1.0, and also impose</span>
				<span class="hl slc">// some upper limit. Make it customisable?</span>
				status_<span class="hl opt">.</span>digital_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">max</span><span class="hl opt">(</span>
					<span class="hl num">1.0</span><span class="hl opt">,</span>
					<span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>status_<span class="hl opt">.</span>digital_gain<span class="hl opt">,</span> <span class="hl num">4.0</span><span class="hl opt">));</span>
				<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Actual exposure &quot;</span> <span class="hl opt">&lt;&lt;</span> actual_exposure<span class="hl opt">;</span>
				<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Use digital_gain &quot;</span> <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>digital_gain<span class="hl opt">;</span>
				<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Effective exposure &quot;</span> <span class="hl opt">&lt;&lt;</span> actual_exposure <span class="hl opt">*</span> status_<span class="hl opt">.</span>digital_gain<span class="hl opt">;</span>
				<span class="hl slc">// Decide whether AEC/AGC has converged.</span>
				<span class="hl kwd">updateLockStatus</span><span class="hl opt">(</span>device_status<span class="hl opt">);</span>
			<span class="hl opt">}</span>
		<span class="hl opt">}</span> <span class="hl kwa">else</span>
			<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Warning<span class="hl opt">) &lt;&lt;</span> <span class="hl kwd">Name</span><span class="hl opt">() &lt;&lt;</span> <span class="hl str">&quot;: no device metadata&quot;</span><span class="hl opt">;</span>
		image_metadata<span class="hl opt">-&gt;</span><span class="hl kwd">Set</span><span class="hl opt">(</span><span class="hl str">&quot;agc.status&quot;</span><span class="hl opt">,</span> status_<span class="hl opt">);</span>
	<span class="hl opt">}</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">Process</span><span class="hl opt">(</span>StatisticsPtr <span class="hl opt">&amp;</span>stats<span class="hl opt">,</span> Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">)</span>
<span class="hl opt">{</span>
	frame_count_<span class="hl opt">++;</span>
	<span class="hl slc">// First a little bit of housekeeping, fetching up-to-date settings and</span>
	<span class="hl slc">// configuration, that kind of thing.</span>
	<span class="hl kwd">housekeepConfig</span><span class="hl opt">();</span>
	<span class="hl slc">// Get the current exposure values for the frame that&apos;s just arrived.</span>
	<span class="hl kwd">fetchCurrentExposure</span><span class="hl opt">(</span>image_metadata<span class="hl opt">);</span>
	<span class="hl slc">// Compute the total gain we require relative to the current exposure.</span>
	<span class="hl kwb">double</span> gain<span class="hl opt">,</span> target_Y<span class="hl opt">;</span>
	<span class="hl kwd">computeGain</span><span class="hl opt">(</span>stats<span class="hl opt">.</span><span class="hl kwd">get</span><span class="hl opt">(),</span> image_metadata<span class="hl opt">,</span> gain<span class="hl opt">,</span> target_Y<span class="hl opt">);</span>
	<span class="hl slc">// Now compute the target (final) exposure which we think we want.</span>
	<span class="hl kwd">computeTargetExposure</span><span class="hl opt">(</span>gain<span class="hl opt">);</span>
	<span class="hl slc">// Some of the exposure has to be applied as digital gain, so work out</span>
	<span class="hl slc">// what that is. This function also tells us whether it&apos;s decided to</span>
	<span class="hl slc">// &quot;desaturate&quot; the image more quickly.</span>
	<span class="hl kwb">bool</span> desaturate <span class="hl opt">=</span> <span class="hl kwd">applyDigitalGain</span><span class="hl opt">(</span>gain<span class="hl opt">,</span> target_Y<span class="hl opt">);</span>
	<span class="hl slc">// The results have to be filtered so as not to change too rapidly.</span>
	<span class="hl kwd">filterExposure</span><span class="hl opt">(</span>desaturate<span class="hl opt">);</span>
	<span class="hl slc">// The last thing is to divide up the exposure value into a shutter time</span>
	<span class="hl slc">// and analogue_gain, according to the current exposure mode.</span>
	<span class="hl kwd">divideUpExposure</span><span class="hl opt">();</span>
	<span class="hl slc">// Finally advertise what we&apos;ve done.</span>
	<span class="hl kwd">writeAndFinish</span><span class="hl opt">(</span>image_metadata<span class="hl opt">,</span> desaturate<span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">updateLockStatus</span><span class="hl opt">(</span>DeviceStatus <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>device_status<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">const double</span> ERROR_FACTOR <span class="hl opt">=</span> <span class="hl num">0.10</span><span class="hl opt">;</span> <span class="hl slc">// make these customisable?</span>
	<span class="hl kwb">const int</span> MAX_LOCK_COUNT <span class="hl opt">=</span> <span class="hl num">5</span><span class="hl opt">;</span>
	<span class="hl slc">// Reset &quot;lock count&quot; when we exceed this multiple of ERROR_FACTOR</span>
	<span class="hl kwb">const double</span> RESET_MARGIN <span class="hl opt">=</span> <span class="hl num">1.5</span><span class="hl opt">;</span>

	<span class="hl slc">// Add 200us to the exposure time error to allow for line quantisation.</span>
	<span class="hl kwb">double</span> exposure_error <span class="hl opt">=</span> last_device_status_<span class="hl opt">.</span>shutter_speed <span class="hl opt">*</span> ERROR_FACTOR <span class="hl opt">+</span> <span class="hl num">200</span><span class="hl opt">;</span>
	<span class="hl kwb">double</span> gain_error <span class="hl opt">=</span> last_device_status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">*</span> ERROR_FACTOR<span class="hl opt">;</span>
	<span class="hl kwb">double</span> target_error <span class="hl opt">=</span> last_target_exposure_ <span class="hl opt">*</span> ERROR_FACTOR<span class="hl opt">;</span>

	<span class="hl slc">// Note that we don&apos;t know the exposure/gain limits of the sensor, so</span>
	<span class="hl slc">// the values we keep requesting may be unachievable. For this reason</span>
	<span class="hl slc">// we only insist that we&apos;re close to values in the past few frames.</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>device_status<span class="hl opt">.</span>shutter_speed <span class="hl opt">&gt;</span> last_device_status_<span class="hl opt">.</span>shutter_speed <span class="hl opt">-</span> exposure_error <span class="hl opt">&amp;&amp;</span>
	    device_status<span class="hl opt">.</span>shutter_speed <span class="hl opt">&lt;</span> last_device_status_<span class="hl opt">.</span>shutter_speed <span class="hl opt">+</span> exposure_error <span class="hl opt">&amp;&amp;</span>
	    device_status<span class="hl opt">.</span>analogue_gain <span class="hl opt">&gt;</span> last_device_status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">-</span> gain_error <span class="hl opt">&amp;&amp;</span>
	    device_status<span class="hl opt">.</span>analogue_gain <span class="hl opt">&lt;</span> last_device_status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">+</span> gain_error <span class="hl opt">&amp;&amp;</span>
	    status_<span class="hl opt">.</span>target_exposure_value <span class="hl opt">&gt;</span> last_target_exposure_ <span class="hl opt">-</span> target_error <span class="hl opt">&amp;&amp;</span>
	    status_<span class="hl opt">.</span>target_exposure_value <span class="hl opt">&lt;</span> last_target_exposure_ <span class="hl opt">+</span> target_error<span class="hl opt">)</span>
		lock_count_ <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>lock_count_ <span class="hl opt">+</span> <span class="hl num">1</span><span class="hl opt">,</span> MAX_LOCK_COUNT<span class="hl opt">);</span>
	<span class="hl kwa">else if</span> <span class="hl opt">(</span>device_status<span class="hl opt">.</span>shutter_speed <span class="hl opt">&lt;</span> last_device_status_<span class="hl opt">.</span>shutter_speed <span class="hl opt">-</span> RESET_MARGIN <span class="hl opt">*</span> exposure_error <span class="hl opt">||</span>
		 device_status<span class="hl opt">.</span>shutter_speed <span class="hl opt">&gt;</span> last_device_status_<span class="hl opt">.</span>shutter_speed <span class="hl opt">+</span> RESET_MARGIN <span class="hl opt">*</span> exposure_error <span class="hl opt">||</span>
		 device_status<span class="hl opt">.</span>analogue_gain <span class="hl opt">&lt;</span> last_device_status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">-</span> RESET_MARGIN <span class="hl opt">*</span> gain_error <span class="hl opt">||</span>
		 device_status<span class="hl opt">.</span>analogue_gain <span class="hl opt">&gt;</span> last_device_status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">+</span> RESET_MARGIN <span class="hl opt">*</span> gain_error <span class="hl opt">||</span>
		 status_<span class="hl opt">.</span>target_exposure_value <span class="hl opt">&lt;</span> last_target_exposure_ <span class="hl opt">-</span> RESET_MARGIN <span class="hl opt">*</span> target_error <span class="hl opt">||</span>
		 status_<span class="hl opt">.</span>target_exposure_value <span class="hl opt">&gt;</span> last_target_exposure_ <span class="hl opt">+</span> RESET_MARGIN <span class="hl opt">*</span> target_error<span class="hl opt">)</span>
		lock_count_ <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span>

	last_device_status_ <span class="hl opt">=</span> device_status<span class="hl opt">;</span>
	last_target_exposure_ <span class="hl opt">=</span> status_<span class="hl opt">.</span>target_exposure_value<span class="hl opt">;</span>

	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Lock count updated to &quot;</span> <span class="hl opt">&lt;&lt;</span> lock_count_<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>locked <span class="hl opt">=</span> lock_count_ <span class="hl opt">==</span> MAX_LOCK_COUNT<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">static void</span> <span class="hl kwd">copy_string</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>s<span class="hl opt">,</span> <span class="hl kwb">char</span> <span class="hl opt">*</span>d<span class="hl opt">,</span> <span class="hl kwb">size_t</span> size<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">size_t</span> length <span class="hl opt">=</span> s<span class="hl opt">.</span><span class="hl kwd">copy</span><span class="hl opt">(</span>d<span class="hl opt">,</span> size <span class="hl opt">-</span> <span class="hl num">1</span><span class="hl opt">);</span>
	d<span class="hl opt">[</span>length<span class="hl opt">] =</span> <span class="hl str">&apos;\0&apos;</span><span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">housekeepConfig</span><span class="hl opt">()</span>
<span class="hl opt">{</span>
	<span class="hl slc">// First fetch all the up-to-date settings, so no one else has to do it.</span>
	status_<span class="hl opt">.</span>ev <span class="hl opt">=</span> ev_<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">=</span> fixed_shutter_<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">=</span> fixed_analogue_gain_<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>flicker_period <span class="hl opt">=</span> flicker_period_<span class="hl opt">;</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;ev &quot;</span> <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>ev <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; fixed_shutter &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; fixed_analogue_gain &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>fixed_analogue_gain<span class="hl opt">;</span>
	<span class="hl slc">// Make sure the &quot;mode&quot; pointers point to the up-to-date things, if</span>
	<span class="hl slc">// they&apos;ve changed.</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span><span class="hl kwd">strcmp</span><span class="hl opt">(</span>metering_mode_name_<span class="hl opt">.</span><span class="hl kwd">c_str</span><span class="hl opt">(),</span> status_<span class="hl opt">.</span>metering_mode<span class="hl opt">)) {</span>
		<span class="hl kwc">auto</span> it <span class="hl opt">=</span> config_<span class="hl opt">.</span>metering_modes<span class="hl opt">.</span><span class="hl kwd">find</span><span class="hl opt">(</span>metering_mode_name_<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>it <span class="hl opt">==</span> config_<span class="hl opt">.</span>metering_modes<span class="hl opt">.</span><span class="hl kwd">end</span><span class="hl opt">())</span>
			<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;Agc: no metering mode &quot;</span> <span class="hl opt">+</span>
						 metering_mode_name_<span class="hl opt">);</span>
		metering_mode_ <span class="hl opt">= &amp;</span>it<span class="hl opt">-&gt;</span>second<span class="hl opt">;</span>
		<span class="hl kwd">copy_string</span><span class="hl opt">(</span>metering_mode_name_<span class="hl opt">,</span> status_<span class="hl opt">.</span>metering_mode<span class="hl opt">,</span>
			    <span class="hl kwa">sizeof</span><span class="hl opt">(</span>status_<span class="hl opt">.</span>metering_mode<span class="hl opt">));</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span><span class="hl kwd">strcmp</span><span class="hl opt">(</span>exposure_mode_name_<span class="hl opt">.</span><span class="hl kwd">c_str</span><span class="hl opt">(),</span> status_<span class="hl opt">.</span>exposure_mode<span class="hl opt">)) {</span>
		<span class="hl kwc">auto</span> it <span class="hl opt">=</span> config_<span class="hl opt">.</span>exposure_modes<span class="hl opt">.</span><span class="hl kwd">find</span><span class="hl opt">(</span>exposure_mode_name_<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>it <span class="hl opt">==</span> config_<span class="hl opt">.</span>exposure_modes<span class="hl opt">.</span><span class="hl kwd">end</span><span class="hl opt">())</span>
			<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;Agc: no exposure profile &quot;</span> <span class="hl opt">+</span>
						 exposure_mode_name_<span class="hl opt">);</span>
		exposure_mode_ <span class="hl opt">= &amp;</span>it<span class="hl opt">-&gt;</span>second<span class="hl opt">;</span>
		<span class="hl kwd">copy_string</span><span class="hl opt">(</span>exposure_mode_name_<span class="hl opt">,</span> status_<span class="hl opt">.</span>exposure_mode<span class="hl opt">,</span>
			    <span class="hl kwa">sizeof</span><span class="hl opt">(</span>status_<span class="hl opt">.</span>exposure_mode<span class="hl opt">));</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span><span class="hl kwd">strcmp</span><span class="hl opt">(</span>constraint_mode_name_<span class="hl opt">.</span><span class="hl kwd">c_str</span><span class="hl opt">(),</span> status_<span class="hl opt">.</span>constraint_mode<span class="hl opt">)) {</span>
		<span class="hl kwc">auto</span> it <span class="hl opt">=</span>
			config_<span class="hl opt">.</span>constraint_modes<span class="hl opt">.</span><span class="hl kwd">find</span><span class="hl opt">(</span>constraint_mode_name_<span class="hl opt">);</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>it <span class="hl opt">==</span> config_<span class="hl opt">.</span>constraint_modes<span class="hl opt">.</span><span class="hl kwd">end</span><span class="hl opt">())</span>
			<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;Agc: no constraint list &quot;</span> <span class="hl opt">+</span>
						 constraint_mode_name_<span class="hl opt">);</span>
		constraint_mode_ <span class="hl opt">= &amp;</span>it<span class="hl opt">-&gt;</span>second<span class="hl opt">;</span>
		<span class="hl kwd">copy_string</span><span class="hl opt">(</span>constraint_mode_name_<span class="hl opt">,</span> status_<span class="hl opt">.</span>constraint_mode<span class="hl opt">,</span>
			    <span class="hl kwa">sizeof</span><span class="hl opt">(</span>status_<span class="hl opt">.</span>constraint_mode<span class="hl opt">));</span>
	<span class="hl opt">}</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;exposure_mode &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> exposure_mode_name_ <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; constraint_mode &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> constraint_mode_name_ <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; metering_mode &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> metering_mode_name_<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">fetchCurrentExposure</span><span class="hl opt">(</span>Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwc">std</span><span class="hl opt">::</span>unique_lock<span class="hl opt">&lt;</span>Metadata<span class="hl opt">&gt;</span> <span class="hl kwd">lock</span><span class="hl opt">(*</span>image_metadata<span class="hl opt">);</span>
	DeviceStatus <span class="hl opt">*</span>device_status <span class="hl opt">=</span>
		image_metadata<span class="hl opt">-&gt;</span>GetLocked<span class="hl opt">&lt;</span>DeviceStatus<span class="hl opt">&gt;(</span><span class="hl str">&quot;device.status&quot;</span><span class="hl opt">);</span>
	<span class="hl kwa">if</span> <span class="hl opt">(!</span>device_status<span class="hl opt">)</span>
		<span class="hl kwa">throw</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">runtime_error</span><span class="hl opt">(</span><span class="hl str">&quot;Agc: no device metadata&quot;</span><span class="hl opt">);</span>
	current_<span class="hl opt">.</span>shutter <span class="hl opt">=</span> device_status<span class="hl opt">-&gt;</span>shutter_speed<span class="hl opt">;</span>
	current_<span class="hl opt">.</span>analogue_gain <span class="hl opt">=</span> device_status<span class="hl opt">-&gt;</span>analogue_gain<span class="hl opt">;</span>
	AgcStatus <span class="hl opt">*</span>agc_status <span class="hl opt">=</span>
		image_metadata<span class="hl opt">-&gt;</span>GetLocked<span class="hl opt">&lt;</span>AgcStatus<span class="hl opt">&gt;(</span><span class="hl str">&quot;agc.status&quot;</span><span class="hl opt">);</span>
	current_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> agc_status <span class="hl opt">?</span> agc_status<span class="hl opt">-&gt;</span>total_exposure_value <span class="hl opt">:</span> <span class="hl num">0</span><span class="hl opt">;</span>
	current_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span> current_<span class="hl opt">.</span>shutter <span class="hl opt">*</span> current_<span class="hl opt">.</span>analogue_gain<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">fetchAwbStatus</span><span class="hl opt">(</span>Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">)</span>
<span class="hl opt">{</span>
	awb_<span class="hl opt">.</span>gain_r <span class="hl opt">=</span> <span class="hl num">1.0</span><span class="hl opt">;</span> <span class="hl slc">// in case not found in metadata</span>
	awb_<span class="hl opt">.</span>gain_g <span class="hl opt">=</span> <span class="hl num">1.0</span><span class="hl opt">;</span>
	awb_<span class="hl opt">.</span>gain_b <span class="hl opt">=</span> <span class="hl num">1.0</span><span class="hl opt">;</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>image_metadata<span class="hl opt">-&gt;</span><span class="hl kwd">Get</span><span class="hl opt">(</span><span class="hl str">&quot;awb.status&quot;</span><span class="hl opt">,</span> awb_<span class="hl opt">) !=</span> <span class="hl num">0</span><span class="hl opt">)</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Warning<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Agc: no AWB status found&quot;</span><span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">static double</span> <span class="hl kwd">compute_initial_Y</span><span class="hl opt">(</span>bcm2835_isp_stats <span class="hl opt">*</span>stats<span class="hl opt">,</span> AwbStatus <span class="hl kwb">const</span> <span class="hl opt">&amp;</span>awb<span class="hl opt">,</span>
				<span class="hl kwb">double</span> weights<span class="hl opt">[],</span> <span class="hl kwb">double</span> gain<span class="hl opt">)</span>
<span class="hl opt">{</span>
	bcm2835_isp_stats_region <span class="hl opt">*</span>regions <span class="hl opt">=</span> stats<span class="hl opt">-&gt;</span>agc_stats<span class="hl opt">;</span>
	<span class="hl slc">// Note how the calculation below means that equal weights give you</span>
	<span class="hl slc">// &quot;average&quot; metering (i.e. all pixels equally important).</span>
	<span class="hl kwb">double</span> R_sum <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">,</span> G_sum <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">,</span> B_sum <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">,</span> pixel_sum <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwb">int</span> i <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span> i <span class="hl opt">&lt;</span> AGC_STATS_SIZE<span class="hl opt">;</span> i<span class="hl opt">++) {</span>
		<span class="hl kwb">double</span> counted <span class="hl opt">=</span> regions<span class="hl opt">[</span>i<span class="hl opt">].</span>counted<span class="hl opt">;</span>
		<span class="hl kwb">double</span> r_sum <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>regions<span class="hl opt">[</span>i<span class="hl opt">].</span>r_sum <span class="hl opt">*</span> gain<span class="hl opt">, ((</span><span class="hl num">1</span> <span class="hl opt">&lt;&lt;</span> PIPELINE_BITS<span class="hl opt">) -</span> <span class="hl num">1</span><span class="hl opt">) *</span> counted<span class="hl opt">);</span>
		<span class="hl kwb">double</span> g_sum <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>regions<span class="hl opt">[</span>i<span class="hl opt">].</span>g_sum <span class="hl opt">*</span> gain<span class="hl opt">, ((</span><span class="hl num">1</span> <span class="hl opt">&lt;&lt;</span> PIPELINE_BITS<span class="hl opt">) -</span> <span class="hl num">1</span><span class="hl opt">) *</span> counted<span class="hl opt">);</span>
		<span class="hl kwb">double</span> b_sum <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>regions<span class="hl opt">[</span>i<span class="hl opt">].</span>b_sum <span class="hl opt">*</span> gain<span class="hl opt">, ((</span><span class="hl num">1</span> <span class="hl opt">&lt;&lt;</span> PIPELINE_BITS<span class="hl opt">) -</span> <span class="hl num">1</span><span class="hl opt">) *</span> counted<span class="hl opt">);</span>
		R_sum <span class="hl opt">+=</span> r_sum <span class="hl opt">*</span> weights<span class="hl opt">[</span>i<span class="hl opt">];</span>
		G_sum <span class="hl opt">+=</span> g_sum <span class="hl opt">*</span> weights<span class="hl opt">[</span>i<span class="hl opt">];</span>
		B_sum <span class="hl opt">+=</span> b_sum <span class="hl opt">*</span> weights<span class="hl opt">[</span>i<span class="hl opt">];</span>
		pixel_sum <span class="hl opt">+=</span> counted <span class="hl opt">*</span> weights<span class="hl opt">[</span>i<span class="hl opt">];</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>pixel_sum <span class="hl opt">==</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Warning<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;compute_initial_Y: pixel_sum is zero&quot;</span><span class="hl opt">;</span>
		<span class="hl kwa">return</span> <span class="hl num">0</span><span class="hl opt">;</span>
	<span class="hl opt">}</span>
	<span class="hl kwb">double</span> Y_sum <span class="hl opt">=</span> R_sum <span class="hl opt">*</span> awb<span class="hl opt">.</span>gain_r <span class="hl opt">*</span> <span class="hl num">.299</span> <span class="hl opt">+</span>
		       G_sum <span class="hl opt">*</span> awb<span class="hl opt">.</span>gain_g <span class="hl opt">*</span> <span class="hl num">.587</span> <span class="hl opt">+</span>
		       B_sum <span class="hl opt">*</span> awb<span class="hl opt">.</span>gain_b <span class="hl opt">*</span> <span class="hl num">.114</span><span class="hl opt">;</span>
	<span class="hl kwa">return</span> Y_sum <span class="hl opt">/</span> pixel_sum <span class="hl opt">/ (</span><span class="hl num">1</span> <span class="hl opt">&lt;&lt;</span> PIPELINE_BITS<span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl slc">// We handle extra gain through EV by adjusting our Y targets. However, you</span>
<span class="hl slc">// simply can&apos;t monitor histograms once they get very close to (or beyond!)</span>
<span class="hl slc">// saturation, so we clamp the Y targets to this value. It does mean that EV</span>
<span class="hl slc">// increases don&apos;t necessarily do quite what you might expect in certain</span>
<span class="hl slc">// (contrived) cases.</span>

<span class="hl ppc">#define EV_GAIN_Y_TARGET_LIMIT 0.9</span>

<span class="hl kwb">static double</span> <span class="hl kwd">constraint_compute_gain</span><span class="hl opt">(</span>AgcConstraint <span class="hl opt">&amp;</span>c<span class="hl opt">,</span> Histogram <span class="hl opt">&amp;</span>h<span class="hl opt">,</span>
				      <span class="hl kwb">double</span> lux<span class="hl opt">,</span> <span class="hl kwb">double</span> ev_gain<span class="hl opt">,</span>
				      <span class="hl kwb">double</span> <span class="hl opt">&amp;</span>target_Y<span class="hl opt">)</span>
<span class="hl opt">{</span>
	target_Y <span class="hl opt">=</span> c<span class="hl opt">.</span>Y_target<span class="hl opt">.</span><span class="hl kwd">Eval</span><span class="hl opt">(</span>c<span class="hl opt">.</span>Y_target<span class="hl opt">.</span><span class="hl kwd">Domain</span><span class="hl opt">().</span><span class="hl kwd">Clip</span><span class="hl opt">(</span>lux<span class="hl opt">));</span>
	target_Y <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>EV_GAIN_Y_TARGET_LIMIT<span class="hl opt">,</span> target_Y <span class="hl opt">*</span> ev_gain<span class="hl opt">);</span>
	<span class="hl kwb">double</span> iqm <span class="hl opt">=</span> h<span class="hl opt">.</span><span class="hl kwd">InterQuantileMean</span><span class="hl opt">(</span>c<span class="hl opt">.</span>q_lo<span class="hl opt">,</span> c<span class="hl opt">.</span>q_hi<span class="hl opt">);</span>
	<span class="hl kwa">return</span> <span class="hl opt">(</span>target_Y <span class="hl opt">*</span> NUM_HISTOGRAM_BINS<span class="hl opt">) /</span> iqm<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">computeGain</span><span class="hl opt">(</span>bcm2835_isp_stats <span class="hl opt">*</span>statistics<span class="hl opt">,</span> Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">,</span>
		      <span class="hl kwb">double</span> <span class="hl opt">&amp;</span>gain<span class="hl opt">,</span> <span class="hl kwb">double</span> <span class="hl opt">&amp;</span>target_Y<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">struct</span> LuxStatus lux <span class="hl opt">= {};</span>
	lux<span class="hl opt">.</span>lux <span class="hl opt">=</span> <span class="hl num">400</span><span class="hl opt">;</span> <span class="hl slc">// default lux level to 400 in case no metadata found</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>image_metadata<span class="hl opt">-&gt;</span><span class="hl kwd">Get</span><span class="hl opt">(</span><span class="hl str">&quot;lux.status&quot;</span><span class="hl opt">,</span> lux<span class="hl opt">) !=</span> <span class="hl num">0</span><span class="hl opt">)</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Warning<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Agc: no lux level found&quot;</span><span class="hl opt">;</span>
	Histogram <span class="hl kwd">h</span><span class="hl opt">(</span>statistics<span class="hl opt">-&gt;</span>hist<span class="hl opt">[</span><span class="hl num">0</span><span class="hl opt">].</span>g_hist<span class="hl opt">,</span> NUM_HISTOGRAM_BINS<span class="hl opt">);</span>
	<span class="hl kwb">double</span> ev_gain <span class="hl opt">=</span> status_<span class="hl opt">.</span>ev <span class="hl opt">*</span> config_<span class="hl opt">.</span>base_ev<span class="hl opt">;</span>
	<span class="hl slc">// The initial gain and target_Y come from some of the regions. After</span>
	<span class="hl slc">// that we consider the histogram constraints.</span>
	target_Y <span class="hl opt">=</span>
		config_<span class="hl opt">.</span>Y_target<span class="hl opt">.</span><span class="hl kwd">Eval</span><span class="hl opt">(</span>config_<span class="hl opt">.</span>Y_target<span class="hl opt">.</span><span class="hl kwd">Domain</span><span class="hl opt">().</span><span class="hl kwd">Clip</span><span class="hl opt">(</span>lux<span class="hl opt">.</span>lux<span class="hl opt">));</span>
	target_Y <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>EV_GAIN_Y_TARGET_LIMIT<span class="hl opt">,</span> target_Y <span class="hl opt">*</span> ev_gain<span class="hl opt">);</span>

	<span class="hl slc">// Do this calculation a few times as brightness increase can be</span>
	<span class="hl slc">// non-linear when there are saturated regions.</span>
	gain <span class="hl opt">=</span> <span class="hl num">1.0</span><span class="hl opt">;</span>
	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwb">int</span> i <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span> i <span class="hl opt">&lt;</span> <span class="hl num">8</span><span class="hl opt">;</span> i<span class="hl opt">++) {</span>
		<span class="hl kwb">double</span> initial_Y <span class="hl opt">=</span> <span class="hl kwd">compute_initial_Y</span><span class="hl opt">(</span>statistics<span class="hl opt">,</span> awb_<span class="hl opt">,</span>
						     metering_mode_<span class="hl opt">-&gt;</span>weights<span class="hl opt">,</span> gain<span class="hl opt">);</span>
		<span class="hl kwb">double</span> extra_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span><span class="hl num">10.0</span><span class="hl opt">,</span> target_Y <span class="hl opt">/ (</span>initial_Y <span class="hl opt">+</span> <span class="hl num">.001</span><span class="hl opt">));</span>
		gain <span class="hl opt">*=</span> extra_gain<span class="hl opt">;</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Initial Y &quot;</span> <span class="hl opt">&lt;&lt;</span> initial_Y <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; target &quot;</span> <span class="hl opt">&lt;&lt;</span> target_Y
				   <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; gives gain &quot;</span> <span class="hl opt">&lt;&lt;</span> gain<span class="hl opt">;</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>extra_gain <span class="hl opt">&lt;</span> <span class="hl num">1.01</span><span class="hl opt">)</span> <span class="hl slc">// close enough</span>
			<span class="hl kwa">break</span><span class="hl opt">;</span>
	<span class="hl opt">}</span>

	<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwc">auto</span> <span class="hl opt">&amp;</span>c <span class="hl opt">: *</span>constraint_mode_<span class="hl opt">) {</span>
		<span class="hl kwb">double</span> new_target_Y<span class="hl opt">;</span>
		<span class="hl kwb">double</span> new_gain <span class="hl opt">=</span>
			<span class="hl kwd">constraint_compute_gain</span><span class="hl opt">(</span>c<span class="hl opt">,</span> h<span class="hl opt">,</span> lux<span class="hl opt">.</span>lux<span class="hl opt">,</span> ev_gain<span class="hl opt">,</span>
						new_target_Y<span class="hl opt">);</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Constraint has target_Y &quot;</span>
				   <span class="hl opt">&lt;&lt;</span> new_target_Y <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; giving gain &quot;</span> <span class="hl opt">&lt;&lt;</span> new_gain<span class="hl opt">;</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>c<span class="hl opt">.</span>bound <span class="hl opt">==</span> <span class="hl kwc">AgcConstraint</span><span class="hl opt">::</span><span class="hl kwc">Bound</span><span class="hl opt">::</span>LOWER <span class="hl opt">&amp;&amp;</span>
		    new_gain <span class="hl opt">&gt;</span> gain<span class="hl opt">) {</span>
			<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Lower bound constraint adopted&quot;</span><span class="hl opt">;</span>
			gain <span class="hl opt">=</span> new_gain<span class="hl opt">,</span> target_Y <span class="hl opt">=</span> new_target_Y<span class="hl opt">;</span>
		<span class="hl opt">}</span> <span class="hl kwa">else if</span> <span class="hl opt">(</span>c<span class="hl opt">.</span>bound <span class="hl opt">==</span> <span class="hl kwc">AgcConstraint</span><span class="hl opt">::</span><span class="hl kwc">Bound</span><span class="hl opt">::</span>UPPER <span class="hl opt">&amp;&amp;</span>
			   new_gain <span class="hl opt">&lt;</span> gain<span class="hl opt">) {</span>
			<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Upper bound constraint adopted&quot;</span><span class="hl opt">;</span>
			gain <span class="hl opt">=</span> new_gain<span class="hl opt">,</span> target_Y <span class="hl opt">=</span> new_target_Y<span class="hl opt">;</span>
		<span class="hl opt">}</span>
	<span class="hl opt">}</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Final gain &quot;</span> <span class="hl opt">&lt;&lt;</span> gain <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; (target_Y &quot;</span> <span class="hl opt">&lt;&lt;</span> target_Y <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; ev &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> status_<span class="hl opt">.</span>ev <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; base_ev &quot;</span> <span class="hl opt">&lt;&lt;</span> config_<span class="hl opt">.</span>base_ev
			   <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;)&quot;</span><span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">computeTargetExposure</span><span class="hl opt">(</span><span class="hl kwb">double</span> gain<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">!=</span> <span class="hl num">0.0</span> <span class="hl opt">&amp;&amp;</span> status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
		<span class="hl slc">// When ag and shutter are both fixed, we need to drive the</span>
		<span class="hl slc">// total exposure so that we end up with a digital gain of at least</span>
		<span class="hl slc">// 1/min_colour_gain. Otherwise we&apos;d desaturate channels causing</span>
		<span class="hl slc">// white to go cyan or magenta.</span>
		<span class="hl kwb">double</span> min_colour_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">({</span> awb_<span class="hl opt">.</span>gain_r<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_g<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_b<span class="hl opt">,</span> <span class="hl num">1.0</span> <span class="hl opt">});</span>
		<span class="hl kwd">ASSERT</span><span class="hl opt">(</span>min_colour_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">);</span>
		target_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span>
			status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">*</span> status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">/</span> min_colour_gain<span class="hl opt">;</span>
	<span class="hl opt">}</span> <span class="hl kwa">else</span> <span class="hl opt">{</span>
		<span class="hl slc">// The statistics reflect the image without digital gain, so the final</span>
		<span class="hl slc">// total exposure we&apos;re aiming for is:</span>
		target_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> current_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">*</span> gain<span class="hl opt">;</span>
		<span class="hl slc">// The final target exposure is also limited to what the exposure</span>
		<span class="hl slc">// mode allows.</span>
		<span class="hl kwb">double</span> max_total_exposure <span class="hl opt">=</span>
			<span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">!=</span> <span class="hl num">0.0</span>
			 <span class="hl opt">?</span> status_<span class="hl opt">.</span>fixed_shutter
			 <span class="hl opt">:</span> exposure_mode_<span class="hl opt">-&gt;</span>shutter<span class="hl opt">.</span><span class="hl kwd">back</span><span class="hl opt">()) *</span>
			<span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span>
			 <span class="hl opt">?</span> status_<span class="hl opt">.</span>fixed_analogue_gain
			 <span class="hl opt">:</span> exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">.</span><span class="hl kwd">back</span><span class="hl opt">());</span>
		target_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>target_<span class="hl opt">.</span>total_exposure<span class="hl opt">,</span>
						  max_total_exposure<span class="hl opt">);</span>
	<span class="hl opt">}</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Target total_exposure &quot;</span> <span class="hl opt">&lt;&lt;</span> target_<span class="hl opt">.</span>total_exposure<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">bool</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">applyDigitalGain</span><span class="hl opt">(</span><span class="hl kwb">double</span> gain<span class="hl opt">,</span> <span class="hl kwb">double</span> target_Y<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">double</span> min_colour_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">({</span> awb_<span class="hl opt">.</span>gain_r<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_g<span class="hl opt">,</span> awb_<span class="hl opt">.</span>gain_b<span class="hl opt">,</span> <span class="hl num">1.0</span> <span class="hl opt">});</span>
	<span class="hl kwd">ASSERT</span><span class="hl opt">(</span>min_colour_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">);</span>
	<span class="hl kwb">double</span> dg <span class="hl opt">=</span> <span class="hl num">1.0</span> <span class="hl opt">/</span> min_colour_gain<span class="hl opt">;</span>
	<span class="hl slc">// I think this pipeline subtracts black level and rescales before we</span>
	<span class="hl slc">// get the stats, so no need to worry about it.</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;after AWB, target dg &quot;</span> <span class="hl opt">&lt;&lt;</span> dg <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; gain &quot;</span> <span class="hl opt">&lt;&lt;</span> gain
			   <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; target_Y &quot;</span> <span class="hl opt">&lt;&lt;</span> target_Y<span class="hl opt">;</span>
	<span class="hl slc">// Finally, if we&apos;re trying to reduce exposure but the target_Y is</span>
	<span class="hl slc">// &quot;close&quot; to 1.0, then the gain computed for that constraint will be</span>
	<span class="hl slc">// only slightly less than one, because the measured Y can never be</span>
	<span class="hl slc">// larger than 1.0. When this happens, demand a large digital gain so</span>
	<span class="hl slc">// that the exposure can be reduced, de-saturating the image much more</span>
	<span class="hl slc">// quickly (and we then approach the correct value more quickly from</span>
	<span class="hl slc">// below).</span>
	<span class="hl kwb">bool</span> desaturate <span class="hl opt">=</span> target_Y <span class="hl opt">&gt;</span> config_<span class="hl opt">.</span>fast_reduce_threshold <span class="hl opt">&amp;&amp;</span>
			  gain <span class="hl opt">&lt;</span> <span class="hl kwd">sqrt</span><span class="hl opt">(</span>target_Y<span class="hl opt">);</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>desaturate<span class="hl opt">)</span>
		dg <span class="hl opt">/=</span> config_<span class="hl opt">.</span>fast_reduce_threshold<span class="hl opt">;</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Digital gain &quot;</span> <span class="hl opt">&lt;&lt;</span> dg <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; desaturate? &quot;</span> <span class="hl opt">&lt;&lt;</span> desaturate<span class="hl opt">;</span>
	target_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span> target_<span class="hl opt">.</span>total_exposure <span class="hl opt">/</span> dg<span class="hl opt">;</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Target total_exposure_no_dg &quot;</span> <span class="hl opt">&lt;&lt;</span> target_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
	<span class="hl kwa">return</span> desaturate<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">filterExposure</span><span class="hl opt">(</span><span class="hl kwb">bool</span> desaturate<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">double</span> speed <span class="hl opt">=</span> frame_count_ <span class="hl opt">&lt;=</span> config_<span class="hl opt">.</span>startup_frames <span class="hl opt">?</span> <span class="hl num">1.0</span> <span class="hl opt">:</span> config_<span class="hl opt">.</span>speed<span class="hl opt">;</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">==</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
		filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> target_<span class="hl opt">.</span>total_exposure<span class="hl opt">;</span>
		filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span> target_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
	<span class="hl opt">}</span> <span class="hl kwa">else</span> <span class="hl opt">{</span>
		<span class="hl slc">// If close to the result go faster, to save making so many</span>
		<span class="hl slc">// micro-adjustments on the way. (Make this customisable?)</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">&lt;</span> <span class="hl num">1.2</span> <span class="hl opt">*</span> target_<span class="hl opt">.</span>total_exposure <span class="hl opt">&amp;&amp;</span>
		    filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">&gt;</span> <span class="hl num">0.8</span> <span class="hl opt">*</span> target_<span class="hl opt">.</span>total_exposure<span class="hl opt">)</span>
			speed <span class="hl opt">=</span> <span class="hl kwd">sqrt</span><span class="hl opt">(</span>speed<span class="hl opt">);</span>
		filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">=</span> speed <span class="hl opt">*</span> target_<span class="hl opt">.</span>total_exposure <span class="hl opt">+</span>
					   filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">* (</span><span class="hl num">1.0</span> <span class="hl opt">-</span> speed<span class="hl opt">);</span>
		<span class="hl slc">// When desaturing, take a big jump down in exposure_no_dg,</span>
		<span class="hl slc">// which we&apos;ll hide with digital gain.</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>desaturate<span class="hl opt">)</span>
			filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span>
				target_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
		<span class="hl kwa">else</span>
			filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span>
				speed <span class="hl opt">*</span> target_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">+</span>
				filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">* (</span><span class="hl num">1.0</span> <span class="hl opt">-</span> speed<span class="hl opt">);</span>
	<span class="hl opt">}</span>
	<span class="hl slc">// We can&apos;t let the no_dg exposure deviate too far below the</span>
	<span class="hl slc">// total exposure, as there might not be enough digital gain available</span>
	<span class="hl slc">// in the ISP to hide it (which will cause nasty oscillation).</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">&lt;</span>
	    filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">*</span> config_<span class="hl opt">.</span>fast_reduce_threshold<span class="hl opt">)</span>
		filtered_<span class="hl opt">.</span>total_exposure_no_dg <span class="hl opt">=</span> filtered_<span class="hl opt">.</span>total_exposure <span class="hl opt">*</span>
						 config_<span class="hl opt">.</span>fast_reduce_threshold<span class="hl opt">;</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;After filtering, total_exposure &quot;</span> <span class="hl opt">&lt;&lt;</span> filtered_<span class="hl opt">.</span>total_exposure
			   <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; no dg &quot;</span> <span class="hl opt">&lt;&lt;</span> filtered_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">divideUpExposure</span><span class="hl opt">()</span>
<span class="hl opt">{</span>
	<span class="hl slc">// Sending the fixed shutter/gain cases through the same code may seem</span>
	<span class="hl slc">// unnecessary, but it will make more sense when extend this to cover</span>
	<span class="hl slc">// variable aperture.</span>
	<span class="hl kwb">double</span> exposure_value <span class="hl opt">=</span> filtered_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
	<span class="hl kwb">double</span> shutter_time<span class="hl opt">,</span> analogue_gain<span class="hl opt">;</span>
	shutter_time <span class="hl opt">=</span> status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">!=</span> <span class="hl num">0.0</span>
			       <span class="hl opt">?</span> status_<span class="hl opt">.</span>fixed_shutter
			       <span class="hl opt">:</span> exposure_mode_<span class="hl opt">-&gt;</span>shutter<span class="hl opt">[</span><span class="hl num">0</span><span class="hl opt">];</span>
	analogue_gain <span class="hl opt">=</span> status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">!=</span> <span class="hl num">0.0</span>
				<span class="hl opt">?</span> status_<span class="hl opt">.</span>fixed_analogue_gain
				<span class="hl opt">:</span> exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">[</span><span class="hl num">0</span><span class="hl opt">];</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>shutter_time <span class="hl opt">*</span> analogue_gain <span class="hl opt">&lt;</span> exposure_value<span class="hl opt">) {</span>
		<span class="hl kwa">for</span> <span class="hl opt">(</span><span class="hl kwb">unsigned int</span> stage <span class="hl opt">=</span> <span class="hl num">1</span><span class="hl opt">;</span>
		     stage <span class="hl opt">&lt;</span> exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">.</span><span class="hl kwd">size</span><span class="hl opt">();</span> stage<span class="hl opt">++) {</span>
			<span class="hl kwa">if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">==</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
				<span class="hl kwa">if</span> <span class="hl opt">(</span>exposure_mode_<span class="hl opt">-&gt;</span>shutter<span class="hl opt">[</span>stage<span class="hl opt">] *</span>
					    analogue_gain <span class="hl opt">&gt;=</span>
				    exposure_value<span class="hl opt">) {</span>
					shutter_time <span class="hl opt">=</span>
						exposure_value <span class="hl opt">/</span> analogue_gain<span class="hl opt">;</span>
					<span class="hl kwa">break</span><span class="hl opt">;</span>
				<span class="hl opt">}</span>
				shutter_time <span class="hl opt">=</span> exposure_mode_<span class="hl opt">-&gt;</span>shutter<span class="hl opt">[</span>stage<span class="hl opt">];</span>
			<span class="hl opt">}</span>
			<span class="hl kwa">if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">==</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
				<span class="hl kwa">if</span> <span class="hl opt">(</span>exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">[</span>stage<span class="hl opt">] *</span>
					    shutter_time <span class="hl opt">&gt;=</span>
				    exposure_value<span class="hl opt">) {</span>
					analogue_gain <span class="hl opt">=</span>
						exposure_value <span class="hl opt">/</span> shutter_time<span class="hl opt">;</span>
					<span class="hl kwa">break</span><span class="hl opt">;</span>
				<span class="hl opt">}</span>
				analogue_gain <span class="hl opt">=</span> exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">[</span>stage<span class="hl opt">];</span>
			<span class="hl opt">}</span>
		<span class="hl opt">}</span>
	<span class="hl opt">}</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Divided up shutter and gain are &quot;</span> <span class="hl opt">&lt;&lt;</span> shutter_time <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; and &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> analogue_gain<span class="hl opt">;</span>
	<span class="hl slc">// Finally adjust shutter time for flicker avoidance (require both</span>
	<span class="hl slc">// shutter and gain not to be fixed).</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>status_<span class="hl opt">.</span>fixed_shutter <span class="hl opt">==</span> <span class="hl num">0.0</span> <span class="hl opt">&amp;&amp;</span>
	    status_<span class="hl opt">.</span>fixed_analogue_gain <span class="hl opt">==</span> <span class="hl num">0.0</span> <span class="hl opt">&amp;&amp;</span>
	    status_<span class="hl opt">.</span>flicker_period <span class="hl opt">!=</span> <span class="hl num">0.0</span><span class="hl opt">) {</span>
		<span class="hl kwb">int</span> flicker_periods <span class="hl opt">=</span> shutter_time <span class="hl opt">/</span> status_<span class="hl opt">.</span>flicker_period<span class="hl opt">;</span>
		<span class="hl kwa">if</span> <span class="hl opt">(</span>flicker_periods <span class="hl opt">&gt;</span> <span class="hl num">0</span><span class="hl opt">) {</span>
			<span class="hl kwb">double</span> new_shutter_time <span class="hl opt">=</span> flicker_periods <span class="hl opt">*</span> status_<span class="hl opt">.</span>flicker_period<span class="hl opt">;</span>
			analogue_gain <span class="hl opt">*=</span> shutter_time <span class="hl opt">/</span> new_shutter_time<span class="hl opt">;</span>
			<span class="hl slc">// We should still not allow the ag to go over the</span>
			<span class="hl slc">// largest value in the exposure mode. Note that this</span>
			<span class="hl slc">// may force more of the total exposure into the digital</span>
			<span class="hl slc">// gain as a side-effect.</span>
			analogue_gain <span class="hl opt">=</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">min</span><span class="hl opt">(</span>analogue_gain<span class="hl opt">,</span>
						 exposure_mode_<span class="hl opt">-&gt;</span>gain<span class="hl opt">.</span><span class="hl kwd">back</span><span class="hl opt">());</span>
			shutter_time <span class="hl opt">=</span> new_shutter_time<span class="hl opt">;</span>
		<span class="hl opt">}</span>
		<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;After flicker avoidance, shutter &quot;</span>
				   <span class="hl opt">&lt;&lt;</span> shutter_time <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; gain &quot;</span> <span class="hl opt">&lt;&lt;</span> analogue_gain<span class="hl opt">;</span>
	<span class="hl opt">}</span>
	filtered_<span class="hl opt">.</span>shutter <span class="hl opt">=</span> shutter_time<span class="hl opt">;</span>
	filtered_<span class="hl opt">.</span>analogue_gain <span class="hl opt">=</span> analogue_gain<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwc">Agc</span><span class="hl opt">::</span><span class="hl kwd">writeAndFinish</span><span class="hl opt">(</span>Metadata <span class="hl opt">*</span>image_metadata<span class="hl opt">,</span> <span class="hl kwb">bool</span> desaturate<span class="hl opt">)</span>
<span class="hl opt">{</span>
	status_<span class="hl opt">.</span>total_exposure_value <span class="hl opt">=</span> filtered_<span class="hl opt">.</span>total_exposure<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>target_exposure_value <span class="hl opt">=</span> desaturate <span class="hl opt">?</span> <span class="hl num">0</span> <span class="hl opt">:</span> target_<span class="hl opt">.</span>total_exposure_no_dg<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>shutter_time <span class="hl opt">=</span> filtered_<span class="hl opt">.</span>shutter<span class="hl opt">;</span>
	status_<span class="hl opt">.</span>analogue_gain <span class="hl opt">=</span> filtered_<span class="hl opt">.</span>analogue_gain<span class="hl opt">;</span>
	<span class="hl slc">// Write to metadata as well, in case anyone wants to update the camera</span>
	<span class="hl slc">// immediately.</span>
	image_metadata<span class="hl opt">-&gt;</span><span class="hl kwd">Set</span><span class="hl opt">(</span><span class="hl str">&quot;agc.status&quot;</span><span class="hl opt">,</span> status_<span class="hl opt">);</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Output written, total exposure requested is &quot;</span>
			   <span class="hl opt">&lt;&lt;</span> filtered_<span class="hl opt">.</span>total_exposure<span class="hl opt">;</span>
	<span class="hl kwd">LOG</span><span class="hl opt">(</span>RPiAgc<span class="hl opt">,</span> Debug<span class="hl opt">) &lt;&lt;</span> <span class="hl str">&quot;Camera exposure update: shutter time &quot;</span> <span class="hl opt">&lt;&lt;</span> filtered_<span class="hl opt">.</span>shutter
			   <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot; analogue gain &quot;</span> <span class="hl opt">&lt;&lt;</span> filtered_<span class="hl opt">.</span>analogue_gain<span class="hl opt">;</span>
<span class="hl opt">}</span>

<span class="hl slc">// Register algorithm with the system.</span>
<span class="hl kwb">static</span> Algorithm <span class="hl opt">*</span><span class="hl kwd">Create</span><span class="hl opt">(</span>Controller <span class="hl opt">*</span>controller<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwa">return</span> <span class="hl opt">(</span>Algorithm <span class="hl opt">*)</span><span class="hl kwa">new</span> <span class="hl kwd">Agc</span><span class="hl opt">(</span>controller<span class="hl opt">);</span>
<span class="hl opt">}</span>
<span class="hl kwb">static</span> RegisterAlgorithm <span class="hl kwd">reg</span><span class="hl opt">(</span>NAME<span class="hl opt">, &amp;</span>Create<span class="hl opt">);</span>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.1</a> (<a href='https://git-scm.com/'>git 2.18.0</a>) at 2025-03-08 10:11:30 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>