diff --git a/gui/res/icons/scopy-default/icons/tool_imuanalyzer.svg b/gui/res/icons/scopy-default/icons/tool_imuanalyzer.svg
new file mode 100644
index 0000000000..ae8d21ade5
--- /dev/null
+++ b/gui/res/icons/scopy-default/icons/tool_imuanalyzer.svg
@@ -0,0 +1,99 @@
+
diff --git a/gui/res/icons/scopy-light/icons/tool_imuanalyzer.svg b/gui/res/icons/scopy-light/icons/tool_imuanalyzer.svg
new file mode 100644
index 0000000000..6ad910c6e8
--- /dev/null
+++ b/gui/res/icons/scopy-light/icons/tool_imuanalyzer.svg
@@ -0,0 +1,99 @@
+
diff --git a/gui/res/resources.qrc b/gui/res/resources.qrc
index f5bb18704d..b78b54c0ed 100644
--- a/gui/res/resources.qrc
+++ b/gui/res/resources.qrc
@@ -177,6 +177,8 @@
icons/device-shapes/square.svgicons/device-shapes/rounded_square.svgicons/info.svg
+ icons/scopy-light/icons/tool_imuanalyzer.svg
+ icons/scopy-default/icons/tool_imuanalyzer.svgicons/scopy-default/icons/search.svg
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index e6c56c3478..825eb64c9a 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -36,6 +36,7 @@ option(ENABLE_PLUGIN_DAC "Enable DAC plugin" ON)
option(ENABLE_PLUGIN_AD9084 "Enable AD9084 plugin" ON)
option(ENABLE_PLUGIN_JESDSTATUS "Enable JESDSTATUS plugin" ON)
option(ENABLE_PLUGIN_PLUTO "Enable PLUTO plugin" ON)
+option(ENABLE_PLUGIN_IMUANALYZER "Enable IMUANALYZER plugin" ON)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${SCOPY_PLUGIN_BUILD_PATH})
@@ -143,6 +144,11 @@ if(ENABLE_PLUGIN_PLUTO)
list(APPEND PLUGINS ${PLUGIN_NAME})
endif()
+if(ENABLE_PLUGIN_IMUANALYZER)
+ add_subdirectory(imu)
+ list(APPEND PLUGINS ${IMUANALYZER_TARGET_NAME})
+endif()
+
install(TARGETS ${PLUGINS} LIBRARY DESTINATION ${SCOPY_PLUGIN_INSTALL_PATH})
set(PLUGINS ${PLUGINS} PARENT_SCOPE)
diff --git a/plugins/imu/.gitignore b/plugins/imu/.gitignore
new file mode 100644
index 0000000000..de39b9ff02
--- /dev/null
+++ b/plugins/imu/.gitignore
@@ -0,0 +1 @@
+include/imuanalyzer/scopy-imuanalyzer_export.h
diff --git a/plugins/imu/CMakeLists.txt b/plugins/imu/CMakeLists.txt
new file mode 100644
index 0000000000..2cce3f680b
--- /dev/null
+++ b/plugins/imu/CMakeLists.txt
@@ -0,0 +1,124 @@
+#
+# Copyright (c) 2025 Analog Devices Inc.
+#
+# This file is part of Scopy
+# (see https://www.github.com/analogdevicesinc/scopy).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+cmake_minimum_required(VERSION 3.9)
+
+set(SCOPY_MODULE imuanalyzer)
+
+message(STATUS "building plugin: " ${SCOPY_MODULE})
+
+project(scopy-${SCOPY_MODULE} VERSION 0.1 LANGUAGES CXX)
+
+include(GenerateExportHeader)
+
+# TODO : split stylesheet / resources and add here TODO : export header files correctly
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
+
+set(SCOPY_QT_COMPONENTS Core Widgets)
+
+file(GLOB SRC_LIST src/*.cpp src/*.cc)
+file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp)
+file(GLOB UI_LIST ui/*.ui)
+
+set(ENABLE_TESTING ON)
+if(ENABLE_TESTING)
+ add_subdirectory(test)
+endif()
+
+set(PROJECT_SOURCES ${SRC_LIST} ${HEADER_LIST} ${UI_LIST})
+
+find_package(
+ Qt${QT_VERSION_MAJOR}
+ COMPONENTS ${SCOPY_QT_COMPONENTS}
+ REQUIRED
+ Core
+ Gui
+ Widgets
+ 3DCore
+ 3DExtras
+ 3DRender
+ 3DInput
+)
+
+add_library(
+ ${PROJECT_NAME} SHARED
+ ${PROJECT_SOURCES}
+ include/imuanalyzer/imuanalyzerinterface.hpp
+ include/imuanalyzer/scenerenderer.hpp
+ include/imuanalyzer/bubblelevelrenderer.hpp
+ include/imuanalyzer/imuanalyzerutils.hpp
+ include/imuanalyzer/imuanalyzersettings.hpp
+ src/datavisualizer.cpp
+ include/imuanalyzer/datavisualizer.hpp
+ # ${PROJECT_RESOURCES}
+)
+
+generate_export_header(
+ ${PROJECT_NAME} EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_export.h
+)
+
+target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE})
+
+target_include_directories(${PROJECT_NAME} PUBLIC scopy-pluginbase scopy-gui)
+target_include_directories(
+ ${PROJECT_NAME}
+ PRIVATE ${IIO_INCLUDE_DIRS}
+ scopy-gui
+ scopy-iio-widgets
+ scopy-iioutil
+ ${OPENGL_INCLUDE_DIRS}
+ ${GLUT_INCLUDE_DIRS}
+)
+
+foreach(comp ${SCOPY_QT_COMPONENTS})
+ set(SCOPY_QT_LIBRARIES ${SCOPY_QT_LIBRARIES} Qt${QT_VERSION_MAJOR}::${comp})
+endforeach()
+
+target_link_libraries(
+ ${PROJECT_NAME}
+ PUBLIC Qt::Widgets
+ Qt::Core
+ scopy-pluginbase
+ scopy-gui
+ scopy-iioutil
+ scopy-iio-widgets
+ Qt5::Core
+ Qt5::Gui
+ Qt5::Widgets
+ Qt5::3DCore
+ Qt5::3DExtras
+ Qt5::3DRender
+ Qt5::3DInput
+)
+
+set(PLUGIN_NAME ${PROJECT_NAME} PARENT_SCOPE)
diff --git a/plugins/imu/include/imuanalyzer/bubblelevelrenderer.hpp b/plugins/imu/include/imuanalyzer/bubblelevelrenderer.hpp
new file mode 100644
index 0000000000..d4739190d9
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/bubblelevelrenderer.hpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef BUBBLELEVELRENDERER_H
+#define BUBBLELEVELRENDERER_H
+
+#include "imuanalyzerutils.hpp"
+#include "scopy-imuanalyzer_export.h"
+#include "plotwidget.h"
+#include
+
+#include
+#include
+#include
+
+namespace scopy {
+
+class SCOPY_IMUANALYZER_EXPORT BubbleLevelRenderer : public QWidget
+{
+ Q_OBJECT
+public:
+ BubbleLevelRenderer(QWidget *parent = nullptr);
+ //~BubbleLevelRenderer();
+
+public Q_SLOTS:
+ void setRot(data3P rot, data3P pos, float temp);
+ void setDisplayPoints(QString displayP);
+
+private:
+ data3P m_rot;
+ QVector xLinePoint{90, 90};
+ QVector yLinePoint{3, 3};
+ PlotWidget *plotWidget;
+ QwtPlotCurve *point;
+ QString m_displayPoints;
+
+public:
+};
+} // namespace scopy
+#endif // BUBBLELEVELRENDERER_H
diff --git a/plugins/imu/include/imuanalyzer/datavisualizer.hpp b/plugins/imu/include/imuanalyzer/datavisualizer.hpp
new file mode 100644
index 0000000000..9be7c58d5f
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/datavisualizer.hpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef DATAVISUALIZER_HPP
+#define DATAVISUALIZER_HPP
+
+#include "scopy-imuanalyzer_export.h"
+#include
+#include
+#include
+#include
+
+#include "imuanalyzerutils.hpp"
+
+namespace scopy {
+
+class DataVisualizer : public QWidget
+{
+ Q_OBJECT
+public:
+ DataVisualizer(QWidget *parent = nullptr);
+
+public Q_SLOTS:
+ void updateValues(data3P rot, data3P pos, float temp);
+
+private:
+ data3P m_rot, m_deltaRot;
+ float temp = 0.0f;
+ QPlainTextEdit *m_accelTextBox, *m_linearATextBox, *m_magnTextBox, *m_tempTextBox;
+};
+} // namespace scopy
+
+#endif // DATAVISUALIZER_HPP
diff --git a/plugins/imu/include/imuanalyzer/imuanalyzer.h b/plugins/imu/include/imuanalyzer/imuanalyzer.h
new file mode 100644
index 0000000000..7debe5547b
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/imuanalyzer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef IMUANALYZER_H
+#define IMUANALYZER_H
+
+#define SCOPY_PLUGIN_NAME IMUAnalyzer
+
+#include "scopy-imuanalyzer_export.h"
+#include "imuanalyzerinterface.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "qloggingcategory.h"
+
+#include
+#include
+#include "style.h"
+
+#include
+#include
+
+namespace scopy {
+
+class SCOPY_IMUANALYZER_EXPORT IMUAnalyzer : public QObject, public PluginBase
+{
+ Q_OBJECT
+ SCOPY_PLUGIN;
+
+public:
+ void initMetadata() override;
+ bool compatible(QString m_param, QString category) override;
+ void loadToolList() override;
+
+ bool onConnect() override;
+ bool onDisconnect() override;
+ bool loadIcon() override;
+
+private:
+ IMUAnalyzerInterface *m_imuInterface;
+};
+
+} // namespace scopy
+
+#endif // IMUANALYZER
diff --git a/plugins/imu/include/imuanalyzer/imuanalyzerinterface.hpp b/plugins/imu/include/imuanalyzer/imuanalyzerinterface.hpp
new file mode 100644
index 0000000000..c9465016e7
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/imuanalyzerinterface.hpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef IMUANALYZERINTERFACE_H
+#define IMUANALYZERINTERFACE_H
+
+#include "scopy-imuanalyzer_export.h"
+#include "scenerenderer.hpp"
+#include "bubblelevelrenderer.hpp"
+#include "imuanalyzersettings.hpp"
+#include "datavisualizer.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace scopy {
+
+class SCOPY_IMUANALYZER_EXPORT IMUAnalyzerInterface : public QWidget
+{
+ Q_OBJECT
+public:
+ IMUAnalyzerInterface(QString uri, QWidget *parent = nullptr);
+ ~IMUAnalyzerInterface();
+
+Q_SIGNALS:
+ void generateRot(data3P rot);
+ void updateValues(data3P rot, data3P pos, float temp);
+
+public:
+ void generateRotation();
+ void initIIODevice();
+
+private:
+ ToolTemplate *m_tool;
+
+ InfoBtn *m_infoBtn;
+ RunBtn *m_runBtn;
+ MenuControlButton *m_rstView;
+ MenuControlButton *m_measureBtn;
+ GearBtn *m_gearBtn;
+
+ SceneRenderer *m_sceneRender;
+ BubbleLevelRenderer *m_bubbleLevelRenderer;
+
+ ImuAnalyzerSettings *m_settingsPanel;
+ DataVisualizer *m_dataV;
+ data3P m_rot = {0.0f, 0.0f, 0.0f};
+ data3P m_dist = {0.0f, 0.0f, 0.0f};
+
+ std::thread t;
+ QString m_uri;
+
+ iio_device *m_device;
+};
+} // namespace scopy
+
+#endif // IMUANALYZERINTERFACE_H
diff --git a/plugins/imu/include/imuanalyzer/imuanalyzersettings.hpp b/plugins/imu/include/imuanalyzer/imuanalyzersettings.hpp
new file mode 100644
index 0000000000..38684dcd8b
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/imuanalyzersettings.hpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef IMUANALYZERSETTINGS_H
+#define IMUANALYZERSETTINGS_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bubblelevelrenderer.hpp"
+#include "scenerenderer.hpp"
+#include "menuspinbox.h"
+
+#include
+#include
+
+#include "scopy-imuanalyzer_export.h"
+#include "iio.h"
+#include "iio-widgets/iiowidget.h"
+#include "iio-widgets/iiowidgetbuilder.h"
+#include "iio-widgets/iiowidgetdata.h"
+
+namespace scopy {
+
+class SCOPY_IMUANALYZER_EXPORT ImuAnalyzerSettings : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ImuAnalyzerSettings(SceneRenderer *scRend, BubbleLevelRenderer *blRend, iio_device *device,
+ QWidget *parent = nullptr);
+
+signals:
+ void updateCubeColor(QColor color);
+ void updatePlaneColor(QColor color);
+ void updateDisplayPoints(QString displayP);
+
+private:
+ iio_device *m_device;
+};
+} // namespace scopy
+#endif // IMUANALYZERSETTINGS_H
diff --git a/plugins/imu/include/imuanalyzer/imuanalyzerutils.hpp b/plugins/imu/include/imuanalyzer/imuanalyzerutils.hpp
new file mode 100644
index 0000000000..1717f53c04
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/imuanalyzerutils.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef IMUANALYZERUTILS_HPP
+#define IMUANALYZERUTILS_HPP
+
+struct data3P
+{
+ float dataX;
+ float dataY;
+ float dataZ;
+};
+
+#endif // IMUANALYZERUTILS_HPP
diff --git a/plugins/imu/include/imuanalyzer/scenerenderer.hpp b/plugins/imu/include/imuanalyzer/scenerenderer.hpp
new file mode 100644
index 0000000000..a83322126c
--- /dev/null
+++ b/plugins/imu/include/imuanalyzer/scenerenderer.hpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef SCENERENDERER_H
+#define SCENERENDERER_H
+
+#include "scopy-imuanalyzer_export.h"
+#include "imuanalyzerutils.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace scopy {
+class SCOPY_IMUANALYZER_EXPORT SceneRenderer : public QWidget
+{
+ Q_OBJECT
+public:
+ SceneRenderer(QWidget *parent = nullptr);
+ // ~SceneRenderer();
+private:
+ Qt3DExtras::Qt3DWindow *view;
+ QWidget *container;
+ Qt3DCore::QEntity *rootEntity;
+
+ Qt3DCore::QTransform *cubeTransform;
+ Qt3DCore::QTransform *planeTransform;
+ Qt3DExtras::QPhongMaterial *m_cubeMaterial;
+ Qt3DExtras::QPhongMaterial *m_planeMaterial;
+
+ Qt3DCore::QEntity *cubeEntity;
+
+ QColor m_cubeColor = QRgb(0x008ECC);
+ QColor m_planeColor = QRgb(0xa0a0a0);
+
+public Q_SLOTS:
+ void resetView();
+ void setRot(data3P rot);
+ void updateCubeColor(QColor color);
+ void updatePlaneColor(QColor color);
+};
+} // namespace scopy
+
+#endif // SCENERENDERER_H
diff --git a/plugins/imu/src/bubblelevelrenderer.cpp b/plugins/imu/src/bubblelevelrenderer.cpp
new file mode 100644
index 0000000000..ab7fd0d44f
--- /dev/null
+++ b/plugins/imu/src/bubblelevelrenderer.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "bubblelevelrenderer.hpp"
+
+using namespace scopy;
+
+BubbleLevelRenderer::BubbleLevelRenderer(QWidget *parent)
+ : QWidget{parent}
+{
+ QHBoxLayout *lay = new QHBoxLayout(this);
+ lay->setMargin(0);
+ setLayout(lay);
+
+ m_rot = {0.0f, 0.0f, 0.0f};
+ m_displayPoints = "Raw: XY";
+
+ plotWidget = new PlotWidget(this);
+ lay->addWidget(plotWidget);
+
+ plotWidget->xAxis()->setInterval(-10.0, 10.0);
+ plotWidget->yAxis()->setInterval(-10.0, 10.0);
+
+ plotWidget->xAxis()->setVisible(true);
+ plotWidget->yAxis()->setVisible(true);
+
+ point = new QwtPlotCurve("Point");
+ point->setSamples(xLinePoint, yLinePoint);
+ point->setStyle(QwtPlotCurve::Dots);
+ point->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::red), QPen(Qt::red), QSize(20, 20)));
+ point->attach(plotWidget->plot());
+}
+
+void BubbleLevelRenderer::setRot(data3P rot, data3P pos, float temp)
+{
+ if(m_displayPoints == "Raw: XY") {
+ xLinePoint = {double(pos.dataX), double(pos.dataX)};
+ yLinePoint = {double(pos.dataY), double(pos.dataY)};
+
+ } else if(m_displayPoints == "Raw: XZ") {
+ xLinePoint = {double(pos.dataX), double(pos.dataX)};
+ yLinePoint = {double(pos.dataZ), double(pos.dataZ)};
+
+ } else if(m_displayPoints == "Raw: YZ") {
+ xLinePoint = {double(pos.dataY), double(pos.dataY)};
+ yLinePoint = {double(pos.dataZ), double(pos.dataZ)};
+
+ } else if(m_displayPoints == "Rotation: XZ") {
+ xLinePoint = {double(rot.dataX), double(rot.dataX)};
+ yLinePoint = {double(rot.dataZ), double(rot.dataZ)};
+
+ } else if(m_displayPoints == "Rotation: ZX") {
+ xLinePoint = {double(rot.dataZ), double(rot.dataZ)};
+ yLinePoint = {double(rot.dataX), double(rot.dataX)};
+ }
+
+ point->setSamples(xLinePoint, yLinePoint);
+ plotWidget->replot();
+}
+
+void BubbleLevelRenderer::setDisplayPoints(QString displayP)
+{
+ m_displayPoints = displayP;
+
+ if(m_displayPoints.contains("Rotation")) {
+ plotWidget->xAxis()->setInterval(-180.0, 180.0);
+ plotWidget->yAxis()->setInterval(-180.0, 180.0);
+
+ } else {
+ plotWidget->xAxis()->setInterval(-10.0, 10.0);
+ plotWidget->yAxis()->setInterval(-10.0, 10.0);
+ }
+}
diff --git a/plugins/imu/src/datavisualizer.cpp b/plugins/imu/src/datavisualizer.cpp
new file mode 100644
index 0000000000..0bf03c09b7
--- /dev/null
+++ b/plugins/imu/src/datavisualizer.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+using namespace scopy;
+
+DataVisualizer::DataVisualizer(QWidget *parent)
+ : QWidget(parent)
+{
+ QHBoxLayout *lay = new QHBoxLayout(this);
+ lay->setMargin(0);
+ lay->setSpacing(10);
+ setLayout(lay);
+
+ m_accelTextBox = new QPlainTextEdit();
+ m_accelTextBox->setFont(QFont("Courier New"));
+ m_accelTextBox->setReadOnly(true);
+ lay->addWidget(m_accelTextBox);
+
+ m_linearATextBox = new QPlainTextEdit();
+ m_linearATextBox->setFont(QFont("Courier New"));
+ m_linearATextBox->setReadOnly(true);
+ lay->addWidget(m_linearATextBox);
+
+ m_tempTextBox = new QPlainTextEdit();
+ m_tempTextBox->setFont(QFont("Courier New"));
+ m_tempTextBox->setReadOnly(true);
+ lay->addWidget(m_tempTextBox);
+
+ this->setFixedHeight(80);
+ this->setFixedWidth(500);
+}
+
+void DataVisualizer::updateValues(data3P rot, data3P pos, float temp)
+{
+ QString infoA = QString("Rotation: \nX: %1º\nY: %2º").arg(rot.dataX).arg(rot.dataY);
+ m_accelTextBox->setPlainText(infoA);
+
+ QString infoL = QString("Linear Acceleration: \nX: %1 m/s²\nY: %2 m/s²\nZ: %3 m/s²")
+ .arg(pos.dataX)
+ .arg(pos.dataY)
+ .arg(pos.dataZ);
+ m_linearATextBox->setPlainText(infoL);
+
+ QString infoT = QString("Temperature: %1ºC\n").arg(temp / 100);
+ m_tempTextBox->setPlainText(infoT);
+}
diff --git a/plugins/imu/src/imuanalyzer.cpp b/plugins/imu/src/imuanalyzer.cpp
new file mode 100644
index 0000000000..5d27efc7a3
--- /dev/null
+++ b/plugins/imu/src/imuanalyzer.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "imuanalyzer.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+Q_LOGGING_CATEGORY(CAT_IMUANALYZER, "IMUAnalyzer");
+using namespace scopy;
+
+bool IMUAnalyzer::compatible(QString m_param, QString category)
+{
+ qDebug(CAT_IMUANALYZER) << "compatible";
+ bool ret = false;
+ Connection *conn = ConnectionProvider::GetInstance()->open(m_param);
+ if(conn == nullptr)
+ return ret;
+
+ for(int i = 0; i < iio_context_get_devices_count(conn->context()); i++) {
+ iio_device *dev = iio_context_get_device(conn->context(), i);
+ std::string name = iio_device_get_name(dev);
+ if(name.find("adis") != std::string::npos) {
+ ret = true;
+ goto finish;
+ }
+ }
+finish:
+
+ ConnectionProvider::GetInstance()->close(m_param);
+ return ret;
+}
+
+void IMUAnalyzer::loadToolList()
+{
+ m_toolList.append(SCOPY_NEW_TOOLMENUENTRY("IMUAnalyzer_tool", "imuanalyzer",
+ ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) +
+ "/icons/tool_imuanalyzer.svg"));
+}
+
+bool IMUAnalyzer::onConnect()
+{
+
+ Connection *conn = ConnectionProvider::GetInstance()->open(m_param);
+
+ if(conn == nullptr) {
+ return false;
+ }
+
+ m_toolList[0]->setEnabled(true);
+
+ m_imuInterface = new IMUAnalyzerInterface(m_param);
+
+ QString tool_name = QString("IMUAnalyzer");
+
+ m_toolList.last()->setTool(m_imuInterface);
+ m_toolList.last()->setVisible(true);
+
+ return true;
+}
+
+bool IMUAnalyzer::onDisconnect()
+{
+ m_toolList[0]->setEnabled(false);
+ m_toolList[0]->setTool(nullptr);
+
+ ConnectionProvider::GetInstance()->close(m_param);
+
+ return true;
+}
+
+bool IMUAnalyzer::loadIcon()
+{
+ SCOPY_PLUGIN_ICON(":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) +
+ "/icons/tool_imuanalyzer.svg");
+ QLabel *logo = new QLabel();
+ QPixmap pixmap(":/gui/icons/scopy-default/icons/logo_analog.svg");
+ int pixmapHeight = 14;
+ pixmap = pixmap.scaledToHeight(pixmapHeight, Qt::SmoothTransformation);
+ logo->setPixmap(pixmap);
+
+ QLabel *footer = new QLabel("IMUAnalyzer");
+ Style::setStyle(footer, style::properties::label::deviceIcon, true);
+
+ m_icon = DeviceIconBuilder().shape(DeviceIconBuilder::SQUARE).headerWidget(logo).footerWidget(footer).build();
+ return true;
+}
+
+void IMUAnalyzer::initMetadata() // not actually needed - putting it here to set priority
+{
+ loadMetadata(
+ R"plugin(
+ {
+ "priority":100,
+ "category":[
+ "iio"
+ ]
+ }
+)plugin");
+}
+
+#include "moc_imuanalyzer.cpp"
diff --git a/plugins/imu/src/imuanalyzerinterface.cpp b/plugins/imu/src/imuanalyzerinterface.cpp
new file mode 100644
index 0000000000..cd84415627
--- /dev/null
+++ b/plugins/imu/src/imuanalyzerinterface.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "imuanalyzerinterface.hpp"
+
+using namespace scopy;
+
+Q_DECLARE_METATYPE(data3P)
+
+std::atomic m_runThread(false);
+
+IMUAnalyzerInterface::IMUAnalyzerInterface(QString uri, QWidget *parent)
+ : QWidget{parent}
+{
+ qRegisterMetaType("data3P");
+
+ QHBoxLayout *lay = new QHBoxLayout(this);
+ lay->setMargin(0);
+ setLayout(lay);
+
+ m_uri = uri;
+ initIIODevice();
+
+ m_tool = new ToolTemplate(this);
+ m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_tool->topContainer()->setVisible(true);
+ m_tool->leftContainer()->setVisible(false);
+ m_tool->rightContainer()->setVisible(true);
+ m_tool->bottomContainer()->setVisible(true);
+ m_tool->topContainerMenuControl()->setVisible(false);
+ m_tool->setRightContainerWidth(300);
+ m_tool->centralContainer()->layout()->setSpacing(10);
+
+ lay->addWidget(m_tool);
+
+ m_dataV = new DataVisualizer();
+ m_tool->addWidgetToCentralContainerHelper(m_dataV);
+ m_dataV->hide();
+
+ connect(this, &IMUAnalyzerInterface::updateValues, m_dataV, &DataVisualizer::updateValues);
+
+ m_infoBtn = new InfoBtn(this, false);
+ m_tool->addWidgetToTopContainerHelper(m_infoBtn, TTA_LEFT);
+
+ m_runBtn = new RunBtn(this);
+ m_tool->addWidgetToTopContainerHelper(m_runBtn, TTA_RIGHT);
+
+ QTabWidget *tabWidget = new QTabWidget(this);
+ m_tool->addWidgetToCentralContainerHelper(tabWidget);
+
+ m_sceneRender = new SceneRenderer();
+ tabWidget->addTab(m_sceneRender, "3D View");
+
+ m_rstView = new MenuControlButton(this);
+ m_rstView->setName("Reset View");
+ m_rstView->checkBox()->setVisible(false);
+ m_rstView->button()->setVisible(false);
+ m_rstView->setCheckable(false);
+ m_tool->addWidgetToBottomContainerHelper(m_rstView, TTA_LEFT);
+
+ connect(m_rstView, &QPushButton::clicked, m_sceneRender, &SceneRenderer::resetView);
+
+ m_measureBtn = new MenuControlButton(this);
+ m_measureBtn->setName("Measure");
+ m_measureBtn->checkBox()->setVisible(false);
+ m_measureBtn->button()->setVisible(false);
+ m_measureBtn->setCheckable(true);
+ m_measureBtn->setChecked(false);
+ m_tool->addWidgetToBottomContainerHelper(m_measureBtn, TTA_RIGHT);
+
+ connect(m_measureBtn, &QPushButton::toggled, this, [=, this](bool toggled) {
+ if(toggled)
+ m_dataV->show();
+ else
+ m_dataV->hide();
+ });
+
+ connect(this, &IMUAnalyzerInterface::generateRot, m_sceneRender, &SceneRenderer::setRot);
+
+ connect(m_runBtn, &QPushButton::toggled, [=, this](bool toggled) {
+ m_runThread = !m_runThread;
+ if(toggled) {
+ t = std::thread(&IMUAnalyzerInterface::generateRotation, this);
+ } else {
+ t.join();
+ }
+ });
+
+ m_bubbleLevelRenderer = new BubbleLevelRenderer(tabWidget);
+ tabWidget->addTab(m_bubbleLevelRenderer, "2D View");
+
+ connect(this, &IMUAnalyzerInterface::updateValues, m_bubbleLevelRenderer, &BubbleLevelRenderer::setRot);
+
+ m_gearBtn = new GearBtn(this);
+ m_tool->addWidgetToTopContainerHelper(m_gearBtn, TTA_RIGHT);
+
+ m_gearBtn->setChecked(true);
+
+ QString key = "Settings";
+ m_settingsPanel = new ImuAnalyzerSettings(m_sceneRender, m_bubbleLevelRenderer, m_device);
+
+ QScrollArea *scrollArea = new QScrollArea;
+ scrollArea->setWidget(m_settingsPanel);
+ scrollArea->setWidgetResizable(true);
+
+ m_tool->rightStack()->add(key, scrollArea);
+
+ connect(m_gearBtn, &GearBtn::toggled, this, [=, this](bool toggled) {
+ if(toggled)
+ m_tool->requestMenu(key);
+
+ m_tool->openRightContainerHelper(toggled);
+ });
+
+ connect(tabWidget, &QTabWidget::currentChanged, [=, this](int index) {
+ if(index == 0)
+ m_rstView->show();
+ else
+ m_rstView->hide();
+ });
+}
+
+IMUAnalyzerInterface::~IMUAnalyzerInterface() { t.join(); }
+
+void IMUAnalyzerInterface::generateRotation()
+{
+
+ iio_channel *linearAccChX = iio_device_find_channel(m_device, "accel_x", false);
+ iio_channel *linearAccChY = iio_device_find_channel(m_device, "accel_y", false);
+ iio_channel *linearAccChZ = iio_device_find_channel(m_device, "accel_z", false);
+
+ iio_channel *tempCh = iio_device_find_channel(m_device, "temp0", false);
+
+ double samplingFreq;
+ iio_device_attr_read_double(m_device, "sampling_frequency", &samplingFreq);
+
+ double linearAccGainX, linearAccGainY, linearAccGainZ;
+ iio_channel_attr_read_double(linearAccChX, "scale", &linearAccGainX);
+ iio_channel_attr_read_double(linearAccChY, "scale", &linearAccGainY);
+ iio_channel_attr_read_double(linearAccChZ, "scale", &linearAccGainZ);
+
+ double tempGain, tempOffset;
+ if(tempCh != nullptr) {
+ iio_channel_attr_read_double(tempCh, "scale", &tempGain);
+ iio_channel_attr_read_double(tempCh, "offset", &tempOffset);
+ }
+
+ double linearAccX, linearAccY, linearAccZ;
+ double temp;
+
+ while(m_runThread) {
+
+ iio_channel_attr_read_double(linearAccChX, "raw", &linearAccX);
+ iio_channel_attr_read_double(linearAccChY, "raw", &linearAccY);
+ iio_channel_attr_read_double(linearAccChZ, "raw", &linearAccZ);
+
+ m_dist.dataX = float(linearAccX * linearAccGainX);
+ m_dist.dataY = float(linearAccY * linearAccGainY);
+ m_dist.dataZ = float(linearAccZ * linearAccGainZ);
+
+ if(tempCh != nullptr) {
+ iio_channel_attr_read_double(tempCh, "raw", &temp);
+ temp = temp * tempGain - tempOffset;
+ }
+
+ m_rot.dataX = atan2(-m_dist.dataX, sqrt(m_dist.dataY * m_dist.dataY + m_dist.dataZ * m_dist.dataZ)) *
+ 180 / 3.14f;
+ m_rot.dataY = atan2(m_dist.dataY, m_dist.dataZ) * 180 / 3.14f;
+ m_rot.dataZ = 0;
+
+ QMetaObject::invokeMethod(this, "generateRot", Qt::QueuedConnection, Q_ARG(data3P, m_rot));
+ QMetaObject::invokeMethod(this, "updateValues", Qt::QueuedConnection, Q_ARG(data3P, m_rot),
+ Q_ARG(data3P, m_dist), Q_ARG(float, float(temp)));
+ }
+}
+
+void IMUAnalyzerInterface::initIIODevice()
+{
+ Connection *conn = ConnectionProvider::GetInstance()->open(m_uri);
+ for(int i = 0; i < iio_context_get_devices_count(conn->context()); i++) {
+ m_device = iio_context_get_device(conn->context(), i);
+ std::string name = iio_device_get_name(m_device);
+ if(name.find("adis") != std::string::npos) {
+ return;
+ }
+ }
+}
+
+#include "moc_imuanalyzerinterface.cpp"
diff --git a/plugins/imu/src/imuanalyzersettings.cpp b/plugins/imu/src/imuanalyzersettings.cpp
new file mode 100644
index 0000000000..33de77c663
--- /dev/null
+++ b/plugins/imu/src/imuanalyzersettings.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "imuanalyzersettings.hpp"
+
+using namespace scopy;
+
+ImuAnalyzerSettings::ImuAnalyzerSettings(SceneRenderer *scRend, BubbleLevelRenderer *blRend, iio_device *device,
+ QWidget *parent)
+ : QWidget{parent}
+{
+ QVBoxLayout *lay = new QVBoxLayout();
+ lay->setMargin(0);
+ lay->setSpacing(10);
+ setLayout(lay);
+
+ m_device = device;
+
+ MenuHeaderWidget *header = new MenuHeaderWidget("Settings", QPen(Qt::blue));
+
+ MenuSectionWidget *generalSettings = new MenuSectionWidget(this);
+ MenuCollapseSection *generalSettingsWidget =
+ new MenuCollapseSection("General Settings", MenuCollapseSection::MHCW_ARROW,
+ MenuCollapseSection::MHW_BASEWIDGET, generalSettings);
+ generalSettingsWidget->contentLayout()->setSpacing(10);
+
+ MenuSectionWidget *bubbleLevelSettings = new MenuSectionWidget(this);
+ MenuCollapseSection *bubbleLevelSettingsWidget =
+ new MenuCollapseSection("2D Settings", MenuCollapseSection::MHCW_ARROW,
+ MenuCollapseSection::MHW_BASEWIDGET, bubbleLevelSettings);
+
+ MenuCombo *displayPoints = new MenuCombo("Display Points");
+ displayPoints->combo()->addItem("Raw: XY");
+ displayPoints->combo()->addItem("Raw: XZ");
+ displayPoints->combo()->addItem("Raw: YZ");
+ displayPoints->combo()->addItem("Rotation: XZ");
+ displayPoints->combo()->addItem("Rotation: ZX");
+ displayPoints->combo()->setCurrentIndex(0);
+
+ connect(this, &ImuAnalyzerSettings::updateDisplayPoints, blRend, &BubbleLevelRenderer::setDisplayPoints);
+ connect(displayPoints->combo(), qOverload(&QComboBox::currentIndexChanged), this,
+ [=](int idx) { emit updateDisplayPoints(displayPoints->combo()->itemText(idx)); });
+
+ bubbleLevelSettingsWidget->contentLayout()->addWidget(displayPoints);
+
+ MenuSectionWidget *sceneRendererSettings = new MenuSectionWidget(this);
+ MenuCollapseSection *sceneRendererSettingsWidget =
+ new MenuCollapseSection("3D Settings", MenuCollapseSection::MHCW_ARROW,
+ MenuCollapseSection::MHW_BASEWIDGET, sceneRendererSettings);
+
+ QPushButton *cubeColorButton = new QPushButton("Select New Cube Color");
+ sceneRendererSettingsWidget->contentLayout()->addWidget(cubeColorButton);
+
+ connect(this, &ImuAnalyzerSettings::updateCubeColor, scRend, &SceneRenderer::updateCubeColor);
+ connect(cubeColorButton, &QPushButton::clicked, [&]() {
+ QColor color = QColorDialog::getColor(Qt::white, this, "Choose a Color");
+ if(color.isValid())
+ emit updateCubeColor(color);
+ });
+
+ QPushButton *planeColorButton = new QPushButton("Select New Plane Color");
+ sceneRendererSettingsWidget->contentLayout()->addWidget(planeColorButton);
+
+ connect(this, &ImuAnalyzerSettings::updatePlaneColor, scRend, &SceneRenderer::updatePlaneColor);
+ connect(planeColorButton, &QPushButton::clicked, [&]() {
+ QColor color = QColorDialog::getColor(Qt::white, this, "Choose a Color");
+ if(color.isValid())
+ emit updatePlaneColor(color);
+ });
+
+ QList attributeWidget = IIOWidgetBuilder(nullptr).device(m_device).buildAll();
+ for(auto widget : attributeWidget) {
+ generalSettingsWidget->contentLayout()->addWidget(widget);
+ }
+
+ generalSettings->layout()->addWidget(generalSettingsWidget);
+ bubbleLevelSettings->layout()->addWidget(bubbleLevelSettingsWidget);
+ sceneRendererSettings->layout()->addWidget(sceneRendererSettingsWidget);
+
+ lay->addWidget(header);
+ lay->addWidget(generalSettings);
+ lay->addWidget(bubbleLevelSettings);
+ lay->addWidget(sceneRendererSettings);
+
+ lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
+}
diff --git a/plugins/imu/src/scenerenderer.cpp b/plugins/imu/src/scenerenderer.cpp
new file mode 100644
index 0000000000..1e7d575ab7
--- /dev/null
+++ b/plugins/imu/src/scenerenderer.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2025 Analog Devices Inc.
+ *
+ * This file is part of Scopy
+ * (see https://www.github.com/analogdevicesinc/scopy).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "scenerenderer.hpp"
+
+using namespace scopy;
+
+SceneRenderer::SceneRenderer(QWidget *parent)
+ : QWidget{parent}
+{
+ view = new Qt3DExtras::Qt3DWindow();
+ container = QWidget::createWindowContainer(view, this);
+ container->setMinimumSize(400, 300);
+
+ QVBoxLayout *layout = new QVBoxLayout();
+ layout->addWidget(container);
+ layout->setMargin(0);
+ this->setLayout(layout);
+
+ // Root entity
+ rootEntity = new Qt3DCore::QEntity();
+
+ // Camera
+ Qt3DRender::QCamera *camera = view->camera();
+ camera->lens()->setPerspectiveProjection(45.0f, 16.f / 9.f, 0.1f, 1000.0f);
+ camera->setPosition(QVector3D(0, 2.5f, 5.0f));
+ camera->setViewCenter(QVector3D(0, 0, 0));
+
+ // Camera controls
+ auto *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
+ camController->setCamera(camera);
+
+ // Initialize cube
+
+ // Cube mesh
+ auto *cubeMesh = new Qt3DExtras::QCuboidMesh();
+
+ // Transform
+ cubeTransform = new Qt3DCore::QTransform();
+
+ // Material
+ m_cubeMaterial = new Qt3DExtras::QPhongMaterial();
+ m_cubeMaterial->setDiffuse(m_cubeColor);
+
+ // Entity
+ cubeEntity = new Qt3DCore::QEntity(rootEntity);
+ cubeEntity->addComponent(cubeMesh);
+ cubeEntity->addComponent(cubeTransform);
+ cubeEntity->addComponent(m_cubeMaterial);
+
+ // Add line axes
+ auto *planeMesh = new Qt3DExtras::QPlaneMesh(rootEntity);
+ planeMesh->setWidth(400);
+ planeMesh->setHeight(300);
+
+ m_planeMaterial = new Qt3DExtras::QPhongMaterial();
+ m_planeMaterial->setDiffuse(m_planeColor);
+
+ // Transform
+ planeTransform = new Qt3DCore::QTransform();
+ planeTransform->setTranslation((QVector3D(0.0f, -1.0f, 0.0f)));
+
+ auto meshEntity = new Qt3DCore::QEntity(rootEntity);
+ meshEntity->addComponent(planeMesh);
+ meshEntity->addComponent(planeTransform);
+ meshEntity->addComponent(m_planeMaterial);
+
+ // Set root entity
+ view->setRootEntity(rootEntity);
+}
+
+void SceneRenderer::resetView()
+{
+ view->camera()->lens()->setPerspectiveProjection(45.0f, 16.f / 9.f, 0.1f, 1000.0f);
+ view->camera()->setPosition(QVector3D(0, 2.5f, 5.0f));
+ view->camera()->setViewCenter(QVector3D(0, 0, 0));
+ view->camera()->setUpVector(QVector3D(0.0f, 1.0f, 0.0f));
+}
+
+void SceneRenderer::setRot(data3P rot)
+{
+ // To match screen rotation with real time rotation
+ cubeTransform->setRotationX(rot.dataX);
+ cubeTransform->setRotationY(rot.dataZ);
+ cubeTransform->setRotationZ(rot.dataY);
+}
+
+void SceneRenderer::updateCubeColor(QColor color) { m_cubeMaterial->setDiffuse(color); }
+
+void SceneRenderer::updatePlaneColor(QColor color) { m_planeMaterial->setDiffuse(color); }
+
+#include "moc_scenerenderer.cpp"
diff --git a/plugins/imu/test/CMakeLists.txt b/plugins/imu/test/CMakeLists.txt
new file mode 100644
index 0000000000..2040573fc2
--- /dev/null
+++ b/plugins/imu/test/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# Copyright (c) 2025 Analog Devices Inc.
+#
+# This file is part of Scopy
+# (see https://www.github.com/analogdevicesinc/scopy).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+cmake_minimum_required(VERSION 3.5)
+
+include(ScopyTest)
diff --git a/resources/emu_xml/adis.xml b/resources/emu_xml/adis.xml
new file mode 100644
index 0000000000..a2823abc35
--- /dev/null
+++ b/resources/emu_xml/adis.xml
@@ -0,0 +1 @@
+]>
\ No newline at end of file
diff --git a/resources/scopy_emu_options_config.json b/resources/scopy_emu_options_config.json
index c076c61c83..3d8a4b36b4 100644
--- a/resources/scopy_emu_options_config.json
+++ b/resources/scopy_emu_options_config.json
@@ -34,6 +34,13 @@
{
"device": "generic",
"uri": "ip:127.0.0.1"
+ },
+ {
+ "device": "adis",
+ "xml_path": "adis.xml",
+ "uri": "ip:127.0.0.1"
}
+
+
]