summaryrefslogtreecommitdiff
path: root/src/ipa/raspberrypi/data
AgeCommit message (Expand)Author
2020-07-03ipa: raspberrypi: Enable focus measure without recompileDavid Plowman
2020-06-25ipa: rpi: Add "focus" algorithmDavid Plowman
2020-06-09libcamera: Add missing SPDX headers to miscellaneous small filesLaurent Pinchart
2020-05-11libcamera: ipa: Raspberry Pi IPANaushir Patuck
#n5'>5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (C) 2019, Raspberry Pi Ltd
 *
 * agc.cpp - AGC/AEC control algorithm
 */

#include "agc.h"

#include <libcamera/base/log.h>

#include "../metadata.h"

using namespace RPiController;
using namespace libcamera;
using libcamera::utils::Duration;
using namespace std::literals::chrono_literals;

LOG_DEFINE_CATEGORY(RPiAgc)

#define NAME "rpi.agc"

Agc::Agc(Controller *controller)
	: AgcAlgorithm(controller),
	  activeChannels_({ 0 }), index_(0)
{
}

char const *Agc::name() const
{
	return NAME;
}

int Agc::read(const libcamera::YamlObject &params)
{
	/*
	 * When there is only a single channel we can read the old style syntax.
	 * Otherwise we expect a "channels" keyword followed by a list of configurations.
	 */
	if (!params.contains("channels")) {
		LOG(RPiAgc, Debug) << "Single channel only";
		channelTotalExposures_.resize(1, 0s);
		channelData_.emplace_back();
		return channelData_.back().channel.read(params, getHardwareConfig());
	}

	const auto &channels = params["channels"].asList();
	for (auto ch = channels.begin(); ch != channels.end(); ch++) {
		LOG(RPiAgc, Debug) << "Read AGC channel";
		channelData_.emplace_back();
		int ret = channelData_.back().channel.read(*ch, getHardwareConfig());
		if (ret)
			return ret;
	}

	LOG(RPiAgc, Debug) << "Read " << channelData_.size() << " channel(s)";
	if (channelData_.empty()) {
		LOG(RPiAgc, Error) << "No AGC channels provided";
		return -1;
	}

	channelTotalExposures_.resize(channelData_.size(), 0s);

	return 0;
}

int Agc::checkChannel(unsigned int channelIndex) const
{
	if (channelIndex >= channelData_.size()) {
		LOG(RPiAgc, Warning) << "AGC channel " << channelIndex << " not available";
		return -1;
	}

	return 0;
}

void Agc::disableAuto(unsigned int channelIndex)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "disableAuto for channel " << channelIndex;
	channelData_[channelIndex].channel.disableAuto();
}

void Agc::enableAuto(unsigned int channelIndex)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "enableAuto for channel " << channelIndex;
	channelData_[channelIndex].channel.enableAuto();
}

unsigned int Agc::getConvergenceFrames() const
{
	/* If there are n channels, it presumably takes n times as long to converge. */
	return channelData_[0].channel.getConvergenceFrames() * activeChannels_.size();
}

std::vector<double> const &Agc::getWeights() const
{
	/*
	 * In future the metering weights may be determined differently, making it
	 * difficult to associate different sets of weight with different channels.
	 * Therefore we shall impose a limitation, at least for now, that all
	 * channels will use the same weights.
	 */
	return channelData_[0].channel.getWeights();
}

void Agc::setEv(unsigned int channelIndex, double ev)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "setEv " << ev << " for channel " << channelIndex;
	channelData_[channelIndex].channel.setEv(ev);
}

void Agc::setFlickerPeriod(unsigned int channelIndex, Duration flickerPeriod)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "setFlickerPeriod " << flickerPeriod
			   << " for channel " << channelIndex;
	channelData_[channelIndex].channel.setFlickerPeriod(flickerPeriod);
}

void Agc::setMaxShutter(Duration maxShutter)
{
	/* Frame durations will be the same across all channels too. */
	for (auto &data : channelData_)
		data.channel.setMaxShutter(maxShutter);
}

void Agc::setFixedShutter(unsigned int channelIndex, Duration fixedShutter)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "setFixedShutter " << fixedShutter
			   << " for channel " << channelIndex;
	channelData_[channelIndex].channel.setFixedShutter(fixedShutter);
}

void Agc::setFixedAnalogueGain(unsigned int channelIndex, double fixedAnalogueGain)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "setFixedAnalogueGain " << fixedAnalogueGain
			   << " for channel " << channelIndex;
	channelData_[channelIndex].channel.setFixedAnalogueGain(fixedAnalogueGain);
}

void Agc::setMeteringMode(std::string const &meteringModeName)
{
	/* Metering modes will be the same across all channels too. */
	for (auto &data : channelData_)
		data.channel.setMeteringMode(meteringModeName);
}

void Agc::setExposureMode(unsigned int channelIndex, std::string const &exposureModeName)
{
	if (checkChannel(channelIndex))
		return;

	LOG(RPiAgc, Debug) << "setExposureMode " << exposureModeName
			   << " for channel " << channelIndex;
	channelData_[channelIndex].channel.setExposureMode(exposureModeName);
}

void Agc::setConstraintMode(unsigned int channelIndex, std::string const &constraintModeName)
{
	if (checkChannel(channelIndex))
		return;

	channelData_[channelIndex].channel.setConstraintMode(constraintModeName);
}

template<typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v)
{
	os << "{";
	for (const auto &e : v)
		os << " " << e;
	os << " }";
	return os;
}

