UPGRADES UPGRADES UPGRADES
This commit is contained in:
parent
2945835eeb
commit
ce6f8e6a46
|
|
@ -0,0 +1,4 @@
|
|||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17-homebrew
|
||||
gradle=9.2.1
|
||||
11
RampTest.c
11
RampTest.c
|
|
@ -18,7 +18,7 @@ AppRAMPCfg_Type AppRAMPCfg =
|
|||
.LFOSCClkFreq = 32000.0,
|
||||
.SysClkFreq = 16000000.0,
|
||||
.AdcClkFreq = 16000000.0,
|
||||
.RcalVal = 10000.0,
|
||||
.RcalVal = 100.0,
|
||||
.ADCRefVolt = 1820.0f,
|
||||
.bTestFinished = bFALSE,
|
||||
|
||||
|
|
@ -64,7 +64,14 @@ AD5940Err AppRAMPCtrl(uint32_t Command, void *pPara)
|
|||
|
||||
if(AD5940_WakeUp(10) > 10) return AD5940ERR_WAKEUP;
|
||||
if(AppRAMPCfg.RAMPInited == bFALSE) return AD5940ERR_APPERROR;
|
||||
if(AppRAMPCfg.RampState == RAMP_STOP) return AD5940ERR_APPERROR;
|
||||
|
||||
// --- CRITICAL FIX: Reset State on Start ---
|
||||
AppRAMPCfg.RampState = RAMP_STATE0;
|
||||
AppRAMPCfg.CurrStepPos = 0;
|
||||
AppRAMPCfg.bFirstDACSeq = bTRUE;
|
||||
AppRAMPCfg.StopRequired = bFALSE;
|
||||
AppRAMPCfg.FifoThresh = 4;
|
||||
// ------------------------------------------
|
||||
|
||||
wupt_cfg.WuptEn = bTRUE;
|
||||
wupt_cfg.WuptEndSeq = WUPTENDSEQ_D;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# File: host/CMakeLists.txt
|
||||
# host/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
project(EISConfigurator VERSION 1.0 LANGUAGES CXX C)
|
||||
|
|
@ -20,22 +21,17 @@ include(FetchContent)
|
|||
option(BUILD_ANDROID "Build for Android" OFF)
|
||||
option(BUILD_IOS "Build for iOS" OFF)
|
||||
|
||||
# --- Dependencies ---
|
||||
# Added SerialPort and PrintSupport for EISConfigurator
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets SerialPort PrintSupport OpenGLWidgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets SerialPort PrintSupport)
|
||||
|
||||
# --- FFTW3 Configuration (Double Precision) ---
|
||||
# ==========================================
|
||||
# --- FFTW3 CONFIGURATION ---
|
||||
# ==========================================
|
||||
|
||||
if(WIN32)
|
||||
# Windows: Expects FFTW3 to be installed/found via Config or PkgConfig
|
||||
# If not found, you might need to adjust paths or use the source build block below for Windows too
|
||||
find_package(FFTW3 CONFIG QUIET)
|
||||
# Windows: Expects FFTW3 to be installed/found via Config
|
||||
find_package(FFTW3 CONFIG REQUIRED)
|
||||
if(TARGET FFTW3::fftw3)
|
||||
add_library(fftw3 ALIAS FFTW3::fftw3)
|
||||
else()
|
||||
# Fallback to building from source on Windows if not found
|
||||
message(STATUS "FFTW3 not found via Config. Building from source...")
|
||||
set(BUILD_FFTW_FROM_SOURCE ON)
|
||||
endif()
|
||||
elseif(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" AND NOT BUILD_IOS)
|
||||
message(STATUS "Detected Apple Silicon Desktop. Using Homebrew FFTW3 (Double).")
|
||||
|
|
@ -52,10 +48,6 @@ elseif(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" AND NOT BUILD_IOS)
|
|||
INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIR}"
|
||||
)
|
||||
else()
|
||||
set(BUILD_FFTW_FROM_SOURCE ON)
|
||||
endif()
|
||||
|
||||
if(BUILD_FFTW_FROM_SOURCE)
|
||||
message(STATUS "Building FFTW3 from source (Double Precision)...")
|
||||
|
||||
set(ENABLE_FLOAT OFF CACHE BOOL "Build double precision" FORCE)
|
||||
|
|
@ -69,17 +61,15 @@ if(BUILD_FFTW_FROM_SOURCE)
|
|||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build Static Libs" FORCE)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "Disable Tests" FORCE)
|
||||
|
||||
# Enhanced NEON detection
|
||||
# Enable NEON for Android ARM64 / iOS
|
||||
if(ANDROID_ABI STREQUAL "arm64-v8a")
|
||||
message(STATUS "Enabling NEON for Android ARM64")
|
||||
set(ENABLE_NEON ON CACHE BOOL "Enable NEON" FORCE)
|
||||
elseif(BUILD_IOS)
|
||||
set(ENABLE_NEON ON CACHE BOOL "Enable NEON" FORCE)
|
||||
elseif(MSVC AND CMAKE_SYSTEM_PROCESSOR MATCHES "(ARM64|arm64|aarch64)")
|
||||
set(ENABLE_NEON ON CACHE BOOL "Enable NEON" FORCE)
|
||||
endif()
|
||||
|
||||
# Only apply sed patch on UNIX-like systems to fix CMake version requirement in FFTW source
|
||||
# Patch for older CMake versions inside the tarball if needed
|
||||
if(UNIX)
|
||||
set(PATCH_CMD sed -i.bak "s/cmake_minimum_required.*/cmake_minimum_required(VERSION 3.16)/" <SOURCE_DIR>/CMakeLists.txt)
|
||||
else()
|
||||
|
|
@ -96,26 +86,18 @@ if(BUILD_FFTW_FROM_SOURCE)
|
|||
FetchContent_MakeAvailable(fftw3_source)
|
||||
endif()
|
||||
|
||||
# --- QCustomPlot Integration ---
|
||||
|
||||
# ==========================================
|
||||
# --- QCUSTOMPLOT ---
|
||||
# ==========================================
|
||||
FetchContent_Declare(
|
||||
QCustomPlot
|
||||
URL https://www.qcustomplot.com/release/2.1.1/QCustomPlot.tar.gz
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(QCustomPlot)
|
||||
|
||||
FetchContent_GetProperties(QCustomPlot)
|
||||
if(NOT qcustomplot_POPULATED)
|
||||
FetchContent_Populate(QCustomPlot)
|
||||
add_library(QCustomPlot
|
||||
${qcustomplot_SOURCE_DIR}/qcustomplot.cpp
|
||||
${qcustomplot_SOURCE_DIR}/qcustomplot.h
|
||||
)
|
||||
target_link_libraries(QCustomPlot PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport)
|
||||
target_include_directories(QCustomPlot PUBLIC ${qcustomplot_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# --- Icon Generation ---
|
||||
# ==========================================
|
||||
# --- ICON GENERATION ---
|
||||
# ==========================================
|
||||
|
||||
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/assets/icon_source.png")
|
||||
|
||||
|
|
@ -125,56 +107,36 @@ if(NOT MAGICK_EXECUTABLE)
|
|||
endif()
|
||||
|
||||
if(EXISTS "${ICON_SOURCE}" AND MAGICK_EXECUTABLE)
|
||||
|
||||
if(WIN32)
|
||||
# --- WINDOWS SPECIFIC ---
|
||||
set(WINDOWS_ICON "${CMAKE_CURRENT_BINARY_DIR}/app_icon.ico")
|
||||
set(WINDOWS_RC "${CMAKE_CURRENT_BINARY_DIR}/app_icon.rc")
|
||||
# Assuming you have a batch script or use the shell script via git bash
|
||||
set(ICON_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_icons.sh")
|
||||
|
||||
# Create .rc file
|
||||
set(ICON_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_icons.bat")
|
||||
file(WRITE "${WINDOWS_RC}" "IDI_ICON1 ICON \"app_icon.ico\"\n")
|
||||
|
||||
# Generate ICO (Requires bash on Windows or a separate .bat script)
|
||||
# For simplicity, we assume a unix-like environment or WSL for generation
|
||||
add_custom_command(
|
||||
OUTPUT "${WINDOWS_ICON}"
|
||||
COMMAND "${ICON_SCRIPT}" "${MAGICK_EXECUTABLE}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND "${ICON_SCRIPT}" "${MAGICK_EXECUTABLE}" "${ICON_SOURCE}" "${WINDOWS_ICON}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS "${ICON_SOURCE}" "${ICON_SCRIPT}"
|
||||
COMMENT "Generating Icons..."
|
||||
COMMENT "Generating Windows Icon..."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Copy generated ico to binary dir for RC compiler
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/app_icon.ico"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/assets/icons/app_icon.ico" "${WINDOWS_ICON}"
|
||||
DEPENDS "${WINDOWS_ICON}"
|
||||
)
|
||||
|
||||
add_custom_target(GenerateIcons DEPENDS "${WINDOWS_ICON}")
|
||||
|
||||
else()
|
||||
# --- MAC/LINUX/ANDROID/IOS SPECIFIC ---
|
||||
set(MACOS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/assets/icons/app_icon.icns")
|
||||
set(IOS_ASSETS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/ios/Assets.xcassets")
|
||||
set(IOS_CONTENTS_JSON "${IOS_ASSETS_PATH}/Contents.json")
|
||||
set(ANDROID_RES_PATH "${CMAKE_CURRENT_SOURCE_DIR}/android/res/mipmap-mdpi/ic_launcher.png")
|
||||
|
||||
set(ICON_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_icons.sh")
|
||||
execute_process(COMMAND chmod +x "${ICON_SCRIPT}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MACOS_ICON}" "${ANDROID_RES_PATH}"
|
||||
OUTPUT "${MACOS_ICON}" "${IOS_CONTENTS_JSON}" "${ANDROID_RES_PATH}"
|
||||
COMMAND "${ICON_SCRIPT}" "${MAGICK_EXECUTABLE}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
DEPENDS "${ICON_SOURCE}" "${ICON_SCRIPT}"
|
||||
COMMENT "Generating Cross-Platform Icons..."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(GenerateIcons DEPENDS "${MACOS_ICON}" "${ANDROID_RES_PATH}")
|
||||
add_custom_target(GenerateIcons DEPENDS "${MACOS_ICON}" "${IOS_CONTENTS_JSON}" "${ANDROID_RES_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -184,9 +146,10 @@ set(PROJECT_SOURCES
|
|||
src/main.cpp
|
||||
src/MainWindow.cpp
|
||||
src/MainWindow_UI.cpp
|
||||
src/MainWindow_Serial.cpp
|
||||
src/MainWindow_Actions.cpp
|
||||
src/MainWindow_Serial.cpp
|
||||
src/GraphWidget.cpp
|
||||
${qcustomplot_SOURCE_DIR}/qcustomplot.cpp
|
||||
)
|
||||
|
||||
if(EXISTS "${ICON_SOURCE}")
|
||||
|
|
@ -200,22 +163,27 @@ endif()
|
|||
set(PROJECT_HEADERS
|
||||
src/MainWindow.h
|
||||
src/GraphWidget.h
|
||||
${qcustomplot_SOURCE_DIR}/qcustomplot.h
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
add_library(EISConfigurator SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
|
||||
else()
|
||||
add_executable(EISConfigurator ${PROJECT_SOURCES} ${PROJECT_HEADERS})
|
||||
endif()
|
||||
# Use qt_add_executable for proper Android/iOS handling
|
||||
qt_add_executable(EISConfigurator MANUAL_FINALIZATION ${PROJECT_SOURCES} ${PROJECT_HEADERS})
|
||||
|
||||
if(EXISTS "${ICON_SOURCE}" AND MAGICK_EXECUTABLE)
|
||||
add_dependencies(EISConfigurator GenerateIcons)
|
||||
endif()
|
||||
|
||||
# --- Mobile Definitions ---
|
||||
if(BUILD_ANDROID OR BUILD_IOS)
|
||||
target_compile_definitions(EISConfigurator PRIVATE IS_MOBILE)
|
||||
endif()
|
||||
|
||||
# --- Linking ---
|
||||
|
||||
# Handle FFTW3 Linking and Include Paths
|
||||
if(TARGET fftw3)
|
||||
set(FFTW_TARGET fftw3)
|
||||
# If built from source via FetchContent, we need to manually add include dirs
|
||||
if(DEFINED fftw3_source_SOURCE_DIR)
|
||||
target_include_directories(EISConfigurator PRIVATE
|
||||
"${fftw3_source_SOURCE_DIR}/api"
|
||||
|
|
@ -223,25 +191,26 @@ if(TARGET fftw3)
|
|||
)
|
||||
endif()
|
||||
else()
|
||||
# Fallback if target isn't defined (shouldn't happen with above logic)
|
||||
set(FFTW_TARGET fftw3)
|
||||
endif()
|
||||
|
||||
target_include_directories(EISConfigurator PRIVATE ${qcustomplot_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(EISConfigurator PRIVATE
|
||||
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::SerialPort Qt6::PrintSupport
|
||||
QCustomPlot
|
||||
${FFTW_TARGET}
|
||||
)
|
||||
|
||||
if(BUILD_ANDROID)
|
||||
target_link_libraries(EISConfigurator PRIVATE log m)
|
||||
set_target_properties(EISConfigurator PROPERTIES QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
set_property(TARGET EISConfigurator PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
endif()
|
||||
|
||||
if(BUILD_IOS)
|
||||
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/Info.plist")
|
||||
message(FATAL_ERROR "Missing ios/Info.plist. Please create it before building.")
|
||||
endif()
|
||||
|
||||
set_target_properties(EISConfigurator PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "EIS Configurator"
|
||||
|
|
@ -250,7 +219,6 @@ if(BUILD_IOS)
|
|||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
RESOURCE "${IOS_ASSETS_PATH}"
|
||||
)
|
||||
|
||||
if(EXISTS "${ICON_SOURCE}")
|
||||
file(MAKE_DIRECTORY "${IOS_ASSETS_PATH}")
|
||||
target_sources(EISConfigurator PRIVATE "${IOS_ASSETS_PATH}")
|
||||
|
|
@ -271,6 +239,5 @@ elseif(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
qt_finalize_executable(EISConfigurator)
|
||||
endif()
|
||||
# CRITICAL: Triggers androiddeployqt to build the APK
|
||||
qt_finalize_executable(EISConfigurator)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Makefile
|
||||
# host/Makefile
|
||||
|
||||
QT_ANDROID_KIT ?= $(HOME)/Qt/6.8.3/android_arm64_v8a
|
||||
QT_IOS_KIT ?= $(HOME)/Qt/6.8.3/ios
|
||||
|
|
@ -13,7 +13,7 @@ TARGET = EISConfigurator
|
|||
|
||||
# Android Specifics
|
||||
PKG_NAME = org.qtproject.example.EISConfigurator
|
||||
# CRITICAL FIX: Qt6 generates 'android-build-debug.apk' by default
|
||||
# Qt6 generates 'android-build-debug.apk' by default
|
||||
APK_PATH = $(BUILD_DIR_ANDROID)/android-build/build/outputs/apk/debug/android-build-debug.apk
|
||||
|
||||
all: macos
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.qtproject.example.YrCrystals"
|
||||
package="org.qtproject.example.EISConfigurator"
|
||||
android:versionName="1.0"
|
||||
android:versionCode="1"
|
||||
android:installLocation="auto">
|
||||
|
|
@ -10,12 +10,12 @@
|
|||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application android:label="Yr Crystals"
|
||||
<application android:label="EIS Configurator"
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
|
||||
<activity android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:label="Yr Crystals"
|
||||
android:label="EIS Configurator"
|
||||
android:screenOrientation="unspecified"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="YrCrystals"/>
|
||||
<meta-data android:name="android.app.lib_name" android:value="EISConfigurator"/>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -119,9 +119,16 @@ void MainWindow::toggleAmperometry() {
|
|||
|
||||
void MainWindow::startLSVBlank() {
|
||||
if (!serial->isOpen()) return;
|
||||
|
||||
// Toggle Logic: If running, stop it.
|
||||
if (lsvState == LSV_RUNNING_BLANK) {
|
||||
stopLSV();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMeasuringImp) toggleMeasurement();
|
||||
if (isMeasuringAmp) toggleAmperometry();
|
||||
if (lsvState != LSV_IDLE) stopLSV();
|
||||
if (lsvState != LSV_IDLE) stopLSV(); // Stop sample if running
|
||||
if (isSweeping) startSweep();
|
||||
|
||||
double start = spinLsvStart->value();
|
||||
|
|
@ -140,8 +147,9 @@ void MainWindow::startLSVBlank() {
|
|||
setButtonBlinking(lsvBlankBtn, true);
|
||||
lsvState = LSV_RUNNING_BLANK;
|
||||
|
||||
// Only clear if we are actually starting a new run
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_BLANK);
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF); // Clear old diff
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
||||
lsvBlankData.clear();
|
||||
|
||||
tabWidget->setCurrentIndex(3);
|
||||
|
|
@ -149,9 +157,16 @@ void MainWindow::startLSVBlank() {
|
|||
|
||||
void MainWindow::startLSVSample() {
|
||||
if (!serial->isOpen()) return;
|
||||
|
||||
// Toggle Logic
|
||||
if (lsvState == LSV_RUNNING_SAMPLE) {
|
||||
stopLSV();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMeasuringImp) toggleMeasurement();
|
||||
if (isMeasuringAmp) toggleAmperometry();
|
||||
if (lsvState != LSV_IDLE) stopLSV();
|
||||
if (lsvState != LSV_IDLE) stopLSV(); // Stop blank if running
|
||||
if (isSweeping) startSweep();
|
||||
|
||||
double start = spinLsvStart->value();
|
||||
|
|
@ -170,8 +185,9 @@ void MainWindow::startLSVSample() {
|
|||
setButtonBlinking(lsvSampleBtn, true);
|
||||
lsvState = LSV_RUNNING_SAMPLE;
|
||||
|
||||
// Only clear if we are actually starting a new run
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_SAMPLE);
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF); // Clear old diff
|
||||
lsvGraph->clearLSV(GraphWidget::LSV_DIFF);
|
||||
lsvSampleData.clear();
|
||||
|
||||
tabWidget->setCurrentIndex(3);
|
||||
|
|
@ -282,108 +298,73 @@ void MainWindow::performCircleFit() {
|
|||
int n = sweepReals.size();
|
||||
if (n < 5) return;
|
||||
|
||||
double measuredRs = -1.0;
|
||||
bool found = false;
|
||||
// 1. Centering (Crucial for stability)
|
||||
double meanX = 0, meanY = 0;
|
||||
for(int i=0; i<n; i++) { meanX += sweepReals[i]; meanY += -sweepImags[i]; }
|
||||
meanX /= n; meanY /= n;
|
||||
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
double img1 = sweepImags[i];
|
||||
double img2 = sweepImags[i+1];
|
||||
// 2. Kasa Fit (Algebraic) on Centered Data
|
||||
// Minimizes sum((x^2 + y^2) - (2Ax + 2By + C))^2
|
||||
|
||||
if ((img1 >= 0 && img2 < 0) || (img1 < 0 && img2 >= 0)) {
|
||||
double r1 = sweepReals[i];
|
||||
double r2 = sweepReals[i+1];
|
||||
double sum_x2 = 0, sum_y2 = 0, sum_xy = 0;
|
||||
double sum_z = 0, sum_zx = 0, sum_zy = 0;
|
||||
|
||||
if (std::abs(img2 - img1) < 1e-9) continue;
|
||||
for(int i=0; i<n; i++) {
|
||||
double xi = sweepReals[i] - meanX;
|
||||
double yi = -sweepImags[i] - meanY;
|
||||
double zi = xi*xi + yi*yi;
|
||||
|
||||
double fraction = (0.0 - img1) / (img2 - img1);
|
||||
double crossingReal = r1 + fraction * (r2 - r1);
|
||||
|
||||
if (!found || crossingReal < measuredRs) {
|
||||
measuredRs = crossingReal;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
sum_x2 += xi*xi;
|
||||
sum_y2 += yi*yi;
|
||||
sum_xy += xi*yi;
|
||||
sum_z += zi;
|
||||
sum_zx += zi*xi;
|
||||
sum_zy += zi*yi;
|
||||
}
|
||||
|
||||
if (found && measuredRs > 0) {
|
||||
lblResultRs->setText(QString(" Rs: %1 Ω (Meas)").arg(measuredRs, 0, 'f', 2));
|
||||
// Solve 3x3 Linear System (Normal Equations) for Centered Kasa
|
||||
// [ 4*sum_x2 4*sum_xy 0 ] [ A ] [ 2*sum_zx ]
|
||||
// [ 4*sum_xy 4*sum_y2 0 ] [ B ] = [ 2*sum_zy ]
|
||||
// [ 0 0 n ] [ C ] [ sum_z ]
|
||||
|
||||
double cond = (cellConstant / measuredRs) * 1000000.0;
|
||||
double C = sum_z / n;
|
||||
|
||||
// Solve 2x2 for A, B
|
||||
double D = 16 * (sum_x2 * sum_y2 - sum_xy * sum_xy);
|
||||
|
||||
if (std::abs(D) < 1e-9) return; // Collinear or insufficient data
|
||||
|
||||
double A = (2 * sum_zx * 4 * sum_y2 - 2 * sum_zy * 4 * sum_xy) / D;
|
||||
double B = (4 * sum_x2 * 2 * sum_zy - 4 * sum_xy * 2 * sum_zx) / D;
|
||||
|
||||
double xc = A + meanX;
|
||||
double yc = B + meanY;
|
||||
double r_sq = A*A + B*B + C;
|
||||
|
||||
if (r_sq <= 0) return;
|
||||
double r = std::sqrt(r_sq);
|
||||
|
||||
// Calculate Intercepts with Real Axis (y=0)
|
||||
// (x - xc)^2 + (0 - yc)^2 = r^2
|
||||
// (x - xc)^2 = r^2 - yc^2
|
||||
|
||||
double term = r*r - yc*yc;
|
||||
if (term < 0) return; // Circle doesn't intersect real axis
|
||||
|
||||
double x1 = xc - std::sqrt(term);
|
||||
double x2 = xc + std::sqrt(term);
|
||||
|
||||
double Rs = std::min(x1, x2);
|
||||
if (Rs < 0) Rs = std::max(x1, x2); // If one is negative, take the positive one
|
||||
|
||||
if (Rs > 0) {
|
||||
lblResultRs->setText(QString(" Rs: %1 Ω").arg(Rs, 0, 'f', 2));
|
||||
|
||||
double cond = (cellConstant / Rs) * 1000000.0;
|
||||
lblResultCond->setText(QString(" Cond: %1 µS/cm").arg(cond, 0, 'f', 2));
|
||||
|
||||
nyquistGraph->setExtrapolatedPoint(measuredRs, 0);
|
||||
return;
|
||||
nyquistGraph->setExtrapolatedPoint(Rs, 0);
|
||||
}
|
||||
|
||||
int startIdx = n - (n / 3);
|
||||
if (startIdx < 0) startIdx = 0;
|
||||
int count = n - startIdx;
|
||||
if (count < 3) return;
|
||||
|
||||
double sum_x = 0, sum_y = 0, sum_x2 = 0, sum_y2 = 0;
|
||||
double sum_xy = 0, sum_x3 = 0, sum_y3 = 0, sum_xy2 = 0, sum_x2y = 0;
|
||||
|
||||
for (int i = startIdx; i < n; i++) {
|
||||
double x = sweepReals[i];
|
||||
double y = -sweepImags[i];
|
||||
|
||||
double x2 = x * x;
|
||||
double y2 = y * y;
|
||||
|
||||
sum_x += x;
|
||||
sum_y += y;
|
||||
sum_x2 += x2;
|
||||
sum_y2 += y2;
|
||||
sum_xy += x * y;
|
||||
sum_x3 += x2 * x;
|
||||
sum_y3 += y2 * y;
|
||||
sum_xy2 += x * y2;
|
||||
sum_x2y += x2 * y;
|
||||
}
|
||||
|
||||
double M11 = sum_x2, M12 = sum_xy, M13 = sum_x;
|
||||
double M21 = sum_xy, M22 = sum_y2, M23 = sum_y;
|
||||
double M31 = sum_x, M32 = sum_y, M33 = (double)count;
|
||||
|
||||
double R1 = sum_x3 + sum_xy2;
|
||||
double R2 = sum_x2y + sum_y3;
|
||||
double R3 = sum_x2 + sum_y2;
|
||||
|
||||
double det = M11*(M22*M33 - M23*M32) - M12*(M21*M33 - M23*M31) + M13*(M21*M32 - M22*M31);
|
||||
|
||||
if (std::abs(det) < 1e-9) return;
|
||||
|
||||
double detA = R1*(M22*M33 - M23*M32) - M12*(R2*M33 - M23*R3) + M13*(R2*M32 - M22*R3);
|
||||
double detB = M11*(R2*M33 - M23*R3) - R1*(M21*M33 - M23*M31) + M13*(M21*R3 - R2*M31);
|
||||
double detC = M11*(M22*R3 - R2*M32) - M12*(M21*R3 - R2*M31) + R1*(M21*M32 - M22*M31);
|
||||
|
||||
double A = detA / det;
|
||||
double B = detB / det;
|
||||
double C = detC / det;
|
||||
|
||||
double xc = A / 2.0;
|
||||
double r_sq = C + (A*A)/4.0 + (B*B)/4.0;
|
||||
|
||||
if (r_sq < 0) return;
|
||||
|
||||
double disc = A*A + 4*C;
|
||||
if (disc < 0) return;
|
||||
|
||||
double x1 = (A - std::sqrt(disc)) / 2.0;
|
||||
double x2 = (A + std::sqrt(disc)) / 2.0;
|
||||
|
||||
double Rs = 0;
|
||||
if (x1 > 0 && x2 > 0) Rs = std::min(x1, x2);
|
||||
else if (x1 > 0) Rs = x1;
|
||||
else if (x2 > 0) Rs = x2;
|
||||
else Rs = std::max(x1, x2);
|
||||
|
||||
lblResultRs->setText(QString(" Rs: %1 Ω").arg(Rs, 0, 'f', 2));
|
||||
|
||||
double cond = (cellConstant / Rs) * 1000000.0;
|
||||
lblResultCond->setText(QString(" Cond: %1 µS/cm").arg(cond, 0, 'f', 2));
|
||||
|
||||
nyquistGraph->setExtrapolatedPoint(Rs, 0);
|
||||
}
|
||||
|
||||
void MainWindow::calibrateCellConstant() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue