summaryrefslogtreecommitdiff
path: root/test/ipa
diff options
context:
space:
mode:
Diffstat (limited to 'test/ipa')
-rw-r--r--test/ipa/ipa_interface_test.cpp109
-rw-r--r--test/ipa/ipa_module_test.cpp9
-rw-r--r--test/ipa/ipa_wrappers_test.cpp394
-rw-r--r--test/ipa/libipa/fixedpoint.cpp108
-rw-r--r--test/ipa/libipa/interpolator.cpp54
-rw-r--r--test/ipa/libipa/meson.build18
-rw-r--r--test/ipa/libipa/vector.cpp100
-rw-r--r--test/ipa/meson.build21
8 files changed, 371 insertions, 442 deletions
diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp
index cafc249b..b8178366 100644
--- a/test/ipa/ipa_interface_test.cpp
+++ b/test/ipa/ipa_interface_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_interface_test.cpp - Test the IPA interface
+ * Test the IPA interface
*/
#include <fcntl.h>
@@ -12,44 +12,52 @@
#include <sys/types.h>
#include <unistd.h>
-#include <libcamera/event_dispatcher.h>
-#include <libcamera/event_notifier.h>
-#include <libcamera/timer.h>
+#include <libcamera/ipa/vimc_ipa_proxy.h>
-#include <ipa/ipa_vimc.h>
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/event_notifier.h>
+#include <libcamera/base/object.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/timer.h>
+
+#include "libcamera/internal/camera_manager.h"
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/pipeline_handler.h"
-#include "device_enumerator.h"
-#include "ipa_manager.h"
-#include "ipa_module.h"
-#include "pipeline_handler.h"
#include "test.h"
-#include "thread.h"
-using namespace std;
using namespace libcamera;
+using namespace std;
+using namespace std::chrono_literals;
class IPAInterfaceTest : public Test, public Object
{
public:
IPAInterfaceTest()
- : trace_(IPAOperationNone), notifier_(nullptr), fd_(-1)
+ : trace_(ipa::vimc::IPAOperationNone), notifier_(nullptr), fd_(-1)
{
}
~IPAInterfaceTest()
{
delete notifier_;
+ ipa_.reset();
+ cameraManager_.reset();
}
protected:
int init() override
{
+ cameraManager_ = make_unique<CameraManager>();
+
/* Create a pipeline handler for vimc. */
- std::vector<PipelineHandlerFactory *> &factories =
- PipelineHandlerFactory::factories();
- for (PipelineHandlerFactory *factory : factories) {
- if (factory->name() == "PipelineHandlerVimc") {
- pipe_ = factory->create(nullptr);
+ const std::vector<PipelineHandlerFactoryBase *> &factories =
+ PipelineHandlerFactoryBase::factories();
+ for (const PipelineHandlerFactoryBase *factory : factories) {
+ if (factory->name() == "vimc") {
+ pipe_ = factory->create(cameraManager_.get());
break;
}
}
@@ -60,22 +68,22 @@ protected:
}
/* Create and open the communication FIFO. */
- int ret = mkfifo(VIMC_IPA_FIFO_PATH, S_IRUSR | S_IWUSR);
+ int ret = mkfifo(ipa::vimc::VimcIPAFIFOPath.c_str(), S_IRUSR | S_IWUSR);
if (ret) {
ret = errno;
cerr << "Failed to create IPA test FIFO at '"
- << VIMC_IPA_FIFO_PATH << "': " << strerror(ret)
+ << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret)
<< endl;
return TestFail;
}
- ret = open(VIMC_IPA_FIFO_PATH, O_RDONLY | O_NONBLOCK);
+ ret = open(ipa::vimc::VimcIPAFIFOPath.c_str(), O_RDONLY | O_NONBLOCK);
if (ret < 0) {
ret = errno;
cerr << "Failed to open IPA test FIFO at '"
- << VIMC_IPA_FIFO_PATH << "': " << strerror(ret)
+ << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret)
<< endl;
- unlink(VIMC_IPA_FIFO_PATH);
+ unlink(ipa::vimc::VimcIPAFIFOPath.c_str());
return TestFail;
}
fd_ = ret;
@@ -91,49 +99,82 @@ protected:
EventDispatcher *dispatcher = thread()->eventDispatcher();
Timer timer;
- ipa_ = IPAManager::instance()->createIPA(pipe_.get(), 0, 0);
+ ipa_ = IPAManager::createIPA<ipa::vimc::IPAProxyVimc>(pipe_.get(), 0, 0);
if (!ipa_) {
cerr << "Failed to create VIMC IPA interface" << endl;
return TestFail;
}
/* Test initialization of IPA module. */
- ipa_->init();
- timer.start(1000);
- while (timer.isRunning() && trace_ != IPAOperationInit)
+ std::string conf = ipa_->configurationFile("vimc.conf");
+ Flags<ipa::vimc::TestFlag> inFlags;
+ Flags<ipa::vimc::TestFlag> outFlags;
+ int ret = ipa_->init(IPASettings{ conf, "vimc" },
+ ipa::vimc::IPAOperationInit,
+ inFlags, &outFlags);
+ if (ret < 0) {
+ cerr << "IPA interface init() failed" << endl;
+ return TestFail;
+ }
+
+ timer.start(1000ms);
+ while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationInit)
dispatcher->processEvents();
- if (trace_ != IPAOperationInit) {
+ if (trace_ != ipa::vimc::IPAOperationInit) {
cerr << "Failed to test IPA initialization sequence"
<< endl;
return TestFail;
}
+ /* Test start of IPA module. */
+ ipa_->start();
+ timer.start(1000ms);
+ while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationStart)
+ dispatcher->processEvents();
+
+ if (trace_ != ipa::vimc::IPAOperationStart) {
+ cerr << "Failed to test IPA start sequence" << endl;
+ return TestFail;
+ }
+
+ /* Test stop of IPA module. */
+ ipa_->stop();
+ timer.start(1000ms);
+ while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationStop)
+ dispatcher->processEvents();
+
+ if (trace_ != ipa::vimc::IPAOperationStop) {
+ cerr << "Failed to test IPA stop sequence" << endl;
+ return TestFail;
+ }
+
return TestPass;
}
void cleanup() override
{
close(fd_);
- unlink(VIMC_IPA_FIFO_PATH);
+ unlink(ipa::vimc::VimcIPAFIFOPath.c_str());
}
private:
- void readTrace(EventNotifier *notifier)
+ void readTrace()
{
- ssize_t s = read(notifier->fd(), &trace_, sizeof(trace_));
+ ssize_t s = read(notifier_->fd(), &trace_, sizeof(trace_));
if (s < 0) {
int ret = errno;
cerr << "Failed to read from IPA test FIFO at '"
- << VIMC_IPA_FIFO_PATH << "': " << strerror(ret)
+ << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret)
<< endl;
- trace_ = IPAOperationNone;
+ trace_ = ipa::vimc::IPAOperationNone;
}
}
std::shared_ptr<PipelineHandler> pipe_;
- std::unique_ptr<IPAInterface> ipa_;
- enum IPAOperationCode trace_;
+ std::unique_ptr<ipa::vimc::IPAProxyVimc> ipa_;
+ std::unique_ptr<CameraManager> cameraManager_;
+ enum ipa::vimc::IPAOperationCode trace_;
EventNotifier *notifier_;
int fd_;
};
diff --git a/test/ipa/ipa_module_test.cpp b/test/ipa/ipa_module_test.cpp
index 287e53fd..1c97da32 100644
--- a/test/ipa/ipa_module_test.cpp
+++ b/test/ipa/ipa_module_test.cpp
@@ -2,13 +2,13 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_module_test.cpp - Test loading of the VIMC IPA module and verify its info
+ * Test loading of the VIMC IPA module and verify its info
*/
#include <iostream>
#include <string.h>
-#include "ipa_module.h"
+#include "libcamera/internal/ipa_module.h"
#include "test.h"
@@ -57,9 +57,8 @@ protected:
const struct IPAModuleInfo testInfo = {
IPA_MODULE_API_VERSION,
0,
- "PipelineHandlerVimc",
- "Dummy IPA for Vimc",
- "GPL-2.0-or-later",
+ "vimc",
+ "vimc",
};
count += runTest("src/ipa/vimc/ipa_vimc.so", testInfo);
diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp
deleted file mode 100644
index 1ae17811..00000000
--- a/test/ipa/ipa_wrappers_test.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers
- */
-
-#include <fcntl.h>
-#include <iostream>
-#include <memory>
-#include <linux/videodev2.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <libcamera/controls.h>
-#include <libipa/ipa_interface_wrapper.h>
-
-#include "device_enumerator.h"
-#include "ipa_context_wrapper.h"
-#include "media_device.h"
-#include "v4l2_subdevice.h"
-
-#include "test.h"
-
-using namespace libcamera;
-using namespace std;
-
-enum Operation {
- Op_init,
- Op_configure,
- Op_mapBuffers,
- Op_unmapBuffers,
- Op_processEvent,
-};
-
-class TestIPAInterface : public IPAInterface
-{
-public:
- TestIPAInterface()
- : sequence_(0)
- {
- }
-
- int init() override
- {
- report(Op_init, TestPass);
- return 0;
- }
-
- void configure(const std::map<unsigned int, IPAStream> &streamConfig,
- const std::map<unsigned int, const ControlInfoMap &> &entityControls) override
- {
- /* Verify streamConfig. */
- if (streamConfig.size() != 2) {
- cerr << "configure(): Invalid number of streams "
- << streamConfig.size() << endl;
- return report(Op_configure, TestFail);
- }
-
- auto iter = streamConfig.find(1);
- if (iter == streamConfig.end()) {
- cerr << "configure(): No configuration for stream 1" << endl;
- return report(Op_configure, TestFail);
- }
- const IPAStream *stream = &iter->second;
- if (stream->pixelFormat != V4L2_PIX_FMT_YUYV ||
- stream->size != Size{ 1024, 768 }) {
- cerr << "configure(): Invalid configuration for stream 1" << endl;
- return report(Op_configure, TestFail);
- }
-
- iter = streamConfig.find(2);
- if (iter == streamConfig.end()) {
- cerr << "configure(): No configuration for stream 2" << endl;
- return report(Op_configure, TestFail);
- }
- stream = &iter->second;
- if (stream->pixelFormat != V4L2_PIX_FMT_NV12 ||
- stream->size != Size{ 800, 600 }) {
- cerr << "configure(): Invalid configuration for stream 2" << endl;
- return report(Op_configure, TestFail);
- }
-
- /* Verify entityControls. */
- auto ctrlIter = entityControls.find(42);
- if (ctrlIter == entityControls.end()) {
- cerr << "configure(): Controls not found" << endl;
- return report(Op_configure, TestFail);
- }
-
- const ControlInfoMap &infoMap = ctrlIter->second;
-
- if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 ||
- infoMap.count(V4L2_CID_CONTRAST) != 1 ||
- infoMap.count(V4L2_CID_SATURATION) != 1) {
- cerr << "configure(): Invalid control IDs" << endl;
- return report(Op_configure, TestFail);
- }
-
- report(Op_configure, TestPass);
- }
-
- void mapBuffers(const std::vector<IPABuffer> &buffers) override
- {
- if (buffers.size() != 2) {
- cerr << "mapBuffers(): Invalid number of buffers "
- << buffers.size() << endl;
- return report(Op_mapBuffers, TestFail);
- }
-
- if (buffers[0].id != 10 ||
- buffers[1].id != 11) {
- cerr << "mapBuffers(): Invalid buffer IDs" << endl;
- return report(Op_mapBuffers, TestFail);
- }
-
- if (buffers[0].planes.size() != 3 ||
- buffers[1].planes.size() != 3) {
- cerr << "mapBuffers(): Invalid number of planes" << endl;
- return report(Op_mapBuffers, TestFail);
- }
-
- if (buffers[0].planes[0].length != 4096 ||
- buffers[0].planes[1].length != 0 ||
- buffers[0].planes[2].length != 0 ||
- buffers[0].planes[0].length != 4096 ||
- buffers[1].planes[1].length != 4096 ||
- buffers[1].planes[2].length != 0) {
- cerr << "mapBuffers(): Invalid length" << endl;
- return report(Op_mapBuffers, TestFail);
- }
-
- if (buffers[0].planes[0].fd.fd() == -1 ||
- buffers[0].planes[1].fd.fd() != -1 ||
- buffers[0].planes[2].fd.fd() != -1 ||
- buffers[0].planes[0].fd.fd() == -1 ||
- buffers[1].planes[1].fd.fd() == -1 ||
- buffers[1].planes[2].fd.fd() != -1) {
- cerr << "mapBuffers(): Invalid dmabuf" << endl;
- return report(Op_mapBuffers, TestFail);
- }
-
- report(Op_mapBuffers, TestPass);
- }
-
- void unmapBuffers(const std::vector<unsigned int> &ids) override
- {
- if (ids.size() != 2) {
- cerr << "unmapBuffers(): Invalid number of ids "
- << ids.size() << endl;
- return report(Op_unmapBuffers, TestFail);
- }
-
- if (ids[0] != 10 || ids[1] != 11) {
- cerr << "unmapBuffers(): Invalid buffer IDs" << endl;
- return report(Op_unmapBuffers, TestFail);
- }
-
- report(Op_unmapBuffers, TestPass);
- }
-
- void processEvent(const IPAOperationData &data) override
- {
- /* Verify operation and data. */
- if (data.operation != Op_processEvent) {
- cerr << "processEvent(): Invalid operation "
- << data.operation << endl;
- return report(Op_processEvent, TestFail);
- }
-
- if (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) {
- cerr << "processEvent(): Invalid data" << endl;
- return report(Op_processEvent, TestFail);
- }
-
- /* Verify controls. */
- if (data.controls.size() != 1) {
- cerr << "processEvent(): Controls not found" << endl;
- return report(Op_processEvent, TestFail);
- }
-
- const ControlList &controls = data.controls[0];
- if (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 ||
- controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 ||
- controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) {
- cerr << "processEvent(): Invalid controls" << endl;
- return report(Op_processEvent, TestFail);
- }
-
- report(Op_processEvent, TestPass);
- }
-
-private:
- void report(Operation op, int status)
- {
- IPAOperationData data;
- data.operation = op;
- data.data.resize(1);
- data.data[0] = status;
- queueFrameAction.emit(sequence_++, data);
- }
-
- unsigned int sequence_;
-};
-
-#define INVOKE(method, ...) \
- invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__)
-
-class IPAWrappersTest : public Test
-{
-public:
- IPAWrappersTest()
- : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1)
- {
- }
-
-protected:
- int init() override
- {
- /* Locate the VIMC Sensor B subdevice. */
- enumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create());
- if (!enumerator_) {
- cerr << "Failed to create device enumerator" << endl;
- return TestFail;
- }
-
- if (enumerator_->enumerate()) {
- cerr << "Failed to enumerate media devices" << endl;
- return TestFail;
- }
-
- DeviceMatch dm("vimc");
- media_ = enumerator_->search(dm);
- if (!media_) {
- cerr << "No VIMC media device found: skip test" << endl;
- return TestSkip;
- }
-
- MediaEntity *entity = media_->getEntityByName("Sensor A");
- if (!entity) {
- cerr << "Unable to find media entity 'Sensor A'" << endl;
- return TestFail;
- }
-
- subdev_ = new V4L2Subdevice(entity);
- if (subdev_->open() < 0) {
- cerr << "Unable to open 'Sensor A' subdevice" << endl;
- return TestFail;
- }
-
- /* Force usage of the C API as that's what we want to test. */
- int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1);
- if (ret)
- return TestFail;
-
- std::unique_ptr<IPAInterface> intf = std::make_unique<TestIPAInterface>();
- wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf)));
- wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction);
-
- /* Create a file descriptor for the buffer-related operations. */
- fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600);
- if (fd_ == -1)
- return TestFail;
-
- ret = ftruncate(fd_, 4096);
- if (ret < 0)
- return TestFail;
-
- return TestPass;
- }
-
- int run() override
- {
- int ret;
-
- /* Test configure(). */
- std::map<unsigned int, IPAStream> config{
- { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } },
- { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } },
- };
- std::map<unsigned int, const ControlInfoMap &> controlInfo;
- controlInfo.emplace(42, subdev_->controls());
- ret = INVOKE(configure, config, controlInfo);
- if (ret == TestFail)
- return TestFail;
-
- /* Test mapBuffers(). */
- std::vector<IPABuffer> buffers(2);
- buffers[0].planes.resize(3);
- buffers[0].id = 10;
- buffers[0].planes[0].fd = FileDescriptor(fd_);
- buffers[0].planes[0].length = 4096;
- buffers[1].id = 11;
- buffers[1].planes.resize(3);
- buffers[1].planes[0].fd = FileDescriptor(fd_);
- buffers[1].planes[0].length = 4096;
- buffers[1].planes[1].fd = FileDescriptor(fd_);
- buffers[1].planes[1].length = 4096;
-
- ret = INVOKE(mapBuffers, buffers);
- if (ret == TestFail)
- return TestFail;
-
- /* Test unmapBuffers(). */
- std::vector<unsigned int> bufferIds = { 10, 11 };
- ret = INVOKE(unmapBuffers, bufferIds);
- if (ret == TestFail)
- return TestFail;
-
- /* Test processEvent(). */
- IPAOperationData data;
- data.operation = Op_processEvent;
- data.data = { 1, 2, 3, 4 };
- data.controls.emplace_back(subdev_->controls());
-
- ControlList &controls = data.controls.back();
- controls.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10));
- controls.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20));
- controls.set(V4L2_CID_SATURATION, static_cast<int32_t>(30));
-
- ret = INVOKE(processEvent, data);
- if (ret == TestFail)
- return TestFail;
-
- /*
- * Test init() last to ensure nothing in the wrappers or
- * serializer depends on init() being called first.
- */
- ret = INVOKE(init);
- if (ret == TestFail)
- return TestFail;
-
- return TestPass;
- }
-
- void cleanup() override
- {
- delete wrapper_;
- delete subdev_;
-
- if (fd_ != -1)
- close(fd_);
- }
-
-private:
- template<typename T, typename... Args1, typename... Args2>
- int invoke(T (IPAInterface::*func)(Args1...), Operation op,
- const char *name, Args2... args)
- {
- data_ = IPAOperationData();
- (wrapper_->*func)(args...);
-
- if (frame_ != sequence_) {
- cerr << "IPAInterface::" << name
- << "(): invalid frame number " << frame_
- << ", expected " << sequence_;
- return TestFail;
- }
-
- sequence_++;
-
- if (data_.operation != op) {
- cerr << "IPAInterface::" << name
- << "(): failed to propagate" << endl;
- return TestFail;
- }
-
- if (data_.data[0] != TestPass) {
- cerr << "IPAInterface::" << name
- << "(): reported an error" << endl;
- return TestFail;
- }
-
- return TestPass;
- }
-
- void queueFrameAction(unsigned int frame, const IPAOperationData &data)
- {
- frame_ = frame;
- data_ = data;
- }
-
- std::shared_ptr<MediaDevice> media_;
- std::unique_ptr<DeviceEnumerator> enumerator_;
- V4L2Subdevice *subdev_;
-
- IPAContextWrapper *wrapper_;
- IPAOperationData data_;
- unsigned int sequence_;
- unsigned int frame_;
- int fd_;
-};
-
-TEST_REGISTER(IPAWrappersTest)
diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp
new file mode 100644
index 00000000..99eb662d
--- /dev/null
+++ b/test/ipa/libipa/fixedpoint.cpp
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Fixed / Floating point utility tests
+ */
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+
+#include "../src/ipa/libipa/fixedpoint.h"
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+class FixedPointUtilsTest : public Test
+{
+protected:
+ /* R for real, I for integer */
+ template<unsigned int IntPrec, unsigned int FracPrec, typename I, typename R>
+ int testFixedToFloat(I input, R expected)
+ {
+ R out = fixedToFloatingPoint<IntPrec, FracPrec, R>(input);
+ R prec = 1.0 / (1 << FracPrec);
+ if (std::abs(out - expected) > prec) {
+ cerr << "Reverse conversion expected " << input
+ << " to convert to " << expected
+ << ", got " << out << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+ template<unsigned int IntPrec, unsigned int FracPrec, typename T>
+ int testSingleFixedPoint(double input, T expected)
+ {
+ T ret = floatingToFixedPoint<IntPrec, FracPrec, T>(input);
+ if (ret != expected) {
+ cerr << "Expected " << input << " to convert to "
+ << expected << ", got " << ret << std::endl;
+ return TestFail;
+ }
+
+ /*
+ * The precision check is fairly arbitrary but is based on what
+ * the rkisp1 is capable of in the crosstalk module.
+ */
+ double f = fixedToFloatingPoint<IntPrec, FracPrec, double>(ret);
+ if (std::abs(f - input) > 0.005) {
+ cerr << "Reverse conversion expected " << ret
+ << " to convert to " << input
+ << ", got " << f << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+ int testFixedPoint()
+ {
+ /*
+ * The second 7.992 test is to test that unused bits don't
+ * affect the result.
+ */
+ std::map<double, uint16_t> testCases = {
+ { 7.992, 0x3ff },
+ { 0.2, 0x01a },
+ { -0.2, 0x7e6 },
+ { -0.8, 0x79a },
+ { -0.4, 0x7cd },
+ { -1.4, 0x74d },
+ { -8, 0x400 },
+ { 0, 0 },
+ };
+
+ int ret;
+ for (const auto &testCase : testCases) {
+ ret = testSingleFixedPoint<4, 7, uint16_t>(testCase.first,
+ testCase.second);
+ if (ret != TestPass)
+ return ret;
+ }
+
+ /* Special case with a superfluous one in the unused bits */
+ ret = testFixedToFloat<4, 7, uint16_t, double>(0xbff, 7.992);
+ if (ret != TestPass)
+ return ret;
+
+ return TestPass;
+ }
+
+ int run()
+ {
+ /* fixed point conversion test */
+ if (testFixedPoint() != TestPass)
+ return TestFail;
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(FixedPointUtilsTest)
diff --git a/test/ipa/libipa/interpolator.cpp b/test/ipa/libipa/interpolator.cpp
new file mode 100644
index 00000000..6abb7760
--- /dev/null
+++ b/test/ipa/libipa/interpolator.cpp
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Interpolator tests
+ */
+
+#include "../src/ipa/libipa/interpolator.h"
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+#define ASSERT_EQ(a, b) \
+ if ((a) != (b)) { \
+ printf(#a " != " #b "\n"); \
+ return TestFail; \
+ }
+
+class InterpolatorTest : public Test
+{
+protected:
+ int run()
+ {
+ Interpolator<int> interpolator;
+ interpolator.setData({ { 10, 100 }, { 20, 200 }, { 30, 300 } });
+
+ ASSERT_EQ(interpolator.getInterpolated(0), 100);
+ ASSERT_EQ(interpolator.getInterpolated(10), 100);
+ ASSERT_EQ(interpolator.getInterpolated(20), 200);
+ ASSERT_EQ(interpolator.getInterpolated(25), 250);
+ ASSERT_EQ(interpolator.getInterpolated(30), 300);
+ ASSERT_EQ(interpolator.getInterpolated(40), 300);
+
+ interpolator.setQuantization(10);
+ unsigned int q = 0;
+ ASSERT_EQ(interpolator.getInterpolated(25, &q), 300);
+ ASSERT_EQ(q, 30);
+ ASSERT_EQ(interpolator.getInterpolated(24, &q), 200);
+ ASSERT_EQ(q, 20);
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(InterpolatorTest)
diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build
new file mode 100644
index 00000000..0f4155d2
--- /dev/null
+++ b/test/ipa/libipa/meson.build
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libipa_test = [
+ {'name': 'fixedpoint', 'sources': ['fixedpoint.cpp']},
+ {'name': 'interpolator', 'sources': ['interpolator.cpp']},
+ {'name': 'vector', 'sources': ['vector.cpp']},
+]
+
+foreach test : libipa_test
+ exe = executable(test['name'], test['sources'],
+ dependencies : [libcamera_private, libipa_dep],
+ implicit_include_directories : false,
+ link_with : [test_libraries],
+ include_directories : [test_includes_internal,
+ '../../../src/ipa/libipa/'])
+
+ test(test['name'], exe, suite : 'ipa')
+endforeach
diff --git a/test/ipa/libipa/vector.cpp b/test/ipa/libipa/vector.cpp
new file mode 100644
index 00000000..8e4ec77d
--- /dev/null
+++ b/test/ipa/libipa/vector.cpp
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Vector tests
+ */
+
+#include "../src/ipa/libipa/vector.h"
+
+#include <cmath>
+#include <iostream>
+
+#include "test.h"
+
+using namespace libcamera::ipa;
+
+#define ASSERT_EQ(a, b) \
+if ((a) != (b)) { \
+ std::cout << #a " != " #b << " (line " << __LINE__ << ")" \
+ << std::endl; \
+ return TestFail; \
+}
+
+class VectorTest : public Test
+{
+protected:
+ int run()
+ {
+ Vector<double, 3> v1{ 0.0 };
+
+ ASSERT_EQ(v1[0], 0.0);
+ ASSERT_EQ(v1[1], 0.0);
+ ASSERT_EQ(v1[2], 0.0);
+
+ ASSERT_EQ(v1.length(), 0.0);
+ ASSERT_EQ(v1.length2(), 0.0);
+
+ Vector<double, 3> v2{{ 1.0, 4.0, 8.0 }};
+
+ ASSERT_EQ(v2[0], 1.0);
+ ASSERT_EQ(v2[1], 4.0);
+ ASSERT_EQ(v2[2], 8.0);
+
+ ASSERT_EQ(v2.x(), 1.0);
+ ASSERT_EQ(v2.y(), 4.0);
+ ASSERT_EQ(v2.z(), 8.0);
+
+ ASSERT_EQ(v2.r(), 1.0);
+ ASSERT_EQ(v2.g(), 4.0);
+ ASSERT_EQ(v2.b(), 8.0);
+
+ ASSERT_EQ(v2.length2(), 81.0);
+ ASSERT_EQ(v2.length(), 9.0);
+ ASSERT_EQ(v2.sum(), 13.0);
+
+ Vector<double, 3> v3{ v2 };
+
+ ASSERT_EQ(v2, v3);
+
+ v3 = Vector<double, 3>{{ 4.0, 4.0, 4.0 }};
+
+ ASSERT_EQ(v2 + v3, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ ASSERT_EQ(v2 + 4.0, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ ASSERT_EQ(v2 - v3, (Vector<double, 3>{{ -3.0, 0.0, 4.0 }}));
+ ASSERT_EQ(v2 - 4.0, (Vector<double, 3>{{ -3.0, 0.0, 4.0 }}));
+ ASSERT_EQ(v2 * v3, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ ASSERT_EQ(v2 * 4.0, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ ASSERT_EQ(v2 / v3, (Vector<double, 3>{{ 0.25, 1.0, 2.0 }}));
+ ASSERT_EQ(v2 / 4.0, (Vector<double, 3>{{ 0.25, 1.0, 2.0 }}));
+
+ ASSERT_EQ(v2.min(v3), (Vector<double, 3>{{ 1.0, 4.0, 4.0 }}));
+ ASSERT_EQ(v2.min(4.0), (Vector<double, 3>{{ 1.0, 4.0, 4.0 }}));
+ ASSERT_EQ(v2.max(v3), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
+ ASSERT_EQ(v2.max(4.0), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
+
+ ASSERT_EQ(v2.dot(v3), 52.0);
+
+ v2 += v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ v2 -= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+ v2 *= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ v2 /= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+
+ v2 += 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ v2 -= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+ v2 *= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ v2 /= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(VectorTest)
diff --git a/test/ipa/meson.build b/test/ipa/meson.build
index f925c50a..ceed15ba 100644
--- a/test/ipa/meson.build
+++ b/test/ipa/meson.build
@@ -1,14 +1,17 @@
+# SPDX-License-Identifier: CC0-1.0
+
+subdir('libipa')
+
ipa_test = [
- ['ipa_module_test', 'ipa_module_test.cpp'],
- ['ipa_interface_test', 'ipa_interface_test.cpp'],
- ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'],
+ {'name': 'ipa_module_test', 'sources': ['ipa_module_test.cpp']},
+ {'name': 'ipa_interface_test', 'sources': ['ipa_interface_test.cpp']},
]
-foreach t : ipa_test
- exe = executable(t[0], t[1],
- dependencies : libcamera_dep,
- link_with : [libipa, test_libraries],
- include_directories : [libipa_includes, test_includes_internal])
+foreach test : ipa_test
+ exe = executable(test['name'], test['sources'],
+ dependencies : [libcamera_private, libipa_dep],
+ link_with : [test_libraries],
+ include_directories : [test_includes_internal])
- test(t[0], exe, suite : 'ipa')
+ test(test['name'], exe, suite : 'ipa')
endforeach