void Agc::setActiveChannels(const std::vector<unsigned int> &activeChannels)
{
	if (activeChannels.empty()) {
		LOG(RPiAgc, Warning) << "No active AGC channels supplied";
		return;
	}

	for (auto index : activeChannels)
		if (checkChannel(index))
			return;

	LOG(RPiAgc, Debug) << "setActiveChannels " << activeChannels;
	activeChannels_ = activeChannels;
}

void Agc::switchMode(CameraMode const &cameraMode,
		     Metadata *metadata)
{
	/*
	 * We run switchMode on every channel, and then we're going to start over
	 * with the first active channel again which means that this channel's
	 * status needs to be the one we leave in the metadata.
	 */
	AgcStatus status;

	for (unsigned int channelIndex = 0; channelIndex < channelData_.size(); channelIndex++) {
		LOG(RPiAgc, Debug) << "switchMode for channel " << channelIndex;
		channelData_[channelIndex].channel.switchMode(cameraMode, metadata);
		if (channelIndex == activeChannels_[0])
			metadata->get("agc.status", status);
	}

	status.channel = activeChannels_[0];
	metadata->set("agc.status", status);
	index_ = 0;
}

static void getDelayedChannelIndex(Metadata *metadata, const char *message, unsigned int &channelIndex)
{
	std::unique_lock<RPiController::Metadata> lock(*metadata);
	AgcStatus *status = metadata->getLocked<AgcStatus>("agc.delayed_status");
	if (status)
		channelIndex = status->channel;
	else {
		/* This does happen at startup, otherwise it would be a Warning or Error. */
		LOG(RPiAgc, Debug) << message;
	}
}

static libcamera::utils::Duration
setCurrentChannelIndexGetExposure(Metadata *metadata, const char *message, unsigned int channelIndex)
{
	std::unique_lock<RPiController::Metadata> lock(*metadata);
	AgcStatus *status = metadata->getLocked<AgcStatus>("agc.status");
	libcamera::utils::Duration dur = 0s;

	if (status) {
		status->channel = channelIndex;
		dur = status->totalExposureValue;
	} else {
		/* This does happen at startup, otherwise it would be a Warning or Error. */
		LOG(RPiAgc, Debug) << message;
	}

	return dur;
}

void Agc::prepare(Metadata *imageMetadata)
{
	/*
	 * The DeviceStatus in the metadata should be correct for the image we
	 * are processing. The delayed status should tell us what channel this frame
	 * was from, so we will use that channel's prepare method.
	 *
	 * \todo To be honest, there's not much that's stateful in the prepare methods
	 * so we should perhaps re-evaluate whether prepare even needs to be done
	 * "per channel".
	 */
	unsigned int channelIndex = activeChannels_[0];
	getDelayedChannelIndex(imageMetadata, "prepare: no delayed status", channelIndex);

	LOG(RPiAgc, Debug) << "prepare for channel " << channelIndex;
	channelData_[channelIndex].channel.prepare(imageMetadata);
}

void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
{
	/*
	 * We want to generate values for the next channel in round robin fashion
	 * (i.e. the channel at location index_ in the activeChannel list), even though
	 * the statistics we have will be for a different channel (which we find
	 * again from the delayed status).
	 */

	/* Generate updated AGC values for channel for new channel that we are requesting. */
	unsigned int channelIndex = activeChannels_[index_];
	AgcChannelData &channelData = channelData_[channelIndex];
	/* The stats that arrived with this image correspond to the following channel. */
	unsigned int statsIndex = 0;
	getDelayedChannelIndex(imageMetadata, "process: no delayed status for stats", statsIndex);
	LOG(RPiAgc, Debug) << "process for channel " << channelIndex;

	/*
	 * We keep a cache of the most recent DeviceStatus and stats for each channel,
	 * so that we can invoke the next channel's process method with the most up to date
	 * values.
	 */
	LOG(RPiAgc, Debug) << "Save DeviceStatus and stats for channel " << statsIndex;
	DeviceStatus deviceStatus;
	if (imageMetadata->get<DeviceStatus>("device.status", deviceStatus) == 0)
		channelData_[statsIndex].deviceStatus = deviceStatus;
	else
		/* Every frame should have a DeviceStatus. */
		LOG(RPiAgc, Error) << "process: no device status found";
	channelData_[statsIndex].statistics = stats;

	/*
	 * Finally fetch the most recent DeviceStatus and stats for the new channel, if both
	 * exist, and call process(). We must make the agc.status metadata record correctly
	 * which channel this is.
	 */
	StatisticsPtr *statsPtr = &stats;
	if (channelData.statistics && channelData.deviceStatus) {
		deviceStatus = *channelData.deviceStatus;
		statsPtr = &channelData.statistics;
	} else {
		/* Can also happen when new channels start. */
		LOG(RPiAgc, Debug) << "process: channel " << channelIndex << " not seen yet";
	}

	channelData.channel.process(*statsPtr, deviceStatus, imageMetadata, channelTotalExposures_);
	auto dur = setCurrentChannelIndexGetExposure(imageMetadata, "process: no AGC status found",
						     channelIndex);
	if (dur)
		channelTotalExposures_[channelIndex] = dur;

	/* And onto the next channel for the next call. */
	index_ = (index_ + 1) % activeChannels_.size();
}

/* Register algorithm with the system. */
static Algorithm *create(Controller *controller)
{
	return (Algorithm *)new Agc(controller);
}
static RegisterAlgorithm reg(NAME, &create);