diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..604a5004 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,43 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +**IMPORTANT:** Always unplug and replug the scope if used before with a different application, e.g. sigrok or the original Hantek software. + +Steps to reproduce the behavior: +1. Go to '...' +2. Select '....' +3. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots or a video to help explain your problem. + +**Computer environment (please complete the following information):** + - OpenHantek version: [use always the latest commit, e.g. *release v2.03, commit 1a2b3c4, ...*] + - OS: [e.g. *linux, macOS, windows, ...*] + - Distribution, version: [e.g. *debian stable amd64, ...*] + - Video hardware: [e.g. *Intel Xeon ... Integrated Graphics*] + - OpenGL version: [e.g. *OpenGL 4.5, Mesa 19.0.3*] + - Qt version: [e.g. *Qt 5.11.x*] + - C++ compiler version: [e.g. *gcc 8.3.x*] + +**Scope device (please complete the following information):** + - Device [e.g. *Hantek 6022BE, ...*] + - Program top line [e.g. *OpenHantek6022 (Build ...) - Device ... (FW ...)*] + - Input signal: [e.g. *calibration out 1 kHz, sine wave 1 Veff @ 50 Hz, ...*] + - Probe setting: [e.g. *no probe, probe X1, probe X10, ...*] + +**Additional context** +Add any other context about the problem here, e.g. the program output if started in a console like xterm. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..bbcbbe7d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 8041f871..b2d98cb5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,16 @@ .o .so .*~ +*~ +*.orig +*.new +*.diff CMakeLists.txt.user -build/ +build* firmware/hex +*.json +*.kate-swp +.directory +*.autosave +ui_*.h +*.sh diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..09eadbb9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "macdeployqtfix"] + path = utils/macdeployqtfix + url = https://github.com/iltommi/macdeployqtfix diff --git a/.travis.yml b/.travis.yml index ab3cb97f..1f4a9b02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,38 +1,67 @@ language: cpp -dist: xenial +dist: bionic addons: apt: packages: - qtbase5-dev - - qttools5-dev-tools + - qttools5-dev - libqt5opengl5-dev - libusb-1.0-0-dev - libfftw3-dev + - binutils-dev + - rpm matrix: include: - compiler: gcc - env: CMAKE_CXX_COMPILER=g++-5 + env: CMAKE_CXX_COMPILER=/usr/bin/g++ os: linux - compiler: clang env: CMAKE_CXX_COMPILER=/usr/bin/clang++ os: osx - + before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install qt5 fftw binutils; export CMAKE_PREFIX_PATH=$(brew --prefix qt5); fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update; brew install qt5 fftw binutils create-dmg ; + export CMAKE_PREFIX_PATH=$(brew --prefix qt5) ; + echo "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ; + fi - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then pip install cmake --user; fi before_script: - mkdir build - cd build - - cmake -DCMAKE_CXX_COMPILER=$CMAKE_CXX_COMPILER -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ../ + - cmake -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} ../ script: - - make -j4 + - make -j4 # create either a bin or a bundle (.app) - depending on openhantek/CMakeLists.txt + - sudo make -j4 package # simply pack the binary, otherwise populate the bundle below + - if test -d openhantek/OpenHantek.app; then + export PATH=${CMAKE_PREFIX_PATH}/bin:${PATH} ; + cd openhantek ; + macdeployqt OpenHantek.app -always-overwrite -verbose=1 ; + python ../../utils/macdeployqtfix/macdeployqtfix.py OpenHantek.app/Contents/MacOS/OpenHantek ${CMAKE_PREFIX_PATH} ; + create-dmg --volname OpenHantek --volicon ../../openhantek/res/images/openhantek.icns --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "OpenHantek.app" 200 190 --hide-extension "OpenHantek.app" --app-drop-link 600 185 --eula ../../LICENSE OpenHantek.dmg OpenHantek.app ; + cd .. ; + else + ls -l packages ; + true; + fi + - ls -l openhantek || true before_deploy: - - sudo make package + - if test -d openhantek/OpenHantek.app; then + cd openhantek ; + sudo tar -cvzf ../packages/openhantek_*_osx_*.tar.gz OpenHantek.app ; + if test -f OpenHantek.dmg ; then + sudo mv OpenHantek.dmg ../packages/$(basename ../packages/openhantek_*_osx_*.tar.gz .tar.gz).dmg ; + fi ; + cd .. ; + else + true; + fi + - ls -l packages || true deploy: provider: releases diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..2302423a --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,170 @@ +3.1.3 +2020-09-30 +RPM build improvements +New command line option --fontsize +Display horizontal trigger level line when marker is moved +Make smooth trigger setting persistent + +3.1.2 +2020-07-29 +Small improvements for FreeBSD +Hide Windows console + +3.1.1 +2020-06-30 +Refresh button to restart roll mode trace +Help menu link to AC modification howto +Minor robustness improvements + +3.1.1-rc3 +2020-06-26 +Real roll mode for slow sample rates + +3.1.1-rc2 +2020-06-23 +Scroll wheel moves the markers +Configuration menu parameter to use AC coupling HW modification + +3.1.1-rc1 +2020-06-20 +Slow timebases up to 10s/div +Near real time screen updates with trigger mode NONE +Screenshot and hardcopy are identical to the screen content +Force OpenGL SL version (option --useGLSL120 or --useGLSL150) + +3.1.0 +2020-05-08 +Demo mode without scope hardware (option -d or --demoMode) +Support for high-dpi displays +Improved pdf readability +Russian translation + +3.0.4 +2020-04-13 +Better marker setting/removing +Fix sample rate spinbox handling +Fix race condition when switching 1ch/2ch +Persistent calibration out frequency +FW 0206 +Updated DDS120 FW 0100 (supports more calibration out frequencies) +Spanish translation + +3.0.3 +2020-03-17 +FW 0205 +Fix histogram issue when switching TY -> XY + +3.0.2 +2020-03-01 +Apply realtime prio and scheduler +Histogram for TY voltage traces +Variable probe gain setting +French translation complete + +3.0.1 +2019-12-13 +More robust USB handling for all OSs +Improve manipulation and export of frozen or single traces +Export XY display +Automatic icon selection for light/dark themes +Eye candy +Automatic start after FW upload + +3.0.0 +2019-11-15 +Major changes: +Support for Sainsmart DDS120 +Support for Hantek6022 AC HW modification +Update to FW 0203 +MacOSX bundle +Measure duty cycle of triggered trace +New versioning scheme + +v2.16 +2019-10-15 +Major changes from v2.16-rc1: +Display measured width of triggered pulse in top line. +Set OpenGL ES as default for Raspberry Pi. + +v2.16-rc1 +2019-10-11 +Major changes from v2.15: +Fix #26 (array overrun during fast increasing of timebase setting) + +v2.15 - withdrawn due to #26 +2019-10-05 +Major changes from v2.14: +Automatic translation (qt linguist), +better automatic versioning (commit date & build number), +Sinc interpolation for fast timebase settings, +modification for FreeBSD compiling. + +v2.14 +2019-09-08 +Major changes from v2.13: +Vpp measure, more oversampling, faster display refresh, +alternating trigger, faster trigger (and new smooth trigger mode), +add support for Voltcraft DSO-2020 USB oscilloscope. + +v2.13 +2019-08-09 +Major changes from v2.12: +User manual, new icons. +Calculate and display dB of AC value. + +v2.12 +2019-07-07 +Major changes from v2.11: +Better frequency detection, Gauss window, +normalise all windows to 0 dBV display, +new math mode CH1*CH2. + +v2.11 +2019-06-28 +Major changes from v2.10: +New math mode, calculate AC part of CH1/CH2 + +v2.10 +2019-06-10 +Major changes from v2.09: +Use new FW 0202 that allows more and better structured sample rates, +reload the correct FW version if the wrong version was loaded before. + +v2.09 +2019-05-25 +Major changes from v2.08: +Add slower timebases 20, 50, 100 ms/div with increased samplesize. + +v2.08 +2019-05-24 +Major changes from v2.07: +This release fixes the quite unstable spectrum display. The annoying amplitude jitter has gone. + +v2.07 +2019-05-22 +Major changes from v2.06: +First complete free open source release. +No longer dependent on nonfree Hantek firmware. + +v2.06 +2019-05-14 +Major changes from v2.05: +Refactor triggering, move from postprocessing chain into earlier hantekdsocontrol. +Last release with 2090,2150, 2250 and 5200 support. + +v2.05 +2019-05-10 +Major changes from v2.03: +Increase sample size to 20000, better trigger modes + +v2.04 - removed due to issues with 10X probe display + +v2.03 +2019-05-05 +Major changes from v2.02: +New checkbox for 10X probes. + +v2.02 +2019-04-26 +Major changes from v2.03: +1st binary release after transfer to OpenHantek/OpenHantek6022 diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d6c269..4cf7eb93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -project(OpenHantekProject) +project(OpenHantek) set(OpenGL_GL_PREFERENCE GLVND) @@ -15,38 +15,65 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/") INCLUDE_DIRECTORIES(".") -# Use CPack to make deb/rpm/zip/exe installer packages +# Use CPack to make tgz/deb/rpm/zip installer packages include(cmake/CPackInfos.cmake) +if(MSVC) + add_compile_options(/W4 -D_CRT_SECURE_NO_WARNINGS) +else() + add_compile_options(-Wall -Wextra -pedantic) +endif() + +# Silence nasty warnings "parameter passing for argument of type ... changed in GCC 7.1" +if(CMAKE_COMPILER_IS_GNUCXX) + add_compile_options(-Wno-psabi) +endif() + +# enable extra feature(s) +if( ${CMAKE_VERSION} VERSION_LESS "3.12.0" ) # deprecated, but still used in older Ubuntu releases + # Enable C++ standard library hardening -> cheap range checks for C++ arrays, vectors, and strings. + add_definitions( -D_GLIBCXX_ASSERTIONS ) + add_definitions( -D_USE_MATH_DEFINES ) + if ( DEFINED HANTEK_AC ) + add_definitions( -DHANTEK_AC ) + endif() +else() # 'add_compile_definitions()' was introduced with CMake 3.12 + add_compile_definitions( _GLIBCXX_ASSERTIONS ) + add_compile_definitions( _USE_MATH_DEFINES ) + if ( DEFINED HANTEK_AC ) + add_compile_definitions( HANTEK_AC ) + endif() +endif() + +# show all compile options and definitions +get_directory_property( CompOpts COMPILE_OPTIONS ) +message( "-- COMPILE_OPTIONS: ${CompOpts}" ) +get_directory_property( CompDefs COMPILE_DEFINITIONS ) +message( "-- COMPILE_DEFINITIONS: ${CompDefs}" ) + # Qt Widgets based Gui with OpenGL canvas add_subdirectory(openhantek) if (WIN32) - install(FILES COPYING readme.md DESTINATION ".") -else() - add_subdirectory(firmware EXCLUDE_FROM_ALL) -endif() - -if("${CMAKE_SYSTEM}" MATCHES "Linux") - if(EXISTS "/lib/udev/rules.d/") - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/firmware/60-hantek.rules" - DESTINATION "/lib/udev/rules.d/" COMPONENT Runtime) - else() - message(WARNING "Could not find udev rules directory (/lib/udev/rules.d/), skipping installation of udev rules.") - endif() + install( + FILES CHANGELOG LICENSE README + DESTINATION "." + ) endif() # Add auxiliary files to the project, so that these files appear in VisualStudio/QtCreator file(GLOB_RECURSE MDFILES "docs/*.md" "openhantek/*.md") -add_custom_target(readme SOURCES readme.md ${MDFILES}) - -# Add "cppcheck" command -add_custom_target(cppcheck COMMAND "cppcheck --enable=all -I \"${CMAKE_CURRENT_LIST_DIR}/openhantek/src\" -q ${SRC} --template=\"{file}:{line}: {severity}: {message}\"") +add_custom_target( + readme + SOURCES CHANGELOG LICENSE README readme.md ${MDFILES} +) # Add "doc" target to build the documentation. find_package(Doxygen QUIET) if (DOXYGEN_FOUND) - add_custom_target(doc + add_custom_target( + doc COMMAND "${CMAKE_COMMAND} -E remove_directory html" - COMMAND "${DOXYGEN_EXECUTABLE} Doxyfile" WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") + COMMAND "${DOXYGEN_EXECUTABLE} Doxyfile" WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" + ) endif() diff --git a/Doxyfile b/Doxyfile index 6238fedd..a77c7776 100644 --- a/Doxyfile +++ b/Doxyfile @@ -309,7 +309,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -409,25 +409,25 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. -EXTRACT_PACKAGE = NO +EXTRACT_PACKAGE = YES # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, @@ -1371,7 +1371,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1683,7 +1683,7 @@ USE_PDFLATEX = YES # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_BATCHMODE = NO +LATEX_BATCHMODE = YES # If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e7eb6476 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +This work is released under the terms of the GNU General Public License, version 3. +The full text of the license can be found at https://www.gnu.org/licenses/gpl-3.0.html diff --git a/README b/README new file mode 100644 index 00000000..47d14db7 --- /dev/null +++ b/README @@ -0,0 +1,4 @@ +OpenHantek is an oscilloscope software for Hantek DSO-6022 USB devices. +The source code and detailed developer documentation is available at: +https://github.com/OpenHantek/OpenHantek6022 (oscilloscope software) +https://github.com/Ho-Ro/Hantek6022API (hardware, firmware and tools) diff --git a/appveyor.yml b/appveyor.yml index ee1e7598..39101caa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,17 +7,41 @@ image: shallow_clone: true before_build: - - set qtpath="C:\Qt\5.11\msvc2015_64" + - set qtpath64="C:\Qt\5.11\msvc2015_64" - set arch64=x64 - set dir64=build_x64 - md %dir64% - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch64% - - cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_PREFIX_PATH=%qtpath% -DCMAKE_BUILD_TYPE=%configuration% -B%dir64% -H. + - cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_PREFIX_PATH=%qtpath64% -DCMAKE_BUILD_TYPE=%configuration% -B%dir64% -H. + + - set qtpath32="C:\Qt\5.11\msvc2015" + - set arch32=x86 + - set dir32=build_x86 + + - md %dir32% + - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch32% + - cmake -G "Visual Studio 14 2015" -DCMAKE_PREFIX_PATH=%qtpath32% -DCMAKE_BUILD_TYPE=%configuration% -B%dir32% -H. build_script: - cmake --build %dir64% --config %configuration% --target package + - cmake --build %dir32% --config %configuration% --target package artifacts: - path: $(dir64)\openhantek\$(configuration) - name: OpenHantek-$(arch64)-$(configuration)-b$(APPVEYOR_BUILD_NUMBER) + name: OpenHantek-Win-$(arch64)-$(configuration)-b$(APPVEYOR_BUILD_NUMBER) + - path: $(dir32)\openhantek\$(configuration) + name: OpenHantek-Win-$(arch32)-$(configuration)-b$(APPVEYOR_BUILD_NUMBER) + +deploy: + - tag: $(APPVEYOR_REPO_TAG_NAME) + provider: GitHub + auth_token: + secure: 4TSFa7zTbxPVDkS9pGWO0BIPD9r2NnnHGe03pg5rjEuvSH5pntkmMdLM7MePJLma + artifact: /.*\.zip/ + draft: true + prerelease: true + force_update: true + on: + #branch: master # release from master branch only + APPVEYOR_REPO_TAG: true # deploy on tag push only diff --git a/cmake/CPackInfos.cmake b/cmake/CPackInfos.cmake index f31c0d5d..825463ab 100644 --- a/cmake/CPackInfos.cmake +++ b/cmake/CPackInfos.cmake @@ -1,7 +1,7 @@ -# This file configures CPack. We setup a version number that contains -# the current git revision if git is found. A zip file is created on -# all platforms. Additionally an NSIS Installer exe is created on windows -# and a .sh installer file for linux. +# This file configures CPack. +# A tgz, deb and rpm file is created for linux (tgz and deb tested on debian stretch and buster). +# A tgz file is created for osx (not tested). +# A zip file and an NSIS Installer exe is created on windows (not tested). find_package(Git QUIET) @@ -14,11 +14,21 @@ if (GIT_EXECUTABLE AND EXISTS "${CMAKE_SOURCE_DIR}/.git") OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() +# message( STATUS "VCS_REVISION: ${VCS_REVISION}" ) if(NOT DEFINED CMD_RESULT) set(VCS_BRANCH "master") + set(GIT_COMMIT_HASH "1") set(VCS_REVISION "na") else() + execute_process( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # message( STATUS "GIT_COMMIT_HASH: ${GIT_COMMIT_HASH}" ) + execute_process( COMMAND ${GIT_EXECUTABLE} status WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} @@ -33,7 +43,7 @@ else() string(REPLACE " " ";" DESCRIBE_STATUS ${DESCRIBE_STATUS}) list(GET DESCRIBE_STATUS 2 VCS_BRANCH) - message(STATUS "Version: ${VCS_BRANCH}/${VCS_REVISION}") + # message(STATUS "Branch: ${VCS_BRANCH}") # /${VCS_REVISION}") execute_process( COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url @@ -41,8 +51,9 @@ else() RESULT_VARIABLE CMD_RESULT OUTPUT_VARIABLE VCS_URL OUTPUT_STRIP_TRAILING_WHITESPACE - ) - + ) + # message( STATUS "VCS_URL: ${VCS_URL}") + set(ENV{LANG} "en_US") if(GIT_VERSION_STRING VERSION_LESS 2.6) set(CHANGELOG "") @@ -53,73 +64,107 @@ else() RESULT_VARIABLE CMD_RESULT OUTPUT_VARIABLE CHANGELOG OUTPUT_STRIP_TRAILING_WHITESPACE - ) + ) endif() file(WRITE "${CMAKE_BINARY_DIR}/changelog" "${CHANGELOG}") endif() -string(TIMESTAMP DATE_VERSION "%d.%m.%Y") -string(TIMESTAMP CURRENT_TIME "%d.%m.%Y %H:%M") +string(TIMESTAMP DATE_VERSION "%Y%m%d") +string(TIMESTAMP CURRENT_TIME "%Y%m%d_%H:%M") if (UNIX) + execute_process( + COMMAND uname -m + WORKING_DIRECTORY "." + OUTPUT_VARIABLE CPACK_ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") - set(CPACK_GENERATOR STGZ) - if (NOT APPLE) - set(CPACK_GENERATOR ${CPACK_GENERATOR} ZIP) - find_program(LSB_RELEASE lsb_release) - execute_process(COMMAND ${LSB_RELEASE} -is - OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT - OUTPUT_STRIP_TRAILING_WHITESPACE + set(CPACK_GENERATOR TGZ) + + if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(CPACK_TARGET "") + set(CPACK_GENERATOR ${CPACK_GENERATOR} DEB RPM) + install( + FILES utils/udev_rules/60-hantek.rules + DESTINATION lib/udev/rules.d + ) + elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(CPACK_TARGET "freebsd_") + set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") + install( + FILES utils/devd_rules_freebsd/openhantek.conf + DESTINATION etc/devd ) - if (LSB_RELEASE_ID_SHORT MATCHES "Ubuntu") - set(CPACK_GENERATOR ${CPACK_GENERATOR} DEB) - else() - set(CPACK_GENERATOR ${CPACK_GENERATOR} RPM) - endif() - endif() + elseif(APPLE) + set(CPACK_TARGET "osx_") + endif() + + # install documentation + FILE(GLOB PDF "docs/*.pdf") + install( + FILES CHANGELOG LICENSE README ${PDF} + DESTINATION share/doc/openhantek + ) + # install application starter and icons + install( + FILES utils/applications/OpenHantek.desktop + DESTINATION share/applications + ) + install( + FILES openhantek/res/images/OpenHantek.png + DESTINATION share/icons/hicolor/48x48/apps + ) + install( + FILES openhantek/res/images/OpenHantek.svg + DESTINATION share/icons/hicolor/scalable/apps + ) + elseif(WIN32) - set(CPACK_GENERATOR NSIS) + set(CPACK_TARGET "win_") + set(CPACK_GENERATOR ${CPACK_GENERATOR} ZIP NSIS) + if ((MSVC AND CMAKE_GENERATOR MATCHES "Win64+") OR (CMAKE_SIZEOF_VOID_P EQUAL 8)) + set(CPACK_ARCH "amd64") + else() + set(CPACK_ARCH "x86") + endif() endif() -set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") +message(STATUS "Package: ${CPACK_GENERATOR}") +message(STATUS "Architecture: ${CPACK_ARCH}") + +set(CPACK_PACKAGE_NAME "openhantek") string(TOLOWER ${CPACK_PACKAGE_NAME} CPACK_PACKAGE_NAME) -set(CPACK_PACKAGE_VERSION "${DATE_VERSION}_${VCS_REVISION}") +set(CPACK_PACKAGE_VERSION "${DATE_VERSION}-${VCS_REVISION}") set(CPACK_PACKAGE_CONTACT "contact@openhantek.org") set(CPACK_PACKAGE_VENDOR "OpenHantek Community") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Digital oscilloscope software for Hantek USB hardware") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Digital oscilloscope software for Hantek DSO6022 USB hardware") set(CPACK_PACKAGE_DESCRIPTION "OpenHantek is an oscilloscope software for\nVoltcraft/Darkwire/Protek/Acetech/Hantek USB devices") set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/readme.md") -if (EXISTS "${CMAKE_SOURCE_DIR}/COPYING") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") +if (EXISTS "${CMAKE_SOURCE_DIR}/LICENSE") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") endif() -# Linux DEB+RPM -set(CPACK_DEBIAN_PACKAGE_SECTION "net") -set(CPACK_ARCH) -IF ((MSVC AND CMAKE_GENERATOR MATCHES "Win64+") OR (CMAKE_SIZEOF_VOID_P EQUAL 8)) - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") - set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") - set(CPACK_ARCH "x86_64") -else() - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i586") - set(CPACK_RPM_PACKAGE_ARCHITECTURE "i586") - set(CPACK_ARCH "x86") -endif() set(CPACK_STRIP_FILES 1) include(CMakeDetermineSystem) -# Linux RPM +# Linux DEB (tested on debian stretch and buster) +# Architecture for package and file name are automatically detected +set(CPACK_DEBIAN_PACKAGE_SECTION "electronics") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT") + +# Linux RPM (not tested on debian) +# Architecture for package and file name are automatically detected set(CPACK_RPM_PACKAGE_RELOCATABLE NO) set(CPACK_RPM_PACKAGE_LICENSE "GPLv2+") set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) -set(CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase-gui%{?_isa} >= 5.4, qt5-qttranslations%{?_isa}") set(CPACK_RPM_CHANGELOG_FILE "${CMAKE_BINARY_DIR}/changelog") - -set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") +set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-1.${CPACK_ARCH}") +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}-1_${CPACK_TARGET}${CPACK_ARCH}") set(CPACK_PACKAGE_INSTALL_DIRECTORY ".") SET(CPACK_OUTPUT_FILE_PREFIX packages) diff --git a/cmake/OpenHantekBundleInfo.plist.in b/cmake/OpenHantekBundleInfo.plist.in new file mode 100644 index 00000000..440b841d --- /dev/null +++ b/cmake/OpenHantekBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + + diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index b9fc74bc..00000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -openhantek.org diff --git a/docs/HANTEK6022_AC_Modification.odt b/docs/HANTEK6022_AC_Modification.odt new file mode 100644 index 00000000..4f4252bf Binary files /dev/null and b/docs/HANTEK6022_AC_Modification.odt differ diff --git a/docs/HANTEK6022_AC_Modification.pdf b/docs/HANTEK6022_AC_Modification.pdf new file mode 100644 index 00000000..c4830825 Binary files /dev/null and b/docs/HANTEK6022_AC_Modification.pdf differ diff --git a/docs/OpenHantek6022_User_Manual.odt b/docs/OpenHantek6022_User_Manual.odt new file mode 100644 index 00000000..c6178ba1 Binary files /dev/null and b/docs/OpenHantek6022_User_Manual.odt differ diff --git a/docs/OpenHantek6022_User_Manual.pdf b/docs/OpenHantek6022_User_Manual.pdf new file mode 100644 index 00000000..ea3901b3 Binary files /dev/null and b/docs/OpenHantek6022_User_Manual.pdf differ diff --git a/docs/OpenHantek6022_zadig_Win10.pdf b/docs/OpenHantek6022_zadig_Win10.pdf new file mode 100644 index 00000000..ca1540a2 Binary files /dev/null and b/docs/OpenHantek6022_zadig_Win10.pdf differ diff --git a/docs/_config.yml b/docs/_config.yml index f59c689e..3dd385df 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,8 +1,8 @@ -title: Openhantek -description: Software for Hantek and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes -google_analytics: +title: OpenHantek 6022 +description: Software for Hantek 6022 and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes +google_analytics: show_downloads: true -theme: jekyll-theme-slate +theme: jekyll-theme-leap-day gems: - jekyll-mentions @@ -12,4 +12,4 @@ exclude: - CNAME - readme.md -logo: images/openhantek.png +logo: images/OpenHantek6022.png diff --git a/docs/build.md b/docs/build.md index 2945a3ad..df46fa73 100644 --- a/docs/build.md +++ b/docs/build.md @@ -2,41 +2,158 @@ layout: default --- ### [Linux](#linux) -For debian (stretch and newer), Ubuntu 17.04+ and Mint 17+ and other deb based distributions install named requirements like this: -> apt install g++ cmake qttools5-dev qttools5-dev-tools libfftw3-dev binutils-dev libusb-1.0-0-dev libqt5opengl5-dev mesa-common-dev libgl1-mesa-dev libgles2-mesa-dev +For Debian (stretch and newer), Ubuntu 17.04+ and Mint 17+ and other deb based distributions install named requirements like this: +> apt install g++ make cmake fakeroot qttools5-dev libfftw3-dev binutils-dev libusb-1.0-0-dev libqt5opengl5-dev mesa-common-dev libgl1-mesa-dev libgles2-mesa-dev For distributions using dnf package manager (Fedora 21+) use this command: -> dnf install cmake gcc-c++ qt5-qtbase-gui qt5-qttools-devel qt5-qttranslations fftw-devel binutils-devel libusb-devel mesa-libGL-devel mesa-libGLES-devel +> dnf install make cmake fakeroot gcc-c++ qt5-qtbase-gui qt5-qttools-devel qt5-qttranslations fftw-devel binutils-devel libusb-devel mesa-libGL-devel mesa-libGLES-devel For OpenSUSE and related distributions use this command -zypper install cmake gcc-c++ qt5-qtbase-devel qt5-qttools-devel qt5-qttranslations binutils-devel libusb-devel Mesa-libGL-devel Mesa-libGLESv2-devel fftw3-devel +> zypper install make cmake fakeroot gcc-c++ libqt5-qtbase libqt5-qttools libqt5-qttranslations libusb-1_0 Mesa-libGL1 Mesa-libGLESv2 fftw3 After you've installed the requirements run the following commands inside the directory of this package: -> mkdir build
-> cd build
-> cmake ../
-> make -j2 -Optionally install the program: + mkdir build + cd build + cmake .. + make -j4 -> sudo make install +After success you can test the newly built program `openhantek/OpenHantek`. +Due to the included debug information this file is quite big (~20 MB), but the size can be reduced with `strip openhantek/OpenHantek` if you want to put it into a user directory. +If you do not install the program, you need to copy the file `utils/udev_rules/60-hantek.rules` to `/lib/udev/rules.d/` yourself, +and replug your device, otherwise you will not have the correct USB permissions to access the device. -If you do not install the program, you need to copy the file `firmware/60-hantek.rules` to `/lib/udev/rules.d/` yourself, -and replug your device, otherwise you will not have the correct permissions to access usb devices. +You can install the program with `sudo make install`, but it is highly recommended to create a debian package, +which allows a clear installation and removal of the package: -### [Apple MacOSX](#apple) -We recommend homebrew to install the required libraries. -> brew update
-> brew install libusb fftw qt5 cmake; + rm -f packages/* + fakeroot make package + sudo apt install packages/openhantek_*_amd64.deb + +If you detect that icons are not displayed correctly, please check if the Qt SVG library is installed on your system. +The Linux systems mentioned above include this lib when you install according to the provided lists. +However, an [alpine linux](https://alpinelinux.org/) user [reported](https://github.com/OpenHantek/OpenHantek6022/issues/42#issuecomment-564329632) that he had to install `qt5-qtsvg` separately. + +---- + +### [RaspberryPi](#raspberrypi) +The general Linux requirements from above also apply to the RPi; precompiled packages are available as [release](https://github.com/OpenHantek/OpenHantek6022/releases) assets. +Please note, it is important that the correct graphics driver is selected, +the OpenGL implementation of Qt requires the `Original non-GL desktop driver`, e.g. on my *RPi3B+*: + + sudo raspi-config + +![Screenshot_20200429_110134](https://user-images.githubusercontent.com/12542359/80594903-22788180-8a24-11ea-9859-eebd51542823.png) + +![Screenshot_20200429_110204](https://user-images.githubusercontent.com/12542359/80594920-2c9a8000-8a24-11ea-8629-9584cfaf367f.png) + +![Screenshot_20200429_110341](https://user-images.githubusercontent.com/12542359/80594963-3cb25f80-8a24-11ea-9d5a-8ca90e836581.png) + +Only the 1st setting `G1 Legacy Original non-GL desktop driver` worked for `OpenHantek6022`, the other two resulted in an error as below: + + QEGLPlatformContext: eglMakeCurrent failed: 3009 + QOpenGLFunctions created with non-current context + +Setting `Original non-GL desktop driver` was reported to work also on *RPi4B+*. + +---- + +### [FreeBSD](#freebsd) +Install the build requirements + + pkg install cmake qt5 fftw3 linux_libusb After you've installed the requirements run the following commands inside the directory of this package: -> mkdir build
-> cd build
-> cmake ../ -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.10.1
-> make -j2 -Please adjust the path to Qt5. You can find the path with the command: -> brew info qt5 + mkdir build + cd build + cmake .. + make -j4 + +After success you can test the newly built program `openhantek/OpenHantek`. +Due to the included debug information this file is quite big (~20 MB), but the size can be reduced with `strip openhantek/OpenHantek` if you want to put it into a user directory. + +In order for OpenHantek to work, make sure that your USB device has permissions for your user. +You can achieve this by copying [`utils/devd_rules_freebsd/openhantek.conf`](../utils/devd_rules_freebsd/openhantek.conf) +to `/usr/local/etc/devd/`, or create a file with similar content for your device: + + ... + + # Hantek DSO-6022BE + + notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b4"; + match "product" "0x6022"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; + }; + + notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b5"; + match "product" "0x6022"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; + }; + + ... + +- The "action" above doesn't use $device-name due to: +https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242111 + +Also please note that devices like this have two vendor/product id combinations, +before and after loading the firmware, hence two commands above. +The action changes the device permissions for supported scope devices: + +`rw------- root operator` becomes `rw-rw---- root openhantek` + +Make sure to be member of the group `openhantek`, e.g.: + + pw groupadd openhantek -g 6022 + pw groupmod openhantek -M + +---- + +### [MacOSX](#macosx) +We recommend homebrew to install the required libraries. + + brew update + brew install libusb fftw qt5 cmake + +If you want to build an OSX bundle make sure the option in `openhantek/CMakeLists.txt` is set accordingly: + + option(BUILD_MACOSX_BUNDLE "Build MacOS app bundle" ON) + +After you've installed the requirements run the following commands inside the top directory of this package: + + mkdir build + cd build + cmake .. + # + make -j4 + # + # now the target was created in subdir openhantek + # .. either as single binary OpenHantek, then you're done + # .. or as a bundle if enabled in ../../openhantek/CMakeLists.txt + # .. but this bundle is still a template as the dynlibs are not yet bundled + # .. this magic will happen now + # + cd openhantek + # + # deploy all necessary Qt dynlibs into the bundle + macdeployqt OpenHantek.app -always-overwrite -verbose=2 + # + # find all other dependencies, and their dependencies, and their... (you got it!) + python ../../macdeployqtfix/macdeployqtfix.py OpenHantek.app/Contents/MacOS/OpenHantek $(brew --prefix qt5) + # + # finally create OpenHantek.dmg from OpenHantek.app + macdeployqt OpenHantek.app -dmg -no-plugins -verbose=2 + # + +---- ### [Windows](#windows) @@ -51,11 +168,40 @@ Hints for Visual Studio 2015/2017 users: * Build for 64bit. 32bit builds theoretically work, but you are on your own then. * Use the **CMake GUI** to setup all required Qt include and library paths. -Microsoft Windows needs an installed driver for every usb device: +#### Microsoft Windows USB driver install (with Zadig) -* Make sure your original Hantek driver is uninstalled. -* Extract `cmake/winusb driver.zip` and customize the `libusb_device.inf` file for your device. The Vendor ID and Device ID as well as a unique GUID need to be entered like in the following example for a Hantek 6022BE. -* Physically plug (or replug) oscilloscope into PC's. From the Windows device manager update driver for your device and point to your modified libusb_device.inf. +The device specific USB driver shipped with the vendor software is not going to work in almost all cases. +You will need to install the WinUSB driver. + +For installing the WinUSB driver you can use the [Zadig](http://zadig.akeo.ie/) executable. +There are two versions, one for Windows XP (zadig_xp.exe), and another one for all other (Vista or higher) +supported Windows versions (zadig.exe). Both 32 and 64 bit Windows versions are supported. + +If you already installed the vendor driver previously, you need to run Zadig and switch to the WinUSB driver (see above). +There's no need to uninstall or deactivate the vendor driver manually, Zadig will handle all of this. + +Note: For Hantek 6022BE and 60222BL you have to assign the WinUSB driver via Zadig twice: +the first time for the initial USB VID/PID the device has when attaching it via USB, +and a second time after the firmware has been uploaded to the device and the device has "renumerated" +with a different VID/PID pair. + +See also the [Zadig wiki page](https://github.com/pbatard/libwdi/wiki/Zadig) for more information. + + - 1st install for the newly plugged scope without firmware (VID/PID 04B4/6022 for 6022BE or VID/PID 04B4/602A for 6022BL). + - 2nd time for the scope with firmware uploaded (VID/PID 04B5/6022 for 6022BE or VID/PID 04B5/602A for 6022BL). + +Some win user reports: + +* black2279's wiki entry +[USB Drivers Installation with Zadig for Hantek 6022 (Windows)](https://github.com/black2279/OpenHantek6022/wiki/USB-Drivers-Installation-with-Zadig-for-Hantek-6022-%28Windows%29) +* raxis13's [success report](https://www.eevblog.com/forum/testgear/hantek-6022be-20mhz-usb-dso/msg2563869/#msg2563869) + +---- + +#### This is the old and more complex procedure (no positive feedback known) + - Make sure your original Hantek driver is uninstalled. + - Extract `cmake/winusb driver.zip` and customize the `libusb_device.inf` file for your device. The Vendor ID and Device ID as well as a unique GUID need to be entered like in the following example for a Hantek 6022BE. + - Physically plug (or replug) oscilloscope into PC's. From the Windows device manager update driver for your device and point to your modified libusb_device.inf. ```` ; ===================================================== @@ -73,4 +219,3 @@ Date = "08/12/2017" ; ========== END USER CONFIGURABLE SECTION ============ ; ===================================================== ```` - diff --git a/docs/images/OpenHantek6022.png b/docs/images/OpenHantek6022.png new file mode 100644 index 00000000..dc68a0fb Binary files /dev/null and b/docs/images/OpenHantek6022.png differ diff --git a/docs/images/openhantek.png b/docs/images/openhantek.png deleted file mode 100644 index f492e4d9..00000000 Binary files a/docs/images/openhantek.png and /dev/null differ diff --git a/docs/images/screenshot_demo_mode.png b/docs/images/screenshot_demo_mode.png new file mode 100644 index 00000000..bb32a64d Binary files /dev/null and b/docs/images/screenshot_demo_mode.png differ diff --git a/docs/images/screenshot_mainwindow.png b/docs/images/screenshot_mainwindow.png index fe313cb6..830efc8c 100644 Binary files a/docs/images/screenshot_mainwindow.png and b/docs/images/screenshot_mainwindow.png differ diff --git a/docs/images/screenshot_mainwindow_dark.png b/docs/images/screenshot_mainwindow_dark.png new file mode 100644 index 00000000..29bf1a55 Binary files /dev/null and b/docs/images/screenshot_mainwindow_dark.png differ diff --git a/docs/images/screenshot_mainwindow_win.png b/docs/images/screenshot_mainwindow_win.png index 1cd5e483..a7b3d783 100644 Binary files a/docs/images/screenshot_mainwindow_win.png and b/docs/images/screenshot_mainwindow_win.png differ diff --git a/docs/images/screenshot_mainwindow_with_zoom.png b/docs/images/screenshot_mainwindow_with_zoom.png new file mode 100644 index 00000000..b3a1475b Binary files /dev/null and b/docs/images/screenshot_mainwindow_with_zoom.png differ diff --git a/docs/index.md b/docs/index.md index 8be4cb8b..266447dd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,21 @@ --- layout: default -overview: true +overview: false --- -# OpenHantek [![Build Status](https://travis-ci.org/OpenHantek/openhantek.svg?branch=master)](https://travis-ci.org/OpenHantek/openhantek) [![Build status](https://ci.appveyor.com/api/projects/status/9w4rd5r04ufqafr4/branch/master?svg=true)](https://ci.appveyor.com/project/davidgraeff/openhantek/branch/master) +# OpenHantek6022 -OpenHantek is a free software for Hantek and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes. +[![Build Status](https://travis-ci.org/OpenHantek/OpenHantek6022.svg)](https://travis-ci.org/OpenHantek/OpenHantek6022) +[![Build status](https://ci.appveyor.com/api/projects/status/github/OpenHantek/openhantek6022?svg=true)](https://ci.appveyor.com/project/Ho-Ro/openhantek6022) +[![CodeFactor](https://www.codefactor.io/repository/github/openhantek/openhantek6022/badge)](https://www.codefactor.io/repository/github/openhantek/openhantek6022) +[![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) -It has started as an alternative to the official Hantek DSO software for Linux users. Nowadays it works under Microsoft Windows and Apple Mac OS as well. +OpenHantek6022 is a free software for Hantek and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes. +It was initially developed by [David Gräff and others](https://github.com/OpenHantek/openhantek/graphs/contributors) on [github.com/OpenHantek/openhantek](https://github.com/OpenHantek/openhantek). After David [stopped maintaining](https://github.com/OpenHantek/openhantek/issues/277) the programm I cloned the repo to provide some updates - but only for Hantek 6022BE/BL on the linux platform. - - - -
Image of main window on linux Image of main window on Windows
- -* Supported operating systems: Linux, MacOSX, Windows¹ -* Supported devices: DSO2xxx Series, DSO52xx Series, 6022BE/BL +Image of main window on linux +* Actively supported devices: Hantek 6022BE/BL on Linux. +* DSO2xxx Series and DSO52xx Series support is still in but untested due to missing devices. +* Other operating systems builds: MacOSX, Windows - untested. +[Technical details and imitations](limitations.md) of Hantek 6022. diff --git a/docs/limitations.md b/docs/limitations.md index 22742cb8..81e112f9 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -1,9 +1,26 @@ --- layout: default --- -## Software triggered devices like the 6022BE +## Valid specs for the 6022BE/BL with [custom firmware by Ho-Ro](https://github.com/Ho-Ro/Hantek6022API). - - Support 48, 24, 16, 8, 4, 2, 1 M and 500, 200, 100 k Hz samplerates. For the 6022 with modded firmware by [jhoenicke](https://github.com/rpcope1/Hantek6022API) - - Can detect rising or falling edge of the signal. - - Note that the first few samples are dropped due to unstable/unusual reading. + - The device has no internal storage (except for a small fifo buffer), all data is streamed in real time as USB bulk transfer. + - Supports 48, 30, 24, 16, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1 MS/s and 500, 200, 100, 60 kS/s samplerates. + - Due to the USB bandwith constraints the max usable samplerate is limited: Max 30 MS/s for CH1 only and max 15 MS/s for CH1+CH2 and also CH2 only - otherwise data overrun occurs. + - Under Linux (even better if using a realtime kernel) the priority of the program can be raised when the user is member of a *realtime* group, e.g. audio: + + 1. set limits in /etc/security/limits.d: + @audio - rtprio 99 + 2. add user to the group, e.g. audio: + usermod -a -G audio + or set the limits only for your user in /etc/security/limits.d: + - rtprio 99 + + - The scope program works with blocks of 20000 samples - either 8 bit samples for CH1 only or 16 bit samples otherwise. + - Two analog input channels with 8-bit sample-width, max input voltage range is -5 V..+5 V, values outside this range are clipped (shown as minimum or maximum value). + - For low effective sampling rates < 1 MS/s a 10X..100X oversampling is used to increase the signal-to-noise ratio by 10..20 dB and to get a better voltage resolution (up to > 11 effective bits). + - The first samples are unstable (due to the use of the ADC far outside the common mode voltage specifications) -> take about 2000 additional values to settle the ADC and drop the first samples. + - No AC coupling available, all signals are measured as DC. (Use either a BNC DC-block or the math mode "CHx AC"). + - A [little HW modification](HANTEK6022_AC_Modification.pdf) adds AC coupling. OpenHantek6022 supports this feature since v2.17-rc5 / FW0204. + - No HW trigger available, SW trigger seaches for trigger condition and positions the trace window accordingly. + - Can detect rising or falling edge (also alternating from trace to trace) of the signal, displayed trigger position can be at 0..100% of screen width. diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 00000000..e13376bc --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,304 @@ +# OpenHantek developer documentation + +This document explains the basic data flow and concepts used and should be amended +whenever a new concept is added to the code base. +Detailed information about an implementation are to be found in the specific directory readmes though. + +## Directory structure + +The *res* folder contains mostly binary resource data that are bundled with the executable, +like icons, images, firmwares and translations. + +The *res/translations* folder contains translations in the Qt format. Use Qt linguist +as a handy graphical interface to translate OpenHantek. A [Translation HowTo](res/translations/Translation_HowTo.md) is available. + +The source code within *src* can be divided into a **core**, that is responsible for device communication, +data acquisition and post processing and the **graphical interface** with several custom widgets, +a configuration interface and an OpenGL renderer. + +### Core structure + +The raw device communcation takes place in the *src/usb* directory, especially via the `USBDevice` class. +To find suitable devices, the `FindDevices` class in the same folder is used. Firmware upload is realized +via the `ezusb` helper methods and the `UploadFirmware` class. + +The hantek protocol structures and constants are defined within `src/hantekprotocol`. + +The heart of OpenHantek is the `src/hantekdso` folder and its `hantekdsocontrol` class. All supported models, +based on the `DsoModel` class, are implemented within a subfolder `src/hantekdso/models` and automatically register +themself to a `ModelRegistry` class. A model is based on (contains) a specification, the `ControlSpecification` class. + +The `hantekdsocontrol` class keeps track of the devices current state (samplerate, selected gain, activated channels, +etc) via the `ControlSettings` class and field. +It outputs the channel separated unprocessed samples via a `samplesAvailable(DSOsamples*)` signal. + +Before the data is presented to the GUI it arrives in the `src/post/postprocessing` class. Several post +processing classes are to be found in this directory as well. + +### Graphical interface structure + +The initial dialog for device selection is realized in *src/selectdevice* where several models +and dialogs are implemented. This is basically a graphical wrapper around the `src/usb/finddevices` class. + +You will find the configuration dialog pages to be implemented in *src/configdialog*. + +Custom widgets like a LevelSlider with a unit suffix reside in *src/widgets*, the custom main window docks are +in *src/docks*. + +The code that is responsible for exporting data to images or to the printer is stored in *src/exporting*. + +The main window itself doesn't do and shouldn't do much more than connecting signals/slots between the core part +and the graphical part. + +All OpenGL rendering takes place in the `GlScope` class. A helper class `GlScopeGraph` contains exactly one +data sample snapshot including all channels for voltage and spectrum and a pointer to the respective GPU buffer. +`GlScope` works normally for **OpenGL 3.2+** and OpenGL ES 2.0+ but if it detects **OpenGL 2.1+** and OpenGL ES 1.2+ on older platforms it switches to a legacy implementation. If both OpenGL and OpenGL Es are present, OpenGL will be prefered, but can be overwritten by the user via a command flag. + +### Export + +All export related funtionality is within *src/exporting*. + +The following exporters are implemented: + +* Export to comma separated value file (CSV): Write to a user selected file, +* Export to an image/pdf: Writes an image/pdf to a user selected file, +* Print exporter: Creates a printable document and opens the print dialog. + +All export classes (exportcsv, exportimage, exportprint) implement the +ExporterInterface and are registered to the ExporterRegistry in the main.cpp. + +Some export classes are still using the legacyExportDrawer class to +draw the grid and paint all the labels, values and graphs. + +The plan is to retire this legacy class and replace the paint code with +a `GlScope` class shared OpenGL drawing code for at least the grid and the +scope graphs. + +## Persistent settings + +Persisent settings are stored in the file `~/.config/OpenHantek/OpenHantek6022.conf`. +Settings are written at program shutdown or with menu entry `File/Save settings` (`Ctrl-S`). +The save/load happens in `src/dsosettings.cpp` in the functions `DsoSettings::save()` and `DsoSettings::load()`. +After loading of the settings the values will be applied to the internal status as well as to the GUI. + +An example of making the calibration output frequency (parameter `calfreq`) persistent can be seen in commit [26c1a49](https://github.com/OpenHantek/OpenHantek6022/commit/26c1a49146818aab7419b319705878a5f072460f) + +`openhantek/src/dsosettings.cpp` + + void DsoSettings::save() { + ... + store->setValue("calfreq", scope.horizontal.calfreq); + ... + } + + void DsoSettings::load() { + ... + if (store->contains("calfreq")) scope.horizontal.calfreq = store->value("calfreq").toDouble(); + ... + } + +`openhantek/src/hantekdso/hantekdsocontrol.cpp` + + void HantekDsoControl::applySettings(DsoSettingsScope *scope) { + ... + setCalFreq(scope->horizontal.calfreq); + ... + } + +## Scope interface + +The scope receives control commands via endpoint 0 and provides the sampled data via bulk transfer on endpoint 6. +The firmware command parser: + + BOOL handle_vendorcommand(BYTE cmd) + { + stop_sampling(); + + /* Set red LED, toggle after timeout. */ + LED_RED(); + ledcounter = ledinit; + + /* Clear EP0BCH/L for each valid command. */ + if ( cmd >= 0xe0 && cmd <= 0xe6 ) { + EP0BCH = 0; + EP0BCL = 0; + while ( EP0CS & bmEPBUSY ) + ; + } + + switch (cmd) { + case 0xa2: + return eeprom(); + + case 0xe0: + return set_voltage( 0, EP0BUF[0] ); + + case 0xe1: + return set_voltage( 1, EP0BUF[0] ); + + case 0xe2: + return set_samplerate( EP0BUF[0] ); + + case 0xe3: + if ( EP0BUF[0] == 1 ) { + /* Set green LED while sampling. */ + LED_GREEN(); + ledcounter = 0; + start_sampling(); + } + return TRUE; + + case 0xe4: + return set_numchannels( EP0BUF[0] ); + + case 0xe5: + SET_COUPLING( EP0BUF[0] ); + return TRUE; + + case 0xe6: + return set_calibration_pulse( EP0BUF[0] ); + + } + + return FALSE; /* Not handled by handlers. */ + } + + +## Data flow +* The procedure `void HantekDsoControl::stateMachine()` controls the raw data capturing, conversion to real world physical values, trigger detection and timing of screen refresh. The `struct Raw` holds all important values of one sampled data block: + + + struct Raw { + unsigned channels = 0; + double samplerate = 0; + unsigned oversampling = 0; + unsigned gainValue[ 2 ] = {1, 1}; // 1,2,5,10,.. + unsigned gainIndex[ 2 ] = {7, 7}; // index 0..7 + unsigned tag = 0; + bool freeRun = false; // small buffer, no trigger + bool valid = false; // samples can be processed + bool rollMode = false; // one complete buffer received, start to roll + unsigned size = 0; + unsigned received = 0; + std::vector< unsigned char > data; + mutable QReadWriteLock lock; + }; + + +* Raw 8-bit ADC values are collected permanently via call to `HantekDsoControl::getSamples(..)` (or `...getDemoSamples(..)` for the demo device) in an own thread `Capturing::Capturing()`. +At fast sample rates (>= 10 kS/s) one big block is requested via USB command to make the transfer more robust against USB interruptions by other traffic, +while at slow sample rates it requests the data in small chunks to allow a permanent screen update in roll mode. +* Raw values are converted in `HantekDsoControl::convertRawDataToSamples()` to real-world double samples (scaled with voltage and sample rate). +The 2X..200X oversampling for slower sample rates is done here. Also overdriving of the inputs is detected. +In `Roll` mode the latest sample values are always put at the end of the result buffer while older samples move toward the beginning of the buffer, +this rolls the displayed trace permanently to the left. +The conversion uses either the factory calibration values from EEPROM or from a user supplied config file. +Read more about [calibration](https://github.com/Ho-Ro/Hantek6022API/blob/master/README.md#create-calibration-values-for-openhantek). +* `searchTriggerPosition()` + * Checks if the signal is triggered and calculates the starting point for a stable display. + The time distance to the following opposite slope is measured and displayed as pulse width in the top row. +* `provideTriggeredData()` handles the trigger mode: + * If the **trigger condition is false** and the **trigger mode is Normal** or the display is paused +then we reuse the last triggered samples so that voltage and spectrum traces +as well as the measurement at the scope's bottom lines are frozen until the trigger condition +becomes true again. The reused samples are emitted at lower speed (every 20 ms) to reduce CPU load but be responsive to user actions. + * If the **trigger condition is false** and the **trigger mode is not Normal** then we display a free running trace and discard the last saved samples. +* The converted `DSOsamples` are emitted to PostProcessing::input() via signal/slot: + +`QObject::connect( &dsoControl, &HantekDsoControl::samplesAvailable, &postProcessing, &PostProcessing::input );` + + + struct DSOsamples { + std::vector< std::vector< double > > data; ///< Pointer to input data from device + double samplerate = 0.0; ///< The samplerate of the input data + unsigned char clipped = 0; ///< Bitmask of clipped channels + bool liveTrigger = false; ///< live samples are triggered + unsigned triggeredPosition = 0; ///< position for a triggered trace, 0 = not triggered + double pulseWidth1 = 0.0; ///< width from trigger point to next opposite slope + double pulseWidth2 = 0.0; ///< width from next opposite slope to third slope + bool freeRunning = false; ///< trigger: NONE, half sample count + unsigned tag = 0; ///< track individual sample blocks (debug support) + mutable QReadWriteLock lock; + }; + + +* PostProzessing calls all processors that were registered in `main.cpp`. + * `MathchannelGenerator::process()` + * which creates a third MATH channel as one of these data sample combinations: + `CH1 + CH2`, `CH1 - CH2`, `CH2 - CH1`, `CH1 * CH2`, `CH1 AC` or `CH2 AC`. + * `SpectrumGenerator::process()` + * For each active channel: + * Calculate the peak-to-peak, DC (average), AC (rms) and effective value ( sqrt( DC² + AC² ) ). + * Apply a user selected window function and scale the result accordingly. + * Calculate the spectrum of the AC part of the signal scaled as dBV. fft: f(t) ⊶ F(ω) + * Calculate the autocorrelation to get the frequency of the signal: + * Calculate power spectrum |F(ω)|² and do an ifft: F(ω) ∙ F(ω) ⊷ f(t) ⊗ f(t) (convolution of f(t) with f(t)) + * This is quite inaccurate at high frequencies. In these cases the first peak value of the spectrum is used. + * `GraphGenerator::process()` + * which works either in TY mode and creates two types of traces: + * voltage over time `GraphGenerator::generateGraphsTYvoltage()` + * spectrum over frequency `GraphGenerator::generateGraphsTYspectrum()` + * or in XY mode and creates a voltage over voltage trace `GraphGenerator::generateGraphsXY()`. + * `GraphGenerator::generateGraphsTYvoltage()` creates up to three (CH1, CH2, MATH) voltage traces. + * `GraphGenerator::generateGraphsTYspectrum()` creates up to three (SP1, SP2, SPM) spectral traces. + * Finally `PostProcessing` emits the signal `processingFinished()` that is connected to: + * `ExporterRegistry::input()` that takes care of exporting to CSV data. + * `MainWindow::showNewData()`. + * `MainWindow::showNewData()` calls `DsoWidget::showNew()` that calls `GlScope::showData()` that calls `Graph::writeData()`. + +t.b.c. + +## Data structures and configuration + +* Two main structures/classes hold most of the current status of the oscilloscope: + +* The struct `ControlSettings` and its substructures (-> `hantekdso/controlsettings.h`) are the main storage for all scope (HW) parameters that are handled by the class `HantekDsoControl`. + + + struct ControlSettings { + ControlSettings( const ControlSamplerateLimits *limits, size_t channelCount ); + ~ControlSettings(); + ControlSettings( const ControlSettings & ) = delete; + ControlSettings operator=( const ControlSettings & ) = delete; + ControlSettingsSamplerate samplerate; ///< The samplerate settings + std::vector< ControlSettingsVoltage > voltage; ///< The amplification settings + ControlSettingsTrigger trigger; ///< The trigger settings + RecordLengthID recordLengthId = 1; ///< The id in the record length array + unsigned channelCount = 0; ///< Number of activated channels + Hantek::CalibrationValues *calibrationValues; ///< Calibration data for the channel offsets & gains + Hantek::ControlGetLimits cmdGetLimits; + }; + +* The class `DsoSettings` (-> `dsosettings.h`) and its substructures `DsoSettingsScope` (`scopesettings.h`) and `DsoSettingsView` (`viewsettings.h`) +are the main storage for all persistent scope (program) parameters, see `DsoSettings::save()` and `DsoSettings::load()`. + + + class DsoSettings { + Q_DECLARE_TR_FUNCTIONS( DsoSettings ) + public: + explicit DsoSettings( const Dso::ControlSpecification *deviceSpecification ); + bool setFilename( const QString &filename ); + + DsoSettingsExport exporting; ///< General options of the program + DsoSettingsScope scope; ///< All oscilloscope related settings + DsoSettingsView view; ///< All view related settings + DsoSettingsPostProcessing post; ///< All post processing related settings + bool alwaysSave = true; ///< Always save the settings on exit + + QByteArray mainWindowGeometry; ///< Geometry of the main window + QByteArray mainWindowState; ///< State of docking windows and toolbars + + /// \brief Read the settings from the last session or another file. + void load(); + + /// \brief Save the settings to the harddisk. + void save(); + + private: + std::unique_ptr< QSettings > store = std::unique_ptr< QSettings >( new QSettings ); + const Dso::ControlSpecification *deviceSpecification; + }; + +GUI input either in the docks of by moving sliders changes the `DsoSettings` parameters directly +and the `ControlSettings` parameters (that live in another thread `dsoControlThread` ) via signal/slot mechanism. +The (big) class `HantekDsoControl` has a member `const DsoSettingsScope *scope` that gives direct read acces to the (persistent) scope settings. diff --git a/firmware/60-hantek.rules b/firmware/60-hantek.rules deleted file mode 100644 index 95b374f3..00000000 --- a/firmware/60-hantek.rules +++ /dev/null @@ -1,26 +0,0 @@ -# Hantek DSO-2090 -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", TAG+="uaccess", TAG+="udev-acl" - -# Hantek DSO-2150 -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", TAG+="uaccess", TAG+="udev-acl" - -# Hantek DSO-2250 -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", TAG+="uaccess", TAG+="udev-acl" - -# Hantek DSO-5200 -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", TAG+="uaccess", TAG+="udev-acl" - -# Hantek DSO-5200A -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", TAG+="uaccess", TAG+="udev-acl" - -# Hantek DSO-6022BE/BL -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="6022", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="602a", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", TAG+="uaccess", TAG+="udev-acl" -SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", TAG+="uaccess", TAG+="udev-acl" diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt deleted file mode 100644 index ff1baf0c..00000000 --- a/firmware/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -project(FirmwareExtractor C) - -find_library( BFD_LIBRARY NAMES bfd libbdf PATH /usr/lib /usr/lib64 ) -find_path(BFD_INCLUDE bfd.h PATHS /usr/include - /usr/local/include - /opt/local/include - /sw/include) - - -if (NOT BFD_LIBRARY OR NOT BFD_INCLUDE) - message(STATUS "BFD not found. Please install binutils-devel (fedora) / binutils-dev (ubuntu)") - return() -endif() - -add_executable(${PROJECT_NAME} extractfw.c) -target_link_libraries(${PROJECT_NAME} ${BFD_LIBRARY} ) -target_include_directories(${PROJECT_NAME} PRIVATE ${BFD_INCLUDE}) diff --git a/firmware/README.md b/firmware/README.md deleted file mode 100644 index 86775da7..00000000 --- a/firmware/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Use the `fwget.sh` script to extract a firmware from a windows driver. -The copyright belongs to Hantek. diff --git a/firmware/extractfw.c b/firmware/extractfw.c deleted file mode 100644 index 4e19c026..00000000 --- a/firmware/extractfw.c +++ /dev/null @@ -1,258 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// OpenHantek -// dsoextractfw.c -// Copyright (C) 2008 Oleg Khudyakov -// prcoder@potrebitel.ru -// Copyright (C) 2010, 2011 Oliver Haag -// oliver.haag@gmail.com -// -// 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 -#include -#include -#include - -#define PACKAGE_VERSION 1 -#define PACKAGE 1 -#include - - -int extractFirmware(const char *filenameDriver, const char *filenameFirmware, const char *filenameLoader); -int writeIntelHex(const char *filename, unsigned char *data, unsigned int length); - -static const char *filenameEndFirmware = "-firmware.hex"; -static const char *filenameEndLoader = "-loader.hex"; -static const char *filenameEndDriver = "1.sys"; -static const char *nameTarget = "pei-i386"; -static const char *nameSection = ".data"; -static const char *nameSymbolFirmware = "_firmware"; -static const char *nameSymbolLoader = "_loader"; - - -/// \brief Parse commandline arguments. -/// \return 0 on success, negative on error. -int main(int argc, char **argv) { - char *filenameDriver, *filenameFirmware, *filenameLoader; - char *charPointer; - int prefixLength; - - if(argc < 2) { - fprintf(stderr, "Usage: %s [] []\n", argv[0]); - return -1; - } - filenameDriver = argv[1]; - - prefixLength = strlen(filenameDriver) - strlen(filenameEndDriver); - - if(argc < 3) { - // Guess correct filename for firmware - filenameFirmware = (char*)malloc(prefixLength + strlen(filenameEndFirmware) + 1); - memcpy(filenameFirmware, filenameDriver, prefixLength); - strcpy(filenameFirmware + prefixLength, filenameEndFirmware); - - // Convert filename to lowercase - charPointer = strrchr(filenameFirmware, '/'); - if(charPointer == NULL) - charPointer = filenameFirmware; - for(; *charPointer != 0; charPointer++) - *charPointer = tolower(*charPointer); - } - else { - filenameFirmware = argv[2]; - } - - if(argc < 4) { - // Guess correct filename for loader - filenameLoader = (char*)malloc(prefixLength + strlen(filenameEndLoader) + 1); - memcpy(filenameLoader, filenameDriver, prefixLength); - strcpy(filenameLoader + prefixLength, filenameEndLoader); - - // Convert filename to lowercase - charPointer = strrchr(filenameLoader, '/'); - if(charPointer == NULL) - charPointer = filenameLoader; - for(; *charPointer != 0; charPointer++) - *charPointer = tolower(*charPointer); - } - else { - filenameFirmware = argv[3]; - } - - return extractFirmware(filenameDriver, filenameFirmware, filenameLoader); -} - -/// \brief Extract firmware and loader data from original driver file. -/// \param filenameDriver Name of the original driver file. -/// \param filenameFirmware Name of the file where the firmware should be saved. -/// \param filenameDriver Name of the file where the loader should be saved. -/// \return 0 on success, negative on error. -int extractFirmware(const char *filenameDriver, const char *filenameFirmware, const char *filenameLoader) { - bfd *bfdDriver; - asection *sectionData; - - asymbol **symbols; - unsigned int symbolCount; - unsigned currentSymbol; - const char *symbolName; - - bfd_size_type offsetFirmware = 0, offsetLoader = 0; - bfd_size_type lengthFirmware = 0, lengthLoader = 0; - unsigned char *bufferFirmware, *bufferLoader; - - // Initialize bfd and open driver file - bfd_init(); - bfdDriver = bfd_openr(filenameDriver, nameTarget); - if(!bfdDriver) { - bfd_perror("Error opening file"); - return -1; - } - - if(!bfd_check_format(bfdDriver, bfd_object)) { - bfd_perror("bfd_check_format"); - bfd_close(bfdDriver); - return -2; - } - - // Search for the data section - for(sectionData = bfdDriver->sections; sectionData != NULL; sectionData = sectionData->next) - if(strcmp(sectionData->name, nameSection) == 0) - break; - if(sectionData == NULL) { - fprintf(stderr, "Section %s not found\n", nameSection); - return -3; - } - printf("Section %s found (starting at 0x%04lx, %li bytes)\n", nameSection, (unsigned long int) sectionData->filepos, (long int) sectionData->size); - - // Search for the symbols we want - symbols = (asymbol**)malloc(bfd_get_symtab_upper_bound(bfdDriver)); - symbolCount = bfd_canonicalize_symtab(bfdDriver, symbols); - for(currentSymbol = 0; currentSymbol < symbolCount; currentSymbol++) { - symbolName = bfd_asymbol_name(symbols[currentSymbol]); - - if(strcmp(symbolName, nameSymbolFirmware) == 0) - offsetFirmware = symbols[currentSymbol]->value; - if(strcmp(symbolName, nameSymbolLoader) == 0) - offsetLoader = symbols[currentSymbol]->value; - } - free(symbols); - - // Calculate position in section and length - offsetFirmware -= sectionData->filepos; - offsetLoader -= sectionData->filepos; - lengthFirmware = offsetLoader - offsetFirmware; - lengthLoader = sectionData->size - lengthFirmware; - - printf("Symbol %s found (offset 0x%04lx, %li bytes)\n", nameSymbolFirmware, (unsigned long int) offsetFirmware, (long int) lengthFirmware); - printf("Symbol %s found (offset 0x%04lx, %li bytes)\n", nameSymbolLoader, (unsigned long int) offsetLoader, (long int) lengthLoader); - - // Extract data - bufferFirmware = (unsigned char*)malloc(lengthFirmware); - bufferLoader = (unsigned char*)malloc(lengthLoader); - if (bufferFirmware == NULL || bufferLoader == NULL) { - fprintf(stderr, "Can't allocate memory\n"); - bfd_close(bfdDriver); - return -4; - } - - if(!bfd_get_section_contents(bfdDriver, sectionData, bufferFirmware, offsetFirmware, lengthFirmware)) { - bfd_perror("Can't get firmware contents"); - bfd_close(bfdDriver); - return -5; - } - - if(!bfd_get_section_contents(bfdDriver, sectionData, bufferLoader, offsetLoader, lengthLoader)) { - bfd_perror("Can't get loader contents"); - bfd_close(bfdDriver); - return -6; - } - - printf("Saving firmware as %s\n", filenameFirmware); - writeIntelHex(filenameFirmware, bufferFirmware, lengthFirmware); - free(bufferFirmware); - - printf("Saving loader as %s\n", filenameLoader); - writeIntelHex(filenameLoader, bufferLoader, lengthLoader); - free(bufferLoader); - - bfd_close(bfdDriver); - - return 0; -} - -/// \brief Save data to a file in intel hex format. -/// \param filename Name of the output file. -/// \param data Pointer to the binary data that should be stored. -/// \param length Size of the data that should be stored. -/// \return 0 on success, negative on error. -int writeIntelHex(const char *filename, unsigned char *data, unsigned int length) -{ - FILE *file; - unsigned char crc, eof; - unsigned int dataIndex, byteIndex, byteCount; - unsigned char *dataPointer; - - file = fopen(filename, "wt"); - if(!file) { - fprintf(stderr, "Can't open %s for writing\n", filename); - fclose(file); - return -1; - } - - for(dataIndex = 0; dataIndex < length; dataIndex += 22) { - eof = -1; // Always check for End of File Record - dataPointer = data + dataIndex; - - // Start code and byte count - byteCount = *dataPointer; - fprintf(file, ":%02X", byteCount); - if(byteCount != 0) - eof = 0; - crc = -*dataPointer++; - dataPointer++; - - // Address - fprintf(file, "%04X", *(unsigned short *) dataPointer); - if(*(unsigned short *) dataPointer != 0) - eof = 0; - crc -= *dataPointer++; - crc -= *dataPointer++; - - // Record type - fprintf(file, "%02X", *dataPointer); - if(*dataPointer != 0x01) - eof = 0; - crc -= *dataPointer++; - - // Data - for(byteIndex = 0; byteIndex < byteCount; byteIndex++) { - fprintf(file, "%02X", *dataPointer); - crc -= *dataPointer++; - } - - // CRC - fprintf(file, "%02X\n", crc); - - if(eof) - break; - } - - fclose(file); - - return 0; -} diff --git a/firmware/fwget.sh b/firmware/fwget.sh deleted file mode 100755 index 9c692c41..00000000 --- a/firmware/fwget.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -shopt -s globstar - -rm -rf build -mkdir build && cd build && cmake ../ && make && cd .. - -if [ ! -d ./hex ]; then -mkdir hex && cd hex && \ -for MODEL in "2090" "2150" "2250" "5200" "5200A"; do - wget http://www.hantek.com/Product/DSO2000/DSO${MODEL}_Driver.zip - unzip DSO${MODEL}_Driver.zip -done && \ -cd .. -fi - -for f in hex/**/*.sys; do - ./build/FirmwareExtractor $f && rm $f -done diff --git a/openhantek/.clang-format b/openhantek/.clang-format index 2e9d003d..a93b003c 100644 --- a/openhantek/.clang-format +++ b/openhantek/.clang-format @@ -5,22 +5,22 @@ AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: false +AlignEscapedNewlinesLeft: true AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: true +AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false @@ -38,7 +38,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 120 +ColumnLimit: 132 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 @@ -64,7 +64,7 @@ JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 +MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false @@ -83,13 +83,12 @@ SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: false +SpacesInAngles: true SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false +SpacesInParentheses: true +SpacesInSquareBrackets: true Standard: Cpp11 TabWidth: 8 UseTab: Never ... - diff --git a/openhantek/CMakeLists.txt b/openhantek/CMakeLists.txt index 04fa8132..7fce2987 100644 --- a/openhantek/CMakeLists.txt +++ b/openhantek/CMakeLists.txt @@ -1,3 +1,5 @@ +# openhantek/CMakeLists.txt + project(OpenHantek CXX) find_package(Qt5Widgets REQUIRED) @@ -22,26 +24,56 @@ file(GLOB_RECURSE HEADERS "src/*.h") file(GLOB_RECURSE UI "src/*.ui") file(GLOB_RECURSE QRC "res/*.qrc") +if(WIN32) + file(GLOB_RECURSE RC "res/*.rc") +endif() + add_custom_target(format SOURCES ".clang-format" COMMAND "clang-format" "-style=file" "-i" "-sort-includes" ${SRC} ${HEADERS}) -add_subdirectory(translations) +# here the translation magic happens +include(translations/Translations.cmake) add_definitions(-DVERSION="${CPACK_PACKAGE_VERSION}") +message( STATUS "VERSION: ${CPACK_PACKAGE_VERSION}" ) set(EXECTYPE "") if(WIN32 AND MINGW) set(EXECTYPE WIN32) endif() +# Enable MacOSX bundle magic in the next line +option(BUILD_MACOSX_BUNDLE "Build MacOS app bundle" ON) +# +if( APPLE AND BUILD_MACOSX_BUNDLE ) + set( EXECTYPE MACOSX_BUNDLE ) + set( MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}" ) + set( MACOSX_BUNDLE_INFO_STRING "Digital oscilloscope software for Hantek DSO6022" ) + set( MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_NAME}" ) + set( MACOSX_BUNDLE_SHORT_VERSION_STRING ${VCS_REVISION} ) + set( MACOSX_BUNDLE_BUNDLE_VERSION ${CPACK_PACKAGE_VERSION} ) + string(TIMESTAMP THISYEAR "%Y") + set( MACOSX_BUNDLE_COPYRIGHT "(C) 2012-${THISYEAR} OpenHantek community" ) + set( MACOSX_BUNDLE_ICON_FILE openhantek.icns ) + set( ICONS ${CMAKE_CURRENT_SOURCE_DIR}/res/images/openhantek.icns ) + set_source_files_properties( ${ICONS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources" ) +endif() + # make executable -add_executable(${PROJECT_NAME} ${EXECTYPE} ${SRC} ${HEADERS} ${UI} ${QRC} ${TRANSLATION_BIN_FILES} ${TRANSLATION_QRC}) +add_executable(${PROJECT_NAME} ${EXECTYPE} ${SRC} ${HEADERS} ${UI} +${QRC} ${RC} ${TRANSLATION_BIN_FILES} ${TRANSLATION_QRC} ${ICONS}) target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::PrintSupport Qt5::OpenGL ${OPENGL_LIBRARIES} ) target_compile_features(${PROJECT_NAME} PRIVATE cxx_range_for) if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE "/W4" "/wd4251" "/wd4127" "/wd4275" "/wd4200" "/nologo" "/J" "/Zi") target_compile_options(${PROJECT_NAME} PRIVATE "$<$:/MDd>") else() + if( APPLE AND BUILD_MACOSX_BUNDLE ) + # Use own template that defines NSPrincipalClass=NSApplication & NSHighResolutionCapable=True + set_target_properties( ${PROJECT_NAME} PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/../cmake/OpenHantekBundleInfo.plist.in + ) + endif() target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wno-long-long -pedantic) target_compile_options(${PROJECT_NAME} PRIVATE "$<$:-DDEBUG>") target_compile_options(${PROJECT_NAME} PRIVATE "$<$:-O0>") @@ -65,6 +97,7 @@ if(NOT WIN32) endif() # install commands -install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "bin") - +if ( NOT (APPLE AND BUILD_MACOSX_BUNDLE) ) + install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "bin") +endif() include(../cmake/copy_qt5_dlls_to_bin_dir.cmake) diff --git a/openhantek/readme.md b/openhantek/readme.md deleted file mode 100644 index 5ac9b656..00000000 --- a/openhantek/readme.md +++ /dev/null @@ -1,80 +0,0 @@ -# OpenHantek developer documentation - -This document explains the basic data flow and concepts used and should be amended -whenever a new concept is added to the code base. -Detailed information about an implementation are to be found in the specific directory readmes though. - -## Directory structure - -The *res* folder contains mostly binary resource data that are bundled with the executable, -like icons, images and firmwares. - -The *translations* folder contains translations in the Qt format. Use Qt linguist -as a handy graphical interface to translate OpenHantek. - -The source code within *src* can be divided into a **core**, that is responsible for device communication, -data acquisition and post processing and the **graphical interface** with several custom widgets, -a configuration interface and an OpenGL renderer. - -### Core structure - -The raw device communcation takes place in the *src/usb* directory, especially via the `USBDevice` class. -To find suitable devices, the `FindDevices` class in the same folder is used. Firmware upload is realized -via the `ezusb` helper methods and the `UploadFirmware` class. - -The hantek protocol structures and constants are defined within `src/hantekprotocol`. - -The heart of OpenHantek is the `src/hantekdso` folder and its `hantekdsocontrol` class. All supported -models, based on the `DsoModel` class, are implemented within a subfolder `src/hantekdso/models` and automatically register themself -to a `ModelRegistry` class. A model is based on (contains) a specification, the `ControlSpecification` class. - -The `hantekdsocontrol` class keeps track of the devices current state (samplerate, selected gain, activated channels, etc) -via the `ControlSettings` class and field. -It outputs the channel separated unprocessed samples via a `samplesAvailable(DSOsamples*)` signal. - -Before the data is presented to the GUI it arrives in the `src/post/postprocessing` class. Several post -processing classes are to be found in this directory as well. - -### Graphical interface structure - -The initial dialog for device selection is realized in *src/selectdevice* where several models -and dialogs are implemented. This is basically a graphical wrapper around the `src/usb/finddevices` class. - -You will find the configuration dialog pages to be implemented in *src/configdialog*. - -Custom widgets like a LevelSlider with a unit suffix reside in *src/widgets*, the custom main window docks are -in *src/docks*. - -The code that is responsible for exporting data to images or to the printer is stored in *src/exporting*. - -The main window itself doesn't do and shouldn't do much more than connecting signals/slots between the core part -and the graphical part. - -All OpenGL rendering takes place in the `GlScope` class. A helper class `GlScopeGraph` contains exactly one -data sample snapshot including all channels for voltage and spectrum and a pointer to the respective GPU buffer. -`GlScope` works for OpenGL 3.2 and OpenGL ES 2.0. If both is present, OpenGL will be prefered, but can be -overwritten by the user via a command flag. - -### Export - -All export related funtionality is within *src/exporting*. - -The following exporters are implemented: - -* Export to comma separated value file (CSV): Write to a user selected file, -* Export to an image/pdf: Writes an image/pdf to a user selected file, -* Print exporter: Creates a printable document and opens the print dialog. - -All export classes (exportcsv, exportimage, exportprint) implement the -ExporterInterface and are registered to the ExporterRegistry in the main.cpp. - -Some export classes are still using the legacyExportDrawer class to -draw the grid and paint all the labels, values and graphs. - -The plan is to retire this legacy class and replace the paint code with -a `GlScope` class shared OpenGL drawing code for at least the grid and the -scope graphs. - -## Data flow - -To be written diff --git a/openhantek/res/OpenHantek.rc b/openhantek/res/OpenHantek.rc new file mode 100644 index 00000000..563f8e40 --- /dev/null +++ b/openhantek/res/OpenHantek.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/OpenHantek.ico" diff --git a/openhantek/res/application.qrc b/openhantek/res/application.qrc index 22f9432e..dc7d3638 100644 --- a/openhantek/res/application.qrc +++ b/openhantek/res/application.qrc @@ -1,10 +1,22 @@ - images/openhantek.png - images/switch.png + images/switch_6022BL.png images/fontawesome-4.7.0.ttf - images/openhantek.svg - images/digitalphosphor.svg + images/OpenHantek.svg + images/phosphor.svg + images/histogram.svg + images/measure.svg + images/pause.svg + images/play.svg + images/refresh.svg + images/zoom.svg + images/darktheme/phosphor.svg + images/darktheme/histogram.svg + images/darktheme/measure.svg + images/darktheme/pause.svg + images/darktheme/play.svg + images/darktheme/refresh.svg + images/darktheme/zoom.svg diff --git a/openhantek/res/configdialog.qrc b/openhantek/res/configdialog.qrc index 72ad7f3e..0d451255 100644 --- a/openhantek/res/configdialog.qrc +++ b/openhantek/res/configdialog.qrc @@ -1,8 +1,7 @@ - images/config/analysis.png - images/config/colors.png + images/config/spectrum.png images/config/scope.png - images/config/files.png + images/config/colors.png diff --git a/openhantek/res/firmware/dds120-firmware.hex b/openhantek/res/firmware/dds120-firmware.hex new file mode 100644 index 00000000..94862a31 --- /dev/null +++ b/openhantek/res/firmware/dds120-firmware.hex @@ -0,0 +1,220 @@ +:203D000012010002FFFFFF40B50420010001010203010A0600020000004001000902890088 +:203D200001010080370904000001FF000000070586020002000904000101FF00010007050C +:203D400082010014010904000201FF00010007058201000C010904000301FF000100070502 +:203D600082010004010904000401FF000100070582010004020904000501FF0001000705F5 +:203D800082010004030904000601FF000100070582010004040904000701FF0001000705CD +:053DA000820100020495 +:203DA6000902390002010080370904000001FF000000070586024000000904000101FF0010 +:193DC600010007058201FF03010904000201FF00010007058201000201AF +:203DE000040309040E0373006900670072006F006B0010036600780032006C0061006600B9 +:203E0000770022035300610069006E0053006D006100720074002000440044005300310048 +:063E20003200300000003A +:040000000201B9320E +:01000B0032C2 +:0100130032BA +:01001B0032B2 +:0100230032AA +:03002B000203AD20 +:030033000202E4E2 +:01003B003292 +:01004300328A +:01004B003282 +:01005300327A +:01005B003272 +:01006300326A +:03006B000202E7A7 +:03007300020D2A51 +:03007B000208EA8E +:03008300020395E0 +:03008B000202FF6F +:0300930002034A1B +:03009B000208E771 +:0300A300020D54F7 +:0300AB00020D55EE +:0300B300020D56E5 +:0300BB00020D59DA +:0300C300020D2902 +:0300CB00020D46DD +:0300D300020D47D4 +:0300DB00020D49CA +:0300E3000208EC24 +:0300EB00020DD132 +:0300F3000208E818 +:0300FB000208E90F +:030103000208EB04 +:03010B000208EDFA +:030113000208EEF1 +:03011B00020DB41E +:03012300020D5278 +:03012B00020D576B +:03013300020D5A60 +:03013B00020D5C56 +:03014300020D5357 +:03014B00020D584A +:03015300020D5B3F +:03015B00020D5D35 +:03016300020DB3D7 +:03016B00020D4A38 +:03017300020D4C2E +:03017B00020D4E24 +:03018300020D501A +:03018B00020D4B17 +:03019300020D4D0D +:03019B00020D4F03 +:0301A300020D51F9 +:0301AB00020D48FA +:0301B300020D2812 +:0C021200750800E4F509F50AC200C201FD +:030230000201B612 +:0301B6000207D865 +:20023300E582FF70407E0EE4FDFC7401B51302801C7402B5130280197405B513028016742B +:200253000AB5130280137414B5131680107B0280647B0680607B00805C7B0480587B0880BB +:200273005475820022BF01497E307D027401B51302801C7402B51302801B7405B51302804F +:200293001A740AB5130280197414B5132080187B007C0280207B107C02801A7B007C008095 +:2002B300147B107C00800E7B207C0080087582002275820022EEF4FFE5A05207EE5B4FF565 +:2002D300A0EDF4FFE5B15207ED5C4FF5B175820122C2DC32C0E0C082C083D2005391EF901A +:2002F300E65D7401F0D083D082D0E032C021C0E0C0F0C082C083C007C006C005C004C0032D +:20031300C002C001C000C0D075D000758200120C185391EF90E65D7410F0D0D0D000D0012A +:20033300D002D003D004D005D006D007D083D082D0F0D0E0D02132C021C0E0C0F0C082C044 +:2003530083C007C006C005C004C003C002C001C000C0D075D000758201120C185391EF9085 +:20037300E65D7420F0D0D0D000D001D002D003D004D005D006D007D083D082D0F0D0E0D082 +:200393002132C0E0C082C083D2015391EF90E65D7408F0D083D082D0E032C0E0C007C00639 +:2003B300C0D075D000AEB17F006306048EB1E509450A6010150974FFB50902150AE509457B +:2003D3000A700100C2CFD0D0D006D007D0E032E582FF30E00BADB17E004305088DB1800361 +:2003F30053B1F7EF30E408AEB14306018EB12253B1FE22AF82BF01028003BF021174072FC4 +:20041300FF90E618F090E61AEFF0758201227582002290E6F574FFF000000090E60474806E +:20043300F000000090E6047482F000000090E6047486F000000090E604E4F02290E6F574A6 +:20045300FFF0000000E50870057E06FF80047E027F0090E648EEF022D2871204257EE87FFB +:2004730003EE24FFFCEF34FFFD8C068D07EC4D70F0E5BB30E7FB00000090E6D07428F000F7 +:20049300000090E6D1E4F0E50870057E06FF80047E047F008EBB00E4F509F50A22AF8290B7 +:2004B300E680E030E7067D1C7E3D80047DA67E3D8F03E4CBC4CB6BCB54F0CB6BFC74162BDF +:2004D300FBE43CFCEB2DFDEC3EFC7E80EFF508703590E612E4F090E61474E0F090E6E27432 +:2004F30001F08D828C838EF0120DB590E625F074012DFAE43CFB8E078A828B838FF0120DF9 +:20051300B590E624F02290E61274D8F090E614E4F090E6D204F08D828C838EF0120DB59009 +:20053300E621F00DBD00010C8D828C838EF0120DB5FF90E62074075FF08D828C838EF0125D +:200553000DB5C423541F90E64004F022AF827E00EE75F007A4FCADF024D6F582ED340DF5C6 +:2005730083E493FBB5070280080EBE11E375820022EC24D6FCED340DFD74062CF582E43D09 +:20059300F583E49390E601F075AF07759DE4759E008C828D83A3E49390E67CF08C828D8386 +:2005B300A3A3E49390E67CF07401F0E4F0F0F0F0F08C828D83A3A3A39390E67CF08C828D79 +:2005D30083A3A3A3A3E49390E67CF07401F0E4F0F0F0F0F08C828D83A3A3A3A3A39390E651 +:2005F3007CF07444F0F0E4F0F0F0F0F0F0F0F0F0F0F0F0F07F0090E67CE4F00FBF6000401D +:20061300F575820122AF82BF00028049BF01028062BF02028067BF050302069ABF0A030278 +:2006330006A4BF14030206AEBF69028014BF6A028019BF6E02801EBF78028023BF9666800B +:200653002875CAF075CBD87582012275CA7375CBDF7582012275CA7875CBEC7582012275A1 +:20067300CA3C75CBF67582012275CA1875CBFC7582012275CA0C75CBFE7582012275CA067C +:2006930075CBFF7582012275CA9C75CBFF7582012275CACE75CBFF7582012275CAE775CBEE +:2006B300FF7582012275820022AD13AE14AF158D828E838FF0E508120D2B75820122E58258 +:2006D3007006850B821204B0758201227582002275820122AF82C00712044FD007007509BA +:2006F300E8750A03BFE0004015EF2419401090E68AE4F090E68BF090E6A0E020E1F9BFE0B9 +:2007130000500302078EEF2419500302078EEF2420FE2E90072A73800C800A801B8026805C +:2007330033803E8049EF2420FF90E740E0F5138F821202337582012290E740E0F582120584 +:200753005F7582012290E740E0FFBF010312046B7582012290E740E0F582120406758201F7 +:200773002290E740E0F5821203E27582012290E740E0F58212061875820122758200229024 +:20079300E613E4F090E615F0D28790E6C2E4F090E6C37480F090E6C0E4F090E6F4F0120402 +:2007B3004F75130175820012023375130175820112023375820112055F7582021204067550 +:2007D30082000204B090E600E053E0E7F0120792E500D2E890E668E0FF43070890E668EF48 +:2007F300F090E65CE0FF43070190E65CEFF0E0FF43071090E65CEFF0E0FF43072090E65C44 +:20081300EFF0E0FF43070890E65CEFF0D2DDD2AF75CA0C75CBFE75C800D2ADD2CA90E680FD +:20083300E043E00AF09005DC120D5E90E680E053E0F7F090E672E4F090E671F090E670F061 +:2008530075B6FF75B4FF75B2FF10000280031208EF10010280F390E682E043E0C0F090E6C8 +:20087300817401F0AE877F004306018E8700000000000000E50E701C90E682E030E6079068 +:20089300E682E020E0D090E682E030E70790E682E020E1C290E682E030E60790E682E020AF +:2008B300E00E90E682E030E7A090E682E030E199900005120D5E90E680E0FF43070190E67E +:1408D30080EFF090000F120D5E90E680E053E0FEF002085C39 +:200DD600308000030000EA1E8000030000AA180100020140EA100101020040EA0F0100027F +:200DF6000140AA0C0201020040EA0B0101020040AA080302020040EA060202020040AA058A +:200E16000302020040AA040605020040EA030504020040AA020C0B020040EA011817020021 +:170E360040EA32302F020040EA147877020040EA0AF0EF020040EA7A +:0108E70032DE +:0108E80032DD +:0108E90032DC +:0108EA0032DB +:0108EB0032DA +:0108EC0032D9 +:0108ED0032D8 +:0108EE0032D7 +:0601E800E478FFF6D8FDEB +:12021E00750D00750E00750FA675103D75111C75123D77 +:2008EF0090E6B9E0FF24F45003020A13EF240A83F582EF241083F583E473213D13591313D5 +:20090F0075137B92B4EE09090A090A0A090A09090909120A95E5826003020A2F90E6A0E075 +:20092F00FF7E0043070190E6A0EFF0020A2F120B29E5826003020A2F90E6A0E0FF7E0043AF +:20094F00070190E6A0EFF0020A2F120B77E5826003020A2F90E6A0E0FF7E0043070190E683 +:20096F00A0EFF0020A2F120C43020A2F1206DFAF8290E740EFF090E68AE4F090E68B04F08B +:20098F00020A2F90E6BAE0F5821206E3E5826003020A2F90E6A0E0FF7E0043070190E6A0B2 +:2009AF00EFF0020A2F90E6BCE0FF75130C7514007515408F821206BCE582701190E6A0E053 +:2009CF00FF7E0043070190E6A0EFF0805390E740E50CF090E68AE4F090E68B04F0804190C6 +:2009EF00E6BCE0FF90E6BAE0F50B8F821206D1E582702D90E6A0E0FF7E0043070190E6A085 +:200A0F00EFF0801C90E6B9E0F5821206E7E582700F90E6A0E0FF7E0043070190E6A0EFF02E +:200A2F0090E6A0E0FF43078090E6A0EFF022AF82747F5FFE24F75003020A91EE240A83F5B1 +:200A4F0082EE240D83F583E4736A6E8191859189918D0A0A0A0A0A0A0A0A0A90E6A022EF6C +:200A6F0030E7067EA27FE680047EA17FE68E828F832290E6A32290E6A42290E6A52290E64F +:200A8F00A6229000002290E6B8E0FFBF80028022BF81028008BF82028037020B2190E74034 +:200AAF00E4F090E741F090E68AF090E68B7402F08064E50E25E0FFE50D420790E740EFF0A8 +:200ACF0090E741E4F090E68AF090E68B7402F0804590E6BCE0F582120A3DAE82AF838E0489 +:200AEF008F05EE4F7003F582228C828D83E0FC30E0067E017F0080047E007F0090E740EED6 +:200B0F00F090E741E4F090E68AF090E68B7402F08004758200227582012290E6B8E0FF60CF +:200B2F0005BF0237801190E6BAE0FFBF0105750E0080317582002290E6BAE0701A90E6BC2B +:200B4F00E0F582120A3DAE82AF83E0FD5305FE8E828F83EDF0800D7582002290E6B9E0F598 +:200B6F00820206E77582012290E6B8E0FF6008BF02028020020C0B90E6BAE0FFBF02030215 +:200B8F000C1490E6BAE0FFBF0106750E01020C147582002290E6BAE0705E90E6BCE0F5822B +:200BAF00120A3DAE82AF83EE4F7003F582228E828F83E0FD7C004305018E828F83EDF090CF +:200BCF00E6BCE0FF53070F90E6BCE0FE530680E4C423CEC423541F6ECE541FCE6ECE30E473 +:200BEF000244E0FD90E683EE2FF0E0FF7E0043072090E683EFF0800D7582002290E6B9E069 +:200C0F00F5820206E775820122AF827E0110AF027E00EF600E750F1C75103D7511A67512E4 +:200C2F003D800C750FA675103D75111C75123DEE1392AF2290E6BBE0FFBF01028019BF02FA +:200C4F00028024BF0302802CBF0603020CFDBF0703020D0D020D1A7E007F3D90E6B3EFF04C +:200C6F0090E6B47400F02290E6B3E510F090E6B4E50FF0227DE07E3D7F8090E6BAE0F51348 +:200C8F007B008B020BEAB5130280428D008E018F028D828E838FF0120DB528F8E439F988DE +:200CAF000589068A0774012DF9E43EFA8F0489828A838CF0120DB5F9B9030280067D007E16 +:200CCF00007F00ED4E6006C3E5139B50B5ED4E600F8D038E0490E6B3ECF090E6B4EDF02290 +:200CEF0090E6A0E0FF43070190E6A0EFF0227E127F3D90E6B3EFF090E6B47412F02290E602 +:190D0F00B3E512F090E6B4E511F02290E6A0E0FF43070190E6A0EFF022A8 +:2001C6007900E94400601B7A00900E4D780075923CE493F2A308B800020592D9F4DAF27565 +:0201E60092FF86 +:010D28003298 +:010D29003297 +:010D2A003296 +:1B0D2B0020F71130F6138883A88220F509F6A8837583002280FEF280F5F022C7 +:010D4600327A +:010D47003279 +:010D48003278 +:010D49003277 +:03004300023F0079 +:203F00000202E700020D2A000208EA00020395000202FF0002034A000208E700020D53004A +:203F2000020D5400020D5500020D5600020D5900020D2900020D4600020D4700020D4900B2 +:203F40000208EC00020D5300020DD1000208E8000208E9000208EB000208ED000208EE0060 +:203F6000020DB400020D5300020D5300020D5300020D5200020D5700020D5A00020D5C00BD +:203F8000020D5800020D5B00020D5D00020DB300020D4A00020D4C00020D4E00020D5000B2 +:183FA000020D4B00020D4D00020D4F00020D5100020D4800020D280007 +:010D4A003276 +:010D4B003275 +:2001EE007800E84400600A790075923CE4F309D8FC7800E84400600C7900903C00E4F0A3A7 +:04020E00D8FCD9FA45 +:010D4C003274 +:010D4D003273 +:010D4E003272 +:010D4F003271 +:010D50003270 +:010D5100326F +:010D5200326E +:010D5300326D +:010D5400326C +:010D5500326B +:010D5600326A +:010D57003269 +:010D58003268 +:010D59003267 +:010D5A003266 +:010D5B003265 +:010D5C003264 +:010D5D003263 +:200D5E00AE82AF8390E600E05418C423541F70057CB1FD801C90E600E05418C423541FFBA5 +:200D7E00BB01067A617B0180047AC27B028A048B058C138D14151374FFB513021514E5131B +:150D9E00451470F1EE24FFFAEF34FFFB8A068B07EA4B70DD2298 +:010DB300320D +:010DB400320C +:0D01B900758121120DD2E58260030201B6AE +:1C0DB50020F71430F6148883A88220F507E6A88375830022E280F7E49322E0224D +:010DD10032EF +:040DD2007582002204 +:00000001FF diff --git a/openhantek/res/firmware/dso2090x86-firmware.hex b/openhantek/res/firmware/dso2090x86-firmware.hex deleted file mode 100644 index 891ac6b1..00000000 --- a/openhantek/res/firmware/dso2090x86-firmware.hex +++ /dev/null @@ -1,372 +0,0 @@ -:0A0BF50000010202030304040505D9 -:1008AF00E4F52DF52CF52BF52AC20BC208C20AC2AE -:1008BF0009120CB8E4F50AF50BD2A6C2C975CDFC26 -:1008CF0075CC2AD2CAD2AFD2AD7508087509077E8A -:1008DF00007F908E0E8F0F7516007517A2750C0086 -:1008EF00750DAC7514007515CC7518007519EC9055 -:1008FF00E680E030E70E850C10850D11851412850A -:10090F001513800C851410851511850C12850D1388 -:10091F00EE54E07003020A3B752E00752F807E00A7 -:10092F007F908E308F31C374129FFF74019ECF243E -:10093F0002CF3400FEE48F298E28F527F526F52502 -:10094F00F524F523F522AF29AE28AD27AC26AB252C -:10095F00AA24A923A822C3120E17502AE52F252552 -:10096F00F582E52E3524F58374CDF0E4FAF9F8E538 -:10097F00252401F525EA3524F524E93523F523E867 -:10098F003522F52280C0E4F525F524F523F522AFB5 -:10099F0029AE28AD27AC26AB25AA24A923A822C3AC -:1009AF00120E175035AE24AF25E5312FF582E53005 -:1009BF003EF583E0FDE52F2FF582E52E3EF583ED25 -:1009CF00F0E4FAF9F8EF2401F525EA3EF524E935CC -:1009DF0023F523E83522F52280B5852E0E852F0FBE -:1009EF0074902480FF740034FFFEC3E5179FF51742 -:1009FF00E5169EF516C3E5119FF511E5109EF5104E -:100A0F00C3E5139FF513E5129EF512C3E50D9FF590 -:100A1F000DE50C9EF50CC3E5159FF515E5149EF538 -:100A2F0014C3E5199FF519E5189EF518D2E843D8B8 -:100A3F002090E668E04409F090E65CE0443DF0D297 -:100A4F00AF90E680E020E105D20E1210C490E68050 -:100A5F00E054F7F0538EF8C20B300905120616C298 -:100A6F0009300B2912124A5024C20B12114B2008C5 -:100A7F001690E682E030E704E020E1EF90E682E0B6 -:100A8F0030E604E020E0E41210F312124C120BFFD8 -:020A9F0080C70E -:010AA1002232 -:1006160090E6B9E070030206F114700302079A240B -:10062600FE700302082F24FB70030206EB1470030E -:100636000206E51470030206D91470030206DF24CD -:1006460005600302089B12124E40030208A790E6BB -:10065600BBE024FE602C14604724FD601614603154 -:1006660024067065E50E90E6B3F0E50F90E6B4F06B -:100676000208A7E51690E6B3F0E51790E6B4F00297 -:1006860008A7E51090E6B3F0E51190E6B4F002088D -:10069600A7E51290E6B3F0E51390E6B4F00208A7DA -:1006A60090E6BAE0FF12111FAA06A9077B01EA49E4 -:1006B600600DEE90E6B3F0EF90E6B4F00208A79076 -:1006C600E6A0E04401F00208A790E6A0E04401F0AD -:1006D6000208A71212160208A712123A0208A71257 -:1006E6000F4E0208A71212040208A7121250400366 -:1006F6000208A790E6B8E0247F602B14603C240231 -:100706006003020790A208E433FF25E0FFA20AE493 -:10071600334F90E740F0E4A3F090E68AF090E68B42 -:100726007402F00208A7E490E740F0A3F090E68A8E -:10073600F090E68B7402F00208A790E6BCE0547EC7 -:10074600FF7E00E0D3948040067C007D0180047C1F -:10075600007D00EC4EFEED4F24F5F582740B3EF560 -:1007660083E493FF3395E0FEEF24A1FFEE34E68F9A -:1007760082F583E0540190E740F0E4A3F090E68A26 -:10078600F090E68B7402F00208A790E6A0E0440120 -:10079600F00208A712125240030208A790E6B8E03A -:1007A60024FE601D240260030208A790E6BAE0B4A6 -:1007B6000105C2080208A790E6A0E04401F002087D -:1007C600A790E6BAE0705990E6BCE0547EFF7E0042 -:1007D600E0D3948040067C007D0180047C007D008F -:1007E600EC4EFEED4F24F5F582740B3EF583E49353 -:1007F600FF3395E0FEEF24A1FFEE34E68F82F5830A -:10080600E054FEF090E6BCE05480FF131313541F2F -:10081600FFE0540F2F90E683F0E04420F00208A793 -:1008260090E6A0E04401F08078121254507390E6EE -:10083600B8E024FE60202402706790E6BAE0B401B6 -:1008460004D208805C90E6BAE06402605490E6A0A8 -:10085600E04401F0804B90E6BCE0547EFF7E00E071 -:10086600D3948040067C007D0180047C007D00ECF2 -:100876004EFEED4F24F5F582740B3EF583E493FFAF -:100886003395E0FEEF24A1FFEE34E68F82F583E098 -:100896004401F0800C120114500790E6A0E04401D8 -:0808A600F090E6A0E04480F0B0 -:0108AE002227 -:0300330002008C3C -:04008C0053D8EF3224 -:03002B00020F13AE -:100F1300C0E0B2A5E509150970021508E509450801 -:100F2300701E75080475091FE50B6401450A7006F8 -:100F3300B2A6D2A78004B2A7D2A6750A00750B0089 -:0B0F430075CDFC75CC23C2CFD0E0328E -:100090001201000200000040B5049020000001029F -:1000A00000010A06000200000040010009022000D1 -:1000B000010100A0FA0904000002FF00000007058A -:1000C0000202000200070586020002000902200069 -:1000D000010100A0FA0904000002FF00000007056A -:1000E00002024000000705860240000004030904E4 -:1000F0000E034F0044004D00200020002000140398 -:10010000440053004F002D00320030003900300011 -:0401100020000000CB -:03004300020C00AC -:03005300020C009C -:100C00000211A8000211EE000211D8000211C0006A -:100C1000020F8500020FBC0002125600021257009C -:100C2000021258000212590002125A0002125B000E -:100C300002125C0002125D0002125E0002125F00EE -:100C400002126000021257000212610002126200DA -:100C500002126300021264000212650002126600B2 -:100C600002126700021257000212570002125700C8 -:100C7000021268000212690002126A0002126B007E -:100C800002126C0002126D0002126E0002126F005E -:100C9000021270000212710002127200021273003E -:100CA000021274000212750002127600021277001E -:080CB000021278000212790023 -:1010F30090E682E030E004E020E60B90E682E03008 -:10110300E119E030E71590E680E04401F07F147EBA -:0C11130000120ECD90E680E054FEF022A9 -:10114B0090E682E044C0F090E681F0438701000016 -:04115B00000000226E -:1010C400300E0990E680E0440AF0800790E680E064 -:1010D4004408F07FDC7E05120ECD90E65D74FFF0CF -:0F10E40090E65FF05391EF90E680E054F7F02232 -:02111F00A9071E -:10112100AE18AF198F828E83A3E064037017AD01EF -:1011310019ED7001228F828E83E07C002FFDEC3E41 -:09114100FEAF0580DF7E007F0097 -:01114A002282 -:100ECD008E3C8F3D90E600E054187012E53D2401F4 -:100EDD00FFE4353CC313F53CEF13F53D801590E66B -:100EED0000E05418FFBF100BE53D25E0F53DE53C56 -:100EFD0033F53CE53D153DAE3C7002153C4E6005AD -:060F0D0012115F80EE22CC -:10115F007400F58690FDA57C05A3E582458370F9A3 -:01116F00225D -:100CB80090E600740AF090E6017443F0000000909A -:100CC800E61274A0F0000000E490E613F0000000C3 -:100CD80090E61474E0F0000000E490E615F00000DF -:100CE8000090E6047480F00000007402F000000038 -:100CF8007406F0000000E4F000000090E60274E6DC -:100D0800F000000090E60374F8F000000090E67030 -:100D1800E04440F0000000E490E609F000000043E1 -:100D2800B20F90E61804F00000007411F000000003 -:100D380090E61A7409F0000000E490E671F090E67D -:100D480072F075B4FF75B6FFC280C20C12124290E1 -:070D5800E67AE04401F022FD -:010BFF0022D3 -:02124A00D322AD -:02124C00D322AB -:02124E00D322A9 -:100F4E0090E680E030E71590E6247402F000000091 -:100F5E00E490E625F0000000D20C8013E490E62425 -:100F6E00F000000090E6257440F0000000C20C90E6 -:070F7E00E6BAE0F51DD322E5 -:1012040090E740E51DF0E490E68AF090E68B04F068 -:02121400D322E3 -:08123A0090E6BAE0F51BD32297 -:1012160090E740E51BF0E490E68AF090E68B04F058 -:02122600D322D1 -:02125000D322A7 -:02125200D322A5 -:02125400D322A3 -:10011400750A00750B0190E678E05410FFC4540F83 -:100124004450F51C13E433F51E90E6B9E0245E70E8 -:100134000302044A24F0B40A0040030206129001A8 -:1001440049F82828730201670201830201C10202EF -:10015400030203030203460203870203C8020409DD -:100164000205D2A20CE43390E740F0E490E68AF072 -:1001740090E68B04F090E6A0E04480F0020614E4DC -:1001840090E68AF090E68BF090E6A0E020E1F9900A -:10019400E68BE0753600F53700000090E6047480C5 -:1001A400F00000007406F0000000E4F043800BD27D -:1001B40080C28090E6A0E04480F0020614E490E659 -:1001C4008AF090E68BF090E6A0E020E1F990E68BCF -:1001D400E0753600F5377F0F7E00120ECD90E745AF -:1001E400E0FF90E747E0FD90E743E0FB90E741E064 -:0F01F400F53F120E2890E6A0E04480F0020614BA -:10020300E490E68AF090E68BF090E6A0E020E1F936 -:1002130090E68BE0753600F53790E741E0F5B17570 -:100223003A0F753BA0E53B153B7002153AE53B647D -:100233000A453A70F090E742E0F5B1753A0F753B25 -:10024300A0E53B153B7002153AE53B640A453A705D -:10025300F090E743E0F5B1753A0F753BA0E53B1528 -:100263003B7002153AE53B640A453A70F090E74467 -:10027300E0F5B1753A0F753BA0E53B153B700215F0 -:100283003AE53B640A453A70F090E745E0F5B1750D -:100293003A0F753BA0E53B153B7002153AE53B640D -:1002A3000A453A70F090E746E0F5B1753A0F753BB1 -:1002B300A0E53B153B7002153AE53B640A453A70ED -:1002C300F090E747E0F5B1753A0F753BA0E53B15B4 -:1002D3003B7002153AE53B640A453A70F0E4F5B128 -:1002E300753A0F753BA0E53B153B7002153AE53BAC -:1002F300640A453A70F090E6A0E04480F0020614E8 -:10030300E490E68AF090E68BF090E6A0E020E1F935 -:1003130090E68BE0753600F53700000090E6047434 -:1003230080F0000000C2807406F0000000E4F0904A -:10033300E740E0FF7E00120ECD90E6A0E04480F09F -:10034300020614E490E68AF090E68BF090E6A0E0D3 -:1003530020E1F990E68BE0753600F5370000009058 -:10036300E6047480F00000007406F0000000E4F07E -:1003730090E740E0FF7E00120ECD90E6A0E04480BF -:10038300F0020614E490E68AF090E68BF090E6A083 -:10039300E020E1F990E68BE0753600F537000000C8 -:1003A30090E6047480F00000007406F0000000E49E -:1003B300F090E740E0FF7E00120ECD90E6A0E0440F -:1003C30080F0020614E490E68AF090E68BF090E663 -:1003D300A0E020E1F990E68BE0753600F5370000E8 -:1003E3000090E6047480F00000007406F000000042 -:1003F300E4F090E740E0FF7E00120ECD90E6A0E02F -:100403004480F0020614E490E68AF090E68BF090C4 -:10041300E6A0E020E1F990E68BE0753600F53700C1 -:10042300000090E6047480F00000007406F0000001 -:1004330000E4F090E740E0FF7E00120ECD90E6A0CE -:10044300E04480F002061490E6BAE0753200F5331A -:10045300A3E0FEE4EE423290E6BEE0753400F535EB -:10046300A3E0FEE4EE423490E6B8E064C060030229 -:10047300053AE5354534700302061490E6A0E02002 -:10048300E1F9C3E5359440E53494005008853436EA -:10049300853537800675360075374090E6B9E0B488 -:1004A300A335E4F538F539C3E5399537E53895369D -:1004B3005060E5332539F582E5323538F583E0FFC1 -:1004C30074402539F582E434E7F583EFF00539E527 -:1004D300397002053880D0E4F538F539C3E539952C -:1004E30037E5389536501874402539F582E434E7FA -:1004F300F58374CDF00539E5397002053880DDAD3B -:10050300377AE779407EE77F40AB07AF33AE3212ED -:100513000BAAE490E68AF090E68BE537F02533F5F5 -:1005230033E5363532F532C3E5359537F535E53400 -:100533009536F53402047590E6B8E0644060030232 -:100543000614E53545347003020614E490E68AF098 -:1005530090E68BF090E6A0E020E1F990E68BE07561 -:100563003600F53790E6B9E0B4A335E4F538F5394C -:10057300C3E5399537E5389536503874402539F554 -:1005830082E434E7F583E0FFE5332539F582E5328C -:100593003538F583EFF00539E5397002053880D039 -:1005A300AD377AE779407EE77F40AB07AF33AE32B2 -:1005B300121029E5372533F533E5363532F532C3E5 -:1005C300E5359537F535E5349536F534020545E4DB -:1005D30090E68AF090E68BF090E6A0E020E1F990B7 -:1005E300E68BE0753600F53700000090E604748072 -:1005F300F00000007406F0000000E4F090E740E033 -:10060300FF7E00120ECD90E6A0E04480F08002D37E -:0206130022C300 -:0106150022C2 -:1011A800C0E0C083C082D2095391EF90E65D74011C -:0811B800F0D082D083D0E032B8 -:1011D800C0E0C083C0825391EF90E65D7404F0D004 -:0611E80082D083D0E0324A -:1011EE00C0E0C083C0825391EF90E65D7402F0D0F0 -:0611FE0082D083D0E03234 -:100F8500C0E0C083C08290E680E030E70E850C109B -:100F9500850D11851412851513800C851410851582 -:100FA50011850C12850D135391EF90E65D7410F0C9 -:070FB500D082D083D0E032AE -:1011C000C0E0C083C082D20B5391EF90E65D7408FB -:0811D000F0D082D083D0E032A0 -:100FBC00C0E0C083C08290E680E030E70E850C1064 -:100FCC00850D11851412851513800C85141085154B -:100FDC0011850C12850D135391EF90E65D7420F082 -:070FEC00D082D083D0E03277 -:011256003265 -:011257003264 -:011258003263 -:011259003262 -:01125A003261 -:01125B003260 -:01125C00325F -:01125D00325E -:01125E00325D -:01125F00325C -:01126000325B -:01126100325A -:011262003259 -:011263003258 -:011264003257 -:011265003256 -:011266003255 -:011267003254 -:011268003253 -:011269003252 -:01126A003251 -:01126B003250 -:01126C00324F -:01126D00324E -:01126E00324D -:01126F00324C -:01127000324B -:01127100324A -:011272003249 -:011273003248 -:011274003247 -:011275003246 -:011276003245 -:011277003244 -:011278003243 -:011279003242 -:10122800AD071FED600BE4FEEEC3947850F20E801C -:01123800F7BE -:011239002292 -:040D5F00A907AB0530 -:100D63007F01121228D2A27F01121228C2A2E4FA32 -:100D7300E9A802088002C333D8FCFC5304807F0136 -:100D8300121228D2A1BC8004D2A08002C2A07F018B -:100D9300121228C2A10ABA08D7E4FAEBA802088003 -:100DA30002C333D8FCFC5304807F01121228D2A162 -:100DB300BC8004D2A08002C2A07F01121228C2A16B -:040DC3000ABA08D789 -:010DC7002209 -:040E2800AE07AC0560 -:100E2C00EEC4540F4410FFEEC454F0FDECC4540F48 -:100E3C004450F540ECC454F0F541EBC4540F4490CD -:100E4C00F542EBC454F0F543E53FC4540F44D0F5E0 -:100E5C0044E53FC454F0F545C2A0C2A1C2A2120D94 -:100E6C005FAD41AF40120D5FAD43AF42120D5FADB0 -:060E7C0045AF44120D5FBA -:010E8200224D -:060E8300AB07AA06AC0556 -:100E8900E4FDE51E6010EA7E000DEE2400F582E423 -:100E99003418F583EAF0EBAE050D74002EF582E403 -:100EA9003418F583EBF0AF050D74002FF582E434A7 -:100EB90018F583ECF07A187B00AF1C12118CAF1C6B -:030EC900120FF312 -:010ECC002203 -:0A1029008E3C8F3D8D3E8A3F8B40C8 -:10103300E4F541E541C3953E5020053DE53DAE3C19 -:101043007002053C14FFE5402541F582E4353FF588 -:0A10530083E0FD120E83054180D9F1 -:01105D002270 -:0A0BAA008E3C8F3D8D3E8A3F8B404C -:100BB400E4FDF541E51E6012E53CFF7E000DEE24E8 -:100BC40000F582E43418F583EFF0E53DAE050D74CD -:100BD400002EF582E43418F583E53DF07A187B00A5 -:100BE400AF1C12118CAB40AA3FAD3EAF1C1211706A -:010BF40022DE -:08124200E4F54BD2E9D2AF2222 -:100FF30090E678E020E6F9C2E990E678E04480F0F4 -:10100300EF25E090E679F090E678E030E0F990E6BD -:1010130078E04440F090E678E020E6F990E678E066 -:0610230030E1D6D2E92203 -:10109200A90790E678E020E6F9E54B702390E67820 -:1010A200E04480F0E925E090E679F08D46AF03A9AF -:1010B200077547018A488949E4F54A754B01D322ED -:0210C200C32247 -:10105E00A90790E678E020E6F9E54B702590E67852 -:10106E00E04480F0E925E0440190E679F08D46AF4A -:10107E0003A9077547018A488949E4F54A754B0368 -:04108E00D322C32284 -:03004B00020AA204 -:100AA200C0E0C083C082C085C084C086758600C095 -:100AB200D075D000C000C001C002C003C006C0078C -:100AC20090E678E030E206754B06020B8C90E678F1 -:100AD200E020E10CE54B64026006754B07020B8CCB -:100AE200E54B24FE605F14603624FE7003020B7D2A -:100AF20024FC7003020B8924086003020B8CAB47B1 -:100B0200AA48A949AF4A054A8F82758300120DC8C7 -:100B120090E679F0E54A65467070754B05806B90FA -:100B2200E679E0AB47AA48A949AE4A8E82758300AE -:100B3200120DF5754B02E5466401704E90E678E0C1 -:100B42004420F08045E54624FEB54A0790E678E069 -:100B52004420F0E54614B54A0A90E678E04440F0B5 -:100B6200754B0090E679E0AB47AA48A949AE4A8E98 -:100B720082758300120DF5054A800F90E678E044F5 -:100B820040F0754B008003754B005391DFD007D0C6 -:100B920006D003D002D001D000D0D0D086D084D0ED -:080BA20085D082D083D0E0323F -:1011700012105EE54B24FA600E146006240770F32B -:0C118000D322E4F54BD322E4F54BD3223C -:10118C00121092E54B24FA600E146006240770F3DB -:0C119C00D322E4F54BD322E4F54BD32220 -:030000000200807B -:0C008000787FE4F6D8FD75814B0208AFD4 -:100DC800BB010CE58229F582E5833AF583E02250E0 -:100DD80006E92582F8E622BBFE06E92582F8E2222A -:0D0DE800E58229F582E5833AF583E4932244 -:100DF500F8BB010DE58229F582E5833AF583E8F034 -:100E0500225006E92582C8F622BBFE05E92582C8DF -:020E1500F222C7 -:100E1700EB9FF5F0EA9E42F0E99D42F0E89C45F031 -:010E270022A8 -:00000001FF diff --git a/openhantek/res/firmware/dso2090x86-loader.hex b/openhantek/res/firmware/dso2090x86-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso2090x86-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso2150x86-firmware.hex b/openhantek/res/firmware/dso2150x86-firmware.hex deleted file mode 100644 index 00ab6553..00000000 --- a/openhantek/res/firmware/dso2150x86-firmware.hex +++ /dev/null @@ -1,372 +0,0 @@ -:0A0BF50000010202030304040505D9 -:1008AF00E4F52DF52CF52BF52AC20BC208C20AC2AE -:1008BF0009120CB8E4F50AF50BD2A6C2C975CDFC26 -:1008CF0075CC2AD2CAD2AFD2AD7508087509077E8A -:1008DF00007F908E0E8F0F7516007517A2750C0086 -:1008EF00750DAC7514007515CC7518007519EC9055 -:1008FF00E680E030E70E850C10850D11851412850A -:10090F001513800C851410851511850C12850D1388 -:10091F00EE54E07003020A3B752E00752F807E00A7 -:10092F007F908E308F31C374129FFF74019ECF243E -:10093F0002CF3400FEE48F298E28F527F526F52502 -:10094F00F524F523F522AF29AE28AD27AC26AB252C -:10095F00AA24A923A822C3120E17502AE52F252552 -:10096F00F582E52E3524F58374CDF0E4FAF9F8E538 -:10097F00252401F525EA3524F524E93523F523E867 -:10098F003522F52280C0E4F525F524F523F522AFB5 -:10099F0029AE28AD27AC26AB25AA24A923A822C3AC -:1009AF00120E175035AE24AF25E5312FF582E53005 -:1009BF003EF583E0FDE52F2FF582E52E3EF583ED25 -:1009CF00F0E4FAF9F8EF2401F525EA3EF524E935CC -:1009DF0023F523E83522F52280B5852E0E852F0FBE -:1009EF0074902480FF740034FFFEC3E5179FF51742 -:1009FF00E5169EF516C3E5119FF511E5109EF5104E -:100A0F00C3E5139FF513E5129EF512C3E50D9FF590 -:100A1F000DE50C9EF50CC3E5159FF515E5149EF538 -:100A2F0014C3E5199FF519E5189EF518D2E843D8B8 -:100A3F002090E668E04409F090E65CE0443DF0D297 -:100A4F00AF90E680E020E105D20E1210C490E68050 -:100A5F00E054F7F0538EF8C20B300905120616C298 -:100A6F0009300B2912124A5024C20B12114B2008C5 -:100A7F001690E682E030E704E020E1EF90E682E0B6 -:100A8F0030E604E020E0E41210F312124C120BFFD8 -:020A9F0080C70E -:010AA1002232 -:1006160090E6B9E070030206F114700302079A240B -:10062600FE700302082F24FB70030206EB1470030E -:100636000206E51470030206D91470030206DF24CD -:1006460005600302089B12124E40030208A790E6BB -:10065600BBE024FE602C14604724FD601614603154 -:1006660024067065E50E90E6B3F0E50F90E6B4F06B -:100676000208A7E51690E6B3F0E51790E6B4F00297 -:1006860008A7E51090E6B3F0E51190E6B4F002088D -:10069600A7E51290E6B3F0E51390E6B4F00208A7DA -:1006A60090E6BAE0FF12111FAA06A9077B01EA49E4 -:1006B600600DEE90E6B3F0EF90E6B4F00208A79076 -:1006C600E6A0E04401F00208A790E6A0E04401F0AD -:1006D6000208A71212160208A712123A0208A71257 -:1006E6000F4E0208A71212040208A7121250400366 -:1006F6000208A790E6B8E0247F602B14603C240231 -:100706006003020790A208E433FF25E0FFA20AE493 -:10071600334F90E740F0E4A3F090E68AF090E68B42 -:100726007402F00208A7E490E740F0A3F090E68A8E -:10073600F090E68B7402F00208A790E6BCE0547EC7 -:10074600FF7E00E0D3948040067C007D0180047C1F -:10075600007D00EC4EFEED4F24F5F582740B3EF560 -:1007660083E493FF3395E0FEEF24A1FFEE34E68F9A -:1007760082F583E0540190E740F0E4A3F090E68A26 -:10078600F090E68B7402F00208A790E6A0E0440120 -:10079600F00208A712125240030208A790E6B8E03A -:1007A60024FE601D240260030208A790E6BAE0B4A6 -:1007B6000105C2080208A790E6A0E04401F002087D -:1007C600A790E6BAE0705990E6BCE0547EFF7E0042 -:1007D600E0D3948040067C007D0180047C007D008F -:1007E600EC4EFEED4F24F5F582740B3EF583E49353 -:1007F600FF3395E0FEEF24A1FFEE34E68F82F5830A -:10080600E054FEF090E6BCE05480FF131313541F2F -:10081600FFE0540F2F90E683F0E04420F00208A793 -:1008260090E6A0E04401F08078121254507390E6EE -:10083600B8E024FE60202402706790E6BAE0B401B6 -:1008460004D208805C90E6BAE06402605490E6A0A8 -:10085600E04401F0804B90E6BCE0547EFF7E00E071 -:10086600D3948040067C007D0180047C007D00ECF2 -:100876004EFEED4F24F5F582740B3EF583E493FFAF -:100886003395E0FEEF24A1FFEE34E68F82F583E098 -:100896004401F0800C120114500790E6A0E04401D8 -:0808A600F090E6A0E04480F0B0 -:0108AE002227 -:0300330002008C3C -:04008C0053D8EF3224 -:03002B00020F13AE -:100F1300C0E0B2A5E509150970021508E509450801 -:100F2300701E75080475091FE50B6401450A7006F8 -:100F3300B2A6D2A78004B2A7D2A6750A00750B0089 -:0B0F430075CDFC75CC23C2CFD0E0328E -:100090001201000200000040B504502100000102DE -:1000A00000010A06000200000040010009022000D1 -:1000B000010100A0FA0904000002FF00000007058A -:1000C0000202000200070586020002000902200069 -:1000D000010100A0FA0904000002FF00000007056A -:1000E00002024000000705860240000004030904E4 -:1000F0000E034F0044004D00200020002000140398 -:10010000440053004F002D00320031003500300014 -:0401100020000000CB -:03004300020C00AC -:03005300020C009C -:100C00000211A8000211EE000211D8000211C0006A -:100C1000020F8500020FBC0002125600021257009C -:100C2000021258000212590002125A0002125B000E -:100C300002125C0002125D0002125E0002125F00EE -:100C400002126000021257000212610002126200DA -:100C500002126300021264000212650002126600B2 -:100C600002126700021257000212570002125700C8 -:100C7000021268000212690002126A0002126B007E -:100C800002126C0002126D0002126E0002126F005E -:100C9000021270000212710002127200021273003E -:100CA000021274000212750002127600021277001E -:080CB000021278000212790023 -:1010F30090E682E030E004E020E60B90E682E03008 -:10110300E119E030E71590E680E04401F07F147EBA -:0C11130000120ECD90E680E054FEF022A9 -:10114B0090E682E044C0F090E681F0438701000016 -:04115B00000000226E -:1010C400300E0990E680E0440AF0800790E680E064 -:1010D4004408F07FDC7E05120ECD90E65D74FFF0CF -:0F10E40090E65FF05391EF90E680E054F7F02232 -:02111F00A9071E -:10112100AE18AF198F828E83A3E064037017AD01EF -:1011310019ED7001228F828E83E07C002FFDEC3E41 -:09114100FEAF0580DF7E007F0097 -:01114A002282 -:100ECD008E3C8F3D90E600E054187012E53D2401F4 -:100EDD00FFE4353CC313F53CEF13F53D801590E66B -:100EED0000E05418FFBF100BE53D25E0F53DE53C56 -:100EFD0033F53CE53D153DAE3C7002153C4E6005AD -:060F0D0012115F80EE22CC -:10115F007400F58690FDA57C05A3E582458370F9A3 -:01116F00225D -:100CB80090E600740AF090E6017443F0000000909A -:100CC800E61274A0F0000000E490E613F0000000C3 -:100CD80090E61474E0F0000000E490E615F00000DF -:100CE8000090E6047480F00000007402F000000038 -:100CF8007406F0000000E4F000000090E60274E6DC -:100D0800F000000090E60374F8F000000090E67030 -:100D1800E04440F0000000E490E609F000000043E1 -:100D2800B20F90E61804F00000007411F000000003 -:100D380090E61A7409F0000000E490E671F090E67D -:100D480072F075B4FF75B6FFC280C20C12124290E1 -:070D5800E67AE04401F022FD -:010BFF0022D3 -:02124A00D322AD -:02124C00D322AB -:02124E00D322A9 -:100F4E0090E680E030E71590E6247402F000000091 -:100F5E00E490E625F0000000D20C8013E490E62425 -:100F6E00F000000090E6257440F0000000C20C90E6 -:070F7E00E6BAE0F51DD322E5 -:1012040090E740E51DF0E490E68AF090E68B04F068 -:02121400D322E3 -:08123A0090E6BAE0F51BD32297 -:1012160090E740E51BF0E490E68AF090E68B04F058 -:02122600D322D1 -:02125000D322A7 -:02125200D322A5 -:02125400D322A3 -:10011400750A00750B0190E678E05410FFC4540F83 -:100124004450F51C13E433F51E90E6B9E0245E70E8 -:100134000302044A24F0B40A0040030206129001A8 -:1001440049F82828730201670201830201C10202EF -:10015400030203030203460203870203C8020409DD -:100164000205D2A20CE43390E740F0E490E68AF072 -:1001740090E68B04F090E6A0E04480F0020614E4DC -:1001840090E68AF090E68BF090E6A0E020E1F9900A -:10019400E68BE0753600F53700000090E6047480C5 -:1001A400F00000007406F0000000E4F043800BD27D -:1001B40080C28090E6A0E04480F0020614E490E659 -:1001C4008AF090E68BF090E6A0E020E1F990E68BCF -:1001D400E0753600F5377F0F7E00120ECD90E745AF -:1001E400E0FF90E747E0FD90E743E0FB90E741E064 -:0F01F400F53F120E2890E6A0E04480F0020614BA -:10020300E490E68AF090E68BF090E6A0E020E1F936 -:1002130090E68BE0753600F53790E741E0F5B17570 -:100223003A0F753BA0E53B153B7002153AE53B647D -:100233000A453A70F090E742E0F5B1753A0F753B25 -:10024300A0E53B153B7002153AE53B640A453A705D -:10025300F090E743E0F5B1753A0F753BA0E53B1528 -:100263003B7002153AE53B640A453A70F090E74467 -:10027300E0F5B1753A0F753BA0E53B153B700215F0 -:100283003AE53B640A453A70F090E745E0F5B1750D -:100293003A0F753BA0E53B153B7002153AE53B640D -:1002A3000A453A70F090E746E0F5B1753A0F753BB1 -:1002B300A0E53B153B7002153AE53B640A453A70ED -:1002C300F090E747E0F5B1753A0F753BA0E53B15B4 -:1002D3003B7002153AE53B640A453A70F0E4F5B128 -:1002E300753A0F753BA0E53B153B7002153AE53BAC -:1002F300640A453A70F090E6A0E04480F0020614E8 -:10030300E490E68AF090E68BF090E6A0E020E1F935 -:1003130090E68BE0753600F53700000090E6047434 -:1003230080F0000000C2807406F0000000E4F0904A -:10033300E740E0FF7E00120ECD90E6A0E04480F09F -:10034300020614E490E68AF090E68BF090E6A0E0D3 -:1003530020E1F990E68BE0753600F5370000009058 -:10036300E6047480F00000007406F0000000E4F07E -:1003730090E740E0FF7E00120ECD90E6A0E04480BF -:10038300F0020614E490E68AF090E68BF090E6A083 -:10039300E020E1F990E68BE0753600F537000000C8 -:1003A30090E6047480F00000007406F0000000E49E -:1003B300F090E740E0FF7E00120ECD90E6A0E0440F -:1003C30080F0020614E490E68AF090E68BF090E663 -:1003D300A0E020E1F990E68BE0753600F5370000E8 -:1003E3000090E6047480F00000007406F000000042 -:1003F300E4F090E740E0FF7E00120ECD90E6A0E02F -:100403004480F0020614E490E68AF090E68BF090C4 -:10041300E6A0E020E1F990E68BE0753600F53700C1 -:10042300000090E6047480F00000007406F0000001 -:1004330000E4F090E740E0FF7E00120ECD90E6A0CE -:10044300E04480F002061490E6BAE0753200F5331A -:10045300A3E0FEE4EE423290E6BEE0753400F535EB -:10046300A3E0FEE4EE423490E6B8E064C060030229 -:10047300053AE5354534700302061490E6A0E02002 -:10048300E1F9C3E5359440E53494005008853436EA -:10049300853537800675360075374090E6B9E0B488 -:1004A300A335E4F538F539C3E5399537E53895369D -:1004B3005060E5332539F582E5323538F583E0FFC1 -:1004C30074402539F582E434E7F583EFF00539E527 -:1004D300397002053880D0E4F538F539C3E539952C -:1004E30037E5389536501874402539F582E434E7FA -:1004F300F58374CDF00539E5397002053880DDAD3B -:10050300377AE779407EE77F40AB07AF33AE3212ED -:100513000BAAE490E68AF090E68BE537F02533F5F5 -:1005230033E5363532F532C3E5359537F535E53400 -:100533009536F53402047590E6B8E0644060030232 -:100543000614E53545347003020614E490E68AF098 -:1005530090E68BF090E6A0E020E1F990E68BE07561 -:100563003600F53790E6B9E0B4A335E4F538F5394C -:10057300C3E5399537E5389536503874402539F554 -:1005830082E434E7F583E0FFE5332539F582E5328C -:100593003538F583EFF00539E5397002053880D039 -:1005A300AD377AE779407EE77F40AB07AF33AE32B2 -:1005B300121029E5372533F533E5363532F532C3E5 -:1005C300E5359537F535E5349536F534020545E4DB -:1005D30090E68AF090E68BF090E6A0E020E1F990B7 -:1005E300E68BE0753600F53700000090E604748072 -:1005F300F00000007406F0000000E4F090E740E033 -:10060300FF7E00120ECD90E6A0E04480F08002D37E -:0206130022C300 -:0106150022C2 -:1011A800C0E0C083C082D2095391EF90E65D74011C -:0811B800F0D082D083D0E032B8 -:1011D800C0E0C083C0825391EF90E65D7404F0D004 -:0611E80082D083D0E0324A -:1011EE00C0E0C083C0825391EF90E65D7402F0D0F0 -:0611FE0082D083D0E03234 -:100F8500C0E0C083C08290E680E030E70E850C109B -:100F9500850D11851412851513800C851410851582 -:100FA50011850C12850D135391EF90E65D7410F0C9 -:070FB500D082D083D0E032AE -:1011C000C0E0C083C082D20B5391EF90E65D7408FB -:0811D000F0D082D083D0E032A0 -:100FBC00C0E0C083C08290E680E030E70E850C1064 -:100FCC00850D11851412851513800C85141085154B -:100FDC0011850C12850D135391EF90E65D7420F082 -:070FEC00D082D083D0E03277 -:011256003265 -:011257003264 -:011258003263 -:011259003262 -:01125A003261 -:01125B003260 -:01125C00325F -:01125D00325E -:01125E00325D -:01125F00325C -:01126000325B -:01126100325A -:011262003259 -:011263003258 -:011264003257 -:011265003256 -:011266003255 -:011267003254 -:011268003253 -:011269003252 -:01126A003251 -:01126B003250 -:01126C00324F -:01126D00324E -:01126E00324D -:01126F00324C -:01127000324B -:01127100324A -:011272003249 -:011273003248 -:011274003247 -:011275003246 -:011276003245 -:011277003244 -:011278003243 -:011279003242 -:10122800AD071FED600BE4FEEEC3947850F20E801C -:01123800F7BE -:011239002292 -:040D5F00A907AB0530 -:100D63007F01121228D2A27F01121228C2A2E4FA32 -:100D7300E9A802088002C333D8FCFC5304807F0136 -:100D8300121228D2A1BC8004D2A08002C2A07F018B -:100D9300121228C2A10ABA08D7E4FAEBA802088003 -:100DA30002C333D8FCFC5304807F01121228D2A162 -:100DB300BC8004D2A08002C2A07F01121228C2A16B -:040DC3000ABA08D789 -:010DC7002209 -:040E2800AE07AC0560 -:100E2C00EEC4540F4410FFEEC454F0FDECC4540F48 -:100E3C004450F540ECC454F0F541EBC4540F4490CD -:100E4C00F542EBC454F0F543E53FC4540F44D0F5E0 -:100E5C0044E53FC454F0F545C2A0C2A1C2A2120D94 -:100E6C005FAD41AF40120D5FAD43AF42120D5FADB0 -:060E7C0045AF44120D5FBA -:010E8200224D -:060E8300AB07AA06AC0556 -:100E8900E4FDE51E6010EA7E000DEE2400F582E423 -:100E99003418F583EAF0EBAE050D74002EF582E403 -:100EA9003418F583EBF0AF050D74002FF582E434A7 -:100EB90018F583ECF07A187B00AF1C12118CAF1C6B -:030EC900120FF312 -:010ECC002203 -:0A1029008E3C8F3D8D3E8A3F8B40C8 -:10103300E4F541E541C3953E5020053DE53DAE3C19 -:101043007002053C14FFE5402541F582E4353FF588 -:0A10530083E0FD120E83054180D9F1 -:01105D002270 -:0A0BAA008E3C8F3D8D3E8A3F8B404C -:100BB400E4FDF541E51E6012E53CFF7E000DEE24E8 -:100BC40000F582E43418F583EFF0E53DAE050D74CD -:100BD400002EF582E43418F583E53DF07A187B00A5 -:100BE400AF1C12118CAB40AA3FAD3EAF1C1211706A -:010BF40022DE -:08124200E4F54BD2E9D2AF2222 -:100FF30090E678E020E6F9C2E990E678E04480F0F4 -:10100300EF25E090E679F090E678E030E0F990E6BD -:1010130078E04440F090E678E020E6F990E678E066 -:0610230030E1D6D2E92203 -:10109200A90790E678E020E6F9E54B702390E67820 -:1010A200E04480F0E925E090E679F08D46AF03A9AF -:1010B200077547018A488949E4F54A754B01D322ED -:0210C200C32247 -:10105E00A90790E678E020E6F9E54B702590E67852 -:10106E00E04480F0E925E0440190E679F08D46AF4A -:10107E0003A9077547018A488949E4F54A754B0368 -:04108E00D322C32284 -:03004B00020AA204 -:100AA200C0E0C083C082C085C084C086758600C095 -:100AB200D075D000C000C001C002C003C006C0078C -:100AC20090E678E030E206754B06020B8C90E678F1 -:100AD200E020E10CE54B64026006754B07020B8CCB -:100AE200E54B24FE605F14603624FE7003020B7D2A -:100AF20024FC7003020B8924086003020B8CAB47B1 -:100B0200AA48A949AF4A054A8F82758300120DC8C7 -:100B120090E679F0E54A65467070754B05806B90FA -:100B2200E679E0AB47AA48A949AE4A8E82758300AE -:100B3200120DF5754B02E5466401704E90E678E0C1 -:100B42004420F08045E54624FEB54A0790E678E069 -:100B52004420F0E54614B54A0A90E678E04440F0B5 -:100B6200754B0090E679E0AB47AA48A949AE4A8E98 -:100B720082758300120DF5054A800F90E678E044F5 -:100B820040F0754B008003754B005391DFD007D0C6 -:100B920006D003D002D001D000D0D0D086D084D0ED -:080BA20085D082D083D0E0323F -:1011700012105EE54B24FA600E146006240770F32B -:0C118000D322E4F54BD322E4F54BD3223C -:10118C00121092E54B24FA600E146006240770F3DB -:0C119C00D322E4F54BD322E4F54BD32220 -:030000000200807B -:0C008000787FE4F6D8FD75814B0208AFD4 -:100DC800BB010CE58229F582E5833AF583E02250E0 -:100DD80006E92582F8E622BBFE06E92582F8E2222A -:0D0DE800E58229F582E5833AF583E4932244 -:100DF500F8BB010DE58229F582E5833AF583E8F034 -:100E0500225006E92582C8F622BBFE05E92582C8DF -:020E1500F222C7 -:100E1700EB9FF5F0EA9E42F0E99D42F0E89C45F031 -:010E270022A8 -:00000001FF diff --git a/openhantek/res/firmware/dso2150x86-loader.hex b/openhantek/res/firmware/dso2150x86-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso2150x86-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso2250x86-firmware.hex b/openhantek/res/firmware/dso2250x86-firmware.hex deleted file mode 100644 index 47b9329e..00000000 --- a/openhantek/res/firmware/dso2250x86-firmware.hex +++ /dev/null @@ -1,372 +0,0 @@ -:0A0BF50000010202030304040505D9 -:1008AF00E4F52DF52CF52BF52AC20BC208C20AC2AE -:1008BF0009120CB8E4F50AF50BD2A6C2C975CDFC26 -:1008CF0075CC2AD2CAD2AFD2AD7508087509077E8A -:1008DF00007F908E0E8F0F7516007517A2750C0086 -:1008EF00750DAC7514007515CC7518007519EC9055 -:1008FF00E680E030E70E850C10850D11851412850A -:10090F001513800C851410851511850C12850D1388 -:10091F00EE54E07003020A3B752E00752F807E00A7 -:10092F007F908E308F31C374129FFF74019ECF243E -:10093F0002CF3400FEE48F298E28F527F526F52502 -:10094F00F524F523F522AF29AE28AD27AC26AB252C -:10095F00AA24A923A822C3120E17502AE52F252552 -:10096F00F582E52E3524F58374CDF0E4FAF9F8E538 -:10097F00252401F525EA3524F524E93523F523E867 -:10098F003522F52280C0E4F525F524F523F522AFB5 -:10099F0029AE28AD27AC26AB25AA24A923A822C3AC -:1009AF00120E175035AE24AF25E5312FF582E53005 -:1009BF003EF583E0FDE52F2FF582E52E3EF583ED25 -:1009CF00F0E4FAF9F8EF2401F525EA3EF524E935CC -:1009DF0023F523E83522F52280B5852E0E852F0FBE -:1009EF0074902480FF740034FFFEC3E5179FF51742 -:1009FF00E5169EF516C3E5119FF511E5109EF5104E -:100A0F00C3E5139FF513E5129EF512C3E50D9FF590 -:100A1F000DE50C9EF50CC3E5159FF515E5149EF538 -:100A2F0014C3E5199FF519E5189EF518D2E843D8B8 -:100A3F002090E668E04409F090E65CE0443DF0D297 -:100A4F00AF90E680E020E105D20E1210C490E68050 -:100A5F00E054F7F0538EF8C20B300905120616C298 -:100A6F0009300B2912124A5024C20B12114B2008C5 -:100A7F001690E682E030E704E020E1EF90E682E0B6 -:100A8F0030E604E020E0E41210F312124C120BFFD8 -:020A9F0080C70E -:010AA1002232 -:1006160090E6B9E070030206F114700302079A240B -:10062600FE700302082F24FB70030206EB1470030E -:100636000206E51470030206D91470030206DF24CD -:1006460005600302089B12124E40030208A790E6BB -:10065600BBE024FE602C14604724FD601614603154 -:1006660024067065E50E90E6B3F0E50F90E6B4F06B -:100676000208A7E51690E6B3F0E51790E6B4F00297 -:1006860008A7E51090E6B3F0E51190E6B4F002088D -:10069600A7E51290E6B3F0E51390E6B4F00208A7DA -:1006A60090E6BAE0FF12111FAA06A9077B01EA49E4 -:1006B600600DEE90E6B3F0EF90E6B4F00208A79076 -:1006C600E6A0E04401F00208A790E6A0E04401F0AD -:1006D6000208A71212160208A712123A0208A71257 -:1006E6000F4E0208A71212040208A7121250400366 -:1006F6000208A790E6B8E0247F602B14603C240231 -:100706006003020790A208E433FF25E0FFA20AE493 -:10071600334F90E740F0E4A3F090E68AF090E68B42 -:100726007402F00208A7E490E740F0A3F090E68A8E -:10073600F090E68B7402F00208A790E6BCE0547EC7 -:10074600FF7E00E0D3948040067C007D0180047C1F -:10075600007D00EC4EFEED4F24F5F582740B3EF560 -:1007660083E493FF3395E0FEEF24A1FFEE34E68F9A -:1007760082F583E0540190E740F0E4A3F090E68A26 -:10078600F090E68B7402F00208A790E6A0E0440120 -:10079600F00208A712125240030208A790E6B8E03A -:1007A60024FE601D240260030208A790E6BAE0B4A6 -:1007B6000105C2080208A790E6A0E04401F002087D -:1007C600A790E6BAE0705990E6BCE0547EFF7E0042 -:1007D600E0D3948040067C007D0180047C007D008F -:1007E600EC4EFEED4F24F5F582740B3EF583E49353 -:1007F600FF3395E0FEEF24A1FFEE34E68F82F5830A -:10080600E054FEF090E6BCE05480FF131313541F2F -:10081600FFE0540F2F90E683F0E04420F00208A793 -:1008260090E6A0E04401F08078121254507390E6EE -:10083600B8E024FE60202402706790E6BAE0B401B6 -:1008460004D208805C90E6BAE06402605490E6A0A8 -:10085600E04401F0804B90E6BCE0547EFF7E00E071 -:10086600D3948040067C007D0180047C007D00ECF2 -:100876004EFEED4F24F5F582740B3EF583E493FFAF -:100886003395E0FEEF24A1FFEE34E68F82F583E098 -:100896004401F0800C120114500790E6A0E04401D8 -:0808A600F090E6A0E04480F0B0 -:0108AE002227 -:0300330002008C3C -:04008C0053D8EF3224 -:03002B00020F13AE -:100F1300C0E0B2A5E509150970021508E509450801 -:100F2300701E75080475091FE50B6401450A7006F8 -:100F3300B2A6D2A78004B2A7D2A6750A00750B0089 -:0B0F430075CDFC75CC23C2CFD0E0328E -:100090001201000200000040B504502200000102DD -:1000A00000010A06000200000040010009022000D1 -:1000B000010100A0FA0904000002FF00000007058A -:1000C0000202000200070586020002000902200069 -:1000D000010100A0FA0904000002FF00000007056A -:1000E00002024000000705860240000004030904E4 -:1000F0000E034F0044004D00200020002000140398 -:10010000440053004F002D00320032003500300013 -:0401100020000000CB -:03004300020C00AC -:03005300020C009C -:100C00000211A8000211EE000211D8000211C0006A -:100C1000020F8500020FBC0002125600021257009C -:100C2000021258000212590002125A0002125B000E -:100C300002125C0002125D0002125E0002125F00EE -:100C400002126000021257000212610002126200DA -:100C500002126300021264000212650002126600B2 -:100C600002126700021257000212570002125700C8 -:100C7000021268000212690002126A0002126B007E -:100C800002126C0002126D0002126E0002126F005E -:100C9000021270000212710002127200021273003E -:100CA000021274000212750002127600021277001E -:080CB000021278000212790023 -:1010F30090E682E030E004E020E60B90E682E03008 -:10110300E119E030E71590E680E04401F07F147EBA -:0C11130000120ECD90E680E054FEF022A9 -:10114B0090E682E044C0F090E681F0438701000016 -:04115B00000000226E -:1010C400300E0990E680E0440AF0800790E680E064 -:1010D4004408F07FDC7E05120ECD90E65D74FFF0CF -:0F10E40090E65FF05391EF90E680E054F7F02232 -:02111F00A9071E -:10112100AE18AF198F828E83A3E064037017AD01EF -:1011310019ED7001228F828E83E07C002FFDEC3E41 -:09114100FEAF0580DF7E007F0097 -:01114A002282 -:100ECD008E3C8F3D90E600E054187012E53D2401F4 -:100EDD00FFE4353CC313F53CEF13F53D801590E66B -:100EED0000E05418FFBF100BE53D25E0F53DE53C56 -:100EFD0033F53CE53D153DAE3C7002153C4E6005AD -:060F0D0012115F80EE22CC -:10115F007400F58690FDA57C05A3E582458370F9A3 -:01116F00225D -:100CB80090E600740AF090E6017443F0000000909A -:100CC800E61274A0F0000000E490E613F0000000C3 -:100CD80090E61474E0F0000000E490E615F00000DF -:100CE8000090E6047480F00000007402F000000038 -:100CF8007406F0000000E4F000000090E60274E6DC -:100D0800F000000090E60374F8F000000090E67030 -:100D1800E04440F0000000E490E609F000000043E1 -:100D2800B20F90E61804F00000007411F000000003 -:100D380090E61A7409F0000000E490E671F090E67D -:100D480072F075B4FF75B6FFC280C20C12124290E1 -:070D5800E67AE04401F022FD -:010BFF0022D3 -:02124A00D322AD -:02124C00D322AB -:02124E00D322A9 -:100F4E0090E680E030E71590E6247402F000000091 -:100F5E00E490E625F0000000D20C8013E490E62425 -:100F6E00F000000090E6257440F0000000C20C90E6 -:070F7E00E6BAE0F51DD322E5 -:1012040090E740E51DF0E490E68AF090E68B04F068 -:02121400D322E3 -:08123A0090E6BAE0F51BD32297 -:1012160090E740E51BF0E490E68AF090E68B04F058 -:02122600D322D1 -:02125000D322A7 -:02125200D322A5 -:02125400D322A3 -:10011400750A00750B0190E678E05410FFC4540F83 -:100124004450F51C13E433F51E90E6B9E0245E70E8 -:100134000302044A24F0B40A0040030206129001A8 -:1001440049F82828730201670201830201C10202EF -:10015400030203030203460203870203C8020409DD -:100164000205D2A20CE43390E740F0E490E68AF072 -:1001740090E68B04F090E6A0E04480F0020614E4DC -:1001840090E68AF090E68BF090E6A0E020E1F9900A -:10019400E68BE0753600F53700000090E6047480C5 -:1001A400F00000007406F0000000E4F043800BD27D -:1001B40080C28090E6A0E04480F0020614E490E659 -:1001C4008AF090E68BF090E6A0E020E1F990E68BCF -:1001D400E0753600F5377F0F7E00120ECD90E745AF -:1001E400E0FF90E747E0FD90E743E0FB90E741E064 -:0F01F400F53F120E2890E6A0E04480F0020614BA -:10020300E490E68AF090E68BF090E6A0E020E1F936 -:1002130090E68BE0753600F53790E741E0F5B17570 -:100223003A0F753BA0E53B153B7002153AE53B647D -:100233000A453A70F090E742E0F5B1753A0F753B25 -:10024300A0E53B153B7002153AE53B640A453A705D -:10025300F090E743E0F5B1753A0F753BA0E53B1528 -:100263003B7002153AE53B640A453A70F090E74467 -:10027300E0F5B1753A0F753BA0E53B153B700215F0 -:100283003AE53B640A453A70F090E745E0F5B1750D -:100293003A0F753BA0E53B153B7002153AE53B640D -:1002A3000A453A70F090E746E0F5B1753A0F753BB1 -:1002B300A0E53B153B7002153AE53B640A453A70ED -:1002C300F090E747E0F5B1753A0F753BA0E53B15B4 -:1002D3003B7002153AE53B640A453A70F0E4F5B128 -:1002E300753A0F753BA0E53B153B7002153AE53BAC -:1002F300640A453A70F090E6A0E04480F0020614E8 -:10030300E490E68AF090E68BF090E6A0E020E1F935 -:1003130090E68BE0753600F53700000090E6047434 -:1003230080F0000000C2807406F0000000E4F0904A -:10033300E740E0FF7E00120ECD90E6A0E04480F09F -:10034300020614E490E68AF090E68BF090E6A0E0D3 -:1003530020E1F990E68BE0753600F5370000009058 -:10036300E6047480F00000007406F0000000E4F07E -:1003730090E740E0FF7E00120ECD90E6A0E04480BF -:10038300F0020614E490E68AF090E68BF090E6A083 -:10039300E020E1F990E68BE0753600F537000000C8 -:1003A30090E6047480F00000007406F0000000E49E -:1003B300F090E740E0FF7E00120ECD90E6A0E0440F -:1003C30080F0020614E490E68AF090E68BF090E663 -:1003D300A0E020E1F990E68BE0753600F5370000E8 -:1003E3000090E6047480F00000007406F000000042 -:1003F300E4F090E740E0FF7E00120ECD90E6A0E02F -:100403004480F0020614E490E68AF090E68BF090C4 -:10041300E6A0E020E1F990E68BE0753600F53700C1 -:10042300000090E6047480F00000007406F0000001 -:1004330000E4F090E740E0FF7E00120ECD90E6A0CE -:10044300E04480F002061490E6BAE0753200F5331A -:10045300A3E0FEE4EE423290E6BEE0753400F535EB -:10046300A3E0FEE4EE423490E6B8E064C060030229 -:10047300053AE5354534700302061490E6A0E02002 -:10048300E1F9C3E5359440E53494005008853436EA -:10049300853537800675360075374090E6B9E0B488 -:1004A300A335E4F538F539C3E5399537E53895369D -:1004B3005060E5332539F582E5323538F583E0FFC1 -:1004C30074402539F582E434E7F583EFF00539E527 -:1004D300397002053880D0E4F538F539C3E539952C -:1004E30037E5389536501874402539F582E434E7FA -:1004F300F58374CDF00539E5397002053880DDAD3B -:10050300377AE779407EE77F40AB07AF33AE3212ED -:100513000BAAE490E68AF090E68BE537F02533F5F5 -:1005230033E5363532F532C3E5359537F535E53400 -:100533009536F53402047590E6B8E0644060030232 -:100543000614E53545347003020614E490E68AF098 -:1005530090E68BF090E6A0E020E1F990E68BE07561 -:100563003600F53790E6B9E0B4A335E4F538F5394C -:10057300C3E5399537E5389536503874402539F554 -:1005830082E434E7F583E0FFE5332539F582E5328C -:100593003538F583EFF00539E5397002053880D039 -:1005A300AD377AE779407EE77F40AB07AF33AE32B2 -:1005B300121029E5372533F533E5363532F532C3E5 -:1005C300E5359537F535E5349536F534020545E4DB -:1005D30090E68AF090E68BF090E6A0E020E1F990B7 -:1005E300E68BE0753600F53700000090E604748072 -:1005F300F00000007406F0000000E4F090E740E033 -:10060300FF7E00120ECD90E6A0E04480F08002D37E -:0206130022C300 -:0106150022C2 -:1011A800C0E0C083C082D2095391EF90E65D74011C -:0811B800F0D082D083D0E032B8 -:1011D800C0E0C083C0825391EF90E65D7404F0D004 -:0611E80082D083D0E0324A -:1011EE00C0E0C083C0825391EF90E65D7402F0D0F0 -:0611FE0082D083D0E03234 -:100F8500C0E0C083C08290E680E030E70E850C109B -:100F9500850D11851412851513800C851410851582 -:100FA50011850C12850D135391EF90E65D7410F0C9 -:070FB500D082D083D0E032AE -:1011C000C0E0C083C082D20B5391EF90E65D7408FB -:0811D000F0D082D083D0E032A0 -:100FBC00C0E0C083C08290E680E030E70E850C1064 -:100FCC00850D11851412851513800C85141085154B -:100FDC0011850C12850D135391EF90E65D7420F082 -:070FEC00D082D083D0E03277 -:011256003265 -:011257003264 -:011258003263 -:011259003262 -:01125A003261 -:01125B003260 -:01125C00325F -:01125D00325E -:01125E00325D -:01125F00325C -:01126000325B -:01126100325A -:011262003259 -:011263003258 -:011264003257 -:011265003256 -:011266003255 -:011267003254 -:011268003253 -:011269003252 -:01126A003251 -:01126B003250 -:01126C00324F -:01126D00324E -:01126E00324D -:01126F00324C -:01127000324B -:01127100324A -:011272003249 -:011273003248 -:011274003247 -:011275003246 -:011276003245 -:011277003244 -:011278003243 -:011279003242 -:10122800AD071FED600BE4FEEEC3947850F20E801C -:01123800F7BE -:011239002292 -:040D5F00A907AB0530 -:100D63007F01121228D2A27F01121228C2A2E4FA32 -:100D7300E9A802088002C333D8FCFC5304807F0136 -:100D8300121228D2A1BC8004D2A08002C2A07F018B -:100D9300121228C2A10ABA08D7E4FAEBA802088003 -:100DA30002C333D8FCFC5304807F01121228D2A162 -:100DB300BC8004D2A08002C2A07F01121228C2A16B -:040DC3000ABA08D789 -:010DC7002209 -:040E2800AE07AC0560 -:100E2C00EEC4540F4410FFEEC454F0FDECC4540F48 -:100E3C004450F540ECC454F0F541EBC4540F4490CD -:100E4C00F542EBC454F0F543E53FC4540F44D0F5E0 -:100E5C0044E53FC454F0F545C2A0C2A1C2A2120D94 -:100E6C005FAD41AF40120D5FAD43AF42120D5FADB0 -:060E7C0045AF44120D5FBA -:010E8200224D -:060E8300AB07AA06AC0556 -:100E8900E4FDE51E6010EA7E000DEE2400F582E423 -:100E99003418F583EAF0EBAE050D74002EF582E403 -:100EA9003418F583EBF0AF050D74002FF582E434A7 -:100EB90018F583ECF07A187B00AF1C12118CAF1C6B -:030EC900120FF312 -:010ECC002203 -:0A1029008E3C8F3D8D3E8A3F8B40C8 -:10103300E4F541E541C3953E5020053DE53DAE3C19 -:101043007002053C14FFE5402541F582E4353FF588 -:0A10530083E0FD120E83054180D9F1 -:01105D002270 -:0A0BAA008E3C8F3D8D3E8A3F8B404C -:100BB400E4FDF541E51E6012E53CFF7E000DEE24E8 -:100BC40000F582E43418F583EFF0E53DAE050D74CD -:100BD400002EF582E43418F583E53DF07A187B00A5 -:100BE400AF1C12118CAB40AA3FAD3EAF1C1211706A -:010BF40022DE -:08124200E4F54BD2E9D2AF2222 -:100FF30090E678E020E6F9C2E990E678E04480F0F4 -:10100300EF25E090E679F090E678E030E0F990E6BD -:1010130078E04440F090E678E020E6F990E678E066 -:0610230030E1D6D2E92203 -:10109200A90790E678E020E6F9E54B702390E67820 -:1010A200E04480F0E925E090E679F08D46AF03A9AF -:1010B200077547018A488949E4F54A754B01D322ED -:0210C200C32247 -:10105E00A90790E678E020E6F9E54B702590E67852 -:10106E00E04480F0E925E0440190E679F08D46AF4A -:10107E0003A9077547018A488949E4F54A754B0368 -:04108E00D322C32284 -:03004B00020AA204 -:100AA200C0E0C083C082C085C084C086758600C095 -:100AB200D075D000C000C001C002C003C006C0078C -:100AC20090E678E030E206754B06020B8C90E678F1 -:100AD200E020E10CE54B64026006754B07020B8CCB -:100AE200E54B24FE605F14603624FE7003020B7D2A -:100AF20024FC7003020B8924086003020B8CAB47B1 -:100B0200AA48A949AF4A054A8F82758300120DC8C7 -:100B120090E679F0E54A65467070754B05806B90FA -:100B2200E679E0AB47AA48A949AE4A8E82758300AE -:100B3200120DF5754B02E5466401704E90E678E0C1 -:100B42004420F08045E54624FEB54A0790E678E069 -:100B52004420F0E54614B54A0A90E678E04440F0B5 -:100B6200754B0090E679E0AB47AA48A949AE4A8E98 -:100B720082758300120DF5054A800F90E678E044F5 -:100B820040F0754B008003754B005391DFD007D0C6 -:100B920006D003D002D001D000D0D0D086D084D0ED -:080BA20085D082D083D0E0323F -:1011700012105EE54B24FA600E146006240770F32B -:0C118000D322E4F54BD322E4F54BD3223C -:10118C00121092E54B24FA600E146006240770F3DB -:0C119C00D322E4F54BD322E4F54BD32220 -:030000000200807B -:0C008000787FE4F6D8FD75814B0208AFD4 -:100DC800BB010CE58229F582E5833AF583E02250E0 -:100DD80006E92582F8E622BBFE06E92582F8E2222A -:0D0DE800E58229F582E5833AF583E4932244 -:100DF500F8BB010DE58229F582E5833AF583E8F034 -:100E0500225006E92582C8F622BBFE05E92582C8DF -:020E1500F222C7 -:100E1700EB9FF5F0EA9E42F0E99D42F0E89C45F031 -:010E270022A8 -:00000001FF diff --git a/openhantek/res/firmware/dso2250x86-loader.hex b/openhantek/res/firmware/dso2250x86-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso2250x86-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso5200ax86-firmware.hex b/openhantek/res/firmware/dso5200ax86-firmware.hex deleted file mode 100644 index 692219ac..00000000 --- a/openhantek/res/firmware/dso5200ax86-firmware.hex +++ /dev/null @@ -1,377 +0,0 @@ -:0A0CF60000010202030304040505D7 -:10090900E4F52DF52CF52BF52AC20BC208C20AC253 -:1009190009120C04E4F50AF50BD2A6C2C975CDFC7F -:1009290075CC2AD2CAD2AFD2AD7508087509077E2F -:10093900007F908E0E8F0F7516007517A2750C002B -:10094900750DAC7514007515CC7518007519EC90FA -:10095900E680E030E70E850C10850D1185141285AF -:100969001513800C851410851511850C12850D132E -:10097900EE54E07003020A95752E00752F807E00F3 -:100989007F908E308F31C374129FFF74019ECF24E4 -:1009990002CF3400FEE48F298E28F527F526F525A8 -:1009A900F524F523F522AF29AE28AD27AC26AB25D2 -:1009B900AA24A923A822C3120E70502AE52F25259F -:1009C900F582E52E3524F58374CDF0E4FAF9F8E5DE -:1009D900252401F525EA3524F524E93523F523E80D -:1009E9003522F52280C0E4F525F524F523F522AF5B -:1009F90029AE28AD27AC26AB25AA24A923A822C352 -:100A0900120E705035AE24AF25E5312FF582E53051 -:100A19003EF583E0FDE52F2FF582E52E3EF583EDCA -:100A2900F0E4FAF9F8EF2401F525EA3EF524E93571 -:100A390023F523E83522F52280B5852E0E852F0F63 -:100A490074902480FF740034FFFEC3E5179FF517E7 -:100A5900E5169EF516C3E5119FF511E5109EF510F3 -:100A6900C3E5139FF513E5129EF512C3E50D9FF536 -:100A79000DE50C9EF50CC3E5159FF515E5149EF5DE -:100A890014C3E5199FF519E5189EF518D2E843D85E -:100A99002090E668E04409F090E65CE0443DF0D23D -:100AA900AF90E680E020E105D20E12110990E680B0 -:100AB900E054F7F0538EF8C20B300905120670C2E4 -:100AC90009300B2912128F5024C20B1211902008E1 -:100AD9001690E682E030E704E020E1EF90E682E05C -:100AE90030E604E020E0E412113812129112129B50 -:020AF90080C7B4 -:010AFB0022D8 -:1006700090E6B9E0700302074B1470030207F424FC -:10068000FE700302088924FB7003020745147003FF -:1006900002073F1470030207331470030207392462 -:1006A0000560030208F5121293400302090190E667 -:1006B000BBE024FE602C14604724FD6016146031FA -:1006C00024067065E50E90E6B3F0E50F90E6B4F011 -:1006D000020901E51690E6B3F0E51790E6B4F002E2 -:1006E0000901E51090E6B3F0E51190E6B4F00209D7 -:1006F00001E51290E6B3F0E51390E6B4F0020901CB -:1007000090E6BAE0FF121164AA06A9077B01EA4944 -:10071000600DEE90E6B3F0EF90E6B4F002090190C0 -:10072000E6A0E04401F002090190E6A0E04401F0F7 -:1007300002090112125B02090112127F0209011261 -:100740000F93020901121249020901121295400386 -:1007500002090190E6B8E0247F602B14603C24027B -:1007600060030207EAA208E433FF25E0FFA20AE4DF -:10077000334F90E740F0E4A3F090E68AF090E68BE8 -:100780007402F0020901E490E740F0A3F090E68AD9 -:10079000F090E68B7402F002090190E6BCE0547E12 -:1007A000FF7E00E0D3948040067C007D0180047CC5 -:1007B000007D00EC4EFEED4F24F6F582740C3EF504 -:1007C00083E493FF3395E0FEEF24A1FFEE34E68F40 -:1007D00082F583E0540190E740F0E4A3F090E68ACC -:1007E000F090E68B7402F002090190E6A0E044016B -:1007F000F0020901121297400302090190E6B8E0E5 -:1008000024FE601D2402600302090190E6BAE0B4F0 -:100810000105C20802090190E6A0E04401F00209C6 -:100820000190E6BAE0705990E6BCE0547EFF7E008D -:10083000E0D3948040067C007D0180047C007D0034 -:10084000EC4EFEED4F24F6F582740C3EF583E493F6 -:10085000FF3395E0FEEF24A1FFEE34E68F82F583AF -:10086000E054FEF090E6BCE05480FF131313541FD5 -:10087000FFE0540F2F90E683F0E04420F0020901DE -:1008800090E6A0E04401F08078121299507390E64F -:10089000B8E024FE60202402706790E6BAE0B4015C -:1008A00004D208805C90E6BAE06402605490E6A04E -:1008B000E04401F0804B90E6BCE0547EFF7E00E017 -:1008C000D3948040067C007D0180047C007D00EC98 -:1008D0004EFEED4F24F6F582740C3EF583E493FF53 -:1008E0003395E0FEEF24A1FFEE34E68F82F583E03E -:1008F0004401F0800C120114500790E6A0E044017E -:08090000F090E6A0E04480F055 -:0109080022CC -:0300330002008C3C -:04008C0053D8EF3224 -:03002B00020F566B -:100F5600C0E0B2A5E509150970021508E5094508BE -:100F66007020B28175080475091FE50B6401450AF6 -:100F76007006B2A6D2A78004B2A7D2A6750A0075DB -:0D0F86000B0075CDFC75CC23C2CFD0E0323E -:100090001201000200000040B5040A5200000102F3 -:1000A00000010A06000200000040010009022000D1 -:1000B000010100A0FA0904000002FF00000007058A -:1000C0000202000200070586020002000902200069 -:1000D000010100A0FA0904000002FF00000007056A -:1000E00002024000000705860240000004030904E4 -:1000F0000E034F0044004D00200020002000140398 -:10010000440053004F002D00350032003000300015 -:0401100041000000AA -:03004300020D00AB -:03005300020D009B -:100D00000211ED000212330002121D000212050052 -:100D1000020FCA000210010002129C0002129D0084 -:100D200002129E0002129F000212A0000212A100F5 -:100D30000212A2000212A3000212A4000212A500D5 -:100D40000212A60002129D000212A7000212A800C1 -:100D50000212A9000212AA000212AB000212AC0099 -:100D60000212AD0002129D0002129D0002129D00AF -:100D70000212AE000212AF000212B0000212B10065 -:100D80000212B2000212B3000212B4000212B50045 -:100D90000212B6000212B7000212B8000212B90025 -:100DA0000212BA000212BB000212BC000212BD0005 -:080DB0000212BE000212BF0096 -:1011380090E682E030E004E020E60B90E682E030C2 -:10114800E119E030E71590E680E04401F07F147E75 -:0C11580000120ECB90E680E054FEF02266 -:1011900090E682E044C0F090E681F04387010000D1 -:0411A0000000002229 -:10110900300E0990E680E0440AF0800790E680E01E -:101119004408F07FDC7E05120ECB90E65D74FFF08B -:0F11290090E65FF05391EF90E680E054F7F022EC -:02116400A907D9 -:10116600AE18AF198F828E83A3E064037017AD01AA -:1011760019ED7001228F828E83E07C002FFDEC3EFC -:09118600FEAF0580DF7E007F0052 -:01118F00223D -:100ECB008E3C8F3D90E600E054187012E53D2401F6 -:100EDB00FFE4353CC313F53CEF13F53D801590E66D -:100EEB0000E05418FFBF100BE53D25E0F53DE53C58 -:100EFB0033F53CE53D153DAE3C7002153C4E6005AF -:060F0B001211A480EE2289 -:1011A4007400F58690FDA57C05A3E582458370F95E -:0111B4002218 -:100C040090E600740AF090E6017443F0000000904E -:100C1400E61274A0F0000000E490E613F000000077 -:100C240090E61474E0F0000000E490E615F0000093 -:100C34000090E6047480F00000007402F0000000EC -:100C44007406F0000000E4F000000090E60274E690 -:100C5400F000000090E60374F8F000000090E670E5 -:100C6400E04440F0000000E490E609F00000004396 -:100C7400B20F90E61804F00000007411F0000000B8 -:100C840090E61A7409F0000000E490E671F090E632 -:100C940072F075B4FF75B6FFC280C20C1212879051 -:070CA400E67AE04401F022B2 -:01129B002230 -:02128F00D32268 -:02129100D32266 -:02129300D32264 -:100F930090E680E030E71590E6247402F00000004C -:100FA300E490E625F0000000D20C8013E490E624E0 -:100FB300F000000090E6257440F0000000C20C90A1 -:070FC300E6BAE0F51DD322A0 -:1012490090E740E51DF0E490E68AF090E68B04F023 -:02125900D3229E -:08127F0090E6BAE0F51BD32252 -:10125B0090E740E51BF0E490E68AF090E68B04F013 -:02126B00D3228C -:02129500D32262 -:02129700D32260 -:02129900D3225E -:10011400750A00750B0190E678E05410FFC4540F83 -:100124004450F51C13E433F51E90E6B9E0245E70E8 -:10013400030204A424F0B40B00400302066C9001F3 -:1001440049F828287302016A0201860202030202A6 -:100154005D02035D0203A00203E1020422020463C0 -:1001640002062C0201C4A20CE43390E740F0E490B0 -:10017400E68AF090E68B04F090E6A0E04480F0027A -:10018400066EE490E68AF090E68BF090E6A0E0201C -:10019400E1F990E68BE0753600F53700000090E653 -:1001A400047480F00000007406F0000000E4F043E2 -:1001B400800BD280C28090E6A0E04480F002066EFC -:1001C400E490E68AF090E68BF090E6A0E020E1F976 -:1001D40090E68BE0753600F5377F0F7E00120ECB6C -:1001E40090E746E0540F4450FFA3E0FDC2A0C2A133 -:1001F400C2A2120DB890E6A0E04480F002066EE4BC -:1002040090E68AF090E68BF090E6A0E020E1F99089 -:10021400E68BE0753600F5377F0F7E00120ECB902B -:10022400E745E0FF90E744E0FD90E747E0FB90E717 -:1002340046E0F53F90E743E0F54090E742E0F541C2 -:1002440090E741E0F54290E740E0F543120F11904A -:09025400E6A0E04480F002066E11 -:10025D00E490E68AF090E68BF090E6A0E020E1F9DC -:10026D0090E68BE0753600F53790E741E0F5B17516 -:10027D003A0F753BA0E53B153B7002153AE53B6423 -:10028D000A453A70F090E742E0F5B1753A0F753BCB -:10029D00A0E53B153B7002153AE53B640A453A7003 -:1002AD00F090E743E0F5B1753A0F753BA0E53B15CE -:1002BD003B7002153AE53B640A453A70F090E7440D -:1002CD00E0F5B1753A0F753BA0E53B153B70021596 -:1002DD003AE53B640A453A70F090E745E0F5B175B3 -:1002ED003A0F753BA0E53B153B7002153AE53B64B3 -:1002FD000A453A70F090E746E0F5B1753A0F753B57 -:10030D00A0E53B153B7002153AE53B640A453A7092 -:10031D00F090E747E0F5B1753A0F753BA0E53B1559 -:10032D003B7002153AE53B640A453A70F0E4F5B1CD -:10033D00753A0F753BA0E53B153B7002153AE53B51 -:10034D00640A453A70F090E6A0E04480F002066E33 -:10035D00E490E68AF090E68BF090E6A0E020E1F9DB -:10036D0090E68BE0753600F53700000090E60474DA -:10037D0080F0000000C2807406F0000000E4F090F0 -:10038D00E740E0FF7E00120ECB90E6A0E04480F047 -:10039D0002066EE490E68AF090E68BF090E6A0E01F -:1003AD0020E1F990E68BE0753600F53700000090FE -:1003BD00E6047480F00000007406F0000000E4F024 -:1003CD0090E740E0FF7E00120ECB90E6A0E0448067 -:1003DD00F002066EE490E68AF090E68BF090E6A0CF -:1003ED00E020E1F990E68BE0753600F5370000006E -:1003FD0090E6047480F00000007406F0000000E444 -:10040D00F090E740E0FF7E00120ECB90E6A0E044B6 -:10041D0080F002066EE490E68AF090E68BF090E6AE -:10042D00A0E020E1F990E68BE0753600F53700008D -:10043D000090E6047480F00000007406F0000000E7 -:10044D00E4F090E740E0FF7E00120ECB90E6A0E0D6 -:10045D004480F002066EE490E68AF090E68BF09010 -:10046D00E6A0E020E1F990E68BE0753600F5370067 -:10047D00000090E6047480F00000007406F00000A7 -:10048D0000E4F090E740E0FF7E00120ECB90E6A076 -:10049D00E04480F002066E90E6BAE0753200F53366 -:1004AD00A3E0FEE4EE423290E6BEE0753400F53591 -:1004BD00A3E0FEE4EE423490E6B8E064C0600302CF -:1004CD000594E5354534700302066E90E6A0E020F4 -:1004DD00E1F9C3E5359440E5349400500885343690 -:1004ED00853537800675360075374090E6B9E0B42E -:1004FD00A335E4F538F539C3E5399537E538953643 -:10050D005060E5332539F582E5323538F583E0FF66 -:10051D0074402539F582E434E7F583EFF00539E5CC -:10052D00397002053880D0E4F538F539C3E53995D1 -:10053D0037E5389536501874402539F582E434E79F -:10054D00F58374CDF00539E5397002053880DDADE0 -:10055D00377AE779407EE77F40AB07AF33AE321293 -:10056D000CABE490E68AF090E68BE537F02533F599 -:10057D0033E5363532F532C3E5359537F535E534A6 -:10058D009536F5340204CF90E6B8E064406003027E -:10059D00066EE5354534700302066EE490E68AF08A -:1005AD0090E68BF090E6A0E020E1F990E68BE07507 -:1005BD003600F53790E6B9E0B4A335E4F538F539F2 -:1005CD00C3E5399537E5389536503874402539F5FA -:1005DD0082E434E7F583E0FFE5332539F582E53232 -:1005ED003538F583EFF00539E5397002053880D0DF -:1005FD00AD377AE779407EE77F40AB07AF33AE3258 -:10060D0012106EE5372533F533E5363532F532C345 -:10061D00E5359537F535E5349536F53402059FE426 -:10062D0090E68AF090E68BF090E6A0E020E1F9905C -:10063D00E68BE0753600F53700000090E604748017 -:10064D00F00000007406F0000000E4F090E740E0D8 -:10065D00FF7E00120ECB90E6A0E04480F08002D326 -:02066D0022C3A6 -:01066F002268 -:1011ED00C0E0C083C082D2095391EF90E65D7401D7 -:0811FD00F0D082D083D0E03273 -:10121D00C0E0C083C0825391EF90E65D7404F0D0BE -:06122D0082D083D0E03204 -:10123300C0E0C083C0825391EF90E65D7402F0D0AA -:0612430082D083D0E032EE -:100FCA00C0E0C083C08290E680E030E70E850C1056 -:100FDA00850D11851412851513800C85141085153D -:100FEA0011850C12850D135391EF90E65D7410F084 -:070FFA00D082D083D0E03269 -:10120500C0E0C083C082D20B5391EF90E65D7408B5 -:08121500F0D082D083D0E0325A -:10100100C0E0C083C08290E680E030E70E850C101E -:10101100850D11851412851513800C851410851505 -:1010210011850C12850D135391EF90E65D7420F03C -:07103100D082D083D0E03231 -:01129C00321F -:01129D00321E -:01129E00321D -:01129F00321C -:0112A000321B -:0112A100321A -:0112A2003219 -:0112A3003218 -:0112A4003217 -:0112A5003216 -:0112A6003215 -:0112A7003214 -:0112A8003213 -:0112A9003212 -:0112AA003211 -:0112AB003210 -:0112AC00320F -:0112AD00320E -:0112AE00320D -:0112AF00320C -:0112B000320B -:0112B100320A -:0112B2003209 -:0112B3003208 -:0112B4003207 -:0112B5003206 -:0112B6003205 -:0112B7003204 -:0112B8003203 -:0112B9003202 -:0112BA003201 -:0112BB003200 -:0112BC0032FF -:0112BD0032FE -:0112BE0032FD -:0112BF0032FC -:10126D00AD071FED600BE4FEEEC3947850F20E80D7 -:01127D00F779 -:01127E00224D -:040DB800A907AB05D7 -:100DBC007F0112126DD2A27F0112126DC2A2E4FA4F -:100DCC00E9A802088002C333D8FCFC5304807F01DD -:100DDC0012126DD2A1BC8004D2A08002C2A07F01ED -:100DEC0012126DC2A10ABA08D7E4FAEBA802088065 -:100DFC0002C333D8FCFC5304807F0112126DD2A1C4 -:100E0C00BC8004D2A08002C2A07F0112126DC2A1CC -:040E1C000ABA08D72F -:010E200022AF -:020F1100AE0729 -:100F1300ED540F4410FFEEFDE53F540F4450F544EC -:100F2300EBF545E541540F4490F546E540F547E5BB -:100F330043540F44D0F548E542F549C2A0C2A1C2CB -:100F4300A2120DB8AD47AF46120DB8AD49AF481266 -:020F53000DB8D7 -:010F55002279 -:060E8100AB07AA06AC0558 -:100E8700E4FDE51E6010EA7E000DEE2400F582E425 -:100E97003418F583EAF0EBAE050D74002EF582E405 -:100EA7003418F583EBF0AF050D74002FF582E434A9 -:100EB70018F583ECF07A187B00AF1C1211D1AF1C28 -:030EC700121038CE -:010ECA002205 -:0A106E008E3C8F3D8D3E8A3F8B4083 -:10107800E4F541E541C3953E5020053DE53DAE3CD4 -:101088007002053C14FFE5402541F582E4353FF543 -:0A10980083E0FD120E81054180D9AE -:0110A200222B -:0A0CAB008E3C8F3D8D3E8A3F8B404A -:100CB500E4FDF541E51E6012E53CFF7E000DEE24E6 -:100CC50000F582E43418F583EFF0E53DAE050D74CB -:100CD500002EF582E43418F583E53DF07A187B00A3 -:100CE500AF1C1211D1AB40AA3FAD3EAF1C1211B5DE -:010CF50022DC -:08128700E4F54FD2E9D2AF22D9 -:1010380090E678E020E6F9C2E990E678E04480F0AE -:10104800EF25E090E679F090E678E030E0F990E678 -:1010580078E04440F090E678E020E6F990E678E021 -:0610680030E1D6D2E922BE -:1010D700A90790E678E020E6F9E54F702390E678D7 -:1010E700E04480F0E925E090E679F08D4AAF03A966 -:1010F70007754B018A4C894DE4F54E754F01D32294 -:02110700C32201 -:1010A300A90790E678E020E6F9E54F702590E67809 -:1010B300E04480F0E925E0440190E679F08D4AAF01 -:1010C30003A907754B018A4C894DE4F54E754F030F -:0410D300D322C3223F -:03004B00020AFCAA -:100AFC00C0E0C083C082C085C084C086758600C03B -:100B0C00D075D000C000C001C002C003C006C00731 -:100B1C0090E678E030E206754F06020BE690E67838 -:100B2C00E020E10CE54F64026006754F07020BE60E -:100B3C00E54F24FE605F14603624FE7003020BD771 -:100B4C0024FC7003020BE324086003020BE6AB4B9E -:100B5C00AA4CA94DAF4E054E8F82758300120E2103 -:100B6C0090E679F0E54E654A7070754F05806B9094 -:100B7C00E679E0AB4BAA4CA94DAE4E8E8275830044 -:100B8C00120E4E754F02E54A6401704E90E678E005 -:100B9C004420F08045E54A24FEB54E0790E678E007 -:100BAC004420F0E54A14B54E0A90E678E04440F053 -:100BBC00754F0090E679E0AB4BAA4CA94DAE4E8E2A -:100BCC0082758300120E4E054E800F90E678E0443D -:100BDC0040F0754F008003754F005391DFD007D064 -:100BEC0006D003D002D001D000D0D0D086D084D093 -:080BFC0085D082D083D0E032E5 -:1011B5001210A3E54F24FA600E146006240770F39D -:0C11C500D322E4F54FD322E4F54FD322EF -:1011D1001210D7E54F24FA600E146006240770F34D -:0C11E100D322E4F54FD322E4F54FD322D3 -:030000000200807B -:0C008000787FE4F6D8FD75814F02090975 -:100E2100BB010CE58229F582E5833AF583E0225086 -:100E310006E92582F8E622BBFE06E92582F8E222D0 -:0D0E4100E58229F582E5833AF583E49322EA -:100E4E00F8BB010DE58229F582E5833AF583E8F0DA -:100E5E00225006E92582C8F622BBFE05E92582C886 -:020E6E00F2226E -:100E7000EB9FF5F0EA9E42F0E99D42F0E89C45F0D8 -:010E8000224F -:00000001FF diff --git a/openhantek/res/firmware/dso5200ax86-loader.hex b/openhantek/res/firmware/dso5200ax86-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso5200ax86-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso5200x86-firmware.hex b/openhantek/res/firmware/dso5200x86-firmware.hex deleted file mode 100644 index 9cd64dcd..00000000 --- a/openhantek/res/firmware/dso5200x86-firmware.hex +++ /dev/null @@ -1,377 +0,0 @@ -:0A0CF60000010202030304040505D7 -:10090900E4F52DF52CF52BF52AC20BC208C20AC253 -:1009190009120C04E4F50AF50BD2A6C2C975CDFC7F -:1009290075CC2AD2CAD2AFD2AD7508087509077E2F -:10093900007F908E0E8F0F7516007517A2750C002B -:10094900750DAC7514007515CC7518007519EC90FA -:10095900E680E030E70E850C10850D1185141285AF -:100969001513800C851410851511850C12850D132E -:10097900EE54E07003020A95752E00752F807E00F3 -:100989007F908E308F31C374129FFF74019ECF24E4 -:1009990002CF3400FEE48F298E28F527F526F525A8 -:1009A900F524F523F522AF29AE28AD27AC26AB25D2 -:1009B900AA24A923A822C3120E70502AE52F25259F -:1009C900F582E52E3524F58374CDF0E4FAF9F8E5DE -:1009D900252401F525EA3524F524E93523F523E80D -:1009E9003522F52280C0E4F525F524F523F522AF5B -:1009F90029AE28AD27AC26AB25AA24A923A822C352 -:100A0900120E705035AE24AF25E5312FF582E53051 -:100A19003EF583E0FDE52F2FF582E52E3EF583EDCA -:100A2900F0E4FAF9F8EF2401F525EA3EF524E93571 -:100A390023F523E83522F52280B5852E0E852F0F63 -:100A490074902480FF740034FFFEC3E5179FF517E7 -:100A5900E5169EF516C3E5119FF511E5109EF510F3 -:100A6900C3E5139FF513E5129EF512C3E50D9FF536 -:100A79000DE50C9EF50CC3E5159FF515E5149EF5DE -:100A890014C3E5199FF519E5189EF518D2E843D85E -:100A99002090E668E04409F090E65CE0443DF0D23D -:100AA900AF90E680E020E105D20E12110990E680B0 -:100AB900E054F7F0538EF8C20B300905120670C2E4 -:100AC90009300B2912128F5024C20B1211902008E1 -:100AD9001690E682E030E704E020E1EF90E682E05C -:100AE90030E604E020E0E412113812129112129B50 -:020AF90080C7B4 -:010AFB0022D8 -:1006700090E6B9E0700302074B1470030207F424FC -:10068000FE700302088924FB7003020745147003FF -:1006900002073F1470030207331470030207392462 -:1006A0000560030208F5121293400302090190E667 -:1006B000BBE024FE602C14604724FD6016146031FA -:1006C00024067065E50E90E6B3F0E50F90E6B4F011 -:1006D000020901E51690E6B3F0E51790E6B4F002E2 -:1006E0000901E51090E6B3F0E51190E6B4F00209D7 -:1006F00001E51290E6B3F0E51390E6B4F0020901CB -:1007000090E6BAE0FF121164AA06A9077B01EA4944 -:10071000600DEE90E6B3F0EF90E6B4F002090190C0 -:10072000E6A0E04401F002090190E6A0E04401F0F7 -:1007300002090112125B02090112127F0209011261 -:100740000F93020901121249020901121295400386 -:1007500002090190E6B8E0247F602B14603C24027B -:1007600060030207EAA208E433FF25E0FFA20AE4DF -:10077000334F90E740F0E4A3F090E68AF090E68BE8 -:100780007402F0020901E490E740F0A3F090E68AD9 -:10079000F090E68B7402F002090190E6BCE0547E12 -:1007A000FF7E00E0D3948040067C007D0180047CC5 -:1007B000007D00EC4EFEED4F24F6F582740C3EF504 -:1007C00083E493FF3395E0FEEF24A1FFEE34E68F40 -:1007D00082F583E0540190E740F0E4A3F090E68ACC -:1007E000F090E68B7402F002090190E6A0E044016B -:1007F000F0020901121297400302090190E6B8E0E5 -:1008000024FE601D2402600302090190E6BAE0B4F0 -:100810000105C20802090190E6A0E04401F00209C6 -:100820000190E6BAE0705990E6BCE0547EFF7E008D -:10083000E0D3948040067C007D0180047C007D0034 -:10084000EC4EFEED4F24F6F582740C3EF583E493F6 -:10085000FF3395E0FEEF24A1FFEE34E68F82F583AF -:10086000E054FEF090E6BCE05480FF131313541FD5 -:10087000FFE0540F2F90E683F0E04420F0020901DE -:1008800090E6A0E04401F08078121299507390E64F -:10089000B8E024FE60202402706790E6BAE0B4015C -:1008A00004D208805C90E6BAE06402605490E6A04E -:1008B000E04401F0804B90E6BCE0547EFF7E00E017 -:1008C000D3948040067C007D0180047C007D00EC98 -:1008D0004EFEED4F24F6F582740C3EF583E493FF53 -:1008E0003395E0FEEF24A1FFEE34E68F82F583E03E -:1008F0004401F0800C120114500790E6A0E044017E -:08090000F090E6A0E04480F055 -:0109080022CC -:0300330002008C3C -:04008C0053D8EF3224 -:03002B00020F566B -:100F5600C0E0B2A5E509150970021508E5094508BE -:100F66007020B28175080475091FE50B6401450AF6 -:100F76007006B2A6D2A78004B2A7D2A6750A0075DB -:0D0F86000B0075CDFC75CC23C2CFD0E0323E -:100090001201000200000040B504005200000102FD -:1000A00000010A06000200000040010009022000D1 -:1000B000010100A0FA0904000002FF00000007058A -:1000C0000202000200070586020002000902200069 -:1000D000010100A0FA0904000002FF00000007056A -:1000E00002024000000705860240000004030904E4 -:1000F0000E034F0044004D00200020002000140398 -:10010000440053004F002D00350032003000300015 -:0401100020000000CB -:03004300020D00AB -:03005300020D009B -:100D00000211ED000212330002121D000212050052 -:100D1000020FCA000210010002129C0002129D0084 -:100D200002129E0002129F000212A0000212A100F5 -:100D30000212A2000212A3000212A4000212A500D5 -:100D40000212A60002129D000212A7000212A800C1 -:100D50000212A9000212AA000212AB000212AC0099 -:100D60000212AD0002129D0002129D0002129D00AF -:100D70000212AE000212AF000212B0000212B10065 -:100D80000212B2000212B3000212B4000212B50045 -:100D90000212B6000212B7000212B8000212B90025 -:100DA0000212BA000212BB000212BC000212BD0005 -:080DB0000212BE000212BF0096 -:1011380090E682E030E004E020E60B90E682E030C2 -:10114800E119E030E71590E680E04401F07F147E75 -:0C11580000120ECB90E680E054FEF02266 -:1011900090E682E044C0F090E681F04387010000D1 -:0411A0000000002229 -:10110900300E0990E680E0440AF0800790E680E01E -:101119004408F07FDC7E05120ECB90E65D74FFF08B -:0F11290090E65FF05391EF90E680E054F7F022EC -:02116400A907D9 -:10116600AE18AF198F828E83A3E064037017AD01AA -:1011760019ED7001228F828E83E07C002FFDEC3EFC -:09118600FEAF0580DF7E007F0052 -:01118F00223D -:100ECB008E3C8F3D90E600E054187012E53D2401F6 -:100EDB00FFE4353CC313F53CEF13F53D801590E66D -:100EEB0000E05418FFBF100BE53D25E0F53DE53C58 -:100EFB0033F53CE53D153DAE3C7002153C4E6005AF -:060F0B001211A480EE2289 -:1011A4007400F58690FDA57C05A3E582458370F95E -:0111B4002218 -:100C040090E600740AF090E6017443F0000000904E -:100C1400E61274A0F0000000E490E613F000000077 -:100C240090E61474E0F0000000E490E615F0000093 -:100C34000090E6047480F00000007402F0000000EC -:100C44007406F0000000E4F000000090E60274E690 -:100C5400F000000090E60374F8F000000090E670E5 -:100C6400E04440F0000000E490E609F00000004396 -:100C7400B20F90E61804F00000007411F0000000B8 -:100C840090E61A7409F0000000E490E671F090E632 -:100C940072F075B4FF75B6FFC280C20C1212879051 -:070CA400E67AE04401F022B2 -:01129B002230 -:02128F00D32268 -:02129100D32266 -:02129300D32264 -:100F930090E680E030E71590E6247402F00000004C -:100FA300E490E625F0000000D20C8013E490E624E0 -:100FB300F000000090E6257440F0000000C20C90A1 -:070FC300E6BAE0F51DD322A0 -:1012490090E740E51DF0E490E68AF090E68B04F023 -:02125900D3229E -:08127F0090E6BAE0F51BD32252 -:10125B0090E740E51BF0E490E68AF090E68B04F013 -:02126B00D3228C -:02129500D32262 -:02129700D32260 -:02129900D3225E -:10011400750A00750B0190E678E05410FFC4540F83 -:100124004450F51C13E433F51E90E6B9E0245E70E8 -:10013400030204A424F0B40B00400302066C9001F3 -:1001440049F828287302016A0201860202030202A6 -:100154005D02035D0203A00203E1020422020463C0 -:1001640002062C0201C4A20CE43390E740F0E490B0 -:10017400E68AF090E68B04F090E6A0E04480F0027A -:10018400066EE490E68AF090E68BF090E6A0E0201C -:10019400E1F990E68BE0753600F53700000090E653 -:1001A400047480F00000007406F0000000E4F043E2 -:1001B400800BD280C28090E6A0E04480F002066EFC -:1001C400E490E68AF090E68BF090E6A0E020E1F976 -:1001D40090E68BE0753600F5377F0F7E00120ECB6C -:1001E40090E746E0540F4450FFA3E0FDC2A0C2A133 -:1001F400C2A2120DB890E6A0E04480F002066EE4BC -:1002040090E68AF090E68BF090E6A0E020E1F99089 -:10021400E68BE0753600F5377F0F7E00120ECB902B -:10022400E745E0FF90E744E0FD90E747E0FB90E717 -:1002340046E0F53F90E743E0F54090E742E0F541C2 -:1002440090E741E0F54290E740E0F543120F11904A -:09025400E6A0E04480F002066E11 -:10025D00E490E68AF090E68BF090E6A0E020E1F9DC -:10026D0090E68BE0753600F53790E741E0F5B17516 -:10027D003A0F753BA0E53B153B7002153AE53B6423 -:10028D000A453A70F090E742E0F5B1753A0F753BCB -:10029D00A0E53B153B7002153AE53B640A453A7003 -:1002AD00F090E743E0F5B1753A0F753BA0E53B15CE -:1002BD003B7002153AE53B640A453A70F090E7440D -:1002CD00E0F5B1753A0F753BA0E53B153B70021596 -:1002DD003AE53B640A453A70F090E745E0F5B175B3 -:1002ED003A0F753BA0E53B153B7002153AE53B64B3 -:1002FD000A453A70F090E746E0F5B1753A0F753B57 -:10030D00A0E53B153B7002153AE53B640A453A7092 -:10031D00F090E747E0F5B1753A0F753BA0E53B1559 -:10032D003B7002153AE53B640A453A70F0E4F5B1CD -:10033D00753A0F753BA0E53B153B7002153AE53B51 -:10034D00640A453A70F090E6A0E04480F002066E33 -:10035D00E490E68AF090E68BF090E6A0E020E1F9DB -:10036D0090E68BE0753600F53700000090E60474DA -:10037D0080F0000000C2807406F0000000E4F090F0 -:10038D00E740E0FF7E00120ECB90E6A0E04480F047 -:10039D0002066EE490E68AF090E68BF090E6A0E01F -:1003AD0020E1F990E68BE0753600F53700000090FE -:1003BD00E6047480F00000007406F0000000E4F024 -:1003CD0090E740E0FF7E00120ECB90E6A0E0448067 -:1003DD00F002066EE490E68AF090E68BF090E6A0CF -:1003ED00E020E1F990E68BE0753600F5370000006E -:1003FD0090E6047480F00000007406F0000000E444 -:10040D00F090E740E0FF7E00120ECB90E6A0E044B6 -:10041D0080F002066EE490E68AF090E68BF090E6AE -:10042D00A0E020E1F990E68BE0753600F53700008D -:10043D000090E6047480F00000007406F0000000E7 -:10044D00E4F090E740E0FF7E00120ECB90E6A0E0D6 -:10045D004480F002066EE490E68AF090E68BF09010 -:10046D00E6A0E020E1F990E68BE0753600F5370067 -:10047D00000090E6047480F00000007406F00000A7 -:10048D0000E4F090E740E0FF7E00120ECB90E6A076 -:10049D00E04480F002066E90E6BAE0753200F53366 -:1004AD00A3E0FEE4EE423290E6BEE0753400F53591 -:1004BD00A3E0FEE4EE423490E6B8E064C0600302CF -:1004CD000594E5354534700302066E90E6A0E020F4 -:1004DD00E1F9C3E5359440E5349400500885343690 -:1004ED00853537800675360075374090E6B9E0B42E -:1004FD00A335E4F538F539C3E5399537E538953643 -:10050D005060E5332539F582E5323538F583E0FF66 -:10051D0074402539F582E434E7F583EFF00539E5CC -:10052D00397002053880D0E4F538F539C3E53995D1 -:10053D0037E5389536501874402539F582E434E79F -:10054D00F58374CDF00539E5397002053880DDADE0 -:10055D00377AE779407EE77F40AB07AF33AE321293 -:10056D000CABE490E68AF090E68BE537F02533F599 -:10057D0033E5363532F532C3E5359537F535E534A6 -:10058D009536F5340204CF90E6B8E064406003027E -:10059D00066EE5354534700302066EE490E68AF08A -:1005AD0090E68BF090E6A0E020E1F990E68BE07507 -:1005BD003600F53790E6B9E0B4A335E4F538F539F2 -:1005CD00C3E5399537E5389536503874402539F5FA -:1005DD0082E434E7F583E0FFE5332539F582E53232 -:1005ED003538F583EFF00539E5397002053880D0DF -:1005FD00AD377AE779407EE77F40AB07AF33AE3258 -:10060D0012106EE5372533F533E5363532F532C345 -:10061D00E5359537F535E5349536F53402059FE426 -:10062D0090E68AF090E68BF090E6A0E020E1F9905C -:10063D00E68BE0753600F53700000090E604748017 -:10064D00F00000007406F0000000E4F090E740E0D8 -:10065D00FF7E00120ECB90E6A0E04480F08002D326 -:02066D0022C3A6 -:01066F002268 -:1011ED00C0E0C083C082D2095391EF90E65D7401D7 -:0811FD00F0D082D083D0E03273 -:10121D00C0E0C083C0825391EF90E65D7404F0D0BE -:06122D0082D083D0E03204 -:10123300C0E0C083C0825391EF90E65D7402F0D0AA -:0612430082D083D0E032EE -:100FCA00C0E0C083C08290E680E030E70E850C1056 -:100FDA00850D11851412851513800C85141085153D -:100FEA0011850C12850D135391EF90E65D7410F084 -:070FFA00D082D083D0E03269 -:10120500C0E0C083C082D20B5391EF90E65D7408B5 -:08121500F0D082D083D0E0325A -:10100100C0E0C083C08290E680E030E70E850C101E -:10101100850D11851412851513800C851410851505 -:1010210011850C12850D135391EF90E65D7420F03C -:07103100D082D083D0E03231 -:01129C00321F -:01129D00321E -:01129E00321D -:01129F00321C -:0112A000321B -:0112A100321A -:0112A2003219 -:0112A3003218 -:0112A4003217 -:0112A5003216 -:0112A6003215 -:0112A7003214 -:0112A8003213 -:0112A9003212 -:0112AA003211 -:0112AB003210 -:0112AC00320F -:0112AD00320E -:0112AE00320D -:0112AF00320C -:0112B000320B -:0112B100320A -:0112B2003209 -:0112B3003208 -:0112B4003207 -:0112B5003206 -:0112B6003205 -:0112B7003204 -:0112B8003203 -:0112B9003202 -:0112BA003201 -:0112BB003200 -:0112BC0032FF -:0112BD0032FE -:0112BE0032FD -:0112BF0032FC -:10126D00AD071FED600BE4FEEEC3947850F20E80D7 -:01127D00F779 -:01127E00224D -:040DB800A907AB05D7 -:100DBC007F0112126DD2A27F0112126DC2A2E4FA4F -:100DCC00E9A802088002C333D8FCFC5304807F01DD -:100DDC0012126DD2A1BC8004D2A08002C2A07F01ED -:100DEC0012126DC2A10ABA08D7E4FAEBA802088065 -:100DFC0002C333D8FCFC5304807F0112126DD2A1C4 -:100E0C00BC8004D2A08002C2A07F0112126DC2A1CC -:040E1C000ABA08D72F -:010E200022AF -:020F1100AE0729 -:100F1300ED540F4410FFEEFDE53F540F4450F544EC -:100F2300EBF545E541540F4490F546E540F547E5BB -:100F330043540F44D0F548E542F549C2A0C2A1C2CB -:100F4300A2120DB8AD47AF46120DB8AD49AF481266 -:020F53000DB8D7 -:010F55002279 -:060E8100AB07AA06AC0558 -:100E8700E4FDE51E6010EA7E000DEE2400F582E425 -:100E97003418F583EAF0EBAE050D74002EF582E405 -:100EA7003418F583EBF0AF050D74002FF582E434A9 -:100EB70018F583ECF07A187B00AF1C1211D1AF1C28 -:030EC700121038CE -:010ECA002205 -:0A106E008E3C8F3D8D3E8A3F8B4083 -:10107800E4F541E541C3953E5020053DE53DAE3CD4 -:101088007002053C14FFE5402541F582E4353FF543 -:0A10980083E0FD120E81054180D9AE -:0110A200222B -:0A0CAB008E3C8F3D8D3E8A3F8B404A -:100CB500E4FDF541E51E6012E53CFF7E000DEE24E6 -:100CC50000F582E43418F583EFF0E53DAE050D74CB -:100CD500002EF582E43418F583E53DF07A187B00A3 -:100CE500AF1C1211D1AB40AA3FAD3EAF1C1211B5DE -:010CF50022DC -:08128700E4F54FD2E9D2AF22D9 -:1010380090E678E020E6F9C2E990E678E04480F0AE -:10104800EF25E090E679F090E678E030E0F990E678 -:1010580078E04440F090E678E020E6F990E678E021 -:0610680030E1D6D2E922BE -:1010D700A90790E678E020E6F9E54F702390E678D7 -:1010E700E04480F0E925E090E679F08D4AAF03A966 -:1010F70007754B018A4C894DE4F54E754F01D32294 -:02110700C32201 -:1010A300A90790E678E020E6F9E54F702590E67809 -:1010B300E04480F0E925E0440190E679F08D4AAF01 -:1010C30003A907754B018A4C894DE4F54E754F030F -:0410D300D322C3223F -:03004B00020AFCAA -:100AFC00C0E0C083C082C085C084C086758600C03B -:100B0C00D075D000C000C001C002C003C006C00731 -:100B1C0090E678E030E206754F06020BE690E67838 -:100B2C00E020E10CE54F64026006754F07020BE60E -:100B3C00E54F24FE605F14603624FE7003020BD771 -:100B4C0024FC7003020BE324086003020BE6AB4B9E -:100B5C00AA4CA94DAF4E054E8F82758300120E2103 -:100B6C0090E679F0E54E654A7070754F05806B9094 -:100B7C00E679E0AB4BAA4CA94DAE4E8E8275830044 -:100B8C00120E4E754F02E54A6401704E90E678E005 -:100B9C004420F08045E54A24FEB54E0790E678E007 -:100BAC004420F0E54A14B54E0A90E678E04440F053 -:100BBC00754F0090E679E0AB4BAA4CA94DAE4E8E2A -:100BCC0082758300120E4E054E800F90E678E0443D -:100BDC0040F0754F008003754F005391DFD007D064 -:100BEC0006D003D002D001D000D0D0D086D084D093 -:080BFC0085D082D083D0E032E5 -:1011B5001210A3E54F24FA600E146006240770F39D -:0C11C500D322E4F54FD322E4F54FD322EF -:1011D1001210D7E54F24FA600E146006240770F34D -:0C11E100D322E4F54FD322E4F54FD322D3 -:030000000200807B -:0C008000787FE4F6D8FD75814F02090975 -:100E2100BB010CE58229F582E5833AF583E0225086 -:100E310006E92582F8E622BBFE06E92582F8E222D0 -:0D0E4100E58229F582E5833AF583E49322EA -:100E4E00F8BB010DE58229F582E5833AF583E8F0DA -:100E5E00225006E92582C8F622BBFE05E92582C886 -:020E6E00F2226E -:100E7000EB9FF5F0EA9E42F0E99D42F0E89C45F0D8 -:010E8000224F -:00000001FF diff --git a/openhantek/res/firmware/dso5200x86-loader.hex b/openhantek/res/firmware/dso5200x86-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso5200x86-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso6022be-firmware.hex b/openhantek/res/firmware/dso6022be-firmware.hex index e875bd25..0e826e0a 100644 --- a/openhantek/res/firmware/dso6022be-firmware.hex +++ b/openhantek/res/firmware/dso6022be-firmware.hex @@ -1,405 +1,260 @@ -:02095e00c105d1 -:100cb80090e6007410f0120f1800000090e612740d -:100cc800a0f0000000e490e613f000000090e614a5 -:100cd80074e0f0000000e490e615f000000090e6f3 -:100ce800047480f00000007402f00000007406f044 -:100cf800000000e4f000000090e61804f000000096 -:100d08007411f000000090e61a7409f000000090d9 -:100d1800e6d27402f000000090e6e214f000000051 -:100d2800e490e671f075b4ff90e670f075b280c299 -:100d3800a4c2a3c2a2c2a7c2a6c2a5f51df51a12d3 -:0a0d4800130990e67ae04401f0225e -:100b7b00e51d64017031751d0290e6f574fff09070 -:100b8b00e6047480f00000007402f00000007406ac -:100b9b00f0000000e4f0000000fffe000fbf0001ba -:100bab000ebe03f7bfe8f4e5bb30e72590e6f4e0b3 -:100bbb0030e01ee5ac20e019e5bb30e7fb90e6d05a -:100bcb007428f0000000e490e6d1f000000075bb43 -:010bdb000613 -:010bdc0022f6 -:02002800d322e1 -:02004000d322c9 -:02004600d322c3 -:10104c0090e680e030e71800000090e6247402f08f -:10105c00000000e490e625f0000000d205801600a8 -:10106c000000e490e624f000000090e6257440f0c7 -:0d107c00000000c20590e6bae0f51bd3228b -:1012dd0090e740e51bf0e490e68af090e68b04f091 -:0212ed00d3220a -:0813010090e6bae0f518d322d2 -:1012ef0090e740e518f0e490e68af090e68b04f082 -:0212ff00d322f8 -:02004800d322c1 -:02004e00d322bb -:02005000d322b9 -:1000560090e678e05410ffc4540f4450f51913e4a9 -:1000660033f51cd20290e6b9e0245e605024f060bd -:100076003024d2b4080040030204d590008f75f0f6 -:1000860003a4c58325f0c5837302024b02029602c0 -:1000960002dc0204460204d50204d50204d5020499 -:1000a60065a205e43390e740f0e490e68af090e636 -:1000b6008b04f090e6a0e04480f00204d790e6ba04 -:1000c600e0752d00f52ea3e0fee4ee422d90e6be8f -:1000d600e0752f00f530a3e0fee4ee422f90e6b87f -:1000e600e064c060030201b3e530452f70030204eb -:1000f600d790e6a0e020e1f9c3e5309440e52f94df -:10010600005008852f3185303280067531007532f2 -:100116004090e6b9e0b4a335e4f533f534c3e534ed -:100126009532e53395315060e52e2534f582e52d7f -:100136003533f583e0ff74402534f582e434e7f582 -:1001460083eff00534e5347002053380d0e4f533ef -:10015600f534c3e5349532e53395315018744025ae -:1001660034f582e434e7f58374cdf00534e5347074 -:1001760002053380ddad327ae779407ee77f40ab1a -:1001860007af2eae2d120fbde490e68af090e68bf7 -:10019600e532f0252ef52ee531352df52dc3e5306a -:1001a6009532f530e52f9531f52f0200ee90e6b841 -:1001b600e0644060030204d7e530452f7003020473 -:1001c600d7e490e68af090e68bf090e6a0e020e196 -:1001d600f990e68be0753100f53290e6b9e0b4a30c -:1001e60035e4f533f534c3e5349532e533953150ce -:1001f6003874402534f582e434e7f583e0ffe52ed4 -:100206002534f582e52d3533f583eff00534e534f5 -:100216007002053380d0ad327ae779407ee77f40c1 -:10022600ab07af2eae2d12112de532252ef52ee59c -:1002360031352df52dc3e5309532f530e52f953165 -:10024600f52f0201bee490e68af090e68bf090e688 -:10025600a0e020e1f990e740e0f53514602914604c -:100266001d24fd601024fb60030204d7c2a4d2a3a0 -:10027600d2a20204d7c2a4c2a3c2a20204d7c2a4b5 -:10028600c2a3d2a20204d7c2a4d2a3c2a20204d796 -:10029600e490e68af090e68bf090e6a0e020e1f9a3 -:1002a60090e740e0f53524fe601a24fd600d24fb3e -:1002b600701bc2a7d2a6d2a50204d7c2a7c2a6c2e5 -:1002c600a50204d7c2a7c2a6d2a50204d7c2a7d246 -:1002d600a6c2a50204d7e490e68af090e68bf090d9 -:1002e600e6a0e020e1f990e740e0f535120ef203d2 -:1002f600880103740403600803c20a034d1003afa8 -:1003060014033b1803291e031730039c3200000315 -:10031600d390e60174eaf090e05074fff0751a018c -:100326000203d390e60174aaf090e05074fff075d2 -:100336001a010203d390e60174caf090e05074fbf0 -:10034600f0751a010203d390e60174caf090e040fa -:100356007401f0a3f0e4f51a807390e60174caf014 -:1003660090e0407402f0a304f0e4f51a805f90e692 -:100376000174caf090e0407405f0a304f0e4f51aa5 -:10038600804b90e60174caf090e0407417f0a30425 -:10039600f0e4f51a803790e60174caf090e04074f4 -:1003a60030f0a3f0e4f51a802490e60174caf090c8 -:1003b600e0407478f0a3f0e4f51a801190e6017439 -:1003c600caf090e04074f0f0a3f0e4f51a90e6f578 -:1003d60074fff090e080e090e6f3f090e081e0902a -:1003e600e6c3f090e082e090e6c1f090e083e09012 -:1003f600e6c2f090e085e090e6c0f090e086e090fe -:10040600e6f4f075af07e51ab4010a74e0f59a74dc -:1004160087f59b800874e0f59a7400f59b759de45a -:10042600e4f59ef533f53490e67be090e67cf00546 -:1004360034e534700205336480453370ea0204d72c -:10044600e490e68af090e68bf090e6a0e020e1f9f1 -:1004560090e740e0f53564017077751d0180729074 -:10046600e6bae0752d00f52ea3e0fee4ee422d90ef -:10047600e6bee0752f00f530a3e0fee4ee422fe580 -:1004860030452f604c90e6a0e020e1f9c3e53094ba -:1004960040e52f94005008852f318530328006754f -:1004a600310075324090e7407401f0e490e68af03e -:1004b60090e68be532f0252ef52ee531352df52d1e -:1004c600c3e5309532f530e52f9531f52f80b0d361 -:0204d60022c33f -:0104d8002201 -:10128100c0e0c083c082d2015391ef90e65d74014a -:08129100f0d082d083d0e032de -:1012b100c0e0c083c0825391ef90e65d7404f0d02a -:0612c10082d083d0e03270 -:1012c700c0e0c083c0825391ef90e65d7402f0d016 -:0612d70082d083d0e0325a -:10108900c0e0c083c08290e680e030e70e850a0e9a -:10109900850b0f851210851311800c85120e85138f -:1010a9000f850a10850b115391ef90e65d7410f0ce -:0710b900d082d083d0e032a9 -:10129900c0e0c083c082d2045391ef90e65d740828 -:0812a900f0d082d083d0e032c6 -:1010c000c0e0c083c08290e680e030e70e850a0e63 -:1010d000850b0f851210851311800c85120e851358 -:1010e0000f850a10850b115391ef90e65d7420f087 -:0710f000d082d083d0e03272 -:01002a0032a3 -:01003200329b -:01004200328b -:01004a003283 -:01005200327b -:010bf90032c9 -:010bfa0032c8 -:010bfb0032c7 -:010bfc0032c6 -:010bfd0032c5 -:010bfe0032c4 -:010bff0032c3 -:010dfa0032c6 -:010dfb0032c5 -:010dfc0032c4 -:010dfd0032c3 -:010dfe0032c2 -:010dff0032c1 -:0113110032a9 -:0113120032a8 -:0113130032a7 -:0113140032a6 -:0113150032a5 -:0113160032a4 -:0113170032a3 -:0113180032a2 -:0113190032a1 -:01131a0032a0 -:01131b00329f -:01131c00329e -:01131d00329d -:01131e00329c -:01131f00329b -:01132000329a -:011321003299 -:011322003298 -:060f7300ab07aa06ac0565 -:100f7900e4fde51c6010ea7e000dee2407f582e42d -:100f890034e1f583eaf0ebae050d74072ef582e442 -:100f990034e1f583ebf0af050d74072ff582e434e6 -:100fa900e1f583ecf07ae17b07af19120ddeaf1999 -:030fb9001210f71c -:010fbc002212 -:0a112d008e368f378d388a398b3ae1 -:10113700e4f53be53bc3953850200537e537ae3638 -:101147007002053614ffe53a253bf582e43539f59b -:0a11570083e0fd120f73053b80d901 -:01116100226b -:0a0fbd008e368f378d388a398b3a53 -:100fc700e4fde51c6012e536ff7e000dee2407f513 -:100fd70082e434e1f583eff0e537ae050d74072eb3 -:100fe700f582e434e1f583e537f07ae17b07af1961 -:0e0ff700120ddeab3aaa39ad38af19120bdd80 -:0110050022c8 -:100960006080e0000e030111010130070100020167 -:1009700000001100fffefffefffeffff0009121244 -:10098000002d123f080301010101010701020401ca -:1009900000000000fffcfffefffeffff090912122e -:1009a000002d363f17180101010101070002010067 -:1009b00000001000fffbfffbfbfbfbfb000912121a -:1009c000002d123f0803013f010101070102000150 -:1009d00000000000fffdffffffffffff09091212eb -:1009e000002d363f6080e087010201013f010107d1 -:1009f00000000200010000000702020707070707c6 -:100a0000000000003f00003f03013f01010101071a -:100a10000202050000000000050707070707070797 -:100a200000003f000000003f010180010101bf07fd -:100a30000202030202021100ffffffffffffffffa0 -:100a400000091212002d123f0101010101010107ed -:100a5000000000000000000007070707070707075e -:100a6000000000000000003f47e080c000000fca07 -:020a70001e0066 -:100f180090e60174caf090e6f574fff090e080e086 -:100f280090e6f3f090e081e090e6c3f090e082e094 -:100f380090e6c1f090e083e090e6c2f090e085e0b2 -:100f480090e6c0f090e086e090e6f4f075af0774a4 -:100f5800e0f59a7400f59b759de4e4f59eff90e634 -:0a0f68007be090e67cf00fbf80f400 -:010f7200225c -:0a00360000010202030304040505a3 -:10077300c204c200c203c201c20275080875090798 -:10078300120cb8c2c975cdf875cc40d2cad2adc26d -:1007930087c2a0d2a17e0e7f008e0c8f0d75140e22 -:1007a300751512750a0e750b1c75120e75133c75b3 -:1007b300160e75175c90e680e030e70e850a0e850d -:1007c3000b0f851210851311800c85120e85130fe4 -:1007d300850a10850b11ee54e070030208f77529a2 -:1007e30000752a807e0e7f008e2b8f2cc374909f02 -:1007f300ff740e9ecf2402cf3400fee48f288e2791 -:10080300f526f525f524f523f522f521af28ae27a6 -:10081300ad26ac25ab24aa23a922a821c3120ee13d -:10082300502ae52a2524f582e5293523f58374cd5d -:10083300f0e4faf9f8e5242401f524ea3523f52355 -:10084300e93522f522e83521f52180c0e4f524f5c8 -:1008530023f522f521af28ae27ad26ac25ab24aa7c -:1008630023a922a821c3120ee15037e52c2524f534 -:1008730082e52b3523f583e0ffe52a2524f582e580 -:10088300293523f583eff0e4faf9f8e5242401f59b -:1008930024ea3523f523e93522f522e83521f5212c -:1008a30080b385290c852a0d74002480ff740e34cf -:1008b300fffec3e5159ff515e5149ef514c3e50f7b -:1008c3009ff50fe50e9ef50ec3e5119ff511e5109b -:1008d3009ef510c3e50b9ff50be50a9ef50ac3e5ec -:1008e300139ff513e5129ef512c3e5179ff517e560 -:1008f300169ef516d2e843d82090e668e04409f046 -:1009030090e65ce0443df0d2af90e680e020e10564 -:10091300d2061211fa90e680e054f7f0538ef8c233 -:10092300043001051204d9c20130042912002850f1 -:1009330024c20412000320001690e682e030e7048c -:10094300e020e1ef90e682e030e604e020e0e4120c -:0a0953001229120040120b7b80c72e -:01095d002277 -:1004d90090e6b9e070030205b514700302065e24c4 -:1004e900fe70030206f324fb70030205af147003c8 -:1004f9000205a914700302059d1470030205a324c3 -:1005090005600302075f120046400302076b90e68d -:10051900bbe024fe602c14604724fd601614603192 -:1005290024067066e50c90e6b3f0e50d90e6b4f0ac -:1005390002076be51490e6b3f0e51590e6b4f00216 -:10054900076be50e90e6b3f0e50f90e6b4f002070d -:100559006be51090e6b3f0e51190e6b4f002076b95 -:1005690090e6bae0ff121255aa06a9077b01ea49eb -:100579004b600dee90e6b3f0ef90e6b4f002076b36 -:1005890090e6a0e04401f002076b90e6a0e0440188 -:10059900f002076b1212ef02076b12130102076bcd -:1005a90012104c02076b1212dd02076b1200484051 -:1005b9000302076b90e6b8e0247f602b14603c24ab -:1005c900026003020654a200e433ff25e0ffa20300 -:1005d900e4334f90e740f0e4a3f090e68af090e628 -:1005e9008b7402f002076be490e740f0a3f090e609 -:1005f9008af090e68b7402f002076b90e6bce05437 -:100609007eff7e00e0d3948040067c007d0180045b -:100619007c007d00ec4efeed4f2436f58274003ee1 -:10062900f583e493ff3395e0feef24a1ffee34e672 -:100639008f82f583e0540190e740f0e4a3f090e65f -:100649008af090e68b7402f002076b90e6a0e04412 -:1006590001f002076b12004e400302076b90e6b8e7 -:10066900e024fe601d2402600302076b90e6bae0f5 -:10067900b40105c20002076b90e6a0e04401f00254 -:10068900076b90e6bae0705990e6bce0547eff7eb5 -:1006990000e0d3948040067c007d0180047c007dcd -:1006a90000ec4efeed4f2436f58274003ef583e4ee -:1006b90093ff3395e0feef24a1ffee34e68f82f538 -:1006c90083e054fef090e6bce05480ff131313540a -:1006d9001fffe0540f2f90e683f0e04420f002075b -:1006e9006b90e6a0e04401f08078120050507390be -:1006f900e6b8e024fe60202402706790e6bae0b410 -:100709000104d200805c90e6bae06402605490e68d -:10071900a0e04401f0804b90e6bce0547eff7e00ef -:10072900e0d3948040067c007d0180047c007d003c -:10073900ec4efeed4f2436f58274003ef583e493ca -:10074900ff3395e0feef24a1ffee34e68f82f583b7 -:10075900e04401f0800c120056500790e6a0e044f6 -:0907690001f090e6a0e04480f0ec -:010772002264 -:0300330002002e9a -:04002e0053d8ef3282 -:03002b0002119629 -:10119600c0e0b287e509150970021508e50945089a -:1011a600701575080475091f300206b2a1d2a08019 -:1011b60004b2a0d2a1c20275cdf875cc40c2cfd080 -:0211c600e03215 -:100e00001201000200000040b5042260000001024f -:100e100000010a0600020000004001000902200053 -:100e200001010080320904000002ff0000000705f4 -:100e300002020002000705860200020009022000eb -:100e400001010080320904000004ff0000000705d2 -:100e50000202400000070586024000000403090466 -:100e60000e034f0044004d0020002000200022030c -:100e7000480061006e00740065006b004400530080 -:100e80004f003600300032003200420045002000a2 -:020e9000000060 -:1012290090e682e030e004e020e60b90e682e030d0 -:10123900e119e030e71590e680e04401f07f147e83 -:0c1249000012100690e680e054fef02237 -:1000030090e682e044c0f090e681f043870100006f -:0400130000000022c7 -:1011fa0030060990e680e0440af0800790e680e035 -:10120a004408f07fdc7e0512100690e65d74fff05c -:0f121a0090e65ff05391ef90e680e054f7f022fa -:08130900e4f541d2e9d2af2264 -:1010f70090e678e020e6f9c2e990e678e04480f0ef -:10110700ef25e090e679f090e678e030e0f990e6b8 -:1011170078e04440f090e678e020e6f990e678e061 -:0611270030e1d6d2e922fe -:1011c800a90790e678e020e6f9e541702390e678f3 -:1011d800e04480f0e925e090e679f08d3caf03a982 -:1011e80007753d018a3e893fe4f540754101d322e8 -:0211f800c32210 -:10116200a90790e678e020e6f9e541702590e67857 -:10117200e04480f0e925e0440190e679f08d3caf4f -:1011820003a907753d018a3e893fe4f54075410395 -:04119200d322c3227f -:03004b00020a7333 -:100a7300c0e0c083c082c085c084c086758600c0c4 -:100a8300d075d000c000c001c002c003c006c007bb -:100a930090e678e030e206754106020b5d90e67859 -:100aa300e020e10ce54164026006754107020b5d3d -:100ab300e54124fe605f14603624fe7003020b4e92 -:100ac30024fc7003020b5a24086003020b5dab3d48 -:100ad300aa3ea93faf4005408f82758300120e9254 -:100ae30090e679f0e540653c7070754105806b9048 -:100af300e679e0ab3daa3ea93fae408e8275830006 -:100b0300120ebf754102e53c6401704e90e678e039 -:100b13004420f08045e53c24feb5400790e678e0ac -:100b23004420f0e53c14b5400a90e678e04440f0f8 -:100b330075410090e679e0ab3daa3ea93fae408ef9 -:100b430082758300120ebf0540800f90e678e04463 -:100b530040f075410080037541005391dfd007d009 -:100b630006d003d002d001d000d0d0d086d084d01c -:080b730085d082d083d0e0326e -:02125500a907e7 -:10125700ae16af178f828e83a3e064037017ad01bc -:1012670019ed7001228f828e83e07c002ffdec3e0a -:09127700feaf0580df7e007f0060 -:01128000224b -:100bdd00121162e54124fa600e146006240770f3c9 -:0c0bed00d322e4f541d322e4f541d322e9 -:100dde001211c8e54124fa600e146006240770f360 -:0c0dee00d322e4f541d322e4f541d322e6 -:101006008e2d8f2e90e600e054187012e52e2401e6 -:10101600ffe4352dc313f52def13f52e801590e65d -:1010260000e05418ffbf100be52e25e0f52ee52d48 -:1010360033f52de52e152eae2d7002152d4e6005bd -:0610460012001780ee22eb -:100017007400f58690fda57c05a3e582458370f9fc -:0100270022b6 -:03004300020c00ac -:03005300020c009c -:100c0000021281000212c7000212b1000212990002 -:100c1000021089000210c00002002a000200320007 -:100c20000200420002004a0002005200020bf900da -:100c3000020bfa00020bfb00020bfc00020bfd0092 -:100c4000020bfe0002003200020bff00020dfa0050 -:100c5000020dfb00020dfc00020dfd00020dfe0066 -:100c6000020dff00020032000200320002003200da -:100c700002131100021312000213130002131400d6 -:100c800002131500021316000213170002131800b6 -:100c90000213190002131a0002131b0002131c0096 -:100ca00002131d0002131e0002131f000213200076 -:080cb0000213210002132200cf -:03000000020d529c -:0c0d5200787fe4f6d8fd758141020d9910 -:100e9200bb010ce58229f582e5833af583e0225015 -:100ea20006e92582f8e622bbfe06e92582f8e2225f -:0d0eb200e58229f582e5833af583e4932279 -:100ebf00f8bb010de58229f582e5833af583e8f069 -:100ecf00225006e92582c8f622bbfe05e92582c815 -:020edf00f222fd -:100ee100eb9ff5f0ea9e42f0e99d42f0e89c45f067 -:010ef10022de -:100ef200d083d082f8e4937012740193700da3a38f -:100f020093f8740193f5828883e4737402936860a2 -:060f1200efa3a3a380dfa2 -:100d5e00020773e493a3f8e493a34003f68001f231 -:100d6e0008dff48029e493a3f85407240cc8c33396 -:100d7e00c4540f4420c8834004f456800146f6df65 -:100d8e00e4800b010204081020408090095ee47e8e -:100d9e00019360bca3ff543f30e509541ffee4935a -:100dae00a360010ecf54c025e060a840b8e493a321 -:100dbe00fae493a3f8e493a3c8c582c8cac583ca4c -:100dce00f0a3c8c582c8cac583cadfe9dee780be04 -:010a72000083 -:03009b0002038cd1 -:1002f50003580103500203480403400803700a032e -:10030500380c03301003681403281803211e031d3d -:0e03150030036032000003d374ea800274aa41 -:0f032300751a01805e79017a017b0980467901a4 -:100332007a017b01803e79017a027b018036790263 -:100342007a037b01802e79057a067b018026790b60 -:100352007a0c7b01801e79177a187b018016792f1f -:100362007a307b01800e79777a787b01800679ef8b -:100372007af07b0190e040e9f0a3eaf0a3ebf0e42d -:0f038200f51a74ca90e601f08047e490e68af01d -:0f03910090e68bf090e6a0e020e1f990e740e0e5 -:0f03a000600a94035006240a90e61af00204d76c -:010b940082de -:010b9a0086d4 -:010cf0008281 -:010cf6008677 -:00000001ff +:04000000020059326F +:01000B0032C2 +:0100130032BA +:01001B0032B2 +:0100230032AA +:03002B000201FBD4 +:0300330002013295 +:01003B003292 +:03004300023F0079 +:01004B003282 +:03005300023F0069 +:1C00B200750800750900750A02750B00750CF4750D01750EF4750F01C200C201BD +:0300E000020056C5 +:030056000207C2DC +:2000E300AF827401B54C0280157402B54C0280127405B54C02800F740AB54C12800C7E48C6 +:2001030080107E24800C7E0080087E6C800475820022EF60067DE07F0080047D1C7F00EDD7 +:20012300F4FFE5A05207ED5E4FF5A075820122C2DC32C0E0C082C083D2005391EF90E65D35 +:200143007401F0D083D082D0E032C021C0E0C0F0C082C083C007C006C005C004C003C0025F +:20016300C001C000C0D075D0007582001212115391EF90E65D7410F0D0D0D000D001D002CD +:20018300D003D004D005D006D007D083D082D0F0D0E0D02132C021C0E0C0F0C082C083C085 +:2001A30007C006C005C004C003C002C001C000C0D075D0007582011212115391EF90E65D38 +:2001C3007420F0D0D0D000D001D002D003D004D005D006D007D083D082D0F0D0E0D0213224 +:2001E300C0E0C082C083D2015391EF90E65D7408F0D083D082D0E032C0E0C0D0B287E50E0F +:20020300450F6019150E74FFB50E02150FE50E450F700A850C0E850D0FB2A0D2A1C2CFD068 +:20022300D0D0E032E582FF30E00BADB17E004305088DB1800353B1F7EF30E408AEB14306ED +:20024300018EB12253B1FE22850A4C754D00905DC01213F0AE82AF83AC097D00C3EC9EEDE8 +:2002630064808FF063F08095F0400990E640E053E07FF02290E640E0FF43078090E640EF19 +:20028300F02285820A7401B50A0280057402B50A18AF0A74072FF50B90E618F090E61AE5DA +:2002A3000BF012024B758201227582002290E6F574FFF000000000E5BB60FC90E604748076 +:2002C300F00000000090E618E4F00000000090E61AE4F00000000090E6047402F000000085 +:2002E3000090E6047406F00000000090E618E50BF00000000090E61AE50BF00000000090A9 +:20030300E604E4F02290E6F574FFF000000000E50870057E06FF80047E027F0090E648EE18 +:20032300F0221202B07EE87F03EE24FFFCEF34FFFD8C068D07EC4D70F0E5BB30E7FB00005F +:20034300000090E6D07428F00000000090E6D1E4F0E50870057E06FF80047E047F008EBBFA +:2003630022AF8290E680E030E7067D1C7E3D80047DA67E3DEF75F010A42416FBE435F0FC3C +:20038300EB2DFDEC3EFC7E80EFF508703590E612E4F090E61474E0F090E6E27401F08D829A +:2003A3008C838EF01213D390E625F074012DFAE43CFB8E078A828B838FF01213D390E624B3 +:2003C300F02290E61274D8F090E614E4F090E6D204F08D828C838EF01213D390E621F00D82 +:2003E300BD00010C8D828C838EF01213D3FF90E62074075FF08D828C838EF01213D3C423C2 +:20040300541F90E64004F002024BAF827E00EE75F005A4FCADF024F8F582ED3416F583E403 +:2004230093FBB5070280080EBE16E375820022EC24F8FCED3416FD8C828D83A3A3A3A3E441 +:200443009390E601F08C828D83A3E493FDA3E493FEA3E493FF75AF07759DE4759E007C2069 +:200463008C031CEB60168D828E838FF01213D3FBA3AD82AE8390E67CEBF080E47F608F0633 +:200483001FEE600790E67CE4F080F375820122AF82BF00028064BF0103020529BF02030203 +:2004A3000537BF0503020545BF0A03020553BF1403020561BF320302056FBF640302057D72 +:2004C300BF69028017BF6A028021BF6E02802BBF78028035BF9602803F02058B750C19750D +:2004E3000D0075CAC075CB6302058F750C1E750D0075CACB75CB7D02058F750C32750D0001 +:2005030075CAE075CBB102058F750C64750D0075CAF075CBD802058F750CFA750D0075CAB2 +:200523006075CBF08066750CF4750D0175CA3075CBF88058750CE8750D0375CA1875CBFC4A +:20054300804A750CC4750D0975CA7075CBFE803C750C88750D1375CA3875CBFF802E750CD2 +:2005630010750D2775CA9C75CBFF8020750CA8750D6175CAD875CBFF8012750C50750DC3FB +:2005830075CAEC75CBFF80047582002275820122AD4CAE4DAF4E8D828E838FF0E508121395 +:2005A3002175820122E582700685108212036475820122758200227582012290E6BBE0FE34 +:2005C3007F0090E6BAE07C004207EC420690E6BFE0FC7D0090E6BEE07A004205EA4204906D +:2005E300E6A0E020E1F990E6B8E0FBBB40028073BBC00280030206BFED4C70030206C3C39E +:2006030074409DE49C50067A407B0080048D028C0390E6A0E020E1F98A4575460075474063 +:200623007548E77549008F438E44758251C007C006C005C004C002120E58D002D004D0059E +:20064300D006D00790E68AE4F00000000090E68BEAF07B00EDC39AFDEC9BFCEA2FFFEB3EB5 +:20066300FE8095ED4C605990E68BE4F090E6A0E020E1F990E68BE0FB8B38753900753A4071 +:20068300753BE7753C008F368E37758251C007C006C005C004C003120DC3E582D003D00474 +:2006A300D005D006D0077003F582227A00EB2FFFEA3EFEEDC39BFDEC9AFC80A775820022E6 +:2006C3007582012275820022AF82C007120308D007C2A0D2A1850C0E850D0FBFE0004015EF +:2006E300EF2419401090E68AE4F090E68BF090E6A0E020E1F9BFA2028023BFE0028021BFBF +:20070300E1028028BFE202802FBFE3028033BFE4028046BFE502804ABFE65D80520205BE2E +:2007230090E740E0F54C7582000200E390E740E0F54C7582010200E390E740E0F582020439 +:200743000D90E740E0FFBF010CD2A0C2A1E4F50EF50F1203257582012290E740E0F5820203 +:20076300028590E740E0F5821202277582012290E740E0F5820204927582002290E613E460 +:20078300F090E615F090E6C2F090E6C37480F090E6C0E4F090E6F4F0120308754C0175826C +:2007A300001200E3754C017582011200E375820112040D75820212028575820002036490F5 +:2007C300E600E054E74410F01208DE12077FD2E890E668E0FF43070890E668EFF090E65CE9 +:2007E300E0FF43070190E65CEFF0E0FF43071090E65CEFF0E0FF43072090E65CEFF0E0FF58 +:2008030043070890E65CEFF0D2DDD2AF75CA3075CBF875C800D2ADD2CA90E680E043E00AA0 +:20082300F09005DC12137C90E680E053E0F7F090E672E4F090E671F090E670F075B109F536 +:20084300A0F58075B6FF75B4FF75B2FFC2A0D2A11000028003120EBC10010280F390E68244 +:20086300E043E0C0F090E6817401F0AE877F004306018E8700000000000000E513701C903F +:20088300E682E030E60790E682E020E0D090E682E030E70790E682E020E1C290E682E030AF +:2008A300E60790E682E020E00E90E682E030E7A090E682E030E19990000512137C90E68025 +:2008C300E0FF43070190E680EFF090000F12137C90E680E053E0FEF00208537E107F3E75C2 +:2008E3004C30754D31754E32754F337550347551357552367553377554387555397556419A +:20090300755742755843755944755A45755B4674182EFCE43FFD90E507E0FB5303F0E4C45F +:20092300CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C828D83EBF074162EFC54 +:20094300E43FFD90E507E0FB53030FEB244CF987038C828D83EBF074142EFCE43FFD90E59A +:2009630008E0FB5303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C82E2 +:200983008D83EBF074122EFCE43FFD90E508E0FB53030FEB244CF987038C828D83EBF07491 +:2009A300102EFCE43FFD90E509E0FB5303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0BE +:2009C300EB244CF987038C828D83EBF0740E2EFCE43FFD90E509E0FB53030FEB244CF987D8 +:2009E300038C828D83EBF0740C2EFCE43FFD90E50AE0FB5303F0E4C4CBC4540F6BCB540F5B +:200A0300CB6BCB30E30244F0EB244CF987038C828D83EBF0740A2EFCE43FFD90E50AE0FB90 +:200A230053030FEB244CF987038C828D83EBF074082EFCE43FFD90E50BE0FB5303F0E4C468 +:200A4300CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C828D83EBF074062EFC43 +:200A6300E43FFD90E50BE0FB53030FEB244CF987038C828D83EBF074042EFCE43FFD90E585 +:200A83000CE0FB5303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C82BD +:200AA3008D83EBF074022EFEE43FFF90E50CE0FD53050FED244CF987058E828F83EDF022BC +:201438000F0E0000000000000200010000000000505555000000000000000000000000007A +:20145800010100000000000002000100000000005055550000000000000000000000000075 +:20147800FAFAFAFAFAF9000002000000000001005055555555555500000000000000000028 +:20149800FAFAF90000000000020000010000000050555555000000000000000000000000F5 +:2014B800C8C8C700000000000200000100000000505555550000000000000000000000006B +:2014D800FAFAF90000000000020000010000000050555555000000000000000000000000B5 +:2014F8009695000000000000020001000000000050555500000000000000000000000000AC +:201518000201000000000000020001000000000050555500000000000000000000000000B3 +:201538004B4A00000000000002000100000000005055550000000000000000000000000001 +:2015580025250000000000000200010000000000505555000000000000000000000000002C +:201578000100000000000000020100000000000050550000000000000000000000000000AA +:201598001E1D000000000000020001000000000050555500000000000000000000000000FB +:2015B800010100000000000002000100000000005055550000000000000000000000000014 +:2015D8000707000000000000020001000000000050555500000000000000000000000000E8 +:2015F80001000000000000000201000000000000505500000000000000000000000000002A +:201618000504000000000000020001000000000050555500000000000000000000000000AC +:2016380080000000000000000300000000000000000000000000000000000000000000000F +:2016580006050000000000000200010000000000505555000000000000000000000000006A +:201678008000000000000000030000000000000000000000000000000000000000000000CF +:20169800030200000000000002000100000000005055550000000000000000000000000030 +:2016B800020200000000000002000100000000005055550000000000000000000000000011 +:2016D8000302000000000000020001000000000050555500000000000000000000000000F0 +:2016F80030781680EA1E381680AA18F81580CA10B81580CA0F7815808A0C181580CA0A58F3 +:2017180014808A08D81680CA06B816808A059816808A04581680CA031816808A02D8158052 +:201738008A013814808A969815808A8C5815808A783815808A6EF814808A6AD81480CA6904 +:0E175800B814808A689814808A667814808A93 +:203D000012010002FFFFFF40B50422600702010203010A060002000000400100090289001F +:203D200001010080FA0904000001FF000000070586020002000904000101FF000100070549 +:203D400082010014010904000201FF00010007058201000C010904000301FF000100070502 +:203D600082010004010904000401FF000100070582010004020904000501FF0001000705F5 +:203D800082010004030904000601FF000100070582010004040904000701FF0001000705CD +:053DA000820100020495 +:203DA6000902390002010080FA0904000001FF000000070586024000000904000101FF004D +:193DC600010007058201FF03010904000201FF00010007058201000201AF +:203DE0000403090416034F00700065006E00480061006E00740065006B00160344005300F9 +:203E00004F002D003600300032003200420045001A03300030003000300030003000300068 +:0C3E2000300030003000300030000000A6 +:200AC30085822CE5272522FDE5283523FE752D02903C00E4F0903C00E06004758200229030 +:200AE300E678E0FB7A0043038090E678EBF0E030E21090000AC006C00512137CD005D0063E +:200B030080D3E52C25E0FB90E679F090E678E020E006903C00E060F3903C00E060047582B5 +:200B2300002290E678E020E2AC90E678E020E14090E678E0FB43034090E678EBF090E678FF +:200B4300E030E606903C00E060F3903C00E0600475820022E52D14FB8B2D7003F5822290F9 +:200B6300000AC006C00512137CD005D006020AD87A007B00C3EA9DEB9E4003020C0DC3EADA +:200B83009522EB95235016EA2524F8EB3525F9AC26888289838CF01213D3FC801DEAC395F2 +:200BA30022F8EB9523F9E82529F8E9352AF9AF2B888289838FF01213D3FC90E679ECF00AD5 +:200BC300BA00010B90E678E020E006903C00E060F3903C00E060047582002290E678E03052 +:200BE300E203020AD890E678E020E18890E678E0FF43074090E678EFF090E678E030E606C4 +:200C0300903C00E060F37582002290E678E0FF43074090E678EFF090E678E030E606903CDF +:200C230000E060F3903C00E060047582002275820122858233903C00E4F004B52E0814B5A9 +:200C43002F0474018001E4FE903C00E06004758200227C007D0090E678E0FB43038090E65F +:200C630078EBF0E030E20C90000AC00612137CD00680D5E53325E0FB3395E0FA4303019063 +:200C8300E679EBF090E678E020E006903C00E060F3903C00E060047582002290E678E0202D +:200CA300E2A690E678E020E11E90E678E0FB43034090E678EBF090E678E030E606903C00EE +:200CC300E060F375820022EE600F90E678E0FB7A0043032090E678EBF090E679E0E4F5341A +:200CE300F53574012534F8E43535F9C3E8952EE9952F506C90E678E020E006903C00E06003 +:200D0300F3903C00E060047582002290E678E030E203020C4B74022534F8E43535F9E8B5CD +:200D23002E13E9B52F0F90E678E0F8790043002090E678E8F0A834A9350534E4B534020564 +:200D430035AC34AD35E82530F8E93531F9AF3290E679E0FB888289838FF0121321020CE5A3 +:200D630090E678E020E006903C00E060F3903C00E060047582002290E678E030E203020C83 +:200D83004B90E678E0FF43074090E678EFF0EC2530FCED3531FDAF3290E679E08C828D8386 +:200DA3008FF012132190E678E030E606903C00E060F3903C00E0600475820022758201223F +:200DC30085823DE4F541F542AB36AC37C3E5419538E54295394003020E547A0090E678E01D +:200DE30030E4067A018C078F3EEA04FFEA243EF98B02A702EF04FAEF243EF9AE41AF420577 +:200E030041E4B541020542EE253AFEEF353BFFAD3C8E828F838DF01213D3F78A2275230007 +:200E230075243E752500752640E4F527F528F529F52AF52B853D82C004C003120AC3E582D8 +:200E4300D003D0047003F582220BBB00010C020DCF75820122AF827E0090E678E030E4067A +:200E63007E01AD448D4AEE04FDEE244AF9AE43A7068D2275230075244A752500752640E4C3 +:200E8300F527F528F529F52AF52B8F82C007120AC3E582D0077003F5822285452E85462FC6 +:190EA3008547308548318549328F82120C35E5827003F58222758201224B +:06008800E478FFF6D8FD4C +:1200CE007512007513007514A675153D75161C75173DAB +:200EBC0090E6B9E0FF24F45003020FEAEF240A83F582EF241083F583E473EE0AEA26EAEA3A +:200EDC0042EA52698BC50E0F0F0F0F0F0F0F0F0F0F0F12106CE582600302100690E6A0E0A6 +:200EFC00FF7E0043070190E6A0EFF0021006121100E582600302100690E6A0E0FF7E004346 +:200F1C00070190E6A0EFF002100612117BE582600302100690E6A0E0FF7E0043070190E6EC +:200F3C00A0EFF00210061206C7E582600302100612123C0210061205B6AF8290E740EFF031 +:200F5C0090E68AE4F090E68B04F002100690E6BAE0F5821205BAE582600302100690E6A044 +:200F7C00E0FF7E0043070190E6A0EFF002100690E6BCE0FF754C11754D00754E408F8212D5 +:200F9C000593E582701190E6A0E0FF7E0043070190E6A0EFF0805390E740E511F090E68A92 +:200FBC00E4F090E68B04F0804190E6BCE0FF90E6BAE0F5108F821205A8E582702D90E6A07B +:200FDC00E0FF7E0043070190E6A0EFF0801C90E6B9E0F5821206CBE582700F90E6A0E0FF78 +:200FFC007E0043070190E6A0EFF090E6A0E0FF43078090E6A0EFF022AF82747F5FFE24F7A5 +:20101C005003021068EE240A83F582EE240D83F583E473414558685C6860686410101010EA +:20103C00101010101090E6A022EF30E7067EA27FE680047EA17FE68E828F832290E6A322F4 +:20105C0090E6A42290E6A52290E6A6229000002290E6B8E0FFBF80028022BF81028008BF92 +:20107C00820280370210F890E740E4F090E741F090E68AF090E68B7402F08064E51325E0A4 +:20109C00FFE512420790E740EFF090E741E4F090E68AF090E68B7402F0804590E6BCE0F5B0 +:2010BC0082121014AE82AF838E048F05EE4F7003F582228C828D83E0FC30E0067E017F007D +:2010DC0080047E007F0090E740EEF090E741E4F090E68AF090E68B7402F08004758200225E +:2010FC007582012290E6B8E0FF6005BF0264801B90E6BAE0FFBF0105751300805E90E6BA1E +:20111C00E0FFBF060280547582002290E6BAE0703D90E6BCE0F582121014AE82AF83E0FD65 +:20113C005305FE8E828F83EDF090E6BCE0FF30E7098F057E004305108D0753071F90E6839D +:20115C00EFF07E0043072090E683EFF0800D7582002290E6B9E0F5820206CB75820122902B +:20117C00E6B8E0FF6008BF0202802902120490E6BAE0FFBF020302120D90E6BAE0FFBF0127 +:20119C0005751301806B90E6BAE0FFBF060280617582002290E6BAE0704A90E6BCE0F58297 +:2011BC00121014AE82AF83EE4F7003F582228E828F83E0FD7C004305018E828F83EDF090DF +:2011DC00E6BCE0FF30E7098F057E004305108D0753071F90E683EFF07E0043072090E68322 +:2011FC00EFF0800D7582002290E6B9E0F5820206CB75820122AF827E0110AF027E00EF609D +:20121C000E75141C75153D7516A675173D800C7514A675153D75161C75173DEE1392AF22E8 +:20123C0090E6BBE0FFBF01028019BF02028024BF0302802CBF06030212F6BF07030213069A +:20125C000213137E007F3D90E6B3EFF090E6B47400F02290E6B3E515F090E6B4E514F02210 +:20127C007DE07E3D7F8090E6BAE0F54C7B008B020BEAB54C0280428D008E018F028D828EDE +:20129C00838FF01213D328F8E439F9880589068A0774012DF9E43EFA8F0489828A838CF077 +:2012BC001213D3F9B9030280067D007E007F00ED4E6006C3E54C9B50B5ED4E600F8D038E66 +:2012DC000490E6B3ECF090E6B4EDF02290E6A0E0FF43070190E6A0EFF0227E127F3D90E6A7 +:2012FC00B3EFF090E6B47412F02290E6B3E517F090E6B4E516F02290E6A0E0FF4307019062 +:05131C00E6A0EFF02245 +:200066007900E94400601B7A00901766780175923CE493F2A308B800020592D9F4DAF275A3 +:0200860092FFE7 +:1B13210020F71130F6138883A88220F509F6A8837583002280FEF280F5F022CB +:203F0000020135000214360002142B000201E30002014D0002019800021428000213700048 +:203F200002137200021373000213740002137700021435000214370002133C0002133E0021 +:203F400002142D00021370000213EF000214290002142A0002142C0002142E0002142F004B +:203F60000213D20002137000021370000213700002137100021375000213780002137A009F +:203F8000021376000213790002137B000213D10002133F0002136A0002136C0002136E00BB +:183FA0000213400002136B0002136D0002136F0002133D000214340092 +:01133C00327E +:01133D00327D +:01133E00327C +:01133F00327B +:01134000327A +:20008E007800E84400600A790075923CE4F309D8FC7801E84400600C7901903C00E4F0A306 +:0400AE00D8FCD9FAA7 +:201341007A10E4FBFCE58225E0F582E58333F583EB33FBEC33FCEB954CF5F0EC954D400638 +:09136100FCABF0438201DADD224D +:01136A003250 +:01136B00324F +:01136C00324E +:01136D00324D +:01136E00324C +:01136F00324B +:01137000324A +:011371003249 +:011372003248 +:011373003247 +:011374003246 +:011375003245 +:011376003244 +:011377003243 +:011378003242 +:011379003241 +:01137A003240 +:01137B00323F +:20137C00AE82AF8390E600E05418C423541F70057CB1FD801C90E600E05418C423541FFB81 +:20139C00BB01067A617B0180047AC27B028A048B058C4C8D4D154C74FFB54C02154DE54CA1 +:1513BC00454D70F1EE24FFFAEF34FFFB8A068B07EA4B70DD223B +:0113D10032E9 +:0113D20032E8 +:0D00590075815B121430E5826003020056D1 +:1C13D30020F71430F6148883A88220F507E6A88375830022E280F7E49322E02229 +:0113EF0032CB +:2013F000C2D5E58330E70DD2D5E4C39582F582E49583F583E54D30E70DB2D5E4C3954CF510 +:181410004CE4954DF54D12134130D50BE4C39582F582E49583F583222F +:011428003291 +:011429003290 +:01142A00328F +:01142B00328E +:01142C00328D +:01142D00328C +:01142E00328B +:01142F00328A +:04143000758200229F +:011434003285 +:011435003284 +:011436003283 +:011437003282 +:00000001FF diff --git a/openhantek/res/firmware/dso6022be-loader.hex b/openhantek/res/firmware/dso6022be-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso6022be-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmware/dso6022bl-firmware.hex b/openhantek/res/firmware/dso6022bl-firmware.hex index e2a72a63..ff6cafe4 100644 --- a/openhantek/res/firmware/dso6022bl-firmware.hex +++ b/openhantek/res/firmware/dso6022bl-firmware.hex @@ -1,513 +1,261 @@ -:20000000020e5490e682e044c0f090e681f04387010000000000227400f58690fda57c053a -:20002000a3e582458370f922d3223202126c53d8ef323202002e00010202030304040505f1 -:20004000d32232020d00d322d32232020b6ed322d32232020d0090e678e05410ffc4540f50 -:200060004450f51913e433f51cd20290e6b9e0245e605324f0603324d2b4090040030205e7 -:200080003a90008f75f003a4c58325f0c5837302024e02029c0202e502048602053a020534 -:2000a0003a02053a0204ca0204a9a205e43390e740f0e490e68af090e68b04f090e6a0e022 -:2000c0004480f002053c90e6bae0752d00f52ea3e0fee4ee422d90e6bee0752f00f530a312 -:2000e000e0fee4ee422f90e6b8e064c060030201b6e530452f700302053c90e6a0e020e15b -:20010000f9c3e5309440e52f94005008852f31853032800675310075324090e6b9e0b4a3f5 -:2001200035e4f533f534c3e5349532e53395315060e52e2534f582e52d3533f583e0ff74fc -:20014000402534f582e434e7f583eff00534e5347002053380d0e4f533f534c3e534953215 -:20016000e5339531501874402534f582e434e7f58374cdf00534e5347002053380ddad32d5 -:200180007ae779407ee77f40ab07af2eae2d121093e490e68af090e68be532f0252ef52eb0 -:2001a000e531352df52dc3e5309532f530e52f9531f52f0200f190e6b8e0644060030205d4 -:2001c0003ce530452f700302053ce490e68af090e68bf090e6a0e020e1f990e68be0753168 -:2001e00000f53290e6b9e0b4a335e4f533f534c3e5349532e5339531503874402534f5827b -:20020000e434e7f583e0ffe52e2534f582e52d3533f583eff00534e5347002053380d0addb -:20022000327ae779407ee77f40ab07af2eae2d121203e532252ef52ee531352df52dc3e5ee -:20024000309532f530e52f9531f52f0201c1e490e68af090e68bf090e6a0e020e1f990e794 -:2002600040e0f53514602914601d24fd601024fb600302053cc283d282d28102053cc2833c -:20028000c282c28102053cc283c282d28102053cc283d282c28102053c02053ce490e68a2f -:2002a000f090e68bf090e6a0e020e1f990e740e0f53524fe601a24fd600d24fb701bc28690 -:2002c000d285d28402053cc286c285c28402053cc286c285d28402053cc286d285c2840207 -:2002e000053c02053ce490e68af090e68bf090e6a0e020e1f990e740e0f535120cd6037f8e -:2003000001036b0403570803b90a03441003a614033818032c1e032030039332000003caa6 -:2003200090e60174eaf0751a010203ca90e60174aaf0751a010203ca90e60174caf0751a81 -:20034000020203ca90e60174caf090e0407401f0a3f0e4f51a807390e60174caf090e04044 -:200360007402f0a304f0e4f51a805f90e60174caf090e0407405f0a304f0e4f51a804b900b -:20038000e60174caf090e0407417f0a304f0e4f51a803790e60174caf090e0407430f0a350 -:2003a000f0e4f51a802490e60174caf090e0407478f0a3f0e4f51a801190e60174caf09039 -:2003c000e04074f0f0a3f0e4f51a90e6f574fff090e080e090e6f3f090e081e090e6c3f032 -:2003e00090e082e090e6c1f090e083e090e6c2f090e085e090e6c0f090e086e090e6f4f0de -:2004000075af07e51a6401704390e6f574fff090e107e090e6f3f090e108e090e6c3f09009 -:20042000e109e090e6c1f090e10ae090e6c2f090e10ce090e6c0f090e10de090e6f4f07598 -:20044000af0774e0f59a7487f59b8017e51ab4020a74e1f59a740ef59b800874e0f59a744d -:2004600000f59b759de4e4f59ef533f53490e67be090e67cf00534e5347002053364804556 -:200480003370ea02053ce490e68af090e68bf090e6a0e020e1f990e740e0f535640160034e -:2004a00002053c751d0102053ce490e68af090e68bf090e6a0e020e1f990e740e0f535b4f9 -:2004c0000104d2878076c287807290e6bae0752d00f52ea3e0fee4ee422d90e6bee0752f3e -:2004e00000f530a3e0fee4ee422fe530452f604c90e6a0e020e1f9c3e5309440e52f94009a -:200500005008852f31853032800675310075324090e7407401f0e490e68af090e68be5329c -:20052000f0252ef52ee531352df52dc3e5309532f530e52f9531f52f80b0d322c32290e679 -:20054000b9e0700302061a1470030206c324fe700302075b24fb70030206141470030206e5 -:200560000e147003020602147003020608240560030207cc12004640030207d890e6bbe057 -:2005800024fe602c14604724fd601614603124067066e50c90e6b3f0e50d90e6b4f0020797 -:2005a000d8e51490e6b3f0e51590e6b4f00207d8e50e90e6b3f0e50f90e6b4f00207d8e57c -:2005c0001090e6b3f0e51190e6b4f00207d890e6bae0ff12132baa06a9077b01ea494b60e8 -:2005e0000dee90e6b3f0ef90e6b4f00207d890e6a0e04401f00207d890e6a0e04401f002c4 -:2006000007d81213e10207d81213f30207d81211220207d81213cf0207d812004840030281 -:2006200007d890e6b8e0247f602b14603c240260030206b9a200e433ff25e0ffa203e4332d -:200640004f90e740f0e4a3f090e68af090e68b7402f00207d8e490e740f0a3f090e68af0e7 -:2006600090e68b7402f00207d890e6bce0547eff7e00e0d3948040067c007d0180047c00ca -:200680007d00ec4efeed4f2436f58274003ef583e493ff3395e0feef24a1ffee34e68f8286 -:2006a000f583e0540190e740f0e4a3f090e68af090e68b7402f00207d890e6a0e04401f00c -:2006c0000207d812004e40030207d890e6b8e024fe601d240260030207d890e6bae0b401d9 -:2006e00005c2000207d890e6a0e04401f00207d890e6bae0705990e6bce0547eff7e00e02c -:20070000d3948040067c007d0180047c007d00ec4efeed4f2436f58274003ef583e493ff50 -:200720003395e0feef24a1ffee34e68f82f583e054fef090e6bce05480ff131313541fff1d -:20074000e0540f2f90e683f0e04420f00207d890e6a0e04401f00207d8807d1200505078f6 -:2007600090e6b8e024fe60232402706c90e6bae0b40104d200806190e6bae0b402048058a6 -:20078000805690e6a0e04401f0804d90e6bce0547eff7e00e0d3948040067c007d0180049f -:2007a0007c007d00ec4efeed4f2436f58274003ef583e493ff3395e0feef24a1ffee34e6fa -:2007c0008f82f583e04401f0800e800c120056500790e6a0e04401f090e6a0e04480f022ab -:2007e000c204c200c203c201c202750808750907120db8c2c975cdf875cc40d2cad2adc221 -:20080000a2c2a0d2a17e0f7f008e0c8f0d75140f751512750a0f750b1c75120f75133c75f2 -:20082000160f75175c90e680e030e70e850a0e850b0f851210851311800c85120e85130f4c -:20084000850a10850b11ee54e07003020964752900752a807e0f7f008e2b8f2cc3748a9fb7 -:20086000ff740f9ecf2402cf3400fee48f288e27f526f525f524f523f522f521af28ae27d3 -:20088000ad26ac25ab24aa23a922a821c3120cc5502ae52a2524f582e5293523f58374cd76 -:2008a000f0e4faf9f8e5242401f524ea3523f523e93522f522e83521f52180c0e4f524f5fb -:2008c00023f522f521af28ae27ad26ac25ab24aa23a922a821c3120cc55037e52c2524f5cc -:2008e00082e52b3523f583e0ffe52a2524f582e5293523f583eff0e4faf9f8e5242401f539 -:2009000024ea3523f523e93522f522e83521f52180b385290c852a0d74002480ff740f3437 -:20092000fffec3e5159ff515e5149ef514c3e50f9ff50fe50e9ef50ec3e5119ff511e51073 -:200940009ef510c3e50b9ff50be50a9ef50ac3e5139ff513e5129ef512c3e5179ff517e5c9 -:20096000169ef516d2e843d82090e668e04409f090e65ce0443df0d2af90e680e020e10548 -:20098000d2061212d090e680e054f7f0538ef8c20430010512053ec20130042912002850a6 -:2009a00024c20412000320001690e682e030e704e020e1ef90e682e030e604e020e0e41277 -:2009c00012ff120040120f8c80c722c1056080e0000e0301110101300701000201000011a7 -:2009e00000fffefffefffeffff00091212002d123f08030101010101070102040100000038 -:200a000000fffcfffefffeffff09091212002d363f171801010101010700020100000010bd -:200a200000fffefffefefefefe00091212002d123f0803013f0101010701020001000000c0 -:200a400000fffdffffffffffff09091212002d363f6080e087010201013f01010700000232 -:200a600000010000000702020707070707000000003f00003f03013f010101010702020572 -:200a80000000000000050707070707070700003f000000003f010180010101bf0702020350 -:200aa0000202021100ffffffffffffffff00091212002d123f01010101010101070000006e -:200ac00000000000000707070707070707000000000000003f6080e10e010201013f01018a -:200ae0000700000200010000000702020707070707000000003f00003f03013f01010101f9 -:200b0000070202050000000000050707070707070700003f000000003f3f010101010101cc -:200b2000070300000000001000feffffffffffffff09091212002d123f01010101010101e9 -:200b40000700000000000000000707070707070707000000000000003f47e080c000000fa1 -:200b6000ca1e0047e107c009000fca1e0000c0e0c083c082c085c084c086758600c0d075aa -:200b8000d000c000c001c002c003c006c00790e678e030e206754106020c5890e678e020fc -:200ba000e10ce54164026006754107020c58e54124fe605f14603624fe7003020c4924fc76 -:200bc0007003020c5524086003020c58ab3daa3ea93faf4005408f82758300120c7690e64b -:200be00079f0e540653c7070754105806b90e679e0ab3daa3ea93fae408e82758300120ca5 -:200c0000a3754102e53c6401704e90e678e04420f08045e53c24feb5400790e678e04420dd -:200c2000f0e53c14b5400a90e678e04440f075410090e679e0ab3daa3ea93fae408e82759e -:200c40008300120ca30540800f90e678e04440f075410080037541005391dfd007d006d00b -:200c600003d002d001d000d0d0d086d084d085d082d083d0e032bb010ce58229f582e583a1 -:200c80003af583e0225006e92582f8e622bbfe06e92582f8e222e58229f582e5833af5834e -:200ca000e49322f8bb010de58229f582e5833af583e8f0225006e92582c8f622bbfe05e952 -:200cc0002582c8f222eb9ff5f0ea9e42f0e99d42f0e89c45f022d083d082f8e493701274cb -:200ce0000193700da3a393f8740193f5828883e4737402936860efa3a3a380df3232323261 -:200d0000021373000213b9000213a30002138b0002115f000211960002002a0002003200aa -:200d20000200420002004a0002005200020cfc00020cfd00020cfe00020cff00020efc0095 -:200d4000020efd0002003200020efe00020eff0002140300021404000214050002140600cb -:200d600002140700020032000200320002003200021408000214090002140a0002140b003c -:200d800002140c0002140d0002140e0002140f000214100002141100021412000214130027 -:200da00002141400021415000214160002141700021418000214190090e6007410f0120f1d -:200dc000ee00000090e61274a0f0000000e490e613f000000090e61474e0f0000000e490fa -:200de000e615f000000090e6047480f00000007402f00000007406f0000000e4f000000006 -:200e000090e61804f00000007411f000000090e61a7409f000000090e6d27402f000000030 -:200e200090e6e214f0000000e490e671f075b40790e670f075b2ffc283c282c281c286c299 -:200e400085c284f51df51a1213fb90e67ae04401f0d28722787fe4f6d8fd758141020e9b7e -:200e60000207e0e493a3f8e493a34003f68001f208dff48029e493a3f85407240cc8c333d2 -:200e8000c4540f4420c8834004f456800146f6dfe4800b01020408102040809009cbe47e1e -:200ea000019360bca3ff543f30e509541ffee493a360010ecf54c025e060a840b8e493a333 -:200ec000fae493a3f8e493a3c8c582c8cac583caf0a3c8c582c8cac583cadfe9dee780be28 -:200ee000121238e54124fa600e146006240770f3d322e4f541d322e4f541d3223232323201 -:200f00001201000200000040b5042a600000010200010a06000200000040010009022000b7 -:200f200001010080320904000002ff0000000705020200020007058602000200090220001c -:200f400001010080320904000004ff00000007050202400000070586024000000403090495 -:200f60000e034f0044004d002000200020001c03480061006e00740065006b003600300040 -:200f80003200320042004c0020000000e51d64017031751d0290e6f574fff090e604748067 -:200fa000f00000007402f00000007406f0000000e4f0000000fffe000fbf00010ebe03f70b -:200fc000bfe8f4e5bb30e72590e6f4e030e01ee5ac20e019e5bb30e7fb90e6d07428f000f4 -:200fe0000000e490e6d1f000000075bb062290e60174caf090e6f574fff090e080e090e6c5 -:20100000f3f090e081e090e6c3f090e082e090e6c1f090e083e090e6c2f090e085e090e6b4 -:20102000c0f090e086e090e6f4f075af0774e0f59a7400f59b759de4e4f59eff90e67be081 -:2010400090e67cf00fbf80f422ab07aa06ac05e4fde51c6010ea7e000dee248ef582e43441 -:20106000e1f583eaf0ebae050d748e2ef582e434e1f583ebf0af050d748e2ff582e434e13d -:20108000f583ecf07ae17b8eaf19121357af191211cd228e368f378d388a398b3ae4fde5dd -:2010a0001c6012e536ff7e000dee248ef582e434e1f583eff0e537ae050d748e2ef582e42f -:2010c00034e1f583e537f07ae17b8eaf19121357ab3aaa39ad38af19120ee0228e2d8f2ec0 -:2010e00090e600e054187012e52e2401ffe4352dc313f52def13f52e801590e600e05418bb -:20110000ffbf100be52e25e0f52ee52d33f52de52e152eae2d7002152d4e60051200178013 -:20112000ee2290e680e030e71800000090e6247402f0000000e490e625f0000000d20580d4 -:2011400016000000e490e624f000000090e6257440f0000000c20590e6bae0f51bd322c030 -:20116000e0c083c08290e680e030e70e850a0e850b0f851210851311800c85120e85130fab -:20118000850a10850b115391ef90e65d7410f0d082d083d0e032c0e0c083c08290e680e073 -:2011a00030e70e850a0e850b0f851210851311800c85120e85130f850a10850b115391ef93 -:2011c00090e65d7420f0d082d083d0e03290e678e020e6f9c2e990e678e04480f0ef25e043 -:2011e00090e679f090e678e030e0f990e678e04440f090e678e020e6f990e678e030e1d675 -:20120000d2e9228e368f378d388a398b3ae4f53be53bc3953850200537e537ae3670020533 -:201220003614ffe53a253bf582e43539f583e0fd121049053b80d922a90790e678e020e61e -:20124000f9e541702590e678e04480f0e925e0440190e679f08d3caf03a907753d018a3ea0 -:20126000893fe4f540754103d322c322c0e0b2a2e509150970021508e5094508701575082e -:201280000475091f300206b2a1d2a08004b2a0d2a1c20275cdf875cc40c2cfd0e032a907c5 -:2012a00090e678e020e6f9e541702390e678e04480f0e925e090e679f08d3caf03a90775b9 -:2012c0003d018a3e893fe4f540754101d322c32230060990e680e0440af0800790e680e0e6 -:2012e0004408f07fdc7e051210dc90e65d74fff090e65ff05391ef90e680e054f7f0229045 -:20130000e682e030e004e020e60b90e682e030e119e030e71590e680e04401f07f147e0056 -:201320001210dc90e680e054fef022a907ae16af178f828e83a3e064037017ad0119ed7084 -:2013400001228f828e83e07c002ffdec3efeaf0580df7e007f002212129ee54124fa600ef2 -:20136000146006240770f3d322e4f541d322e4f541d322c0e0c083c082d2015391ef90e611 -:201380005d7401f0d082d083d0e032c0e0c083c082d2045391ef90e65d7408f0d082d08352 -:2013a000d0e032c0e0c083c0825391ef90e65d7404f0d082d083d0e032c0e0c083c0825319 -:2013c00091ef90e65d7402f0d082d083d0e03290e740e51bf0e490e68af090e68b04f0d38a -:2013e0002290e740e518f0e490e68af090e68b04f0d32290e6bae0f518d322e4f541d2e982 -:20140000d2af22323232323232323232323232323232323232323232323295b32ff2da0662 -:20142000ae907b06cfa9ef8279e0f50c27414e70eb404f4e58946f5ad304fd12fe04c50852 -:201440002009f609b1cede877f70730193ea77299b5536e0b728dfd37f08930d2aa25623fd -:20146000bc08ee4cef88ef9103226f44c919db00d787dfe0df108bc07b60ec6d5609f744bd -:201480005b26ff0213c841107597532e5b61f5da6be6df106a70e7605726eb818749bf604d -:2014a000edc99702a9604d10f58a8bd0534fdba1fea47f1327c1fa037613ef02da902f62f1 -:2014c0000932eb5d5f8eb302fe96bb14ce0aecd1f9689604ff03f7a87f817f2476452704ca -:2014e00098245d37bd25679c34047c8817c0f66caf116f107215fc07512477c129d0f906d4 -:201500002d417f807d445b03bd1e73429795da207f40729457e1c13a56d05f10df04fb0816 -:20152000b764d350b7365db6fd10dc13d588fee5f9027d68ffc1ca10f408e6c1c778fc3b9e -:201540001ce53f15a5cd7f9df70523446f58a7912ab0df43ff13d71de781af49b7c0d8f89d -:20156000d665df432f203d847910f10b1e61ff08ce54df23d47effe00d886f8add68e94b97 -:20158000b3731e40fb509fb7fbacc6311f201c84cb5ab6a9fa4f7d6cca20eb5a9f9def4059 -:2015a00059bcfe33ad2419615c84bf20e506df15ef06ed8ea3b0ece1d294d188db1afe2c8e -:2015c0006523db3ad75059907ecfef1af520b300af188e0ee7c2bc12be02f5a3da46accf73 -:2015e0005a05bf10533ba6b98d68bf00fc07e9c8fdc2cbb0f50aef0291459f982f2433d6d5 -:20160000f614ff14fc0182807651e44ae6707ad4fc51f781f2717624aeb49bf8b618ed089b -:20162000ee0c4f307fc1835dfc18a10d1b20cbbbc351dfa01d17ed527c8edb8126649f3dbc -:201640001470fb00a5806846fd66e689964fef0fad05da6bd382ef44db345a60dd2c7b4077 -:20166000cdd0ff5c5b0742b3fb009ea9f318d5b0ec41d31c9bb6be40bd25ff00ef53779ca8 -:201680002301fe107ba8f5e6395dedced7b4b6089d053103fb20de10f98aa16250e6f919d3 -:2016a0005116aa08aa047c96ef68fd00db287754fb089f34640fdbb4eb497f8077020dadf1 -:2016c00035897a82ffc5d106ff22ff50fb01f3287992aa4557e8ff014084fe66f91a71d871 -:2016e0003746ca4c27a8fd40f718b9ecfc24a9de8f14ef0bbd26f7256a40dd3bb060760a02 -:20170000a74dcf20ef60cf08fc70dac15ee07802f75c53705f40fd66fd212fc9fb0a94102a -:20172000e192ffc9332dee47fdc6354bf7145d4dfd098f367562ff00ae927ed13fd2fb0c99 -:201740005990b981fdb02a95f1a44ec0cba0cdf0e81ef784f38ccbc1e4e163886e74cf0141 -:20176000de056f80e932f9a07b62760bab4c39c4fb40d700ff06de81e440ee20bdbc6a8680 -:201780000c233c42749833cbff90f5a86ef178c4bdabdf129a007d89a7329bc1af1966204f -:2017a000da059f11bf016fb88776bf44b60d77517f463f10e303e504df0c59969b705719f0 -:2017c000365145099e54df2aea1177705a28dfa6eabef7a1fce31e022e21e7366710ff40ef -:2017e00020227f407b01e6985d017f70ee2fbc57f001eb41ef09f6079da7d608b31e5e3cd2 -:2018000006206a04529ecbb81004ebecf304bfa41e75e716648efd19f303bd029dbabd9586 -:201820002e327b8893182b64cf20d709cf5a7f50e315fd01ff70d31c840a77825df0ef44e9 -:201840008f01f607ce309e93f510ed823d20e700ff11ea05ea009fa0e219e6aff66274553b -:2018600065e06ee87f500c4cff089d42b9146f88ea1b3c61eea41b187422df85fa88f62102 -:20188000089a7f9d4955f2c196443620ab60e86d767028abfb4397867f009e103764ebc51d -:2018a0006c53dc40ef2afae4fd265f505d28575eb624bb0e8a1ddf34f44d35353f01df140f -:2018c000fe80bf041f146785e293733e5f408ef5ff2207bcaf8255266d50a41e8e80bf0282 -:2018e0001d64bfc7fac0fb8f4e88475e4e99fb469b55bb56cc24bf375a89d342ffa8afe4e1 -:20190000396ac73df75117245e60a6329395f32cd624de00b020f104598bb5c82a203c44f8 -:201920000701fb061b41f51f9f00aa11619a5f038ba17f40ff808fa68342d8907750f5856a -:201940009912fe00c76c6d815f7289c91f186b90fbd155217b52f924af24ff68ae5bd50024 -:2019600056766ec4af62d7c3f6c0a602bcd32be8dfa23f84b634ff0c54cdd96daf909f43fd -:20198000543dfc43bb8ae540f9073619ff613fa8f96ebde1dd195f8092056d982c917b02c7 -:2019a000c82597146d40f78d4f1ab72266b2e20cfd03ff416f104b8497e06027dc148f36d5 -:2019c00023f7f6c0f76a1300f5067d58731ab5818b761b666f298d54ff748f107275fe201e -:2019e0003c5a7b025604a799ff463f40f50bdf05f712fb20e007be23e10cf905532afd84bd -:201a000070233504d781ec28ef06bb1ef71096087424be104e51bd58efc37a00ee887c10d3 -:201a2000d0129b10bfa11b6067305212b73bab40f59200cc4f68e226f614e701ee81fe9c59 -:201a40002bd0ff409cd7fe932c86ff801d24fbce7c124ad0ce6ad1044c568f01dec691ea07 -:201a60009c220dd9a785d480ec83e320973cff91bc117e42d8a3ef85d982fb5e59819e3095 -:201a8000aaeef8275721bf11fb0af9815a1653a29f28421cdf60de866d28eb086fbaa81032 -:201aa0009a885708dfa25d19c904cbc83d220c34ff04fe0832c96ee401de2f005526b029f7 -:201ac0006a84be50eaa2f203be60c7d90e446f5abf2aff14d7443f2867580f803748a711ad -:201ae0008c309c12dad231e7e724fac1d210bf0d597057020be175216f8bef099f88728491 -:201b0000b3247b036645ff81ed207a49ab50fe64f812fd0a942d5b36ef71d4377e407d22ed -:201b20006547ff815490a61a7f00f3362aa427c356103766b348b3027600f54edfa2afe1f8 -:201b4000e31ded999f3037a8c674fb2339a9fe38e71f97a3b3b8ae09fe44fbc4dc305fa46f -:201b60007e4cf95cf5969be0ee84f15667647310ff843bd59f8a41153f42f20af750bc4067 -:201b8000540a6e0367287fc1ff10d6cbfe00fe949a52fe00e3425fccf10efd8acc09d9c237 -:201ba000ea41ce10d90d3fc22a4415345d0c65f3f227fc61ab262fe6fc01ff08543cfb943e -:201bc0008350ff0d2ec0ad2aef00ff04d890fea8de019f2a3710f240fd0ef3145b80eb0068 -:201be0009718f9315f157d84f708c8ea7ddb3e566633dc09a7da49b1f784d3246e10df4ce6 -:201c00005b86bc04eec3af413c627dc6de1373d85d43fc16f310d5a0bfc9f795cbf28f22b9 -:201c2000dda6f645ff80e82c846367e0d60f7a499fbf3300c2141d61aeb0fe6193867d7acb -:201c4000b190dfb2e92dee00b639fbc67d92ec0a85267651e9dcf680df89d502e7408794cb -:201c6000db24ff46bf51ff88af40bb20ad37e3e27b0b37a08e40dce0fd98ff267eb0ba4944 -:201c80002d8277a19f008f60b985bc84fa47bfd49a84ba4d7fb6c29ca250b425cf0bd980e7 -:201ca0003357dbd235261563cb18648a1d62fccd97a62a52fb6bb0b57f02032af40dd91adb -:201cc000ec02bf41bf00ef008c1cdf14b909dba1af6afb46eb04fa04ded04b147a020c941f -:201ce000fc00df00ff024610aeab0b020f3cf6c0ab23777a7043ba20a468bf3afd026f8408 -:201d00002609f7c4fd79ef64efdf9b037508f3937a2fe41df4ca2d21eaabf0407b36f12c58 -:201d20003d108d84bd81d7197e0cbdd607f8cd133b0cbf8fad5a73b8de18471bbf24fa47d8 -:201d4000b9acdc46ff112f805fb17fafffc4db67df00ad00cb02575c1970342b8a41ecd57a -:201d600006d2bb20bd28770949329b94e826e8b18e35d5a5f901de898b14fc0b7b88e32da3 -:201d8000186b370877cbf3c3f7a8fe10f7004f96dd80b79cf22cfc53fbc66629ee20a009dc -:201da000bc9a479af359e3048fe8fb30fd4affc0bca0db00ef12ed4fed9ecf10ea29ff80a1 -:201dc000fb0a59a0b6d0fd0b37846e81a537e308574a748cf384df0a8de4fe80e502b90076 -:201de00018817d026609b04965d13ac9560e6d4d4f08b55572b275b0c40d06cddf81dac0c4 -:201e0000ea8bf501f5027d04f50cf6a0f987268841887f6255c1e3269a213e40c5806be187 -:201e2000f4a05b8af60a7e402700fb006e0c7397c748e98bdd7cfa04cd7899fb74f86148fd -:201e4000a9be9c637b48eb36db00e722d6a2b2328f2c7722e843f532fe16f85cddc0e5a0c8 -:201e60008580ffac591033b59e80c750596df311220e9a655bb6ff8cf90809490b51552d66 -:201e80000837f5ec1d245595b203eb283f406b4da5207b205f1a7c22fa9eacd1fb04c5eabe -:201ea000c2043fc0ba6ec30af947fe058302a30ee316521ca7394cd4e7a0ea913fb86f2af6 -:201ec0006706b61033ca77d1fb057b4b66046f14fda03714fe405917db38fd083f01f26295 -:201ee0005d8e7dd15532da80f724f1287e44bd13a8109d707f40de506e10fb00f8865df705 -:201f000012918e87fb23dc2cffc81f3d9b10de184d6a31083e856e00261b955ab612af74e3 -:201f2000c4c7c5e5f700114ae2687790731e708ccea07bc0f30af70125c7750bfe90ff7035 -:201f40000e22ff02e7403d0699642626ae2dec38b5d4ff08df08d4303d5e9fe45c259a24c6 -:201f6000d6b8ebb0bc142b3ecb50726b9d63afc0351976535c892f80f705679ce225b8a18e -:201f80009240e50dcf0b0d96bf01798fff14fb2267f0fe29a9816d10df22bd855f94571244 -:201fa0002ec0ffda6bc24d598c696d06df00fd22d4309ea0d221c935ffd3df189d683cb034 -:201fc0002415bb1bb872d522ff207400dfabff42f5933eda7fd02a480b005b68ff087301c9 -:201fe000f185ce8bab44fe245386de063a42f9e0fbe8dd523f50f40d671aeff57f55dea393 -:2020000018f690ee78d680bf402b29cba3ff0c53487d107e00efccb100e7827f11eb905fb5 -:20202000017f13fe5877065a6cdf206c62f6251fe5bfa36e855b42ff40fc02d9387e80ba90 -:2020400010ad6c3e4abe11ba10ece4850bf7646f50cd205f84aca0c735ef28da10fd02fba9 -:20206000205beddfdc3909ff67f759964879535e94f7829c213fe2dfa1de80ed66aeaae7e2 -:20208000804e0833012f815e707b00f36497851e047b16ba6a12cc3d803c90ef813f20efce -:2020a00004fb02b8cd7752661b96205721e7127360d70a7d0079abaa19ba092728af003719 -:2020c00020ff5125049d145b6cda65eed3f3533c34611136b2ad413f01b86ccd01ff98e345 -:2020e000027c20c521dc00ff29b4a53528da58f9768dc67e285d09b80df580f90ebd047f21 -:202100000073609b24bd20cfa63708fa54914c7b90d5681f44ff827f691ec6ca25a581a6be -:2021200046de2bfed0df907ba067037b14bfd5fb60d329770d03d27bd2f6863b69bfc98640 -:2021400014d6231ecd65327d12df90ff2933919481bd129f02b70af804f1bc334653d17bff -:2021600001efa4bf0bbf24aea4bdb7b44697023a3a7fa2f500f959f7edd6a8b138de0a5a5c -:2021800020bd03f7a0f6044708ae10b41c79cffe817a107f12fdf4958c79c02d68f5c4571f -:2021a00024eb60e4041f04fdc4d1005f45cc162f22582c6b18b50c97886f00e789c74b3f25 -:2021c00009eb40e7a14fa55b346e1ee3c5be80a61eb5409fa0bfa37d9a2d2bde80d5477794 -:2021e00000b626e1e5c9003f1565687908ef64d38a5d10bfd4f94679899b2a5a24cb00ce05 -:20220000207a89164d4a8b3e51ff84da009e1d7321ff187b0bef1c6702f8883e10e6103b18 -:20222000005f04a30076a69f008c60b304f188ba45dbc87d807f0cdd3ca228bf345723ef58 -:20224000007f08ff0ef7b4d3faee4f7d54ee03ce40f3edcd029c927600ee413f11f944b79f -:2022600010df33d404ff403efcd21767e21c033f19d635472feb0afcc06b90ed2e7780778c -:2022800050de50ef82f721ab02db226a28c7006e13ce085720fb455f8b5f08b793cd68f264 -:2022a000049f49ce2dd34af515a31ef23d9f118f90f11e3d095b04d9b83724c353f960cf73 -:2022c00018f3c59f152752d4a5e100a58c77e11b287780bfa2fa8638c6930b6e680f62fe22 -:2022e00048da0d7f30fee92d04a306bf5435c8cfd18bcbbd5df6096ac9bc12fe1ddd90efa8 -:2023000004fd06d550cc43ff0c6b20db243c00e72afa00f921ae1c7e00fd01dd25b4c0bf11 -:2023200004f92293a6edf5f8105f089181fee10efc1f428f6ada312f12ed6087208428f7bc -:2023400010ff483ede5f01fe21fb218a552323fe28f9d2f216ff65f62e7ce4c278d7a8bcf4 -:2023600000f4cd8b6bae81d58cc92742a2beaa5a32cc8e5632b3254d21bf15ec31ff74ebd7 -:2023800000f284fd414e117e6a71d57d2e3f402608f108f7a8db02ef50f7c4e5206887be83 -:2023a00001bf0addb60700b52ad94754bf0caf3599c750b723ab9aff4ad708d7389fb61f3d -:2023c000037c40fd04fb12bfc83f44bfc6dda237ad9f24af88f730e751f3427e21ef73842b -:2023e0004c7d00ef84b3317f403ea38e72ef3bdba87c38ab1695185da3d756de62ca62f1c4 -:20240000407e10ff0c94cfdd88dbd077805d18ef156700de3ae3f1e7257a120700f68297ff -:2024200002fb8477d063d0af80fd411795ee70af08f903fe08179b44cad709e0409e285a91 -:2024400006fcaef754d999eb82ee9cfe034402ee201c087e0491236f59570cfe69c509fb0e -:20246000239dea56a13cd77d15ffc29f387700fc2c5b447400ddc574c0bf0ee4c19a0daf2e -:2024800004f515ff20c8d80e34fce14a146345feccdb43af29bfa3df57af02fe283ab8f633 -:2024a00000be53fb19f301f10097a47ef0270f7a20799aed18d485fd993b94a440df1beff6 -:2024c00000e34b7e0871837f06bc30ae4cbf14c883d5726348ba12df9e9810f77ebfb2ee14 -:2024e00050ae2b9c01b500cf2dc93c9e04f864f3f0b4a5bf1afd807b0d2a15c22fa831aa95 -:2025000004ffc43d1c8fc3d540ff006109cbaca300dd04ff22c90c7b02344afe207732ce4a -:20252000808629cf22fc96d881ed897a3d6940fa1d8c3cef22daa19182bf41fc6517a2d64c -:2025400050df30574df520da40ec01d688bf6a390c6babebe3c9156e3355e43f02cf129d35 -:2025600090bb08d2403fa9b337c9f04d11ee28dd16ff4e750407e19e137eedefd57008b747 -:2025800002572dbfa0ba63b37b7fb0e741ebd2bab8cf563564ee90ef12fd127e07faa673a1 -:2025a00001afca30847633dc65f9007f4b9da8bb8afd245dc1bb09fe367a30d3425f08f663 -:2025c00000ff072ba03d0445d4f702db06e5843742b7b27f4bd441ee196d047a11fb10efcf -:2025e000107729bb927e087d00fd281f21a2307b467f84dda1d2054b08f58066caaf043fa1 -:202600000068102ec1ffa287d0cf264ea15dc633e456289684aa57f06c57227e009c75eb55 -:2026200024ff87798cfd92a904f100464aa0c8fdc4bae8f44c67210c80ff23a3039c0ef1ac -:2026400001ff1f6f061f00b3a1af907d00bb297750bed0f5fa7500fa3251804f8896939e7f -:2026600004df2b7f6c76458b50b3482f80fea46762985b7c0afb8526417f1377e7d434cd91 -:2026800042bea86b403fa1d7a025833c4ce6387728758bfd08f729fbe0c92456c0fb0aedae -:2026a000087750d40ad50afdd2df64820acf02fa75b103ffa40e29ffb5eac055c04be23f43 -:2026c000147d6398447f0cff25bf49d308f9d817336f44d8909a282ab8f900efd26f0cfa8f -:2026e000d0d724ce3dab8e3713cf4863b04f46f2063840d4928f13f742fb857e6293437b60 -:2027000092d70ab603bf02ff70e972bc0226827f547a0d9b035f0aef15b752c308fb15c48f -:2027200051ae81b091d801f790b782e289ebe2db007b2df543fa20ac007f38c723ef00ef07 -:2027400004cfc8d31fd542df74faabf7fdb776fc016a0f7f802f0dab04fa0cf121fe63eef5 -:2027600012f788cf14bfc37e10d9caaa40d505f081eb20ff417e42af3931147a28cb58f709 -:2027800094eac8348afd82773ecfc8ef3fb868ff869f42a5cbfbc0f610f55e9d00ee30db9c -:2027a00020f3d41d99b739aba75905adc56634a105b5c81704fd86f938e5013e30f7a87f6c -:2027c00002e250ed0cbe5f8e30df44bf32ce40efd89a0957087de0ef50fdc93f8825185b45 -:2027e0000055008a4cc1157b447b787f486e14e5c6ffded70d7b52fe176627c983ffa2f322 -:2028000000bdcd8fc3b309ff25dead7b99adca5720cd03ff015e00ef20f7b4d490d7027ecc -:20282000004741bf0caa01ff43f2e87f20ee4c9542dec5fa805d923d8cff01ebc4373b8454 -:2028400010394ccd69f732e3865b4ebaa0f321ff803404e720b72c7340ec047e05f54817e9 -:2028600080f714dd285bb2b740bf9aee02bd447448d5a0fd01eb752ec52a40efa0f3917704 -:2028800008afc8f3e0ce02f7006d197406bb02fd24bc50fca09ec8fa3566b193e0de94e721 -:2028a00028fc00f55aec010a20ff083d02794b251dbb716e56ff2025632ca0c9837faf3233 -:2028c000087adcf164df027dc27f48fbd6b05ca125f4c8d220db42bd02be03d601f5c9ffdc -:2028e000817bd8bf05b380ff0987793380a600b652f302cdc0bd0fe47193907b2cc1247ed4 -:2029000030fb50fad29f00fe7fe8a40310e72f7e42dbc33a20ee00ffc26e8a17120e4c1f9e -:202920001187686506fea8cb09df7c6d167b627871f19ad304ff12fc98bfb93f9b37949159 -:2029400010bf113b5fe370e208be0df6607c910507ac806b2922087d88ec280b2b4400d732 -:20296000d2bb5ab546d712ec812c28bd003504edf016977f1aff0ebbd1ff017a8af164dee2 -:202980001c6f145c140f177100afe1d684ec683787d700bf8d7592ef04975063c0faf983f8 -:2029a00010fe84ef8dba495e83df62dfa019a0ef61e90cf6603f11d704de88fbb6b647ae24 -:2029c00003dd40ff54fb15fc137bb46e808d10ee8237a74780df12b743ab8a59a72604aca0 -:2029e00000ff105522f64b6b07ff00fe50cd884d00a71853729041be98bb8affb1fe60e9c8 -:202a0000057d227b583a20dba2435acc5e7940e25b3c43b7235cf205cc2b05b4012fa08df2 -:202a200064a440ff5b2f267de0df00ff8c3aeb4ec2b5a1b664ef285f6abfcdcf2dd590c79f -:202a4000419e13fe1175047fc23f0bfc216922ee07d7414912dd00ff43dfc5dd07ba0e7e74 -:202a600008f59267c3a2d19f20ffa020346a20bc55fd4ed510f7a1f2d84e1d983a9900b8bd -:202a8000109f1a6a18f300fa50e44897b6d605ff027911ee9b6d867550ea807f155704f73e -:202aa00000d8400c00a474253b420af3059945dc55df042e50f5864104ff80ec91ae8400d8 -:202ac000a48e00313669107103fdc4d699be713731f680ef54df50db007f209f70ef2927f9 -:202ae00000beeaffe0dbc2df18f5995740390c6f4ba334caf51fdb4201b740d7c87f609fb0 -:202b0000080b78e3257502fae07d50fb4a5f02e8519e40ba2abc543f843f63dd60eea9ff1b -:202b20004cdf00d901af05ad92fb8a3d09532038092796c530f9402f00ce8cdf8635907709 -:202b400052b7401fcd3d43fac4ff38f9007d01fea2b93d7748b611b502a45deb04fb206412 -:202b600044ffc0b6b183110c487a00cf2dfb10ef4bff50fe7967dcb36add932a045d326b8a -:202b800088fe33c70bcaa73841de029d82aac2a61a8f047b06f5f073c1eb14b707f224bfd6 -:202ba00000f731ff2067a1f3d375222e6aec8a3634fa00af63df00d718fc04bc823d003f5d -:202bc0000272a8f480db43ae71ff28430c9a90650bf328d3288e80bb603f41ff00cf041f68 -:202be00000e5b02b196c9cfe12fa24fc05f205337ad221f5645d4aaa05fff117b13b0edf9f -:202c000034ef17f689bb16ff013c09fd057f597f1067987510ef49afa1fe00deac6306f18e -:202c200012cf02c181afc7fdad5c28f712cb40ad25d7b9bc043b0c75409e09d6b3f741e74a -:202c4000114e684bced15c3b82ff249f151113b716de04fe40bf215e84bf4a7fa5bf03769b -:202c60000366467b20ff0043c3cb20cb05fd01b1b2bfd0ee12e254ef48e7a43972ec945fd8 -:202c800070b0ca6b15fec0df30e00ca65aff41ad6ad5a97712dba4cd64bc3684024d924e5e -:202ca00000ee672d8c770cff70af077da2fa6da3ecf9c4dd2cf2c09815d794fb224710eb5a -:202cc00001663075408323ff54b112f713de80f645d3ac4b087b20bf01fcdc7b74fe50bf48 -:202ce00050cf1d96662d410e405b5b9305fb18fd085f48184359aadb83bd5ad70fb01c2f1f -:202d0000a5c640bf00fe88631a545dc7c8fe24b3087d07c921860c5ccdab61a901df84edfa -:202d200018cf623f145f06560d1608bb166c5cda4425163ce64aba7634c9005dd21fa0eeaa -:202d400042be27ffe63d64a32ec556b9ccff24fb19e3016a25d843f140ad2a470b6e3e0a80 -:202d6000007aa0bd214daafc01f17de920c7017495a6448f51cfd1374abe837d193205a680 -:202d800070d901ff18e640ff20fb1ac170c60bb646da0a3e806f01e619d68a3508df08e7fe -:202da000400e01bbd8fd904744d46c52865852f629de644f83acdeeb9fa5206f2e27c36e56 -:202dc000c81fb1e609ef5d7ad40f42fe2dcd08ffd9f900cfb4b6433c6dbd819701d708ba1c -:202de00050ff0bf51876d17c85388066adef9bfdd40fa06fc1fd9ee3d2ee92f5b16a4e777a -:202e00000077c45f08d32bc6cd9a4a3f529d7bf441c484a9921a59b9136162b941b760ee39 -:202e20000079a671b0bf21a5e09f205f515b47f08f29524b02e43f70067a40ba081d14ef60 -:202e400000fc40fe003f867e20df08de68fe88c3d8e35bef027959772937c6fc09ce74b9e9 -:202e6000004f06b1299dc5b014ee0be51ede64ff00a820c4887302dda41f90f753dd51abe4 -:202e800020bd202702d210ecc2c7189909fd405d407e04e809f998fd863f08dc00fa1c7be6 -:202ea000a07f08a68cf609331a9df01f40340ad7c0be80bfa5dd0a75a49848282571a9f8cb -:202ec00084fba09524ff6c9bd84f90b380bf407f81df016e40fe1afa18df04bf02f300b329 -:202ee000419f03feb99de09d08bf524f2991885904e740ff00fe694390f9b29791b955ca0c -:202f00005abc428d081ff82720b51cdee2fa29f9c22fa20e511500ff00fe038f36ec94f37a -:202f2000a0ce98fd805e06f404dfc3ae007992392ad700ff6967325b62ff20f500fc102c13 -:202f400024a7409700df803588a70cb801fda1ed5117507e2078279d06f8e65dd1ada6dfe6 -:202f600000a31876107f10e709ac0b7005fba835807f487f143d6596129c50f27696a80bcc -:202f8000a1dfcef1b68872fd800e74f382bea88943bc081f40f000ff48bb614a527d727922 -:202fa00041fdd2b660ae405e85e9907d21fb30ed1cbd48e7cdbc61fa0c7bc35634eea11e7e -:202fc00008fb41f201ff05ea02cc02ef216921ef76f384160f1c8267ca7a54efec57cdcbfa -:202fe00000750a75a01828ffb053a1cfc9ef12eda0f621ef45f688314aa30c7c242f8c5f87 -:2030000009ec30f730de1c5dc8ef08ba01a790b346d642ba20ef05ff0aff03fe616930f486 -:20302000209f9ac567cd43e5406907f06c7a74a76ca755095e64a4495c7711fc1e7b109e2e -:20304000009296be02af506f20d8327e2d53602f14fd698301ff80e53488a07f16ec040917 -:2030600014a803b541a654ff24df52087527206908fe80b718b3249f31f9405d49f5a99a0d -:2030800088bf00f20569427d06d1107e2c79747f0af7d57ff0da00db83f5f2e900fa08fb83 -:2030a00008ffcc89a05c01e52cbe5edd42ec394e202d92ae027f69b62adb4bd384db44dd28 -:2030c000007c7ddda08d905308b703cd95fb09f618b150bbd45ea35f28e9c89800f742b783 -:2030e00000ff808ec0b601fd413c74d70ef54ffd40c312952596d0d606e67092e89f1addc1 -:2031000032bf155da6ff0b79007f63dd87fe80ff80af004fb43e085b62f9f53f17af209e7a -:20312000c11761f169e9a3ab86c24af50ded0dcfd08600f5405f20af11fdc987543540fb8d -:20314000483f407fc47f0ab962df22dc24e4409e401e60c18ddf20ff01df2fd710e303eb2d -:2031600020bcea4504d10a72807714fc089021f890e92c8d86afcc8a0adbcecca0bf0acec8 -:20318000889da33e60ad827d5ca7e2f604fe28ff29af56eecd29563e877224bf33f7b2befd -:2031a00040fb83af0aaf077508da0a920adf9a2680bc80b5b49dca9014e342ef6dea1d6f1f -:2031c0000c3f319f68da4d4f43cb49fa30fc04cd28ff825e857d65b721df06bec4b811f240 -:2031e0000afe315dc58e6ced1afb005f1337897848bb40ff46ec01ba25bb637b645685f0b2 -:2032000011fae12f102f04af41fd0cfb87fe08bb82de92af65d509bf5bfe613e11bef5bff6 -:2032200004fe294ea0dfe1ce8cf7d31b00ff1897416f047646fac01c412683982ce291243d -:2032400000fea0ef116d89bb24ed81f515bb117d41bf88ef3c89859ea993117f13b354985d -:2032600080db005408d4156d69ffacca05ee9825491d1b7fb44fb173e23d6057abf085385e -:20328000409d2a2d30b81d7206ae75c7d1ae3afd00ff18f41bdd409b4afb027f9b7ea13f40 -:2032a000c0ef12b900ab6619806b1be92a150061244eb8f370b230ab208f021f32f728b2ee -:2032c00001c724b7503510f700ff4052d6d648e34b7eca9451f7c4be02073aef40df58dfde -:2032e000005f02bf1a468cc5385dead3146695c90eb7a58b43b509bae5ad123f0779807fc1 -:20330000409d907d05f71b55239a0ca364c9d46b207f34985896a0ff641fc0bbd2fe40f782 -:20332000099e38e5c47ba85697894d961d1b81fb0bad0c7898ff401faded45d3585e3cc639 -:2033400052d9147b407f0cffd9a3204706f9047705fc00ff537d10d70af7c6eb0efe005fb8 -:2033600080ee10a714bbc1efec8d44b7f7bf2ede4af308df06ff208d0076143df02d80ff3a -:2033800006bbb1d8a0f9487ec9763467d04bd3f0007f42f233ff664107eecc92213c42df6f -:2033a00000ef803e4ffb095d48ddb4fe3f5ed021516b25f8007f8af6818f817900fb8dc51c -:2033c00050f334bf003d18ff32ff10ff829d10b492ff18cf827b205ff8f32add078e293f5d -:2033e0000a4f04f289e7ccf7d9770dc610db00fdc63f08d50455c0ab0c4bad3f357711cad0 -:2034000006be12be49ff62fa0bff5af3204fa831995146f62959409f20ef00fc448d10df83 -:203420007ada84b50a5d00bf00b5a96a144b821f397a20d749f77ce5a43a20f717d7a43e01 -:20344000e17e92f743c869f639d8306514ffe1d504eb22fa18ff582d82ef62ef806f79fedc -:2034600018f58ac9296748ef4927805c417e14a67db2d7cac1fec53b1233613e2bb38ab7ce -:2034800004f362df81d614ff886d13ba883c12fff6cc22f788b342ff00f745dda0f779d79c -:2034a000b0f370ed49fb84bf44b880ee00cbf24c10f500ef01ed67abc6dbb3e3a8f3900eae -:2034c000501782f87cd395ea07bf30cd0f9a2499116cc6b54adfd5e633c6cb178defb10b25 -:2034e000007f875399fd0aff1cff002f622fab7d09bf805b505cccb34d9da0f590a14669a4 -:203500004873483986fba4ee09f850ff2aed0ebac3a7286d0a57b0fe44fcc5ba887740fe23 -:2035200028df2cf6815b086302774ec913fc84b388660cd904f308cbc2cf46f9183fa12fb1 -:20354000a0dfa87902f608fd20b4d2fa10b218b7369b482910dba0fb20d9557b116b61cb64 -:2035600004f9a695039e15f72e86a825706b0a3cbbfb58c7b9062ffc00fa9517c23b22f749 -:2035800004eb14e710ff16ffc2d700af182d51fe448d407e68bf05dbcaadc6cfac7e80f7fe -:2035a000136fe6c9b9e6815b20fd063e403f68d6819c83afe7bb32bf11e720cf44211647b6 -:2035c0000afdc2fe105f00e90edea8cc1e94619f7a5d600f866c41ed20ef21af03ff0d3f27 -:2035e00008eec17f10d7acf1206dd668401c6963003c1638097b822f825db0cfc4d7e05531 -:2036000024c680df552b02bc34f744fd06cf04e189fca37c47f5007d1e7fb71e80ff22dfae -:2036200000fb187b42bf86fca9ef93de36f7b3f4e07103bf008f811fd1ce549f5b140c73da -:20364000a02f18ff15a4b0f6259f55ff60cd18fb04a7087706ff1b3b14dd44f541e6c65fd2 -:2036600044aac23e889516ba634d1fd528d96085238ffafc6e7744ad25de95fc87bcc3bf0e -:2036800041be84ffc0db18bfa5c3009b52ba902e89ad0edbd448906d48bd8069aafdacce22 -:2036a00001dda8dbad5f04fdc06fcbd8b9e72de200d121f71670a223c98e63bd80f729b719 -:2036c00050fb58f6885f44af0efdc8df4c7d20fa400900d1247a284b037630e7c64b10f412 -:2036e00000d748ebe9d311ffc07b60fb65ff05fbc1bf2cf166bfc87702a683cb43ff9d77ad -:20370000006730f740fd4cbd17de20e564d268af142255ff13ad82d740eb194ec3a9823f2c -:203720001162409715bfde6361ef477d40b6003a186b817b937316e914f629ffc0fa48bf6f -:2037400080a304d9a0de182780f784ff2ab561fb1077003c00f8183e816a844fb66b11ed89 -:2037600000fe16457b64005502df00ff20758aea077bc11f572a14ff203f38dda4c75edcc4 -:2037800080f3a7ab44fd01fa562cc15b616500f901eac4fbc093c08fb0723ce70cfa00fe36 -:2037a00020d68f7e48fe1c2b68eb407390ff047a1a7441fd40ee20df21e38ed10ffda25ffd -:2037c000017f59b9002c10b149e69055c12c411350494425137f83cf4a6b9bdf904a80f6b0 -:2037e00000db282b28e34ef77f8a06ced0ef19b7d629c0ce55f682bca07615ac137c077ddf -:2038000028f538f86d8f80771c931df718dbea9e20d7807e1063226d99ae908b649f08fcca -:20382000009f1978093ec87d50c71196c89bc0470c73418306fe0d8b41cec47fb63335da7b -:2038400000e7fa7f00675a3f20fdea879474cae3a5dd4aff08bf409918e1a6b218ffd2a67a -:2038600008ef237b146fd9ff40fbdc59422ee4e910d730372267325fad4905fde06b41f7c8 -:2038800040ffc96b925213f7267b8071486d74c728f3fc3c42c102bf152d53f5a47f05eb91 -:2038a00000bb80ee2de842aa289fc4fb21fb80e9e55fcdb5a2b900e255f98a5f7a9061fa34 -:2038c00020ad80bfba53609f61eb10fe1d7b21f68a3b0dfb007f04f5cafd963d011b167b3b -:2038e00090f6569e00bea0f837d99aa34056b81fdadeb57d42dd03d7ddaf2ae167db4a95a3 -:20390000247e44fc21cd0bf74afd8cf12b6c207f80bf069e0220034f89fba4d780bdc4770d -:20392000007f18b7f2cc50544eb978a646bd525f3dd68df158eeac0f0bee4996c32218ff98 -:2039400000f3dcb12bb8c1f801fe49f7137ba7dc493746fd279d00d10cf74c7350f348bd99 -:2039600041f5064acb5f0c42972e844d8b3768df229e174fc4ab32e1d2ff02fe02771a732b -:20398000023f404bc0dd417bdcfb9ef680dc003e011ea0ad0eea759c25f6823c02ac1dfee6 -:2039a00048b4d5e663f7125f01e64a3f9c2f07e51b67875623e5240aa4dc889f15ef649f1b -:2039c00000f19566526f583904e96039413e98cf509792560a7da0a510ee50be72ef48db12 -:2039e0000df72269f01df3a8e0fc29f68be985c677dc20e74a7a19e5026b07f39d0f9ee722 -:203a0000220310fe35e846d944f5688d1cea0e92cb8adae600fc229f68fe44f348a552e1d4 -:203a20006474499e9f8ce0cfb07b54ee99f211ef0bf4367355f6a0332573c6bb08ed826f30 -:203a400004fb46ecd933489f4dfd1013428e8b1d2867b8ff2c6f8a2500ff01f3d0bf101a21 -:203a600042cdf2bca09f1b25386730bf50f7226d72ef902fac6518ea9c6901ff84ed08fdf8 -:203a800020ef00ee0cde01ff80e100f388ffc55828fe27ff077b1097082b42f73df24ae508 -:203aa00000fe26dba43b89d5ce74aeb9e28c41fe00e70973048d8d6dac5c92d6715fa2e4c0 -:203ac00004ff00f730e7d0b6aef706f9802ec631b4ef453fe0dbb27d007701bf0d5e23f739 -:203ae00005bf9ef3609f2576ad8d003d10ff58394cd72b6523ee0de680bd209ea4f702f47d -:203b000070cf28f004fec1b718e315f318df8477196380ed5239c7c3626907e5c2f608ff6b -:203b200010af11fa8e7721b768e930e9696a80de81ffc44f24bb8dff24e529c95695c4f5a6 -:203b4000087d348352bd08b6803e86dcd0ff83ba44930aba748708f390f724abac6800fb3a -:203b600028ff45fc54b0b84d44bd4e5b01fe70f3e9ca09f886a881aa40f544bcf03742fc21 -:203b8000083b58eeb1a74aec316e79cba449f880055d0876297d900f00db52c5226f22e715 -:203ba000044d2cdf00ee80fce02f49c61055483f7cfe42dcb6ff035e866540831ec408fef1 -:203bc000006fa0fd20e58455992f44ef60bf41fb9603b25f137919f683fd08b342cae04fea -:203be00020ffe89f4aecb89f7a382f5f24ef28ab20f54af251d773af13b6c43f36b351dcef -:203c0000207c819f22d7007250b143b203dfc2ee00f310ef45b708e52a3f00b292db229fd1 -:203c200000b3293b605a10fd297f967758cd14eb71fb297ec61d02db093ba2ff91baa47ea8 -:203c40000056096f475c8073a87ed1c68affd645007f253f44f7dbd3b6d7903f149704ffc9 -:203c600098e9d87fb8f6b9ad47eb013f14fc46f6c8fe527101fd305f249dc5fd50cf50eba7 -:203c800030df4dfd319f3a2e027f19baa03b36af1535423c8c7a29f329f731df88ff06be1a -:203ca000a8afe4d3103d11b780e412e7bb45603fc2d5204bb7f8386b03bf24ffab9c589875 -:203cc00002cf48df831f18d74c5f80ee41e4325600f62929b41150ab806b11fd56ff8a674e -:203ce0003877a8cf81bf82dbd40c00f3b3af1475505a4b7b3e6a60af940828d42057822269 -:203d0000146360fd30ae00ff40c727eb1a69176e40bf95e344fb09fd01ef24dfd8bf841fe8 -:203d20004475036934eb004fd12a2039449f8ac15ee201f82dfb21ce9f5e203f6b1a0fd45a -:203d40000076d6bf12c6cc6792bb58d7909bd9e4155fe41559ba409b06e5a1774637004fbf -:203d6000e25700f482a6e2d6d19595f5264cd472409634ec18cf5443c03de83c58eb423f35 -:203d800020ee00b790f6687e90ef00ffa2c412fb167e94f5802f83bd12f218fb227b857745 -:203da00040d3465e446fa05f5a40c0df01e1c0bded5e78335a6a33c53162d045ad625086c3 -:203dc000007d403f047c30e22cff267c4cfdb6a509f903ff40ef3a8a04fd00df54ea116d51 -:203de0008c794ad33cc40b34b1d360e605fb2ad0e9a5893a027b33b7947d606f118447df4b -:203e00008046705c02fe4bff80ada1f3c87b22fc89fe0a7f19e842ae1dec24f916fd5a36da -:203e200048ed095f21574fdb801f18e30ebac26e947721bca4b962bfb1da05af0239c0bc55 -:203e400000ea5937304f41ff20a31cdb00fd25d709fe3efee0b3903f91e704efe2f388fd0c -:203e6000023612efa8b932af502f07fd72bbaabd25254436847630fb48b68d378794c1ff2a -:203e800082733462c3db9625016ca1ff86fc20fb00eb4eda00f98cf620fa9678112e29e78f -:203ea00012ef08f5b0cd76eca16b8fef50b7f0ffe58d1dbb8d970269489d19e80837213650 -:203ec00008fb0ce8e0bf84ff00ef3db705bf16b45a9600bd223a224f3cfb09ff06bf1a6e52 -:203ee0000651555f197688f225e72abc030f034f849b4a9d94fd49d8b0d690fd62fb48b632 -:203f000000bb8c29401f0dff41418a4f0295a3fc415db46f01df40fb0cf75af744bf197e6b -:203f200004a70016538fb1b30d7b18f7127b15eec17ebadafabe803a00ee343b28ff05f88d -:203f4000827fb02be8bfbba780ff4efb239000cd30ebaa1a0c790efe821e408f8543047811 -:203f6000004f8e9484b194bb82cb02e662b406b2a0ac08bf414f0db4132828eb12eb9c1de1 -:203f800040bfe1df90e7517f026b989f1aefac7a087840cf417f33da4ebc2e83cd9b212b7d -:203fa00099e420f06fed008d306f4037bce62bca94249be509fd440f67d48d9613aaa46925 -:203fc000427c205f448c10db677d24d741b0401f11c1d5dd01aa425b3076507730df127ee2 -:203fe000986e3876352712e727d362d738f785bf60fe447eb0e8a154a166043f01ff5bf7c9 -:00000001ff +:04000000020059326F +:01000B0032C2 +:0100130032BA +:01001B0032B2 +:0100230032AA +:03002B000201FBD4 +:0300330002013295 +:01003B003292 +:03004300023F0079 +:01004B003282 +:03005300023F0069 +:1C00B200750800750900750A02750B00750CF4750D01750EF4750F01C200C201BD +:0300E000020056C5 +:030056000207C7D7 +:2000E300AF827401B54C0280157402B54C0280127405B54C02800F740AB54C12800C7E24EA +:2001030080107E12800C7E0080087E36800475820022EF60067D707F0080047D0E7F00ED9D +:20012300F4FFE5805207ED5E4FF58075820122C2DC32C0E0C082C083D2005391EF90E65D75 +:200143007401F0D083D082D0E032C021C0E0C0F0C082C083C007C006C005C004C003C0025F +:20016300C001C000C0D075D0007582001212165391EF90E65D7410F0D0D0D000D001D002C8 +:20018300D003D004D005D006D007D083D082D0F0D0E0D02132C021C0E0C0F0C082C083C085 +:2001A30007C006C005C004C003C002C001C000C0D075D0007582011212165391EF90E65D33 +:2001C3007420F0D0D0D000D001D002D003D004D005D006D007D083D082D0F0D0E0D0213224 +:2001E300C0E0C082C083D2015391EF90E65D7408F0D083D082D0E032C0E0C0D0B2A2E50EF4 +:20020300450F6019150E74FFB50E02150FE50E450F700A850C0E850D0FB2A0D2A1C2CFD068 +:20022300D0D0E032E582FF30E00BADB17E004305088DB1800353B1F7EF30E408AEB14306ED +:20024300018EB12253B1FE22850A4C754D00905DC01213F5AE82AF83AC097D00C3EC9EEDE3 +:2002630064808FF063F08095F0400990E640E053E07FF02290E640E0FF43078090E640EF19 +:20028300F02285820A7401B50A0280057402B50A18AF0A74072FF50B90E618F090E61AE5DA +:2002A3000BF012024B758201227582002290E6F574FFF000000000E5BB60FC90E604748076 +:2002C300F00000000090E618E4F00000000090E61AE4F00000000090E6047402F000000085 +:2002E3000090E6047406F00000000090E618E50BF00000000090E61AE50BF00000000090A9 +:20030300E604E4F02290E6F574FFF000000000E50870057E06FF80047E027F0090E648EE18 +:20032300F022D2871202B07EE87F03EE24FFFCEF34FFFD8C068D07EC4D70F0E5BB30E7FB06 +:200343000000000090E6D07428F00000000090E6D1E4F0E50870057E06FF80047E047F0043 +:200363008EBB22AF8290E680E030E7067D1C7E3D80047DA67E3DEF75F010A42416FBE435DF +:20038300F0FCEB2DFDEC3EFC7E80EFF508703590E612E4F090E61474E0F090E6E27401F0BD +:2003A3008D828C838EF01213D890E625F074012DFAE43CFB8E078A828B838FF01213D890A4 +:2003C300E624F02290E61274D8F090E614E4F090E6D204F08D828C838EF01213D890E62170 +:2003E300F00DBD00010C8D828C838EF01213D8FF90E62074075FF08D828C838EF01213D8A2 +:20040300C423541F90E64004F002024BAF827E00EE75F005A4FCADF024FDF582ED3416F57E +:2004230083E493FBB5070280080EBE16E375820022EC24FDFCED3416FD8C828D83A3A3A35C +:20044300A3E49390E601F08C828D83A3E493FDA3E493FEA3E493FF75AF07759DE4759E007E +:200463007C208C031CEB60168D828E838FF01213D8FBA3AD82AE8390E67CEBF080E47F6027 +:200483008F061FEE600790E67CE4F080F375820122AF82BF00028064BF010302052BBF0271 +:2004A30003020539BF0503020547BF0A03020555BF1403020563BF3203020571BF640302E5 +:2004C300057FBF69028017BF6A028021BF6E02802BBF78028035BF9602803F02058D750C15 +:2004E30019750D0075CAC075CB63020591750C1E750D0075CACB75CB7D020591750C32757C +:200503000D0075CAE075CBB1020591750C64750D0075CAF075CBD8020591750CFA750D00E0 +:2005230075CA6075CBF08066750CF4750D0175CA3075CBF88058750CE8750D0375CA1875D2 +:20054300CBFC804A750CC4750D0975CA7075CBFE803C750C88750D1375CA3875CBFF802E8C +:20056300750C10750D2775CA9C75CBFF8020750CA8750D6175CAD875CBFF8012750C50754A +:200583000DC375CAEC75CBFF80047582002275820122AD4CAE4DAF4E8D828E838FF0E508EA +:2005A30012132675820122E582700685108212036675820122758200227582012290E6BBE6 +:2005C300E0FE7F0090E6BAE07C004207EC420690E6BFE0FC7D0090E6BEE07A004205EA4223 +:2005E3000490E6A0E020E1F990E6B8E0FBBB40028073BBC00280030206C1ED4C700302068E +:20060300C5C374409DE49C50067A407B0080048D028C0390E6A0E020E1F98A457546007562 +:2006230047407548E77549008F438E44758251C007C006C005C004C002120E5DD002D004E7 +:20064300D005D006D00790E68AE4F00000000090E68BEAF07B00EDC39AFDEC9BFCEA2FFF09 +:20066300EB3EFE8095ED4C605990E68BE4F090E6A0E020E1F990E68BE0FB8B3875390075C2 +:200683003A40753BE7753C008F368E37758251C007C006C005C004C003120DC8E582D003C9 +:2006A300D004D005D006D0077003F582227A00EB2FFFEA3EFEEDC39BFDEC9AFC80A7758234 +:2006C30000227582012275820022AF82C007120308D007C2A0D2A1850C0E850D0FBFE00022 +:2006E3004015EF2419401090E68AE4F090E68BF090E6A0E020E1F9BFA2028023BFE002804A +:2007030021BFE1028028BFE202802FBFE3028033BFE4028046BFE502804ABFE65D80520211 +:2007230005C090E740E0F54C7582000200E390E740E0F54C7582010200E390E740E0F5827A +:2007430002040F90E740E0FFBF010CD2A0C2A1E4F50EF50F1203257582012290E740E0F57F +:200763008202028590E740E0F5821202277582012290E740E0F5820204947582002290E6D1 +:2007830013E4F090E615F0D28790E6C2E4F090E6C37480F090E6C0E4F090E6F4F0120308F1 +:2007A300754C017582001200E3754C017582011200E375820112040F758202120285758233 +:2007C3000002036690E600E054E74410F01208E3120781D2E890E668E0FF43070890E66898 +:2007E300EFF090E65CE0FF43070190E65CEFF0E0FF43071090E65CEFF0E0FF43072090E6C1 +:200803005CEFF0E0FF43070890E65CEFF0D2DDD2AF75CA3075CBF875C800D2ADD2CA90E613 +:2008230080E043E00AF09005DC12138190E680E053E0F7F090E672E4F090E671F090E670B8 +:20084300F075B109F5A0F58075B6FF75B4FF75B2FFC2A0D2A11000028003120EC110010296 +:2008630080F390E682E043E0C0F090E6817401F0AE877F004306018E8700000000000000E8 +:20088300E513701C90E682E030E60790E682E020E0D090E682E030E70790E682E020E1C2A3 +:2008A30090E682E030E60790E682E020E00E90E682E030E7A090E682E030E19990000512A2 +:2008C300138190E680E0FF43070190E680EFF090000F12138190E680E053E0FEF0020858EE +:2008E3007E107F3E754C30754D31754E32754F337550347551357552367553377554387574 +:200903005539755641755742755843755944755A45755B4674182EFCE43FFD90E507E0FBB3 +:200923005303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C828D83EB0A +:20094300F074162EFCE43FFD90E507E0FB53030FEB244CF987038C828D83EBF074142EFC8B +:20096300E43FFD90E508E0FB5303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CDE +:20098300F987038C828D83EBF074122EFCE43FFD90E508E0FB53030FEB244CF987038C825F +:2009A3008D83EBF074102EFCE43FFD90E509E0FB5303F0E4C4CBC4540F6BCB540FCB6BCBA8 +:2009C30030E30244F0EB244CF987038C828D83EBF0740E2EFCE43FFD90E509E0FB53030F6A +:2009E300EB244CF987038C828D83EBF0740C2EFCE43FFD90E50AE0FB5303F0E4C4CBC45428 +:200A03000F6BCB540FCB6BCB30E30244F0EB244CF987038C828D83EBF0740A2EFCE43FFD42 +:200A230090E50AE0FB53030FEB244CF987038C828D83EBF074082EFCE43FFD90E50BE0FBFC +:200A43005303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CF987038C828D83EBE9 +:200A6300F074062EFCE43FFD90E50BE0FB53030FEB244CF987038C828D83EBF074042EFC86 +:200A8300E43FFD90E50CE0FB5303F0E4C4CBC4540F6BCB540FCB6BCB30E30244F0EB244CB9 +:200AA300F987038C828D83EBF074022EFEE43FFF90E50CE0FD53050FED244CF987058E823C +:050AC3008F83EDF0221D +:20143D000F0E00000000000002000100000000005055550000000000000000000000000075 +:20145D00010100000000000002000100000000005055550000000000000000000000000070 +:20147D00FAFAFAFAFAF9000002000000000001005055555555555500000000000000000023 +:20149D00FAFAF90000000000020000010000000050555555000000000000000000000000F0 +:2014BD00C8C8C7000000000002000001000000005055555500000000000000000000000066 +:2014DD00FAFAF90000000000020000010000000050555555000000000000000000000000B0 +:2014FD009695000000000000020001000000000050555500000000000000000000000000A7 +:20151D000201000000000000020001000000000050555500000000000000000000000000AE +:20153D004B4A000000000000020001000000000050555500000000000000000000000000FC +:20155D00252500000000000002000100000000005055550000000000000000000000000027 +:20157D000100000000000000020100000000000050550000000000000000000000000000A5 +:20159D001E1D000000000000020001000000000050555500000000000000000000000000F6 +:2015BD0001010000000000000200010000000000505555000000000000000000000000000F +:2015DD000707000000000000020001000000000050555500000000000000000000000000E3 +:2015FD00010000000000000002010000000000005055000000000000000000000000000025 +:20161D000504000000000000020001000000000050555500000000000000000000000000A7 +:20163D0080000000000000000300000000000000000000000000000000000000000000000A +:20165D00060500000000000002000100000000005055550000000000000000000000000065 +:20167D008000000000000000030000000000000000000000000000000000000000000000CA +:20169D0003020000000000000200010000000000505555000000000000000000000000002B +:2016BD0002020000000000000200010000000000505555000000000000000000000000000C +:2016DD000302000000000000020001000000000050555500000000000000000000000000EB +:2016FD00307D1680EA1E3D1680AA18FD1580CA10BD1580CA0F7D15808A0C1D1580CA0A5DCB +:20171D0014808A08DD1680CA06BD16808A059D16808A045D1680CA031D16808A02DD15802F +:20173D008A013D14808A969D15808A8C5D15808A783D15808A6EFD14808A6ADD1480CA69E1 +:0E175D00BD14808A689D14808A667D14808A7F +:203D000012010002FFFFFF40B5042A600702010203010A0600020000004001000902890017 +:203D200001010080FA0904000001FF000000070586020002000904000101FF000100070549 +:203D400082010014010904000201FF00010007058201000C010904000301FF000100070502 +:203D600082010004010904000401FF000100070582010004020904000501FF0001000705F5 +:203D800082010004030904000601FF000100070582010004040904000701FF0001000705CD +:053DA000820100020495 +:203DA6000902390002010080FA0904000001FF000000070586024000000904000101FF004D +:193DC600010007058201FF03010904000201FF00010007058201000201AF +:203DE0000403090416034F00700065006E00480061006E00740065006B00160344005300F9 +:203E00004F002D00360030003200320042004C001A03300030003000300030003000300061 +:0C3E2000300030003000300030000000A6 +:200AC80085822CE5272522FDE5283523FE752D02903C00E4F0903C00E0600475820022902B +:200AE800E678E0FB7A0043038090E678EBF0E030E21090000AC006C005121381D005D00634 +:200B080080D3E52C25E0FB90E679F090E678E020E006903C00E060F3903C00E060047582B0 +:200B2800002290E678E020E2AC90E678E020E14090E678E0FB43034090E678EBF090E678FA +:200B4800E030E606903C00E060F3903C00E0600475820022E52D14FB8B2D7003F5822290F4 +:200B6800000AC006C005121381D005D006020ADD7A007B00C3EA9DEB9E4003020C12C3EAC6 +:200B88009522EB95235016EA2524F8EB3525F9AC26888289838CF01213D8FC801DEAC395E8 +:200BA80022F8EB9523F9E82529F8E9352AF9AF2B888289838FF01213D8FC90E679ECF00ACB +:200BC800BA00010B90E678E020E006903C00E060F3903C00E060047582002290E678E0304D +:200BE800E203020ADD90E678E020E18890E678E0FF43074090E678EFF090E678E030E606BA +:200C0800903C00E060F37582002290E678E0FF43074090E678EFF090E678E030E606903CDA +:200C280000E060F3903C00E060047582002275820122858233903C00E4F004B52E0814B5A4 +:200C48002F0474018001E4FE903C00E06004758200227C007D0090E678E0FB43038090E65A +:200C680078EBF0E030E20C90000AC006121381D00680D5E53325E0FB3395E0FA4303019059 +:200C8800E679EBF090E678E020E006903C00E060F3903C00E060047582002290E678E02028 +:200CA800E2A690E678E020E11E90E678E0FB43034090E678EBF090E678E030E606903C00E9 +:200CC800E060F375820022EE600F90E678E0FB7A0043032090E678EBF090E679E0E4F53415 +:200CE800F53574012534F8E43535F9C3E8952EE9952F506C90E678E020E006903C00E060FE +:200D0800F3903C00E060047582002290E678E030E203020C5074022534F8E43535F9E8B5C3 +:200D28002E13E9B52F0F90E678E0F8790043002090E678E8F0A834A9350534E4B53402055F +:200D480035AC34AD35E82530F8E93531F9AF3290E679E0FB888289838FF0121326020CEA94 +:200D680090E678E020E006903C00E060F3903C00E060047582002290E678E030E203020C7E +:200D88005090E678E0FF43074090E678EFF0EC2530FCED3531FDAF3290E679E08C828D837C +:200DA8008FF012132690E678E030E606903C00E060F3903C00E06004758200227582012235 +:200DC80085823DE4F541F542AB36AC37C3E5419538E54295394003020E597A0090E678E013 +:200DE80030E4067A018C078F3EEA04FFEA243EF98B02A702EF04FAEF243EF9AE41AF420572 +:200E080041E4B541020542EE253AFEEF353BFFAD3C8E828F838DF01213D8F78A22752300FD +:200E280075243E752500752640E4F527F528F529F52AF52B853D82C004C003120AC8E582CE +:200E4800D003D0047003F582220BBB00010C020DD475820122AF827E0090E678E030E40670 +:200E68007E01AD448D4AEE04FDEE244AF9AE43A7068D2275230075244A752500752640E4BE +:200E8800F527F528F529F52AF52B8F82C007120AC8E582D0077003F5822285452E85462FBC +:190EA8008547308548318549328F82120C3AE5827003F582227582012241 +:06008800E478FFF6D8FD4C +:1200CE007512007513007514A675153D75161C75173DAB +:200EC10090E6B9E0FF24F45003020FEFEF240A83F582EF241083F583E473F30FEF2BEFEF12 +:200EE10047EF576E90CA0E0F0F0F0F0F0F0F0F0F0F0F121071E582600302100B90E6A0E079 +:200F0100FF7E0043070190E6A0EFF002100B121105E582600302100B90E6A0E0FF7E004331 +:200F2100070190E6A0EFF002100B121180E582600302100B90E6A0E0FF7E0043070190E6D8 +:200F4100A0EFF002100B1206C9E582600302100B12124102100B1205B8AF8290E740EFF014 +:200F610090E68AE4F090E68B04F002100B90E6BAE0F5821205BCE582600302100B90E6A033 +:200F8100E0FF7E0043070190E6A0EFF002100B90E6BCE0FF754C11754D00754E408F8212CB +:200FA1000595E582701190E6A0E0FF7E0043070190E6A0EFF0805390E740E511F090E68A8B +:200FC100E4F090E68B04F0804190E6BCE0FF90E6BAE0F5108F821205AAE582702D90E6A074 +:200FE100E0FF7E0043070190E6A0EFF0801C90E6B9E0F5821206CDE582700F90E6A0E0FF71 +:201001007E0043070190E6A0EFF090E6A0E0FF43078090E6A0EFF022AF82747F5FFE24F79F +:20102100500302106DEE240A83F582EE240D83F583E473464A5D6D616D656D6910101010B3 +:20104100101010101090E6A022EF30E7067EA27FE680047EA17FE68E828F832290E6A322EF +:2010610090E6A42290E6A52290E6A6229000002290E6B8E0FFBF80028022BF81028008BF8D +:20108100820280370210FD90E740E4F090E741F090E68AF090E68B7402F08064E51325E09A +:2010A100FFE512420790E740EFF090E741E4F090E68AF090E68B7402F0804590E6BCE0F5AB +:2010C10082121019AE82AF838E048F05EE4F7003F582228C828D83E0FC30E0067E017F0073 +:2010E10080047E007F0090E740EEF090E741E4F090E68AF090E68B7402F080047582002259 +:201101007582012290E6B8E0FF6005BF0264801B90E6BAE0FFBF0105751300805E90E6BA18 +:20112100E0FFBF060280547582002290E6BAE0703D90E6BCE0F582121019AE82AF83E0FD5B +:201141005305FE8E828F83EDF090E6BCE0FF30E7098F057E004305108D0753071F90E68398 +:20116100EFF07E0043072090E683EFF0800D7582002290E6B9E0F5820206CD758201229024 +:20118100E6B8E0FF6008BF0202802902120990E6BAE0FFBF020302121290E6BAE0FFBF0118 +:2011A10005751301806B90E6BAE0FFBF060280617582002290E6BAE0704A90E6BCE0F58292 +:2011C100121019AE82AF83EE4F7003F582228E828F83E0FD7C004305018E828F83EDF090D5 +:2011E100E6BCE0FF30E7098F057E004305108D0753071F90E683EFF07E0043072090E6831D +:20120100EFF0800D7582002290E6B9E0F5820206CD75820122AF827E0110AF027E00EF6095 +:201221000E75141C75153D7516A675173D800C7514A675153D75161C75173DEE1392AF22E3 +:2012410090E6BBE0FFBF01028019BF02028024BF0302802CBF06030212FBBF070302130B8B +:201261000213187E007F3D90E6B3EFF090E6B47400F02290E6B3E515F090E6B4E514F02206 +:201281007DE07E3D7F8090E6BAE0F54C7B008B020BEAB54C0280428D008E018F028D828ED9 +:2012A100838FF01213D828F8E439F9880589068A0774012DF9E43EFA8F0489828A838CF06D +:2012C1001213D8F9B9030280067D007E007F00ED4E6006C3E54C9B50B5ED4E600F8D038E5C +:2012E1000490E6B3ECF090E6B4EDF02290E6A0E0FF43070190E6A0EFF0227E127F3D90E6A2 +:20130100B3EFF090E6B47412F02290E6B3E517F090E6B4E516F02290E6A0E0FF430701905C +:05132100E6A0EFF02240 +:200066007900E94400601B7A0090176B780175923CE493F2A308B800020592D9F4DAF2759E +:0200860092FFE7 +:1B13260020F71130F6138883A88220F509F6A8837583002280FEF280F5F022C6 +:203F00000201350002143B00021430000201E30002014D000201980002142D000213750034 +:203F200002137700021378000213790002137C0002143A0002143C000213410002134300F9 +:203F400002143200021375000213F40002142E0002142F0002143100021433000214340023 +:203F60000213D7000213750002137500021375000213760002137A0002137D0002137F0077 +:203F800002137B0002137E00021380000213D6000213440002136F00021371000213730093 +:183FA00002134500021370000213720002137400021342000214390074 +:011341003279 +:011342003278 +:011343003277 +:011344003276 +:011345003275 +:20008E007800E84400600A790075923CE4F309D8FC7801E84400600C7901903C00E4F0A306 +:0400AE00D8FCD9FAA7 +:201346007A10E4FBFCE58225E0F582E58333F583EB33FBEC33FCEB954CF5F0EC954D400633 +:09136600FCABF0438201DADD2248 +:01136F00324B +:01137000324A +:011371003249 +:011372003248 +:011373003247 +:011374003246 +:011375003245 +:011376003244 +:011377003243 +:011378003242 +:011379003241 +:01137A003240 +:01137B00323F +:01137C00323E +:01137D00323D +:01137E00323C +:01137F00323B +:01138000323A +:20138100AE82AF8390E600E05418C423541F70057CB1FD801C90E600E05418C423541FFB7C +:2013A100BB01067A617B0180047AC27B028A048B058C4C8D4D154C74FFB54C02154DE54C9C +:1513C100454D70F1EE24FFFAEF34FFFB8A068B07EA4B70DD2236 +:0113D60032E4 +:0113D70032E3 +:0D00590075815B121435E5826003020056CC +:1C13D80020F71430F6148883A88220F507E6A88375830022E280F7E49322E02224 +:0113F40032C6 +:2013F500C2D5E58330E70DD2D5E4C39582F582E49583F583E54D30E70DB2D5E4C3954CF50B +:181415004CE4954DF54D12134630D50BE4C39582F582E49583F5832225 +:01142D00328C +:01142E00328B +:01142F00328A +:011430003289 +:011431003288 +:011432003287 +:011433003286 +:011434003285 +:04143500758200229A +:011439003280 +:01143A00327F +:01143B00327E +:01143C00327D +:00000001FF diff --git a/openhantek/res/firmware/dso6022bl-loader.hex b/openhantek/res/firmware/dso6022bl-loader.hex deleted file mode 100644 index d7758b6f..00000000 --- a/openhantek/res/firmware/dso6022bl-loader.hex +++ /dev/null @@ -1,60 +0,0 @@ -:1003680090E668E0FF74FFF0E0B40B04EFF0D322EE -:0603780090E668EFF0C3FF -:01037E00225C -:1001B500907FE9E064A360030202C5A3E07508002F -:1001C500F509A3E0FEE4EE4208907FEEE0750A0033 -:1001D500F50BA3E0FEE4EE420A907FE8E064407090 -:1001E50064E50B450A70030202D6E4907FC5F090E2 -:1001F5007FB4E020E3F9907FC5E0750C00F50DE4D0 -:10020500FCFDC3ED950DEC950C501F74C02DF582CA -:10021500E4347EF583E0FFE5092DF582E5083CF53C -:1002250083EFF00DBD00010C80D8E50D2509F5091A -:10023500E50C3508F508C3E50B950DF50BE50A95B5 -:100245000CF50A809C907FE8E064C060030202D64A -:10025500E50B450A607BC3E50B9440E50A94005025 -:1002650008850A0C850B0D8006750C00750D40E49C -:10027500FCFDC3ED950DEC950C501FE5092DF582A0 -:10028500E5083CF583E0FF74002DF582E4347FF545 -:1002950083EFF00DBD00010C80D8907FB5E50DF022 -:1002A5002509F509E50C3508F508C3E50B950DF5A8 -:1002B5000BE50A950CF50A907FB4E030E29280F7E1 -:1002C500907FE9E0B4AC0AE4907F00F0907FB5043C -:0802D500F0907FB4E04402F058 -:0102DD0022FE -:1000800090E6B9E064A36003020198A3E07508005C -:10009000F509A3E0FEE4EE420890E6BEE0750A0032 -:1000A000F50BA3E0FEE4EE420A90E6B8E06440708F -:1000B00066E50B450A70030201ADE490E68AF0A301 -:1000C000F090E6A0E020E1F990E68BE0750C00F5F9 -:1000D0000DE4FCFDC3ED950DEC950C501F74402D07 -:1000E000F582E434E7F583E0FFE5092DF582E508C4 -:1000F0003CF583EFF00DBD00010C80D8E50D25091E -:10010000F509E50C3508F508C3E50B950DF50BE58C -:100110000A950CF50A809A90E6B8E064C060030284 -:1001200001ADE50B450A70030201ADC3E50B944038 -:10013000E50A94005008850A0C850B0D8006750CA5 -:1001400000750D40E4FCFDC3ED950DEC950C501FC2 -:10015000E5092DF582E5083CF583E0FF74402DF5B7 -:1001600082E434E7F583EFF00DBD00010C80D8E4A4 -:1001700090E68AF0A3E50DF02509F509E50C3508B0 -:10018000F508C3E50B950DF50BE50A950CF50A90FE -:10019000E6A0E030E18C80F790E6B9E0B4AC0E90D8 -:1001A000E7407401F0E490E68AF0A304F090E6A042 -:0401B000E04480F0B7 -:0101B4002228 -:1002DE00C2011203689200907F95E044C0F0D2E80C -:1002EE0030000890E65D74FFF08006907FAB74FFDF -:1002FE00F030000890E6687408F08007907FAFE059 -:10030E004401F030000890E65C7401F08006907FA6 -:10031E00AE7401F0D2AF3001FD30000512008080C6 -:08032E00031201B5C20180EECB -:03000300020336BF -:10033600C0E0C083C082C085C084C086758600D2F6 -:10034600015391EF30000890E65D7401F08006904D -:100356007FAB7401F0D086D084D085D082D083D094 -:02036600E03283 -:03004300020400B4 -:0404000002033600BD -:0300000002037F79 -:0C037F00787FE4F6D8FD7581200202DED4 -:00000001FF diff --git a/openhantek/res/firmwares.qrc b/openhantek/res/firmwares.qrc index 3bf4b4ce..6f9e3254 100644 --- a/openhantek/res/firmwares.qrc +++ b/openhantek/res/firmwares.qrc @@ -1,18 +1,7 @@ - firmware/dso2090x86-firmware.hex - firmware/dso2090x86-loader.hex - firmware/dso2150x86-firmware.hex - firmware/dso2150x86-loader.hex - firmware/dso2250x86-firmware.hex - firmware/dso2250x86-loader.hex - firmware/dso5200ax86-firmware.hex - firmware/dso5200ax86-loader.hex - firmware/dso5200x86-firmware.hex - firmware/dso5200x86-loader.hex firmware/dso6022be-firmware.hex - firmware/dso6022be-loader.hex firmware/dso6022bl-firmware.hex - firmware/dso6022bl-loader.hex + firmware/dds120-firmware.hex diff --git a/openhantek/res/images/OpenHantek.ico b/openhantek/res/images/OpenHantek.ico new file mode 100644 index 00000000..8baebe72 Binary files /dev/null and b/openhantek/res/images/OpenHantek.ico differ diff --git a/openhantek/res/images/OpenHantek.png b/openhantek/res/images/OpenHantek.png new file mode 100644 index 00000000..d96497e1 Binary files /dev/null and b/openhantek/res/images/OpenHantek.png differ diff --git a/openhantek/res/images/openhantek.svg b/openhantek/res/images/OpenHantek.svg similarity index 53% rename from openhantek/res/images/openhantek.svg rename to openhantek/res/images/OpenHantek.svg index 5fbd1d0d..834b1920 100644 --- a/openhantek/res/images/openhantek.svg +++ b/openhantek/res/images/OpenHantek.svg @@ -13,11 +13,11 @@ height="48" id="svg2" version="1.1" - inkscape:version="0.47pre4 r22446" - sodipodi:docname="hantekdso.svg" - inkscape:export-filename="/home/oliver/Entwicklung/Qt/HantekDSO/res/images/hantekdso.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" + sodipodi:docname="OpenHantek.svg" + inkscape:export-filename="/home/horo/projects/Measure/OpenHantek/OpenHantek6022/openhantek/res/images/OpenHantek.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96"> - - - + inkscape:window-x="265" + inkscape:window-y="19" + inkscape:window-maximized="0" + inkscape:showpageshadow="false" + showborder="true" + borderlayer="true"> image/svg+xml - + @@ -94,45 +89,42 @@ id="layer1" transform="translate(0,-1004.3622)"> - + sodipodi:nodetypes="cccccccccc" + inkscape:connector-curvature="0" /> + sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc" + inkscape:connector-curvature="0" /> + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" /> + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" /> + width="46" + height="46.000076" + x="1" + y="1005.3622" /> diff --git a/openhantek/res/images/config/analysis.png b/openhantek/res/images/config/analysis.png deleted file mode 100644 index 4c2a1f03..00000000 Binary files a/openhantek/res/images/config/analysis.png and /dev/null differ diff --git a/openhantek/res/images/config/analysis.svg b/openhantek/res/images/config/analysis.svg deleted file mode 100644 index bc218f04..00000000 --- a/openhantek/res/images/config/analysis.svg +++ /dev/null @@ -1,5877 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/openhantek/res/images/config/colors.png b/openhantek/res/images/config/colors.png index b3c35485..890c4bee 100644 Binary files a/openhantek/res/images/config/colors.png and b/openhantek/res/images/config/colors.png differ diff --git a/openhantek/res/images/config/files.png b/openhantek/res/images/config/files.png deleted file mode 100644 index 3f55aa61..00000000 Binary files a/openhantek/res/images/config/files.png and /dev/null differ diff --git a/openhantek/res/images/config/options.png b/openhantek/res/images/config/options.png index 11a0c22e..3c66b38e 100644 Binary files a/openhantek/res/images/config/options.png and b/openhantek/res/images/config/options.png differ diff --git a/openhantek/res/images/config/options.svg b/openhantek/res/images/config/options.svg deleted file mode 100644 index 20c28a59..00000000 --- a/openhantek/res/images/config/options.svg +++ /dev/null @@ -1,580 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/openhantek/res/images/config/scope.png b/openhantek/res/images/config/scope.png index 09da8624..a16053f5 100644 Binary files a/openhantek/res/images/config/scope.png and b/openhantek/res/images/config/scope.png differ diff --git a/openhantek/res/images/config/scope.svg b/openhantek/res/images/config/scope.svg new file mode 100644 index 00000000..d722ccdf --- /dev/null +++ b/openhantek/res/images/config/scope.svg @@ -0,0 +1,97 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/openhantek/res/images/config/spectrum.png b/openhantek/res/images/config/spectrum.png new file mode 100644 index 00000000..b6369c78 Binary files /dev/null and b/openhantek/res/images/config/spectrum.png differ diff --git a/openhantek/res/images/darktheme/histogram.svg b/openhantek/res/images/darktheme/histogram.svg new file mode 100644 index 00000000..f2bd5893 --- /dev/null +++ b/openhantek/res/images/darktheme/histogram.svg @@ -0,0 +1,92 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/openhantek/res/images/darktheme/measure.svg b/openhantek/res/images/darktheme/measure.svg new file mode 100644 index 00000000..6d8c08ae --- /dev/null +++ b/openhantek/res/images/darktheme/measure.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/openhantek/res/images/darktheme/pause.svg b/openhantek/res/images/darktheme/pause.svg new file mode 100644 index 00000000..fae1825e --- /dev/null +++ b/openhantek/res/images/darktheme/pause.svg @@ -0,0 +1,67 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/openhantek/res/images/darktheme/phosphor.svg b/openhantek/res/images/darktheme/phosphor.svg new file mode 100644 index 00000000..9359f803 --- /dev/null +++ b/openhantek/res/images/darktheme/phosphor.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/openhantek/res/images/darktheme/play.svg b/openhantek/res/images/darktheme/play.svg new file mode 100644 index 00000000..2c49c0d7 --- /dev/null +++ b/openhantek/res/images/darktheme/play.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/openhantek/res/images/darktheme/refresh.svg b/openhantek/res/images/darktheme/refresh.svg new file mode 100644 index 00000000..536c4928 --- /dev/null +++ b/openhantek/res/images/darktheme/refresh.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/openhantek/res/images/darktheme/zoom.svg b/openhantek/res/images/darktheme/zoom.svg new file mode 100644 index 00000000..9e941335 --- /dev/null +++ b/openhantek/res/images/darktheme/zoom.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/openhantek/res/images/digitalphosphor.png b/openhantek/res/images/digitalphosphor.png deleted file mode 100644 index bf04c5f5..00000000 Binary files a/openhantek/res/images/digitalphosphor.png and /dev/null differ diff --git a/openhantek/res/images/histogram.svg b/openhantek/res/images/histogram.svg new file mode 100644 index 00000000..2c919e56 --- /dev/null +++ b/openhantek/res/images/histogram.svg @@ -0,0 +1,92 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/openhantek/res/images/measure.svg b/openhantek/res/images/measure.svg new file mode 100644 index 00000000..f574511e --- /dev/null +++ b/openhantek/res/images/measure.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/openhantek/res/images/openhantek.png b/openhantek/res/images/openhantek.png deleted file mode 100644 index f492e4d9..00000000 Binary files a/openhantek/res/images/openhantek.png and /dev/null differ diff --git a/openhantek/res/images/pause.svg b/openhantek/res/images/pause.svg new file mode 100644 index 00000000..ce5241e0 --- /dev/null +++ b/openhantek/res/images/pause.svg @@ -0,0 +1,67 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/openhantek/res/images/digitalphosphor.svg b/openhantek/res/images/phosphor.svg similarity index 59% rename from openhantek/res/images/digitalphosphor.svg rename to openhantek/res/images/phosphor.svg index a06679e3..c3fb68fc 100644 --- a/openhantek/res/images/digitalphosphor.svg +++ b/openhantek/res/images/phosphor.svg @@ -9,13 +9,13 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" + width="64" + height="64" id="svg2816" version="1.1" - inkscape:version="0.47pre4 r22446" - sodipodi:docname="digitalphosphor.svg" - inkscape:export-filename="/home/oliver/Entwicklung/Qt/HantekDSO/res/images/actions/digitalphosphor.png" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" + sodipodi:docname="phosphor.svg" + inkscape:export-filename="phosphor.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> + inkscape:window-x="2664" + inkscape:window-y="169" + inkscape:window-maximized="0" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + objecttolerance="10000" + guidetolerance="10000" + gridtolerance="10000" + inkscape:pagecheckerboard="true" + inkscape:showpageshadow="false"> + snapvisiblegridlinesonly="true" + originx="0.2764286" + originy="2.5898968" /> @@ -99,22 +110,24 @@ id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" - transform="translate(0,-32)"> + transform="translate(1.0234618,12.41009)"> + sodipodi:nodetypes="csc" + inkscape:connector-curvature="0" /> + sodipodi:nodetypes="csc" + inkscape:connector-curvature="0" /> + sodipodi:nodetypes="csc" + inkscape:connector-curvature="0" /> diff --git a/openhantek/res/images/play.svg b/openhantek/res/images/play.svg new file mode 100644 index 00000000..eba88f39 --- /dev/null +++ b/openhantek/res/images/play.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/openhantek/res/images/refresh.svg b/openhantek/res/images/refresh.svg new file mode 100644 index 00000000..62ef4179 --- /dev/null +++ b/openhantek/res/images/refresh.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/openhantek/res/images/switch.png b/openhantek/res/images/switch_6022BL.png similarity index 94% rename from openhantek/res/images/switch.png rename to openhantek/res/images/switch_6022BL.png index 11e8a768..61cc8070 100644 Binary files a/openhantek/res/images/switch.png and b/openhantek/res/images/switch_6022BL.png differ diff --git a/openhantek/res/images/zoom.svg b/openhantek/res/images/zoom.svg new file mode 100644 index 00000000..da6756b8 --- /dev/null +++ b/openhantek/res/images/zoom.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/openhantek/src/OH_BUILD.h b/openhantek/src/OH_BUILD.h new file mode 100644 index 00000000..8ef6427d --- /dev/null +++ b/openhantek/src/OH_BUILD.h @@ -0,0 +1,2 @@ +// Do not edit, will be re-created at each commit! +#define OH_BUILD "20201020 build 741" diff --git a/openhantek/src/OH_VERSION.h b/openhantek/src/OH_VERSION.h new file mode 100644 index 00000000..597a9352 --- /dev/null +++ b/openhantek/src/OH_VERSION.h @@ -0,0 +1,15 @@ +// define the version that is shown on top of the program +// if undefined (for development commits) the build will be shown by OpenHantek + +// #define OH_VERSION "3.1.4-rc1" + +#ifdef OH_VERSION +#undef VERSION +#define VERSION OH_VERSION +#else +#include "OH_BUILD.h" +#ifdef OH_BUILD +#undef VERSION +#define VERSION OH_BUILD +#endif +#endif diff --git a/openhantek/src/configdialog/DsoConfigAnalysisPage.cpp b/openhantek/src/configdialog/DsoConfigAnalysisPage.cpp deleted file mode 100644 index 5c02fc23..00000000 --- a/openhantek/src/configdialog/DsoConfigAnalysisPage.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include "DsoConfigAnalysisPage.h" - -DsoConfigAnalysisPage::DsoConfigAnalysisPage(DsoSettings *settings, QWidget *parent) - : QWidget(parent), settings(settings) { - // Initialize lists for comboboxes - QStringList windowFunctionStrings; - windowFunctionStrings << tr("Rectangular") << tr("Hamming") << tr("Hann") << tr("Cosine") << tr("Lanczos") - << tr("Bartlett") << tr("Triangular") << tr("Gauss") << tr("Bartlett-Hann") << tr("Blackman") - //<< tr("Kaiser") - << tr("Nuttall") << tr("Blackman-Harris") << tr("Blackman-Nuttall") << tr("Flat top"); - - // Initialize elements - windowFunctionLabel = new QLabel(tr("Window function")); - windowFunctionComboBox = new QComboBox(); - windowFunctionComboBox->addItems(windowFunctionStrings); - windowFunctionComboBox->setCurrentIndex((int)settings->post.spectrumWindow); - - referenceLevelLabel = new QLabel(tr("Reference level")); - referenceLevelSpinBox = new QDoubleSpinBox(); - referenceLevelSpinBox->setDecimals(1); - referenceLevelSpinBox->setMinimum(-40.0); - referenceLevelSpinBox->setMaximum(100.0); - referenceLevelSpinBox->setValue(settings->post.spectrumReference); - referenceLevelUnitLabel = new QLabel(tr("dBm")); - referenceLevelLayout = new QHBoxLayout(); - referenceLevelLayout->addWidget(referenceLevelSpinBox); - referenceLevelLayout->addWidget(referenceLevelUnitLabel); - - minimumMagnitudeLabel = new QLabel(tr("Minimum magnitude")); - minimumMagnitudeSpinBox = new QDoubleSpinBox(); - minimumMagnitudeSpinBox->setDecimals(1); - minimumMagnitudeSpinBox->setMinimum(-40.0); - minimumMagnitudeSpinBox->setMaximum(100.0); - minimumMagnitudeSpinBox->setValue(settings->post.spectrumLimit); - minimumMagnitudeUnitLabel = new QLabel(tr("dBm")); - minimumMagnitudeLayout = new QHBoxLayout(); - minimumMagnitudeLayout->addWidget(minimumMagnitudeSpinBox); - minimumMagnitudeLayout->addWidget(minimumMagnitudeUnitLabel); - - spectrumLayout = new QGridLayout(); - spectrumLayout->addWidget(windowFunctionLabel, 0, 0); - spectrumLayout->addWidget(windowFunctionComboBox, 0, 1); - spectrumLayout->addWidget(referenceLevelLabel, 1, 0); - spectrumLayout->addLayout(referenceLevelLayout, 1, 1); - spectrumLayout->addWidget(minimumMagnitudeLabel, 2, 0); - spectrumLayout->addLayout(minimumMagnitudeLayout, 2, 1); - - spectrumGroup = new QGroupBox(tr("Spectrum")); - spectrumGroup->setLayout(spectrumLayout); - - mainLayout = new QVBoxLayout(); - mainLayout->addWidget(spectrumGroup); - mainLayout->addStretch(1); - - setLayout(mainLayout); -} - -/// \brief Saves the new settings. -void DsoConfigAnalysisPage::saveSettings() { - settings->post.spectrumWindow = (Dso::WindowFunction)windowFunctionComboBox->currentIndex(); - settings->post.spectrumReference = referenceLevelSpinBox->value(); - settings->post.spectrumLimit = minimumMagnitudeSpinBox->value(); -} diff --git a/openhantek/src/configdialog/DsoConfigColorsPage.cpp b/openhantek/src/configdialog/DsoConfigColorsPage.cpp index 85e96e7c..120f6560 100644 --- a/openhantek/src/configdialog/DsoConfigColorsPage.cpp +++ b/openhantek/src/configdialog/DsoConfigColorsPage.cpp @@ -2,127 +2,137 @@ #include "DsoConfigColorsPage.h" -DsoConfigColorsPage::DsoConfigColorsPage(DsoSettings *settings, QWidget *parent) : QWidget(parent), settings(settings) { +DsoConfigColorsPage::DsoConfigColorsPage( DsoSettings *settings, QWidget *parent ) : QWidget( parent ), settings( settings ) { // Initialize elements DsoSettingsView &colorSettings = settings->view; enum { COL_LABEL = 0, COL_SCR_CHANNEL, COL_SCR_SPECTRUM, COL_PRT_CHANNEL, COL_PRT_SPECTRUM }; // Plot Area - graphLabel = new QLabel(tr("
")); // 4*80 - graphLabel->setAlignment(Qt::AlignRight); - graphLabel->setTextFormat(Qt::RichText); + screenColorsLabel = new QLabel( tr( "Screen" ) ); + screenColorsLabel->setAlignment( Qt::AlignHCenter ); + printColorsLabel = new QLabel( tr( "Print" ) ); + printColorsLabel->setAlignment( Qt::AlignHCenter ); - screenColorsLabel = new QLabel(tr("Screen")); - screenColorsLabel->setAlignment(Qt::AlignHCenter); - printColorsLabel = new QLabel(tr("Print")); - printColorsLabel->setAlignment(Qt::AlignHCenter); + backgroundLabel = new QLabel( tr( "Background" ) ); + backgroundColorBox = new ColorBox( colorSettings.screen.background ); + printBackgroundColorBox = new ColorBox( colorSettings.print.background ); - axesLabel = new QLabel(tr("Axes")); - axesColorBox = new ColorBox(colorSettings.screen.axes); - printAxesColorBox = new ColorBox(colorSettings.print.axes); + gridLabel = new QLabel( tr( "Grid" ) ); + gridColorBox = new ColorBox( colorSettings.screen.grid ); + printGridColorBox = new ColorBox( colorSettings.print.grid ); - backgroundLabel = new QLabel(tr("Background")); - backgroundColorBox = new ColorBox(colorSettings.screen.background); - printBackgroundColorBox = new ColorBox(colorSettings.print.background); + axesLabel = new QLabel( tr( "Axes" ) ); + axesColorBox = new ColorBox( colorSettings.screen.axes ); + printAxesColorBox = new ColorBox( colorSettings.print.axes ); - borderLabel = new QLabel(tr("Border")); - borderColorBox = new ColorBox(colorSettings.screen.border); - printBorderColorBox = new ColorBox(colorSettings.print.border); + borderLabel = new QLabel( tr( "Border" ) ); + borderColorBox = new ColorBox( colorSettings.screen.border ); + printBorderColorBox = new ColorBox( colorSettings.print.border ); - gridLabel = new QLabel(tr("Grid")); - gridColorBox = new ColorBox(colorSettings.screen.grid); - printGridColorBox = new ColorBox(colorSettings.print.grid); + markersLabel = new QLabel( tr( "Markers" ) ); + markersColorBox = new ColorBox( colorSettings.screen.markers ); + printMarkersColorBox = new ColorBox( colorSettings.print.markers ); - markersLabel = new QLabel(tr("Markers")); - markersColorBox = new ColorBox(colorSettings.screen.markers); - printMarkersColorBox = new ColorBox(colorSettings.print.markers); - - textLabel = new QLabel(tr("Text")); - textColorBox = new ColorBox(colorSettings.screen.text); - printTextColorBox = new ColorBox(colorSettings.print.text); + textLabel = new QLabel( tr( "Text" ) ); + textColorBox = new ColorBox( colorSettings.screen.text ); + printTextColorBox = new ColorBox( colorSettings.print.text ); // Graph category - screenChannelLabel = new QLabel(tr("Channel")); - screenChannelLabel->setAlignment(Qt::AlignHCenter); - screenSpectrumLabel = new QLabel(tr("Spectrum")); - screenSpectrumLabel->setAlignment(Qt::AlignHCenter); - printChannelLabel = new QLabel(tr("Channel")); - printChannelLabel->setAlignment(Qt::AlignHCenter); - printSpectrumLabel = new QLabel(tr("Spectrum")); - printSpectrumLabel->setAlignment(Qt::AlignHCenter); - - for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel) { - colorLabel.push_back(new QLabel(settings->scope.voltage[channel].name)); - screenChannelColorBox.push_back(new ColorBox(colorSettings.screen.voltage[channel])); - screenSpectrumColorBox.push_back(new ColorBox(colorSettings.screen.spectrum[channel])); - printChannelColorBox.push_back(new ColorBox(colorSettings.print.voltage[channel])); - printSpectrumColorBox.push_back(new ColorBox(colorSettings.print.spectrum[channel])); + screenChannelLabel = new QLabel( tr( "Channel" ) ); + screenChannelLabel->setAlignment( Qt::AlignHCenter ); + screenSpectrumLabel = new QLabel( tr( "Spectrum" ) ); + screenSpectrumLabel->setAlignment( Qt::AlignHCenter ); + printChannelLabel = new QLabel( tr( "Channel" ) ); + printChannelLabel->setAlignment( Qt::AlignHCenter ); + printSpectrumLabel = new QLabel( tr( "Spectrum" ) ); + printSpectrumLabel->setAlignment( Qt::AlignHCenter ); + + for ( ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel ) { + colorLabel.push_back( new QLabel( settings->scope.voltage[ channel ].name ) ); + screenChannelColorBox.push_back( new ColorBox( colorSettings.screen.voltage[ channel ] ) ); + screenSpectrumColorBox.push_back( new ColorBox( colorSettings.screen.spectrum[ channel ] ) ); + printChannelColorBox.push_back( new ColorBox( colorSettings.print.voltage[ channel ] ) ); + printSpectrumColorBox.push_back( new ColorBox( colorSettings.print.spectrum[ channel ] ) ); } // Plot Area Layout colorsLayout = new QGridLayout(); - colorsLayout->setColumnStretch(COL_LABEL, 1); - colorsLayout->setColumnMinimumWidth(COL_SCR_CHANNEL, 80); - colorsLayout->setColumnMinimumWidth(COL_SCR_SPECTRUM, 80); - colorsLayout->setColumnMinimumWidth(COL_PRT_CHANNEL, 80); - colorsLayout->setColumnMinimumWidth(COL_PRT_SPECTRUM, 80); + colorsLayout->setColumnStretch( COL_LABEL, 1 ); + colorsLayout->setColumnMinimumWidth( COL_SCR_CHANNEL, 80 ); + colorsLayout->setColumnMinimumWidth( COL_SCR_SPECTRUM, 80 ); + colorsLayout->setColumnMinimumWidth( COL_PRT_CHANNEL, 80 ); + colorsLayout->setColumnMinimumWidth( COL_PRT_SPECTRUM, 80 ); int row = 0; - colorsLayout->addWidget(screenColorsLabel, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printColorsLabel, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( screenColorsLabel, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printColorsLabel, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(backgroundLabel, row, COL_LABEL); - colorsLayout->addWidget(backgroundColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printBackgroundColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( backgroundLabel, row, COL_LABEL ); + colorsLayout->addWidget( backgroundColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printBackgroundColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(gridLabel, row, COL_LABEL); - colorsLayout->addWidget(gridColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printGridColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( gridLabel, row, COL_LABEL ); + colorsLayout->addWidget( gridColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printGridColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(axesLabel, row, COL_LABEL); - colorsLayout->addWidget(axesColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printAxesColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( axesLabel, row, COL_LABEL ); + colorsLayout->addWidget( axesColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printAxesColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(borderLabel, row, COL_LABEL); - colorsLayout->addWidget(borderColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printBorderColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( borderLabel, row, COL_LABEL ); + colorsLayout->addWidget( borderColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printBorderColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(markersLabel, row, COL_LABEL); - colorsLayout->addWidget(markersColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printMarkersColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( markersLabel, row, COL_LABEL ); + colorsLayout->addWidget( markersColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printMarkersColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - colorsLayout->addWidget(textLabel, row, COL_LABEL); - colorsLayout->addWidget(textColorBox, row, COL_SCR_CHANNEL, 1, 2); - colorsLayout->addWidget(printTextColorBox, row, COL_PRT_CHANNEL, 1, 2); + colorsLayout->addWidget( textLabel, row, COL_LABEL ); + colorsLayout->addWidget( textColorBox, row, COL_SCR_CHANNEL, 1, 2 ); + colorsLayout->addWidget( printTextColorBox, row, COL_PRT_CHANNEL, 1, 2 ); ++row; - // Graph - colorsLayout->addWidget(graphLabel, row, COL_LABEL, 1, COL_PRT_SPECTRUM - COL_LABEL + 1); + // horizontal line + horizontalLine = new QLabel( "
" ); + horizontalLine->setAlignment( Qt::AlignRight ); + horizontalLine->setTextFormat( Qt::RichText ); + colorsLayout->addWidget( horizontalLine, row, COL_LABEL, 1, COL_PRT_SPECTRUM - COL_LABEL + 1 ); ++row; - colorsLayout->addWidget(screenChannelLabel, row, COL_SCR_CHANNEL); - colorsLayout->addWidget(screenSpectrumLabel, row, COL_SCR_SPECTRUM); - colorsLayout->addWidget(printChannelLabel, row, COL_PRT_CHANNEL); - colorsLayout->addWidget(printSpectrumLabel, row, COL_PRT_SPECTRUM); + colorsLayout->addWidget( screenChannelLabel, row, COL_SCR_CHANNEL ); + colorsLayout->addWidget( screenSpectrumLabel, row, COL_SCR_SPECTRUM ); + colorsLayout->addWidget( printChannelLabel, row, COL_PRT_CHANNEL ); + colorsLayout->addWidget( printSpectrumLabel, row, COL_PRT_SPECTRUM ); ++row; - for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel, ++row) { - colorsLayout->addWidget(colorLabel[channel], row, COL_LABEL); - colorsLayout->addWidget(screenChannelColorBox[channel], row, COL_SCR_CHANNEL); - colorsLayout->addWidget(screenSpectrumColorBox[channel], row, COL_SCR_SPECTRUM); - colorsLayout->addWidget(printChannelColorBox[channel], row, COL_PRT_CHANNEL); - colorsLayout->addWidget(printSpectrumColorBox[channel], row, COL_PRT_SPECTRUM); + for ( ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel, ++row ) { + colorsLayout->addWidget( colorLabel[ channel ], row, COL_LABEL ); + colorsLayout->addWidget( screenChannelColorBox[ channel ], row, COL_SCR_CHANNEL ); + colorsLayout->addWidget( screenSpectrumColorBox[ channel ], row, COL_SCR_SPECTRUM ); + colorsLayout->addWidget( printChannelColorBox[ channel ], row, COL_PRT_CHANNEL ); + colorsLayout->addWidget( printSpectrumColorBox[ channel ], row, COL_PRT_SPECTRUM ); } - colorsGroup = new QGroupBox(tr("Screen and Print Colors")); - colorsGroup->setLayout(colorsLayout); + // horizontal line + horizontalLine = new QLabel( "
" ); + horizontalLine->setAlignment( Qt::AlignRight ); + horizontalLine->setTextFormat( Qt::RichText ); + + colorsLayout->addWidget( horizontalLine, ++row, COL_LABEL, 1, COL_PRT_SPECTRUM - COL_LABEL + 1 ); + + screenColorCheckBox = new QCheckBox( tr( "Export hardcopy images or pdf files with printer colors" ) ); + screenColorCheckBox->setChecked( settings->view.printerColorImages ); + colorsLayout->addWidget( screenColorCheckBox, ++row, COL_LABEL ); + + colorsGroup = new QGroupBox( tr( "Screen and Print Colors" ) ); + colorsGroup->setLayout( colorsLayout ); // Main layout mainLayout = new QVBoxLayout(); - mainLayout->addWidget(colorsGroup); - mainLayout->addStretch(1); + mainLayout->addWidget( colorsGroup ); + mainLayout->addStretch( 1 ); - setLayout(mainLayout); + setLayout( mainLayout ); } /// \brief Saves the new settings. @@ -146,10 +156,11 @@ void DsoConfigColorsPage::saveSettings() { colorSettings.print.text = printTextColorBox->getColor(); // Graph category - for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel) { - colorSettings.screen.voltage[channel] = screenChannelColorBox[channel]->getColor(); - colorSettings.screen.spectrum[channel] = screenSpectrumColorBox[channel]->getColor(); - colorSettings.print.voltage[channel] = printChannelColorBox[channel]->getColor(); - colorSettings.print.spectrum[channel] = printSpectrumColorBox[channel]->getColor(); + for ( ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel ) { + colorSettings.screen.voltage[ channel ] = screenChannelColorBox[ channel ]->getColor(); + colorSettings.screen.spectrum[ channel ] = screenSpectrumColorBox[ channel ]->getColor(); + colorSettings.print.voltage[ channel ] = printChannelColorBox[ channel ]->getColor(); + colorSettings.print.spectrum[ channel ] = printSpectrumColorBox[ channel ]->getColor(); } + settings->view.printerColorImages = screenColorCheckBox->isChecked(); } diff --git a/openhantek/src/configdialog/DsoConfigColorsPage.h b/openhantek/src/configdialog/DsoConfigColorsPage.h index 00075428..6571c467 100644 --- a/openhantek/src/configdialog/DsoConfigColorsPage.h +++ b/openhantek/src/configdialog/DsoConfigColorsPage.h @@ -2,7 +2,7 @@ #include -#include "settings.h" +#include "dsosettings.h" #include #include @@ -23,7 +23,7 @@ class DsoConfigColorsPage : public QWidget { Q_OBJECT public: - DsoConfigColorsPage(DsoSettings *settings, QWidget *parent = 0); + DsoConfigColorsPage( DsoSettings *settings, QWidget *parent = nullptr ); public slots: void saveSettings(); @@ -40,15 +40,16 @@ class DsoConfigColorsPage : public QWidget { QLabel *axesLabel, *backgroundLabel, *borderLabel, *gridLabel, *markersLabel, *textLabel; ColorBox *axesColorBox, *backgroundColorBox, *borderColorBox, *gridColorBox, *markersColorBox, *textColorBox; - ColorBox *printAxesColorBox, *printBackgroundColorBox, *printBorderColorBox, *printGridColorBox, - *printMarkersColorBox, *printTextColorBox; + ColorBox *printAxesColorBox, *printBackgroundColorBox, *printBorderColorBox, *printGridColorBox, *printMarkersColorBox, + *printTextColorBox; - QLabel *graphLabel; + QLabel *horizontalLine; QLabel *screenChannelLabel, *screenSpectrumLabel, *printChannelLabel, *printSpectrumLabel; - std::vector colorLabel; - std::vector screenChannelColorBox; - std::vector screenSpectrumColorBox; - std::vector printChannelColorBox; - std::vector printSpectrumColorBox; + std::vector< QLabel * > colorLabel; + std::vector< ColorBox * > screenChannelColorBox; + std::vector< ColorBox * > screenSpectrumColorBox; + std::vector< ColorBox * > printChannelColorBox; + std::vector< ColorBox * > printSpectrumColorBox; + QCheckBox *screenColorCheckBox; }; diff --git a/openhantek/src/configdialog/DsoConfigFilesPage.cpp b/openhantek/src/configdialog/DsoConfigFilesPage.cpp deleted file mode 100644 index 64e3b2ab..00000000 --- a/openhantek/src/configdialog/DsoConfigFilesPage.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include "DsoConfigFilesPage.h" - -DsoConfigFilesPage::DsoConfigFilesPage(DsoSettings *settings, QWidget *parent) : QWidget(parent), settings(settings) { - // Export group - screenColorCheckBox = new QCheckBox(tr("Export Images with Screen Colors")); - screenColorCheckBox->setChecked(settings->view.screenColorImages); - - imageWidthLabel = new QLabel(tr("Image width")); - imageWidthSpinBox = new QSpinBox(); - imageWidthSpinBox->setMinimum(100); - imageWidthSpinBox->setMaximum(9999); - imageWidthSpinBox->setValue(settings->exporting.imageSize.width()); - imageHeightLabel = new QLabel(tr("Image height")); - imageHeightSpinBox = new QSpinBox(); - imageHeightSpinBox->setMinimum(100); - imageHeightSpinBox->setMaximum(9999); - imageHeightSpinBox->setValue(settings->exporting.imageSize.height()); - - exportLayout = new QGridLayout(); - exportLayout->addWidget(screenColorCheckBox, 0, 0, 1, 2); - exportLayout->addWidget(imageWidthLabel, 1, 0); - exportLayout->addWidget(imageWidthSpinBox, 1, 1); - exportLayout->addWidget(imageHeightLabel, 2, 0); - exportLayout->addWidget(imageHeightSpinBox, 2, 1); - - exportGroup = new QGroupBox(tr("Export")); - exportGroup->setLayout(exportLayout); - - // Configuration group - saveOnExitCheckBox = new QCheckBox(tr("Save default settings on exit")); - saveOnExitCheckBox->setChecked(settings->alwaysSave); - saveNowButton = new QPushButton(tr("Save default settings now")); - - configurationLayout = new QVBoxLayout(); - configurationLayout->addWidget(saveOnExitCheckBox, 0); - configurationLayout->addWidget(saveNowButton, 1); - - configurationGroup = new QGroupBox(tr("Configuration")); - configurationGroup->setLayout(configurationLayout); - - // Main layout - mainLayout = new QVBoxLayout(); - mainLayout->addWidget(exportGroup); - mainLayout->addWidget(configurationGroup); - mainLayout->addStretch(1); - - setLayout(mainLayout); - - connect(saveNowButton, &QAbstractButton::clicked, [settings]() { settings->save(); }); -} - -/// \brief Saves the new settings. -void DsoConfigFilesPage::saveSettings() { - settings->alwaysSave = saveOnExitCheckBox->isChecked(); - settings->view.screenColorImages = screenColorCheckBox->isChecked(); - settings->exporting.imageSize.setWidth(imageWidthSpinBox->value()); - settings->exporting.imageSize.setHeight(imageHeightSpinBox->value()); -} diff --git a/openhantek/src/configdialog/DsoConfigFilesPage.h b/openhantek/src/configdialog/DsoConfigFilesPage.h deleted file mode 100644 index 8bcfd719..00000000 --- a/openhantek/src/configdialog/DsoConfigFilesPage.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include - -#include "settings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////// -/// \class DsoConfigFilesPage configpages.h -/// \brief Config page for file loading/saving. -class DsoConfigFilesPage : public QWidget { - Q_OBJECT - - public: - DsoConfigFilesPage(DsoSettings *settings, QWidget *parent = 0); - - public slots: - void saveSettings(); - - private: - DsoSettings *settings; - - QVBoxLayout *mainLayout; - - QGroupBox *configurationGroup; - QVBoxLayout *configurationLayout; - QCheckBox *saveOnExitCheckBox; - QPushButton *saveNowButton; - - QGroupBox *exportGroup; - QGridLayout *exportLayout; - QCheckBox *screenColorCheckBox; - QLabel *imageWidthLabel; - QSpinBox *imageWidthSpinBox; - QLabel *imageHeightLabel; - QSpinBox *imageHeightSpinBox; -}; diff --git a/openhantek/src/configdialog/DsoConfigScopePage.cpp b/openhantek/src/configdialog/DsoConfigScopePage.cpp index c01a1509..5592854e 100644 --- a/openhantek/src/configdialog/DsoConfigScopePage.cpp +++ b/openhantek/src/configdialog/DsoConfigScopePage.cpp @@ -2,55 +2,123 @@ #include "DsoConfigScopePage.h" -DsoConfigScopePage::DsoConfigScopePage(DsoSettings *settings, QWidget *parent) : QWidget(parent), settings(settings) { +DsoConfigScopePage::DsoConfigScopePage( DsoSettings *settings, QWidget *parent ) : QWidget( parent ), settings( settings ) { // Initialize lists for comboboxes QStringList interpolationStrings; - interpolationStrings << tr("Off") << tr("Linear"); + interpolationStrings << tr( "Off" ) << tr( "Linear" ); + QList< double > timebaseSteps = {1.0, 2.0, 5.0, 10.0}; - // Initialize elements - interpolationLabel = new QLabel(tr("Interpolation")); + maxTimebaseLabel = new QLabel( tr( "Set slowest possible timebase" ) ); + maxTimebaseSiSpinBox = new SiSpinBox(); + maxTimebaseSiSpinBox = new SiSpinBox( UNIT_SECONDS ); + maxTimebaseSiSpinBox->setSteps( timebaseSteps ); + maxTimebaseSiSpinBox->setMinimum( 0.1 ); // default 1000 ms/div (scopesettings.h) + maxTimebaseSiSpinBox->setMaximum( 10.0 ); // possible steps: 100, 200, 500, 1000, 2000, 5000, 10000 ms + maxTimebaseSiSpinBox->setValue( settings->scope.horizontal.maxTimebase ); + + acquireIntervalLabel = new QLabel( tr( "Minimal time between captured frames
(Longer times reduce the CPU load)" ) ); + acquireIntervalSiSpinBox = new SiSpinBox(); + acquireIntervalSiSpinBox = new SiSpinBox( UNIT_SECONDS ); + acquireIntervalSiSpinBox->setSteps( timebaseSteps ); + acquireIntervalSiSpinBox->setMinimum( 1e-3 ); // minimal 1 ms holdOff + acquireIntervalSiSpinBox->setMaximum( 100e-3 ); // up to 100 ms holdOff + acquireIntervalSiSpinBox->setValue( settings->scope.horizontal.acquireInterval ); + + horizontalLayout = new QGridLayout(); + horizontalLayout->addWidget( maxTimebaseLabel, 0, 0 ); + horizontalLayout->addWidget( maxTimebaseSiSpinBox, 0, 1 ); + horizontalLayout->addWidget( acquireIntervalLabel, 1, 0 ); + horizontalLayout->addWidget( acquireIntervalSiSpinBox, 1, 1 ); + horizontalGroup = new QGroupBox( tr( "Horizontal" ) ); + horizontalGroup->setLayout( horizontalLayout ); + + interpolationLabel = new QLabel( tr( "Interpolation" ) ); interpolationComboBox = new QComboBox(); - interpolationComboBox->addItems(interpolationStrings); - interpolationComboBox->setCurrentIndex(settings->view.interpolation); - digitalPhosphorDepthLabel = new QLabel(tr("Digital phosphor depth")); + interpolationComboBox->addItems( interpolationStrings ); + interpolationComboBox->setCurrentIndex( settings->view.interpolation ); + digitalPhosphorDepthLabel = new QLabel( tr( "Digital phosphor depth" ) ); digitalPhosphorDepthSpinBox = new QSpinBox(); - digitalPhosphorDepthSpinBox->setMinimum(2); - digitalPhosphorDepthSpinBox->setMaximum(99); - digitalPhosphorDepthSpinBox->setValue(settings->view.digitalPhosphorDepth); + digitalPhosphorDepthSpinBox->setMinimum( 2 ); + digitalPhosphorDepthSpinBox->setMaximum( 99 ); + digitalPhosphorDepthSpinBox->setValue( int( settings->view.digitalPhosphorDepth ) ); graphLayout = new QGridLayout(); - graphLayout->addWidget(interpolationLabel, 1, 0); - graphLayout->addWidget(interpolationComboBox, 1, 1); - graphLayout->addWidget(digitalPhosphorDepthLabel, 2, 0); - graphLayout->addWidget(digitalPhosphorDepthSpinBox, 2, 1); + graphLayout->addWidget( interpolationLabel, 1, 0 ); + graphLayout->addWidget( interpolationComboBox, 1, 1 ); + graphLayout->addWidget( digitalPhosphorDepthLabel, 2, 0 ); + graphLayout->addWidget( digitalPhosphorDepthSpinBox, 2, 1 ); - graphGroup = new QGroupBox(tr("Graph")); - graphGroup->setLayout(graphLayout); + graphGroup = new QGroupBox( tr( "Graph" ) ); + graphGroup->setLayout( graphLayout ); - cursorsLabel = new QLabel(tr("Position")); + cursorsLabel = new QLabel( tr( "Position" ) ); cursorsComboBox = new QComboBox(); - cursorsComboBox->addItem("Left", Qt::LeftToolBarArea); - cursorsComboBox->addItem("Right", Qt::RightToolBarArea); - cursorsComboBox->setCurrentIndex(settings->view.cursorGridPosition == Qt::LeftToolBarArea ? 0 : 1); + cursorsComboBox->addItem( tr( "Left" ), Qt::LeftToolBarArea ); + cursorsComboBox->addItem( tr( "Right" ), Qt::RightToolBarArea ); + cursorsComboBox->setCurrentIndex( settings->view.cursorGridPosition == Qt::LeftToolBarArea ? 0 : 1 ); cursorsLayout = new QGridLayout(); - cursorsLayout->addWidget(cursorsLabel, 0, 0); - cursorsLayout->addWidget(cursorsComboBox, 0, 1); + cursorsLayout->addWidget( cursorsLabel, 0, 0 ); + cursorsLayout->addWidget( cursorsComboBox, 0, 1 ); + + cursorsGroup = new QGroupBox( tr( "Cursors" ) ); + cursorsGroup->setLayout( cursorsLayout ); + + // Export group + zoomImageCheckBox = new QCheckBox( tr( "Export zoomed screen in double height" ) ); + zoomImageCheckBox->setChecked( settings->view.zoomImage ); + exportLayout = new QGridLayout(); + exportLayout->addWidget( zoomImageCheckBox, 2, 0, 1, 2 ); + + exportGroup = new QGroupBox( tr( "Export" ) ); + exportGroup->setLayout( exportLayout ); + + // Configuration group + saveOnExitCheckBox = new QCheckBox( tr( "Save settings on exit" ) ); + saveOnExitCheckBox->setChecked( settings->alwaysSave ); + defaultSettingsCheckBox = new QCheckBox( tr( "Apply default settings after next restart" ) ); + defaultSettingsCheckBox->setChecked( 0 == settings->configVersion ); + saveNowButton = new QPushButton( tr( "Save settings now" ) ); + hasACmodificationCheckBox = + new QCheckBox( tr( "Scope has hardware modification for AC coupling (restart needed to apply the change)" ) ); + hasACmodificationCheckBox->setChecked( settings->scope.hasACmodification ); + + configurationLayout = new QGridLayout(); + configurationLayout->addWidget( saveOnExitCheckBox, 0, 0 ); + configurationLayout->addWidget( saveNowButton, 0, 1 ); + configurationLayout->addWidget( defaultSettingsCheckBox, 1, 0, 1, 2 ); + if ( settings->scope.hasACcoupling ) { + hasACmodificationCheckBox->setChecked( true ); // check but do not show the box + } else { + configurationLayout->addWidget( hasACmodificationCheckBox, 2, 0, 1, 2 ); // show it + } + + configurationGroup = new QGroupBox( tr( "Configuration" ) ); + configurationGroup->setLayout( configurationLayout ); - cursorsGroup = new QGroupBox(tr("Cursors")); - cursorsGroup->setLayout(cursorsLayout); mainLayout = new QVBoxLayout(); - mainLayout->addWidget(graphGroup); - mainLayout->addWidget(cursorsGroup); - mainLayout->addStretch(1); + mainLayout->addWidget( horizontalGroup ); + mainLayout->addWidget( graphGroup ); + mainLayout->addWidget( exportGroup ); + mainLayout->addWidget( cursorsGroup ); + mainLayout->addWidget( configurationGroup ); + mainLayout->addStretch( 1 ); - setLayout(mainLayout); + setLayout( mainLayout ); + connect( saveNowButton, &QAbstractButton::clicked, [settings]() { settings->save(); } ); } /// \brief Saves the new settings. void DsoConfigScopePage::saveSettings() { - settings->view.interpolation = (Dso::InterpolationMode)interpolationComboBox->currentIndex(); - settings->view.digitalPhosphorDepth = digitalPhosphorDepthSpinBox->value(); - settings->view.cursorGridPosition = (Qt::ToolBarArea)cursorsComboBox->currentData().toUInt(); + settings->scope.hasACmodification = hasACmodificationCheckBox->isChecked(); + settings->scope.horizontal.maxTimebase = maxTimebaseSiSpinBox->value(); + settings->scope.horizontal.acquireInterval = acquireIntervalSiSpinBox->value(); + settings->view.interpolation = Dso::InterpolationMode( interpolationComboBox->currentIndex() ); + settings->view.digitalPhosphorDepth = unsigned( digitalPhosphorDepthSpinBox->value() ); + settings->view.cursorGridPosition = Qt::ToolBarArea( cursorsComboBox->currentData().toUInt() ); + settings->alwaysSave = saveOnExitCheckBox->isChecked(); + if ( defaultSettingsCheckBox->isChecked() ) + settings->configVersion = 0; + settings->view.zoomImage = zoomImageCheckBox->isChecked(); } diff --git a/openhantek/src/configdialog/DsoConfigScopePage.h b/openhantek/src/configdialog/DsoConfigScopePage.h index a185a35a..a890bbe4 100644 --- a/openhantek/src/configdialog/DsoConfigScopePage.h +++ b/openhantek/src/configdialog/DsoConfigScopePage.h @@ -2,8 +2,9 @@ #include -#include "settings.h" +#include "dsosettings.h" +#include "sispinbox.h" #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -21,7 +23,7 @@ class DsoConfigScopePage : public QWidget { Q_OBJECT public: - DsoConfigScopePage(DsoSettings *settings, QWidget *parent = 0); + DsoConfigScopePage( DsoSettings *settings, QWidget *parent = nullptr ); public slots: void saveSettings(); @@ -31,6 +33,13 @@ class DsoConfigScopePage : public QWidget { QVBoxLayout *mainLayout; + QGroupBox *horizontalGroup; + QGridLayout *horizontalLayout; + QLabel *maxTimebaseLabel; + SiSpinBox *maxTimebaseSiSpinBox; + QLabel *acquireIntervalLabel; + SiSpinBox *acquireIntervalSiSpinBox; + QGroupBox *graphGroup; QGridLayout *graphLayout; QLabel *digitalPhosphorDepthLabel; @@ -42,4 +51,15 @@ class DsoConfigScopePage : public QWidget { QGridLayout *cursorsLayout; QLabel *cursorsLabel; QComboBox *cursorsComboBox; + + QGroupBox *configurationGroup; + QGridLayout *configurationLayout; + QCheckBox *hasACmodificationCheckBox; + QCheckBox *saveOnExitCheckBox; + QCheckBox *defaultSettingsCheckBox; + QPushButton *saveNowButton; + + QGroupBox *exportGroup; + QGridLayout *exportLayout; + QCheckBox *zoomImageCheckBox; }; diff --git a/openhantek/src/configdialog/DsoConfigSpectrumPage.cpp b/openhantek/src/configdialog/DsoConfigSpectrumPage.cpp new file mode 100644 index 00000000..87317ed1 --- /dev/null +++ b/openhantek/src/configdialog/DsoConfigSpectrumPage.cpp @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "DsoConfigSpectrumPage.h" + +DsoConfigSpectrumPage::DsoConfigSpectrumPage( DsoSettings *settings, QWidget *parent ) : QWidget( parent ), settings( settings ) { + // Initialize lists for comboboxes + QStringList windowFunctionStrings; + windowFunctionStrings << tr( "Rectangular" ) << tr( "Hamming" ) << tr( "Hann" ) << tr( "Cosine" ) << tr( "Lanczos" ) + << tr( "Bartlett" ) << tr( "Triangular" ) << tr( "Gauss" ) << tr( "Bartlett-Hann" ) + << tr( "Blackman" ) + //<< tr("Kaiser") + << tr( "Nuttall" ) << tr( "Blackman-Harris" ) << tr( "Blackman-Nuttall" ) << tr( "Flat top" ); + + // Initialize elements + windowFunctionLabel = new QLabel( tr( "Window function" ) ); + windowFunctionComboBox = new QComboBox(); + windowFunctionComboBox->addItems( windowFunctionStrings ); + windowFunctionComboBox->setCurrentIndex( int( settings->post.spectrumWindow ) ); + + referenceLevelLabel = new QLabel( + tr( "Reference level
0 dBu = -2.2 dBV
0 dBm (@600 Ω) = -2.2 dBV
0 dBm (@50 Ω) = -13 dBV" ) ); + referenceLevelSpinBox = new QDoubleSpinBox(); + referenceLevelSpinBox->setDecimals( 1 ); + referenceLevelSpinBox->setMinimum( -100.0 ); + referenceLevelSpinBox->setMaximum( 100.0 ); + referenceLevelSpinBox->setValue( settings->post.spectrumReference ); + referenceLevelUnitLabel = new QLabel( tr( "dBV" ) ); + referenceLevelLayout = new QHBoxLayout(); + referenceLevelLayout->addWidget( referenceLevelSpinBox ); + referenceLevelLayout->addWidget( referenceLevelUnitLabel ); + + minimumMagnitudeLabel = new QLabel( tr( "Minimum magnitude" ) ); + minimumMagnitudeSpinBox = new QDoubleSpinBox(); + minimumMagnitudeSpinBox->setDecimals( 1 ); + minimumMagnitudeSpinBox->setMinimum( -100.0 ); + minimumMagnitudeSpinBox->setMaximum( 100.0 ); + minimumMagnitudeSpinBox->setValue( settings->post.spectrumLimit ); + minimumMagnitudeUnitLabel = new QLabel( tr( "dBV" ) ); + minimumMagnitudeLayout = new QHBoxLayout(); + minimumMagnitudeLayout->addWidget( minimumMagnitudeSpinBox ); + minimumMagnitudeLayout->addWidget( minimumMagnitudeUnitLabel ); + + spectrumLayout = new QGridLayout(); + spectrumLayout->addWidget( windowFunctionLabel, 0, 0 ); + spectrumLayout->addWidget( windowFunctionComboBox, 0, 1 ); + spectrumLayout->addWidget( referenceLevelLabel, 1, 0 ); + spectrumLayout->addLayout( referenceLevelLayout, 1, 1 ); + spectrumLayout->addWidget( minimumMagnitudeLabel, 2, 0 ); + spectrumLayout->addLayout( minimumMagnitudeLayout, 2, 1 ); + + spectrumGroup = new QGroupBox( tr( "Spectrum" ) ); + spectrumGroup->setLayout( spectrumLayout ); + + mainLayout = new QVBoxLayout(); + mainLayout->addWidget( spectrumGroup ); + mainLayout->addStretch( 1 ); + + setLayout( mainLayout ); +} + +/// \brief Saves the new settings. +void DsoConfigSpectrumPage::saveSettings() { + settings->post.spectrumWindow = Dso::WindowFunction( windowFunctionComboBox->currentIndex() ); + settings->post.spectrumReference = referenceLevelSpinBox->value(); + settings->post.spectrumLimit = minimumMagnitudeSpinBox->value(); +} diff --git a/openhantek/src/configdialog/DsoConfigAnalysisPage.h b/openhantek/src/configdialog/DsoConfigSpectrumPage.h similarity index 77% rename from openhantek/src/configdialog/DsoConfigAnalysisPage.h rename to openhantek/src/configdialog/DsoConfigSpectrumPage.h index aa408b51..4705c543 100644 --- a/openhantek/src/configdialog/DsoConfigAnalysisPage.h +++ b/openhantek/src/configdialog/DsoConfigSpectrumPage.h @@ -2,7 +2,7 @@ #include -#include "settings.h" +#include "dsosettings.h" #include #include @@ -15,13 +15,13 @@ #include //////////////////////////////////////////////////////////////////////////////// -/// \class DsoConfigAnalysisPage configpages.h -/// \brief Config page for the data analysis. -class DsoConfigAnalysisPage : public QWidget { +/// \class DsoConfigSpectrumPage configpages.h +/// \brief Config page for the data spectral analysis. +class DsoConfigSpectrumPage : public QWidget { Q_OBJECT public: - DsoConfigAnalysisPage(DsoSettings *settings, QWidget *parent = 0); + DsoConfigSpectrumPage( DsoSettings *settings, QWidget *parent = nullptr ); public slots: void saveSettings(); diff --git a/openhantek/src/configdialog/configdialog.cpp b/openhantek/src/configdialog/configdialog.cpp index eabda2f2..2f74909f 100644 --- a/openhantek/src/configdialog/configdialog.cpp +++ b/openhantek/src/configdialog/configdialog.cpp @@ -1,20 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ -/*#if defined(OS_UNIX) -#define CONFIG_PATH QDir::homePath() + "/.config/paranoiacs.net/openhantek" -#define CONFIG_FILE CONFIG_PATH "/openhantek.conf" -#elif defined(OS_DARWIN) -#define CONFIG_PATH QDir::homePath() + "/Library/Application Support/OpenHantek" -#define CONFIG_FILE CONFIG_PATH "/openhantek.plist" -#elif defined(OS_WINDOWS) -//#define CONFIG_PATH QDir::homePath() + "" // Too hard to get and this OS sucks -anyway, ignore it -#define CONFIG_FILE "HKEY_CURRENT_USER\\Software\\paranoiacs.net\\OpenHantek" -#endif*/ - -#define CONFIG_LIST_WIDTH 128 ///< The width of the page selection widget -#define CONFIG_LIST_ITEMHEIGHT 80 ///< The height of one item in the page selection widget -#define CONFIG_LIST_ICONSIZE 48 ///< The icon size in the page selection widget + +#define CONFIG_LIST_WIDTH 128 ///< The width of the page selection widget +#define CONFIG_LIST_ITEMHEIGHT 100 ///< The height of one item in the page selection widget +#define CONFIG_LIST_ICONWIDTH 80 ///< The icon size in the page selection widget +#define CONFIG_LIST_ICONHEIGHT 64 ///< The icon size in the page selection widget #include #include @@ -26,12 +16,11 @@ anyway, ignore it #include "configdialog.h" -#include "DsoConfigAnalysisPage.h" #include "DsoConfigColorsPage.h" -#include "DsoConfigFilesPage.h" #include "DsoConfigScopePage.h" +#include "DsoConfigSpectrumPage.h" -#include "settings.h" +#include "dsosettings.h" //////////////////////////////////////////////////////////////////////////////// // class DsoConfigDialog @@ -39,107 +28,102 @@ anyway, ignore it /// \param settings The target settings object. /// \param parent The parent widget. /// \param flags Flags for the window manager. -DsoConfigDialog::DsoConfigDialog(DsoSettings *settings, QWidget *parent, Qt::WindowFlags flags) - : QDialog(parent, flags), settings(settings) { - - this->setWindowTitle(tr("Settings")); +DsoConfigDialog::DsoConfigDialog( DsoSettings *settings, QWidget *parent ) : QDialog( parent ), settings( settings ) { + this->setWindowTitle( tr( "Settings" ) ); this->contentsWidget = new QListWidget; - this->contentsWidget->setViewMode(QListView::IconMode); - this->contentsWidget->setIconSize(QSize(CONFIG_LIST_ICONSIZE, CONFIG_LIST_ICONSIZE)); - this->contentsWidget->setMovement(QListView::Static); + this->contentsWidget->setViewMode( QListView::IconMode ); + this->contentsWidget->setIconSize( QSize( CONFIG_LIST_ICONWIDTH, CONFIG_LIST_ICONHEIGHT ) ); + this->contentsWidget->setMovement( QListView::Static ); this->contentsWidget->setGridSize( - QSize(CONFIG_LIST_WIDTH - 2 * this->contentsWidget->frameWidth(), CONFIG_LIST_ITEMHEIGHT)); - this->contentsWidget->setMaximumWidth(CONFIG_LIST_WIDTH); - this->contentsWidget->setMinimumWidth(CONFIG_LIST_WIDTH); - this->contentsWidget->setMinimumHeight(CONFIG_LIST_ITEMHEIGHT * 3 + 2 * (this->contentsWidget->frameWidth())); - - this->analysisPage = new DsoConfigAnalysisPage(settings); - this->colorsPage = new DsoConfigColorsPage(settings); - this->filesPage = new DsoConfigFilesPage(settings); - this->scopePage = new DsoConfigScopePage(settings); + QSize( CONFIG_LIST_WIDTH - 2 * this->contentsWidget->frameWidth(), CONFIG_LIST_ITEMHEIGHT ) ); + this->contentsWidget->setMaximumWidth( CONFIG_LIST_WIDTH ); + this->contentsWidget->setMinimumWidth( CONFIG_LIST_WIDTH ); + this->contentsWidget->setMinimumHeight( CONFIG_LIST_ITEMHEIGHT * 3 + 2 * ( this->contentsWidget->frameWidth() ) ); + + this->spectrumPage = new DsoConfigSpectrumPage( settings ); + this->colorsPage = new DsoConfigColorsPage( settings ); + this->scopePage = new DsoConfigScopePage( settings ); this->pagesWidget = new QStackedWidget; - this->pagesWidget->addWidget(this->analysisPage); - this->pagesWidget->addWidget(this->colorsPage); - this->pagesWidget->addWidget(this->filesPage); - this->pagesWidget->addWidget(this->scopePage); + this->pagesWidget->addWidget( this->scopePage ); + this->pagesWidget->addWidget( this->spectrumPage ); + this->pagesWidget->addWidget( this->colorsPage ); - this->acceptButton = new QPushButton(tr("&Ok")); - this->acceptButton->setDefault(true); - this->applyButton = new QPushButton(tr("&Apply")); - this->rejectButton = new QPushButton(tr("&Cancel")); + this->acceptButton = new QPushButton( tr( "&Ok" ) ); + this->acceptButton->setDefault( true ); + this->applyButton = new QPushButton( tr( "&Apply" ) ); + this->rejectButton = new QPushButton( tr( "&Cancel" ) ); this->createIcons(); - this->contentsWidget->setCurrentRow(0); + this->contentsWidget->setCurrentRow( 0 ); - this->horizontalLayout = new QHBoxLayout; - this->horizontalLayout->addWidget(this->contentsWidget); - this->horizontalLayout->addWidget(this->pagesWidget, 1); + this->sectionsLayout = new QHBoxLayout; + this->sectionsLayout->addWidget( this->contentsWidget ); + this->sectionsLayout->addWidget( this->pagesWidget, 1 ); this->buttonsLayout = new QHBoxLayout; - this->buttonsLayout->setSpacing(8); - this->buttonsLayout->addStretch(1); - this->buttonsLayout->addWidget(this->acceptButton); - this->buttonsLayout->addWidget(this->applyButton); - this->buttonsLayout->addWidget(this->rejectButton); + this->buttonsLayout->setSpacing( 8 ); + this->buttonsLayout->addStretch( 1 ); + this->buttonsLayout->addWidget( this->acceptButton ); + this->buttonsLayout->addWidget( this->applyButton ); + this->buttonsLayout->addWidget( this->rejectButton ); this->mainLayout = new QVBoxLayout; - this->mainLayout->addLayout(this->horizontalLayout); - this->mainLayout->addStretch(1); - this->mainLayout->addSpacing(8); - this->mainLayout->addLayout(this->buttonsLayout); - this->setLayout(this->mainLayout); - - connect(this->acceptButton, &QAbstractButton::clicked, this, &DsoConfigDialog::accept); - connect(this->applyButton, &QAbstractButton::clicked, this, &DsoConfigDialog::apply); - connect(this->rejectButton, &QAbstractButton::clicked, this, &QDialog::reject); + mainLayout->addSpacing( 8 ); + this->mainLayout->addLayout( this->sectionsLayout ); + this->mainLayout->addStretch( 1 ); + this->mainLayout->addSpacing( 8 ); + this->mainLayout->addLayout( this->buttonsLayout ); + this->setLayout( this->mainLayout ); + + connect( this->acceptButton, &QAbstractButton::clicked, this, &DsoConfigDialog::accept ); + connect( this->applyButton, &QAbstractButton::clicked, this, &DsoConfigDialog::apply ); + connect( this->rejectButton, &QAbstractButton::clicked, this, &QDialog::reject ); } + /// \brief Cleans up the dialog. DsoConfigDialog::~DsoConfigDialog() {} + /// \brief Create the icons for the pages. void DsoConfigDialog::createIcons() { - QListWidgetItem *analysisButton = new QListWidgetItem(contentsWidget); - analysisButton->setIcon(QIcon(":config/analysis.png")); - analysisButton->setText(tr("Analysis")); - - QListWidgetItem *colorsButton = new QListWidgetItem(contentsWidget); - colorsButton->setIcon(QIcon(":config/colors.png")); - colorsButton->setText(tr("Colors")); + QListWidgetItem *scopeButton = new QListWidgetItem( contentsWidget ); + scopeButton->setIcon( QIcon( ":config/scope.png" ) ); + scopeButton->setText( tr( "Scope" ) ); - QListWidgetItem *filesButton = new QListWidgetItem(contentsWidget); - filesButton->setIcon(QIcon(":config/files.png")); - filesButton->setText(tr("Files")); + QListWidgetItem *spectrumButton = new QListWidgetItem( contentsWidget ); + spectrumButton->setIcon( QIcon( ":config/spectrum.png" ) ); + spectrumButton->setText( tr( "Spectrum" ) ); - QListWidgetItem *scopeButton = new QListWidgetItem(contentsWidget); - scopeButton->setIcon(QIcon(":config/scope.png")); - scopeButton->setText(tr("Scope")); + QListWidgetItem *colorsButton = new QListWidgetItem( contentsWidget ); + colorsButton->setIcon( QIcon( ":config/colors.png" ) ); + colorsButton->setText( tr( "Colors" ) ); - connect(contentsWidget, &QListWidget::currentItemChanged, this, - &DsoConfigDialog::changePage); + connect( contentsWidget, &QListWidget::currentItemChanged, this, &DsoConfigDialog::changePage ); } + /// \brief Saves the settings and closes the dialog. void DsoConfigDialog::accept() { this->apply(); - QDialog::accept(); } + /// \brief Saves the settings. void DsoConfigDialog::apply() { - this->analysisPage->saveSettings(); - this->colorsPage->saveSettings(); - this->filesPage->saveSettings(); this->scopePage->saveSettings(); + this->spectrumPage->saveSettings(); + this->colorsPage->saveSettings(); } + /// \brief Change the config page. /// \param current The page that has been selected. /// \param previous The page that was selected before. -void DsoConfigDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) { - if (!current) current = previous; - - pagesWidget->setCurrentIndex(contentsWidget->row(current)); +void DsoConfigDialog::changePage( QListWidgetItem *current, QListWidgetItem *previous ) { + if ( !current ) + current = previous; + pagesWidget->setCurrentIndex( contentsWidget->row( current ) ); } diff --git a/openhantek/src/configdialog/configdialog.h b/openhantek/src/configdialog/configdialog.h index f6b577fe..7bca1cd1 100644 --- a/openhantek/src/configdialog/configdialog.h +++ b/openhantek/src/configdialog/configdialog.h @@ -2,10 +2,9 @@ #include -class DsoConfigAnalysisPage; -class DsoConfigColorsPage; -class DsoConfigFilesPage; +class DsoConfigSpectrumPage; class DsoConfigScopePage; +class DsoConfigColorsPage; class DsoSettings; class QHBoxLayout; @@ -22,14 +21,14 @@ class DsoConfigDialog : public QDialog { Q_OBJECT public: - DsoConfigDialog(DsoSettings *settings, QWidget *parent = 0, Qt::WindowFlags flags = 0); + DsoConfigDialog( DsoSettings *settings, QWidget *parent = nullptr ); ~DsoConfigDialog(); public slots: void accept(); void apply(); - void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void changePage( QListWidgetItem *current, QListWidgetItem *previous ); private: void createIcons(); @@ -37,16 +36,15 @@ class DsoConfigDialog : public QDialog { DsoSettings *settings; QVBoxLayout *mainLayout; - QHBoxLayout *horizontalLayout; + QHBoxLayout *sectionsLayout; QHBoxLayout *buttonsLayout; QListWidget *contentsWidget; QStackedWidget *pagesWidget; - DsoConfigAnalysisPage *analysisPage; - DsoConfigColorsPage *colorsPage; - DsoConfigFilesPage *filesPage; DsoConfigScopePage *scopePage; + DsoConfigSpectrumPage *spectrumPage; + DsoConfigColorsPage *colorsPage; QPushButton *acceptButton, *applyButton, *rejectButton; }; diff --git a/openhantek/src/docks/HorizontalDock.cpp b/openhantek/src/docks/HorizontalDock.cpp index 27cb695f..3433dda8 100644 --- a/openhantek/src/docks/HorizontalDock.cpp +++ b/openhantek/src/docks/HorizontalDock.cpp @@ -3,10 +3,11 @@ #include #include #include +#include +#include #include #include #include -#include #include @@ -17,198 +18,222 @@ #include "sispinbox.h" #include "utils/printutils.h" -template struct SELECT { - template - static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { +static int row = 0; + +template < typename... Args > struct SELECT { + template < typename C, typename R > static constexpr auto OVERLOAD_OF( R ( C::*pmf )( Args... ) ) -> decltype( pmf ) { return pmf; } }; -HorizontalDock::HorizontalDock(DsoSettingsScope *scope, QWidget *parent, Qt::WindowFlags flags) - : QDockWidget(tr("Horizontal"), parent, flags), scope(scope) { +HorizontalDock::HorizontalDock( DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent ) + : QDockWidget( tr( "Horizontal" ), parent ), scope( scope ) { // Initialize elements - this->samplerateLabel = new QLabel(tr("Samplerate")); - this->samplerateSiSpinBox = new SiSpinBox(UNIT_SAMPLES); - this->samplerateSiSpinBox->setMinimum(1); - this->samplerateSiSpinBox->setMaximum(1e8); - this->samplerateSiSpinBox->setUnitPostfix("/s"); - - timebaseSteps << 1.0 << 2.0 << 4.0 << 10.0; + this->samplerateLabel = new QLabel( tr( "Samplerate" ) ); + this->samplerateSiSpinBox = new SiSpinBox( UNIT_SAMPLES ); + this->samplerateSiSpinBox->setMinimum( 1 ); + this->samplerateSiSpinBox->setMaximum( 1e8 ); + this->samplerateSiSpinBox->setUnitPostfix( tr( "/s" ) ); - this->timebaseLabel = new QLabel(tr("Timebase")); - this->timebaseSiSpinBox = new SiSpinBox(UNIT_SECONDS); - this->timebaseSiSpinBox->setSteps(timebaseSteps); - this->timebaseSiSpinBox->setMinimum(1e-9); - this->timebaseSiSpinBox->setMaximum(3.6e3); + timebaseSteps << 1.0 << 2.0 << 5.0 << 10.0; - this->frequencybaseLabel = new QLabel(tr("Frequencybase")); - this->frequencybaseSiSpinBox = new SiSpinBox(UNIT_HERTZ); - this->frequencybaseSiSpinBox->setMinimum(1.0); - this->frequencybaseSiSpinBox->setMaximum(100e6); + this->timebaseLabel = new QLabel( tr( "Timebase" ) ); + this->timebaseSiSpinBox = new SiSpinBox( UNIT_SECONDS ); + this->timebaseSiSpinBox->setSteps( timebaseSteps ); + this->timebaseSiSpinBox->setMinimum( 1e-9 ); + this->timebaseSiSpinBox->setMaximum( 1e3 ); - this->recordLengthLabel = new QLabel(tr("Record length")); - this->recordLengthComboBox = new QComboBox(); - - this->formatLabel = new QLabel(tr("Format")); + this->formatLabel = new QLabel( tr( "Format" ) ); this->formatComboBox = new QComboBox(); - for (Dso::GraphFormat format: Dso::GraphFormatEnum) - this->formatComboBox->addItem(Dso::graphFormatString(format)); + for ( Dso::GraphFormat format : Dso::GraphFormatEnum ) + this->formatComboBox->addItem( Dso::graphFormatString( format ) ); + + this->calfreqLabel = new QLabel( tr( "Calibration out" ) ); + this->calfreqSiSpinBox = new SiSpinBox( UNIT_HERTZ ); + this->calfreqSiSpinBox->setSteps( spec->calfreqSteps ); + this->calfreqSiSpinBox->setMinimum( spec->calfreqSteps.first() ); + this->calfreqSiSpinBox->setMaximum( spec->calfreqSteps.last() ); this->dockLayout = new QGridLayout(); - this->dockLayout->setColumnMinimumWidth(0, 64); - this->dockLayout->setColumnStretch(1, 1); - this->dockLayout->addWidget(this->samplerateLabel, 0, 0); - this->dockLayout->addWidget(this->samplerateSiSpinBox, 0, 1); - this->dockLayout->addWidget(this->timebaseLabel, 1, 0); - this->dockLayout->addWidget(this->timebaseSiSpinBox, 1, 1); - this->dockLayout->addWidget(this->frequencybaseLabel, 2, 0); - this->dockLayout->addWidget(this->frequencybaseSiSpinBox, 2, 1); - this->dockLayout->addWidget(this->recordLengthLabel, 3, 0); - this->dockLayout->addWidget(this->recordLengthComboBox, 3, 1); - this->dockLayout->addWidget(this->formatLabel, 4, 0); - this->dockLayout->addWidget(this->formatComboBox, 4, 1); + this->dockLayout->setColumnMinimumWidth( 0, 64 ); + this->dockLayout->setColumnStretch( 1, 1 ); + this->dockLayout->setSpacing( DOCK_LAYOUT_SPACING ); + + row = 0; // allows flexible shift up/down + this->dockLayout->addWidget( this->timebaseLabel, row, 0 ); + this->dockLayout->addWidget( this->timebaseSiSpinBox, row++, 1 ); + this->dockLayout->addWidget( this->samplerateLabel, row, 0 ); + this->dockLayout->addWidget( this->samplerateSiSpinBox, row++, 1 ); + this->dockLayout->addWidget( this->formatLabel, row, 0 ); + this->dockLayout->addWidget( this->formatComboBox, row++, 1 ); + this->dockLayout->addWidget( this->calfreqLabel, row, 0 ); + this->dockLayout->addWidget( this->calfreqSiSpinBox, row++, 1 ); this->dockWidget = new QWidget(); - SetupDockWidget(this, dockWidget, dockLayout); + SetupDockWidget( this, dockWidget, dockLayout ); + + // Load settings into GUI + this->loadSettings( scope ); // Connect signals and slots - connect(this->samplerateSiSpinBox, SELECT::OVERLOAD_OF(&QDoubleSpinBox::valueChanged), this, &HorizontalDock::samplerateSelected); - connect(this->timebaseSiSpinBox, SELECT::OVERLOAD_OF(&QDoubleSpinBox::valueChanged), this, &HorizontalDock::timebaseSelected); - connect(this->frequencybaseSiSpinBox, SELECT::OVERLOAD_OF(&QDoubleSpinBox::valueChanged), this, &HorizontalDock::frequencybaseSelected); - connect(this->recordLengthComboBox, SELECT::OVERLOAD_OF(&QComboBox::currentIndexChanged), this, &HorizontalDock::recordLengthSelected); - connect(this->formatComboBox, SELECT::OVERLOAD_OF(&QComboBox::currentIndexChanged), this, &HorizontalDock::formatSelected); + connect( this->samplerateSiSpinBox, SELECT< double >::OVERLOAD_OF( &QDoubleSpinBox::valueChanged ), this, + &HorizontalDock::samplerateSelected ); + connect( this->timebaseSiSpinBox, SELECT< double >::OVERLOAD_OF( &QDoubleSpinBox::valueChanged ), this, + &HorizontalDock::timebaseSelected ); + connect( this->formatComboBox, SELECT< int >::OVERLOAD_OF( &QComboBox::currentIndexChanged ), this, + &HorizontalDock::formatSelected ); + connect( this->calfreqSiSpinBox, SELECT< double >::OVERLOAD_OF( &QDoubleSpinBox::valueChanged ), this, + &HorizontalDock::calfreqSelected ); +} +void HorizontalDock::loadSettings( DsoSettingsScope *scope ) { // Set values - this->setSamplerate(scope->horizontal.samplerate); - this->setTimebase(scope->horizontal.timebase); - this->setFrequencybase(scope->horizontal.frequencybase); - // this->setRecordLength(scope->horizontal.recordLength); - this->setFormat(scope->horizontal.format); + this->setSamplerate( scope->horizontal.samplerate ); + this->setTimebase( scope->horizontal.timebase ); + this->setFormat( scope->horizontal.format ); + this->setCalfreq( scope->horizontal.calfreq ); } /// \brief Don't close the dock, just hide it. /// \param event The close event that should be handled. -void HorizontalDock::closeEvent(QCloseEvent *event) { +void HorizontalDock::closeEvent( QCloseEvent *event ) { this->hide(); - event->accept(); } -void HorizontalDock::setFrequencybase(double frequencybase) { - QSignalBlocker blocker(frequencybaseSiSpinBox); - frequencybaseSiSpinBox->setValue(frequencybase); -} -void HorizontalDock::setSamplerate(double samplerate) { - QSignalBlocker blocker(samplerateSiSpinBox); - samplerateSiSpinBox->setValue(samplerate); +double HorizontalDock::setSamplerate( double samplerate ) { + // printf( "HD::setSamplerate( %g )\n", samplerate ); + QSignalBlocker blocker( timebaseSiSpinBox ); + timebaseSiSpinBox->setMaximum( scope->horizontal.maxTimebase ); + blocker = QSignalBlocker( samplerateSiSpinBox ); + samplerateSiSpinBox->setValue( samplerate ); + return samplerateSiSpinBox->value(); } -double HorizontalDock::setTimebase(double timebase) { - QSignalBlocker blocker(timebaseSiSpinBox); + +double HorizontalDock::setTimebase( double timebase ) { + // printf( "HD::setTimebase( %g )\n", timebase ); + QSignalBlocker blocker( timebaseSiSpinBox ); // timebaseSteps are repeated in each decade - double decade = pow(10, floor(log10(timebase))); + double decade = pow( 10, floor( log10( timebase ) ) ); double vNorm = timebase / decade; - for (int i = 0; i < timebaseSteps.size() - 1; ++i) { - if (timebaseSteps.at(i) <= vNorm && vNorm < timebaseSteps.at(i + 1)) { - timebaseSiSpinBox->setValue(decade * timebaseSteps.at(i)); + for ( int i = 0; i < timebaseSteps.size() - 1; ++i ) { + if ( timebaseSteps.at( i ) <= vNorm && vNorm < timebaseSteps.at( i + 1 ) ) { + timebaseSiSpinBox->setValue( decade * timebaseSteps.at( i ) ); break; } } + // printf( "return %g\n", timebaseSiSpinBox->value() ); + calculateSamplerateSteps( timebase ); return timebaseSiSpinBox->value(); } -int addRecordLength(QComboBox *recordLengthComboBox, unsigned recordLength) { - recordLengthComboBox->addItem( - recordLength == UINT_MAX ? QCoreApplication::translate("HorizontalDock","Roll") : valueToString(recordLength, UNIT_SAMPLES, 3), recordLength); - return recordLengthComboBox->count()-1; -} - -void HorizontalDock::setRecordLength(unsigned int recordLength) { - QSignalBlocker blocker(recordLengthComboBox); - int index = recordLengthComboBox->findData(recordLength); - scope->horizontal.recordLength = recordLength; - - if (index == -1) { - index = addRecordLength(recordLengthComboBox, recordLength); - } - recordLengthComboBox->setCurrentIndex(index); -} -int HorizontalDock::setFormat(Dso::GraphFormat format) { - QSignalBlocker blocker(formatComboBox); - if (format >= Dso::GraphFormat::TY && format <= Dso::GraphFormat::XY) { - formatComboBox->setCurrentIndex(format); +int HorizontalDock::setFormat( Dso::GraphFormat format ) { + QSignalBlocker blocker( formatComboBox ); + if ( format >= Dso::GraphFormat::TY && format <= Dso::GraphFormat::XY ) { + formatComboBox->setCurrentIndex( format ); return format; } - return -1; } -void HorizontalDock::setAvailableRecordLengths(const std::vector &recordLengths) { - QSignalBlocker blocker(recordLengthComboBox); - - recordLengthComboBox->clear(); - for (auto recordLength : recordLengths) { - addRecordLength(recordLengthComboBox, recordLength); - } - setRecordLength(scope->horizontal.recordLength); +double HorizontalDock::setCalfreq( double calfreq ) { + QSignalBlocker blocker( calfreqSiSpinBox ); + calfreqSiSpinBox->setValue( calfreq ); + return calfreqSiSpinBox->value(); } -void HorizontalDock::setSamplerateLimits(double minimum, double maximum) { - QSignalBlocker blocker(samplerateSiSpinBox); - this->samplerateSiSpinBox->setMinimum(minimum); - this->samplerateSiSpinBox->setMaximum(maximum); + +void HorizontalDock::setSamplerateLimits( double minimum, double maximum ) { + // printf( "HD::setSamplerateLimits( %g, %g )\n", minimum, maximum ); + QSignalBlocker blocker( samplerateSiSpinBox ); + if ( bool( minimum ) ) + this->samplerateSiSpinBox->setMinimum( minimum ); + if ( bool( maximum ) ) + this->samplerateSiSpinBox->setMaximum( maximum ); } -void HorizontalDock::setSamplerateSteps(int mode, QList steps) { + +void HorizontalDock::setSamplerateSteps( int mode, const QList< double > steps ) { + // printf( "HD::setSamplerateSteps( %d )\n", mode ); + // qDebug() << "HD::setSamplerateSteps" << mode << steps; + samplerateSteps = steps; // Assume that method is invoked for fixed samplerate devices only - QSignalBlocker samplerateBlocker(samplerateSiSpinBox); - samplerateSiSpinBox->setMode(mode); - samplerateSiSpinBox->setSteps(steps); - samplerateSiSpinBox->setMinimum(steps.first()); - samplerateSiSpinBox->setMaximum(steps.last()); + QSignalBlocker samplerateBlocker( samplerateSiSpinBox ); + samplerateSiSpinBox->setMode( mode ); + samplerateSiSpinBox->setSteps( steps ); + samplerateSiSpinBox->setMinimum( steps.first() ); + samplerateSiSpinBox->setMaximum( steps.last() ); // Make reasonable adjustments to the timebase spinbox - QSignalBlocker timebaseBlocker(timebaseSiSpinBox); - timebaseSiSpinBox->setMinimum(pow(10, floor(log10(1.0 / steps.last())))); - timebaseSiSpinBox->setMaximum(pow(10, ceil(log10(8192.0 / (steps.first() * 10))))); + QSignalBlocker timebaseBlocker( timebaseSiSpinBox ); + timebaseSiSpinBox->setMinimum( pow( 10, floor( log10( 1.0 / steps.last() ) ) ) ); + calculateSamplerateSteps( timebaseSiSpinBox->value() ); } -/// \brief Called when the frequencybase spinbox changes its value. -/// \param frequencybase The frequencybase in hertz. -void HorizontalDock::frequencybaseSelected(double frequencybase) { - scope->horizontal.frequencybase = frequencybase; - emit frequencybaseChanged(frequencybase); -} /// \brief Called when the samplerate spinbox changes its value. /// \param samplerate The samplerate in samples/second. -void HorizontalDock::samplerateSelected(double samplerate) { +void HorizontalDock::samplerateSelected( double samplerate ) { + // printf( "HD::samplerateSelected( %g )\n", samplerate ); scope->horizontal.samplerate = samplerate; - scope->horizontal.samplerateSource = DsoSettingsScopeHorizontal::Samplerrate; - emit samplerateChanged(samplerate); + emit samplerateChanged( samplerate ); } + /// \brief Called when the timebase spinbox changes its value. /// \param timebase The timebase in seconds. -void HorizontalDock::timebaseSelected(double timebase) { +void HorizontalDock::timebaseSelected( double timebase ) { + // printf( "HD::timebaseSelected( %g )\n", timebase ); scope->horizontal.timebase = timebase; - scope->horizontal.samplerateSource = DsoSettingsScopeHorizontal::Duration; - emit timebaseChanged(timebase); + calculateSamplerateSteps( timebase ); + emit timebaseChanged( timebase ); +} + + +void HorizontalDock::calculateSamplerateSteps( double timebase ) { + int size = samplerateSteps.size(); + if ( size ) { + // search appropriate min & max sample rate + double min = samplerateSteps[ 0 ]; + double max = samplerateSteps[ 0 ]; + for ( int id = 0; id < size; ++id ) { + double sRate = samplerateSteps[ id ]; + // printf( "sRate %g, sRate*timebase %g\n", sRate, sRate * timebase ); + // min must be < maxRate + // find minimal samplerate to get at least this number of samples per div + if ( id < size - 1 && sRate * timebase <= 10 ) { // 10 samples/div + min = sRate; + } + // max must be > minRate + // find max samplesrate to get not more then this number of samples per div + // number should be <= 1000 to get enough samples for two full screens (to ensure triggering) + if ( id && sRate * timebase <= 1000 ) { // 1000 samples/div + max = sRate; + } + } + min = qMax( min, qMin( 10e3, max ) ); // not less than 10kS unless max is smaller + // printf( "HD::cSS( %g ) -> %g, %g\n", timebase, min, max ); + setSamplerateLimits( min, max ); + } } -/// \brief Called when the record length combo box changes its value. -/// \param index The index of the combo box item. -void HorizontalDock::recordLengthSelected(int index) { - scope->horizontal.recordLength = this->recordLengthComboBox->itemData(index).toUInt(); - emit recordLengthChanged(index); -} /// \brief Called when the format combo box changes its value. /// \param index The index of the combo box item. -void HorizontalDock::formatSelected(int index) { - scope->horizontal.format = (Dso::GraphFormat)index; - emit formatChanged(scope->horizontal.format); +void HorizontalDock::formatSelected( int index ) { + scope->horizontal.format = Dso::GraphFormat( index ); + emit formatChanged( scope->horizontal.format ); +} + + +/// \brief Called when the calfreq spinbox changes its value. +/// \param calfreq The calibration frequency in hertz. +void HorizontalDock::calfreqSelected( double calfreq ) { + // printf( "calfreqSelected: %g\n", calfreq ); + scope->horizontal.calfreq = calfreq; + emit calfreqChanged( calfreq ); } diff --git a/openhantek/src/docks/HorizontalDock.h b/openhantek/src/docks/HorizontalDock.h index da1b16d6..78ea1fbf 100644 --- a/openhantek/src/docks/HorizontalDock.h +++ b/openhantek/src/docks/HorizontalDock.h @@ -7,6 +7,7 @@ #include +#include "hantekdso/controlspecification.h" #include "hantekdso/enums.h" class QLabel; @@ -16,9 +17,10 @@ class QComboBox; class SiSpinBox; struct DsoSettingsScope; +// struct ControlSpecification; -Q_DECLARE_METATYPE(std::vector) -Q_DECLARE_METATYPE(std::vector) +Q_DECLARE_METATYPE( std::vector< unsigned > ) +Q_DECLARE_METATYPE( std::vector< double > ) /// \brief Dock window for the horizontal axis. /// It contains the settings for the timebase and the display format. @@ -30,69 +32,69 @@ class HorizontalDock : public QDockWidget { /// \param settings The target settings object. /// \param parent The parent widget. /// \param flags Flags for the window manager. - HorizontalDock(DsoSettingsScope *scope, QWidget *parent, Qt::WindowFlags flags = 0); + HorizontalDock( DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent ); - /// \brief Changes the frequencybase. - /// \param frequencybase The frequencybase in hertz. - void setFrequencybase(double timebase); /// \brief Changes the samplerate. /// \param samplerate The samplerate in seconds. - void setSamplerate(double samplerate); + double setSamplerate( double samplerate ); /// \brief Changes the timebase. /// \param timebase The timebase in seconds. - double setTimebase(double timebase); + double setTimebase( double timebase ); /// \brief Changes the record length if the new value is supported. /// \param recordLength The record length in samples. - void setRecordLength(unsigned int recordLength); + void setRecordLength( unsigned int recordLength ); /// \brief Changes the format if the new value is supported. /// \param format The format for the horizontal axis. /// \return Index of format-value, -1 on error. - int setFormat(Dso::GraphFormat format); - /// \brief Updates the available record lengths in the combo box. - /// \param recordLengths The available record lengths for the combo box. - void setAvailableRecordLengths(const std::vector &recordLengths); + int setFormat( Dso::GraphFormat format ); /// \brief Updates the minimum and maximum of the samplerate spin box. /// \param minimum The minimum value the spin box should accept. /// \param maximum The minimum value the spin box should accept. - void setSamplerateLimits(double minimum, double maximum); + void setSamplerateLimits( double minimum, double maximum ); /// \brief Updates the mode and steps of the samplerate spin box. - /// \param mode The mode value the spin box should accept. /// \param steps The steps value the spin box should accept. - void setSamplerateSteps(int mode, QList sampleSteps); + void setSamplerateSteps( int mode, QList< double > sampleSteps ); + void calculateSamplerateSteps( double timebase ); + /// \brief Changes the calibration frequency. + /// \param calfreq The calibration frequency in hertz. + double setCalfreq( double calfreq ); + + public slots: + /// \brief Loads settings into GUI + /// \param scope Settings to load + void loadSettings( DsoSettingsScope *scope ); protected: - void closeEvent(QCloseEvent *event); - - QGridLayout *dockLayout; ///< The main layout for the dock window - QWidget *dockWidget; ///< The main widget for the dock window - QLabel *samplerateLabel; ///< The label for the samplerate spinbox - QLabel *timebaseLabel; ///< The label for the timebase spinbox - QLabel *frequencybaseLabel; ///< The label for the frequencybase spinbox - QLabel *recordLengthLabel; ///< The label for the record length combobox - QLabel *formatLabel; ///< The label for the format combobox - SiSpinBox *samplerateSiSpinBox; ///< Selects the samplerate for aquisitions - SiSpinBox *timebaseSiSpinBox; ///< Selects the timebase for voltage graphs - SiSpinBox *frequencybaseSiSpinBox; ///< Selects the frequencybase for spectrum graphs - QComboBox *recordLengthComboBox; ///< Selects the record length for aquisitions - QComboBox *formatComboBox; ///< Selects the way the sampled data is - /// interpreted and shown - - DsoSettingsScope *scope; ///< The settings provided by the parent class - QList timebaseSteps; ///< Steps for the timebase spinbox + void closeEvent( QCloseEvent *event ); + QGridLayout *dockLayout; ///< The main layout for the dock window + QWidget *dockWidget; ///< The main widget for the dock window + QLabel *samplerateLabel; ///< The label for the samplerate spinbox + QLabel *timebaseLabel; ///< The label for the timebase spinbox + QLabel *formatLabel; ///< The label for the format combobox + QLabel *calfreqLabel; ///< The label for the calibration frequency spinbox + SiSpinBox *samplerateSiSpinBox; ///< Selects the samplerate for aquisitions + SiSpinBox *timebaseSiSpinBox; ///< Selects the timebase for voltage graphs + QComboBox *formatComboBox; ///< Selects the way the sampled data is + /// interpreted and shown + SiSpinBox *calfreqSiSpinBox; ///< Selects the calibration frequency + + DsoSettingsScope *scope; ///< The settings provided by the parent class + QList< double > timebaseSteps; ///< Steps for the timebase spinbox + QList< double > calfreqSteps; ///< Steps for the calfreq spinbox + QList< double > samplerateSteps; ///< Possible sampe rates QStringList formatStrings; ///< Strings for the formats protected slots: - void frequencybaseSelected(double frequencybase); - void samplerateSelected(double samplerate); - void timebaseSelected(double timebase); - void recordLengthSelected(int index); - void formatSelected(int index); + void samplerateSelected( double samplerate ); + void timebaseSelected( double timebase ); + void formatSelected( int index ); + void calfreqSelected( double calfreq ); signals: - void frequencybaseChanged(double frequencybase); ///< The frequencybase has been changed - void samplerateChanged(double samplerate); ///< The samplerate has been changed - void timebaseChanged(double timebase); ///< The timebase has been changed - void recordLengthChanged(unsigned long recordLength); ///< The recordd length has been changed - void formatChanged(Dso::GraphFormat format); ///< The viewing format has been changed + void samplerateChanged( double samplerate ); ///< The samplerate has been changed + void timebaseChanged( double timebase ); ///< The timebase has been changed + void recordLengthChanged( unsigned long recordLength ); ///< The recordd length has been changed + void formatChanged( Dso::GraphFormat format ); ///< The viewing format has been changed + void calfreqChanged( double calfreq ); ///< The timebase has been changed }; diff --git a/openhantek/src/docks/SpectrumDock.cpp b/openhantek/src/docks/SpectrumDock.cpp index 2d53607b..d380e182 100644 --- a/openhantek/src/docks/SpectrumDock.cpp +++ b/openhantek/src/docks/SpectrumDock.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -12,89 +13,144 @@ #include "SpectrumDock.h" #include "dockwindows.h" -#include "settings.h" +#include "dsosettings.h" #include "sispinbox.h" #include "utils/printutils.h" -template struct SELECT { - template - static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { + +template < typename... Args > struct SELECT { + template < typename C, typename R > static constexpr auto OVERLOAD_OF( R ( C::*pmf )( Args... ) ) -> decltype( pmf ) { return pmf; } }; -SpectrumDock::SpectrumDock(DsoSettingsScope *scope, QWidget *parent, Qt::WindowFlags flags) - : QDockWidget(tr("Spectrum"), parent, flags), scope(scope) { +SpectrumDock::SpectrumDock( DsoSettingsScope *scope, QWidget *parent ) : QDockWidget( tr( "Spectrum" ), parent ), scope( scope ) { // Initialize lists for comboboxes - this->magnitudeSteps = { 1e0 , 2e0 , 3e0 , 6e0 , 1e1 , 2e1 , 3e1 , 6e1 , 1e2 , 2e2 , 3e2, 6e2 }; - for (const auto& magnitude: magnitudeSteps) - this->magnitudeStrings << valueToString(magnitude, UNIT_DECIBEL, 0); + this->magnitudeSteps = {1, 2, 3, 6, 10, 20, 40, 60, 80, 100}; + for ( const auto &magnitude : magnitudeSteps ) + this->magnitudeStrings << valueToString( magnitude, UNIT_DECIBEL, 0 ); this->dockLayout = new QGridLayout(); - this->dockLayout->setColumnMinimumWidth(0, 64); - this->dockLayout->setColumnStretch(1, 1); + this->dockLayout->setColumnMinimumWidth( 0, 64 ); + this->dockLayout->setColumnStretch( 1, 1 ); + this->dockLayout->setSpacing( DOCK_LAYOUT_SPACING ); // Initialize elements - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { + unsigned channel; + for ( channel = 0; channel < scope->voltage.size(); ++channel ) { ChannelBlock b; - b.magnitudeComboBox=(new QComboBox()); - b.usedCheckBox=(new QCheckBox(scope->voltage[channel].name)); + b.magnitudeComboBox = ( new QComboBox() ); + QString name = scope->spectrum[ channel ].name; + name.insert( int( channel ), '&' ); // &SP1, S&P2, SP&M + b.usedCheckBox = ( new QCheckBox( name ) ); - channelBlocks.push_back(b); + channelBlocks.push_back( b ); - this->dockLayout->addWidget(b.usedCheckBox, (int)channel, 0); - this->dockLayout->addWidget(b.magnitudeComboBox, (int)channel, 1); + this->dockLayout->addWidget( b.usedCheckBox, int( channel ), 0 ); + this->dockLayout->addWidget( b.magnitudeComboBox, int( channel ), 1 ); - b.magnitudeComboBox->addItems(this->magnitudeStrings); - this->setMagnitude(channel, scope->spectrum[channel].magnitude); - this->setUsed(channel, scope->spectrum[channel].used); + b.magnitudeComboBox->addItems( this->magnitudeStrings ); // Connect signals and slots - connect(b.usedCheckBox, &QCheckBox::toggled, [this,channel](bool checked) { + connect( b.usedCheckBox, &QCheckBox::toggled, [this, channel]( bool checked ) { // Send signal if it was one of the checkboxes - if (channel < this->scope->voltage.size()) { - this->scope->spectrum[channel].used = checked; - emit usedChanged(channel, checked); - } - }); - - connect(b.magnitudeComboBox, SELECT::OVERLOAD_OF(&QComboBox::currentIndexChanged), [this,channel](unsigned index) { - // Send signal if it was one of the comboboxes - if (channel < this->scope->voltage.size()) { - this->scope->spectrum[channel].magnitude = this->magnitudeSteps.at(index); - emit magnitudeChanged(channel, this->scope->spectrum[channel].magnitude); + if ( channel < this->scope->voltage.size() ) { + this->scope->spectrum[ channel ].used = checked; + emit usedChanged( channel, checked ); } - }); + } ); + + connect( b.magnitudeComboBox, SELECT< int >::OVERLOAD_OF( &QComboBox::currentIndexChanged ), + [this, channel]( unsigned index ) { + // Send signal if it was one of the comboboxes + if ( channel < this->scope->voltage.size() ) { + this->scope->spectrum[ channel ].magnitude = this->magnitudeSteps.at( index ); + emit magnitudeChanged( channel, this->scope->spectrum[ channel ].magnitude ); + } + } ); } + frequencybaseLabel = new QLabel( tr( "Frequencybase" ) ); + frequencybaseSiSpinBox = new SiSpinBox( UNIT_HERTZ ); + frequencybaseSiSpinBox->setMinimum( 0.1 ); + frequencybaseSiSpinBox->setMaximum( 100e6 ); + dockLayout->addWidget( this->frequencybaseLabel, int( channel ), 0 ); + dockLayout->addWidget( this->frequencybaseSiSpinBox, int( channel ), 1 ); + connect( frequencybaseSiSpinBox, SELECT< double >::OVERLOAD_OF( &QDoubleSpinBox::valueChanged ), this, + &SpectrumDock::frequencybaseSelected ); + + // Load settings into GUI + this->loadSettings( scope ); dockWidget = new QWidget(); - SetupDockWidget(this, dockWidget, dockLayout); + SetupDockWidget( this, dockWidget, dockLayout ); } + +void SpectrumDock::loadSettings( DsoSettingsScope *scope ) { + // Initialize elements + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + this->setMagnitude( channel, scope->spectrum[ channel ].magnitude ); + this->setUsed( channel, scope->spectrum[ channel ].used ); + } + setFrequencybase( scope->horizontal.frequencybase ); +} + + /// \brief Don't close the dock, just hide it /// \param event The close event that should be handled. -void SpectrumDock::closeEvent(QCloseEvent *event) { +void SpectrumDock::closeEvent( QCloseEvent *event ) { this->hide(); - event->accept(); } -int SpectrumDock::setMagnitude(ChannelID channel, double magnitude) { - if (channel >= scope->voltage.size()) return -1; - QSignalBlocker blocker(channelBlocks[channel].magnitudeComboBox); - auto indexIt = std::find(magnitudeSteps.begin(),magnitudeSteps.end(),magnitude); - if (indexIt == magnitudeSteps.end()) return -1; - int index = (int)std::distance(magnitudeSteps.begin(), indexIt); - channelBlocks[channel].magnitudeComboBox->setCurrentIndex(index); +int SpectrumDock::setMagnitude( ChannelID channel, double magnitude ) { + if ( channel >= scope->voltage.size() ) + return -1; + QSignalBlocker blocker( channelBlocks[ channel ].magnitudeComboBox ); + + auto indexIt = std::find( magnitudeSteps.begin(), magnitudeSteps.end(), magnitude ); + if ( indexIt == magnitudeSteps.end() ) + return -1; + int index = int( std::distance( magnitudeSteps.begin(), indexIt ) ); + channelBlocks[ channel ].magnitudeComboBox->setCurrentIndex( index ); return index; } -unsigned SpectrumDock::setUsed(ChannelID channel, bool used) { - if (channel >= scope->voltage.size()) return INT_MAX; - QSignalBlocker blocker(channelBlocks[channel].usedCheckBox); - channelBlocks[channel].usedCheckBox->setChecked(used); +unsigned SpectrumDock::setUsed( ChannelID channel, bool used ) { + if ( channel >= scope->voltage.size() ) + return INT_MAX; + QSignalBlocker blocker( channelBlocks[ channel ].usedCheckBox ); + + channelBlocks[ channel ].usedCheckBox->setChecked( used ); return channel; } + + +/// \brief Called when the samplerate from horizontal dock changes its value. +/// \param samplerare The samplerate in hertz. +void SpectrumDock::setSamplerate( double samplerate ) { + // printf( "SD::setSamplerate( %g )\n", samplerate ); + double maxFreqBase = samplerate / DIVS_TIME / 2; // Nyquist frequency + frequencybaseSiSpinBox->setMaximum( maxFreqBase ); + if ( frequencybaseSiSpinBox->value() > maxFreqBase ) + setFrequencybase( maxFreqBase ); +} + + +void SpectrumDock::setFrequencybase( double frequencybase ) { + // printf( "SD::setFrequencybase( %g )\n", frequencybase ); + QSignalBlocker blocker( frequencybaseSiSpinBox ); + frequencybaseSiSpinBox->setValue( frequencybase ); +} + + +/// \brief Called when the frequencybase spinbox changes its value. +/// \param frequencybase The frequencybase in hertz. +void SpectrumDock::frequencybaseSelected( double frequencybase ) { + // printf( "SD::frequencybaseSelected( %g )\n", frequencybase ); + scope->horizontal.frequencybase = frequencybase; + emit frequencybaseChanged( frequencybase ); +} diff --git a/openhantek/src/docks/SpectrumDock.h b/openhantek/src/docks/SpectrumDock.h index 11b5e12b..4ee56942 100644 --- a/openhantek/src/docks/SpectrumDock.h +++ b/openhantek/src/docks/SpectrumDock.h @@ -24,39 +24,58 @@ class SpectrumDock : public QDockWidget { /// \param settings The target settings object. /// \param parent The parent widget. /// \param flags Flags for the window manager. - SpectrumDock(DsoSettingsScope *scope, QWidget *parent, Qt::WindowFlags flags = 0); + SpectrumDock( DsoSettingsScope *scope, QWidget *parent ); /// \brief Sets the magnitude for a channel. /// \param channel The channel, whose magnitude should be set. /// \param magnitude The magnitude in dB. /// \return Index of magnitude-value, -1 on error. - int setMagnitude(ChannelID channel, double magnitude); + int setMagnitude( ChannelID channel, double magnitude ); /// \brief Enables/disables a channel. /// \param channel The channel, that should be enabled/disabled. /// \param used True if the channel should be enabled, false otherwise. /// \return Index of channel, INT_MAX on error. - unsigned setUsed(ChannelID channel, bool used); + unsigned setUsed( ChannelID channel, bool used ); + + /// \brief Changes the frequencybase. + /// \param frequencybase The frequencybase in hertz. + void setFrequencybase( double timebase ); + + public slots: + /// \brief Loads settings into GUI + /// \param scope Settings to load + void loadSettings( DsoSettingsScope *scope ); + + /// \brief Called when the samplerate from horizontal dock changes its value. + /// \param samplerare The samplerate in hertz. + void setSamplerate( double samplerate ); + + private slots: + void frequencybaseSelected( double frequencybase ); protected: - void closeEvent(QCloseEvent *event); + void closeEvent( QCloseEvent *event ); - QGridLayout *dockLayout; ///< The main layout for the dock window - QWidget *dockWidget; ///< The main widget for the dock window + QGridLayout *dockLayout; ///< The main layout for the dock window + QWidget *dockWidget; ///< The main widget for the dock window struct ChannelBlock { - QCheckBox * usedCheckBox; ///< Enable/disable a specific channel - QComboBox * magnitudeComboBox; ///< Select the vertical magnitude for the spectrums + QCheckBox *usedCheckBox; ///< Enable/disable a specific channel + QComboBox *magnitudeComboBox; ///< Select the vertical magnitude for the spectrums }; - std::vector channelBlocks; + std::vector< ChannelBlock > channelBlocks; - DsoSettingsScope* scope; ///< The settings provided by the parent class + DsoSettingsScope *scope; ///< The settings provided by the parent class - std::vector magnitudeSteps; ///< The selectable magnitude steps in dB/div - QStringList magnitudeStrings; ///< String representations for the magnitude steps + std::vector< double > magnitudeSteps; ///< The selectable magnitude steps in dB/div + QStringList magnitudeStrings; ///< String representations for the magnitude steps + QLabel *frequencybaseLabel; ///< The label for the frequencybase spinbox + SiSpinBox *frequencybaseSiSpinBox; ///< Selects the frequencybase for spectrum graphs signals: - void magnitudeChanged(ChannelID channel, double magnitude); ///< A magnitude has been selected - void usedChanged(ChannelID channel, bool used); ///< A spectrum has been enabled/disabled + void magnitudeChanged( ChannelID channel, double magnitude ); ///< A magnitude has been selected + void usedChanged( ChannelID channel, bool used ); ///< A spectrum has been enabled/disabled + void frequencybaseChanged( double frequencybase ); ///< The frequencybase has been changed }; diff --git a/openhantek/src/docks/TriggerDock.cpp b/openhantek/src/docks/TriggerDock.cpp index 03e9297e..7013a16f 100644 --- a/openhantek/src/docks/TriggerDock.cpp +++ b/openhantek/src/docks/TriggerDock.cpp @@ -12,105 +12,104 @@ #include "TriggerDock.h" #include "dockwindows.h" +#include "dsosettings.h" #include "hantekdso/controlspecification.h" -#include "settings.h" #include "sispinbox.h" #include "utils/printutils.h" -TriggerDock::TriggerDock(DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent, - Qt::WindowFlags flags) - : QDockWidget(tr("Trigger"), parent, flags), scope(scope), mSpec(spec) { + +TriggerDock::TriggerDock( DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent ) + : QDockWidget( tr( "Trigger" ), parent ), scope( scope ), mSpec( spec ) { // Initialize lists for comboboxes - for (ChannelID channel = 0; channel < mSpec->channels; ++channel) - this->sourceStandardStrings << tr("CH%1").arg(channel + 1); - for (const Dso::SpecialTriggerChannel &specialTrigger : mSpec->specialTriggerChannels) - this->sourceSpecialStrings.append(QString::fromStdString(specialTrigger.name)); + for ( ChannelID channel = 0; channel < mSpec->channels; ++channel ) + this->sourceStandardStrings << tr( "CH%1" ).arg( channel + 1 ); + // add "smooth" source + for ( ChannelID channel = 0; channel < mSpec->channels; ++channel ) + this->sourceStandardStrings << tr( "CH%1 smooth" ).arg( channel + 1 ); // Initialize elements - this->modeLabel = new QLabel(tr("Mode")); - this->modeComboBox = new QComboBox(); - for (Dso::TriggerMode mode : mSpec->triggerModes) this->modeComboBox->addItem(Dso::triggerModeString(mode)); - - this->slopeLabel = new QLabel(tr("Slope")); - this->slopeComboBox = new QComboBox(); - for (Dso::Slope slope : Dso::SlopeEnum) this->slopeComboBox->addItem(Dso::slopeString(slope)); - - this->sourceLabel = new QLabel(tr("Source")); - this->sourceComboBox = new QComboBox(); - this->sourceComboBox->addItems(this->sourceStandardStrings); - this->sourceComboBox->addItems(this->sourceSpecialStrings); - - this->dockLayout = new QGridLayout(); - this->dockLayout->setColumnMinimumWidth(0, 64); - this->dockLayout->setColumnStretch(1, 1); - this->dockLayout->addWidget(this->modeLabel, 0, 0); - this->dockLayout->addWidget(this->modeComboBox, 0, 1); - this->dockLayout->addWidget(this->sourceLabel, 1, 0); - this->dockLayout->addWidget(this->sourceComboBox, 1, 1); - this->dockLayout->addWidget(this->slopeLabel, 2, 0); - this->dockLayout->addWidget(this->slopeComboBox, 2, 1); - - this->dockWidget = new QWidget(); - SetupDockWidget(this, dockWidget, dockLayout); - - // Set values - setMode(scope->trigger.mode); - setSlope(scope->trigger.slope); - setSource(scope->trigger.special, scope->trigger.source); + modeLabel = new QLabel( tr( "Mode" ) ); + modeComboBox = new QComboBox(); + for ( Dso::TriggerMode mode : mSpec->triggerModes ) + modeComboBox->addItem( Dso::triggerModeString( mode ) ); + + slopeLabel = new QLabel( tr( "Slope" ) ); + slopeComboBox = new QComboBox(); + for ( Dso::Slope slope : Dso::SlopeEnum ) + slopeComboBox->addItem( Dso::slopeString( slope ) ); + + sourceLabel = new QLabel( tr( "Source" ) ); + sourceComboBox = new QComboBox(); + sourceComboBox->addItems( sourceStandardStrings ); + + dockLayout = new QGridLayout(); + dockLayout->setColumnMinimumWidth( 0, 64 ); + dockLayout->setColumnStretch( 1, 1 ); + dockLayout->setSpacing( DOCK_LAYOUT_SPACING ); + dockLayout->addWidget( modeLabel, 0, 0 ); + dockLayout->addWidget( modeComboBox, 0, 1 ); + dockLayout->addWidget( sourceLabel, 1, 0 ); + dockLayout->addWidget( sourceComboBox, 1, 1 ); + dockLayout->addWidget( slopeLabel, 2, 0 ); + dockLayout->addWidget( slopeComboBox, 2, 1 ); + + dockWidget = new QWidget(); + SetupDockWidget( this, dockWidget, dockLayout ); + + // Load settings into GUI + loadSettings( scope ); // Connect signals and slots - connect(this->modeComboBox, static_cast(&QComboBox::currentIndexChanged), - [this, spec](int index) { - this->scope->trigger.mode = mSpec->triggerModes[(unsigned)index]; - emit modeChanged(this->scope->trigger.mode); - }); - connect(this->slopeComboBox, static_cast(&QComboBox::currentIndexChanged), - [this](int index) { - this->scope->trigger.slope = (Dso::Slope)index; - emit slopeChanged(this->scope->trigger.slope); - }); - connect(this->sourceComboBox, static_cast(&QComboBox::currentIndexChanged), - [this](int index) { - bool special = false; - - if (index >= this->sourceStandardStrings.count()) { - index -= this->sourceStandardStrings.count(); - special = true; - } - - this->scope->trigger.source = (unsigned)index; - this->scope->trigger.special = special; - emit sourceChanged(special, (unsigned)index); - }); + connect( modeComboBox, static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ), [this]( int index ) { + this->scope->trigger.mode = mSpec->triggerModes[ unsigned( index ) ]; + emit modeChanged( this->scope->trigger.mode ); + } ); + connect( slopeComboBox, static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ), [this]( int index ) { + this->scope->trigger.slope = Dso::Slope( index ); + emit slopeChanged( this->scope->trigger.slope ); + } ); + connect( sourceComboBox, static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ), + [this]( unsigned index ) { + bool smooth = index >= mSpec->channels; + this->scope->trigger.smooth = smooth; + this->scope->trigger.source = index & ( mSpec->channels - 1 ); + emit sourceChanged( index & ( mSpec->channels - 1 ), smooth ); + } ); +} + +void TriggerDock::loadSettings( DsoSettingsScope *scope ) { + // Set values + setMode( scope->trigger.mode ); + setSlope( scope->trigger.slope ); + setSource( int( scope->trigger.source ), scope->trigger.smooth ); } /// \brief Don't close the dock, just hide it /// \param event The close event that should be handled. -void TriggerDock::closeEvent(QCloseEvent *event) { +void TriggerDock::closeEvent( QCloseEvent *event ) { this->hide(); event->accept(); } -void TriggerDock::setMode(Dso::TriggerMode mode) { - int index = std::find(mSpec->triggerModes.begin(), mSpec->triggerModes.end(), mode) - mSpec->triggerModes.begin(); - QSignalBlocker blocker(modeComboBox); - modeComboBox->setCurrentIndex(index); +void TriggerDock::setMode( Dso::TriggerMode mode ) { + int index = int( std::find( mSpec->triggerModes.begin(), mSpec->triggerModes.end(), mode ) - mSpec->triggerModes.begin() ); + QSignalBlocker blocker( modeComboBox ); + modeComboBox->setCurrentIndex( index ); + emit modeChanged( this->scope->trigger.mode ); } -void TriggerDock::setSlope(Dso::Slope slope) { - QSignalBlocker blocker(slopeComboBox); - slopeComboBox->setCurrentIndex((int)slope); +void TriggerDock::setSlope( Dso::Slope slope ) { + QSignalBlocker blocker( slopeComboBox ); + slopeComboBox->setCurrentIndex( int( slope ) ); } -void TriggerDock::setSource(bool special, unsigned int id) { - if ((!special && id >= (unsigned int)this->sourceStandardStrings.count()) || - (special && id >= (unsigned int)this->sourceSpecialStrings.count())) +void TriggerDock::setSource( int id, bool smooth ) { + if ( smooth ) + id += mSpec->channels; + if ( id >= this->sourceStandardStrings.count() ) return; - - int index = (int)id; - if (special) index += this->sourceStandardStrings.count(); - QSignalBlocker blocker(sourceComboBox); - sourceComboBox->setCurrentIndex(index); + QSignalBlocker blocker( sourceComboBox ); + sourceComboBox->setCurrentIndex( id ); } diff --git a/openhantek/src/docks/TriggerDock.h b/openhantek/src/docks/TriggerDock.h index e361aa16..6e969c99 100644 --- a/openhantek/src/docks/TriggerDock.h +++ b/openhantek/src/docks/TriggerDock.h @@ -2,11 +2,11 @@ #pragma once +#include +#include #include #include #include -#include -#include #include "hantekdso/enums.h" @@ -27,23 +27,28 @@ class TriggerDock : public QDockWidget { /// \param spec /// \param parent The parent widget. /// \param flags Flags for the window manager. - TriggerDock(DsoSettingsScope *scope, const Dso::ControlSpecification* mSpec, QWidget *parent, Qt::WindowFlags flags = 0); + TriggerDock( DsoSettingsScope *scope, const Dso::ControlSpecification *mSpec, QWidget *parent ); /// \brief Changes the trigger mode if the new mode is supported. /// \param mode The trigger mode. - void setMode(Dso::TriggerMode mode); + void setMode( Dso::TriggerMode mode ); /// \brief Changes the trigger source if the new source is supported. - /// \param special true for a special channel (EXT, ...) as trigger source. /// \param id The number of the channel, that should be used as trigger. - void setSource(bool special, unsigned int id); + /// \param smooth Don't trigger on glitches + void setSource( int id, bool smooth ); /// \brief Changes the trigger slope if the new slope is supported. /// \param slope The trigger slope. - void setSlope(Dso::Slope slope); + void setSlope( Dso::Slope slope ); + + public slots: + /// \brief Loads settings into GUI + /// \param scope Settings to load + void loadSettings( DsoSettingsScope *scope ); protected: - void closeEvent(QCloseEvent *event); + void closeEvent( QCloseEvent *event ); QGridLayout *dockLayout; ///< The main layout for the dock window QWidget *dockWidget; ///< The main widget for the dock window @@ -55,12 +60,12 @@ class TriggerDock : public QDockWidget { QComboBox *slopeComboBox; ///< Select the slope that causes triggering DsoSettingsScope *scope; ///< The settings provided by the parent class - const Dso::ControlSpecification* mSpec; + const Dso::ControlSpecification *mSpec; QStringList sourceStandardStrings; ///< Strings for the standard trigger sources - QStringList sourceSpecialStrings; ///< Strings for the special trigger sources + signals: - void modeChanged(Dso::TriggerMode); ///< The trigger mode has been changed - void sourceChanged(bool special, unsigned int id); ///< The trigger source has been changed - void slopeChanged(Dso::Slope); ///< The trigger slope has been changed + void modeChanged( Dso::TriggerMode ); ///< The trigger mode has been changed + void sourceChanged( unsigned int id, bool smooth ); ///< The trigger source has been changed + void slopeChanged( Dso::Slope ); ///< The trigger slope has been changed }; diff --git a/openhantek/src/docks/VoltageDock.cpp b/openhantek/src/docks/VoltageDock.cpp index 5cfb7ad0..295878f6 100644 --- a/openhantek/src/docks/VoltageDock.cpp +++ b/openhantek/src/docks/VoltageDock.cpp @@ -1,10 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ -#include #include -#include +#include #include -#include #include #include @@ -12,118 +10,186 @@ #include "VoltageDock.h" #include "dockwindows.h" -#include "settings.h" +#include "dsosettings.h" #include "sispinbox.h" #include "utils/printutils.h" -template struct SELECT { - template - static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { + +template < typename... Args > struct SELECT { + template < typename C, typename R > static constexpr auto OVERLOAD_OF( R ( C::*pmf )( Args... ) ) -> decltype( pmf ) { return pmf; } }; -VoltageDock::VoltageDock(DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent, Qt::WindowFlags flags) - : QDockWidget(tr("Voltage"), parent, flags), scope(scope), spec(spec) { - +VoltageDock::VoltageDock( DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent ) + : QDockWidget( tr( "Voltage" ), parent ), scope( scope ), spec( spec ) { // Initialize lists for comboboxes - for (Dso::Coupling c: spec->couplings) - couplingStrings.append(Dso::couplingString(c)); + for ( Dso::Coupling c : spec->couplings ) + if ( c == Dso::Coupling::DC || scope->hasACcoupling || scope->hasACmodification ) + couplingStrings.append( Dso::couplingString( c ) ); - for( auto e: Dso::MathModeEnum ) { - modeStrings.append(Dso::mathModeString(e)); + for ( auto e : Dso::MathModeEnum ) { + modeStrings.append( Dso::mathModeString( e ) ); } - for (double gainStep: scope->gainSteps) - gainStrings << valueToString(gainStep, UNIT_VOLTS, 0); + for ( double gainStep : scope->gainSteps ) { + gainStrings << valueToString( gainStep, UNIT_VOLTS, 0 ); + } dockLayout = new QGridLayout(); - dockLayout->setColumnMinimumWidth(0, 64); - dockLayout->setColumnStretch(1, 1); - + dockLayout->setColumnMinimumWidth( 0, 64 ); + dockLayout->setColumnStretch( 1, 1 ); // stretch ComboBox in 2nd (middle) column + dockLayout->setColumnStretch( 2, 1 ); // stretch ComboBox in 3rd (last) column + dockLayout->setSpacing( DOCK_LAYOUT_SPACING ); // Initialize elements - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { + int row = 0; + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { ChannelBlock b; - b.miscComboBox=(new QComboBox()); - b.gainComboBox=(new QComboBox()); - b.invertCheckBox=(new QCheckBox(tr("Invert"))); - b.usedCheckBox=(new QCheckBox(scope->voltage[channel].name)); - - channelBlocks.push_back(std::move(b)); - - if (channel < spec->channels) - b.miscComboBox->addItems(couplingStrings); + if ( channel < spec->channels ) + b.usedCheckBox = new QCheckBox( tr( "CH&%1" ).arg( channel + 1 ) ); // define shortcut 1 / 2 else - b.miscComboBox->addItems(modeStrings); - - b.gainComboBox->addItems(gainStrings); - - dockLayout->addWidget(b.usedCheckBox, (int)channel * 3, 0); - dockLayout->addWidget(b.gainComboBox, (int)channel * 3, 1); - dockLayout->addWidget(b.miscComboBox, (int)channel * 3 + 1, 1); - dockLayout->addWidget(b.invertCheckBox, (int)channel * 3 + 2, 1); - - if (channel < spec->channels) - setCoupling(channel, scope->voltage[channel].couplingOrMathIndex); + b.usedCheckBox = new QCheckBox( tr( "MA&TH" ) ); + b.miscComboBox = new QComboBox(); + b.gainComboBox = new QComboBox(); + b.invertCheckBox = new QCheckBox( tr( "Invert" ) ); + b.attnSpinBox = new QSpinBox(); + b.attnSpinBox->setMinimum( ATTENUATION_MIN ); + b.attnSpinBox->setMaximum( ATTENUATION_MAX ); + b.attnSpinBox->setPrefix( tr( "x" ) ); + + channelBlocks.push_back( std::move( b ) ); + + if ( channel < spec->channels ) + b.miscComboBox->addItems( couplingStrings ); else - setMode(scope->voltage[channel].couplingOrMathIndex); - setGain(channel, scope->voltage[channel].gainStepIndex); - setUsed(channel, scope->voltage[channel].used); - - connect(b.gainComboBox, SELECT::OVERLOAD_OF(&QComboBox::currentIndexChanged), [this,channel](int index) { - this->scope->voltage[channel].gainStepIndex = (unsigned)index; - emit gainChanged(channel, this->scope->gain(channel)); - }); - connect(b.invertCheckBox, &QAbstractButton::toggled, [this,channel](bool checked) { - this->scope->voltage[channel].inverted = checked; - }); - connect(b.miscComboBox, SELECT::OVERLOAD_OF(&QComboBox::currentIndexChanged), [this,channel,spec,scope](int index){ - this->scope->voltage[channel].couplingOrMathIndex = (unsigned)index; - if (channel < spec->channels) { - emit couplingChanged(channel, scope->coupling(channel, spec)); - } else { - emit modeChanged(Dso::getMathMode(this->scope->voltage[channel])); - } - }); - connect(b.usedCheckBox, &QAbstractButton::toggled, [this,channel](bool checked) { - this->scope->voltage[channel].used = checked; - emit usedChanged(channel, checked); - }); + b.miscComboBox->addItems( modeStrings ); + + b.gainComboBox->addItems( gainStrings ); + + dockLayout->addWidget( b.usedCheckBox, row, 0 ); + dockLayout->addWidget( b.gainComboBox, row++, 1, 1, 2 ); + dockLayout->addWidget( b.invertCheckBox, row, 0 ); + dockLayout->addWidget( b.attnSpinBox, row, 1, 1, 1 ); + dockLayout->addWidget( b.miscComboBox, row++, 2, 1, 1 ); + + // draw divider line + if ( channel < spec->channels ) { + QFrame *divider = new QFrame(); + divider->setLineWidth( 1 ); + divider->setFrameShape( QFrame::HLine ); + dockLayout->addWidget( divider, row++, 0, 1, 3 ); + } + + connect( b.gainComboBox, SELECT< int >::OVERLOAD_OF( &QComboBox::currentIndexChanged ), [this, channel]( unsigned index ) { + this->scope->voltage[ channel ].gainStepIndex = index; + emit gainChanged( channel, this->scope->gain( channel ) ); + } ); + connect( b.attnSpinBox, SELECT< int >::OVERLOAD_OF( &QSpinBox::valueChanged ), [this, channel]( unsigned attnValue ) { + this->scope->voltage[ channel ].probeAttn = attnValue; + setAttn( channel, attnValue ); + emit probeAttnChanged( channel, attnValue ); // make sure to set the probe first, since this will influence the gain + emit gainChanged( channel, this->scope->gain( channel ) ); + } ); + connect( b.invertCheckBox, &QAbstractButton::toggled, [this, channel]( bool checked ) { + this->scope->voltage[ channel ].inverted = checked; + emit invertedChanged( channel, checked ); + } ); + connect( b.miscComboBox, SELECT< int >::OVERLOAD_OF( &QComboBox::currentIndexChanged ), + [this, channel, spec, scope]( unsigned index ) { + this->scope->voltage[ channel ].couplingOrMathIndex = index; + if ( channel < spec->channels ) { + // setCoupling(channel, (unsigned)index); + emit couplingChanged( channel, scope->coupling( channel, spec ) ); + } else { + emit modeChanged( Dso::getMathMode( this->scope->voltage[ channel ] ) ); + } + } ); + connect( b.usedCheckBox, &QAbstractButton::toggled, [this, channel]( bool checked ) { + this->scope->voltage[ channel ].used = checked; + emit usedChanged( channel, checked ); + } ); } + // Load settings into GUI + this->loadSettings( scope, spec ); + dockWidget = new QWidget(); - SetupDockWidget(this, dockWidget, dockLayout); + SetupDockWidget( this, dockWidget, dockLayout ); +} + +void VoltageDock::loadSettings( DsoSettingsScope *scope, const Dso::ControlSpecification *spec ) { + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + if ( channel < spec->channels ) { + if ( int( scope->voltage[ channel ].couplingOrMathIndex ) < couplingStrings.size() ) + setCoupling( channel, scope->voltage[ channel ].couplingOrMathIndex ); + } else { + setMode( scope->voltage[ channel ].couplingOrMathIndex ); + } + + setGain( channel, scope->voltage[ channel ].gainStepIndex ); + setUsed( channel, scope->voltage[ channel ].used ); + setAttn( channel, scope->voltage[ channel ].probeAttn ); + setInverted( channel, scope->voltage[ channel ].inverted ); + } } /// \brief Don't close the dock, just hide it /// \param event The close event that should be handled. -void VoltageDock::closeEvent(QCloseEvent *event) { +void VoltageDock::closeEvent( QCloseEvent *event ) { hide(); event->accept(); } -void VoltageDock::setCoupling(ChannelID channel, unsigned couplingIndex) { - if (channel >= spec->channels) return; - if (couplingIndex >= spec->couplings.size()) return; - QSignalBlocker blocker(channelBlocks[channel].miscComboBox); - channelBlocks[channel].miscComboBox->setCurrentIndex((int)couplingIndex); +void VoltageDock::setCoupling( ChannelID channel, unsigned couplingIndex ) { + if ( channel >= spec->channels ) + return; + if ( couplingIndex >= spec->couplings.size() ) + return; + QSignalBlocker blocker( channelBlocks[ channel ].miscComboBox ); + channelBlocks[ channel ].miscComboBox->setCurrentIndex( int( couplingIndex ) ); +} + +void VoltageDock::setGain( ChannelID channel, unsigned gainStepIndex ) { + if ( channel >= scope->voltage.size() ) + return; + if ( gainStepIndex >= scope->gainSteps.size() ) + return; + QSignalBlocker blocker( channelBlocks[ channel ].gainComboBox ); + channelBlocks[ channel ].gainComboBox->setCurrentIndex( int( gainStepIndex ) ); +} + +void VoltageDock::setAttn( ChannelID channel, double attnValue ) { + if ( channel >= scope->voltage.size() ) + return; + QSignalBlocker blocker( channelBlocks[ channel ].gainComboBox ); + int index = channelBlocks[ channel ].gainComboBox->currentIndex(); + gainStrings.clear(); + for ( double gainStep : scope->gainSteps ) { + gainStrings << valueToString( gainStep * attnValue, UNIT_VOLTS, -1 ); // auto format + } + channelBlocks[ channel ].gainComboBox->clear(); + channelBlocks[ channel ].gainComboBox->addItems( gainStrings ); + channelBlocks[ channel ].gainComboBox->setCurrentIndex( index ); + scope->voltage[ channel ].probeAttn = attnValue; + channelBlocks[ channel ].attnSpinBox->setValue( int( attnValue ) ); } -void VoltageDock::setGain(ChannelID channel, unsigned gainStepIndex) { - if (channel >= scope->voltage.size()) return; - if (gainStepIndex >= scope->gainSteps.size()) return; - QSignalBlocker blocker(channelBlocks[channel].gainComboBox); - channelBlocks[channel].gainComboBox->setCurrentIndex((unsigned)gainStepIndex); +void VoltageDock::setMode( unsigned mathModeIndex ) { + QSignalBlocker blocker( channelBlocks[ spec->channels ].miscComboBox ); + channelBlocks[ spec->channels ].miscComboBox->setCurrentIndex( int( mathModeIndex ) ); } -void VoltageDock::setMode(unsigned mathModeIndex) { - QSignalBlocker blocker(channelBlocks[spec->channels].miscComboBox); - channelBlocks[spec->channels].miscComboBox->setCurrentIndex((int)mathModeIndex); +void VoltageDock::setUsed( ChannelID channel, bool used ) { + if ( channel >= scope->voltage.size() ) + return; + QSignalBlocker blocker( channelBlocks[ channel ].usedCheckBox ); + channelBlocks[ channel ].usedCheckBox->setChecked( used ); } -void VoltageDock::setUsed(ChannelID channel, bool used) { - if (channel >= scope->voltage.size()) return; - QSignalBlocker blocker(channelBlocks[channel].usedCheckBox); - channelBlocks[channel].usedCheckBox->setChecked(used); +void VoltageDock::setInverted( ChannelID channel, bool inverted ) { + if ( channel >= scope->voltage.size() ) + return; + QSignalBlocker blocker( channelBlocks[ channel ].invertCheckBox ); + channelBlocks[ channel ].invertCheckBox->setChecked( inverted ); } diff --git a/openhantek/src/docks/VoltageDock.h b/openhantek/src/docks/VoltageDock.h index 260a84b7..430204f3 100644 --- a/openhantek/src/docks/VoltageDock.h +++ b/openhantek/src/docks/VoltageDock.h @@ -2,15 +2,19 @@ #pragma once -#include -#include #include #include +#include +#include #include +#include -#include "scopesettings.h" #include "hantekdso/controlspecification.h" #include "post/postprocessingsettings.h" +#include "scopesettings.h" + +#define ATTENUATION_MIN 1 ///< Minimum probe attenuation +#define ATTENUATION_MAX 1000 ///< Maximum probe attenuation class SiSpinBox; @@ -25,41 +29,58 @@ class VoltageDock : public QDockWidget { /// \param settings The target settings object. /// \param parent The parent widget. /// \param flags Flags for the window manager. - VoltageDock(DsoSettingsScope *scope, const Dso::ControlSpecification* spec, QWidget *parent, Qt::WindowFlags flags = 0); + VoltageDock( DsoSettingsScope *scope, const Dso::ControlSpecification *spec, QWidget *parent ); /// \brief Sets the coupling for a channel. /// \param channel The channel, whose coupling should be set. /// \param couplingIndex The coupling-mode index. - void setCoupling(ChannelID channel, unsigned couplingIndex); + void setCoupling( ChannelID channel, unsigned couplingIndex ); /// \brief Sets the gain for a channel. /// \param channel The channel, whose gain should be set. /// \param gain The gain in volts. - void setGain(ChannelID channel, unsigned gainStepIndex); + void setGain( ChannelID channel, unsigned gainStepIndex ); + + /// \brief Sets the probe attenuation for a channel. + /// \param channel The channel, whose attn should be set. + /// \param attn The attn value. + void setAttn( ChannelID channel, double attnValue ); /// \brief Sets the mode for the math channel. /// \param mathModeIndex The math-mode index. - void setMode(unsigned mathModeIndex); + void setMode( unsigned mathModeIndex ); /// \brief Enables/disables a channel. /// \param channel The channel, that should be enabled/disabled. /// \param used True if the channel should be enabled, false otherwise. - void setUsed(ChannelID channel, bool used); + void setUsed( ChannelID channel, bool used ); + + /// \brief Set channel inverted. + /// \param channel The channel, that should be inverted. + /// \param used True if the channel should be inverted, false otherwise. + void setInverted( ChannelID channel, bool inverted ); + + public slots: + /// \brief Loads settings into GUI + /// \param scope Settings to load + /// \param spec Current scope specifications + void loadSettings( DsoSettingsScope *scope, const Dso::ControlSpecification *spec ); protected: - void closeEvent(QCloseEvent *event); + void closeEvent( QCloseEvent *event ); - QGridLayout *dockLayout; ///< The main layout for the dock window - QWidget *dockWidget; ///< The main widget for the dock window + QGridLayout *dockLayout; ///< The main layout for the dock window + QWidget *dockWidget; ///< The main widget for the dock window struct ChannelBlock { - QCheckBox * usedCheckBox; ///< Enable/disable a specific channel - QComboBox * gainComboBox; ///< Select the vertical gain for the channels - QComboBox * miscComboBox; ///< Select coupling for real and mode for math channels - QCheckBox * invertCheckBox; ///< Select if the channels should be displayed inverted + QCheckBox *usedCheckBox; ///< Enable/disable a specific channel + QComboBox *gainComboBox; ///< Select the vertical gain for the channels + QComboBox *miscComboBox; ///< Select coupling for real and mode for math channels + QCheckBox *invertCheckBox; ///< Select if the channels should be displayed inverted + QSpinBox *attnSpinBox; ///< Enter the attenuation probe value }; - std::vector channelBlocks; + std::vector< ChannelBlock > channelBlocks; DsoSettingsScope *scope; ///< The settings provided by the parent class const Dso::ControlSpecification *spec; @@ -67,10 +88,13 @@ class VoltageDock : public QDockWidget { QStringList couplingStrings; ///< The strings for the couplings QStringList modeStrings; ///< The strings for the math mode QStringList gainStrings; ///< String representations for the gain steps + QStringList attnStrings; ///< String representations for the probe attn steps signals: - void couplingChanged(ChannelID channel, Dso::Coupling coupling); ///< A coupling has been selected - void gainChanged(ChannelID channel, double gain); ///< A gain has been selected - void modeChanged(Dso::MathMode mode); ///< The mode for the math channels has been changed - void usedChanged(ChannelID channel, bool used); ///< A channel has been enabled/disabled + void couplingChanged( ChannelID channel, Dso::Coupling coupling ); ///< A coupling has been selected + void gainChanged( ChannelID channel, double gain ); ///< A gain has been selected + void modeChanged( Dso::MathMode mode ); ///< The mode for the math channels has been changed + void usedChanged( ChannelID channel, bool used ); ///< A channel has been enabled/disabled + void probeAttnChanged( ChannelID channel, double probeAttn ); ///< A channel probe attenuation has been changed + void invertedChanged( ChannelID channel, bool inverted ); ///< A channel "inverted" has been toggled }; diff --git a/openhantek/src/docks/dockconstants.h b/openhantek/src/docks/dockconstants.h new file mode 100644 index 00000000..9e49a7e0 --- /dev/null +++ b/openhantek/src/docks/dockconstants.h @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +// spacing between the individual entries of the docks +const int DOCK_LAYOUT_SPACING = 1; diff --git a/openhantek/src/docks/dockwindows.cpp b/openhantek/src/docks/dockwindows.cpp index 37fff337..3f127b3c 100644 --- a/openhantek/src/docks/dockwindows.cpp +++ b/openhantek/src/docks/dockwindows.cpp @@ -8,29 +8,32 @@ #include -#include "post/postprocessingsettings.h" +#include "dockwindows.h" #include "hantekdso/enums.h" #include "hantekprotocol/types.h" -#include "dockwindows.h" +#include "post/postprocessingsettings.h" + -void SetupDockWidget(QDockWidget *dockWindow, QWidget *dockWidget, QLayout *layout) { - dockWindow->setObjectName(dockWindow->windowTitle()); - dockWindow->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); - dockWidget->setLayout(layout); - dockWidget->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::DefaultType)); - dockWindow->setWidget(dockWidget); +void SetupDockWidget( QDockWidget *dockWindow, QWidget *dockWidget, QLayout *layout ) { + dockWindow->setObjectName( dockWindow->windowTitle() ); + dockWindow->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); + dockWindow->setFeatures( QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable ); // do not close + dockWidget->setLayout( layout ); + dockWidget->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::DefaultType ) ); + dockWindow->setWidget( dockWidget ); } + void registerDockMetaTypes() { - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType >(); - qRegisterMetaType >(); - qRegisterMetaType("ChannelID"); + qRegisterMetaType< Dso::TriggerMode >(); + qRegisterMetaType< Dso::MathMode >(); + qRegisterMetaType< Dso::Slope >(); + qRegisterMetaType< Dso::Coupling >(); + qRegisterMetaType< Dso::GraphFormat >(); + qRegisterMetaType< Dso::ChannelMode >(); + qRegisterMetaType< Dso::WindowFunction >(); + qRegisterMetaType< Dso::InterpolationMode >(); + qRegisterMetaType< std::vector< unsigned > >(); + qRegisterMetaType< std::vector< double > >(); + qRegisterMetaType< ChannelID >( "ChannelID" ); } diff --git a/openhantek/src/docks/dockwindows.h b/openhantek/src/docks/dockwindows.h index 3a479b44..19800f9a 100644 --- a/openhantek/src/docks/dockwindows.h +++ b/openhantek/src/docks/dockwindows.h @@ -5,5 +5,9 @@ #include #include +#include "dockconstants.h" +#include "viewconstants.h" + + void registerDockMetaTypes(); -void SetupDockWidget(QDockWidget *dockWindow, QWidget *dockWidget, QLayout *layout); +void SetupDockWidget( QDockWidget *dockWindow, QWidget *dockWidget, QLayout *layout ); diff --git a/openhantek/src/dsosettings.cpp b/openhantek/src/dsosettings.cpp new file mode 100644 index 00000000..54e0f1a2 --- /dev/null +++ b/openhantek/src/dsosettings.cpp @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +// #include +#include + +#include "dsosettings.h" +#include "dsowidget.h" + +/// \brief Set the number of channels. +/// \param channels The new channel count, that will be applied to lists. +DsoSettings::DsoSettings( const Dso::ControlSpecification *deviceSpecification ) : deviceSpecification( deviceSpecification ) { + // Add new channels to the list + unsigned char trace_hue[] = {60, 240, 0, 120}; // yellow, blue, red, green + unsigned index = 0; + scope.hasACcoupling = deviceSpecification->hasACcoupling; + while ( scope.spectrum.size() < deviceSpecification->channels ) { + // Spectrum + DsoSettingsScopeSpectrum newSpectrum; + newSpectrum.name = tr( "SP%1" ).arg( index + 1 ); + scope.spectrum.push_back( newSpectrum ); + + // Voltage + DsoSettingsScopeVoltage newVoltage; + newVoltage.name = tr( "CH%1" ).arg( index + 1 ); + scope.voltage.push_back( newVoltage ); + + view.screen.voltage.push_back( QColor::fromHsv( trace_hue[ index ], 0xff, 0xff ) ); + view.screen.spectrum.push_back( QColor::fromHsv( trace_hue[ index ] - 30, 0xff, 0xff ) ); + view.print.voltage.push_back( view.screen.voltage.back().darker() ); + view.print.spectrum.push_back( view.screen.spectrum.back().darker() ); + if ( ++index >= sizeof trace_hue ) + index = 0; + } + + DsoSettingsScopeSpectrum newSpectrum; + newSpectrum.name = tr( "SPM" ); + scope.spectrum.push_back( newSpectrum ); + + DsoSettingsScopeVoltage newVoltage; + newVoltage.couplingOrMathIndex = unsigned( Dso::MathMode::ADD_CH1_CH2 ); + newVoltage.name = tr( "MATH" ); + scope.voltage.push_back( newVoltage ); + + view.screen.voltage.push_back( QColor::fromHsv( 300, 0xff, 0xff ) ); // purple + view.screen.spectrum.push_back( QColor::fromHsv( 320, 0xff, 0xff ) ); + view.print.voltage.push_back( QColor::fromHsv( 300, 0xff, 0xff ) ); + view.print.spectrum.push_back( QColor::fromHsv( 320, 0xff, 0xff ) ); + + load(); +} + + +bool DsoSettings::setFilename( const QString &filename ) { + std::unique_ptr< QSettings > local = std::unique_ptr< QSettings >( new QSettings( filename, QSettings::IniFormat ) ); + if ( local->status() != QSettings::NoError ) { + qWarning() << "Could not change the settings file to " << filename; + return false; + } + storeSettings.swap( local ); + return true; +} + + +void DsoSettings::load() { + // Start with default configuration? + if ( storeSettings->value( "configuration/version", 0 ).toUInt() < CONFIG_VERSION ) { + // incompatible change or config reset by user + storeSettings->clear(); // start with a clean config storage + setDefaultConfig(); + return; + } + + alwaysSave = storeSettings->value( "configuration/alwaysSave", alwaysSave ).toBool(); + + // Oscilloscope settings + storeSettings->beginGroup( "scope" ); + // Horizontal axis + storeSettings->beginGroup( "horizontal" ); + if ( storeSettings->contains( "format" ) ) + scope.horizontal.format = Dso::GraphFormat( storeSettings->value( "format" ).toInt() ); + if ( storeSettings->contains( "frequencybase" ) ) + scope.horizontal.frequencybase = storeSettings->value( "frequencybase" ).toDouble(); + for ( int marker = 0; marker < 2; ++marker ) { + QString name; + name = QString( "marker%1" ).arg( marker ); + if ( storeSettings->contains( name ) ) + scope.setMarker( unsigned( marker ), storeSettings->value( name ).toDouble() ); + } + if ( storeSettings->contains( "timebase" ) ) + scope.horizontal.timebase = storeSettings->value( "timebase" ).toDouble(); + if ( storeSettings->contains( "maxTimebase" ) ) + scope.horizontal.maxTimebase = storeSettings->value( "maxTimebase" ).toDouble(); + if ( storeSettings->contains( "acquireInterval" ) ) + scope.horizontal.acquireInterval = storeSettings->value( "acquireInterval" ).toDouble(); + if ( storeSettings->contains( "recordLength" ) ) + scope.horizontal.recordLength = storeSettings->value( "recordLength" ).toUInt(); + if ( storeSettings->contains( "samplerate" ) ) + scope.horizontal.samplerate = storeSettings->value( "samplerate" ).toDouble(); + if ( storeSettings->contains( "calfreq" ) ) + scope.horizontal.calfreq = storeSettings->value( "calfreq" ).toDouble(); + storeSettings->endGroup(); // horizontal + // Trigger + storeSettings->beginGroup( "trigger" ); + if ( storeSettings->contains( "mode" ) ) + scope.trigger.mode = Dso::TriggerMode( storeSettings->value( "mode" ).toUInt() ); + if ( storeSettings->contains( "position" ) ) + scope.trigger.offset = storeSettings->value( "position" ).toDouble(); + if ( storeSettings->contains( "slope" ) ) + scope.trigger.slope = Dso::Slope( storeSettings->value( "slope" ).toUInt() ); + if ( storeSettings->contains( "source" ) ) + scope.trigger.source = storeSettings->value( "source" ).toUInt(); + if ( storeSettings->contains( "smooth" ) ) + scope.trigger.smooth = storeSettings->value( "smooth" ).toBool(); + storeSettings->endGroup(); // trigger + // Spectrum + for ( ChannelID channel = 0; channel < scope.spectrum.size(); ++channel ) { + storeSettings->beginGroup( QString( "spectrum%1" ).arg( channel ) ); + if ( storeSettings->contains( "magnitude" ) ) + scope.spectrum[ channel ].magnitude = storeSettings->value( "magnitude" ).toDouble(); + if ( storeSettings->contains( "offset" ) ) + scope.spectrum[ channel ].offset = storeSettings->value( "offset" ).toDouble(); + if ( storeSettings->contains( "used" ) ) + scope.spectrum[ channel ].used = storeSettings->value( "used" ).toBool(); + storeSettings->beginGroup( "cursor" ); + if ( storeSettings->contains( "shape" ) ) + scope.spectrum[ channel ].cursor.shape = + DsoSettingsScopeCursor::CursorShape( storeSettings->value( "shape" ).toUInt() ); + for ( int marker = 0; marker < 2; ++marker ) { + QString name; + name = QString( "x%1" ).arg( marker ); + if ( storeSettings->contains( name ) ) + scope.spectrum[ channel ].cursor.pos[ marker ].setX( storeSettings->value( name ).toDouble() ); + name = QString( "y%1" ).arg( marker ); + if ( storeSettings->contains( name ) ) + scope.spectrum[ channel ].cursor.pos[ marker ].setY( storeSettings->value( name ).toDouble() ); + } + storeSettings->endGroup(); // cursor + storeSettings->endGroup(); // spectrum%1 + } + // Voltage + bool defaultConfig = deviceSpecification->isDemoDevice; // use default channel setting in demo mode + if ( storeSettings->contains( "hasACmodification" ) ) + scope.hasACmodification = storeSettings->value( "hasACmodification" ).toBool(); + for ( ChannelID channel = 0; channel < scope.voltage.size(); ++channel ) { + storeSettings->beginGroup( QString( "voltage%1" ).arg( channel ) ); + if ( storeSettings->contains( "gainStepIndex" ) ) + scope.voltage[ channel ].gainStepIndex = storeSettings->value( "gainStepIndex" ).toUInt(); + if ( storeSettings->contains( "couplingOrMathIndex" ) ) { + scope.voltage[ channel ].couplingOrMathIndex = storeSettings->value( "couplingOrMathIndex" ).toUInt(); + if ( channel < deviceSpecification->channels ) + if ( scope.voltage[ channel ].couplingOrMathIndex >= deviceSpecification->couplings.size() || + ( !scope.hasACcoupling && !scope.hasACmodification ) ) + scope.voltage[ channel ].couplingOrMathIndex = 0; // set to default if out of range + } + if ( storeSettings->contains( "inverted" ) ) + scope.voltage[ channel ].inverted = storeSettings->value( "inverted" ).toBool(); + if ( storeSettings->contains( "offset" ) ) + scope.voltage[ channel ].offset = storeSettings->value( "offset" ).toDouble(); + if ( storeSettings->contains( "trigger" ) ) + scope.voltage[ channel ].trigger = storeSettings->value( "trigger" ).toDouble(); + if ( storeSettings->contains( "probeAttn" ) ) + scope.voltage[ channel ].probeAttn = storeSettings->value( "probeAttn" ).toDouble(); + if ( storeSettings->contains( "used" ) ) + scope.voltage[ channel ].used = storeSettings->value( "used" ).toBool(); + else // no config file found, e.g. 1st run + defaultConfig = true; // start with default config + + if ( defaultConfig ) { // useful default: show both voltage channels + setDefaultConfig(); + } + + storeSettings->beginGroup( "cursor" ); + if ( storeSettings->contains( "shape" ) ) + scope.voltage[ channel ].cursor.shape = DsoSettingsScopeCursor::CursorShape( storeSettings->value( "shape" ).toUInt() ); + for ( int marker = 0; marker < 2; ++marker ) { + QString name; + name = QString( "x%1" ).arg( marker ); + if ( storeSettings->contains( name ) ) + scope.voltage[ channel ].cursor.pos[ marker ].setX( storeSettings->value( name ).toDouble() ); + name = QString( "y%1" ).arg( marker ); + if ( storeSettings->contains( name ) ) + scope.voltage[ channel ].cursor.pos[ marker ].setY( storeSettings->value( name ).toDouble() ); + } + storeSettings->endGroup(); // cursor + storeSettings->endGroup(); // voltage%1 + } + + // Post processing + if ( storeSettings->contains( "spectrumLimit" ) ) + post.spectrumLimit = storeSettings->value( "spectrumLimit" ).toDouble(); + if ( storeSettings->contains( "spectrumReference" ) ) + post.spectrumReference = storeSettings->value( "spectrumReference" ).toDouble(); + if ( storeSettings->contains( "spectrumWindow" ) ) + post.spectrumWindow = Dso::WindowFunction( storeSettings->value( "spectrumWindow" ).toInt() ); + storeSettings->endGroup(); // scope + + // View + storeSettings->beginGroup( "view" ); + // Colors + storeSettings->beginGroup( "color" ); + DsoSettingsColorValues *colors; + for ( int mode = 0; mode < 2; ++mode ) { + if ( mode == 0 ) { + colors = &view.screen; + storeSettings->beginGroup( "screen" ); + } else { + colors = &view.print; + storeSettings->beginGroup( "print" ); + } + + if ( storeSettings->contains( "axes" ) ) + colors->axes = storeSettings->value( "axes" ).value< QColor >(); + if ( storeSettings->contains( "background" ) ) + colors->background = storeSettings->value( "background" ).value< QColor >(); + if ( storeSettings->contains( "border" ) ) + colors->border = storeSettings->value( "border" ).value< QColor >(); + if ( storeSettings->contains( "grid" ) ) + colors->grid = storeSettings->value( "grid" ).value< QColor >(); + if ( storeSettings->contains( "markers" ) ) + colors->markers = storeSettings->value( "markers" ).value< QColor >(); + for ( ChannelID channel = 0; channel < scope.spectrum.size(); ++channel ) { + QString key = QString( "spectrum%1" ).arg( channel ); + if ( storeSettings->contains( key ) ) + colors->spectrum[ channel ] = storeSettings->value( key ).value< QColor >(); + } + if ( storeSettings->contains( "text" ) ) + colors->text = storeSettings->value( "text" ).value< QColor >(); + for ( ChannelID channel = 0; channel < scope.voltage.size(); ++channel ) { + QString key = QString( "voltage%1" ).arg( channel ); + if ( storeSettings->contains( key ) ) + colors->voltage[ channel ] = storeSettings->value( key ).value< QColor >(); + } + storeSettings->endGroup(); // screen / print + } + storeSettings->endGroup(); // color + // Other view settings + if ( storeSettings->contains( "histogram" ) ) + scope.histogram = storeSettings->value( "histogram" ).toBool(); + if ( storeSettings->contains( "digitalPhosphor" ) ) + view.digitalPhosphor = storeSettings->value( "digitalPhosphor" ).toBool(); + if ( storeSettings->contains( "interpolation" ) ) + view.interpolation = Dso::InterpolationMode( storeSettings->value( "interpolation" ).toInt() ); + if ( storeSettings->contains( "printerColorImages" ) ) + view.printerColorImages = storeSettings->value( "printerColorImages" ).toBool(); + if ( storeSettings->contains( "zoom" ) ) + view.zoom = storeSettings->value( "zoom" ).toBool(); + if ( storeSettings->contains( "cursorGridPosition" ) ) + view.cursorGridPosition = Qt::ToolBarArea( storeSettings->value( "cursorGridPosition" ).toUInt() ); + if ( storeSettings->contains( "cursorsVisible" ) ) + view.cursorsVisible = storeSettings->value( "cursorsVisible" ).toBool(); + storeSettings->endGroup(); // view + + storeSettings->beginGroup( "window" ); + mainWindowGeometry = storeSettings->value( "geometry" ).toByteArray(); + mainWindowState = storeSettings->value( "state" ).toByteArray(); + storeSettings->endGroup(); // window +} + + +void DsoSettings::save() { + // Use default configuration after restart? + if ( 0 == configVersion ) { + storeSettings->clear(); + return; + } + storeSettings->beginGroup( "configuration" ); + storeSettings->setValue( "version", configVersion ); + storeSettings->setValue( "alwaysSave", alwaysSave ); + storeSettings->endGroup(); // configuration + + // Oszilloskope settings + storeSettings->beginGroup( "scope" ); + // Horizontal axis + storeSettings->beginGroup( "horizontal" ); + storeSettings->setValue( "format", scope.horizontal.format ); + storeSettings->setValue( "frequencybase", scope.horizontal.frequencybase ); + for ( int marker = 0; marker < 2; ++marker ) + storeSettings->setValue( QString( "marker%1" ).arg( marker ), scope.getMarker( unsigned( marker ) ) ); + storeSettings->setValue( "timebase", scope.horizontal.timebase ); + storeSettings->setValue( "maxTimebase", scope.horizontal.maxTimebase ); + storeSettings->setValue( "acquireInterval", scope.horizontal.acquireInterval ); + storeSettings->setValue( "recordLength", scope.horizontal.recordLength ); + storeSettings->setValue( "samplerate", scope.horizontal.samplerate ); + storeSettings->setValue( "calfreq", scope.horizontal.calfreq ); + storeSettings->endGroup(); // horizontal + // Trigger + storeSettings->beginGroup( "trigger" ); + storeSettings->setValue( "mode", unsigned( scope.trigger.mode ) ); + storeSettings->setValue( "position", scope.trigger.offset ); + storeSettings->setValue( "slope", unsigned( scope.trigger.slope ) ); + storeSettings->setValue( "source", scope.trigger.source ); + storeSettings->setValue( "smooth", scope.trigger.smooth ); + storeSettings->endGroup(); // trigger + // Spectrum + for ( ChannelID channel = 0; channel < scope.spectrum.size(); ++channel ) { + storeSettings->beginGroup( QString( "spectrum%1" ).arg( channel ) ); + storeSettings->setValue( "magnitude", scope.spectrum[ channel ].magnitude ); + storeSettings->setValue( "offset", scope.spectrum[ channel ].offset ); + storeSettings->setValue( "used", scope.spectrum[ channel ].used ); + storeSettings->beginGroup( "cursor" ); + storeSettings->setValue( "shape", scope.spectrum[ channel ].cursor.shape ); + for ( int marker = 0; marker < 2; ++marker ) { + QString name; + name = QString( "x%1" ).arg( marker ); + storeSettings->setValue( name, scope.spectrum[ channel ].cursor.pos[ marker ].x() ); + name = QString( "y%1" ).arg( marker ); + storeSettings->setValue( name, scope.spectrum[ channel ].cursor.pos[ marker ].y() ); + } + storeSettings->endGroup(); // cursor + storeSettings->endGroup(); // spectrum%1 + } + // Voltage + storeSettings->setValue( "hasACmodification", scope.hasACmodification ); + for ( ChannelID channel = 0; channel < scope.voltage.size(); ++channel ) { + storeSettings->beginGroup( QString( "voltage%1" ).arg( channel ) ); + storeSettings->setValue( "gainStepIndex", scope.voltage[ channel ].gainStepIndex ); + storeSettings->setValue( "couplingOrMathIndex", scope.voltage[ channel ].couplingOrMathIndex ); + storeSettings->setValue( "inverted", scope.voltage[ channel ].inverted ); + storeSettings->setValue( "offset", scope.voltage[ channel ].offset ); + storeSettings->setValue( "trigger", scope.voltage[ channel ].trigger ); + storeSettings->setValue( "used", scope.voltage[ channel ].used ); + storeSettings->setValue( "probeAttn", scope.voltage[ channel ].probeAttn ); + storeSettings->beginGroup( "cursor" ); + storeSettings->setValue( "shape", scope.voltage[ channel ].cursor.shape ); + for ( int marker = 0; marker < 2; ++marker ) { + QString name; + name = QString( "x%1" ).arg( marker ); + storeSettings->setValue( name, scope.voltage[ channel ].cursor.pos[ marker ].x() ); + name = QString( "y%1" ).arg( marker ); + storeSettings->setValue( name, scope.voltage[ channel ].cursor.pos[ marker ].y() ); + } + storeSettings->endGroup(); // cursor + storeSettings->endGroup(); // voltage%1 + } + + // Post processing + storeSettings->setValue( "spectrumLimit", post.spectrumLimit ); + storeSettings->setValue( "spectrumReference", post.spectrumReference ); + storeSettings->setValue( "spectrumWindow", unsigned( post.spectrumWindow ) ); + storeSettings->endGroup(); // scope + + // View + storeSettings->beginGroup( "view" ); + // Colors + + storeSettings->beginGroup( "color" ); + DsoSettingsColorValues *colors; + for ( int mode = 0; mode < 2; ++mode ) { + if ( mode == 0 ) { + colors = &view.screen; + storeSettings->beginGroup( "screen" ); + } else { + colors = &view.print; + storeSettings->beginGroup( "print" ); + } + + storeSettings->setValue( "axes", colors->axes.name( QColor::HexArgb ) ); + storeSettings->setValue( "background", colors->background.name( QColor::HexArgb ) ); + storeSettings->setValue( "border", colors->border.name( QColor::HexArgb ) ); + storeSettings->setValue( "grid", colors->grid.name( QColor::HexArgb ) ); + storeSettings->setValue( "markers", colors->markers.name( QColor::HexArgb ) ); + for ( ChannelID channel = 0; channel < scope.spectrum.size(); ++channel ) + storeSettings->setValue( QString( "spectrum%1" ).arg( channel ), colors->spectrum[ channel ].name( QColor::HexArgb ) ); + storeSettings->setValue( "text", colors->text.name( QColor::HexArgb ) ); + for ( ChannelID channel = 0; channel < scope.voltage.size(); ++channel ) + storeSettings->setValue( QString( "voltage%1" ).arg( channel ), colors->voltage[ channel ].name( QColor::HexArgb ) ); + storeSettings->endGroup(); // screen / print + } + storeSettings->endGroup(); // color + + // Other view settings + storeSettings->setValue( "histogram", scope.histogram ); + storeSettings->setValue( "digitalPhosphor", view.digitalPhosphor ); + storeSettings->setValue( "interpolation", view.interpolation ); + storeSettings->setValue( "printerColorImages", view.printerColorImages ); + storeSettings->setValue( "zoom", view.zoom ); + storeSettings->setValue( "cursorGridPosition", view.cursorGridPosition ); + storeSettings->setValue( "cursorsVisible", view.cursorsVisible ); + storeSettings->endGroup(); // view + + storeSettings->beginGroup( "window" ); + storeSettings->setValue( "geometry", mainWindowGeometry ); + storeSettings->setValue( "state", mainWindowState ); + storeSettings->endGroup(); // window +} + + +void DsoSettings::setDefaultConfig() { + scope.voltage[ 0 ].used = true; + scope.voltage[ 0 ].offset = MARGIN_TOP / 2; // mid of upper screen half + scope.voltage[ 1 ].used = true; + scope.voltage[ 1 ].offset = MARGIN_BOTTOM / 2; // mid of lower screen half +} diff --git a/openhantek/src/dsosettings.h b/openhantek/src/dsosettings.h new file mode 100644 index 00000000..85a01d1f --- /dev/null +++ b/openhantek/src/dsosettings.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ + +// increment this value after incompatible config changes +#define CONFIG_VERSION 1 + +#pragma once + +#include +#include +#include +#include +#include + +//#include "exporting/exportsettings.h" +#include "post/postprocessingsettings.h" +#include "scopesettings.h" +#include "viewsettings.h" + +/// \brief Holds the settings of the program. +class DsoSettings { + Q_DECLARE_TR_FUNCTIONS( DsoSettings ) + + public: + explicit DsoSettings( const Dso::ControlSpecification *deviceSpecification ); + bool setFilename( const QString &filename ); + + DsoSettingsScope scope; ///< All oscilloscope related settings + DsoSettingsView view; ///< All view related settings + DsoSettingsPostProcessing post; ///< All post processing related settings + bool exportProcessedSamples = true; ///< General options of the program + bool alwaysSave = true; ///< Always save the settings on exit + unsigned configVersion = CONFIG_VERSION; ///< Handle incompatible changes + + QByteArray mainWindowGeometry; ///< Geometry of the main window + QByteArray mainWindowState; ///< State of docking windows and toolbars + + /// \brief Read the settings from the last session or another file. + void load(); + + /// \brief Save the settings to the harddisk. + void save(); + + private: + std::unique_ptr< QSettings > storeSettings = std::unique_ptr< QSettings >( new QSettings ); + const Dso::ControlSpecification *deviceSpecification; + void setDefaultConfig(); +}; diff --git a/openhantek/src/dsowidget.cpp b/openhantek/src/dsowidget.cpp index f8c43fc0..4d9ec116 100644 --- a/openhantek/src/dsowidget.cpp +++ b/openhantek/src/dsowidget.cpp @@ -11,8 +11,8 @@ #include "dsowidget.h" -#include "post/postprocessingsettings.h" #include "post/graphgenerator.h" +#include "post/postprocessingsettings.h" #include "post/ppresult.h" #include "utils/printutils.h" @@ -20,751 +20,1035 @@ #include "glscope.h" #include "scopesettings.h" #include "viewconstants.h" -#include "viewsettings.h" -#include "widgets/levelslider.h" #include "widgets/datagrid.h" +#include "widgets/levelslider.h" static int zoomScopeRow = 0; -DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso::ControlSpecification *spec, - QWidget *parent, Qt::WindowFlags flags) - : QWidget(parent, flags), scope(scope), view(view), spec(spec), mainScope(GlScope::createNormal(scope, view)), - zoomScope(GlScope::createZoomed(scope, view)) { + +DsoWidget::DsoWidget( DsoSettingsScope *scope, DsoSettingsView *view, const Dso::ControlSpecification *spec, QWidget *parent ) + : QWidget( parent ), scope( scope ), view( view ), spec( spec ), mainScope( GlScope::createNormal( scope, view ) ), + zoomScope( GlScope::createZoomed( scope, view ) ) { // Palette for this widget QPalette palette; - palette.setColor(QPalette::Background, view->screen.background); - palette.setColor(QPalette::WindowText, view->screen.text); - - setupSliders(mainSliders); - setupSliders(zoomSliders); - - connect(mainScope, &GlScope::markerMoved, [this](unsigned cursorIndex, unsigned marker) { - mainSliders.markerSlider->setValue(marker, this->scope->getMarker(marker)); - mainScope->updateCursor(cursorIndex); - zoomScope->updateCursor(cursorIndex); - }); - connect(zoomScope, &GlScope::markerMoved, [this](unsigned cursorIndex, unsigned marker) { - mainScope->updateCursor(cursorIndex); - zoomScope->updateCursor(cursorIndex); - }); - - // The table for the settings + palette.setColor( QPalette::Background, view->colors->background ); + palette.setColor( QPalette::WindowText, view->colors->text ); + + setupSliders( mainSliders ); + setupSliders( zoomSliders ); + + connect( mainScope, &GlScope::markerMoved, [this]( unsigned cursorIndex, unsigned marker ) { + mainSliders.markerSlider->setValue( int( marker ), this->scope->getMarker( marker ) ); + mainScope->updateCursor( cursorIndex ); + zoomScope->updateCursor( cursorIndex ); + } ); + connect( zoomScope, &GlScope::markerMoved, [this]( unsigned cursorIndex ) { + mainScope->updateCursor( cursorIndex ); + zoomScope->updateCursor( cursorIndex ); + } ); + + // The table for the settings at screen top settingsTriggerLabel = new QLabel(); - settingsTriggerLabel->setMinimumWidth(160); - settingsTriggerLabel->setIndent(5); - settingsRecordLengthLabel = new QLabel(); - settingsRecordLengthLabel->setAlignment(Qt::AlignRight); - settingsRecordLengthLabel->setPalette(palette); + settingsTriggerLabel->setMinimumWidth( 320 ); + settingsTriggerLabel->setIndent( 5 ); + settingsSamplesOnScreen = new QLabel(); + settingsSamplesOnScreen->setAlignment( Qt::AlignRight ); + settingsSamplesOnScreen->setPalette( palette ); settingsSamplerateLabel = new QLabel(); - settingsSamplerateLabel->setAlignment(Qt::AlignRight); - settingsSamplerateLabel->setPalette(palette); + settingsSamplerateLabel->setAlignment( Qt::AlignRight ); + settingsSamplerateLabel->setPalette( palette ); settingsTimebaseLabel = new QLabel(); - settingsTimebaseLabel->setAlignment(Qt::AlignRight); - settingsTimebaseLabel->setPalette(palette); + settingsTimebaseLabel->setAlignment( Qt::AlignRight ); + settingsTimebaseLabel->setPalette( palette ); settingsFrequencybaseLabel = new QLabel(); - settingsFrequencybaseLabel->setAlignment(Qt::AlignRight); - settingsFrequencybaseLabel->setPalette(palette); + settingsFrequencybaseLabel->setAlignment( Qt::AlignRight ); + settingsFrequencybaseLabel->setPalette( palette ); swTriggerStatus = new QLabel(); - swTriggerStatus->setMinimumWidth(30); - swTriggerStatus->setText(tr("TR")); - swTriggerStatus->setAlignment(Qt::AlignCenter); - swTriggerStatus->setAutoFillBackground(true); - swTriggerStatus->setVisible(false); + swTriggerStatus->setMinimumWidth( 20 ); + swTriggerStatus->setText( tr( "TR" ) ); + swTriggerStatus->setAlignment( Qt::AlignCenter ); + swTriggerStatus->setAutoFillBackground( true ); + swTriggerStatus->setVisible( false ); settingsLayout = new QHBoxLayout(); - settingsLayout->addWidget(swTriggerStatus); - settingsLayout->addWidget(settingsTriggerLabel); - settingsLayout->addWidget(settingsRecordLengthLabel, 1); - settingsLayout->addWidget(settingsSamplerateLabel, 1); - settingsLayout->addWidget(settingsTimebaseLabel, 1); - settingsLayout->addWidget(settingsFrequencybaseLabel, 1); + settingsLayout->addWidget( swTriggerStatus ); + settingsLayout->addWidget( settingsTriggerLabel ); + settingsLayout->addWidget( settingsSamplesOnScreen, 1 ); + settingsLayout->addWidget( settingsSamplerateLabel, 1 ); + settingsLayout->addWidget( settingsTimebaseLabel, 1 ); + settingsLayout->addWidget( settingsFrequencybaseLabel, 1 ); // The table for the marker details markerInfoLabel = new QLabel(); - markerInfoLabel->setMinimumWidth(160); - markerInfoLabel->setPalette(palette); + markerInfoLabel->setAlignment( Qt::AlignLeft ); + markerInfoLabel->setPalette( palette ); markerTimeLabel = new QLabel(); - markerTimeLabel->setAlignment(Qt::AlignRight); - markerTimeLabel->setPalette(palette); + markerTimeLabel->setAlignment( Qt::AlignLeft ); + markerTimeLabel->setPalette( palette ); markerFrequencyLabel = new QLabel(); - markerFrequencyLabel->setAlignment(Qt::AlignRight); - markerFrequencyLabel->setPalette(palette); + markerFrequencyLabel->setAlignment( Qt::AlignLeft ); + markerFrequencyLabel->setPalette( palette ); markerTimebaseLabel = new QLabel(); - markerTimebaseLabel->setAlignment(Qt::AlignRight); - markerTimebaseLabel->setPalette(palette); + markerTimebaseLabel->setAlignment( Qt::AlignRight ); + markerTimebaseLabel->setPalette( palette ); markerFrequencybaseLabel = new QLabel(); - markerFrequencybaseLabel->setAlignment(Qt::AlignRight); - markerFrequencybaseLabel->setPalette(palette); + markerFrequencybaseLabel->setAlignment( Qt::AlignRight ); + markerFrequencybaseLabel->setPalette( palette ); markerLayout = new QHBoxLayout(); - markerLayout->addWidget(markerInfoLabel); - markerLayout->addWidget(markerTimeLabel, 1); - markerLayout->addWidget(markerFrequencyLabel, 1); - markerLayout->addWidget(markerTimebaseLabel, 1); - markerLayout->addWidget(markerFrequencybaseLabel, 1); + markerLayout->addWidget( markerInfoLabel ); + markerLayout->addWidget( markerTimeLabel, 1 ); + markerLayout->addWidget( markerFrequencyLabel, 1 ); + markerLayout->addWidget( markerTimebaseLabel, 1 ); + markerLayout->addWidget( markerFrequencybaseLabel, 1 ); - // The table for the measurements + // The table for the measurements at screen bottom QPalette tablePalette = palette; measurementLayout = new QGridLayout(); - measurementLayout->setColumnMinimumWidth(0, 64); - measurementLayout->setColumnMinimumWidth(1, 32); - measurementLayout->setColumnStretch(2, 2); - measurementLayout->setColumnStretch(3, 2); - measurementLayout->setColumnStretch(4, 3); - measurementLayout->setColumnStretch(5, 3); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - tablePalette.setColor(QPalette::WindowText, view->screen.voltage[channel]); - measurementNameLabel.push_back(new QLabel(scope->voltage[channel].name)); - measurementNameLabel[channel]->setPalette(tablePalette); - measurementMiscLabel.push_back(new QLabel()); - measurementMiscLabel[channel]->setPalette(tablePalette); - measurementGainLabel.push_back(new QLabel()); - measurementGainLabel[channel]->setAlignment(Qt::AlignRight); - measurementGainLabel[channel]->setPalette(tablePalette); - tablePalette.setColor(QPalette::WindowText, view->screen.spectrum[channel]); - measurementMagnitudeLabel.push_back(new QLabel()); - measurementMagnitudeLabel[channel]->setAlignment(Qt::AlignRight); - measurementMagnitudeLabel[channel]->setPalette(tablePalette); - measurementAmplitudeLabel.push_back(new QLabel()); - measurementAmplitudeLabel[channel]->setAlignment(Qt::AlignRight); - measurementAmplitudeLabel[channel]->setPalette(palette); - measurementFrequencyLabel.push_back(new QLabel()); - measurementFrequencyLabel[channel]->setAlignment(Qt::AlignRight); - measurementFrequencyLabel[channel]->setPalette(palette); - setMeasurementVisible(channel); - measurementLayout->addWidget(measurementNameLabel[channel], (int)channel, 0); - measurementLayout->addWidget(measurementMiscLabel[channel], (int)channel, 1); - measurementLayout->addWidget(measurementGainLabel[channel], (int)channel, 2); - measurementLayout->addWidget(measurementMagnitudeLabel[channel], (int)channel, 3); - measurementLayout->addWidget(measurementAmplitudeLabel[channel], (int)channel, 4); - measurementLayout->addWidget(measurementFrequencyLabel[channel], (int)channel, 5); - if ((unsigned)channel < spec->channels) - updateVoltageCoupling((unsigned)channel); + int iii = 0; + measurementLayout->setColumnMinimumWidth( iii++, 80 ); + measurementLayout->setColumnMinimumWidth( iii++, 30 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + measurementLayout->setColumnStretch( iii++, 3 ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + tablePalette.setColor( QPalette::WindowText, view->colors->voltage[ channel ] ); + measurementNameLabel.push_back( new QLabel( scope->voltage[ channel ].name ) ); + measurementNameLabel[ channel ]->setAlignment( Qt::AlignCenter ); + measurementNameLabel[ channel ]->setPalette( tablePalette ); + measurementNameLabel[ channel ]->setAutoFillBackground( true ); + measurementMiscLabel.push_back( new QLabel() ); + measurementMiscLabel[ channel ]->setAlignment( Qt::AlignLeft ); + measurementMiscLabel[ channel ]->setPalette( tablePalette ); + measurementGainLabel.push_back( new QLabel() ); + measurementGainLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementGainLabel[ channel ]->setPalette( tablePalette ); + tablePalette.setColor( QPalette::WindowText, view->colors->spectrum[ channel ] ); + measurementMagnitudeLabel.push_back( new QLabel() ); + measurementMagnitudeLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementMagnitudeLabel[ channel ]->setPalette( tablePalette ); + measurementVppLabel.push_back( new QLabel() ); + measurementVppLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementVppLabel[ channel ]->setPalette( palette ); + measurementRMSLabel.push_back( new QLabel() ); + measurementRMSLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementRMSLabel[ channel ]->setPalette( palette ); + measurementDCLabel.push_back( new QLabel() ); + measurementDCLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementDCLabel[ channel ]->setPalette( palette ); + measurementACLabel.push_back( new QLabel() ); + measurementACLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementACLabel[ channel ]->setPalette( palette ); + measurementdBLabel.push_back( new QLabel() ); + measurementdBLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementdBLabel[ channel ]->setPalette( palette ); + measurementFrequencyLabel.push_back( new QLabel() ); + measurementFrequencyLabel[ channel ]->setAlignment( Qt::AlignRight ); + measurementFrequencyLabel[ channel ]->setPalette( palette ); + setMeasurementVisible( channel ); + iii = 0; + measurementLayout->addWidget( measurementNameLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementMiscLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementGainLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementMagnitudeLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementVppLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementRMSLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementDCLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementACLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementdBLabel[ channel ], int( channel ), iii++ ); + measurementLayout->addWidget( measurementFrequencyLabel[ channel ], int( channel ), iii++ ); + if ( channel < spec->channels ) + updateVoltageCoupling( channel ); else updateMathMode(); - updateVoltageDetails((unsigned)channel); - updateSpectrumDetails((unsigned)channel); + updateVoltageDetails( channel ); + updateSpectrumDetails( channel ); } // Cursors - cursorDataGrid = new DataGrid(this); - cursorDataGrid->setBackgroundColor(view->screen.background); - cursorDataGrid->addItem(tr("Markers"), view->screen.text); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - cursorDataGrid->addItem(scope->voltage[channel].name, view->screen.voltage[channel]); - } - for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { - cursorDataGrid->addItem(scope->spectrum[channel].name, view->screen.spectrum[channel]); - } - cursorDataGrid->selectItem(0); - - connect(cursorDataGrid, &DataGrid::itemSelected, [this] (unsigned index) { - mainScope->cursorSelected(index); - zoomScope->cursorSelected(index); - }); - connect(cursorDataGrid, &DataGrid::itemUpdated, [this, scope] (unsigned index) { + cursorDataGrid = new DataGrid( this ); + cursorDataGrid->setBackgroundColor( view->colors->background ); + cursorDataGrid->addItem( tr( "Markers" ), view->colors->text ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + cursorDataGrid->addItem( scope->voltage[ channel ].name, view->colors->voltage[ channel ] ); + } + for ( ChannelID channel = 0; channel < scope->spectrum.size(); ++channel ) { + cursorDataGrid->addItem( scope->spectrum[ channel ].name, view->colors->spectrum[ channel ] ); + } + cursorDataGrid->selectItem( 0 ); + + connect( cursorDataGrid, &DataGrid::itemSelected, [this]( unsigned index ) { + mainScope->cursorSelected( index ); + zoomScope->cursorSelected( index ); + } ); + connect( cursorDataGrid, &DataGrid::itemUpdated, [this, scope]( unsigned index ) { unsigned channelCount = scope->countChannels(); - if (0 < index && index < channelCount + 1) { + if ( 0 < index && index < channelCount + 1 ) { ChannelID channel = index - 1; - if (scope->voltage[channel].used) { - unsigned shape = (unsigned)scope->voltage[channel].cursor.shape; - if (shape == DsoSettingsScopeCursor::NONE) { - scope->voltage[channel].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; + if ( scope->voltage[ channel ].used ) { + unsigned shape = scope->voltage[ channel ].cursor.shape; + if ( shape == DsoSettingsScopeCursor::NONE ) { + scope->voltage[ channel ].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; } else { - scope->voltage[channel].cursor.shape = DsoSettingsScopeCursor::NONE; + scope->voltage[ channel ].cursor.shape = DsoSettingsScopeCursor::NONE; } } - } else if (channelCount < index && index < 2 * channelCount + 1) { + } else if ( channelCount < index && index < 2 * channelCount + 1 ) { ChannelID channel = index - channelCount - 1; - if (scope->spectrum[channel].used) { - unsigned shape = (unsigned)scope->spectrum[channel].cursor.shape; - if (shape == DsoSettingsScopeCursor::NONE) { - scope->spectrum[channel].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; + if ( scope->spectrum[ channel ].used ) { + unsigned shape = scope->spectrum[ channel ].cursor.shape; + if ( shape == DsoSettingsScopeCursor::NONE ) { + scope->spectrum[ channel ].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; } else { - scope->spectrum[channel].cursor.shape = DsoSettingsScopeCursor::NONE; + scope->spectrum[ channel ].cursor.shape = DsoSettingsScopeCursor::NONE; } } } updateMarkerDetails(); - mainScope->updateCursor(index); - zoomScope->updateCursor(index); - }); + mainScope->updateCursor( index ); + zoomScope->updateCursor( index ); + } ); scope->horizontal.cursor.shape = DsoSettingsScopeCursor::VERTICAL; // The layout for the widgets mainLayout = new QGridLayout(); - mainLayout->setColumnStretch(3, 1); // Scopes increase their size + mainLayout->setColumnStretch( 3, 1 ); // Scopes increase their size // Bars around the scope, needed because the slider-drawing-area is outside // the scope at min/max - mainLayout->setColumnMinimumWidth(2, mainSliders.triggerPositionSlider->preMargin()); - mainLayout->setColumnMinimumWidth(4, mainSliders.triggerPositionSlider->postMargin()); - mainLayout->setSpacing(0); + mainLayout->setColumnMinimumWidth( 2, mainSliders.triggerPositionSlider->preMargin() ); + mainLayout->setColumnMinimumWidth( 4, mainSliders.triggerPositionSlider->postMargin() ); + mainLayout->setSpacing( 0 ); int row = 0; - mainLayout->addLayout(settingsLayout, row++, 1, 1, 5); - // 5x5 box for mainScope & mainSliders - mainLayout->setRowMinimumHeight(row + 1, mainSliders.offsetSlider->preMargin()); - mainLayout->setRowMinimumHeight(row + 3, mainSliders.offsetSlider->postMargin()); - mainLayout->setRowStretch(row + 2, 1); - mainLayout->addWidget(mainScope, row + 2, 3); - mainLayout->addWidget(mainSliders.offsetSlider, row + 1, 1, 3, 2, Qt::AlignRight); - mainLayout->addWidget(mainSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom); - mainLayout->addWidget(mainSliders.triggerLevelSlider, row + 1, 4, 3, 2, Qt::AlignLeft); - mainLayout->addWidget(mainSliders.markerSlider, row + 3, 2, 2, 3, Qt::AlignTop); - row += 5; + // display settings on top of scope + mainLayout->addLayout( settingsLayout, row, 1, 1, 5 ); + ++row; + // 5x5 box for mainScope & mainSliders & markerSlider + mainLayout->addWidget( mainSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom ); + ++row; + mainLayout->setRowMinimumHeight( row, mainSliders.voltageOffsetSlider->preMargin() ); + mainLayout->addWidget( mainSliders.voltageOffsetSlider, row, 1, 3, 2, Qt::AlignRight ); + mainLayout->addWidget( mainSliders.triggerLevelSlider, row, 4, 3, 2, Qt::AlignLeft ); + ++row; + mainLayout->setRowStretch( row, 1 ); // the scope gets max space + mainLayout->addWidget( mainScope, row, 3 ); + ++row; + mainLayout->setRowMinimumHeight( row, mainSliders.voltageOffsetSlider->postMargin() ); + mainLayout->addWidget( mainSliders.markerSlider, row, 2, 2, 3, Qt::AlignTop ); + row += 2; // end 5x5 box // Separators and markerLayout - mainLayout->setRowMinimumHeight(row++, 5); - mainLayout->addLayout(markerLayout, row++, 1, 1, 5); - mainLayout->setRowMinimumHeight(row++, 4); - // 5x5 box for zoomScope & zoomSliders - zoomScopeRow = row + 2; - mainLayout->addWidget(zoomScope, zoomScopeRow, 3); - mainLayout->addWidget(zoomSliders.offsetSlider, row + 1, 1, 3, 2, Qt::AlignRight); - mainLayout->addWidget(zoomSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom); - mainLayout->addWidget(zoomSliders.triggerLevelSlider, row + 1, 4, 3, 2, Qt::AlignLeft); - row += 5; + ++row; + mainLayout->addLayout( markerLayout, row, 1, 1, 5 ); + ++row; + ++row; + // 5x4 box for zoomScope & zoomSliders + mainLayout->addWidget( zoomSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom ); + ++row; + mainLayout->setRowMinimumHeight( row, zoomSliders.voltageOffsetSlider->preMargin() ); + mainLayout->addWidget( zoomSliders.voltageOffsetSlider, row, 1, 3, 2, Qt::AlignRight ); + mainLayout->addWidget( zoomSliders.triggerLevelSlider, row, 4, 3, 2, Qt::AlignLeft ); + ++row; + mainLayout->addWidget( zoomScope, zoomScopeRow = row, 3 ); + ++row; + mainLayout->setRowMinimumHeight( row, zoomSliders.voltageOffsetSlider->postMargin() ); + ++row; // end 5x4 box // Separator and embedded measurementLayout - mainLayout->setRowMinimumHeight(row++, 8); - mainLayout->addLayout(measurementLayout, row++, 1, 1, 5); + ++row; + mainLayout->addLayout( measurementLayout, row, 1, 1, 5 ); - updateCursorGrid(view->cursorsVisible); + updateCursorGrid( view->cursorsVisible ); // The widget itself - setPalette(palette); - setBackgroundRole(QPalette::Background); - setAutoFillBackground(true); - setLayout(mainLayout); + setPalette( palette ); + setBackgroundRole( QPalette::Background ); + setAutoFillBackground( true ); + setLayout( mainLayout ); // Connect change-signals of sliders - connect(mainSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); - connect(zoomSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); + connect( mainSliders.voltageOffsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset ); + connect( zoomSliders.voltageOffsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset ); + + connect( mainSliders.triggerPositionSlider, &LevelSlider::valueChanged, + [this]( int index, double value ) { updateTriggerOffset( index, value, true ); } ); + connect( zoomSliders.triggerPositionSlider, &LevelSlider::valueChanged, + [this]( int index, double value ) { updateTriggerOffset( index, value, false ); } ); + + connect( mainSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel ); + connect( zoomSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel ); + + // show a horizontal level line as long as the trigger level slider is active + connect( mainSliders.triggerLevelSlider, &LevelSlider::valueChanged, [this]( int index, double value, bool pressed ) { + mainScope->generateGrid( index, value, pressed ); + zoomScope->generateGrid( index, value, pressed ); + } ); + connect( zoomSliders.triggerLevelSlider, &LevelSlider::valueChanged, [this]( int index, double value, bool pressed ) { + mainScope->generateGrid( index, value, pressed ); + zoomScope->generateGrid( index, value, pressed ); + } ); + + connect( mainSliders.markerSlider, &LevelSlider::valueChanged, [this]( int index, double value ) { + updateMarker( unsigned( index ), value ); + mainScope->updateCursor(); + zoomScope->updateCursor(); + } ); + zoomSliders.markerSlider->setEnabled( false ); +} - connect(mainSliders.triggerPositionSlider, &LevelSlider::valueChanged, [this](int index, double value) { - updateTriggerPosition(index, value, true); - }); - connect(zoomSliders.triggerPositionSlider, &LevelSlider::valueChanged, [this](int index, double value) { - updateTriggerPosition(index, value, false); - }); - connect(mainSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); - connect(zoomSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); +void DsoWidget::switchToPrintColors() { + if ( view->printerColorImages ) { + view->colors = &view->print; + setColors(); + } +} - connect(mainSliders.markerSlider, &LevelSlider::valueChanged, [this](int index, double value) { - updateMarker(index, value); - mainScope->updateCursor(); - zoomScope->updateCursor(); - }); - zoomSliders.markerSlider->setEnabled(false); + +void DsoWidget::restoreScreenColors() { + if ( view->colors != &view->screen ) { + view->colors = &view->screen; + setColors(); + } } -void DsoWidget::updateCursorGrid(bool enabled) { - if (!enabled) { - cursorDataGrid->selectItem(0); - cursorDataGrid->setParent(nullptr); - mainScope->cursorSelected(0); - zoomScope->cursorSelected(0); + +void DsoWidget::setColors() { + // Palette for this widget + QPalette paletteNow; + paletteNow.setColor( QPalette::Background, view->colors->background ); + paletteNow.setColor( QPalette::WindowText, view->colors->text ); + settingsSamplesOnScreen->setPalette( paletteNow ); + settingsSamplerateLabel->setPalette( paletteNow ); + settingsTimebaseLabel->setPalette( paletteNow ); + settingsFrequencybaseLabel->setPalette( paletteNow ); + markerInfoLabel->setPalette( paletteNow ); + markerTimeLabel->setPalette( paletteNow ); + markerFrequencyLabel->setPalette( paletteNow ); + markerTimebaseLabel->setPalette( paletteNow ); + markerFrequencybaseLabel->setPalette( paletteNow ); + QPalette tablePalette = paletteNow; + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + tablePalette.setColor( QPalette::WindowText, view->colors->voltage[ channel ] ); + measurementNameLabel[ channel ]->setPalette( tablePalette ); + measurementMiscLabel[ channel ]->setPalette( tablePalette ); + measurementGainLabel[ channel ]->setPalette( tablePalette ); + mainSliders.voltageOffsetSlider->setColor( ( channel ), view->colors->voltage[ channel ] ); + zoomSliders.voltageOffsetSlider->setColor( ( channel ), view->colors->voltage[ channel ] ); + mainSliders.voltageOffsetSlider->setColor( unsigned( scope->voltage.size() ) + channel, view->colors->spectrum[ channel ] ); + zoomSliders.voltageOffsetSlider->setColor( unsigned( scope->voltage.size() ) + channel, view->colors->spectrum[ channel ] ); + tablePalette.setColor( QPalette::WindowText, view->colors->spectrum[ channel ] ); + measurementMagnitudeLabel[ channel ]->setPalette( tablePalette ); + measurementVppLabel[ channel ]->setPalette( paletteNow ); + measurementRMSLabel[ channel ]->setPalette( paletteNow ); + measurementDCLabel[ channel ]->setPalette( paletteNow ); + measurementACLabel[ channel ]->setPalette( paletteNow ); + measurementdBLabel[ channel ]->setPalette( paletteNow ); + measurementFrequencyLabel[ channel ]->setPalette( paletteNow ); + } + + tablePalette = palette(); + tablePalette.setColor( QPalette::WindowText, view->colors->voltage[ scope->trigger.source ] ); + settingsTriggerLabel->setPalette( tablePalette ); + updateTriggerSource(); + setPalette( paletteNow ); + setBackgroundRole( QPalette::Background ); +} + + +void DsoWidget::updateCursorGrid( bool enabled ) { + if ( !enabled ) { + cursorDataGrid->selectItem( 0 ); + cursorDataGrid->setParent( nullptr ); + mainScope->cursorSelected( 0 ); + zoomScope->cursorSelected( 0 ); return; } - switch (view->cursorGridPosition) { + switch ( view->cursorGridPosition ) { case Qt::LeftToolBarArea: - if (mainLayout->itemAtPosition(0, 0) == nullptr) { - cursorDataGrid->setParent(nullptr); - mainLayout->addWidget(cursorDataGrid, 0, 0, mainLayout->rowCount(), 1); + if ( mainLayout->itemAtPosition( 0, 0 ) == nullptr ) { + cursorDataGrid->setParent( nullptr ); + mainLayout->addWidget( cursorDataGrid, 0, 0, mainLayout->rowCount(), 1 ); } break; case Qt::RightToolBarArea: - if (mainLayout->itemAtPosition(0, 6) == nullptr) { - cursorDataGrid->setParent(nullptr); - mainLayout->addWidget(cursorDataGrid, 0, 6, mainLayout->rowCount(), 1); + if ( mainLayout->itemAtPosition( 0, 6 ) == nullptr ) { + cursorDataGrid->setParent( nullptr ); + mainLayout->addWidget( cursorDataGrid, 0, 6, mainLayout->rowCount(), 1 ); } break; default: - if (cursorDataGrid->parent() != nullptr) { - cursorDataGrid->setParent(nullptr); + if ( cursorDataGrid->parent() != nullptr ) { + cursorDataGrid->setParent( nullptr ); } break; } } -void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { + +void DsoWidget::setupSliders( DsoWidget::Sliders &sliders ) { // The offset sliders for all possible channels - sliders.offsetSlider = new LevelSlider(Qt::RightArrow); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - sliders.offsetSlider->addSlider(scope->voltage[channel].name, channel); - sliders.offsetSlider->setColor(channel, view->screen.voltage[channel]); - sliders.offsetSlider->setLimits(channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); - sliders.offsetSlider->setStep(channel, 0.2); - sliders.offsetSlider->setValue(channel, scope->voltage[channel].offset); - sliders.offsetSlider->setIndexVisible(channel, scope->voltage[channel].used); - } - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - sliders.offsetSlider->addSlider(scope->spectrum[channel].name, scope->voltage.size() + channel); - sliders.offsetSlider->setColor(scope->voltage.size() + channel, view->screen.spectrum[channel]); - sliders.offsetSlider->setLimits(scope->voltage.size() + channel, -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2); - sliders.offsetSlider->setStep(scope->voltage.size() + channel, 0.2); - sliders.offsetSlider->setValue(scope->voltage.size() + channel, scope->spectrum[channel].offset); - sliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, scope->spectrum[channel].used); + sliders.voltageOffsetSlider = new LevelSlider( Qt::RightArrow ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + sliders.voltageOffsetSlider->addSlider( scope->voltage[ channel ].name, int( channel ) ); + sliders.voltageOffsetSlider->setColor( ( channel ), view->colors->voltage[ channel ] ); + sliders.voltageOffsetSlider->setLimits( int( channel ), -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2 ); + sliders.voltageOffsetSlider->setStep( int( channel ), 0.2 ); + sliders.voltageOffsetSlider->setValue( int( channel ), scope->voltage[ channel ].offset ); + sliders.voltageOffsetSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); + } + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + sliders.voltageOffsetSlider->addSlider( scope->spectrum[ channel ].name, int( scope->voltage.size() + channel ) ); + sliders.voltageOffsetSlider->setColor( unsigned( scope->voltage.size() ) + channel, view->colors->spectrum[ channel ] ); + sliders.voltageOffsetSlider->setLimits( int( scope->voltage.size() + channel ), -DIVS_VOLTAGE / 2, DIVS_VOLTAGE / 2 ); + sliders.voltageOffsetSlider->setStep( int( scope->voltage.size() + channel ), 0.2 ); + sliders.voltageOffsetSlider->setValue( int( scope->voltage.size() + channel ), scope->spectrum[ channel ].offset ); + sliders.voltageOffsetSlider->setIndexVisible( unsigned( scope->voltage.size() ) + channel, + scope->spectrum[ channel ].used ); } // The triggerPosition slider - sliders.triggerPositionSlider = new LevelSlider(Qt::DownArrow); + sliders.triggerPositionSlider = new LevelSlider( Qt::DownArrow ); sliders.triggerPositionSlider->addSlider(); - sliders.triggerPositionSlider->setLimits(0, 0.0, 1.0); - sliders.triggerPositionSlider->setStep(0, 0.2 / (double)DIVS_TIME); - sliders.triggerPositionSlider->setValue(0, scope->trigger.position); - sliders.triggerPositionSlider->setIndexVisible(0, true); + sliders.triggerPositionSlider->setLimits( 0, 0.0, 1.0 ); + sliders.triggerPositionSlider->setStep( 0, 0.2 / double( DIVS_TIME ) ); + sliders.triggerPositionSlider->setValue( 0, scope->trigger.offset ); + sliders.triggerPositionSlider->setIndexVisible( 0, true ); // The sliders for the trigger levels - sliders.triggerLevelSlider = new LevelSlider(Qt::LeftArrow); - for (ChannelID channel = 0; channel < spec->channels; ++channel) { - sliders.triggerLevelSlider->addSlider((int)channel); - sliders.triggerLevelSlider->setColor(channel, - (!scope->trigger.special && channel == scope->trigger.source) - ? view->screen.voltage[channel] - : view->screen.voltage[channel].darker()); - adaptTriggerLevelSlider(sliders, channel); - sliders.triggerLevelSlider->setValue(channel, scope->voltage[channel].trigger); - sliders.triggerLevelSlider->setIndexVisible(channel, scope->voltage[channel].used); + sliders.triggerLevelSlider = new LevelSlider( Qt::LeftArrow ); + for ( ChannelID channel = 0; channel < spec->channels; ++channel ) { + sliders.triggerLevelSlider->addSlider( int( channel ) ); + sliders.triggerLevelSlider->setColor( channel, ( channel == scope->trigger.source ) + ? view->colors->voltage[ channel ] + : view->colors->voltage[ channel ].darker() ); + adaptTriggerLevelSlider( sliders, channel ); + sliders.triggerLevelSlider->setValue( int( channel ), scope->voltage[ channel ].trigger ); + sliders.triggerLevelSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); } // The marker slider - sliders.markerSlider = new LevelSlider(Qt::UpArrow); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - sliders.markerSlider->addSlider(QString::number(marker + 1), marker); - sliders.markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); - sliders.markerSlider->setStep(marker, MARKER_STEP); - sliders.markerSlider->setValue(marker, scope->horizontal.cursor.pos[marker].x()); - sliders.markerSlider->setIndexVisible(marker, true); + sliders.markerSlider = new LevelSlider( Qt::UpArrow ); + for ( int marker = 0; marker < 2; ++marker ) { + sliders.markerSlider->addSlider( QString::number( marker + 1 ), marker ); + sliders.markerSlider->setLimits( marker, MARGIN_LEFT, MARGIN_RIGHT ); + sliders.markerSlider->setStep( marker, MARKER_STEP ); + sliders.markerSlider->setValue( marker, scope->horizontal.cursor.pos[ marker ].x() ); + sliders.markerSlider->setIndexVisible( unsigned( marker ), true ); } } + /// \brief Set the trigger level sliders minimum and maximum to the new values. -void DsoWidget::adaptTriggerLevelSlider(DsoWidget::Sliders &sliders, ChannelID channel) { - sliders.triggerLevelSlider->setLimits((int)channel, - (-DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel), - (DIVS_VOLTAGE / 2 - scope->voltage[channel].offset) * scope->gain(channel)); - sliders.triggerLevelSlider->setStep((int)channel, scope->gain(channel) * 0.05); +void DsoWidget::adaptTriggerLevelSlider( DsoWidget::Sliders &sliders, ChannelID channel ) { + // printf( "DW::adaptTriggerLevelSlider( %d )\n", channel ); + sliders.triggerLevelSlider->setLimits( int( channel ), + ( -DIVS_VOLTAGE / 2 - scope->voltage[ channel ].offset ) * scope->gain( channel ), + ( DIVS_VOLTAGE / 2 - scope->voltage[ channel ].offset ) * scope->gain( channel ) ); + sliders.triggerLevelSlider->setStep( int( channel ), scope->gain( channel ) * 0.05 ); + double value = sliders.triggerLevelSlider->value( int( channel ) ); + if ( bool( value ) ) { // ignore when first called at init + // updateTriggerLevel(channel, value); + } } -/// \brief Show/Hide a line of the measurement table. -void DsoWidget::setMeasurementVisible(ChannelID channel) { - bool visible = scope->voltage[channel].used || scope->spectrum[channel].used; - - measurementNameLabel[channel]->setVisible(visible); - measurementMiscLabel[channel]->setVisible(visible); - - measurementAmplitudeLabel[channel]->setVisible(visible); - measurementFrequencyLabel[channel]->setVisible(visible); - if (!visible) { - measurementGainLabel[channel]->setText(QString()); - measurementAmplitudeLabel[channel]->setText(QString()); - measurementFrequencyLabel[channel]->setText(QString()); +/// \brief Show/Hide a line of the measurement table. +void DsoWidget::setMeasurementVisible( ChannelID channel ) { + bool visible = scope->voltage[ channel ].used || scope->spectrum[ channel ].used; + + measurementNameLabel[ channel ]->setVisible( visible ); + measurementMiscLabel[ channel ]->setVisible( visible ); + measurementGainLabel[ channel ]->setVisible( visible ); + measurementVppLabel[ channel ]->setVisible( visible ); + measurementRMSLabel[ channel ]->setVisible( visible ); + measurementDCLabel[ channel ]->setVisible( visible ); + measurementACLabel[ channel ]->setVisible( visible ); + measurementdBLabel[ channel ]->setVisible( visible ); + measurementFrequencyLabel[ channel ]->setVisible( visible ); + if ( !visible ) { + measurementGainLabel[ channel ]->setText( QString() ); + measurementVppLabel[ channel ]->setText( QString() ); + measurementRMSLabel[ channel ]->setText( QString() ); + measurementDCLabel[ channel ]->setText( QString() ); + measurementACLabel[ channel ]->setText( QString() ); + measurementdBLabel[ channel ]->setText( QString() ); + measurementFrequencyLabel[ channel ]->setText( QString() ); } - measurementGainLabel[channel]->setVisible(scope->voltage[channel].used); - if (!scope->voltage[channel].used) { measurementGainLabel[channel]->setText(QString()); } + measurementGainLabel[ channel ]->setVisible( scope->voltage[ channel ].used ); + if ( !scope->voltage[ channel ].used ) { + measurementGainLabel[ channel ]->setText( QString() ); + } - measurementMagnitudeLabel[channel]->setVisible(scope->spectrum[channel].used); - if (!scope->spectrum[channel].used) { measurementMagnitudeLabel[channel]->setText(QString()); } + measurementMagnitudeLabel[ channel ]->setVisible( scope->spectrum[ channel ].used ); + if ( !scope->spectrum[ channel ].used ) { + measurementMagnitudeLabel[ channel ]->setText( QString() ); + } } -static QString markerToString(DsoSettingsScope *scope, unsigned index) { - double value = (DIVS_TIME * (0.5 - scope->trigger.position) + scope->getMarker(index)) * scope->horizontal.timebase; - int precision = 3 - (int)floor(log10(fabs(value))); - - if (scope->horizontal.timebase < 1e-9) - return QApplication::tr("%L1 ps").arg(value / 1e-12, 0, 'f', qBound(0, precision - 12, 3)); - else if (scope->horizontal.timebase < 1e-6) - return QApplication::tr("%L1 ns").arg(value / 1e-9, 0, 'f', qBound(0, precision - 9, 3)); - else if (scope->horizontal.timebase < 1e-3) - return QApplication::tr("%L1 µs").arg(value / 1e-6, 0, 'f', qBound(0, precision - 6, 3)); - else if (scope->horizontal.timebase < 1) - return QApplication::tr("%L1 ms").arg(value / 1e-3, 0, 'f', qBound(0, precision - 3, 3)); - else - return QApplication::tr("%L1 s").arg(value, 0, 'f', qBound(0, precision, 3)); -} /// \brief Update the label about the marker measurements void DsoWidget::updateMarkerDetails() { - double divs = fabs(scope->horizontal.cursor.pos[1].x() - scope->horizontal.cursor.pos[0].x()); + double m1 = scope->horizontal.cursor.pos[ 0 ].x() + DIVS_TIME / 2; // zero at center -> zero at left margin + double m2 = scope->horizontal.cursor.pos[ 1 ].x() + DIVS_TIME / 2; // zero at center -> zero at left margin + if ( m1 > m2 ) + std::swap( m1, m2 ); + double divs = m2 - m1; + // t = 0 at trigger position + double time0 = ( m1 - DIVS_TIME * scope->trigger.offset ) * scope->horizontal.timebase; + double time1 = ( m2 - DIVS_TIME * scope->trigger.offset ) * scope->horizontal.timebase; double time = divs * scope->horizontal.timebase; + double freq0 = m1 * scope->horizontal.frequencybase; + double freq1 = m2 * scope->horizontal.frequencybase; double freq = divs * scope->horizontal.frequencybase; - - QString prefix(tr("Markers")); - if (view->zoom) { - prefix = tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3); - markerTimebaseLabel->setText(valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + tr("/div")); - markerFrequencybaseLabel->setText( - valueToString(divs * scope->horizontal.frequencybase / DIVS_TIME, UNIT_HERTZ, 4) + tr("/div")); - } - markerInfoLabel->setText(prefix.append(": %1 %2").arg(markerToString(scope, 0)).arg(markerToString(scope, 1))); - markerTimeLabel->setText(valueToString(time, UNIT_SECONDS, 4)); - markerFrequencyLabel->setText(valueToString(1.0 / time, UNIT_HERTZ, 4)); + bool timeUsed = false; + bool freqUsed = false; int index = 0; - cursorDataGrid->updateInfo(index++, true, QString(), valueToString(time, UNIT_SECONDS, 4), valueToString(freq, UNIT_HERTZ, 4)); - - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - if (scope->voltage[channel].used) { - QPointF p0 = scope->voltage[channel].cursor.pos[0]; - QPointF p1 = scope->voltage[channel].cursor.pos[1]; - cursorDataGrid->updateInfo(index, true, - scope->voltage[channel].cursor.shape != DsoSettingsScopeCursor::NONE ? tr("ON") : tr("OFF"), - valueToString(fabs(p1.x() - p0.x()) * scope->horizontal.timebase, UNIT_SECONDS, 4), - valueToString(fabs(p1.y() - p0.y()) * scope->gain(channel), UNIT_VOLTS, 4)); + cursorDataGrid->updateInfo( unsigned( index++ ), true, QString(), valueToString( time, UNIT_SECONDS, 3 ), + valueToString( freq, UNIT_HERTZ, 3 ) ); + + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + if ( scope->voltage[ channel ].used ) { + timeUsed = true; // at least one voltage channel used -> show marker time details + QPointF p0 = scope->voltage[ channel ].cursor.pos[ 0 ]; + QPointF p1 = scope->voltage[ channel ].cursor.pos[ 1 ]; + cursorDataGrid->updateInfo( unsigned( index ), true, + scope->voltage[ channel ].cursor.shape != DsoSettingsScopeCursor::NONE ? tr( "ON" ) + : tr( "OFF" ), + valueToString( fabs( p1.x() - p0.x() ) * scope->horizontal.timebase, UNIT_SECONDS, 4 ), + valueToString( fabs( p1.y() - p0.y() ) * scope->gain( channel ), UNIT_VOLTS, 4 ) ); } else { - cursorDataGrid->updateInfo(index, false); + cursorDataGrid->updateInfo( unsigned( index ), false ); } ++index; } - for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { - if (scope->spectrum[channel].used) { - QPointF p0 = scope->spectrum[channel].cursor.pos[0]; - QPointF p1 = scope->spectrum[channel].cursor.pos[1]; - cursorDataGrid->updateInfo(index, true, - scope->spectrum[channel].cursor.shape != DsoSettingsScopeCursor::NONE ? tr("ON") : tr("OFF"), - valueToString(fabs(p1.x() - p0.x()) * scope->horizontal.frequencybase, UNIT_HERTZ, 4), - valueToString(fabs(p1.y() - p0.y()) * scope->spectrum[channel].magnitude, UNIT_DECIBEL, 4)); + for ( ChannelID channel = 0; channel < scope->spectrum.size(); ++channel ) { + if ( scope->spectrum[ channel ].used ) { + freqUsed = true; // at least one spec channel used -> show marker freq details + QPointF p0 = scope->spectrum[ channel ].cursor.pos[ 0 ]; + QPointF p1 = scope->spectrum[ channel ].cursor.pos[ 1 ]; + cursorDataGrid->updateInfo( + unsigned( index ), true, + scope->spectrum[ channel ].cursor.shape != DsoSettingsScopeCursor::NONE ? tr( "ON" ) : tr( "OFF" ), + valueToString( fabs( p1.x() - p0.x() ) * scope->horizontal.frequencybase, UNIT_HERTZ, 4 ), + valueToString( fabs( p1.y() - p0.y() ) * scope->spectrum[ channel ].magnitude, UNIT_DECIBEL, 4 ) ); } else { - cursorDataGrid->updateInfo(index, false); + cursorDataGrid->updateInfo( unsigned( index ), false ); } ++index; } + + if ( divs >= DIVS_TIME || ( m1 <= 0 && m2 <= 0 ) || ( m1 >= DIVS_TIME && m2 >= DIVS_TIME ) ) { + // markers at left/right margins -> don't display + markerInfoLabel->setVisible( false ); + markerTimeLabel->setVisible( false ); + markerFrequencyLabel->setVisible( false ); + markerTimebaseLabel->setVisible( false ); + markerFrequencybaseLabel->setVisible( false ); + } else { + markerInfoLabel->setVisible( true ); + markerTimeLabel->setVisible( true ); + markerFrequencyLabel->setVisible( true ); + markerTimebaseLabel->setVisible( view->zoom ); + markerFrequencybaseLabel->setVisible( view->zoom ); + QString mInfo( tr( "Markers " ) ); + QString mTime( tr( "Time: " ) ); + QString mFreq( tr( "Frequency: " ) ); + if ( view->zoom ) { + if ( divs != 0.0 ) + mInfo = tr( "Zoom x%L1 " ).arg( DIVS_TIME / divs, -1, 'g', 3 ); + else // avoid div by zero + mInfo = tr( "Zoom --- " ); + mTime = " t: "; + mFreq = " f: "; + markerTimebaseLabel->setText( " " + valueToString( time / DIVS_TIME, UNIT_SECONDS, 3 ) + tr( "/div" ) ); + markerTimebaseLabel->setVisible( timeUsed ); + markerFrequencybaseLabel->setText( " " + valueToString( freq / DIVS_TIME, UNIT_HERTZ, 3 ) + tr( "/div" ) ); + markerFrequencybaseLabel->setVisible( freqUsed ); + } + markerInfoLabel->setText( mInfo ); + if ( timeUsed ) { + mTime += QString( "%1" ).arg( valueToString( time0, UNIT_SECONDS, 3 ) ); + if ( time > 0 ) + mTime += QString( " ... %1, Δt: %2 (%3) " ) + .arg( valueToString( time1, UNIT_SECONDS, 3 ), valueToString( time, UNIT_SECONDS, 3 ), + valueToString( 1 / time, UNIT_HERTZ, 3 ) ); + markerTimeLabel->setText( mTime ); + } else { + markerTimeLabel->setText( "" ); + } + if ( freqUsed ) { + mFreq += QString( "%1" ).arg( valueToString( freq0, UNIT_HERTZ, 3 ) ); + if ( freq > 0 ) + mFreq += QString( " ... %2, Δf: %3 " ) + .arg( valueToString( freq1, UNIT_HERTZ, 3 ), valueToString( freq, UNIT_HERTZ, 3 ) ); + markerFrequencyLabel->setText( mFreq ); + } else + markerFrequencyLabel->setText( "" ); + } } -/// \brief Update the label about the trigger settings -void DsoWidget::updateSpectrumDetails(ChannelID channel) { - setMeasurementVisible(channel); - if (scope->spectrum[channel].used) - measurementMagnitudeLabel[channel]->setText(valueToString(scope->spectrum[channel].magnitude, UNIT_DECIBEL, 3) + - tr("/div")); +/// \brief Update the label about the spectrum settings +void DsoWidget::updateSpectrumDetails( ChannelID channel ) { + setMeasurementVisible( channel ); + if ( scope->spectrum[ channel ].used ) + measurementMagnitudeLabel[ channel ]->setText( valueToString( scope->spectrum[ channel ].magnitude, UNIT_DECIBEL, 3 ) + + tr( "/div" ) ); else - measurementMagnitudeLabel[channel]->setText(QString()); + measurementMagnitudeLabel[ channel ]->setText( QString() ); } + /// \brief Update the label about the trigger settings void DsoWidget::updateTriggerDetails() { // Update the trigger details QPalette tablePalette = palette(); - tablePalette.setColor(QPalette::WindowText, view->screen.voltage[scope->trigger.source]); - settingsTriggerLabel->setPalette(tablePalette); - QString levelString = valueToString(scope->voltage[scope->trigger.source].trigger, UNIT_VOLTS, 3); - QString pretriggerString = tr("%L1%").arg((int)(scope->trigger.position * 100 + 0.5)); - settingsTriggerLabel->setText(tr("%1 %2 %3 %4") - .arg(scope->voltage[scope->trigger.source].name, - Dso::slopeString(scope->trigger.slope), levelString, pretriggerString)); - - /// \todo This won't work for special trigger sources + tablePalette.setColor( QPalette::WindowText, view->colors->voltage[ scope->trigger.source ] ); + settingsTriggerLabel->setPalette( tablePalette ); + QString levelString = valueToString( scope->voltage[ scope->trigger.source ].trigger, UNIT_VOLTS, 3 ); + QString pretriggerString = tr( "%L1%" ).arg( int( round( scope->trigger.offset * 100 ) ) ); + QString pre = Dso::slopeString( scope->trigger.slope ); // trigger slope + QString post = pre; // opposite trigger slope + if ( scope->trigger.slope == Dso::Slope::Positive ) + post = Dso::slopeString( Dso::Slope::Negative ); + else if ( scope->trigger.slope == Dso::Slope::Negative ) + post = Dso::slopeString( Dso::Slope::Positive ); + QString pulseWidthString = bool( pulseWidth1 ) ? pre + valueToString( pulseWidth1, UNIT_SECONDS, 3 ) + post : ""; + pulseWidthString += bool( pulseWidth2 ) ? valueToString( pulseWidth2, UNIT_SECONDS, 3 ) + pre : ""; + if ( bool( pulseWidth1 ) && bool( pulseWidth2 ) ) { + int dutyCyle = int( 0.5 + ( 100.0 * pulseWidth1 ) / ( pulseWidth1 + pulseWidth2 ) ); + pulseWidthString += " (" + QString::number( dutyCyle ) + "%)"; + } + if ( scope->trigger.mode != Dso::TriggerMode::ROLL ) { + settingsTriggerLabel->setText( tr( "%1 %2 %3 %4 %5" ) + .arg( scope->voltage[ scope->trigger.source ].name, + Dso::slopeString( scope->trigger.slope ), levelString, pretriggerString, + pulseWidthString ) ); + } else { + settingsTriggerLabel->setText( "" ); + } } + /// \brief Update the label about the trigger settings -void DsoWidget::updateVoltageDetails(ChannelID channel) { - if (channel >= scope->voltage.size()) return; +void DsoWidget::updateVoltageDetails( ChannelID channel ) { + if ( channel >= scope->voltage.size() ) + return; - setMeasurementVisible(channel); + setMeasurementVisible( channel ); - if (scope->voltage[channel].used) - measurementGainLabel[channel]->setText(valueToString(scope->gain(channel), UNIT_VOLTS, 3) + tr("/div")); + if ( scope->voltage[ channel ].used ) + measurementGainLabel[ channel ]->setText( valueToString( scope->gain( channel ), UNIT_VOLTS, 3 ) + tr( "/div" ) ); else - measurementGainLabel[channel]->setText(QString()); + measurementGainLabel[ channel ]->setText( QString() ); } + /// \brief Handles frequencybaseChanged signal from the horizontal dock. /// \param frequencybase The frequencybase used for displaying the trace. -void DsoWidget::updateFrequencybase(double frequencybase) { - settingsFrequencybaseLabel->setText(valueToString(frequencybase, UNIT_HERTZ, 4) + tr("/div")); +void DsoWidget::updateFrequencybase( double frequencybase ) { + settingsFrequencybaseLabel->setText( valueToString( frequencybase, UNIT_HERTZ, -1 ) + tr( "/div" ) ); + updateMarkerDetails(); } + /// \brief Updates the samplerate field after changing the samplerate. /// \param samplerate The samplerate set in the oscilloscope. -void DsoWidget::updateSamplerate(double samplerate) { - settingsSamplerateLabel->setText(valueToString(samplerate, UNIT_SAMPLES, 4) + tr("/s")); +void DsoWidget::updateSamplerate( double newSamplerate ) { + samplerate = newSamplerate; + dotsOnScreen = unsigned( samplerate * timebase * DIVS_TIME + 0.99 ); + // printf( "DsoWidget::updateSamplerate( %g ) -> %d\n", samplerate, dotsOnScreen ); + settingsSamplerateLabel->setText( valueToString( samplerate, UNIT_SAMPLES, -1 ) + tr( "/s" ) ); } + /// \brief Handles timebaseChanged signal from the horizontal dock. /// \param timebase The timebase used for displaying the trace. -void DsoWidget::updateTimebase(double timebase) { - settingsTimebaseLabel->setText(valueToString(timebase, UNIT_SECONDS, 4) + tr("/div")); - +void DsoWidget::updateTimebase( double newTimebase ) { + timebase = newTimebase; + dotsOnScreen = unsigned( samplerate * timebase * DIVS_TIME + 0.99 ); + // printf( "DsoWidget::updateTimebase( %g ) -> %d\n", timebase, dotsOnScreen ); + settingsTimebaseLabel->setText( valueToString( timebase, UNIT_SECONDS, -1 ) + tr( "/div" ) ); updateMarkerDetails(); } + /// \brief Handles magnitudeChanged signal from the spectrum dock. /// \param channel The channel whose magnitude was changed. -void DsoWidget::updateSpectrumMagnitude(ChannelID channel) { updateSpectrumDetails(channel); } +void DsoWidget::updateSpectrumMagnitude( ChannelID channel ) { updateSpectrumDetails( channel ); } /// \brief Handles usedChanged signal from the spectrum dock. /// \param channel The channel whose used-state was changed. /// \param used The new used-state for the channel. -void DsoWidget::updateSpectrumUsed(ChannelID channel, bool used) { - if (channel >= (unsigned int)scope->voltage.size()) return; - -// if (!used && cursorDataGrid->spectrumCursors[channel].selector->isChecked()) cursorDataGrid->selectItem(0); - - mainSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); - zoomSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); - - updateSpectrumDetails(channel); +void DsoWidget::updateSpectrumUsed( ChannelID channel, bool used ) { + if ( channel >= unsigned( scope->voltage.size() ) ) + return; + bool spectrumUsed = false; + for ( size_t channel = 0; channel < scope->voltage.size(); ++channel ) + if ( scope->spectrum[ channel ].used ) + spectrumUsed = true; + settingsFrequencybaseLabel->setVisible( spectrumUsed ); // hide text if no spectrum channel used + mainSliders.voltageOffsetSlider->setIndexVisible( unsigned( scope->voltage.size() ) + channel, used ); + zoomSliders.voltageOffsetSlider->setIndexVisible( unsigned( scope->voltage.size() ) + channel, used ); + + updateSpectrumDetails( channel ); updateMarkerDetails(); } + /// \brief Handles modeChanged signal from the trigger dock. void DsoWidget::updateTriggerMode() { updateTriggerDetails(); } + /// \brief Handles slopeChanged signal from the trigger dock. void DsoWidget::updateTriggerSlope() { updateTriggerDetails(); } + /// \brief Handles sourceChanged signal from the trigger dock. void DsoWidget::updateTriggerSource() { // Change the colors of the trigger sliders - if (scope->trigger.special || scope->trigger.source >= spec->channels) { - mainSliders.triggerPositionSlider->setColor(0, view->screen.border); - zoomSliders.triggerPositionSlider->setColor(0, view->screen.border); - } else { - mainSliders.triggerPositionSlider->setColor(0, view->screen.voltage[scope->trigger.source]); - zoomSliders.triggerPositionSlider->setColor(0, view->screen.voltage[scope->trigger.source]); - } - - for (ChannelID channel = 0; channel < spec->channels; ++channel) { - QColor color = (!scope->trigger.special && channel == scope->trigger.source) - ? view->screen.voltage[channel] - : view->screen.voltage[channel].darker(); - mainSliders.triggerLevelSlider->setColor(channel, color); - zoomSliders.triggerLevelSlider->setColor(channel, color); + mainSliders.triggerPositionSlider->setColor( 0, view->colors->voltage[ scope->trigger.source ] ); + zoomSliders.triggerPositionSlider->setColor( 0, view->colors->voltage[ scope->trigger.source ] ); + + for ( ChannelID channel = 0; channel < spec->channels; ++channel ) { + QColor color = + ( channel == scope->trigger.source ) ? view->colors->voltage[ channel ] : view->colors->voltage[ channel ].darker(); + mainSliders.triggerLevelSlider->setColor( channel, color ); + zoomSliders.triggerLevelSlider->setColor( channel, color ); } updateTriggerDetails(); } + /// \brief Handles couplingChanged signal from the voltage dock. /// \param channel The channel whose coupling was changed. -void DsoWidget::updateVoltageCoupling(ChannelID channel) { - if (channel >= (unsigned int)scope->voltage.size()) return; - - measurementMiscLabel[channel]->setText(Dso::couplingString(scope->coupling(channel, spec))); +void DsoWidget::updateVoltageCoupling( ChannelID channel ) { + if ( channel >= unsigned( scope->voltage.size() ) ) + return; + measurementMiscLabel[ channel ]->setText( Dso::couplingString( scope->coupling( channel, spec ) ) ); } + /// \brief Handles modeChanged signal from the voltage dock. void DsoWidget::updateMathMode() { - measurementMiscLabel[spec->channels]->setText( - Dso::mathModeString(Dso::getMathMode(scope->voltage[spec->channels]))); + measurementMiscLabel[ spec->channels ]->setText( Dso::mathModeString( Dso::getMathMode( scope->voltage[ spec->channels ] ) ) ); } + /// \brief Handles gainChanged signal from the voltage dock. /// \param channel The channel whose gain was changed. -void DsoWidget::updateVoltageGain(ChannelID channel) { - if (channel >= (unsigned int)scope->voltage.size()) return; - - if (channel < spec->channels) { - adaptTriggerLevelSlider(mainSliders, channel); - adaptTriggerLevelSlider(zoomSliders, channel); +void DsoWidget::updateVoltageGain( ChannelID channel ) { + if ( channel >= unsigned( scope->voltage.size() ) ) + return; + if ( channel < spec->channels ) { + adaptTriggerLevelSlider( mainSliders, channel ); + adaptTriggerLevelSlider( zoomSliders, channel ); } - - updateVoltageDetails(channel); + updateVoltageDetails( channel ); } + /// \brief Handles usedChanged signal from the voltage dock. /// \param channel The channel whose used-state was changed. /// \param used The new used-state for the channel. -void DsoWidget::updateVoltageUsed(ChannelID channel, bool used) { - if (channel >= (unsigned int)scope->voltage.size()) return; +void DsoWidget::updateVoltageUsed( ChannelID channel, bool used ) { + if ( channel >= unsigned( scope->voltage.size() ) ) + return; -// if (!used && cursorDataGrid->voltageCursors[channel].selector->isChecked()) cursorDataGrid->selectItem(0); + // if (!used && cursorDataGrid->voltageCursors[channel].selector->isChecked()) cursorDataGrid->selectItem(0); - mainSliders.offsetSlider->setIndexVisible(channel, used); - zoomSliders.offsetSlider->setIndexVisible(channel, used); + mainSliders.voltageOffsetSlider->setIndexVisible( channel, used ); + zoomSliders.voltageOffsetSlider->setIndexVisible( channel, used ); - mainSliders.triggerLevelSlider->setIndexVisible(channel, used); - zoomSliders.triggerLevelSlider->setIndexVisible(channel, used); + mainSliders.triggerLevelSlider->setIndexVisible( channel, used ); + zoomSliders.triggerLevelSlider->setIndexVisible( channel, used ); - setMeasurementVisible(channel); - updateVoltageDetails(channel); + setMeasurementVisible( channel ); + updateVoltageDetails( channel ); updateMarkerDetails(); } + /// \brief Change the record length. -void DsoWidget::updateRecordLength(unsigned long size) { - settingsRecordLengthLabel->setText(valueToString(size, UNIT_SAMPLES, 4)); +void DsoWidget::updateRecordLength( unsigned long size ) { + settingsSamplesOnScreen->setText( valueToString( size, UNIT_SAMPLES, -1 ) + tr( " on screen" ) ); } -/// \brief Show/hide the zoom view. -void DsoWidget::updateZoom(bool enabled) { - mainLayout->setRowStretch(zoomScopeRow, enabled ? 1 : 0); - zoomScope->setVisible(enabled); - - if (enabled) { - zoomSliders.offsetSlider->show(); - zoomSliders.triggerPositionSlider->show(); - zoomSliders.triggerLevelSlider->show(); - } else { - zoomSliders.offsetSlider->hide(); - zoomSliders.triggerPositionSlider->hide(); - zoomSliders.triggerLevelSlider->hide(); - } +/// \brief Show/hide the zoom view. +void DsoWidget::updateZoom( bool enabled ) { + mainLayout->setRowStretch( zoomScopeRow, enabled ? 1 : 0 ); + zoomScope->setVisible( enabled ); + zoomSliders.voltageOffsetSlider->setVisible( enabled ); + zoomSliders.triggerPositionSlider->setVisible( enabled ); + zoomSliders.triggerLevelSlider->setVisible( enabled ); // Show time-/frequencybase and zoom factor if the magnified scope is shown - markerLayout->setStretch(3, enabled ? 1 : 0); - markerTimebaseLabel->setVisible(enabled); - markerLayout->setStretch(4, enabled ? 1 : 0); - markerFrequencybaseLabel->setVisible(enabled); + markerLayout->setStretch( 3, enabled ? 1 : 0 ); + markerTimebaseLabel->setVisible( enabled ); + markerLayout->setStretch( 4, enabled ? 1 : 0 ); + markerFrequencybaseLabel->setVisible( enabled ); updateMarkerDetails(); repaint(); } + /// \brief Prints analyzed data. -void DsoWidget::showNew(std::shared_ptr data) { - mainScope->showData(data); - zoomScope->showData(data); - - if (spec->isSoftwareTriggerDevice) { - QPalette triggerLabelPalette = palette(); - triggerLabelPalette.setColor(QPalette::WindowText, Qt::black); - triggerLabelPalette.setColor(QPalette::Background, data->softwareTriggerTriggered ? Qt::green : Qt::red); - swTriggerStatus->setPalette(triggerLabelPalette); - swTriggerStatus->setVisible(true); - } - - updateRecordLength(data.get()->sampleCount()); - - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - if (scope->voltage[channel].used && data.get()->data(channel)) { - // Amplitude string representation (4 significant digits) - measurementAmplitudeLabel[channel]->setText( - valueToString(data.get()->data(channel)->computeAmplitude(), UNIT_VOLTS, 4)); - // Frequency string representation (5 significant digits) - measurementFrequencyLabel[channel]->setText( - valueToString(data.get()->data(channel)->frequency, UNIT_HERTZ, 5)); +void DsoWidget::showNew( std::shared_ptr< PPresult > analysedData ) { + mainScope->showData( analysedData ); + zoomScope->showData( analysedData ); + + QPalette triggerLabelPalette = palette(); + if ( scope->trigger.mode == Dso::TriggerMode::ROLL ) { + triggerLabelPalette.setColor( QPalette::WindowText, view->colors->background ); + triggerLabelPalette.setColor( QPalette::Background, view->colors->background ); + } else { + triggerLabelPalette.setColor( QPalette::WindowText, Qt::black ); + triggerLabelPalette.setColor( QPalette::Background, analysedData->softwareTriggerTriggered ? Qt::green : Qt::red ); + } + swTriggerStatus->setPalette( triggerLabelPalette ); + swTriggerStatus->setVisible( true ); + updateRecordLength( dotsOnScreen ); + pulseWidth1 = analysedData.get()->data( 0 )->pulseWidth1; + pulseWidth2 = analysedData.get()->data( 0 )->pulseWidth2; + updateTriggerDetails(); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + if ( scope->voltage[ channel ].used && analysedData.get()->data( channel ) ) { + // Vpp Amplitude string representation (3 significant digits) + measurementVppLabel[ channel ]->setText( valueToString( analysedData.get()->data( channel )->vpp, UNIT_VOLTS, 3 ) + + tr( "pp" ) ); + // RMS Amplitude string representation (3 significant digits) + measurementRMSLabel[ channel ]->setText( valueToString( analysedData.get()->data( channel )->rms, UNIT_VOLTS, 3 ) + + tr( "rms" ) ); + // DC Amplitude string representation (3 significant digits) + measurementDCLabel[ channel ]->setText( valueToString( analysedData.get()->data( channel )->dc, UNIT_VOLTS, 3 ) + "=" ); + // AC Amplitude string representation (3 significant digits) + measurementACLabel[ channel ]->setText( valueToString( analysedData.get()->data( channel )->ac, UNIT_VOLTS, 3 ) + "~" ); + // dB Amplitude string representation (3 significant digits) + measurementdBLabel[ channel ]->setText( valueToString( analysedData.get()->data( channel )->dB, UNIT_DECIBEL, 3 ) ); + // Frequency string representation (3 significant digits) + measurementFrequencyLabel[ channel ]->setText( + valueToString( analysedData.get()->data( channel )->frequency, UNIT_HERTZ, 4 ) ); + // Highlight clipped channel + QPalette validPalette; + if ( analysedData.get()->data( channel )->valid ) { // normal display + validPalette.setColor( QPalette::WindowText, view->colors->voltage[ channel ] ); + validPalette.setColor( QPalette::Background, view->colors->background ); + } else { // warning + validPalette.setColor( QPalette::WindowText, Qt::black ); + validPalette.setColor( QPalette::Background, Qt::red ); + } + measurementNameLabel[ channel ]->setPalette( validPalette ); } } } -void DsoWidget::showEvent(QShowEvent *event) { - QWidget::showEvent(event); + +void DsoWidget::showEvent( QShowEvent *event ) { + QWidget::showEvent( event ); // Apply settings and update measured values updateTriggerDetails(); - updateRecordLength(scope->horizontal.recordLength); - updateFrequencybase(scope->horizontal.frequencybase); - updateSamplerate(scope->horizontal.samplerate); - updateTimebase(scope->horizontal.timebase); - updateZoom(view->zoom); + updateRecordLength( scope->horizontal.recordLength ); + updateFrequencybase( scope->horizontal.frequencybase ); + updateSamplerate( scope->horizontal.samplerate ); + updateTimebase( scope->horizontal.timebase ); + updateZoom( view->zoom ); updateTriggerSource(); - adaptTriggerPositionSlider(); + adaptTriggerOffsetSlider(); } + /// \brief Handles valueChanged signal from the offset sliders. /// \param channel The channel whose offset was changed. /// \param value The new offset for the channel. -void DsoWidget::updateOffset(ChannelID channel, double value) { - if (channel < scope->voltage.size()) { - scope->voltage[channel].offset = value; +void DsoWidget::updateOffset( ChannelID channel, double value ) { + if ( channel < scope->voltage.size() ) { + scope->voltage[ channel ].offset = value; - if (channel < spec->channels) { - adaptTriggerLevelSlider(mainSliders, channel); - adaptTriggerLevelSlider(zoomSliders, channel); + if ( channel < spec->channels ) { + adaptTriggerLevelSlider( mainSliders, channel ); + adaptTriggerLevelSlider( zoomSliders, channel ); } - } else if (channel < scope->voltage.size() * 2) - scope->spectrum[channel - scope->voltage.size()].offset = value; - - if (channel < scope->voltage.size() * 2) { - if (mainSliders.offsetSlider->value(channel) != value) { - const QSignalBlocker blocker(mainSliders.offsetSlider); - mainSliders.offsetSlider->setValue(channel, value); + } else if ( channel < scope->voltage.size() * 2 ) + scope->spectrum[ channel - scope->voltage.size() ].offset = value; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if ( channel < scope->voltage.size() * 2 ) { + if ( mainSliders.voltageOffsetSlider->value( int( channel ) ) != value ) { // double != comparison is safe in this case + const QSignalBlocker blocker( mainSliders.voltageOffsetSlider ); + mainSliders.voltageOffsetSlider->setValue( int( channel ), value ); } - if (zoomSliders.offsetSlider->value(channel) != value) { - const QSignalBlocker blocker(zoomSliders.offsetSlider); - zoomSliders.offsetSlider->setValue(channel, value); + if ( zoomSliders.voltageOffsetSlider->value( int( channel ) ) != value ) { // double != comparison is safe in this case + const QSignalBlocker blocker( zoomSliders.voltageOffsetSlider ); + zoomSliders.voltageOffsetSlider->setValue( int( channel ), value ); } } - - emit offsetChanged(channel, value); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + emit voltageOffsetChanged( channel, value ); } + /// \brief Translate horizontal position (0..1) from main view to zoom view. -double DsoWidget::mainToZoom(double position) const { - double m1 = scope->getMarker(0); - double m2 = scope->getMarker(1); - if (m1 > m2) std::swap(m1, m2); - return ((position - 0.5) * DIVS_TIME - m1) / (m2 - m1); +double DsoWidget::mainToZoom( double position ) const { + double m1 = scope->getMarker( 0 ); + double m2 = scope->getMarker( 1 ); + if ( m1 > m2 ) + std::swap( m1, m2 ); + return ( ( position - 0.5 ) * DIVS_TIME - m1 ) / std::max( m2 - m1, 1e-9 ); } + /// \brief Translate horizontal position (0..1) from zoom view to main view. -double DsoWidget::zoomToMain(double position) const { - double m1 = scope->getMarker(0); - double m2 = scope->getMarker(1); - if (m1 > m2) std::swap(m1, m2); - return 0.5 + (m1 + position * (m2 - m1)) / DIVS_TIME; +double DsoWidget::zoomToMain( double position ) const { + double m1 = scope->getMarker( 0 ); + double m2 = scope->getMarker( 1 ); + if ( m1 > m2 ) + std::swap( m1, m2 ); + return 0.5 + ( m1 + position * ( m2 - m1 ) ) / DIVS_TIME; } + /// \brief Handles signals affecting trigger position in the zoom view. -void DsoWidget::adaptTriggerPositionSlider() { - double value = mainToZoom(scope->trigger.position); +void DsoWidget::adaptTriggerOffsetSlider() { + double value = mainToZoom( scope->trigger.offset ); LevelSlider &slider = *zoomSliders.triggerPositionSlider; - const QSignalBlocker blocker(slider); - if (slider.minimum(0) <= value && value <= slider.maximum(0)) { - slider.setEnabled(true); - slider.setValue(0, value); + const QSignalBlocker blocker( slider ); + if ( slider.minimum( 0 ) <= value && value <= slider.maximum( 0 ) ) { + slider.setEnabled( true ); + slider.setValue( 0, value ); } else { - slider.setEnabled(false); - if (value < slider.minimum(0)) { - slider.setValue(0, slider.minimum(0)); + slider.setEnabled( false ); + if ( value < slider.minimum( 0 ) ) { + slider.setValue( 0, slider.minimum( 0 ) ); } else { - slider.setValue(0, slider.maximum(0)); + slider.setValue( 0, slider.maximum( 0 ) ); } } } + /// \brief Handles valueChanged signal from the triggerPosition slider. /// \param index The index of the slider. /// \param value The new triggerPosition in seconds relative to the first /// sample. -void DsoWidget::updateTriggerPosition(int index, double value, bool mainView) { - if (index != 0) return; +void DsoWidget::updateTriggerOffset( int index, double value, bool mainView ) { + if ( index != 0 ) + return; - if (mainView) { - scope->trigger.position = value; - adaptTriggerPositionSlider(); + if ( mainView ) { + scope->trigger.offset = value; + adaptTriggerOffsetSlider(); } else { - scope->trigger.position = zoomToMain(value); - const QSignalBlocker blocker(mainSliders.triggerPositionSlider); - mainSliders.triggerPositionSlider->setValue(index, scope->trigger.position); + scope->trigger.offset = zoomToMain( value ); + const QSignalBlocker blocker( mainSliders.triggerPositionSlider ); + mainSliders.triggerPositionSlider->setValue( index, scope->trigger.offset ); } updateTriggerDetails(); updateMarkerDetails(); - emit triggerPositionChanged(scope->trigger.position * scope->horizontal.timebase * DIVS_TIME); + emit triggerPositionChanged( scope->trigger.offset ); } + /// \brief Handles valueChanged signal from the trigger level slider. /// \param channel The index of the slider. /// \param value The new trigger level. -void DsoWidget::updateTriggerLevel(ChannelID channel, double value) { - scope->voltage[channel].trigger = value; - - if (mainSliders.triggerLevelSlider->value(channel) != value) { - const QSignalBlocker blocker(mainSliders.triggerLevelSlider); - mainSliders.triggerLevelSlider->setValue(channel, value); +void DsoWidget::updateTriggerLevel( ChannelID channel, double value ) { + // printf("DW::updateTriggerLevel( %d, %g )\n", channel, value); + scope->voltage[ channel ].trigger = value; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if ( mainSliders.triggerLevelSlider->value( int( channel ) ) != value ) { // double != comparison is safe in this case + const QSignalBlocker blocker( mainSliders.triggerLevelSlider ); + mainSliders.triggerLevelSlider->setValue( int( channel ), value ); } - if (zoomSliders.triggerLevelSlider->value(channel) != value) { - const QSignalBlocker blocker(zoomSliders.triggerLevelSlider); - zoomSliders.triggerLevelSlider->setValue(channel, value); + if ( zoomSliders.triggerLevelSlider->value( int( channel ) ) != value ) { // double != comparison is safe in this case + const QSignalBlocker blocker( zoomSliders.triggerLevelSlider ); + zoomSliders.triggerLevelSlider->setValue( int( channel ), value ); } - +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif updateTriggerDetails(); - emit triggerLevelChanged(channel, value); + emit triggerLevelChanged( channel, value ); } + /// \brief Handles valueChanged signal from the marker slider. /// \param marker The index of the slider. /// \param value The new marker position. -void DsoWidget::updateMarker(int marker, double value) { - scope->setMarker(marker, value); - adaptTriggerPositionSlider(); +void DsoWidget::updateMarker( unsigned marker, double value ) { + scope->setMarker( marker, value ); + adaptTriggerOffsetSlider(); + updateMarkerDetails(); +} + + +/// \brief Update the sliders settings. +void DsoWidget::updateSlidersSettings() { + // The offset sliders for all possible channels + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + updateOffset( channel, scope->voltage[ channel ].offset ); + mainSliders.voltageOffsetSlider->setColor( ( channel ), view->colors->voltage[ channel ] ); + mainSliders.voltageOffsetSlider->setValue( int( channel ), scope->voltage[ channel ].offset ); + mainSliders.voltageOffsetSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); + zoomSliders.voltageOffsetSlider->setColor( ( channel ), view->colors->voltage[ channel ] ); + zoomSliders.voltageOffsetSlider->setValue( int( channel ), scope->voltage[ channel ].offset ); + zoomSliders.voltageOffsetSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); + + updateOffset( unsigned( scope->voltage.size() + channel ), scope->spectrum[ channel ].offset ); + mainSliders.voltageOffsetSlider->setColor( unsigned( scope->voltage.size() ) + channel, view->colors->spectrum[ channel ] ); + mainSliders.voltageOffsetSlider->setValue( int( scope->voltage.size() + channel ), scope->spectrum[ channel ].offset ); + mainSliders.voltageOffsetSlider->setIndexVisible( unsigned( scope->voltage.size() ) + channel, + scope->spectrum[ channel ].used ); + zoomSliders.voltageOffsetSlider->setColor( unsigned( scope->voltage.size() ) + channel, view->colors->spectrum[ channel ] ); + zoomSliders.voltageOffsetSlider->setValue( int( scope->voltage.size() + channel ), scope->spectrum[ channel ].offset ); + zoomSliders.voltageOffsetSlider->setIndexVisible( unsigned( scope->voltage.size() ) + channel, + scope->spectrum[ channel ].used ); + } + + // The trigger position slider + mainSliders.triggerPositionSlider->setValue( 0, scope->trigger.offset ); + updateTriggerOffset( 0, scope->trigger.offset, true ); // main slider + updateTriggerOffset( 0, mainToZoom( scope->trigger.offset ), false ); // zoom slider + + // The sliders for the trigger levels + for ( ChannelID channel = 0; channel < spec->channels; ++channel ) { + mainSliders.triggerLevelSlider->setValue( int( channel ), scope->voltage[ channel ].trigger ); + adaptTriggerLevelSlider( mainSliders, channel ); + mainSliders.triggerLevelSlider->setColor( channel, ( channel == scope->trigger.source ) + ? view->colors->voltage[ channel ] + : view->colors->voltage[ channel ].darker() ); + mainSliders.triggerLevelSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); + zoomSliders.triggerLevelSlider->setValue( int( channel ), scope->voltage[ channel ].trigger ); + adaptTriggerLevelSlider( zoomSliders, channel ); + zoomSliders.triggerLevelSlider->setColor( channel, ( channel == scope->trigger.source ) + ? view->colors->voltage[ channel ] + : view->colors->voltage[ channel ].darker() ); + zoomSliders.triggerLevelSlider->setIndexVisible( channel, scope->voltage[ channel ].used ); + } + updateTriggerDetails(); + + // The marker slider + for ( int marker = 0; marker < 2; ++marker ) { + mainSliders.markerSlider->setValue( marker, scope->horizontal.cursor.pos[ marker ].x() ); + } updateMarkerDetails(); } diff --git a/openhantek/src/dsowidget.h b/openhantek/src/dsowidget.h index e1aca1f5..c581b5db 100644 --- a/openhantek/src/dsowidget.h +++ b/openhantek/src/dsowidget.h @@ -2,15 +2,16 @@ #pragma once +#include #include #include #include -#include #include #include "glscope.h" -#include "levelslider.h" #include "hantekdso/controlspecification.h" +#include "levelslider.h" +#include "viewsettings.h" class SpectrumGenerator; struct DsoSettingsScope; @@ -23,9 +24,8 @@ class DsoWidget : public QWidget { Q_OBJECT public: - struct Sliders { - LevelSlider *offsetSlider; ///< The sliders for the graph offsets + LevelSlider *voltageOffsetSlider; ///< The sliders for the graph offsets LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger LevelSlider *triggerLevelSlider; ///< The sliders for the trigger level LevelSlider *markerSlider; ///< The sliders for the markers @@ -36,38 +36,41 @@ class DsoWidget : public QWidget { /// \param dataAnalyzer The data analyzer that should be used as data source. /// \param parent The parent widget. /// \param flags Flags for the window manager. - DsoWidget(DsoSettingsScope* scope, DsoSettingsView* view, const Dso::ControlSpecification* spec, QWidget *parent = 0, Qt::WindowFlags flags = 0); + DsoWidget( DsoSettingsScope *scope, DsoSettingsView *view, const Dso::ControlSpecification *spec, QWidget *parent = nullptr ); // Data arrived - void showNew(std::shared_ptr data); + void showNew( std::shared_ptr< PPresult > analysedData ); + + void switchToPrintColors(); + void restoreScreenColors(); protected: - virtual void showEvent(QShowEvent *event); - void setupSliders(Sliders &sliders); - void adaptTriggerLevelSlider(DsoWidget::Sliders &sliders, ChannelID channel); - void adaptTriggerPositionSlider(); - void setMeasurementVisible(ChannelID channel); + virtual void showEvent( QShowEvent *event ); + void setupSliders( Sliders &sliders ); + void adaptTriggerLevelSlider( DsoWidget::Sliders &sliders, ChannelID channel ); + void adaptTriggerOffsetSlider(); + void setMeasurementVisible( ChannelID channel ); void updateMarkerDetails(); - void updateSpectrumDetails(ChannelID channel); + void updateSpectrumDetails( ChannelID channel ); void updateTriggerDetails(); - void updateVoltageDetails(ChannelID channel); + void updateVoltageDetails( ChannelID channel ); - double mainToZoom(double position) const; - double zoomToMain(double position) const; + double mainToZoom( double position ) const; + double zoomToMain( double position ) const; Sliders mainSliders; Sliders zoomSliders; - QGridLayout *mainLayout; ///< The main layout for this widget + QGridLayout *mainLayout; ///< The main layout for this widget QHBoxLayout *settingsLayout; ///< The table for the settings info QLabel *settingsTriggerLabel; ///< The trigger details - QLabel *settingsRecordLengthLabel; ///< The record length + QLabel *settingsSamplesOnScreen; ///< The displayed dots on screen QLabel *settingsSamplerateLabel; ///< The samplerate QLabel *settingsTimebaseLabel; ///< The timebase of the main scope QLabel *settingsFrequencybaseLabel; ///< The frequencybase of the main scope - QLabel *swTriggerStatus; ///< The status of SW trigger + QLabel *swTriggerStatus; ///< The status of SW trigger QHBoxLayout *markerLayout; ///< The table for the marker details QLabel *markerInfoLabel; ///< The info about the zoom factor @@ -76,29 +79,41 @@ class DsoWidget : public QWidget { QLabel *markerTimebaseLabel; ///< The timebase for the zoomed scope QLabel *markerFrequencybaseLabel; ///< The frequencybase for the zoomed scope - QGridLayout *measurementLayout; ///< The table for the signal details - std::vector measurementNameLabel; ///< The name of the channel - std::vector measurementGainLabel; ///< The gain for the voltage (V/div) - std::vector measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div) - std::vector measurementMiscLabel; ///< Coupling or math mode - std::vector measurementAmplitudeLabel; ///< Amplitude of the signal (V) - std::vector measurementFrequencyLabel; ///< Frequency of the signal (Hz) + QGridLayout *measurementLayout; ///< The table for the signal details + std::vector< QLabel * > measurementNameLabel; ///< The name of the channel + std::vector< QLabel * > measurementGainLabel; ///< The gain for the voltage (V/div) + std::vector< QLabel * > measurementMagnitudeLabel; ///< The magnitude for the spectrum (dB/div) + std::vector< QLabel * > measurementMiscLabel; ///< Coupling or math mode + std::vector< QLabel * > measurementVppLabel; ///< Peak-to-peak amplitude of the signal (V) + std::vector< QLabel * > measurementRMSLabel; ///< RMS Amplitude of the signal (V) = sqrt( DC² + AC² ) + std::vector< QLabel * > measurementDCLabel; ///< DC Amplitude of the signal (V) + std::vector< QLabel * > measurementACLabel; ///< AC Amplitude of the signal (V) + std::vector< QLabel * > measurementdBLabel; ///< AC Amplitude in dB + std::vector< QLabel * > measurementFrequencyLabel; ///< Frequency of the signal (Hz) DataGrid *cursorDataGrid; - DsoSettingsScope* scope; - DsoSettingsView* view; - const Dso::ControlSpecification* spec; + DsoSettingsScope *scope; + DsoSettingsView *view; + const Dso::ControlSpecification *spec; + + GlScope *mainScope; ///< The main scope screen + GlScope *zoomScope; ///< The optional magnified scope screen - GlScope *mainScope; ///< The main scope screen - GlScope *zoomScope; ///< The optional magnified scope screen + private: + double samplerate; + double timebase; + unsigned int dotsOnScreen; + double pulseWidth1 = 0.0; + double pulseWidth2 = 0.0; + void setColors(); public slots: // Horizontal axis // void horizontalFormatChanged(HorizontalFormat format); - void updateFrequencybase(double frequencybase); - void updateSamplerate(double samplerate); - void updateTimebase(double timebase); + void updateFrequencybase( double frequencybase ); + void updateSamplerate( double samplerate ); + void updateTimebase( double timebase ); // Trigger void updateTriggerMode(); @@ -106,32 +121,35 @@ class DsoWidget : public QWidget { void updateTriggerSource(); // Spectrum - void updateSpectrumMagnitude(ChannelID channel); - void updateSpectrumUsed(ChannelID channel, bool used); + void updateSpectrumMagnitude( ChannelID channel ); + void updateSpectrumUsed( ChannelID channel, bool used ); // Vertical axis - void updateVoltageCoupling(ChannelID channel); + void updateVoltageCoupling( ChannelID channel ); void updateMathMode(); - void updateVoltageGain(ChannelID channel); - void updateVoltageUsed(ChannelID channel, bool used); + void updateVoltageGain( ChannelID channel ); + void updateVoltageUsed( ChannelID channel, bool used ); // Menus - void updateRecordLength(unsigned long size); + void updateRecordLength( unsigned long size ); + + // Scope control + void updateZoom( bool enabled ); + void updateCursorGrid( bool enabled ); // Scope control - void updateZoom(bool enabled); - void updateCursorGrid(bool enabled); + void updateSlidersSettings(); private slots: // Sliders - void updateOffset(ChannelID channel, double value); - void updateTriggerPosition(int index, double value, bool mainView = true); - void updateTriggerLevel(ChannelID channel, double value); - void updateMarker(int marker, double value); + void updateOffset( ChannelID channel, double value ); + void updateTriggerOffset( int index, double value, bool mainView = true ); + void updateTriggerLevel( ChannelID channel, double value ); + void updateMarker( unsigned marker, double value ); signals: // Sliders - void offsetChanged(ChannelID channel, double value); ///< A graph offset has been changed - void triggerPositionChanged(double value); ///< The pretrigger has been changed - void triggerLevelChanged(ChannelID channel, double value); ///< A trigger level has been changed + void voltageOffsetChanged( ChannelID channel, double value ); ///< A graph offset has been changed + void triggerPositionChanged( double value ); ///< The pretrigger has been changed + void triggerLevelChanged( ChannelID channel, double value ); ///< A trigger level has been changed }; diff --git a/openhantek/src/exporting/exportcsv.cpp b/openhantek/src/exporting/exportcsv.cpp index d543bbf6..1e7eb305 100644 --- a/openhantek/src/exporting/exportcsv.cpp +++ b/openhantek/src/exporting/exportcsv.cpp @@ -1,102 +1,113 @@ // SPDX-License-Identifier: GPL-2.0+ #include "exportcsv.h" +#include "dsosettings.h" #include "exporterregistry.h" -#include "post/ppresult.h" -#include "settings.h" #include "iconfont/QtAwesome.h" +#include "post/ppresult.h" #include -#include #include #include +#include +#include ExporterCSV::ExporterCSV() {} -void ExporterCSV::create(ExporterRegistry *registry) { this->registry = registry; data.reset(); } +void ExporterCSV::create( ExporterRegistry *newRegistry ) { + this->registry = newRegistry; + data.reset(); +} -QIcon ExporterCSV::icon() { return iconFont->icon(fa::filetexto); } +int ExporterCSV::faIcon() { return fa::filetexto; } -QString ExporterCSV::name() { return QCoreApplication::tr("Export CSV"); } +QString ExporterCSV::name() { return tr( "Export &CSV .." ); } ExporterInterface::Type ExporterCSV::type() { return Type::SnapshotExport; } -bool ExporterCSV::samples(const std::shared_ptr data) { - this->data = std::move(data); +bool ExporterCSV::samples( const std::shared_ptr< PPresult > newData ) { + data = std::move( newData ); return false; } bool ExporterCSV::save() { - QStringList filters; - filters << QCoreApplication::tr("Comma-Separated Values (*.csv)"); - - QFileDialog fileDialog(nullptr, QCoreApplication::tr("Export file..."), QString(), filters.join(";;")); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - if (fileDialog.exec() != QDialog::Accepted) return false; + QFileDialog fileDialog( nullptr, tr( "Save CSV" ), QString(), tr( "Comma-Separated Values (*.csv)" ) ); + fileDialog.setFileMode( QFileDialog::AnyFile ); + fileDialog.setAcceptMode( QFileDialog::AcceptSave ); + if ( fileDialog.exec() != QDialog::Accepted ) + return false; - QFile csvFile(fileDialog.selectedFiles().first()); - if (!csvFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false; + QFile csvFile( fileDialog.selectedFiles().first() ); + if ( !csvFile.open( QIODevice::WriteOnly | QIODevice::Text ) ) + return false; - QTextStream csvStream(&csvFile); - csvStream.setRealNumberNotation(QTextStream::FixedNotation); - csvStream.setRealNumberPrecision(10); + QTextStream csvStream( &csvFile ); + csvStream.setRealNumberNotation( QTextStream::FixedNotation ); + csvStream.setRealNumberPrecision( 10 ); size_t chCount = registry->settings->scope.voltage.size(); - std::vector voltageData(size_t(chCount), nullptr); - std::vector spectrumData(size_t(chCount), nullptr); + std::vector< const SampleValues * > voltageData( size_t( chCount ), nullptr ); + std::vector< const SampleValues * > spectrumData( size_t( chCount ), nullptr ); size_t maxRow = 0; bool isSpectrumUsed = false; double timeInterval = 0; double freqInterval = 0; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (data->data(channel)) { - if (registry->settings->scope.voltage[channel].used) { - voltageData[channel] = &(data->data(channel)->voltage); - maxRow = std::max(maxRow, voltageData[channel]->sample.size()); - timeInterval = data->data(channel)->voltage.interval; + // use semicolon as data separator if comma is already used as decimal separator - e.g. with german locale + const char *sep = QLocale::system().decimalPoint() == ',' ? ";" : ","; + + for ( ChannelID channel = 0; channel < chCount; ++channel ) { + if ( data->data( channel ) ) { + if ( registry->settings->scope.voltage[ channel ].used ) { + voltageData[ channel ] = &( data->data( channel )->voltage ); + maxRow = std::max( maxRow, voltageData[ channel ]->sample.size() ); + timeInterval = data->data( channel )->voltage.interval; } - if (registry->settings->scope.spectrum[channel].used) { - spectrumData[channel] = &(data->data(channel)->spectrum); - maxRow = std::max(maxRow, spectrumData[channel]->sample.size()); - freqInterval = data->data(channel)->spectrum.interval; + if ( registry->settings->scope.spectrum[ channel ].used ) { + spectrumData[ channel ] = &( data->data( channel )->spectrum ); + maxRow = std::max( maxRow, spectrumData[ channel ]->sample.size() ); + freqInterval = data->data( channel )->spectrum.interval; isSpectrumUsed = true; } } } // Start with channel names - csvStream << "\"t\""; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (voltageData[channel] != nullptr) { csvStream << ",\"" << registry->settings->scope.voltage[channel].name << "\""; } + csvStream << "\"t / s\""; + for ( ChannelID channel = 0; channel < chCount; ++channel ) { + if ( voltageData[ channel ] != nullptr ) { + csvStream << sep << "\"" << registry->settings->scope.voltage[ channel ].name << " / V\""; + } } - if (isSpectrumUsed) { - csvStream << ",\"f\""; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (spectrumData[channel] != nullptr) { - csvStream << ",\"" << registry->settings->scope.spectrum[channel].name << "\""; + if ( isSpectrumUsed ) { + csvStream << sep << "\"f / Hz\""; + for ( ChannelID channel = 0; channel < chCount; ++channel ) { + if ( spectrumData[ channel ] != nullptr ) { + csvStream << sep << "\"" << registry->settings->scope.spectrum[ channel ].name << " / dB\""; } } } csvStream << "\n"; - for (unsigned int row = 0; row < maxRow; ++row) { - - csvStream << timeInterval * row; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (voltageData[channel] != nullptr) { - csvStream << ","; - if (row < voltageData[channel]->sample.size()) { csvStream << voltageData[channel]->sample[row]; } + for ( unsigned int row = 0; row < maxRow; ++row ) { + csvStream << QLocale::system().toString( timeInterval * row ); + for ( ChannelID channel = 0; channel < chCount; ++channel ) { + if ( voltageData[ channel ] != nullptr ) { + csvStream << sep; + if ( row < voltageData[ channel ]->sample.size() ) { + csvStream << QLocale::system().toString( voltageData[ channel ]->sample[ row ] ); + } } } - if (isSpectrumUsed) { - csvStream << "," << freqInterval * row; - for (ChannelID channel = 0; channel < chCount; ++channel) { - if (spectrumData[channel] != nullptr) { - csvStream << ","; - if (row < spectrumData[channel]->sample.size()) { csvStream << spectrumData[channel]->sample[row]; } + if ( isSpectrumUsed ) { + csvStream << sep << QLocale::system().toString( freqInterval * row ); + for ( ChannelID channel = 0; channel < chCount; ++channel ) { + if ( spectrumData[ channel ] != nullptr ) { + csvStream << sep; + if ( row < spectrumData[ channel ]->sample.size() ) { + csvStream << QLocale::system().toString( spectrumData[ channel ]->sample[ row ] ); + } } } } @@ -108,4 +119,5 @@ bool ExporterCSV::save() { return true; } + float ExporterCSV::progress() { return data ? 1.0f : 0; } diff --git a/openhantek/src/exporting/exportcsv.h b/openhantek/src/exporting/exportcsv.h index 09a2278c..89eb9ada 100644 --- a/openhantek/src/exporting/exportcsv.h +++ b/openhantek/src/exporting/exportcsv.h @@ -3,17 +3,19 @@ #pragma once #include "exporterinterface.h" -class ExporterCSV : public ExporterInterface -{ -public: +class ExporterCSV : public ExporterInterface { + Q_DECLARE_TR_FUNCTIONS( LegacyExportDrawer ) + + public: ExporterCSV(); - virtual void create(ExporterRegistry *registry) override; - virtual QIcon icon() override; - virtual QString name() override; - virtual Type type() override; - virtual bool samples(const std::shared_ptrdata) override; - virtual bool save() override; - virtual float progress() override; -private: - std::shared_ptr data; + void create( ExporterRegistry *registry ) override; + int faIcon() override; + QString name() override; + Type type() override; + bool samples( const std::shared_ptr< PPresult > newData ) override; + bool save() override; + float progress() override; + + private: + std::shared_ptr< PPresult > data; }; diff --git a/openhantek/src/exporting/exporterinterface.cpp b/openhantek/src/exporting/exporterinterface.cpp new file mode 100644 index 00000000..3c1c30b1 --- /dev/null +++ b/openhantek/src/exporting/exporterinterface.cpp @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "exporterinterface.h" + +ExporterInterface::~ExporterInterface() {} diff --git a/openhantek/src/exporting/exporterinterface.h b/openhantek/src/exporting/exporterinterface.h index 2720d102..674a6f72 100644 --- a/openhantek/src/exporting/exporterinterface.h +++ b/openhantek/src/exporting/exporterinterface.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include @@ -15,22 +16,22 @@ class PPresult; * in the main routine to make an Exporter available. */ class ExporterInterface { -public: - + public: + virtual ~ExporterInterface(); /** - * Starts up this exporter. Aquires resources etc. Do not call this directly, it - * will be called by the exporter registry at some point. Release your resources in the - * destructor as usual. - * @param registry The exporter registry instance. This is used to obtain a reference - * to the settings. - */ - virtual void create(ExporterRegistry *registry) = 0; + * Starts up this exporter. Aquires resources etc. Do not call this directly, it + * will be called by the exporter registry at some point. Release your resources in the + * destructor as usual. + * @param registry The exporter registry instance. This is used to obtain a reference + * to the settings. + */ + virtual void create( ExporterRegistry *registry ) = 0; /** * @return Return the icon representation of this exporter. Will be used in graphical * interfaces. */ - virtual QIcon icon() = 0; + virtual int faIcon() = 0; /** * @return Return the text representation / name of this exporter. Will be used in graphical @@ -54,7 +55,7 @@ class ExporterInterface { * If it is a snapshot exporter, only one set of samples will be received. * @return Return true if you want to receive another sample or false if you are done (progres()==1). */ - virtual bool samples(const std::shared_ptr) = 0; + virtual bool samples( const std::shared_ptr< PPresult > ) = 0; /** * Exporter: Save your received data and perform any conversions necessary. @@ -73,6 +74,7 @@ class ExporterInterface { * A continous exporter may report the used memory / reservered memory ratio here. */ virtual float progress() = 0; -protected: + + protected: ExporterRegistry *registry; }; diff --git a/openhantek/src/exporting/exporterprocessor.cpp b/openhantek/src/exporting/exporterprocessor.cpp index 7e435500..71c52dd3 100644 --- a/openhantek/src/exporting/exporterprocessor.cpp +++ b/openhantek/src/exporting/exporterprocessor.cpp @@ -1,6 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "exporterprocessor.h" #include "exporterregistry.h" -ExporterProcessor::ExporterProcessor(ExporterRegistry *registry) : registry(registry) {} +ExporterProcessor::ExporterProcessor( ExporterRegistry *registry ) : registry( registry ) {} -void ExporterProcessor::process(PPresult *data) { registry->addRawSamples(data); } +void ExporterProcessor::process( PPresult *data ) { registry->addRawSamples( data ); } diff --git a/openhantek/src/exporting/exporterprocessor.h b/openhantek/src/exporting/exporterprocessor.h index a920ac9f..3250a890 100644 --- a/openhantek/src/exporting/exporterprocessor.h +++ b/openhantek/src/exporting/exporterprocessor.h @@ -1,14 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "post/processor.h" class ExporterRegistry; -class ExporterProcessor : public Processor -{ -public: - ExporterProcessor(ExporterRegistry* registry); - virtual void process(PPresult *) override; -private: - ExporterRegistry* registry; +class ExporterProcessor : public Processor { + public: + explicit ExporterProcessor( ExporterRegistry *registry ); + void process( PPresult * ) override; + + private: + ExporterRegistry *registry; }; diff --git a/openhantek/src/exporting/exporterregistry.cpp b/openhantek/src/exporting/exporterregistry.cpp index 249eba36..230e6581 100644 --- a/openhantek/src/exporting/exporterregistry.cpp +++ b/openhantek/src/exporting/exporterregistry.cpp @@ -6,79 +6,80 @@ #include #include "controlspecification.h" +#include "dsosettings.h" #include "post/ppresult.h" -#include "settings.h" -ExporterRegistry::ExporterRegistry(const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings, - QObject *parent) - : QObject(parent), deviceSpecification(deviceSpecification), settings(settings) {} +ExporterRegistry::ExporterRegistry( const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings, QObject *parent ) + : QObject( parent ), deviceSpecification( deviceSpecification ), settings( settings ) {} -bool ExporterRegistry::processData(std::shared_ptr &data, ExporterInterface *const &exporter) { - if (!exporter->samples(data)) { - waitToSaveExporters.insert(exporter); +bool ExporterRegistry::processData( std::shared_ptr< PPresult > &data, ExporterInterface *const &exporter ) { + if ( !exporter->samples( data ) ) { + waitToSaveExporters.insert( exporter ); emit exporterProgressChanged(); return true; } return false; } -void ExporterRegistry::addRawSamples(PPresult *d) { - if (settings->exporting.useProcessedSamples) return; - std::shared_ptr data(d); - enabledExporters.remove_if([&data, this](ExporterInterface *const &i) { return processData(data, i); }); +void ExporterRegistry::addRawSamples( PPresult *d ) { + if ( settings->exportProcessedSamples ) + return; + std::shared_ptr< PPresult > data( d ); + enabledExporters.remove_if( [&data, this]( ExporterInterface *const &i ) { return processData( data, i ); } ); } -void ExporterRegistry::input(std::shared_ptr data) { - if (!settings->exporting.useProcessedSamples) return; - enabledExporters.remove_if([&data, this](ExporterInterface *const &i) { return processData(data, i); }); +void ExporterRegistry::input( std::shared_ptr< PPresult > data ) { + if ( !settings->exportProcessedSamples ) + return; + enabledExporters.remove_if( [&data, this]( ExporterInterface *const &i ) { return processData( data, i ); } ); } -void ExporterRegistry::registerExporter(ExporterInterface *exporter) { - exporters.push_back(exporter); - exporter->create(this); +void ExporterRegistry::registerExporter( ExporterInterface *exporter ) { + exporters.push_back( exporter ); + exporter->create( this ); } -void ExporterRegistry::setExporterEnabled(ExporterInterface *exporter, bool enabled) { +void ExporterRegistry::setExporterEnabled( ExporterInterface *exporter, bool enabled ) { bool wasInList = false; - enabledExporters.remove_if([exporter, &wasInList](ExporterInterface *inlist) { - if (inlist == exporter) { + enabledExporters.remove_if( [exporter, &wasInList]( ExporterInterface *inlist ) { + if ( inlist == exporter ) { wasInList = true; return true; } else return false; - }); + } ); - if (enabled) { + if ( enabled ) { // If the exporter was waiting for the user save confirmation, // reset it instead. - auto localFind = waitToSaveExporters.find(exporter); - if (localFind != waitToSaveExporters.end()) { - waitToSaveExporters.erase(localFind); - exporter->create(this); + auto localFind = waitToSaveExporters.find( exporter ); + if ( localFind != waitToSaveExporters.end() ) { + waitToSaveExporters.erase( localFind ); + exporter->create( this ); } - enabledExporters.push_back(exporter); - } else if (wasInList) { + enabledExporters.push_back( exporter ); + } else if ( wasInList ) { // If exporter made some progress: Add to waiting for GUI list - if (exporter->progress() > 0) { - waitToSaveExporters.insert(exporter); + if ( exporter->progress() > 0 ) { + waitToSaveExporters.insert( exporter ); emit exporterProgressChanged(); } else // Reset exporter - exporter->create(this); + exporter->create( this ); } } void ExporterRegistry::checkForWaitingExporters() { - for (ExporterInterface *exporter : waitToSaveExporters) { - if (exporter->save()) { - emit exporterStatusChanged(exporter->name(), tr("Data saved")); + for ( ExporterInterface *exporter : waitToSaveExporters ) { + if ( exporter->save() ) { + emit exporterStatusChanged( exporter->name(), tr( "Data saved" ) ); } else { - emit exporterStatusChanged(exporter->name(), tr("No data exported")); + emit exporterStatusChanged( exporter->name(), tr( "No data exported" ) ); } - exporter->create(this); + exporter->create( this ); } waitToSaveExporters.clear(); } -std::vector::const_iterator ExporterRegistry::begin() { return exporters.begin(); } +std::vector< ExporterInterface * >::const_iterator ExporterRegistry::begin() { return exporters.begin(); } -std::vector::const_iterator ExporterRegistry::end() { return exporters.end(); } +std::vector< ExporterInterface * >::const_iterator ExporterRegistry::end() { return exporters.end(); } diff --git a/openhantek/src/exporting/exporterregistry.h b/openhantek/src/exporting/exporterregistry.h index fe9fae14..2c978ef3 100644 --- a/openhantek/src/exporting/exporterregistry.h +++ b/openhantek/src/exporting/exporterregistry.h @@ -22,23 +22,24 @@ class ExporterInterface; class ExporterRegistry : public QObject { Q_OBJECT + public: - explicit ExporterRegistry(const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings, - QObject *parent = nullptr); + explicit ExporterRegistry( const Dso::ControlSpecification *deviceSpecification, DsoSettings *settings, + QObject *parent = nullptr ); // Sample input. This will proably be performed in the post processing // thread context. Do not open GUI dialogs or interrupt the control flow. - void addRawSamples(PPresult *data); - void input(std::shared_ptr data); + void addRawSamples( PPresult *data ); + void input( std::shared_ptr< PPresult > data ); - void registerExporter(ExporterInterface *exporter); - void setExporterEnabled(ExporterInterface *exporter, bool enabled); + void registerExporter( ExporterInterface *exporter ); + void setExporterEnabled( ExporterInterface *exporter, bool enabled ); void checkForWaitingExporters(); // Iterate over this class object - std::vector::const_iterator begin(); - std::vector::const_iterator end(); + std::vector< ExporterInterface * >::const_iterator begin(); + std::vector< ExporterInterface * >::const_iterator end(); /// Device specifications const Dso::ControlSpecification *deviceSpecification; @@ -46,19 +47,20 @@ class ExporterRegistry : public QObject { private: /// List of all available exporters - std::vector exporters; + std::vector< ExporterInterface * > exporters; /// List of exporters that collect samples at the moment - std::list enabledExporters; + std::list< ExporterInterface * > enabledExporters; /// List of exporters that wait to be called back by the user to save their work - std::set waitToSaveExporters; + std::set< ExporterInterface * > waitToSaveExporters; /// Process data from addRawSamples() or input() in the given exporter. Add the /// exporter to waitToSaveExporters if it finishes. /// /// @return Return true if the exporter has finished and want to be removed from the /// enabledExporters list. - bool processData(std::shared_ptr &data, ExporterInterface *const &exporter); + bool processData( std::shared_ptr< PPresult > &data, ExporterInterface *const &exporter ); + signals: - void exporterStatusChanged(const QString &exporterName, const QString &status); + void exporterStatusChanged( const QString &exporterName, const QString &status ); void exporterProgressChanged(); }; diff --git a/openhantek/src/exporting/exportimage.cpp b/openhantek/src/exporting/exportimage.cpp deleted file mode 100644 index 9139251d..00000000 --- a/openhantek/src/exporting/exportimage.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include "exportimage.h" -#include "exporterregistry.h" -#include "iconfont/QtAwesome.h" -#include "legacyexportdrawer.h" -#include "post/ppresult.h" -#include "settings.h" -#include "viewsettings.h" - -#include -#include -#include - -ExporterImage::ExporterImage() {} - -void ExporterImage::create(ExporterRegistry *registry) { this->registry = registry; data.reset(); } - -QIcon ExporterImage::icon() { return iconFont->icon(fa::image); } - -QString ExporterImage::name() { return QCoreApplication::tr("Export Image/PDF"); } - -ExporterInterface::Type ExporterImage::type() { return Type::SnapshotExport; } - -bool ExporterImage::samples(const std::shared_ptr data) { - this->data = std::move(data); - return false; -} - -bool ExporterImage::save() { - QStringList filters; - filters << QCoreApplication::tr("Portable Document Format (*.pdf)") - << QCoreApplication::tr("Image (*.png *.xpm *.jpg)"); - - QFileDialog fileDialog(nullptr, QCoreApplication::tr("Export file..."), QString(), filters.join(";;")); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - if (fileDialog.exec() != QDialog::Accepted) return false; - - bool isPdf = filters.indexOf(fileDialog.selectedNameFilter()) == 0; - std::unique_ptr paintDevice; - - const DsoSettingsColorValues *colorValues = &(registry->settings->view.print); - - if (!isPdf) { - // We need a QPixmap for image-export - QPixmap *qPixmap = new QPixmap(registry->settings->exporting.imageSize); - qPixmap->fill(colorValues->background); - paintDevice = std::unique_ptr(qPixmap); - } else { - // We need a QPrinter for printing, pdf- and ps-export - std::unique_ptr printer = std::unique_ptr(new QPrinter(QPrinter::HighResolution)); - printer->setOrientation(registry->settings->view.zoom ? QPrinter::Portrait : QPrinter::Landscape); - printer->setPageMargins(20, 20, 20, 20, QPrinter::Millimeter); - printer->setOutputFileName(fileDialog.selectedFiles().first()); - printer->setOutputFormat(QPrinter::PdfFormat); - paintDevice = std::move(printer); - } - - if (!paintDevice) return false; - - LegacyExportDrawer::exportSamples(data.get(), paintDevice.get(), registry->deviceSpecification, registry->settings, - false, colorValues); - - if (!isPdf) static_cast(paintDevice.get())->save(fileDialog.selectedFiles().first()); - return true; -} - -float ExporterImage::progress() { return data ? 1.0f : 0; } diff --git a/openhantek/src/exporting/exportimage.h b/openhantek/src/exporting/exportimage.h deleted file mode 100644 index b8dd2f26..00000000 --- a/openhantek/src/exporting/exportimage.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once -#include "exporterinterface.h" - -class ExporterImage : public ExporterInterface -{ -public: - ExporterImage(); - virtual void create(ExporterRegistry *registry) override; - virtual QIcon icon() override; - virtual QString name() override; - virtual Type type() override; - virtual bool samples(const std::shared_ptrdata) override; - virtual bool save() override; - virtual float progress() override; -private: - std::shared_ptr data; -}; diff --git a/openhantek/src/exporting/exportprint.cpp b/openhantek/src/exporting/exportprint.cpp deleted file mode 100644 index 4b4b81b7..00000000 --- a/openhantek/src/exporting/exportprint.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include "exportprint.h" -#include "exporterregistry.h" -#include "iconfont/QtAwesome.h" -#include "legacyexportdrawer.h" -#include "post/ppresult.h" -#include "settings.h" -#include "viewsettings.h" - -#include -#include -#include - -ExporterPrint::ExporterPrint() {} - -void ExporterPrint::create(ExporterRegistry *registry) { this->registry = registry; data.reset(); } - -QIcon ExporterPrint::icon() { return iconFont->icon(fa::print); } - -QString ExporterPrint::name() { return QCoreApplication::tr("Print"); } - -ExporterInterface::Type ExporterPrint::type() { return Type::SnapshotExport; } - -bool ExporterPrint::samples(const std::shared_ptr data) { - this->data = std::move(data); - return false; -} - -bool ExporterPrint::save() { - // We need a QPrinter for printing, pdf- and ps-export - std::unique_ptr printer = std::unique_ptr(new QPrinter(QPrinter::HighResolution)); - printer->setOrientation(registry->settings->view.zoom ? QPrinter::Portrait : QPrinter::Landscape); - printer->setPageMargins(20, 20, 20, 20, QPrinter::Millimeter); - - // Show the printing dialog - QPrintDialog dialog(printer.get()); - dialog.setWindowTitle(QCoreApplication::tr("Print oscillograph")); - if (dialog.exec() != QDialog::Accepted) { return false; } - - const DsoSettingsColorValues *colorValues = &(registry->settings->view.print); - - LegacyExportDrawer::exportSamples(data.get(), printer.get(), registry->deviceSpecification, registry->settings, - true, colorValues); - - return true; -} - -float ExporterPrint::progress() { return data ? 1.0f : 0; } diff --git a/openhantek/src/exporting/exportprint.h b/openhantek/src/exporting/exportprint.h deleted file mode 100644 index 51fcd135..00000000 --- a/openhantek/src/exporting/exportprint.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once -#include "exporterinterface.h" - -class ExporterPrint : public ExporterInterface -{ -public: - ExporterPrint(); - virtual void create(ExporterRegistry *registry) override; - virtual QIcon icon() override; - virtual QString name() override; - virtual Type type() override; - virtual bool samples(const std::shared_ptrdata) override; - virtual bool save() override; - virtual float progress() override; -private: - std::shared_ptr data; -}; diff --git a/openhantek/src/exporting/exportsettings.h b/openhantek/src/exporting/exportsettings.h deleted file mode 100644 index 996873ed..00000000 --- a/openhantek/src/exporting/exportsettings.h +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include - -/// \brief Holds the export options of the program. -struct DsoSettingsExport { - QSize imageSize = QSize(640, 480); ///< Size of exported images in pixels - unsigned exportSizeBytes = 1024*1024*10; ///< For exporters that save a continous stream. Default: 10 Megabytes - bool useProcessedSamples = true; ///< Export raw or processed samples -}; diff --git a/openhantek/src/exporting/legacyexportdrawer.cpp b/openhantek/src/exporting/legacyexportdrawer.cpp deleted file mode 100644 index 20cb7ba4..00000000 --- a/openhantek/src/exporting/legacyexportdrawer.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include -#include -#include - -#include -#include -#include - -#include "legacyexportdrawer.h" - -#include "controlspecification.h" -#include "post/graphgenerator.h" -#include "post/ppresult.h" -#include "settings.h" -#include "utils/printutils.h" -#include "viewconstants.h" - -#define tr(msg) QCoreApplication::translate("Exporter", msg) - -bool LegacyExportDrawer::exportSamples(const PPresult *result, QPaintDevice* paintDevice, - const Dso::ControlSpecification *deviceSpecification, - const DsoSettings *settings, bool isPrinter, const DsoSettingsColorValues *colorValues) { - // Create a painter for our device - QPainter painter(paintDevice); - - // Get line height - QFont font; - QFontMetrics fontMetrics(font, paintDevice); - double lineHeight = fontMetrics.height(); - - painter.setBrush(Qt::SolidPattern); - - // Draw the settings table - double stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; - - // Print trigger details - painter.setPen(colorValues->voltage[settings->scope.trigger.source]); - QString levelString = valueToString(settings->scope.voltage[settings->scope.trigger.source].trigger, UNIT_VOLTS, 3); - QString pretriggerString = tr("%L1%").arg((int)(settings->scope.trigger.position * 100 + 0.5)); - painter.drawText(QRectF(0, 0, lineHeight * 10, lineHeight), - tr("%1 %2 %3 %4") - .arg(settings->scope.voltage[settings->scope.trigger.source].name, - Dso::slopeString(settings->scope.trigger.slope), levelString, pretriggerString)); - - double scopeHeight; - - { // DataAnalyser mutex lock - // Print sample count - painter.setPen(colorValues->text); - painter.drawText(QRectF(lineHeight * 10, 0, stretchBase, lineHeight), tr("%1 S").arg(result->sampleCount()), - QTextOption(Qt::AlignRight)); - // Print samplerate - painter.drawText(QRectF(lineHeight * 10 + stretchBase, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.samplerate, UNIT_SAMPLES) + tr("/s"), - QTextOption(Qt::AlignRight)); - // Print timebase - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.timebase, UNIT_SECONDS, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - // Print frequencybase - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, 0, stretchBase, lineHeight), - valueToString(settings->scope.horizontal.frequencybase, UNIT_HERTZ, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - - // Draw the measurement table - stretchBase = (double)(paintDevice->width() - lineHeight * 6) / 10; - int channelCount = 0; - for (int channel = settings->scope.voltage.size() - 1; channel >= 0; channel--) { - if ((settings->scope.voltage[channel].used || settings->scope.spectrum[channel].used) && - result->data(channel)) { - ++channelCount; - double top = (double)paintDevice->height() - channelCount * lineHeight; - - // Print label - painter.setPen(colorValues->voltage[channel]); - painter.drawText(QRectF(0, top, lineHeight * 4, lineHeight), settings->scope.voltage[channel].name); - // Print coupling/math mode - if ((unsigned int)channel < deviceSpecification->channels) - painter.drawText(QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), - Dso::couplingString(settings->scope.coupling(channel, deviceSpecification))); - else - painter.drawText( - QRectF(lineHeight * 4, top, lineHeight * 2, lineHeight), - Dso::mathModeString(Dso::getMathMode(settings->scope.voltage[channel]))); - - // Print voltage gain - painter.drawText(QRectF(lineHeight * 6, top, stretchBase * 2, lineHeight), - valueToString(settings->scope.gain(channel), UNIT_VOLTS, 0) + tr("/div"), - QTextOption(Qt::AlignRight)); - // Print spectrum magnitude - if (settings->scope.spectrum[channel].used) { - painter.setPen(colorValues->spectrum[channel]); - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 2, top, stretchBase * 2, lineHeight), - valueToString(settings->scope.spectrum[channel].magnitude, UNIT_DECIBEL, 0) + - tr("/div"), - QTextOption(Qt::AlignRight)); - } - - // Amplitude string representation (4 significant digits) - painter.setPen(colorValues->text); - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 4, top, stretchBase * 3, lineHeight), - valueToString(result->data(channel)->computeAmplitude(), UNIT_VOLTS, 4), - QTextOption(Qt::AlignRight)); - // Frequency string representation (5 significant digits) - painter.drawText(QRectF(lineHeight * 6 + stretchBase * 7, top, stretchBase * 3, lineHeight), - valueToString(result->data(channel)->frequency, UNIT_HERTZ, 5), - QTextOption(Qt::AlignRight)); - } - } - - // Draw the marker table - stretchBase = (double)(paintDevice->width() - lineHeight * 10) / 4; - painter.setPen(colorValues->text); - - // Calculate variables needed for zoomed scope - double m1 = settings->scope.getMarker(0); - double m2 = settings->scope.getMarker(1); - double divs = fabs(m2 - m1); - double time = divs * settings->scope.horizontal.timebase; - double zoomFactor = DIVS_TIME / divs; - double zoomOffset = (m1 + m2) / 2; - - if (settings->view.zoom) { - scopeHeight = (double)(paintDevice->height() - (channelCount + 5) * lineHeight) / 2; - double top = 2.5 * lineHeight + scopeHeight; - - painter.drawText(QRectF(0, top, stretchBase, lineHeight), - tr("Zoom x%L1").arg(DIVS_TIME / divs, -1, 'g', 3)); - - painter.drawText(QRectF(lineHeight * 10, top, stretchBase, lineHeight), - valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase, top, stretchBase, lineHeight), - valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); - - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase, lineHeight), - valueToString(time / DIVS_TIME, UNIT_SECONDS, 3) + tr("/div"), - QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 3, top, stretchBase, lineHeight), - valueToString(divs * settings->scope.horizontal.frequencybase / DIVS_TIME, UNIT_HERTZ, 3) + - tr("/div"), - QTextOption(Qt::AlignRight)); - } else { - scopeHeight = (double)paintDevice->height() - (channelCount + 4) * lineHeight; - double top = 2.5 * lineHeight + scopeHeight; - - painter.drawText(QRectF(0, top, stretchBase, lineHeight), tr("Marker 1/2")); - - painter.drawText(QRectF(lineHeight * 10, top, stretchBase * 2, lineHeight), - valueToString(time, UNIT_SECONDS, 4), QTextOption(Qt::AlignRight)); - painter.drawText(QRectF(lineHeight * 10 + stretchBase * 2, top, stretchBase * 2, lineHeight), - valueToString(1.0 / time, UNIT_HERTZ, 4), QTextOption(Qt::AlignRight)); - } - - // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph - painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(paintDevice->width() - 1) / 2, (scopeHeight - 1) / 2 + lineHeight * 1.5), - false); - - // Draw the graphs - painter.setRenderHint(QPainter::Antialiasing); - painter.setBrush(Qt::NoBrush); - - for (int zoomed = 0; zoomed < (settings->view.zoom ? 2 : 1); ++zoomed) { - switch (settings->scope.horizontal.format) { - case Dso::GraphFormat::TY: - // Add graphs for channels - for (ChannelID channel = 0; channel < settings->scope.voltage.size(); ++channel) { - if (settings->scope.voltage[channel].used && result->data(channel)) { - painter.setPen(QPen(colorValues->voltage[channel], 0)); - - // What's the horizontal distance between sampling points? - double horizontalFactor = - result->data(channel)->voltage.interval / settings->scope.horizontal.timebase; - // How many samples are visible? - double centerPosition, centerOffset; - if (zoomed) { - centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; - } else { - centerPosition = DIVS_TIME / 2 / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / 2; - } - unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); - unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), - (int)result->data(channel)->voltage.sample.size() - 1); - - // Draw graph - QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; - - for (unsigned int position = firstPosition; position <= lastPosition; ++position) - graph[position - firstPosition] = QPointF(position * horizontalFactor - DIVS_TIME / 2, - result->data(channel)->voltage.sample[position] / - settings->scope.gain(channel) + - settings->scope.voltage[channel].offset); - - painter.drawPolyline(graph, lastPosition - firstPosition + 1); - delete[] graph; - } - } - - // Add spectrum graphs - for (ChannelID channel = 0; channel < settings->scope.spectrum.size(); ++channel) { - if (settings->scope.spectrum[channel].used && result->data(channel)) { - painter.setPen(QPen(colorValues->spectrum[channel], 0)); - - // What's the horizontal distance between sampling points? - double horizontalFactor = - result->data(channel)->spectrum.interval / settings->scope.horizontal.frequencybase; - // How many samples are visible? - double centerPosition, centerOffset; - if (zoomed) { - centerPosition = (zoomOffset + DIVS_TIME / 2) / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / zoomFactor / 2; - } else { - centerPosition = DIVS_TIME / 2 / horizontalFactor; - centerOffset = DIVS_TIME / horizontalFactor / 2; - } - unsigned int firstPosition = qMax((int)(centerPosition - centerOffset), 0); - unsigned int lastPosition = qMin((int)(centerPosition + centerOffset), - (int)result->data(channel)->spectrum.sample.size() - 1); - - // Draw graph - QPointF *graph = new QPointF[lastPosition - firstPosition + 1]; - - for (unsigned int position = firstPosition; position <= lastPosition; ++position) - graph[position - firstPosition] = - QPointF(position * horizontalFactor - DIVS_TIME / 2, - result->data(channel)->spectrum.sample[position] / - settings->scope.spectrum[channel].magnitude + - settings->scope.spectrum[channel].offset); - - painter.drawPolyline(graph, lastPosition - firstPosition + 1); - delete[] graph; - } - } - break; - - case Dso::GraphFormat::XY: - break; - - default: - break; - } - - // Set DIVS_TIME / zoomFactor x DIVS_VOLTAGE matrix for zoomed - // oscillograph - painter.setMatrix(QMatrix((paintDevice->width() - 1) / DIVS_TIME * zoomFactor, 0, 0, - -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(paintDevice->width() - 1) / 2 - - zoomOffset * zoomFactor * (paintDevice->width() - 1) / DIVS_TIME, - (scopeHeight - 1) * 1.5 + lineHeight * 4), - false); - } - } // dataanalyser mutex release - - drawGrids(painter, colorValues, lineHeight, scopeHeight, paintDevice->width(), - isPrinter, settings->view.zoom); - painter.end(); - - return true; -} - -void LegacyExportDrawer::drawGrids(QPainter &painter, const DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, - int scopeWidth, bool isPrinter, bool zoom) { - painter.setRenderHint(QPainter::Antialiasing, false); - for (int zoomed = 0; zoomed < (zoom ? 2 : 1); ++zoomed) { - // Set DIVS_TIME x DIVS_VOLTAGE matrix for oscillograph - painter.setMatrix(QMatrix((scopeWidth - 1) / DIVS_TIME, 0, 0, -(scopeHeight - 1) / DIVS_VOLTAGE, - (double)(scopeWidth - 1) / 2, - (scopeHeight - 1) * (zoomed + 0.5) + lineHeight * 1.5 + lineHeight * 2.5 * zoomed), - false); - - // Grid lines - painter.setPen(QPen(colorValues->grid, 0)); - - if (isPrinter) { - // Draw vertical lines - for (int div = 1; div < DIVS_TIME / 2; ++div) { - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { - painter.drawLine(QPointF((double)-div - 0.02, (double)-dot / 5), - QPointF((double)-div + 0.02, (double)-dot / 5)); - painter.drawLine(QPointF((double)-div - 0.02, (double)dot / 5), - QPointF((double)-div + 0.02, (double)dot / 5)); - painter.drawLine(QPointF((double)div - 0.02, (double)-dot / 5), - QPointF((double)div + 0.02, (double)-dot / 5)); - painter.drawLine(QPointF((double)div - 0.02, (double)dot / 5), - QPointF((double)div + 0.02, (double)dot / 5)); - } - } - // Draw horizontal lines - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { - for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { - painter.drawLine(QPointF((double)-dot / 5, (double)-div - 0.02), - QPointF((double)-dot / 5, (double)-div + 0.02)); - painter.drawLine(QPointF((double)dot / 5, (double)-div - 0.02), - QPointF((double)dot / 5, (double)-div + 0.02)); - painter.drawLine(QPointF((double)-dot / 5, (double)div - 0.02), - QPointF((double)-dot / 5, (double)div + 0.02)); - painter.drawLine(QPointF((double)dot / 5, (double)div - 0.02), - QPointF((double)dot / 5, (double)div + 0.02)); - } - } - } else { - // Draw vertical lines - for (int div = 1; div < DIVS_TIME / 2; ++div) { - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * 5; ++dot) { - painter.drawPoint(QPointF(-div, (double)-dot / 5)); - painter.drawPoint(QPointF(-div, (double)dot / 5)); - painter.drawPoint(QPointF(div, (double)-dot / 5)); - painter.drawPoint(QPointF(div, (double)dot / 5)); - } - } - // Draw horizontal lines - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { - for (int dot = 1; dot < DIVS_TIME / 2 * 5; ++dot) { - if (dot % 5 == 0) continue; // Already done by vertical lines - painter.drawPoint(QPointF((double)-dot / 5, -div)); - painter.drawPoint(QPointF((double)dot / 5, -div)); - painter.drawPoint(QPointF((double)-dot / 5, div)); - painter.drawPoint(QPointF((double)dot / 5, div)); - } - } - } - - // Axes - painter.setPen(QPen(colorValues->axes, 0)); - painter.drawLine(QPointF(-DIVS_TIME / 2, 0), QPointF(DIVS_TIME / 2, 0)); - painter.drawLine(QPointF(0, -DIVS_VOLTAGE / 2), QPointF(0, DIVS_VOLTAGE / 2)); - for (double div = 0.2; div <= DIVS_TIME / 2; div += 0.2) { - painter.drawLine(QPointF(div, -0.05), QPointF(div, 0.05)); - painter.drawLine(QPointF(-div, -0.05), QPointF(-div, 0.05)); - } - for (double div = 0.2; div <= DIVS_VOLTAGE / 2; div += 0.2) { - painter.drawLine(QPointF(-0.05, div), QPointF(0.05, div)); - painter.drawLine(QPointF(-0.05, -div), QPointF(0.05, -div)); - } - - // Borders - painter.setPen(QPen(colorValues->border, 0)); - painter.drawRect(QRectF(-DIVS_TIME / 2, -DIVS_VOLTAGE / 2, DIVS_TIME, DIVS_VOLTAGE)); - } -} diff --git a/openhantek/src/exporting/legacyexportdrawer.h b/openhantek/src/exporting/legacyexportdrawer.h deleted file mode 100644 index e9d3d3ce..00000000 --- a/openhantek/src/exporting/legacyexportdrawer.h +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include -#include -#include -#include "exportsettings.h" - -class DsoSettings; -class PPresult; -struct DsoSettingsColorValues; -namespace Dso { struct ControlSpecification; } - -/// \brief Exports the oscilloscope screen to a file or prints it. -/// TODO -/// Rewrite image exporter with OpenGL drawn grid and graphs -/// -/// Sources: -/// http://doc.qt.io/qt-5/qoffscreensurface.html -/// http://doc.qt.io/qt-5/qopenglframebufferobject.html -/// -/// https://dangelog.wordpress.com/2013/02/10/using-fbos-instead-of-pbuffers-in-qt-5-2/ -class LegacyExportDrawer { - public: - /// Draw the graphs coming from source and labels to the destination paintdevice. - static bool exportSamples(const PPresult *source, QPaintDevice* dest, - const Dso::ControlSpecification* deviceSpecification, - const DsoSettings *settings, bool isPrinter, const DsoSettingsColorValues *colorValues); - - private: - static void drawGrids(QPainter &painter, const DsoSettingsColorValues *colorValues, double lineHeight, double scopeHeight, - int scopeWidth, bool isPrinter, bool zoom); -}; diff --git a/openhantek/src/exporting/readme.md b/openhantek/src/exporting/readme.md index 5c81a388..18f98726 100644 --- a/openhantek/src/exporting/readme.md +++ b/openhantek/src/exporting/readme.md @@ -1,7 +1,8 @@ # Content This directory contains exporting functionality and exporters, namely -* Export to comma separated value file (CSV): Write to a user selected file, +* Export to comma separated value file (CSV): Write to a user selected file, +use localisation for data and decimal separator * Export to an image/pdf: Writes an image/pdf to a user selected file, * Print exporter: Creates a printable document and opens the print dialog. diff --git a/openhantek/src/glscope.cpp b/openhantek/src/glscope.cpp index f6b0e71f..4f15090e 100644 --- a/openhantek/src/glscope.cpp +++ b/openhantek/src/glscope.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include "glscope.h" @@ -22,168 +23,270 @@ #include "viewconstants.h" #include "viewsettings.h" -GlScope *GlScope::createNormal(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) { - GlScope *s = new GlScope(scope, view, parent); + +GlScope *GlScope::createNormal( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent ) { + GlScope *s = new GlScope( scope, view, parent ); s->zoomed = false; return s; } -GlScope *GlScope::createZoomed(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) { - GlScope *s = new GlScope(scope, view, parent); + +GlScope *GlScope::createZoomed( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent ) { + GlScope *s = new GlScope( scope, view, parent ); s->zoomed = true; return s; } -void GlScope::fixOpenGLversion(QSurfaceFormat::RenderableType t) { - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); + +void GlScope::useQSurfaceFormat( QSurfaceFormat::RenderableType t ) { + QCoreApplication::setAttribute( Qt::AA_ShareOpenGLContexts, true ); // Prefer full desktop OpenGL without fixed pipeline QSurfaceFormat format; - format.setSamples(4); // Antia-Aliasing, Multisampling - format.setProfile(QSurfaceFormat::CoreProfile); - if (t==QSurfaceFormat::OpenGLES) { - format.setVersion(2, 0); - format.setRenderableType(QSurfaceFormat::OpenGLES); - QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); + // TODO: check from time to time if warning has gone, comment next line for now + format.setSamples( 4 ); // antialiasing gives warning with some HW, Qt & OpenGL versions. + format.setProfile( QSurfaceFormat::CoreProfile ); + if ( t == QSurfaceFormat::OpenGLES ) { + format.setVersion( 2, 0 ); + format.setRenderableType( QSurfaceFormat::OpenGLES ); + QCoreApplication::setAttribute( Qt::AA_UseOpenGLES, true ); } else { - format.setVersion(3, 2); - format.setRenderableType(QSurfaceFormat::OpenGL); + format.setVersion( 3, 2 ); + format.setRenderableType( QSurfaceFormat::OpenGL ); } - QSurfaceFormat::setDefaultFormat(format); + QSurfaceFormat::setDefaultFormat( format ); } -GlScope::GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) - : QOpenGLWidget(parent), scope(scope), view(view) { + +GlScope::GlScope( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent ) + : QOpenGLWidget( parent ), scope( scope ), view( view ) { + // get OpenGL version to define appropriate OpenGLSL version + // reason: + // some not so new intel graphic driver report a very conservative version + // e.g. debian buster -> "2.1 Mesa 18.3.4" + QOffscreenSurface surface; + surface.create(); + QOpenGLContext context; + context.create(); + context.makeCurrent( &surface ); + QString glVersion = reinterpret_cast< const char * >( context.functions()->glGetString( GL_VERSION ) ); + GLSLversion = glVersion >= "3.2" ? 150 : 120; // version string "3.2 xxxx" > "3.2" is true + // qDebug() << "OpenGL version" << glVersion << GLSLversion; + surface.destroy(); + cursorInfo.clear(); - cursorInfo.push_back(&scope->horizontal.cursor); + cursorInfo.push_back( &scope->horizontal.cursor ); selectedCursor = 0; - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - cursorInfo.push_back(&scope->voltage[channel].cursor); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + cursorInfo.push_back( &scope->voltage[ channel ].cursor ); } - for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { - cursorInfo.push_back(&scope->spectrum[channel].cursor); + for ( ChannelID channel = 0; channel < scope->spectrum.size(); ++channel ) { + cursorInfo.push_back( &scope->spectrum[ channel ].cursor ); } - vaMarker.resize(cursorInfo.size()); + vaMarker.resize( cursorInfo.size() ); +} + + +GlScope::~GlScope() { /* virtual destructor necessary */ } -GlScope::~GlScope() {/* virtual destructor necessary */} -QPointF GlScope::eventToPosition(QMouseEvent *event) { - QPointF position((double)(event->x() - width() / 2) * DIVS_TIME / (double)width(), - (double)(height() / 2 - event->y()) * DIVS_VOLTAGE / (double)height()); - if (zoomed) { - double m1 = scope->getMarker(0); - double m2 = scope->getMarker(1); - if (m1 > m2) std::swap(m1, m2); - position.setX(m1 + (0.5 + (position.x() / DIVS_TIME)) * (m2 - m1)); +QPointF GlScope::posToPosition( QPointF pos ) { + QPointF position( double( pos.x() - width() / 2 ) * DIVS_TIME / double( width() ), + double( height() / 2 - pos.y() ) * DIVS_VOLTAGE / double( height() ) ); + if ( zoomed ) { + double m1 = scope->getMarker( 0 ); + double m2 = scope->getMarker( 1 ); + if ( m1 > m2 ) + std::swap( m1, m2 ); + position.setX( m1 + ( 0.5 + ( position.x() / DIVS_TIME ) ) * ( m2 - m1 ) ); } return position; } -void GlScope::mousePressEvent(QMouseEvent *event) { - if (!(zoomed && selectedCursor == 0) && event->button() == Qt::LeftButton) { - QPointF position = eventToPosition(event); + +void GlScope::mousePressEvent( QMouseEvent *event ) { + if ( !( zoomed && selectedCursor == 0 ) && event->button() == Qt::LeftButton ) { + QPointF position = posToPosition( event->pos() ); selectedMarker = NO_MARKER; - DsoSettingsScopeCursor *cursor = cursorInfo[selectedCursor]; + DsoSettingsScopeCursor *cursor = cursorInfo[ selectedCursor ]; // Capture nearest marker located within snap area (+/- 1% of full scale). - double dX0 = fabs(cursor->pos[0].x() - position.x()); - double dX1 = fabs(cursor->pos[1].x() - position.x()); - double dY0 = fabs(cursor->pos[0].y() - position.y()); - double dY1 = fabs(cursor->pos[1].y() - position.y()); + double dX0 = fabs( cursor->pos[ 0 ].x() - position.x() ); + double dX1 = fabs( cursor->pos[ 1 ].x() - position.x() ); + double dY0 = fabs( cursor->pos[ 0 ].y() - position.y() ); + double dY1 = fabs( cursor->pos[ 1 ].y() - position.y() ); - switch (cursor->shape) { + switch ( cursor->shape ) { case DsoSettingsScopeCursor::RECTANGULAR: - if (std::min(dX0, dX1) < 1.0 / DIVS_SUB && std::min(dY0, dY1) < 1.0 / DIVS_SUB) { + if ( std::min( dX0, dX1 ) < 1.0 / DIVS_SUB && std::min( dY0, dY1 ) < 1.0 / DIVS_SUB ) { // Do we need to swap Y-coords? - if ((dX0 < dX1 && dY0 > dY1) || (dX0 > dX1 && dY0 < dY1)) { - std::swap(cursor->pos[0].ry(), cursor->pos[1].ry()); + if ( ( dX0 < dX1 && dY0 > dY1 ) || ( dX0 > dX1 && dY0 < dY1 ) ) { + std::swap( cursor->pos[ 0 ].ry(), cursor->pos[ 1 ].ry() ); } - selectedMarker = (dX0 < dX1) ? 0 : 1; + selectedMarker = ( dX0 < dX1 ) ? 0 : 1; } break; case DsoSettingsScopeCursor::VERTICAL: - if (dX0 < dX1) { - if (dX0 < 1.0 / DIVS_SUB) selectedMarker = 0; + if ( dX0 < dX1 ) { + if ( dX0 < 1.0 / DIVS_SUB ) + selectedMarker = 0; } else { - if (dX1 < 1.0 / DIVS_SUB) selectedMarker = 1; + if ( dX1 < 1.0 / DIVS_SUB ) + selectedMarker = 1; } break; case DsoSettingsScopeCursor::HORIZONTAL: - if (dY0 < dY1) { - if (dY0 < 1.0 / DIVS_SUB) selectedMarker = 0; + if ( dY0 < dY1 ) { + if ( dY0 < 1.0 / DIVS_SUB ) + selectedMarker = 0; } else { - if (dY1 < 1.0 / DIVS_SUB) selectedMarker = 1; + if ( dY1 < 1.0 / DIVS_SUB ) + selectedMarker = 1; } break; case DsoSettingsScopeCursor::NONE: break; - default: - break; } - if (selectedMarker != NO_MARKER) { - cursorInfo[selectedCursor]->pos[selectedMarker] = position; - if (selectedCursor == 0) emit markerMoved(selectedCursor, selectedMarker); + if ( selectedMarker != NO_MARKER ) { + cursorInfo[ selectedCursor ]->pos[ selectedMarker ] = position; + if ( selectedCursor == 0 ) + emit markerMoved( selectedCursor, selectedMarker ); } } event->accept(); } -void GlScope::mouseMoveEvent(QMouseEvent *event) { - if (!(zoomed && selectedCursor == 0) && (event->buttons() & Qt::LeftButton) != 0) { - QPointF position = eventToPosition(event); - if (selectedMarker == NO_MARKER) { + +void GlScope::mouseMoveEvent( QMouseEvent *event ) { + if ( !( zoomed && selectedCursor == 0 ) && ( event->buttons() & Qt::LeftButton ) != 0 ) { + QPointF position = posToPosition( event->pos() ); + if ( selectedMarker == NO_MARKER ) { + // qDebug() << "mouseMoveEvent"; // User started draging outside the snap area of any marker: // move all markers to current position and select last marker in the array. - for (unsigned marker = 0; marker < MARKER_COUNT; ++marker) { - cursorInfo[selectedCursor]->pos[marker] = position; - emit markerMoved(selectedCursor, marker); + for ( unsigned marker = 0; marker < 2; ++marker ) { + cursorInfo[ selectedCursor ]->pos[ marker ] = position; + emit markerMoved( selectedCursor, marker ); selectedMarker = marker; } - } else if (selectedMarker < MARKER_COUNT) { - cursorInfo[selectedCursor]->pos[selectedMarker] = position; - emit markerMoved(selectedCursor, selectedMarker); + } else if ( selectedMarker < 2 ) { + cursorInfo[ selectedCursor ]->pos[ selectedMarker ] = position; + emit markerMoved( selectedCursor, selectedMarker ); } } event->accept(); } -void GlScope::mouseReleaseEvent(QMouseEvent *event) { - if (!(zoomed && selectedCursor == 0) && event->button() == Qt::LeftButton) { - QPointF position = eventToPosition(event); - if (selectedMarker < MARKER_COUNT) { - cursorInfo[selectedCursor]->pos[selectedMarker] = position; - emit markerMoved(selectedCursor, selectedMarker); + +void GlScope::mouseReleaseEvent( QMouseEvent *event ) { + if ( !( zoomed && selectedCursor == 0 ) && event->button() == Qt::LeftButton ) { + QPointF position = posToPosition( event->pos() ); + if ( selectedMarker < 2 ) { + // qDebug() << "mouseReleaseEvent"; + cursorInfo[ selectedCursor ]->pos[ selectedMarker ] = position; + emit markerMoved( selectedCursor, selectedMarker ); + } + selectedMarker = NO_MARKER; + } + event->accept(); +} + + +void GlScope::mouseDoubleClickEvent( QMouseEvent *event ) { + if ( !( zoomed && selectedCursor == 0 ) && ( event->buttons() & Qt::LeftButton ) != 0 ) { + // left double click positions two markers left and right of clicked pos with zoom=100 + QPointF position = posToPosition( event->pos() ); + if ( selectedMarker == NO_MARKER ) { + // User double clicked outside the snap area of any marker + QPointF p = QPointF( 0.5, 0 ); // 10x zoom + if ( event->modifiers() & Qt::CTRL ) // 100x zoom + p /= 10; + if ( event->modifiers() & Qt::SHIFT ) // center at trigger position + position = QPointF( 10 * scope->trigger.offset - 5, 0 ); + // move 1st marker left of current position. + cursorInfo[ selectedCursor ]->pos[ 0 ] = position - p; + emit markerMoved( selectedCursor, 0 ); + // move 2nd marker right of current position to make zoom=10 or 100. + cursorInfo[ selectedCursor ]->pos[ 1 ] = position + p; + emit markerMoved( selectedCursor, 1 ); + // select no marker + selectedMarker = NO_MARKER; + } + } else if ( !( zoomed && selectedCursor == 0 ) && ( event->buttons() & Qt::RightButton ) != 0 ) { + // right double click moves all markers out of the way + cursorInfo[ selectedCursor ]->pos[ 0 ] = QPointF( MARGIN_LEFT, 0 ); + cursorInfo[ selectedCursor ]->pos[ 1 ] = QPointF( MARGIN_RIGHT, 0 ); + emit markerMoved( selectedCursor, 0 ); + emit markerMoved( selectedCursor, 1 ); + } + event->accept(); +} + + +void GlScope::wheelEvent( QWheelEvent *event ) { + static std::vector< int > zoomList = {1, 2, 5, 10, 20, 50, 100, 200, 500}; + if ( !( zoomed && selectedCursor == 0 ) ) { + if ( selectedMarker == NO_MARKER ) { + double step = event->angleDelta().y() / 1200.0; // one click = 0.1 + // qDebug() << "wheeelEvent" << selectedCursor << event->globalPos() << step; + double &m1 = cursorInfo[ selectedCursor ]->pos[ 0 ].rx(); + double &m2 = cursorInfo[ selectedCursor ]->pos[ 1 ].rx(); + if ( m1 > m2 ) + std::swap( m1, m2 ); + double dm = m2 - m1; + if ( event->modifiers() & Qt::CTRL ) { // zoom in/out + if ( ( step > 0 && dm <= 1 ) || ( step < 0 && dm < 1 ) ) + step *= 0.1; + if ( dm >= 3 * step ) { + m1 += step; + m2 -= step; + } + } else { + if ( step < 0 ) { // shift zoom range left .. + step = qMax( step, MARGIN_LEFT - m1 ); // .. until m1 == MARGIN_LEFT + } else { // shift zoom range right .. + step = qMin( step, MARGIN_RIGHT - m2 ); // .. until m2 == MARGIN_RIGHT + } + m1 += step; + m2 += step; + } } + emit markerMoved( selectedCursor, 0 ); + emit markerMoved( selectedCursor, 1 ); selectedMarker = NO_MARKER; } event->accept(); } -void GlScope::paintEvent(QPaintEvent *event) { - // Draw error message if OpenGL failed - if (!shaderCompileSuccess) { - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing, true); + +void GlScope::paintEvent( QPaintEvent *event ) { + if ( shaderCompileSuccess ) { + QOpenGLWidget::paintEvent( event ); + } else if ( !zoomed ) { // draw error message on normal view if OpenGL failed + QPainter painter( this ); + painter.setRenderHint( QPainter::Antialiasing, true ); QFont font = painter.font(); - font.setPointSize(18); - painter.setFont(font); - painter.drawText(rect(), Qt::AlignCenter, errorMessage); - event->accept(); - } else - QOpenGLWidget::paintEvent(event); + font.setPointSize( 18 ); + painter.setFont( font ); + painter.drawText( rect(), Qt::AlignCenter | Qt::TextWordWrap, errorMessage ); + } + event->accept(); // consume the event } + +unsigned GlScope::forceGLSLversion = 0; + void GlScope::initializeGL() { - if (!QOpenGLShaderProgram::hasOpenGLShaderPrograms(context())) { - errorMessage = tr("System does not support OpenGL Shading Language (GLSL)"); + if ( !QOpenGLShaderProgram::hasOpenGLShaderPrograms( context() ) ) { + errorMessage = tr( "System does not support OpenGL Shading Language (GLSL)" ); return; } - if (m_program) { - qWarning() << "OpenGL init called twice!"; + if ( m_program ) { + qWarning() << tr( "OpenGL init called twice!" ); return; } - auto program = std::unique_ptr(new QOpenGLShaderProgram(context())); + auto program = std::unique_ptr< QOpenGLShaderProgram >( new QOpenGLShaderProgram( context() ) ); const char *vshaderES = R"( #version 100 @@ -201,7 +304,23 @@ void GlScope::initializeGL() { void main() { gl_FragColor = colour; } )"; - const char *vshaderDesktop = R"( + const char *vshaderDesktop120 = R"( + #version 120 + attribute highp vec3 vertex; + uniform mat4 matrix; + void main() + { + gl_Position = matrix * vec4(vertex, 1.0); + gl_PointSize = 1.0; + } + )"; + const char *fshaderDesktop120 = R"( + #version 120 + uniform highp vec4 colour; + void main() { gl_FragColor = colour; } + )"; + + const char *vshaderDesktop150 = R"( #version 150 in highp vec3 vertex; uniform mat4 matrix; @@ -211,383 +330,500 @@ void GlScope::initializeGL() { gl_PointSize = 1.0; } )"; - const char *fshaderDesktop = R"( + const char *fshaderDesktop150 = R"( #version 150 uniform highp vec4 colour; out vec4 flatColor; void main() { flatColor = colour; } )"; - qDebug() << "compile shaders"; + if ( GlScope::forceGLSLversion ) + GLSLversion = GlScope::forceGLSLversion; + // qDebug() << "compile shaders" << GlScope::forceGLSLversion << GLSLversion; + + const char *vshaderDesktop = GLSLversion == 120 ? vshaderDesktop120 : vshaderDesktop150; + const char *fshaderDesktop = GLSLversion == 120 ? fshaderDesktop120 : fshaderDesktop150; + // Compile vertex shader - bool usesOpenGL = QSurfaceFormat::defaultFormat().renderableType()==QSurfaceFormat::OpenGL; - if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, usesOpenGL ? vshaderDesktop : vshaderES) || - !program->addShaderFromSourceCode(QOpenGLShader::Fragment, usesOpenGL ? fshaderDesktop : fshaderES)) { - errorMessage = "Failed to compile OpenGL shader programs.\n" + program->log(); + bool usesOpenGL = QSurfaceFormat::defaultFormat().renderableType() == QSurfaceFormat::OpenGL; + if ( !program->addShaderFromSourceCode( QOpenGLShader::Vertex, usesOpenGL ? vshaderDesktop : vshaderES ) || + !program->addShaderFromSourceCode( QOpenGLShader::Fragment, usesOpenGL ? fshaderDesktop : fshaderES ) ) { + errorMessage = tr( "Failed to compile OpenGL shader programs.\n" ) + program->log(); return; } // Link shader pipeline - if (!program->link() || !program->bind()) { - errorMessage = "Failed to link/bind OpenGL shader programs\n" + program->log(); + if ( !program->link() || !program->bind() ) { + errorMessage = tr( "Failed to link/bind OpenGL shader programs.\n" ) + program->log(); return; } - vertexLocation = program->attributeLocation("vertex"); - matrixLocation = program->uniformLocation("matrix"); - colorLocation = program->uniformLocation("colour"); + vertexLocation = program->attributeLocation( "vertex" ); + matrixLocation = program->uniformLocation( "matrix" ); + colorLocation = program->uniformLocation( "colour" ); - if (vertexLocation == -1 || colorLocation == -1 || matrixLocation == -1) { - qWarning() << "Failed to locate shader variable"; + if ( vertexLocation == -1 || colorLocation == -1 || matrixLocation == -1 ) { + qWarning() << tr( "Failed to locate shader variable." ); return; } program->bind(); auto *gl = context()->functions(); - gl->glDisable(GL_DEPTH_TEST); - gl->glEnable(GL_BLEND); + gl->glDisable( GL_DEPTH_TEST ); + gl->glEnable( GL_BLEND ); // Enable depth buffer - gl->glEnable(GL_DEPTH_TEST); + gl->glEnable( GL_DEPTH_TEST ); // Enable back face culling - gl->glEnable(GL_CULL_FACE); - gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl->glEnable( GL_CULL_FACE ); + gl->glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - QColor bg = view->screen.background; - gl->glClearColor((GLfloat)bg.redF(), (GLfloat)bg.greenF(), (GLfloat)bg.blueF(), (GLfloat)bg.alphaF()); + QColor bg = view->colors->background; + gl->glClearColor( GLfloat( bg.redF() ), GLfloat( bg.greenF() ), GLfloat( bg.blueF() ), GLfloat( bg.alphaF() ) ); - generateGrid(program.get()); { m_vaoMarker.create(); - QOpenGLVertexArrayObject::Binder b(&m_vaoMarker); + QOpenGLVertexArrayObject::Binder b( &m_vaoMarker ); m_marker.create(); m_marker.bind(); - m_marker.setUsagePattern(QOpenGLBuffer::StaticDraw); - m_marker.allocate(int(vaMarker.size() * sizeof(Vertices))); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); + m_marker.setUsagePattern( QOpenGLBuffer::StaticDraw ); + m_marker.allocate( int( vaMarker.size() * sizeof( Vertices ) ) ); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, 0, 3, 0 ); } updateCursor(); - m_program = std::move(program); + m_program = std::move( program ); + + generateGrid(); // initialize the grid draw structures + shaderCompileSuccess = true; } -void GlScope::showData(std::shared_ptr data) { - if (!shaderCompileSuccess) return; + +void GlScope::showData( std::shared_ptr< PPresult > newData ) { + if ( !shaderCompileSuccess ) + return; makeCurrent(); // Remove too much entries - while (view->digitalPhosphorDraws() < m_GraphHistory.size()) m_GraphHistory.pop_back(); + while ( view->digitalPhosphorDraws() < m_GraphHistory.size() ) + m_GraphHistory.pop_back(); // Add if missing - if (view->digitalPhosphorDraws() > m_GraphHistory.size()) { m_GraphHistory.resize(m_GraphHistory.size() + 1); } + if ( view->digitalPhosphorDraws() > m_GraphHistory.size() ) { + m_GraphHistory.resize( m_GraphHistory.size() + 1 ); + } // Move last item to front - m_GraphHistory.splice(m_GraphHistory.begin(), m_GraphHistory, std::prev(m_GraphHistory.end())); + m_GraphHistory.splice( m_GraphHistory.begin(), m_GraphHistory, std::prev( m_GraphHistory.end() ) ); // Add new entry - m_GraphHistory.front().writeData(data.get(), m_program.get(), vertexLocation); + m_GraphHistory.front().writeData( newData.get(), m_program.get(), vertexLocation ); // doneCurrent(); update(); } -void GlScope::generateVertices(unsigned marker, const DsoSettingsScopeCursor &cursor) { + +void GlScope::generateVertices( unsigned marker, const DsoSettingsScopeCursor &cursor ) { const float Z_ORDER = 1.0f; - switch (cursor.shape) { + switch ( cursor.shape ) { case DsoSettingsScopeCursor::NONE: - vaMarker[marker] = { - QVector3D(-DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER), - QVector3D(-DIVS_TIME, DIVS_VOLTAGE, Z_ORDER), - QVector3D( DIVS_TIME, DIVS_VOLTAGE, Z_ORDER), - QVector3D( DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER) - }; + vaMarker[ marker ] = {QVector3D( -DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER ), QVector3D( -DIVS_TIME, DIVS_VOLTAGE, Z_ORDER ), + QVector3D( DIVS_TIME, DIVS_VOLTAGE, Z_ORDER ), QVector3D( DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER )}; break; case DsoSettingsScopeCursor::VERTICAL: - vaMarker[marker] = { - QVector3D(cursor.pos[0].x(), -DIVS_VOLTAGE, Z_ORDER), - QVector3D(cursor.pos[0].x(), DIVS_VOLTAGE, Z_ORDER), - QVector3D(cursor.pos[1].x(), DIVS_VOLTAGE, Z_ORDER), - QVector3D(cursor.pos[1].x(), -DIVS_VOLTAGE, Z_ORDER) - }; + vaMarker[ marker ] = {QVector3D( GLfloat( cursor.pos[ 0 ].x() ), -GLfloat( DIVS_VOLTAGE ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 0 ].x() ), GLfloat( DIVS_VOLTAGE ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), GLfloat( DIVS_VOLTAGE ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), -GLfloat( DIVS_VOLTAGE ), Z_ORDER )}; break; case DsoSettingsScopeCursor::HORIZONTAL: - vaMarker[marker] = { - QVector3D(-DIVS_TIME, cursor.pos[0].y(), Z_ORDER), - QVector3D( DIVS_TIME, cursor.pos[0].y(), Z_ORDER), - QVector3D( DIVS_TIME, cursor.pos[1].y(), Z_ORDER), - QVector3D(-DIVS_TIME, cursor.pos[1].y(), Z_ORDER) - }; + vaMarker[ marker ] = {QVector3D( -GLfloat( DIVS_TIME ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER ), + QVector3D( GLfloat( DIVS_TIME ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER ), + QVector3D( GLfloat( DIVS_TIME ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER ), + QVector3D( -GLfloat( DIVS_TIME ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER )}; break; case DsoSettingsScopeCursor::RECTANGULAR: - if ((cursor.pos[1].x() - cursor.pos[0].x()) * (cursor.pos[1].y() - cursor.pos[0].y()) > 0.0) { - vaMarker[marker] = { - QVector3D(cursor.pos[0].x(), cursor.pos[0].y(), Z_ORDER), - QVector3D(cursor.pos[1].x(), cursor.pos[0].y(), Z_ORDER), - QVector3D(cursor.pos[1].x(), cursor.pos[1].y(), Z_ORDER), - QVector3D(cursor.pos[0].x(), cursor.pos[1].y(), Z_ORDER) - }; + if ( ( cursor.pos[ 1 ].x() - cursor.pos[ 0 ].x() ) * ( cursor.pos[ 1 ].y() - cursor.pos[ 0 ].y() ) > 0.0 ) { + vaMarker[ marker ] = {QVector3D( GLfloat( cursor.pos[ 0 ].x() ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 0 ].x() ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER )}; } else { - vaMarker[marker] = { - QVector3D(cursor.pos[0].x(), cursor.pos[0].y(), Z_ORDER), - QVector3D(cursor.pos[0].x(), cursor.pos[1].y(), Z_ORDER), - QVector3D(cursor.pos[1].x(), cursor.pos[1].y(), Z_ORDER), - QVector3D(cursor.pos[1].x(), cursor.pos[0].y(), Z_ORDER) - }; + vaMarker[ marker ] = {QVector3D( GLfloat( cursor.pos[ 0 ].x() ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 0 ].x() ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), GLfloat( cursor.pos[ 1 ].y() ), Z_ORDER ), + QVector3D( GLfloat( cursor.pos[ 1 ].x() ), GLfloat( cursor.pos[ 0 ].y() ), Z_ORDER )}; } break; - default: - break; } } -void GlScope::updateCursor(unsigned index) { - if (index > 0) { - generateVertices(index, *cursorInfo[index]); - } else for (index = 0; index < cursorInfo.size(); ++index) { - generateVertices(index, *cursorInfo[index]); - } + +void GlScope::updateCursor( unsigned index ) { + if ( index > 0 ) { + generateVertices( index, *cursorInfo[ index ] ); + } else + for ( index = 0; index < cursorInfo.size(); ++index ) { + generateVertices( index, *cursorInfo[ index ] ); + } // Write coordinates to GPU makeCurrent(); m_marker.bind(); - m_marker.write(0, vaMarker.data(), vaMarker.size() * sizeof(Vertices)); + m_marker.write( 0, vaMarker.data(), int( vaMarker.size() * sizeof( Vertices ) ) ); } + void GlScope::paintGL() { - if (!shaderCompileSuccess) return; + if ( !shaderCompileSuccess ) + return; auto *gl = context()->functions(); + QColor bg = view->colors->background; + gl->glClearColor( GLfloat( bg.redF() ), GLfloat( bg.greenF() ), GLfloat( bg.blueF() ), GLfloat( bg.alphaF() ) ); + // Clear OpenGL buffer and configure settings // TODO Don't clear if view->digitalPhosphorDraws()>1 - gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - gl->glLineWidth(1); + gl->glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + gl->glLineWidth( 1 ); m_program->bind(); // Apply zoom settings via matrix transformation - if (zoomed) { + if ( zoomed ) { QMatrix4x4 m; - m.scale(QVector3D(DIVS_TIME / (GLfloat)fabs(scope->getMarker(1) - scope->getMarker(0)), 1.0f, 1.0f)); - m.translate((GLfloat) - (scope->getMarker(0) + scope->getMarker(1)) / 2, 0.0f, 0.0f); - m_program->setUniformValue(matrixLocation, pmvMatrix * m); + m.scale( QVector3D( GLfloat( DIVS_TIME ) / GLfloat( fabs( scope->getMarker( 1 ) - scope->getMarker( 0 ) ) ), 1.0f, 1.0f ) ); + m.translate( -GLfloat( scope->getMarker( 0 ) + scope->getMarker( 1 ) ) / 2, 0.0f, 0.0f ); + m_program->setUniformValue( matrixLocation, pmvMatrix * m ); } drawMarkers(); unsigned historyIndex = 0; - for (Graph &graph : m_GraphHistory) { - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - if (scope->horizontal.format == Dso::GraphFormat::TY) { - drawSpectrumChannelGraph(channel, graph, (int)historyIndex); + for ( Graph &graph : m_GraphHistory ) { + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + if ( scope->horizontal.format == Dso::GraphFormat::TY ) { + drawSpectrumChannelGraph( channel, graph, int( historyIndex ) ); + if ( scope->histogram ) { + drawHistogramChannelGraph( channel, graph, int( historyIndex ) ); + } } - drawVoltageChannelGraph(channel, graph, (int)historyIndex); + drawVoltageChannelGraph( channel, graph, int( historyIndex ) ); } ++historyIndex; } - if (zoomed) { m_program->setUniformValue(matrixLocation, pmvMatrix); } + if ( zoomed ) { + m_program->setUniformValue( matrixLocation, pmvMatrix ); + } drawGrid(); m_program->release(); } -void GlScope::resizeGL(int width, int height) { - if (!shaderCompileSuccess) return; + +void GlScope::resizeGL( int width, int height ) { + if ( !shaderCompileSuccess ) + return; auto *gl = context()->functions(); - gl->glViewport(0, 0, (GLint)width, (GLint)height); + gl->glViewport( 0, 0, GLint( width ), GLint( height ) ); // Set axes to div-scale and apply correction for exact pixelization - float pixelizationWidthCorrection = (float)width / (width - 1); - float pixelizationHeightCorrection = (float)height / (height - 1); + float pixelizationWidthCorrection = float( width ) / ( width - 1 ); + float pixelizationHeightCorrection = float( height ) / ( height - 1 ); pmvMatrix.setToIdentity(); - pmvMatrix.ortho(-(DIVS_TIME / 2.0f) * pixelizationWidthCorrection, (DIVS_TIME / 2.0f) * pixelizationWidthCorrection, - -(DIVS_VOLTAGE / 2.0f) * pixelizationHeightCorrection, - (DIVS_VOLTAGE / 2.0f) * pixelizationHeightCorrection, -1.0f, 1.0f); + pmvMatrix.ortho( -float( DIVS_TIME ) / 2.0f * pixelizationWidthCorrection, + float( DIVS_TIME ) / 2.0f * pixelizationWidthCorrection, + -float( DIVS_VOLTAGE ) / 2.0f * pixelizationHeightCorrection, + float( DIVS_VOLTAGE ) / 2.0f * pixelizationHeightCorrection, -1.0f, 1.0f ); m_program->bind(); - m_program->setUniformValue(matrixLocation, pmvMatrix); + m_program->setUniformValue( matrixLocation, pmvMatrix ); m_program->release(); } -void GlScope::generateGrid(QOpenGLShaderProgram *program) { - gridDrawCounts[0] = 0; - gridDrawCounts[1] = 0; - gridDrawCounts[2] = 0; - m_grid.create(); +// draw 4 small crosses @ (x,y), (-x,y), (x,-y) and (-x,-y) +// section 0:grid, 1:axes, 2:border +void GlScope::draw4Cross( std::vector< QVector3D > &va, int section, float x, float y ) { + const float d = 0.05f; // cross size + for ( int xSign : {-1, 1} ) { + for ( int ySign : {-1, 1} ) { + gridDrawCounts[ section ] += 4; + va.push_back( QVector3D( xSign * ( x - d ), ySign * y, 0 ) ); + va.push_back( QVector3D( xSign * ( x + d ), ySign * y, 0 ) ); + va.push_back( QVector3D( xSign * x, ySign * ( y - d ), 0 ) ); + va.push_back( QVector3D( xSign * x, ySign * ( y + d ), 0 ) ); + } + } +} + +// prepare the static grid structure that is shown by 'drawGrid()' +// show a line as long as the trigger level marker is clicked or moved +// index: channel, value: trigger level, pressed: marker is activated +void GlScope::generateGrid( int index, double value, bool pressed ) { + // printf( "prepareGrid( %d, %g, %d )\n", index, value, pressed ); + + QOpenGLShaderProgram *program = m_program.get(); + if ( program == nullptr ) + return; + + for ( int iii = 0; iii < gridItems; ++iii ) + gridDrawCounts[ iii ] = 0; + + if ( !m_grid.isCreated() ) + m_grid.create(); m_grid.bind(); - m_grid.setUsagePattern(QOpenGLBuffer::StaticDraw); + m_grid.setUsagePattern( QOpenGLBuffer::StaticDraw ); + + std::vector< QVector3D > vaGrid; - std::vector vaGrid; + int item = 0; { // Bind draw vertical lines - m_vaoGrid[0].create(); - QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[0]); + if ( !m_vaoGrid[ item ].isCreated() ) + m_vaoGrid[ item ].create(); + QOpenGLVertexArrayObject::Binder b( &m_vaoGrid[ item ] ); m_grid.bind(); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); - } - - // Draw vertical lines - for (int div = 1; div < DIVS_TIME / 2; ++div) { - for (int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) { - float dotPosition = (float)dot / DIVS_SUB; - gridDrawCounts[0] += 4; - vaGrid.push_back(QVector3D(-div, -dotPosition, 0)); - vaGrid.push_back(QVector3D(-div, dotPosition, 0)); - vaGrid.push_back(QVector3D(div, -dotPosition, 0)); - vaGrid.push_back(QVector3D(div, dotPosition, 0)); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, 0, 3, 0 ); + } + + // Draw vertical dot lines + for ( int vDiv = 1; vDiv < DIVS_TIME / 2; ++vDiv ) { + for ( int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot ) { + float dotPosition = float( dot ) / DIVS_SUB; + gridDrawCounts[ item ] += 4; + vaGrid.push_back( QVector3D( -vDiv, -dotPosition, 0 ) ); + vaGrid.push_back( QVector3D( -vDiv, dotPosition, 0 ) ); + vaGrid.push_back( QVector3D( vDiv, -dotPosition, 0 ) ); + vaGrid.push_back( QVector3D( vDiv, dotPosition, 0 ) ); } } - // Draw horizontal lines - for (int div = 1; div < DIVS_VOLTAGE / 2; ++div) { - for (int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) { - if (dot % DIVS_SUB == 0) continue; // Already done by vertical lines - float dotPosition = (float)dot / DIVS_SUB; - gridDrawCounts[0] += 4; - vaGrid.push_back(QVector3D(-dotPosition, -div, 0)); - vaGrid.push_back(QVector3D(dotPosition, -div, 0)); - vaGrid.push_back(QVector3D(-dotPosition, div, 0)); - vaGrid.push_back(QVector3D(dotPosition, div, 0)); + // Draw horizontal dot lines + for ( int hDiv = 1; hDiv < DIVS_VOLTAGE / 2; ++hDiv ) { + for ( int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot ) { + if ( dot % DIVS_SUB == 0 ) + continue; // Already done by vertical lines + float dotPosition = float( dot ) / DIVS_SUB; + gridDrawCounts[ item ] += 4; + vaGrid.push_back( QVector3D( -dotPosition, -hDiv, 0 ) ); + vaGrid.push_back( QVector3D( dotPosition, -hDiv, 0 ) ); + vaGrid.push_back( QVector3D( -dotPosition, hDiv, 0 ) ); + vaGrid.push_back( QVector3D( dotPosition, hDiv, 0 ) ); } } + ++item; + { // Bind draw axes - m_vaoGrid[1].create(); - QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[1]); + if ( !m_vaoGrid[ item ].isCreated() ) + m_vaoGrid[ item ].create(); + QOpenGLVertexArrayObject::Binder b( &m_vaoGrid[ item ] ); m_grid.bind(); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, int( vaGrid.size() * sizeof( QVector3D ) ), 3 ); } // Axes // Horizontal axis - gridDrawCounts[1] += 4; - vaGrid.push_back(QVector3D(-DIVS_TIME / 2, 0, 0)); - vaGrid.push_back(QVector3D(DIVS_TIME / 2, 0, 0)); + gridDrawCounts[ item ] += 2; + vaGrid.push_back( QVector3D( -DIVS_TIME / 2, 0, 0 ) ); + vaGrid.push_back( QVector3D( DIVS_TIME / 2, 0, 0 ) ); // Vertical axis - vaGrid.push_back(QVector3D(0, -DIVS_VOLTAGE / 2, 0)); - vaGrid.push_back(QVector3D(0, DIVS_VOLTAGE / 2, 0)); + gridDrawCounts[ item ] += 2; + vaGrid.push_back( QVector3D( 0, -DIVS_VOLTAGE / 2, 0 ) ); + vaGrid.push_back( QVector3D( 0, DIVS_VOLTAGE / 2, 0 ) ); // Subdiv lines on horizontal axis - for (int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) { - float linePosition = (float)line / DIVS_SUB; - gridDrawCounts[1] += 4; - vaGrid.push_back(QVector3D(linePosition, -0.05f, 0)); - vaGrid.push_back(QVector3D(linePosition, 0.05f, 0)); - vaGrid.push_back(QVector3D(-linePosition, -0.05f, 0)); - vaGrid.push_back(QVector3D(-linePosition, 0.05f, 0)); + for ( int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line ) { + float linePosition = float( line ) / DIVS_SUB; + gridDrawCounts[ item ] += 4; + vaGrid.push_back( QVector3D( linePosition, -0.05f, 0 ) ); + vaGrid.push_back( QVector3D( linePosition, 0.05f, 0 ) ); + vaGrid.push_back( QVector3D( -linePosition, -0.05f, 0 ) ); + vaGrid.push_back( QVector3D( -linePosition, 0.05f, 0 ) ); } // Subdiv lines on vertical axis - for (int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) { - float linePosition = (float)line / DIVS_SUB; - gridDrawCounts[1] += 4; - vaGrid.push_back(QVector3D(-0.05f, linePosition, 0)); - vaGrid.push_back(QVector3D(0.05f, linePosition, 0)); - vaGrid.push_back(QVector3D(-0.05f, -linePosition, 0)); - vaGrid.push_back(QVector3D(0.05f, -linePosition, 0)); + for ( int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line ) { + float linePosition = float( line ) / DIVS_SUB; + gridDrawCounts[ item ] += 4; + vaGrid.push_back( QVector3D( -0.05f, linePosition, 0 ) ); + vaGrid.push_back( QVector3D( 0.05f, linePosition, 0 ) ); + vaGrid.push_back( QVector3D( -0.05f, -linePosition, 0 ) ); + vaGrid.push_back( QVector3D( 0.05f, -linePosition, 0 ) ); } - { - m_vaoGrid[2].create(); - QOpenGLVertexArrayObject::Binder b(&m_vaoGrid[2]); + // Draw vertical cross lines + for ( int vDiv = 1; vDiv < DIVS_TIME / 2; ++vDiv ) { + for ( int hDiv = 1; hDiv < DIVS_VOLTAGE / 2; ++hDiv ) { + draw4Cross( vaGrid, 1, vDiv, hDiv ); + } + } + // Draw horizontal cross lines + for ( int hDiv = 1; hDiv < DIVS_VOLTAGE / 2; ++hDiv ) { + for ( int vDiv = 1; vDiv < DIVS_TIME / 2; ++vDiv ) { + if ( vDiv % DIVS_SUB == 0 ) + continue; // Already done by vertical lines + draw4Cross( vaGrid, 1, vDiv, hDiv ); + } + } + + ++item; + + { // Border + if ( !m_vaoGrid[ item ].isCreated() ) + m_vaoGrid[ item ].create(); + QOpenGLVertexArrayObject::Binder b( &m_vaoGrid[ item ] ); m_grid.bind(); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, int(vaGrid.size() * sizeof(QVector3D)), 3); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, int( vaGrid.size() * sizeof( QVector3D ) ), 3 ); } + gridDrawCounts[ item ] += 4; + vaGrid.push_back( QVector3D( -DIVS_TIME / 2, -DIVS_VOLTAGE / 2, 0 ) ); + vaGrid.push_back( QVector3D( DIVS_TIME / 2, -DIVS_VOLTAGE / 2, 0 ) ); + vaGrid.push_back( QVector3D( DIVS_TIME / 2, DIVS_VOLTAGE / 2, 0 ) ); + vaGrid.push_back( QVector3D( -DIVS_TIME / 2, DIVS_VOLTAGE / 2, 0 ) ); - // Border - gridDrawCounts[2] += 4; - vaGrid.push_back(QVector3D(-DIVS_TIME / 2, -DIVS_VOLTAGE / 2, 0)); - vaGrid.push_back(QVector3D(DIVS_TIME / 2, -DIVS_VOLTAGE / 2, 0)); - vaGrid.push_back(QVector3D(DIVS_TIME / 2, DIVS_VOLTAGE / 2, 0)); - vaGrid.push_back(QVector3D(-DIVS_TIME / 2, DIVS_VOLTAGE / 2, 0)); + ++item; + + + { // prepare (dynamic) trigger level marker line + if ( !m_vaoGrid[ item ].isCreated() ) + m_vaoGrid[ item ].create(); + QOpenGLVertexArrayObject::Binder b( &m_vaoGrid[ item ] ); + m_grid.bind(); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, int( vaGrid.size() * sizeof( QVector3D ) ), 3 ); + } + if ( pressed && index >= 0 ) { + triggerLineColor = view->colors->voltage[ unsigned( index ) ]; + if ( index != int( scope->trigger.source ) ) + triggerLineColor = triggerLineColor.darker(); + gridDrawCounts[ item ] += 2; + float yPos = float( ( value / scope->gain( unsigned( index ) ) + scope->voltage[ unsigned( index ) ].offset ) ); + vaGrid.push_back( QVector3D( -DIVS_TIME / 2, yPos, 0 ) ); + vaGrid.push_back( QVector3D( DIVS_TIME / 2, yPos, 0 ) ); + } - m_grid.allocate(&vaGrid[0], int(vaGrid.size() * sizeof(QVector3D))); + m_grid.allocate( &vaGrid[ 0 ], int( vaGrid.size() * sizeof( QVector3D ) ) ); m_grid.release(); } + void GlScope::drawGrid() { auto *gl = context()->functions(); - gl->glLineWidth(1); + + gl->glLineWidth( 1 ); + + int item = 3; + // Trigger level (draw this on top of the other items) + m_vaoGrid[ item ].bind(); + m_program->setUniformValue( colorLocation, triggerLineColor ); + gl->glDrawArrays( GL_LINES, 0, gridDrawCounts[ item ] ); + m_vaoGrid[ item ].release(); // Grid - m_vaoGrid[0].bind(); - m_program->setUniformValue(colorLocation, view->screen.grid); - gl->glDrawArrays(GL_POINTS, 0, gridDrawCounts[0]); - m_vaoGrid[0].release(); + item = 0; + m_vaoGrid[ item ].bind(); + m_program->setUniformValue( colorLocation, view->colors->grid ); + gl->glDrawArrays( GL_POINTS, 0, gridDrawCounts[ item ] ); + m_vaoGrid[ item ].release(); - // Axes - m_vaoGrid[1].bind(); - m_program->setUniformValue(colorLocation, view->screen.axes); - gl->glDrawArrays(GL_LINES, 0, gridDrawCounts[1]); - m_vaoGrid[1].release(); + + // Axes and div crosses + ++item; + m_vaoGrid[ item ].bind(); + m_program->setUniformValue( colorLocation, view->colors->axes ); + gl->glDrawArrays( GL_LINES, 0, gridDrawCounts[ item ] ); + m_vaoGrid[ item ].release(); // Border - m_vaoGrid[2].bind(); - m_program->setUniformValue(colorLocation, view->screen.border); - gl->glDrawArrays(GL_LINE_LOOP, 0, gridDrawCounts[2]); - m_vaoGrid[2].release(); + ++item; + m_vaoGrid[ item ].bind(); + m_program->setUniformValue( colorLocation, view->colors->border ); + gl->glDrawArrays( GL_LINE_LOOP, 0, gridDrawCounts[ item ] ); + m_vaoGrid[ item ].release(); } -void GlScope::drawVertices(QOpenGLFunctions *gl, unsigned marker, QColor color) { - m_program->setUniformValue(colorLocation, (marker == selectedCursor) ? color : color.darker()); - gl->glDrawArrays(GL_LINE_LOOP, GLint(marker * VERTICES_ARRAY_SIZE), VERTICES_ARRAY_SIZE); - if (cursorInfo[marker]->shape == DsoSettingsScopeCursor::RECTANGULAR) { - color.setAlphaF(0.25); - m_program->setUniformValue(colorLocation, color.darker()); - gl->glDrawArrays(GL_TRIANGLE_FAN, GLint(marker * VERTICES_ARRAY_SIZE), VERTICES_ARRAY_SIZE); + +void GlScope::drawVertices( QOpenGLFunctions *gl, unsigned marker, QColor color ) { + m_program->setUniformValue( colorLocation, ( marker == selectedCursor ) ? color : color.darker() ); + gl->glDrawArrays( GL_LINE_LOOP, GLint( marker * VERTICES_ARRAY_SIZE ), GLint( VERTICES_ARRAY_SIZE ) ); + if ( cursorInfo[ marker ]->shape == DsoSettingsScopeCursor::RECTANGULAR ) { + color.setAlphaF( 0.25 ); + m_program->setUniformValue( colorLocation, color.darker() ); + gl->glDrawArrays( GL_TRIANGLE_FAN, GLint( marker * VERTICES_ARRAY_SIZE ), GLint( VERTICES_ARRAY_SIZE ) ); } } + void GlScope::drawMarkers() { auto *gl = context()->functions(); m_vaoMarker.bind(); unsigned marker = 0; - drawVertices(gl, marker, view->screen.markers); + drawVertices( gl, marker, view->colors->markers ); ++marker; - if (view->cursorsVisible) { - gl->glDepthMask(GL_FALSE); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel, ++marker) { - if (scope->voltage[channel].used) { - drawVertices(gl, marker, view->screen.voltage[channel]); + if ( view->cursorsVisible ) { + gl->glDepthMask( GL_FALSE ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel, ++marker ) { + if ( scope->voltage[ channel ].used ) { + drawVertices( gl, marker, view->colors->voltage[ channel ] ); } } - for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel, ++marker) { - if (scope->spectrum[channel].used) { - drawVertices(gl, marker, view->screen.spectrum[channel]); + for ( ChannelID channel = 0; channel < scope->spectrum.size(); ++channel, ++marker ) { + if ( scope->spectrum[ channel ].used ) { + drawVertices( gl, marker, view->colors->spectrum[ channel ] ); } } - gl->glDepthMask(GL_TRUE); + gl->glDepthMask( GL_TRUE ); } m_vaoMarker.release(); } -void GlScope::drawVoltageChannelGraph(ChannelID channel, Graph &graph, int historyIndex) { - if (!scope->voltage[channel].used) return; - m_program->setUniformValue(colorLocation, view->screen.voltage[channel].darker(100 + 10 * historyIndex)); - Graph::VaoCount &v = graph.vaoVoltage[channel]; +void GlScope::drawVoltageChannelGraph( ChannelID channel, Graph &graph, int historyIndex ) { + if ( !scope->voltage[ channel ].used ) + return; - QOpenGLVertexArrayObject::Binder b(v.first); - const GLenum dMode = (view->interpolation == Dso::INTERPOLATION_OFF) ? GL_POINTS : GL_LINE_STRIP; - context()->functions()->glDrawArrays(dMode, 0, v.second); + m_program->setUniformValue( colorLocation, view->colors->voltage[ channel ].darker( 100 + 10 * historyIndex ) ); + Graph::VaoCount &v = graph.vaoVoltage[ channel ]; + + QOpenGLVertexArrayObject::Binder b( v.first ); + const GLenum dMode = ( view->interpolation == Dso::INTERPOLATION_OFF ) ? GL_POINTS : GL_LINE_STRIP; + context()->functions()->glDrawArrays( dMode, 0, v.second ); } -void GlScope::drawSpectrumChannelGraph(ChannelID channel, Graph &graph, int historyIndex) { - if (!scope->spectrum[channel].used) return; - m_program->setUniformValue(colorLocation, view->screen.spectrum[channel].darker(100 + 10 * historyIndex)); - Graph::VaoCount &v = graph.vaoSpectrum[channel]; +void GlScope::drawHistogramChannelGraph( ChannelID channel, Graph &graph, int historyIndex ) { + if ( !scope->voltage[ channel ].used ) + return; + + m_program->setUniformValue( colorLocation, view->colors->voltage[ channel ].darker( 100 + 10 * historyIndex ) ); + Graph::VaoCount &h = graph.vaoHistogram[ channel ]; + + QOpenGLVertexArrayObject::Binder b( h.first ); + const GLenum dMode = ( view->interpolation == Dso::INTERPOLATION_OFF ) ? GL_POINTS : GL_LINES; + context()->functions()->glDrawArrays( dMode, 0, h.second ); +} + + +void GlScope::drawSpectrumChannelGraph( ChannelID channel, Graph &graph, int historyIndex ) { + if ( !scope->spectrum[ channel ].used ) + return; + + m_program->setUniformValue( colorLocation, view->colors->spectrum[ channel ].darker( 100 + 10 * historyIndex ) ); + Graph::VaoCount &v = graph.vaoSpectrum[ channel ]; - QOpenGLVertexArrayObject::Binder b(v.first); - const GLenum dMode = (view->interpolation == Dso::INTERPOLATION_OFF) ? GL_POINTS : GL_LINE_STRIP; - context()->functions()->glDrawArrays(dMode, 0, v.second); + QOpenGLVertexArrayObject::Binder b( v.first ); + const GLenum dMode = ( view->interpolation == Dso::INTERPOLATION_OFF ) ? GL_POINTS : GL_LINE_STRIP; + context()->functions()->glDrawArrays( dMode, 0, v.second ); } diff --git a/openhantek/src/glscope.h b/openhantek/src/glscope.h index 4d32e32d..e7c9fe5b 100644 --- a/openhantek/src/glscope.h +++ b/openhantek/src/glscope.h @@ -2,15 +2,15 @@ #pragma once -#include #include +#include -#include #include #include #include #include #include +#include #include "glscopegraph.h" #include "hantekdso/enums.h" @@ -26,60 +26,67 @@ class GlScope : public QOpenGLWidget { Q_OBJECT public: - static GlScope *createNormal(DsoSettingsScope *scope, DsoSettingsView *view, - QWidget *parent = 0); - static GlScope *createZoomed(DsoSettingsScope *scope, DsoSettingsView *view, - QWidget *parent = 0); + static GlScope *createNormal( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent = nullptr ); + static GlScope *createZoomed( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent = nullptr ); /** - * We need at least OpenGL 3.2 with shader version 150 or + * We need at least OpenGL 3.2 with shader version 150 (else version 120) or * OpenGL ES 2.0 with shader version 100. */ - static void fixOpenGLversion(QSurfaceFormat::RenderableType t=QSurfaceFormat::DefaultRenderableType); + static void useQSurfaceFormat( QSurfaceFormat::RenderableType t = QSurfaceFormat::DefaultRenderableType ); + // force either GLSL version 1.20 or 1.50 + static void useOpenGLSLversion( unsigned version ) { forceGLSLversion = version; } /** * Show new post processed data * @param data */ - void showData(std::shared_ptr data); - void updateCursor(unsigned index = 0); - void cursorSelected(unsigned index) { selectedCursor = index; updateCursor(index); } + void showData( std::shared_ptr< PPresult > newData ); + void updateCursor( unsigned index = 0 ); + void cursorSelected( unsigned index ) { + selectedCursor = index; + updateCursor( index ); + } + void generateGrid( int index = -1, double value = 0.0, bool pressed = false ); protected: /// \brief Initializes the scope widget. /// \param settings The settings that should be used. /// \param parent The parent widget. - GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent = 0); - virtual ~GlScope(); - GlScope(const GlScope&) = delete; + GlScope( DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent = nullptr ); + ~GlScope() override; + GlScope( const GlScope & ) = delete; /// \brief Initializes OpenGL output. - virtual void initializeGL() override; + void initializeGL() override; /// \brief Draw the graphs, marker and the grid. - virtual void paintGL() override; + void paintGL() override; /// \brief Resize the widget. /// \param width The new width of the widget. /// \param height The new height of the widget. - virtual void resizeGL(int width, int height) override; + void resizeGL( int width, int height ) override; - virtual void mousePressEvent(QMouseEvent *event) override; - virtual void mouseMoveEvent(QMouseEvent *event) override; - virtual void mouseReleaseEvent(QMouseEvent *event) override; - virtual void paintEvent(QPaintEvent *event) override; + void mousePressEvent( QMouseEvent *event ) override; + void mouseMoveEvent( QMouseEvent *event ) override; + void mouseReleaseEvent( QMouseEvent *event ) override; + void mouseDoubleClickEvent( QMouseEvent *event ) override; + void wheelEvent( QWheelEvent *event ) override; + void paintEvent( QPaintEvent *event ) override; /// \brief Draw the grid. void drawGrid(); /// Draw vertical lines at marker positions void drawMarkers(); - void generateVertices(unsigned marker, const DsoSettingsScopeCursor &cursor); - void drawVertices(QOpenGLFunctions *gl, unsigned marker, QColor color); + void generateVertices( unsigned marker, const DsoSettingsScopeCursor &cursor ); + void drawVertices( QOpenGLFunctions *gl, unsigned marker, QColor color ); - void drawVoltageChannelGraph(ChannelID channel, Graph &graph, int historyIndex); - void drawSpectrumChannelGraph(ChannelID channel, Graph &graph, int historyIndex); - QPointF eventToPosition(QMouseEvent *event); + void drawVoltageChannelGraph( ChannelID channel, Graph &graph, int historyIndex ); + void drawHistogramChannelGraph( ChannelID channel, Graph &graph, int historyIndex ); + void drawSpectrumChannelGraph( ChannelID channel, Graph &graph, int historyIndex ); + QPointF posToPosition( QPointF pos ); signals: - void markerMoved(unsigned cursorIndex, unsigned marker); + void markerMoved( unsigned cursorIndex, unsigned marker ); private: // User settings @@ -89,35 +96,39 @@ class GlScope : public QOpenGLWidget { // Marker const unsigned NO_MARKER = UINT_MAX; - #pragma pack(push, 1) +#pragma pack( push, 1 ) struct Vertices { QVector3D a, b, c, d; }; - #pragma pack(pop) - const unsigned VERTICES_ARRAY_SIZE = sizeof(Vertices) / sizeof(QVector3D); - std::vector vaMarker; +#pragma pack( pop ) + const unsigned VERTICES_ARRAY_SIZE = sizeof( Vertices ) / sizeof( QVector3D ); + std::vector< Vertices > vaMarker; unsigned selectedMarker = NO_MARKER; QOpenGLBuffer m_marker; QOpenGLVertexArrayObject m_vaoMarker; // Cursors - std::vector cursorInfo; + std::vector< DsoSettingsScopeCursor * > cursorInfo; unsigned selectedCursor = 0; // Grid QOpenGLBuffer m_grid; - QOpenGLVertexArrayObject m_vaoGrid[3]; - GLsizei gridDrawCounts[3]; - void generateGrid(QOpenGLShaderProgram *program); + static const int gridItems = 4; + QOpenGLVertexArrayObject m_vaoGrid[ gridItems ]; + GLsizei gridDrawCounts[ gridItems ]; + void draw4Cross( std::vector< QVector3D > &va, int section, float x, float y ); + QColor triggerLineColor = QColor( "black" ); // Graphs - std::list m_GraphHistory; + std::list< Graph > m_GraphHistory; unsigned currentGraphInHistory = 0; // OpenGL shader, matrix, var-locations + unsigned int GLSLversion = 150; + static unsigned forceGLSLversion; bool shaderCompileSuccess = false; QString errorMessage; - std::unique_ptr m_program; + std::unique_ptr< QOpenGLShaderProgram > m_program; QMatrix4x4 pmvMatrix; ///< projection, view matrix int colorLocation; int vertexLocation; diff --git a/openhantek/src/glscopegraph.cpp b/openhantek/src/glscopegraph.cpp index 29ca3ade..6cf576ac 100644 --- a/openhantek/src/glscopegraph.cpp +++ b/openhantek/src/glscopegraph.cpp @@ -1,66 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "glscopegraph.h" #include -Graph::Graph() : buffer(QOpenGLBuffer::VertexBuffer) { +Graph::Graph() : buffer( QOpenGLBuffer::VertexBuffer ) { buffer.create(); - buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + buffer.setUsagePattern( QOpenGLBuffer::DynamicDraw ); } -void Graph::writeData(PPresult *data, QOpenGLShaderProgram *program, int vertexLocation) { +void Graph::writeData( PPresult *data, QOpenGLShaderProgram *program, int vertexLocation ) { // Determine memory int neededMemory = 0; - for (ChannelGraph &cg : data->vaChannelVoltage) neededMemory += cg.size() * sizeof(QVector3D); - for (ChannelGraph &cg : data->vaChannelSpectrum) neededMemory += cg.size() * sizeof(QVector3D); + for ( ChannelGraph &cg : data->vaChannelVoltage ) + neededMemory += int( cg.size() * sizeof( QVector3D ) ); + for ( ChannelGraph &cg : data->vaChannelHistogram ) + neededMemory += int( cg.size() * sizeof( QVector3D ) ); + for ( ChannelGraph &cg : data->vaChannelSpectrum ) + neededMemory += int( cg.size() * sizeof( QVector3D ) ); buffer.bind(); program->bind(); // Allocate space if necessary - if (neededMemory > allocatedMem) { - buffer.allocate(neededMemory); + if ( neededMemory > allocatedMem ) { + buffer.allocate( neededMemory ); allocatedMem = neededMemory; } // Write data to buffer int offset = 0; - vaoVoltage.resize(data->vaChannelVoltage.size()); - vaoSpectrum.resize(data->vaChannelSpectrum.size()); - for (ChannelID channel = 0; channel < vaoVoltage.size(); ++channel) { + vaoVoltage.resize( data->vaChannelVoltage.size() ); + vaoHistogram.resize( data->vaChannelHistogram.size() ); + vaoSpectrum.resize( data->vaChannelSpectrum.size() ); + for ( ChannelID channel = 0; channel < std::max( std::max( vaoVoltage.size(), vaoHistogram.size() ), vaoSpectrum.size() ); + ++channel ) { int dataSize; // Voltage channel - if (channel < vaoVoltage.size()) { - VaoCount &v = vaoVoltage[channel]; - if (!v.first) { + if ( channel < vaoVoltage.size() ) { + VaoCount &v = vaoVoltage[ channel ]; + if ( !v.first ) { v.first = new QOpenGLVertexArrayObject; - if (!v.first->create()) throw new std::runtime_error("QOpenGLVertexArrayObject create failed"); + if ( !v.first->create() ) + throw new std::runtime_error( "QOpenGLVertexArrayObject create failed" ); } - ChannelGraph &gVoltage = data->vaChannelVoltage[channel]; + ChannelGraph &gVoltage = data->vaChannelVoltage[ channel ]; v.first->bind(); - dataSize = int(gVoltage.size() * sizeof(QVector3D)); - buffer.write(offset, gVoltage.data(), dataSize); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, 0); + dataSize = int( gVoltage.size() * sizeof( QVector3D ) ); + buffer.write( offset, gVoltage.data(), dataSize ); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, offset, 3, 0 ); v.first->release(); - v.second = (int)gVoltage.size(); + v.second = int( gVoltage.size() ); + offset += dataSize; + } + + // Histogram channel + if ( channel < vaoHistogram.size() ) { + VaoCount &h = vaoHistogram[ channel ]; + if ( !h.first ) { + h.first = new QOpenGLVertexArrayObject; + if ( !h.first->create() ) + throw new std::runtime_error( "QOpenGLVertexArrayObject create failed" ); + } + ChannelGraph &gHistogram = data->vaChannelHistogram[ channel ]; + h.first->bind(); + dataSize = int( gHistogram.size() * sizeof( QVector3D ) ); + buffer.write( offset, gHistogram.data(), dataSize ); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, offset, 3, 0 ); + h.first->release(); + h.second = int( gHistogram.size() ); offset += dataSize; } // Spectrum channel - if (channel < vaoSpectrum.size()) { - VaoCount &s = vaoSpectrum[channel]; - if (!s.first) { + if ( channel < vaoSpectrum.size() ) { + VaoCount &s = vaoSpectrum[ channel ]; + if ( !s.first ) { s.first = new QOpenGLVertexArrayObject; - if (!s.first->create()) throw new std::runtime_error("QOpenGLVertexArrayObject create failed"); + if ( !s.first->create() ) + throw new std::runtime_error( "QOpenGLVertexArrayObject create failed" ); } - ChannelGraph &gSpectrum = data->vaChannelSpectrum[channel]; + ChannelGraph &gSpectrum = data->vaChannelSpectrum[ channel ]; s.first->bind(); - dataSize = int(gSpectrum.size() * sizeof(QVector3D)); - buffer.write(offset, gSpectrum.data(), dataSize); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, 0); + dataSize = int( gSpectrum.size() * sizeof( QVector3D ) ); + buffer.write( offset, gSpectrum.data(), dataSize ); + program->enableAttributeArray( vertexLocation ); + program->setAttributeBuffer( vertexLocation, GL_FLOAT, offset, 3, 0 ); s.first->release(); - s.second = (int)gSpectrum.size(); + s.second = int( gSpectrum.size() ); offset += dataSize; } } @@ -69,13 +98,19 @@ void Graph::writeData(PPresult *data, QOpenGLShaderProgram *program, int vertexL } Graph::~Graph() { - for (auto &vao : vaoVoltage) { + for ( auto &vao : vaoVoltage ) { vao.first->destroy(); delete vao.first; } - for (auto &vao : vaoSpectrum) { + for ( auto &vao : vaoHistogram ) { vao.first->destroy(); delete vao.first; } - if (buffer.isCreated()) { buffer.destroy(); } + for ( auto &vao : vaoSpectrum ) { + vao.first->destroy(); + delete vao.first; + } + if ( buffer.isCreated() ) { + buffer.destroy(); + } } diff --git a/openhantek/src/glscopegraph.h b/openhantek/src/glscopegraph.h index 222bd19d..008cefbe 100644 --- a/openhantek/src/glscopegraph.h +++ b/openhantek/src/glscopegraph.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include @@ -13,15 +15,16 @@ struct Graph { explicit Graph(); - Graph(const Graph &) = delete; - Graph(const Graph &&) = delete; + Graph( const Graph & ) = delete; + Graph( const Graph && ) = delete; ~Graph(); - void writeData(PPresult *data, QOpenGLShaderProgram *program, int vertexLocation); - typedef std::pair VaoCount; + void writeData( PPresult *data, QOpenGLShaderProgram *program, int vertexLocation ); + typedef std::pair< QOpenGLVertexArrayObject *, GLsizei > VaoCount; public: int allocatedMem = 0; QOpenGLBuffer buffer; - std::vector vaoVoltage; - std::vector vaoSpectrum; + std::vector< VaoCount > vaoVoltage; + std::vector< VaoCount > vaoHistogram; + std::vector< VaoCount > vaoSpectrum; }; diff --git a/openhantek/src/hantekdso/capturing.cpp b/openhantek/src/hantekdso/capturing.cpp new file mode 100644 index 00000000..86721e32 --- /dev/null +++ b/openhantek/src/hantekdso/capturing.cpp @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0+ + +// #define TIMESTAMPDEBUG + +#include "capturing.h" +#include "usb/scopedevice.h" +#include +#include + + +Capturing::Capturing( HantekDsoControl *hdc ) : hdc( hdc ) { hdc->capturing = true; } + + +void Capturing::run() { + forever { + if ( !hdc->capturing || QThread::currentThread()->isInterruptionRequested() ) { + hdc->quitSampling(); // stop the scope + hdc->stopStateMachine(); // stop the state machine + return; // stop this thread + } + if ( hdc->scope ) { // device is initialized + if ( hdc->sampling ) { + capture(); + // add user defined hold-off time to lower CPU load + QThread::msleep( unsigned( 1000 * hdc->scope->horizontal.acquireInterval ) ); + } else { + QThread::msleep( unsigned( hdc->displayInterval ) ); + } + } + } +} + + +double id2sr( uint8_t timediv ) { + if ( timediv < 100 ) + return timediv * 1e6; + return ( timediv - 100 ) * 1e4; +} + + +void Capturing::xferSamples() { + QWriteLocker locker( &hdc->raw.lock ); + if ( !freeRun ) + swap( data, hdc->raw.data ); + hdc->raw.channels = channels; + hdc->raw.samplerate = samplerate; + hdc->raw.oversampling = oversampling; + hdc->raw.gainValue[ 0 ] = gainValue[ 0 ]; + hdc->raw.gainValue[ 1 ] = gainValue[ 1 ]; + hdc->raw.gainIndex[ 0 ] = gainIndex[ 0 ]; + hdc->raw.gainIndex[ 1 ] = gainIndex[ 1 ]; + hdc->raw.freeRun = freeRun; + hdc->raw.valid = valid; + hdc->raw.tag = tag; +} + + +void Capturing::capture() { + if ( !hdc->samplingStarted ) + return; + int errorCode; + // Send all pending control commands + ControlCommand *controlCommand = hdc->firstControlCommand; + while ( controlCommand ) { + if ( controlCommand->pending ) { + switch ( int( controlCommand->code ) ) { + case uint8_t( ControlCode::CONTROL_SETGAIN_CH1 ): + gainValue[ 0 ] = controlCommand->data()[ 0 ]; + gainIndex[ 0 ] = controlCommand->data()[ 1 ]; + break; + case uint8_t( ControlCode::CONTROL_SETGAIN_CH2 ): + gainValue[ 1 ] = controlCommand->data()[ 0 ]; + gainIndex[ 1 ] = controlCommand->data()[ 1 ]; + break; + case uint8_t( ControlCode::CONTROL_SETNUMCHANNELS ): + if ( realSlow ) { // force 2 channels for slow samplings where roll mode is possible + if ( *controlCommand->data() == channels ) + controlCommand->pending = false; + else + *controlCommand->data() = 2; + } + channels = *controlCommand->data(); + break; + case uint8_t( ControlCode::CONTROL_SETSAMPLERATE ): + samplerate = id2sr( controlCommand->data()[ 0 ] ); + uint8_t sampleIndex = controlCommand->data()[ 1 ]; + oversampling = uint8_t( hdc->specification->fixedSampleRates[ sampleIndex ].oversampling ); + effectiveSamplerate = hdc->specification->fixedSampleRates[ sampleIndex ].samplerate; + if ( !realSlow && effectiveSamplerate < 10e3 && + hdc->scope->trigger.mode == Dso::TriggerMode::ROLL ) { // switch to real slow rolling + for ( auto it = data.begin(); it != data.end(); ++it ) + *it = 0x80; // fill with "zeros" + QWriteLocker locker( &hdc->raw.lock ); + hdc->raw.rollMode = false; + swap( data, hdc->raw.data ); // "clear screen" + } + realSlow = effectiveSamplerate < 10e3; + if ( realSlow ) { // roll mode possible? + if ( channels != 2 ) // always switch to two channels + hdc->modifyCommand< ControlSetNumChannels >( ControlCode::CONTROL_SETNUMCHANNELS )->setNumChannels( 2 ); + } else { + if ( channels == 2 && hdc->isSingleChannel() ) // switch back to real user setting + hdc->modifyCommand< ControlSetNumChannels >( ControlCode::CONTROL_SETNUMCHANNELS )->setNumChannels( 1 ); + } + break; + } + QString name = ""; + if ( controlCommand->code >= 0xe0 && controlCommand->code <= 0xe6 ) + name = hdc->controlNames[ controlCommand->code - 0xe0 ]; + timestampDebug( QString( "Sending control command 0x%1 (%2): %3" ) + .arg( QString::number( controlCommand->code, 16 ), name, + hexdecDump( controlCommand->data(), unsigned( controlCommand->size() ) ) ) ); + if ( hdc->scopeDevice->isRealHW() ) { // do the USB communication with scope HW + errorCode = hdc->scopeDevice->controlWrite( controlCommand ); + if ( errorCode < 0 ) { + qWarning( "Sending control command %2x failed: %s", uint8_t( controlCommand->code ), + libUsbErrorString( errorCode ).toLocal8Bit().data() ); + + if ( errorCode == LIBUSB_ERROR_NO_DEVICE ) { + return; + } + } else { + controlCommand->pending = false; + } + } else { + controlCommand->pending = false; + } + } + controlCommand = controlCommand->next; + } + valid = true; + freeRun = hdc->triggerModeNONE() && realSlow; + // sample step by step into the target if rollMode, else buffer and switch one big block + dp = freeRun ? &hdc->raw.data : &data; + rawSamplesize = hdc->grossSampleCount( hdc->getSamplesize() * oversampling ) * channels; + dp->resize( rawSamplesize, 0x80 ); + if ( tag && freeRun ) // in free run mode transfer settings immediately + xferSamples(); + ++tag; + if ( hdc->scopeDevice->isRealHW() ) { + received = getRealSamples(); + } else { + received = getDemoSamples(); + } + if ( received != rawSamplesize ) { + // qDebug() << "retval != rawSamplesize" << received << rawSamplesize; + auto end = dp->end(); + for ( auto it = dp->begin(); it != end; ++it ) + *it = 0x80; // fill with "zeros" + valid = false; + hdc->raw.rollMode = false; + } else { + hdc->raw.rollMode = true; // one complete buffer available, start to roll + } + if ( !freeRun ) // in normal capturing mode transfer after capturing one block + xferSamples(); +} + + +unsigned Capturing::getRealSamples() { + int errorCode; + errorCode = hdc->scopeDevice->controlWrite( hdc->getCommand( ControlCode::CONTROL_STARTSAMPLING ) ); + if ( errorCode < 0 ) { + qWarning() << "controlWrite: Getting sample data failed: " << libUsbErrorString( errorCode ); + dp->clear(); + return 0; + } + // Save raw data to temporary buffer + // timestampDebug( QString( "Request packet %1: %2 bytes" ).arg( tag ).arg( rawSamplesize ) ); + hdc->raw.received = 0; + int retval = hdc->scopeDevice->bulkReadMulti( dp->data(), rawSamplesize, realSlow, hdc->raw.received ); + if ( retval < 0 ) { + if ( retval == LIBUSB_ERROR_NO_DEVICE ) + hdc->scopeDevice->disconnectFromDevice(); + qWarning() << "bulkReadMulti: Getting sample data failed: " << libUsbErrorString( retval ); + dp->clear(); + return 0; + } + // timestampDebug( QString( "Received packet %1: %2 bytes" ).arg( tag ).arg( retval ) ); + return unsigned( retval ); +} + + +unsigned Capturing::getDemoSamples() { + const uint8_t binaryOffset = 0x80; // ADC format: binary offset + const int8_t V_zero = 0; // ADC = 0V + const int8_t V_plus_1 = 25; // ADC = 1V + const int8_t V_plus_2 = 50; // ADC = 2V + const int8_t V_minus_1 = -25; // ADC = -1V + const int8_t V_minus_2 = -50; // ADC = -2V + const int gain1 = int( gainValue[ 0 ] ); + const int gain2 = int( gainValue[ 1 ] ); + static int ch1 = 0; + static int ch2 = 0; + static int counter = 0; + unsigned received = 0; + hdc->raw.received = 0; + // timestampDebug( QString( "Request dummy packet %1: %2 bytes" ).arg( tag ).arg( rawSamplesize ) ); + int deltaT = 99; + // deltaT (=99) defines the frequency of the dummy signals: + // ch1 = 1 kHz and ch2 = 500 Hz + // uncomment the next two lines to disable sample slowdown for low sample rates + // if ( samplerate < 10e6 ) + // deltaT = int( round( deltaT * samplerate / 10e6 ) ); + // adapt demo samples for high sample rates >10 MS/s + if ( samplerate > 10e6 ) + deltaT = int( round( deltaT * samplerate / 10e6 ) ); + const unsigned packetLength = 512 * 78; // 50 blocks for one screen width of 20000 + unsigned block = 0; + dp->resize( rawSamplesize, binaryOffset ); + auto end = dp->end(); + unsigned packet = 0; + // bool couplingAC1 = hdc->scope->coupling( 0, hdc->specification ) == Dso::Coupling::AC; // not yet used + bool couplingAC2 = hdc->scope->coupling( 1, hdc->specification ) == Dso::Coupling::AC; + for ( auto it = dp->begin(); it != end; ++it ) { + if ( ++counter >= deltaT ) { + counter = 0; + if ( --ch1 < V_minus_2 ) { + ch1 = V_plus_2; + if ( couplingAC2 ) + ch2 = ch2 <= V_zero ? V_plus_1 : V_minus_1; // -1V <-> +1V + else + ch2 = ch2 <= V_plus_1 ? V_plus_2 : V_zero; // 0V <-> 2V + } + } + *it = uint8_t( qBound( 0, ch1 * gain1 + binaryOffset, 0xFF ) ); // clip if outside 8bit range + ++received; + if ( 2 == channels ) { + *++it = uint8_t( qBound( 0, ch2 * gain2 + binaryOffset, 0xFF ) ); // clip .. + ++received; + } + if ( ( block += channels ) >= packetLength ) { + ++packet; + block = 0; + hdc->raw.received = received; + QThread::usleep( unsigned( 1e6 * packetLength / channels / samplerate ) ); + if ( !hdc->capturing || hdc->scopeDevice->hasStopped() ) + break; + } + } + // timestampDebug( QString( "Received dummy packet %1: %2 bytes" ).arg( packet ).arg( rawSamplesize ) ); + return received; +} diff --git a/openhantek/src/hantekdso/capturing.h b/openhantek/src/hantekdso/capturing.h new file mode 100644 index 00000000..1bc44a2f --- /dev/null +++ b/openhantek/src/hantekdso/capturing.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include "hantekdsocontrol.h" + +class Capturing : public QThread { + Q_OBJECT + + public: + Capturing( HantekDsoControl *hdc ); + void quitCapturing() { hdc->capturing = false; } + + private: + void run() override; + void capture(); + unsigned getRealSamples(); + unsigned getDemoSamples(); + void xferSamples(); + // bool active = true; + HantekDsoControl *hdc; + unsigned channels = 0; + double effectiveSamplerate = 0; + bool realSlow = false; + double samplerate = 0; + unsigned oversampling = 0; + unsigned rawSamplesize = 0; + unsigned received = 0; + unsigned gainValue[ 2 ] = {0, 0}; // 1,2,5,10,.. + unsigned gainIndex[ 2 ] = {0, 0}; // index 0..7 + unsigned tag = 0; + bool valid = true; + bool freeRun = false; + std::vector< unsigned char > data; + std::vector< unsigned char > *dp = &data; +}; diff --git a/openhantek/src/hantekdso/controlsettings.cpp b/openhantek/src/hantekdso/controlsettings.cpp index 6b119baf..afde69a6 100644 --- a/openhantek/src/hantekdso/controlsettings.cpp +++ b/openhantek/src/hantekdso/controlsettings.cpp @@ -1,19 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "controlsettings.h" #include "hantekprotocol/definitions.h" namespace Dso { -ControlSettings::ControlSettings(const ControlSamplerateLimits * limits, size_t channelCount) : cmdGetLimits(channelCount) -{ +ControlSettings::ControlSettings( const ControlSamplerateLimits *limits, size_t channelCount ) : cmdGetLimits() { samplerate.limits = limits; - trigger.level.resize(channelCount); - voltage.resize(channelCount); - offsetLimit = new Hantek::OffsetsPerGainStep[channelCount]; + trigger.level.resize( channelCount ); + voltage.resize( channelCount ); + calibrationValues = new Hantek::CalibrationValues; } -ControlSettings::~ControlSettings() -{ - delete [] offsetLimit; -} +ControlSettings::~ControlSettings() { delete calibrationValues; } -} +} // namespace Dso diff --git a/openhantek/src/hantekdso/controlsettings.h b/openhantek/src/hantekdso/controlsettings.h index a328f507..6cb017cc 100644 --- a/openhantek/src/hantekdso/controlsettings.h +++ b/openhantek/src/hantekdso/controlsettings.h @@ -1,12 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "enums.h" -#include "hantekprotocol/types.h" -#include "hantekprotocol/bulkcode.h" #include "hantekprotocol/controlStructs.h" +#include "hantekprotocol/types.h" namespace Hantek { -struct OffsetsPerGainStep; +struct CalibrationValues; } namespace Dso { @@ -23,44 +24,44 @@ struct ControlSettingsSamplerateTarget { /// \brief Stores the current samplerate settings of the device. struct ControlSettingsSamplerate { ControlSettingsSamplerateTarget target; ///< The target samplerate values - const ControlSamplerateLimits * limits; ///< The samplerate limits + const ControlSamplerateLimits *limits; ///< The samplerate limits unsigned int downsampler = 1; ///< The variable downsampling factor - double current = 1e8; ///< The current samplerate + double current = 1e6; ///< The current samplerate }; /// \brief Stores the current trigger settings of the device. struct ControlSettingsTrigger { - std::vector level; ///< The trigger level for each channel in V - double position = 0.0; ///< The current pretrigger position - unsigned int point = 0; ///< The trigger position in Hantek coding - Dso::TriggerMode mode = Dso::TriggerMode::HARDWARE_SOFTWARE; ///< The trigger mode - Dso::Slope slope = Dso::Slope::Positive; ///< The trigger slope - bool special = false; ///< true, if the trigger source is special - unsigned int source = 0; ///< The trigger source + std::vector< double > level; ///< The trigger level for each channel in V + double position = 0.0; ///< The current pretrigger position + unsigned int point = 0; ///< The trigger position in Hantek coding + Dso::TriggerMode mode = Dso::TriggerMode::AUTO; ///< The trigger mode + Dso::Slope slope = Dso::Slope::Positive; ///< The trigger slope + unsigned int source = 0; ///< The trigger source + bool smooth = false; ///< Don't trigger on glitches }; /// \brief Stores the current amplification settings of the device. struct ControlSettingsVoltage { - double offset = 0.0; ///< The screen offset for each channel - double offsetReal = 0.0; ///< The real offset for each channel (Due to quantization) - unsigned gain = 0; ///< The gain id - bool used = false; ///< true, if the channel is used + double offset = 0.0; ///< The screen offset for each channel + unsigned gain = 0; ///< The gain id + bool used = false; ///< true, if the channel is used + bool inverted = false; ///< true, if the channel is inverted + double probeAttn = 1.0; ///< attenuation of probe + Dso::Coupling coupling = Dso::Coupling::DC; ///< The coupling }; /// \brief Stores the current settings of the device. struct ControlSettings { - ControlSettings(const ControlSamplerateLimits *limits, size_t channelCount); + ControlSettings( const ControlSamplerateLimits *limits, size_t channelCount ); ~ControlSettings(); - ControlSettingsSamplerate samplerate; ///< The samplerate settings - std::vector voltage; ///< The amplification settings - ControlSettingsTrigger trigger; ///< The trigger settings - RecordLengthID recordLengthId = 1; ///< The id in the record length array - unsigned usedChannels = 0; ///< Number of activated channels - unsigned swSampleMargin = 2000; ///< Software trigger, sample margin - Hantek::OffsetsPerGainStep *offsetLimit; ///< Calibration data for the channel offsets - - - Hantek::ControlBeginCommand beginCommandControl; + ControlSettings( const ControlSettings & ) = delete; + ControlSettings operator=( const ControlSettings & ) = delete; + ControlSettingsSamplerate samplerate; ///< The samplerate settings + std::vector< ControlSettingsVoltage > voltage; ///< The amplification settings + ControlSettingsTrigger trigger; ///< The trigger settings + RecordLengthID recordLengthId = 1; ///< The id in the record length array + unsigned channelCount = 0; ///< Number of activated channels + Hantek::CalibrationValues *calibrationValues; ///< Calibration data for the channel offsets & gains Hantek::ControlGetLimits cmdGetLimits; }; -} +} // namespace Dso diff --git a/openhantek/src/hantekdso/controlspecification.cpp b/openhantek/src/hantekdso/controlspecification.cpp index 9cdccc9f..bbffd9ab 100644 --- a/openhantek/src/hantekdso/controlspecification.cpp +++ b/openhantek/src/hantekdso/controlspecification.cpp @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "controlspecification.h" -Dso::ControlSpecification::ControlSpecification(unsigned channels) : channels(channels) { - voltageLimit.resize(channels); +Dso::ControlSpecification::ControlSpecification( unsigned channels ) : channels( channels ) { + voltageScale.resize( channels ); + voltageOffset.resize( channels ); } diff --git a/openhantek/src/hantekdso/controlspecification.h b/openhantek/src/hantekdso/controlspecification.h index 964207bb..7515d625 100644 --- a/openhantek/src/hantekdso/controlspecification.h +++ b/openhantek/src/hantekdso/controlspecification.h @@ -5,7 +5,6 @@ #include "enums.h" #include "hantekprotocol/definitions.h" #include "hantekprotocol/types.h" -#include "hantekprotocol/bulkcode.h" #include namespace Dso { @@ -14,81 +13,61 @@ using namespace Hantek; /// \brief Stores the samplerate limits for calculations. struct ControlSamplerateLimits { - double base; ///< The base for sample rate calculations - double max; ///< The maximum sample rate - unsigned int maxDownsampler; ///< The maximum downsampling ratio - std::vector recordLengths; ///< Available record lengths, UINT_MAX means rolling + double base; ///< The base for sample rate calculations + double max; ///< The maximum sample rate + std::vector< unsigned > recordLengths; ///< Available record lengths, UINT_MAX means rolling }; /// \brief Stores the samplerate limits. struct ControlSpecificationSamplerate { - ControlSamplerateLimits single = {50e6, 50e6, 0, std::vector()}; ///< The limits for single channel mode - ControlSamplerateLimits multi = {100e6, 100e6, 0, std::vector()}; ///< The limits for multi channel mode + ControlSamplerateLimits single = {50e6, 50e6, std::vector< unsigned >()}; ///< The limits for single channel mode + ControlSamplerateLimits multi = {100e6, 100e6, std::vector< unsigned >()}; ///< The limits for multi channel mode }; struct ControlSpecificationGainLevel { /// The index of the selected gain on the hardware - unsigned char gainIndex; - /// Available voltage steps in V/screenheight - double gainSteps; + unsigned char gainValue; // 1, 2, 5, 10, .. + /// Available voltage steps in V/div + double Vdiv; + // double gainError; }; struct FixedSampleRate { - unsigned char id; double samplerate; -}; - -struct SpecialTriggerChannel { - std::string name; - int hardwareID; + unsigned char id; + unsigned oversampling; }; /// \brief Stores the specifications of the currently connected device. struct ControlSpecification { - ControlSpecification(unsigned channels); + explicit ControlSpecification( unsigned channels ); const ChannelID channels; - // Interface - BulkCode cmdSetChannels = BulkCode::INVALID; ///< Command for setting used channels - BulkCode cmdSetSamplerate = BulkCode::INVALID; ///< Command for samplerate settings - BulkCode cmdSetRecordLength = BulkCode::INVALID; ///< Command for buffer settings - BulkCode cmdSetTrigger = BulkCode::INVALID; ///< Command for trigger settings - BulkCode cmdSetPretrigger = BulkCode::INVALID; ///< Command for pretrigger settings - BulkCode cmdForceTrigger = BulkCode::FORCETRIGGER; ///< Command for forcing a trigger event - BulkCode cmdCaptureStart = BulkCode::STARTSAMPLING; ///< Command for starting the sampling - BulkCode cmdTriggerEnabled = BulkCode::ENABLETRIGGER; ///< Command for enabling the trigger - BulkCode cmdGetData = BulkCode::GETDATA; ///< Command for retrieve sample data - BulkCode cmdGetCaptureState = BulkCode::GETCAPTURESTATE; ///< Command for retrieve the capture state - BulkCode cmdSetGain = BulkCode::SETGAIN; ///< Command for setting the gain - // Limits - ControlSpecificationSamplerate samplerate; ///< The samplerate specifications - std::vector bufferDividers; ///< Samplerate dividers for record lengths - unsigned char sampleSize; ///< Number of bits per sample + ControlSpecificationSamplerate samplerate; ///< The samplerate specifications - /// For devices that support only fixed sample rates (isFixedSamplerateDevice=true) - std::vector fixedSampleRates; + /// For devices that support only fixed sample rates + std::vector< FixedSampleRate > fixedSampleRates; // Calibration - - /// The sample values at the top of the screen - typedef std::vector VoltageLimit; - std::vector voltageLimit; // Per channel + /// DSO6022 has calibration in small EEPROM, DDS120 has big fw EEPROM + bool hasCalibrationEEPROM = true; + bool isDemoDevice = false; + /// The sample values for one div + typedef std::vector< double > VoltageScale; + std::vector< VoltageScale > voltageScale; // Per channel + typedef std::vector< double > VoltageOffset; + std::vector< VoltageOffset > voltageOffset; // Per channel /// Gain levels - std::vector gain; + std::vector< ControlSpecificationGainLevel > gain; // Features - std::vector specialTriggerChannels; - std::vector couplings = {Dso::Coupling::DC, Dso::Coupling::AC}; - std::vector triggerModes = {TriggerMode::HARDWARE_SOFTWARE, TriggerMode::WAIT_FORCE, - TriggerMode::SINGLE}; - bool isFixedSamplerateDevice = false; - bool isSoftwareTriggerDevice = false; - bool useControlNoBulk = false; - bool supportsCaptureState = true; - bool supportsOffset = true; - bool supportsCouplingRelays = true; + std::vector< Coupling > couplings = {Dso::Coupling::DC, Dso::Coupling::AC}; + std::vector< TriggerMode > triggerModes = {TriggerMode::ROLL, TriggerMode::AUTO, TriggerMode::NORMAL, TriggerMode::SINGLE}; int fixedUSBinLength = 0; + bool hasACcoupling = false; + + QList< double > calfreqSteps; }; -} +} // namespace Dso diff --git a/openhantek/src/hantekdso/dsomodel.cpp b/openhantek/src/hantekdso/dsomodel.cpp index 968bb55e..c517ebe6 100644 --- a/openhantek/src/hantekdso/dsomodel.cpp +++ b/openhantek/src/hantekdso/dsomodel.cpp @@ -1,13 +1,13 @@ - // SPDX-License-Identifier: GPL-2.0+ #include "dsomodel.h" #include "modelregistry.h" -DSOModel::DSOModel(int id, long vendorID, long productID, long vendorIDnoFirmware, long productIDnoFirmware, - const std::string &firmwareToken, const std::string &name, - const Dso::ControlSpecification &&specification) - : ID(id), vendorID(vendorID), productID(productID), vendorIDnoFirmware(vendorIDnoFirmware), - productIDnoFirmware(productIDnoFirmware), firmwareToken(firmwareToken), name(name), specification(specification) { - ModelRegistry::get()->add(this); +DSOModel::DSOModel( int id, unsigned vendorID, unsigned productID, unsigned vendorIDnoFirmware, unsigned productIDnoFirmware, + unsigned firmwareVersion, const std::string &firmwareToken, const std::string &name, + const Dso::ControlSpecification &&specification ) + : ID( id ), vendorID( vendorID ), productID( productID ), vendorIDnoFirmware( vendorIDnoFirmware ), + productIDnoFirmware( productIDnoFirmware ), firmwareVersion( firmwareVersion ), firmwareToken( firmwareToken ), name( name ), + specification( specification ) { + ModelRegistry::get()->add( this ); } diff --git a/openhantek/src/hantekdso/dsomodel.h b/openhantek/src/hantekdso/dsomodel.h index eb469304..053cf584 100644 --- a/openhantek/src/hantekdso/dsomodel.h +++ b/openhantek/src/hantekdso/dsomodel.h @@ -1,4 +1,3 @@ - // SPDX-License-Identifier: GPL-2.0+ #pragma once @@ -17,23 +16,26 @@ class HantekDsoControl; class DSOModel { public: const int ID; - const long vendorID; ///< The USB vendor ID - const long productID; ///< The USB product ID - const long vendorIDnoFirmware; ///< The USB vendor ID if no firmware is flashed yet - const long productIDnoFirmware; ///< The USB product ID if no firmware is flashed yet + const unsigned vendorID; ///< The USB vendor ID + const unsigned productID; ///< The USB product ID + const unsigned vendorIDnoFirmware; ///< The USB vendor ID if no firmware is uploaded yet + const unsigned productIDnoFirmware; ///< The USB product ID if no firmware is uploaded yet + const unsigned firmwareVersion; ///< The BCD version of the uploaded firmware /// Firmwares are compiled into the executable with a filename pattern of devicename-firmware.hex and /// devicename-loader.hex. /// The firmwareToken is the "devicename" of the pattern above. std::string firmwareToken; std::string name; ///< User visible name. Does not need internationalisation/translation. + protected: Dso::ControlSpecification specification; public: /// This model may need to modify the HantekDsoControl class to work correctly - virtual void applyRequirements(HantekDsoControl *) const = 0; - DSOModel(int id, long vendorID, long productID, long vendorIDnoFirmware, long productIDnoFirmware, - const std::string &firmwareToken, const std::string &name, const Dso::ControlSpecification &&specification); + virtual void applyRequirements( HantekDsoControl * ) const = 0; + DSOModel( int id, unsigned vendorID, unsigned productID, unsigned vendorIDnoFirmware, unsigned productIDnoFirmware, + unsigned firmwareVersion, const std::string &firmwareToken, const std::string &name, + const Dso::ControlSpecification &&specification ); virtual ~DSOModel() = default; /// Return the device specifications inline const Dso::ControlSpecification *spec() const { return &specification; } diff --git a/openhantek/src/hantekdso/dsosamples.h b/openhantek/src/hantekdso/dsosamples.h index faeb6837..568c84e9 100644 --- a/openhantek/src/hantekdso/dsosamples.h +++ b/openhantek/src/hantekdso/dsosamples.h @@ -8,8 +8,14 @@ #include struct DSOsamples { - std::vector> data; ///< Pointer to input data from device - double samplerate = 0.0; ///< The samplerate of the input data - bool append = false; ///< true, if waiting data should be appended + std::vector< std::vector< double > > data; ///< Pointer to input data from device + double samplerate = 0.0; ///< The samplerate of the input data + unsigned char clipped = 0; ///< Bitmask of clipped channels + bool liveTrigger = false; ///< live samples are triggered + unsigned triggeredPosition = 0; ///< position for a triggered trace, 0 = not triggered + double pulseWidth1 = 0.0; ///< width from trigger point to next opposite slope + double pulseWidth2 = 0.0; ///< width from next opposite slope to third slope + bool freeRunning = false; ///< trigger: NONE, half sample count + unsigned tag = 0; ///< track individual sample blocks (debug support) mutable QReadWriteLock lock; }; diff --git a/openhantek/src/hantekdso/enums.cpp b/openhantek/src/hantekdso/enums.cpp index 8c573ffd..41693d6c 100644 --- a/openhantek/src/hantekdso/enums.cpp +++ b/openhantek/src/hantekdso/enums.cpp @@ -1,96 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "enums.h" #include namespace Dso { - Enum TriggerModeEnum; - Enum SlopeEnum; - Enum GraphFormatEnum; - - /// \brief Return string representation of the given channel mode. - /// \param mode The ::ChannelMode that should be returned as string. - /// \return The string that should be used in labels etc., empty when invalid. - QString channelModeString(ChannelMode mode) { - switch (mode) { - case ChannelMode::Voltage: - return QCoreApplication::tr("Voltage"); - case ChannelMode::Spectrum: - return QCoreApplication::tr("Spectrum"); - } - return QString(); - } +Enum< Dso::TriggerMode, Dso::TriggerMode::ROLL, Dso::TriggerMode::SINGLE > TriggerModeEnum; +Enum< Dso::Slope, Dso::Slope::Positive, Dso::Slope::Both > SlopeEnum; +Enum< Dso::GraphFormat, Dso::GraphFormat::TY, Dso::GraphFormat::XY > GraphFormatEnum; - /// \brief Return string representation of the given graph format. - /// \param format The ::GraphFormat that should be returned as string. - /// \return The string that should be used in labels etc. - QString graphFormatString(GraphFormat format) { - switch (format) { - case GraphFormat::TY: - return QCoreApplication::tr("T - Y"); - case GraphFormat::XY: - return QCoreApplication::tr("X - Y"); - } - return QString(); +/// \brief Return string representation of the given graph format. +/// \param format The ::GraphFormat that should be returned as string. +/// \return The string that should be used in labels etc. +QString graphFormatString( GraphFormat format ) { + switch ( format ) { + case GraphFormat::TY: + return QCoreApplication::tr( "T - Y" ); + case GraphFormat::XY: + return QCoreApplication::tr( "X - Y" ); } + return QString(); +} - /// \brief Return string representation of the given channel coupling. - /// \param coupling The ::Coupling that should be returned as string. - /// \return The string that should be used in labels etc. - QString couplingString(Coupling coupling) { - switch (coupling) { - case Coupling::AC: - return QCoreApplication::tr("AC"); - case Coupling::DC: - return QCoreApplication::tr("DC"); - case Coupling::GND: - return QCoreApplication::tr("GND"); - } - return QString(); +/// \brief Return string representation of the given channel coupling. +/// \param coupling The ::Coupling that should be returned as string. +/// \return The string that should be used in labels etc. +QString couplingString( Coupling coupling ) { + switch ( coupling ) { + case Coupling::AC: + return QCoreApplication::tr( "AC" ); + case Coupling::DC: + return QCoreApplication::tr( "DC" ); + case Coupling::GND: + return QCoreApplication::tr( "GND" ); } + return QString(); +} - /// \brief Return string representation of the given trigger mode. - /// \param mode The ::TriggerMode that should be returned as string. - /// \return The string that should be used in labels etc. - QString triggerModeString(TriggerMode mode) { - switch (mode) { - case TriggerMode::WAIT_FORCE: - return QCoreApplication::tr("Wait/Force"); - case TriggerMode::HARDWARE_SOFTWARE: - return QCoreApplication::tr("Hard-/Software"); - case TriggerMode::SINGLE: - return QCoreApplication::tr("Single"); - } - return QString(); - } - - /// \brief Return string representation of the given trigger slope. - /// \param slope The ::Slope that should be returned as string. - /// \return The string that should be used in labels etc. - QString slopeString(Slope slope) { - switch (slope) { - case Slope::Positive: - return QString::fromUtf8("\u2197"); - case Slope::Negative: - return QString::fromUtf8("\u2198"); - default: - return QString(); - } +/// \brief Return string representation of the given trigger mode. +/// \param mode The ::TriggerMode that should be returned as string. +/// \return The string that should be used in labels etc. +QString triggerModeString( TriggerMode mode ) { + switch ( mode ) { + case TriggerMode::ROLL: + return QCoreApplication::tr( "Roll" ); + case TriggerMode::AUTO: + return QCoreApplication::tr( "Auto" ); + case TriggerMode::NORMAL: + return QCoreApplication::tr( "Normal" ); + case TriggerMode::SINGLE: + return QCoreApplication::tr( "Single" ); } + return QString(); +} - /// \brief Return string representation of the given graph interpolation mode. - /// \param interpolation The ::InterpolationMode that should be returned as - /// string. - /// \return The string that should be used in labels etc. - QString interpolationModeString(InterpolationMode interpolation) { - switch (interpolation) { - case INTERPOLATION_OFF: - return QCoreApplication::tr("Off"); - case INTERPOLATION_LINEAR: - return QCoreApplication::tr("Linear"); - case INTERPOLATION_SINC: - return QCoreApplication::tr("Sinc"); - default: - return QString(); - } +/// \brief Return string representation of the given trigger slope. +/// \param slope The ::Slope that should be returned as string. +/// \return The string that should be used in labels etc. +QString slopeString( Slope slope ) { + switch ( slope ) { +#if defined Q_OS_WIN // avoid unicode mismatch + case Slope::Positive: + return QString::fromUtf8( "/" ); + case Slope::Negative: + return QString::fromUtf8( "\\" ); + case Slope::Both: + return QString::fromUtf8( "X" ); +#else + case Slope::Positive: + return QString::fromUtf8( "\u2197" ); // "↗" + case Slope::Negative: + return QString::fromUtf8( "\u2198" ); // "↘" + case Slope::Both: + return QString::fromUtf8( "\u2928" ); // "⤨" +#endif } + return QString(); } + +} // namespace Dso diff --git a/openhantek/src/hantekdso/enums.h b/openhantek/src/hantekdso/enums.h index f89711c9..95dde2d9 100644 --- a/openhantek/src/hantekdso/enums.h +++ b/openhantek/src/hantekdso/enums.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "utils/enumclass.h" @@ -18,53 +20,54 @@ enum GraphFormat { XY ///< CH1 on X-axis, CH2 on Y-axis }; -extern Enum GraphFormatEnum; +extern Enum< Dso::GraphFormat, Dso::GraphFormat::TY, Dso::GraphFormat::XY > GraphFormatEnum; /// \enum Coupling /// \brief The coupling modes for the channels. enum class Coupling { - AC, ///< Offset filtered out by condensator DC, ///< No filtering + AC, ///< Offset filtered out by capacitor GND ///< Channel is grounded }; /// \enum TriggerMode /// \brief The different triggering modes. enum class TriggerMode { - HARDWARE_SOFTWARE, ///< Normal hardware trigger (or software trigger) mode - WAIT_FORCE, ///< Automatic without trigger event - SINGLE ///< Stop after the first trigger event -}; -extern Enum TriggerModeEnum; + ROLL, ///< Free running without any trigger + AUTO, ///< Automatic without trigger event + NORMAL, ///< Normal hardware trigger (or software trigger) mode + SINGLE ///< Stop after the first trigger event +}; // +extern Enum< Dso::TriggerMode, Dso::TriggerMode::ROLL, Dso::TriggerMode::SINGLE > TriggerModeEnum; /// \enum Slope /// \brief The slope that causes a trigger. enum class Slope : uint8_t { Positive = 0, ///< From lower to higher voltage - Negative = 1 ///< From higher to lower voltage + Negative = 1, ///< From higher to lower voltage + Both = 2 ///< At 1st level crossing up or down }; -extern Enum SlopeEnum; +extern Enum< Dso::Slope, Dso::Slope::Positive, Dso::Slope::Both > SlopeEnum; /// \enum InterpolationMode /// \brief The different interpolation modes for the graphs. enum InterpolationMode { INTERPOLATION_OFF = 0, ///< Just dots for each sample INTERPOLATION_LINEAR, ///< Sample dots connected by lines - INTERPOLATION_SINC, ///< Smooth graph through the dots INTERPOLATION_COUNT ///< Total number of interpolation modes }; -QString channelModeString(ChannelMode mode); -QString graphFormatString(GraphFormat format); -QString couplingString(Coupling coupling); -QString triggerModeString(TriggerMode mode); -QString slopeString(Slope slope); -QString interpolationModeString(InterpolationMode interpolation); -} +// QString channelModeString(ChannelMode mode); +QString graphFormatString( GraphFormat format ); +QString couplingString( Coupling coupling ); +QString triggerModeString( TriggerMode mode ); +QString slopeString( Slope slope ); +// QString interpolationModeString(InterpolationMode interpolation); +} // namespace Dso -Q_DECLARE_METATYPE(Dso::TriggerMode) -Q_DECLARE_METATYPE(Dso::Slope) -Q_DECLARE_METATYPE(Dso::Coupling) -Q_DECLARE_METATYPE(Dso::GraphFormat) -Q_DECLARE_METATYPE(Dso::ChannelMode) -Q_DECLARE_METATYPE(Dso::InterpolationMode) +Q_DECLARE_METATYPE( Dso::TriggerMode ) +Q_DECLARE_METATYPE( Dso::Slope ) +Q_DECLARE_METATYPE( Dso::Coupling ) +Q_DECLARE_METATYPE( Dso::GraphFormat ) +Q_DECLARE_METATYPE( Dso::ChannelMode ) +Q_DECLARE_METATYPE( Dso::InterpolationMode ) diff --git a/openhantek/src/hantekdso/errorcodes.h b/openhantek/src/hantekdso/errorcodes.h index 97aee670..da187f60 100644 --- a/openhantek/src/hantekdso/errorcodes.h +++ b/openhantek/src/hantekdso/errorcodes.h @@ -12,4 +12,4 @@ enum class ErrorCode { PARAMETER = -3 ///< Parameter out of range }; -} +} // namespace Dso diff --git a/openhantek/src/hantekdso/hantekdsocontrol.cpp b/openhantek/src/hantekdso/hantekdsocontrol.cpp index bd50286d..960dc0ae 100644 --- a/openhantek/src/hantekdso/hantekdsocontrol.cpp +++ b/openhantek/src/hantekdso/hantekdsocontrol.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ +// #define TIMESTAMPDEBUG + #include #include #include @@ -12,1321 +14,792 @@ #include #include +#include + #include "hantekdsocontrol.h" -#include "hantekprotocol/bulkStructs.h" #include "hantekprotocol/controlStructs.h" -#include "models/modelDSO6022.h" -#include "usb/usbdevice.h" +#include "scopesettings.h" +#include "usb/scopedevice.h" using namespace Hantek; using namespace Dso; -/// \brief Start sampling process. -void HantekDsoControl::enableSampling(bool enabled) { - sampling = enabled; - - // Emit signals for initial settings - // emit availableRecordLengthsChanged(controlsettings.samplerate.limits->recordLengths); - // updateSamplerateLimits(); - // emit recordLengthChanged(getRecordLength()); - // if (!isRollMode()) emit recordTimeChanged((double)getRecordLength() / controlsettings.samplerate.current); - // emit samplerateChanged(controlsettings.samplerate.current); - - emit samplingStatusChanged(enabled); -} - -const USBDevice *HantekDsoControl::getDevice() const { return device; } - -const DSOsamples &HantekDsoControl::getLastSamples() { return result; } -HantekDsoControl::HantekDsoControl(USBDevice *device) - : device(device), specification(device->getModel()->spec()), - controlsettings(&(specification->samplerate.single), specification->channels) { - if (device == nullptr) throw new std::runtime_error("No usb device for HantekDsoControl"); - - qRegisterMetaType(); - - if (specification->fixedUSBinLength) device->overwriteInPacketLength(specification->fixedUSBinLength); +HantekDsoControl::HantekDsoControl( ScopeDevice *device, const DSOModel *model ) + : scopeDevice( device ), model( model ), specification( model->spec() ), + controlsettings( &( specification->samplerate.single ), specification->channels ) { + qRegisterMetaType< DSOsamples * >(); + if ( device && specification->fixedUSBinLength ) + device->overwriteInPacketLength( unsigned( specification->fixedUSBinLength ) ); // Apply special requirements by the devices model - device->getModel()->applyRequirements(this); - + model->applyRequirements( this ); retrieveChannelLevelData(); + stateMachineRunning = true; } + HantekDsoControl::~HantekDsoControl() { - while (firstBulkCommand) { - BulkCommand *t = firstBulkCommand->next; - delete firstBulkCommand; - firstBulkCommand = t; - } - while (firstControlCommand) { + while ( firstControlCommand ) { ControlCommand *t = firstControlCommand->next; delete firstControlCommand; firstControlCommand = t; } } -int HantekDsoControl::bulkCommand(const std::vector *command, int attempts) const { - if (specification->useControlNoBulk) return LIBUSB_SUCCESS; - - // Send BeginCommand control command - int errorCode = device->controlWrite(&controlsettings.beginCommandControl); - if (errorCode < 0) return errorCode; - - // Send bulk command - return device->bulkWrite(command->data(), command->size(), attempts); -} - -unsigned HantekDsoControl::getChannelCount() const { return specification->channels; } - -const ControlSettings *HantekDsoControl::getDeviceSettings() const { return &controlsettings; } - -const std::vector &HantekDsoControl::getAvailableRecordLengths() const { // - return controlsettings.samplerate.limits->recordLengths; -} - -double HantekDsoControl::getMinSamplerate() const { - return (double)specification->samplerate.single.base / specification->samplerate.single.maxDownsampler; -} - -double HantekDsoControl::getMaxSamplerate() const { - if (controlsettings.usedChannels <= 1) - return specification->samplerate.multi.max; - else - return specification->samplerate.single.max; -} - -bool HantekDsoControl::isSampling() const { return sampling; } - -/// \brief Updates the interval of the periodic thread timer. -void HantekDsoControl::updateInterval() { - // Check the current oscilloscope state everytime 25% of the time the buffer - // should be refilled - if (isRollMode()) - cycleTime = (int)((double)getPacketSize() / (isFastRate() ? 1 : specification->channels) / - controlsettings.samplerate.current * 250); - else - cycleTime = (int)((double)getRecordLength() / controlsettings.samplerate.current * 250); - - // Not more often than every 10 ms though but at least once every second - cycleTime = qBound(10, cycleTime, 1000); -} - -bool HantekDsoControl::isRollMode() const { - return controlsettings.samplerate.limits->recordLengths[controlsettings.recordLengthId] == UINT_MAX; -} - -bool HantekDsoControl::isFastRate() const { - return controlsettings.samplerate.limits == &specification->samplerate.multi; -} - -unsigned HantekDsoControl::getRecordLength() const { - return controlsettings.samplerate.limits->recordLengths[controlsettings.recordLengthId]; -} - -Dso::ErrorCode HantekDsoControl::retrieveChannelLevelData() { - // Get channel level data - int errorCode = device->controlRead(&controlsettings.cmdGetLimits); - if (errorCode < 0) { - qWarning() << tr("Couldn't get channel level data from oscilloscope"); - emit statusMessage(tr("Couldn't get channel level data from oscilloscope"), 0); - emit communicationError(); - return Dso::ErrorCode::CONNECTION; - } - - memcpy(controlsettings.offsetLimit, controlsettings.cmdGetLimits.data(), - sizeof(OffsetsPerGainStep) * specification->channels); - - return Dso::ErrorCode::NONE; -} - -unsigned HantekDsoControl::calculateTriggerPoint(unsigned value) { - unsigned result = value; - - // Each set bit inverts all bits with a lower value - for (unsigned bitValue = 1; bitValue; bitValue <<= 1) - if (result & bitValue) result ^= bitValue - 1; - - return result; -} - -std::pair HantekDsoControl::getCaptureState() const { - int errorCode; - - if (!specification->supportsCaptureState) return std::make_pair(CAPTURE_READY, 0); - - errorCode = bulkCommand(getCommand(BulkCode::GETCAPTURESTATE), 1); - if (errorCode < 0) { - qWarning() << "Getting capture state failed: " << libUsbErrorString(errorCode); - return std::make_pair(CAPTURE_ERROR, 0); - } - - BulkResponseGetCaptureState response; - errorCode = device->bulkRead(&response); - if (errorCode < 0) { - qWarning() << "Getting capture state failed: " << libUsbErrorString(errorCode); - return std::make_pair(CAPTURE_ERROR, 0); - } - - return std::make_pair((int)response.getCaptureState(), response.getTriggerPoint()); -} - -std::vector HantekDsoControl::getSamples(unsigned &previousSampleCount) const { - int errorCode; - if (!specification->useControlNoBulk) { - // Request data - errorCode = bulkCommand(getCommand(BulkCode::GETDATA), 1); - } else { - errorCode = device->controlWrite(getCommand(ControlCode::CONTROL_ACQUIIRE_HARD_DATA)); - } - if (errorCode < 0) { - qWarning() << "Getting sample data failed: " << libUsbErrorString(errorCode); - emit communicationError(); - return std::vector(); - } - - unsigned totalSampleCount = this->getSampleCount(); - - // To make sure no samples will remain in the scope buffer, also check the - // sample count before the last sampling started - if (totalSampleCount < previousSampleCount) { - std::swap(totalSampleCount, previousSampleCount); - } else { - previousSampleCount = totalSampleCount; - } - - unsigned dataLength = (specification->sampleSize > 8) ? totalSampleCount * 2 : totalSampleCount; - - // Save raw data to temporary buffer - std::vector data(dataLength); - int errorcode = device->bulkReadMulti(data.data(), dataLength); - if (errorcode < 0) { - qWarning() << "Getting sample data failed: " << libUsbErrorString(errorcode); - return std::vector(); - } - data.resize((size_t)errorcode); - - static unsigned id = 0; - ++id; - timestampDebug(QString("Received packet %1").arg(id)); - - return data; -} - -void HantekDsoControl::convertRawDataToSamples(const std::vector &rawData) { - const size_t totalSampleCount = (specification->sampleSize > 8) ? rawData.size() / 2 : rawData.size(); - - QWriteLocker locker(&result.lock); - result.samplerate = controlsettings.samplerate.current; - result.append = isRollMode(); - // Prepare result buffers - result.data.resize(specification->channels); - for (ChannelID channelCounter = 0; channelCounter < specification->channels; ++channelCounter) - result.data[channelCounter].clear(); - - const unsigned extraBitsSize = specification->sampleSize - 8; // Number of extra bits - const unsigned short extraBitsMask = (0x00ff << extraBitsSize) & 0xff00; // Mask for extra bits extraction - - // Convert channel data - if (isFastRate()) { - // Fast rate mode, one channel is using all buffers - ChannelID channel = 0; - for (; channel < specification->channels; ++channel) { - if (controlsettings.voltage[channel].used) break; - } - - if (channel >= specification->channels) return; - - // Resize sample vector - result.data[channel].resize(totalSampleCount); - - const unsigned gainID = controlsettings.voltage[channel].gain; - const unsigned short limit = specification->voltageLimit[channel][gainID]; - const double offset = controlsettings.voltage[channel].offsetReal; - const double gainStep = specification->gain[gainID].gainSteps; - - // Convert data from the oscilloscope and write it into the sample buffer - unsigned bufferPosition = controlsettings.trigger.point * 2; - if (specification->sampleSize > 8) { - for (unsigned pos = 0; pos < totalSampleCount; ++pos, ++bufferPosition) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - - const unsigned short low = rawData[bufferPosition]; - const unsigned extraBitsPosition = bufferPosition % specification->channels; - const unsigned shift = (8 - (specification->channels - 1 - extraBitsPosition) * extraBitsSize); - const unsigned short high = - ((unsigned short int)rawData[totalSampleCount + bufferPosition - extraBitsPosition] << shift) & - extraBitsMask; - - result.data[channel][pos] = ((double)(low + high) / limit - offset) * gainStep; - } - } else { - for (unsigned pos = 0; pos < totalSampleCount; ++pos, ++bufferPosition) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - - double dataBuf = (double)((int)rawData[bufferPosition]); - result.data[channel][pos] = (dataBuf / limit - offset) * gainStep; - } - } - } else { - // Normal mode, channels are using their separate buffers - for (ChannelID channel = 0; channel < specification->channels; ++channel) { - result.data[channel].resize(totalSampleCount / specification->channels); - - const unsigned gainID = controlsettings.voltage[channel].gain; - const unsigned short limit = specification->voltageLimit[channel][gainID]; - const double offset = controlsettings.voltage[channel].offsetReal; - const double gainStep = specification->gain[gainID].gainSteps; - int shiftDataBuf = 0; - - // Convert data from the oscilloscope and write it into the sample buffer - unsigned bufferPosition = controlsettings.trigger.point * 2; - if (specification->sampleSize > 8) { - // Additional most significant bits after the normal data - unsigned extraBitsIndex = 8 - channel * 2; // Bit position offset for extra bits extraction - - for (unsigned realPosition = 0; realPosition < result.data[channel].size(); - ++realPosition, bufferPosition += specification->channels) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - - const unsigned short low = rawData[bufferPosition + specification->channels - 1 - channel]; - const unsigned short high = - ((unsigned short int)rawData[totalSampleCount + bufferPosition] << extraBitsIndex) & - extraBitsMask; - - result.data[channel][realPosition] = ((double)(low + high) / limit - offset) * gainStep; - } - } else if (device->getModel()->ID == ModelDSO6022BE::ID) { - // if device is 6022BE, drop heading & trailing samples - const unsigned DROP_DSO6022_HEAD = 0x410; - const unsigned DROP_DSO6022_TAIL = 0x3F0; - if (!isRollMode()) { - result.data[channel].resize(result.data[channel].size() - (DROP_DSO6022_HEAD + DROP_DSO6022_TAIL)); - // if device is 6022BE, offset DROP_DSO6022_HEAD incrementally - bufferPosition += DROP_DSO6022_HEAD * 2; - } - bufferPosition += channel; - shiftDataBuf = 0x83; - } else { - bufferPosition += specification->channels - 1 - channel; - } - for (unsigned pos = 0; pos < result.data[channel].size(); - ++pos, bufferPosition += specification->channels) { - if (bufferPosition >= totalSampleCount) bufferPosition %= totalSampleCount; - double dataBuf = (double)((int)(rawData[bufferPosition] - shiftDataBuf)); - result.data[channel][pos] = (dataBuf / limit - offset) * gainStep; - } - } - } -} -double HantekDsoControl::getBestSamplerate(double samplerate, bool fastRate, bool maximum, - unsigned *downsampler) const { - // Abort if the input value is invalid - if (samplerate <= 0.0) return 0.0; +bool HantekDsoControl::deviceNotConnected() { return !scopeDevice->isConnected(); } - double bestSamplerate = 0.0; - // Get samplerate specifications for this mode and model - const ControlSamplerateLimits *limits; - if (fastRate) - limits = &(specification->samplerate.multi); +void HantekDsoControl::restoreTargets() { + // qDebug() << "restoreTargets()"; + if ( controlsettings.samplerate.target.samplerateSet == ControlSettingsSamplerateTarget::Samplerrate ) + setSamplerate(); else - limits = &(specification->samplerate.single); - - // Get downsampling factor that would provide the requested rate - double bestDownsampler = limits->base / specification->bufferDividers[controlsettings.recordLengthId] / samplerate; - // Base samplerate sufficient, or is the maximum better? - if (bestDownsampler < 1.0 && - (samplerate <= limits->max / specification->bufferDividers[controlsettings.recordLengthId] || !maximum)) { - bestDownsampler = 0.0; - bestSamplerate = limits->max / specification->bufferDividers[controlsettings.recordLengthId]; - } else { - switch (specification->cmdSetSamplerate) { - case BulkCode::SETTRIGGERANDSAMPLERATE: - // DSO-2090 supports the downsampling factors 1, 2, 4 and 5 using - // valueFast or all even values above using valueSlow - if ((maximum && bestDownsampler <= 5.0) || (!maximum && bestDownsampler < 6.0)) { - // valueFast is used - if (maximum) { - // The samplerate shall not be higher, so we round up - bestDownsampler = ceil(bestDownsampler); - if (bestDownsampler > 2.0) // 3 and 4 not possible with the DSO-2090 - bestDownsampler = 5.0; - } else { - // The samplerate shall not be lower, so we round down - bestDownsampler = floor(bestDownsampler); - if (bestDownsampler > 2.0 && bestDownsampler < 5.0) // 3 and 4 not possible with the DSO-2090 - bestDownsampler = 2.0; - } - } else { - // valueSlow is used - if (maximum) { - bestDownsampler = ceil(bestDownsampler / 2.0) * 2.0; // Round up to next even value - } else { - bestDownsampler = floor(bestDownsampler / 2.0) * 2.0; // Round down to next even value - } - if (bestDownsampler > 2.0 * 0x10001) // Check for overflow - bestDownsampler = 2.0 * 0x10001; - } - break; - - case BulkCode::CSETTRIGGERORSAMPLERATE: - // DSO-5200 may not supports all downsampling factors, requires testing - if (maximum) { - bestDownsampler = ceil(bestDownsampler); // Round up to next integer value - } else { - bestDownsampler = floor(bestDownsampler); // Round down to next integer value - } - break; - - case BulkCode::ESETTRIGGERORSAMPLERATE: - // DSO-2250 doesn't have a fast value, so it supports all downsampling - // factors - if (maximum) { - bestDownsampler = ceil(bestDownsampler); // Round up to next integer value - } else { - bestDownsampler = floor(bestDownsampler); // Round down to next integer value - } - break; - - default: - return 0.0; - } - - // Limit maximum downsampler value to avoid overflows in the sent commands - if (bestDownsampler > limits->maxDownsampler) bestDownsampler = limits->maxDownsampler; - - bestSamplerate = limits->base / bestDownsampler / specification->bufferDividers[controlsettings.recordLengthId]; - } - - if (downsampler) *downsampler = (unsigned)bestDownsampler; - return bestSamplerate; -} - -unsigned HantekDsoControl::getSampleCount() const { - if (isRollMode()) { - // TODO handle libusb error - return getPacketSize(); - } else { - if (isFastRate()) - return getRecordLength(); - else - return getRecordLength() * specification->channels; - } + setRecordTime(); } -unsigned HantekDsoControl::updateRecordLength(RecordLengthID index) { - if (index >= controlsettings.samplerate.limits->recordLengths.size()) return 0; - - switch (specification->cmdSetRecordLength) { - case BulkCode::SETTRIGGERANDSAMPLERATE: - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE)->setRecordLength(index); - break; - - case BulkCode::DSETBUFFER: - if (specification->cmdSetPretrigger == BulkCode::FSETBUFFER) { - modifyCommand(BulkCode::DSETBUFFER)->setRecordLength(index); - } else { - // SetBuffer5200 bulk command for record length - BulkSetBuffer5200 *commandSetBuffer5200 = modifyCommand(BulkCode::DSETBUFFER); - - commandSetBuffer5200->setUsedPre(DTriggerPositionUsed::ON); - commandSetBuffer5200->setUsedPost(DTriggerPositionUsed::ON); - commandSetBuffer5200->setRecordLength(index); - } - - break; - - default: - return 0; - } - - // Check if the divider has changed and adapt samplerate limits accordingly - bool bDividerChanged = - specification->bufferDividers[index] != specification->bufferDividers[controlsettings.recordLengthId]; - controlsettings.recordLengthId = index; - - if (bDividerChanged) { - this->updateSamplerateLimits(); +void HantekDsoControl::updateSamplerateLimits() { + QList< double > sampleSteps; + double limit = isSingleChannel() ? specification->samplerate.single.max : specification->samplerate.multi.max; - // Samplerate dividers changed, recalculate it - this->restoreTargets(); + if ( controlsettings.samplerate.current > limit ) { + setSamplerate( limit ); } - - return controlsettings.samplerate.limits->recordLengths[index]; -} - -unsigned HantekDsoControl::updateSamplerate(unsigned downsampler, bool fastRate) { - // Get samplerate limits - const ControlSamplerateLimits *limits = - fastRate ? &specification->samplerate.multi : &specification->samplerate.single; - - // Set the calculated samplerate - switch (specification->cmdSetSamplerate) { - case BulkCode::SETTRIGGERANDSAMPLERATE: { - short int downsamplerValue = 0; - unsigned char samplerateId = 0; - bool downsampling = false; - - if (downsampler <= 5) { - // All dividers up to 5 are done using the special samplerate IDs - if (downsampler == 0 && limits->base >= limits->max) - samplerateId = 1; - else if (downsampler <= 2) - samplerateId = downsampler; - else { // Downsampling factors 3 and 4 are not supported - samplerateId = 3; - downsampler = 5; - downsamplerValue = (short int)0xffff; - } - } else { - // For any dividers above the downsampling factor can be set directly - downsampler &= ~0x0001; // Only even values possible - downsamplerValue = (short int)(0x10001 - (downsampler >> 1)); - - downsampling = true; + for ( auto &v : specification->fixedSampleRates ) { + if ( v.samplerate <= limit ) { + sampleSteps << v.samplerate; } - - // Pointers to needed commands - BulkSetTriggerAndSamplerate *commandSetTriggerAndSamplerate = - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE); - - // Store if samplerate ID or downsampling factor is used - commandSetTriggerAndSamplerate->setDownsamplingMode(downsampling); - // Store samplerate ID - commandSetTriggerAndSamplerate->setSamplerateId(samplerateId); - // Store downsampling factor - commandSetTriggerAndSamplerate->setDownsampler(downsamplerValue); - // Set fast rate when used - commandSetTriggerAndSamplerate->setFastRate(false /*fastRate*/); - - break; - } - case BulkCode::CSETTRIGGERORSAMPLERATE: { - // Split the resulting divider into the values understood by the device - // The fast value is kept at 4 (or 3) for slow sample rates - long int valueSlow = qMax(((long int)downsampler - 3) / 2, (long int)0); - unsigned char valueFast = downsampler - valueSlow * 2; - - // Pointers to needed commands - BulkSetSamplerate5200 *commandSetSamplerate5200 = - modifyCommand(BulkCode::CSETTRIGGERORSAMPLERATE); - BulkSetTrigger5200 *commandSetTrigger5200 = - modifyCommand(BulkCode::ESETTRIGGERORSAMPLERATE); - - // Store samplerate fast value - commandSetSamplerate5200->setSamplerateFast(4 - valueFast); - // Store samplerate slow value (two's complement) - commandSetSamplerate5200->setSamplerateSlow(valueSlow == 0 ? 0 : 0xffff - valueSlow); - // Set fast rate when used - commandSetTrigger5200->setFastRate(fastRate); - - break; - } - case BulkCode::ESETTRIGGERORSAMPLERATE: { - // Pointers to needed commands - BulkSetSamplerate2250 *commandSetSamplerate2250 = - modifyCommand(BulkCode::ESETTRIGGERORSAMPLERATE); - - bool downsampling = downsampler > 1; - // Store downsampler state value - commandSetSamplerate2250->setDownsampling(downsampling); - // Store samplerate value - commandSetSamplerate2250->setSamplerate(downsampling ? 0x10001 - downsampler : 0); - // Set fast rate when used - commandSetSamplerate2250->setFastRate(fastRate); - - break; - } - default: - return UINT_MAX; - } - - // Update settings - bool fastRateChanged = fastRate != (controlsettings.samplerate.limits == &specification->samplerate.multi); - if (fastRateChanged) { controlsettings.samplerate.limits = limits; } - - controlsettings.samplerate.downsampler = downsampler; - if (downsampler) - controlsettings.samplerate.current = controlsettings.samplerate.limits->base / - specification->bufferDividers[controlsettings.recordLengthId] / - downsampler; - else - controlsettings.samplerate.current = - controlsettings.samplerate.limits->max / specification->bufferDividers[controlsettings.recordLengthId]; - - // Update dependencies - this->setPretriggerPosition(controlsettings.trigger.position); - - // Emit signals for changed settings - if (fastRateChanged) { - emit availableRecordLengthsChanged(controlsettings.samplerate.limits->recordLengths); - emit recordLengthChanged(getRecordLength()); } - - // Check for Roll mode - if (!isRollMode()) emit recordTimeChanged((double)getRecordLength() / controlsettings.samplerate.current); - emit samplerateChanged(controlsettings.samplerate.current); - - return downsampler; + // qDebug() << "HDC::updateSamplerateLimits " << sampleSteps; + emit samplerateSet( 1, sampleSteps ); } -void HantekDsoControl::restoreTargets() { - if (controlsettings.samplerate.target.samplerateSet == ControlSettingsSamplerateTarget::Samplerrate) - this->setSamplerate(); - else - this->setRecordTime(); -} -void HantekDsoControl::updateSamplerateLimits() { - if (specification->isFixedSamplerateDevice) { - QList sampleSteps; - for (auto &v : specification->fixedSampleRates) { sampleSteps << v.samplerate; } - emit samplerateSet(1, sampleSteps); - } else { - // Works only if the minimum samplerate for normal mode is lower than for fast - // rate mode, which is the case for all models - const double min = - (double)specification->samplerate.single.base / specification->samplerate.single.maxDownsampler; - const double max = getMaxSamplerate(); - - emit samplerateLimitsChanged(min / specification->bufferDividers[controlsettings.recordLengthId], - max / specification->bufferDividers[controlsettings.recordLengthId]); +void HantekDsoControl::controlSetSamplerate( uint8_t sampleIndex ) { + static uint8_t lastIndex = 0xFF; + uint8_t id = specification->fixedSampleRates[ sampleIndex ].id; + modifyCommand< ControlSetSamplerate >( ControlCode::CONTROL_SETSAMPLERATE )->setSamplerate( id, sampleIndex ); + if ( sampleIndex != lastIndex ) { // samplerate has changed, start new sampling + restartSampling(); } + lastIndex = sampleIndex; } -Dso::ErrorCode HantekDsoControl::setRecordLength(unsigned index) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - if (!updateRecordLength(index)) return Dso::ErrorCode::PARAMETER; - - restoreTargets(); - setPretriggerPosition(controlsettings.trigger.position); - - emit recordLengthChanged(getRecordLength()); - return Dso::ErrorCode::NONE; -} -Dso::ErrorCode HantekDsoControl::setSamplerate(double samplerate) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; +Dso::ErrorCode HantekDsoControl::setSamplerate( double samplerate ) { + // printf( "HDC::setSamplerate( %g )\n", samplerate ); + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; - if (samplerate == 0.0) { + if ( samplerate == 0.0 ) { samplerate = controlsettings.samplerate.target.samplerate; } else { controlsettings.samplerate.target.samplerate = samplerate; controlsettings.samplerate.target.samplerateSet = ControlSettingsSamplerateTarget::Samplerrate; } - - if (!specification->isSoftwareTriggerDevice) { - // When possible, enable fast rate if it is required to reach the requested - // samplerate - bool fastRate = (controlsettings.usedChannels <= 1) && - (samplerate > specification->samplerate.single.max / - specification->bufferDividers[controlsettings.recordLengthId]); - - // What is the nearest, at least as high samplerate the scope can provide? - unsigned downsampler = 0; - getBestSamplerate(samplerate, fastRate, false, &(downsampler)); - - // Set the calculated samplerate - if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) - return Dso::ErrorCode::PARAMETER; - else { - return Dso::ErrorCode::NONE; - } - } else { - unsigned sampleId; - for (sampleId = 0; sampleId < specification->fixedSampleRates.size() - 1; ++sampleId) - if (specification->fixedSampleRates[sampleId].samplerate == samplerate) break; - modifyCommand(ControlCode::CONTROL_SETTIMEDIV) - ->setDiv(specification->fixedSampleRates[sampleId].id); - controlsettings.samplerate.current = samplerate; - - // Check for Roll mode - if (!isRollMode()) - emit recordTimeChanged((double)(getRecordLength() - controlsettings.swSampleMargin) / - controlsettings.samplerate.current); - emit samplerateChanged(controlsettings.samplerate.current); - - return Dso::ErrorCode::NONE; + uint8_t sampleIndex; + for ( sampleIndex = 0; sampleIndex < specification->fixedSampleRates.size() - 1; ++sampleIndex ) { + if ( long( round( specification->fixedSampleRates[ sampleIndex ].samplerate ) ) == + long( round( samplerate ) ) ) // dont compare double == double + break; } + controlSetSamplerate( sampleIndex ); + setDownsampling( specification->fixedSampleRates[ sampleIndex ].oversampling ); + controlsettings.samplerate.current = samplerate; + // printf( "HDC::sSr: emit samplerateChanged(%g)\n", samplerate ); + emit samplerateChanged( samplerate ); + return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setRecordTime(double duration) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - if (duration == 0.0) { +Dso::ErrorCode HantekDsoControl::setRecordTime( double duration ) { + // printf( "setRecordTime( %g )\n", duration ); + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + + if ( duration == 0.0 ) { duration = controlsettings.samplerate.target.duration; + return Dso::ErrorCode::NONE; } else { controlsettings.samplerate.target.duration = duration; controlsettings.samplerate.target.samplerateSet = ControlSettingsSamplerateTarget::Duration; } + // printf( "duration = %g\n", duration ); - if (!specification->isFixedSamplerateDevice) { - // Calculate the maximum samplerate that would still provide the requested - // duration - double maxSamplerate = - (double)specification->samplerate.single.recordLengths[controlsettings.recordLengthId] / duration; - - // When possible, enable fast rate if the record time can't be set that low - // to improve resolution - bool fastRate = (controlsettings.usedChannels <= 1) && - (maxSamplerate >= specification->samplerate.multi.base / - specification->bufferDividers[controlsettings.recordLengthId]); - - // What is the nearest, at most as high samplerate the scope can provide? - unsigned downsampler = 0; - - // Set the calculated samplerate - if (this->updateSamplerate(downsampler, fastRate) == UINT_MAX) - return Dso::ErrorCode::PARAMETER; - else { - return Dso::ErrorCode::NONE; - } - } else { - // For now - we go for the 10240 size sampling - the other seems not to be supported - // Find highest samplerate using less than 10240 samples to obtain our duration. - unsigned sampleCount = 10240; + double srLimit; + if ( isSingleChannel() ) + srLimit = ( specification->samplerate.single ).max; + else + srLimit = ( specification->samplerate.multi ).max; + // For now - we go for the SAMPLESIZE (= 20000) size sampling, defined in hantekdsocontrol.h + // Find highest samplerate using less equal half of these samples to obtain our duration. + uint8_t sampleIndex = 0; + for ( uint8_t iii = 0; iii < specification->fixedSampleRates.size(); ++iii ) { + double sRate = specification->fixedSampleRates[ iii ].samplerate; + // qDebug() << "sampleIndex:" << sampleIndex << "sRate:" << sRate << "sRate*duration:" << sRate * duration; // Ensure that at least 1/2 of remaining samples are available for SW trigger algorithm - if (specification->isSoftwareTriggerDevice) { - sampleCount = (sampleCount - controlsettings.swSampleMargin) / 2; - } - unsigned sampleId = 0; - for (unsigned id = 0; id < specification->fixedSampleRates.size(); ++id) { - if (specification->fixedSampleRates[id].samplerate * duration < sampleCount) sampleId = id; + // for stability reason avoid the highest sample rate as default + if ( sRate < srLimit && sRate * duration <= SAMPLESIZE / 2 ) { + sampleIndex = iii; } - // Usable sample value - modifyCommand(ControlCode::CONTROL_SETTIMEDIV) - ->setDiv(specification->fixedSampleRates[sampleId].id); - controlsettings.samplerate.current = specification->fixedSampleRates[sampleId].samplerate; - - emit samplerateChanged(controlsettings.samplerate.current); - return Dso::ErrorCode::NONE; } + controlSetSamplerate( sampleIndex ); + setDownsampling( specification->fixedSampleRates[ sampleIndex ].oversampling ); + double samplerate = specification->fixedSampleRates[ sampleIndex ].samplerate; + controlsettings.samplerate.current = samplerate; + // printf( "HDC::sRT: emit samplerateChanged(%g)\n", samplerate ); + emit samplerateChanged( samplerate ); + return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setChannelUsed(ChannelID channel, bool used) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - if (channel >= specification->channels) return Dso::ErrorCode::PARAMETER; - // Update settings - controlsettings.voltage[channel].used = used; - ChannelID channelCount = 0; - for (unsigned c = 0; c < specification->channels; ++c) { - if (controlsettings.voltage[c].used) ++channelCount; +Dso::ErrorCode HantekDsoControl::setCalFreq( double calfreq ) { + unsigned int cf = unsigned( calfreq ) / 1000; // 1000, ..., 100000 -> 1, ..., 100 + if ( cf == 0 ) { // 50, 60, 100, 200, 500 -> 105, 106, 110, 120, 150 + cf = 100 + unsigned( calfreq ) / 10; + if ( 110 == cf ) // special case for sigrok FW (e.g. DDS120) 100Hz -> 0 + cf = 0; } + // printf( "HDC::setCalFreq( %g ) -> %d\n", calfreq, cf ); + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + // control command for setting + modifyCommand< ControlSetCalFreq >( ControlCode::CONTROL_SETCALFREQ )->setCalFreq( uint8_t( cf ) ); + return Dso::ErrorCode::NONE; +} + +Dso::ErrorCode HantekDsoControl::setChannelUsed( ChannelID channel, bool used ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; + // Update settings + controlsettings.voltage[ channel ].used = used; // Calculate the UsedChannels field for the command UsedChannels usedChannels = UsedChannels::USED_CH1; + controlsettings.channelCount = 1; - if (controlsettings.voltage[1].used) { - if (controlsettings.voltage[0].used) { + if ( controlsettings.voltage[ 1 ].used ) { + controlsettings.channelCount = 2; + if ( controlsettings.voltage[ 0 ].used ) { usedChannels = UsedChannels::USED_CH1CH2; } else { - // DSO-2250 uses a different value for channel 2 - if (specification->cmdSetChannels == BulkCode::BSETCHANNELS) - usedChannels = UsedChannels::BUSED_CH2; - else - usedChannels = UsedChannels::USED_CH2; + usedChannels = UsedChannels::USED_CH2; } } - - switch (specification->cmdSetChannels) { - case BulkCode::SETTRIGGERANDSAMPLERATE: { - // SetTriggerAndSamplerate bulk command for trigger source - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE) - ->setUsedChannels((uint8_t)usedChannels); - break; - } - case BulkCode::BSETCHANNELS: { - // SetChannels2250 bulk command for active channels - modifyCommand(BulkCode::BSETCHANNELS)->setUsedChannels((uint8_t)usedChannels); - break; - } - case BulkCode::ESETTRIGGERORSAMPLERATE: { - // SetTrigger5200s bulk command for trigger source - modifyCommand(BulkCode::ESETTRIGGERORSAMPLERATE)->setUsedChannels((uint8_t)usedChannels); - break; - } - default: - break; - } - + setSingleChannel( usedChannels == UsedChannels::USED_CH1 ); + // qDebug() << "usedChannels" << int( usedChannels ); + modifyCommand< ControlSetNumChannels >( ControlCode::CONTROL_SETNUMCHANNELS )->setNumChannels( isSingleChannel() ? 1 : 2 ); // Check if fast rate mode availability changed - bool fastRateChanged = (controlsettings.usedChannels <= 1) != (channelCount <= 1); - controlsettings.usedChannels = channelCount; - - if (fastRateChanged) this->updateSamplerateLimits(); - + this->updateSamplerateLimits(); + this->restoreTargets(); + // sampleSetupChanged = true; // skip next raw samples block to avoid artefacts return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setCoupling(ChannelID channel, Dso::Coupling coupling) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - if (channel >= specification->channels) return Dso::ErrorCode::PARAMETER; - - // SetRelays control command for coupling relays - if (specification->supportsCouplingRelays) { - modifyCommand(ControlCode::CONTROL_SETRELAYS) - ->setCoupling(channel, coupling != Dso::Coupling::AC); - } +Dso::ErrorCode HantekDsoControl::setChannelInverted( ChannelID channel, bool inverted ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; + // Update settings + // printf("setChannelInverted %s\n", inverted?"true":"false"); + controlsettings.voltage[ channel ].inverted = inverted; return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setGain(ChannelID channel, double gain) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - if (channel >= specification->channels) return Dso::ErrorCode::PARAMETER; +Dso::ErrorCode HantekDsoControl::setGain( ChannelID channel, double gain ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; - // Find lowest gain voltage thats at least as high as the requested - unsigned gainID; - for (gainID = 0; gainID < specification->gain.size() - 1; ++gainID) - if (specification->gain[gainID].gainSteps >= gain) break; - - if (specification->useControlNoBulk) { - if (channel == 0) { - modifyCommand(ControlCode::CONTROL_SETVOLTDIV_CH1) - ->setDiv(specification->gain[gainID].gainIndex); - } else if (channel == 1) { - modifyCommand(ControlCode::CONTROL_SETVOLTDIV_CH2) - ->setDiv(specification->gain[gainID].gainIndex); - } else - qDebug("%s: Unsuported channel: %i\n", __func__, channel); - } else { - // SetGain bulk command for gain - modifyCommand(BulkCode::SETGAIN)->setGain(channel, specification->gain[gainID].gainIndex); + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; - // SetRelays control command for gain relays - ControlSetRelays *controlSetRelays = modifyCommand(ControlCode::CONTROL_SETRELAYS); - controlSetRelays->setBelow1V(channel, gainID < 3); - controlSetRelays->setBelow100mV(channel, gainID < 6); - } + static uint8_t lastGain[ 2 ] = {0xFF, 0xFF}; + gain /= controlsettings.voltage[ channel ].probeAttn; // gain needs to be scaled by probe attenuation + // Find lowest gain voltage thats at least as high as the requested + uint8_t gainID; + for ( gainID = 0; gainID < specification->gain.size() - 1; ++gainID ) + if ( specification->gain[ gainID ].Vdiv >= gain ) + break; + uint8_t gainValue = specification->gain[ gainID ].gainValue; + if ( channel == 0 ) { + modifyCommand< ControlSetGain_CH1 >( ControlCode::CONTROL_SETGAIN_CH1 )->setGainCH1( gainValue, gainID ); + if ( lastGain[ 0 ] != gainValue ) { // HW gain changed, start new samples + restartSampling(); + } + lastGain[ 0 ] = gainValue; + } else if ( channel == 1 ) { + modifyCommand< ControlSetGain_CH2 >( ControlCode::CONTROL_SETGAIN_CH2 )->setGainCH2( gainValue, gainID ); + if ( lastGain[ 1 ] != gainValue ) { // HW gain changed, start new samples + restartSampling(); + } + lastGain[ 1 ] = gainValue; + } else + qDebug( "%s: Unsupported channel: %i\n", __func__, channel ); + controlsettings.voltage[ channel ].gain = gainID; + return Dso::ErrorCode::NONE; +} - controlsettings.voltage[channel].gain = gainID; - this->setOffset(channel, controlsettings.voltage[channel].offset); +Dso::ErrorCode HantekDsoControl::setProbe( ChannelID channel, double probeAttn ) { + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; + controlsettings.voltage[ channel ].probeAttn = probeAttn; + // printf( "setProbe %g\n", probeAttn ); return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setOffset(ChannelID channel, const double offset) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - if (channel >= specification->channels) return Dso::ErrorCode::PARAMETER; +Dso::ErrorCode HantekDsoControl::setCoupling( ChannelID channel, Dso::Coupling coupling ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; - if (specification->supportsOffset) { - Offset &channelOffLimit = controlsettings.offsetLimit[channel].step[controlsettings.voltage[channel].gain]; - // Calculate the offset value - // The range is given by the calibration data (convert from big endian) - unsigned short int minimum = ((unsigned short int)*((unsigned char *)&(channelOffLimit.start)) << 8) + - *((unsigned char *)&(channelOffLimit.start) + 1); - unsigned short int maximum = ((unsigned short int)*((unsigned char *)&(channelOffLimit.end)) << 8) + - *((unsigned char *)&(channelOffLimit.end) + 1); - unsigned short int offsetValue = offset * (maximum - minimum) + minimum + 0.5; - double offsetReal = (double)(offsetValue - minimum) / (maximum - minimum); + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; - modifyCommand(ControlCode::CONTROL_SETOFFSET)->setChannel(channel, offsetValue); - controlsettings.voltage[channel].offsetReal = offsetReal; + static int lastCoupling[ 2 ] = {-1, -1}; + if ( hasCommand( ControlCode::CONTROL_SETCOUPLING ) ) // don't send command if it is not implemented (like on the 6022) + modifyCommand< ControlSetCoupling >( ControlCode::CONTROL_SETCOUPLING ) + ->setCoupling( channel, coupling == Dso::Coupling::DC ); + controlsettings.voltage[ channel ].coupling = coupling; + if ( lastCoupling[ channel ] != int( coupling ) ) { // HW coupling changed, start new samples + restartSampling(); } - - controlsettings.voltage[channel].offset = offset; - - this->setTriggerLevel(channel, controlsettings.trigger.level[channel]); - + lastCoupling[ channel ] = int( coupling ); return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setTriggerMode(Dso::TriggerMode mode) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; +Dso::ErrorCode HantekDsoControl::setTriggerMode( Dso::TriggerMode mode ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + + static Dso::TriggerMode lastMode; controlsettings.trigger.mode = mode; + if ( Dso::TriggerMode::SINGLE != mode ) + enableSampling( true ); + // trigger mode changed NONE <-> !NONE + if ( ( Dso::TriggerMode::ROLL == mode && Dso::TriggerMode::ROLL != lastMode ) || + ( Dso::TriggerMode::ROLL != mode && Dso::TriggerMode::ROLL == lastMode ) ) { + restartSampling(); // invalidate old samples + raw.freeRun = Dso::TriggerMode::ROLL == mode; + } + lastMode = mode; + newTriggerParam = true; return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setTriggerSource(bool special, unsigned id) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - if (specification->isSoftwareTriggerDevice) return Dso::ErrorCode::UNSUPPORTED; - - if (!special && id >= specification->channels) return Dso::ErrorCode::PARAMETER; - - if (special && id >= specification->specialTriggerChannels.size()) return Dso::ErrorCode::PARAMETER; - - int hardwareID = special ? specification->specialTriggerChannels[id].hardwareID : (int)id; - - switch (specification->cmdSetTrigger) { - case BulkCode::SETTRIGGERANDSAMPLERATE: - // SetTriggerAndSamplerate bulk command for trigger source - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE)->setTriggerSource(1 - hardwareID); - break; - - case BulkCode::CSETTRIGGERORSAMPLERATE: - // SetTrigger2250 bulk command for trigger source - modifyCommand(BulkCode::CSETTRIGGERORSAMPLERATE)->setTriggerSource(2 + hardwareID); - break; - - case BulkCode::ESETTRIGGERORSAMPLERATE: - // SetTrigger5200 bulk command for trigger source - modifyCommand(BulkCode::ESETTRIGGERORSAMPLERATE)->setTriggerSource(1 - hardwareID); - break; - - default: - return Dso::ErrorCode::UNSUPPORTED; - } - - // SetRelays control command for external trigger relay - modifyCommand(ControlCode::CONTROL_SETRELAYS)->setTrigger(special); - - controlsettings.trigger.special = special; - controlsettings.trigger.source = id; - - // Apply trigger level of the new source - if (special) { - // SetOffset control command for changed trigger level - modifyCommand(ControlCode::CONTROL_SETOFFSET)->setTrigger(0x7f); - } else - this->setTriggerLevel(id, controlsettings.trigger.level[id]); +Dso::ErrorCode HantekDsoControl::setTriggerSource( ChannelID channel, bool smooth ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + // printf("setTriggerSource( %d, %d )\n", channel, smooth); + controlsettings.trigger.source = channel; + controlsettings.trigger.smooth = smooth; + newTriggerParam = true; return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setTriggerLevel(ChannelID channel, double level) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - if (channel >= specification->channels) return Dso::ErrorCode::PARAMETER; - - controlsettings.trigger.level[channel] = level; - if (!specification->supportsOffset) return Dso::ErrorCode::UNSUPPORTED; - - // Calculate the trigger level value - unsigned short minimum, maximum; - if (specification->sampleSize > 8) { - Offset &offsetLimit = controlsettings.offsetLimit[channel].step[controlsettings.voltage[channel].gain]; - // The range is the same as used for the offsets for 10 bit models - minimum = ((unsigned short)*((unsigned char *)&(offsetLimit.start)) << 8) + - *((unsigned char *)&(offsetLimit.start) + 1); - maximum = - ((unsigned short)*((unsigned char *)&(offsetLimit.end)) << 8) + *((unsigned char *)&(offsetLimit.end) + 1); - } else { - // It's from 0x00 to 0xfd for the 8 bit models - minimum = 0x00; - maximum = 0xfd; - } - - // Never get out of the limits - const unsigned gainID = controlsettings.voltage[channel].gain; - const double offsetReal = controlsettings.voltage[channel].offsetReal; - const double gainStep = specification->gain[gainID].gainSteps; - const unsigned short levelValue = qBound( - minimum, (unsigned short)(((offsetReal + level / gainStep) * (maximum - minimum) + 0.5) + minimum), maximum); - - // Check if the set channel is the trigger source - if (!controlsettings.trigger.special && channel == controlsettings.trigger.source) { - // SetOffset control command for trigger level - modifyCommand(ControlCode::CONTROL_SETOFFSET)->setTrigger(levelValue); - } - - /// \todo Get alternating trigger in here +// trigger level in Volt +Dso::ErrorCode HantekDsoControl::setTriggerLevel( ChannelID channel, double level ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + if ( channel >= specification->channels ) + return Dso::ErrorCode::PARAMETER; + // printf("setTriggerLevel( %d, %g )\n", channel, level); + controlsettings.trigger.level[ channel ] = level; + newTriggerParam = true; + displayInterval = 0; // update screen immediately return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::setTriggerSlope(Dso::Slope slope) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - switch (specification->cmdSetTrigger) { - case BulkCode::SETTRIGGERANDSAMPLERATE: { - // SetTriggerAndSamplerate bulk command for trigger slope - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE)->setTriggerSlope((uint8_t)slope); - break; - } - case BulkCode::CSETTRIGGERORSAMPLERATE: { - // SetTrigger2250 bulk command for trigger slope - modifyCommand(BulkCode::CSETTRIGGERORSAMPLERATE)->setTriggerSlope((uint8_t)slope); - break; - } - case BulkCode::ESETTRIGGERORSAMPLERATE: { - // SetTrigger5200 bulk command for trigger slope - modifyCommand(BulkCode::ESETTRIGGERORSAMPLERATE)->setTriggerSlope((uint8_t)slope); - break; - } - default: - return Dso::ErrorCode::UNSUPPORTED; - } +Dso::ErrorCode HantekDsoControl::setTriggerSlope( Dso::Slope slope ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + // printf("setTriggerSlope( %d )\n", (int)slope); controlsettings.trigger.slope = slope; + newTriggerParam = true; return Dso::ErrorCode::NONE; } -void HantekDsoControl::forceTrigger() { modifyCommand(BulkCode::FORCETRIGGER); } - -Dso::ErrorCode HantekDsoControl::setPretriggerPosition(double position) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - // All trigger positions are measured in samples - double positionSamples = position * controlsettings.samplerate.current; - unsigned recordLength = getRecordLength(); - // Fast rate mode uses both channels - if (isFastRate()) positionSamples /= specification->channels; - - switch (specification->cmdSetPretrigger) { - case BulkCode::SETTRIGGERANDSAMPLERATE: { - // Calculate the position value (Start point depending on record length) - unsigned triggerPosition = isRollMode() ? 0x1 : 0x7ffff - recordLength + (unsigned)positionSamples; - - // SetTriggerAndSamplerate bulk command for trigger position - modifyCommand(BulkCode::SETTRIGGERANDSAMPLERATE) - ->setTriggerPosition(triggerPosition); - break; - } - case BulkCode::FSETBUFFER: { - // Calculate the position values (Inverse, maximum is 0x7ffff) - unsigned positionPre = 0x7ffff - recordLength + (unsigned)positionSamples; - unsigned positionPost = 0x7ffff - (unsigned)positionSamples; - - // SetBuffer2250 bulk command for trigger position - BulkSetBuffer2250 *commandSetBuffer2250 = modifyCommand(BulkCode::FSETBUFFER); - commandSetBuffer2250->setTriggerPositionPre(positionPre); - commandSetBuffer2250->setTriggerPositionPost(positionPost); - break; - } - case BulkCode::ESETTRIGGERORSAMPLERATE: { - // Calculate the position values (Inverse, maximum is 0xffff) - unsigned positionPre = 0xffff - recordLength + (unsigned)positionSamples; - unsigned positionPost = 0xffff - (unsigned)positionSamples; - - // SetBuffer5200 bulk command for trigger position - BulkSetBuffer5200 *commandSetBuffer5200 = modifyCommand(BulkCode::DSETBUFFER); - commandSetBuffer5200->setTriggerPositionPre((unsigned short)positionPre); - commandSetBuffer5200->setTriggerPositionPost((unsigned short)positionPost); - break; - } - default: - return Dso::ErrorCode::UNSUPPORTED; - } +// set trigger position (0.0 - 1.0) +Dso::ErrorCode HantekDsoControl::setTriggerOffset( double position ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + // printf("setTriggerPosition( %g )\n", position); controlsettings.trigger.position = position; + newTriggerParam = true; return Dso::ErrorCode::NONE; } -Dso::ErrorCode HantekDsoControl::stringCommand(const QString &commandString) { - if (!device->isConnected()) return Dso::ErrorCode::CONNECTION; - - QStringList commandParts = commandString.split(' ', QString::SkipEmptyParts); - - if (commandParts.count() < 1) return Dso::ErrorCode::PARAMETER; - if (commandParts[0] != "send") return Dso::ErrorCode::UNSUPPORTED; - if (commandParts.count() < 2) return Dso::ErrorCode::PARAMETER; - - uint8_t codeIndex = 0; - hexParse(commandParts[2], &codeIndex, 1); - QString data = commandString.section(' ', 2, -1, QString::SectionSkipEmpty); - - if (commandParts[1] == "bulk") { - if (!command[codeIndex]) return Dso::ErrorCode::UNSUPPORTED; - BulkCommand *c = modifyCommand((BulkCode)codeIndex); - hexParse(data, c->data(), c->size()); - return Dso::ErrorCode::NONE; - } else if (commandParts[1] == "control") { - if (!control[codeIndex]) return Dso::ErrorCode::UNSUPPORTED; +// Initialize the device with the current settings. +void HantekDsoControl::applySettings( DsoSettingsScope *dsoSettingsScope ) { + scope = dsoSettingsScope; + bool mathUsed = dsoSettingsScope->anyUsed( specification->channels ); + for ( ChannelID channel = 0; channel < specification->channels; ++channel ) { + setProbe( channel, dsoSettingsScope->voltage[ channel ].probeAttn ); + setGain( channel, dsoSettingsScope->gain( channel ) ); + setTriggerLevel( channel, dsoSettingsScope->voltage[ channel ].trigger ); + setChannelUsed( channel, mathUsed | dsoSettingsScope->anyUsed( channel ) ); + setChannelInverted( channel, dsoSettingsScope->voltage[ channel ].inverted ); + setCoupling( channel, Dso::Coupling( dsoSettingsScope->voltage[ channel ].couplingOrMathIndex ) ); + } - ControlCommand *c = modifyCommand((ControlCode)codeIndex); - hexParse(data, c->data(), c->size()); - return Dso::ErrorCode::NONE; - } else - return Dso::ErrorCode::UNSUPPORTED; + setRecordTime( dsoSettingsScope->horizontal.timebase * DIVS_TIME ); + setCalFreq( dsoSettingsScope->horizontal.calfreq ); + setTriggerMode( dsoSettingsScope->trigger.mode ); + setTriggerOffset( dsoSettingsScope->trigger.offset ); + setTriggerSlope( dsoSettingsScope->trigger.slope ); + setTriggerSource( dsoSettingsScope->trigger.source, dsoSettingsScope->trigger.smooth ); } -void HantekDsoControl::addCommand(BulkCommand *newCommand, bool pending) { - newCommand->pending = pending; - command[(uint8_t)newCommand->code] = newCommand; - newCommand->next = firstBulkCommand; - firstBulkCommand = newCommand; + +/// \brief Starts a new sampling block. +void HantekDsoControl::restartSampling() { + scopeDevice->stopSampling(); + raw.rollMode = false; } -const BulkCommand *HantekDsoControl::getCommand(BulkCode code) const { return command[(uint8_t)code]; } -void HantekDsoControl::addCommand(ControlCommand *newCommand, bool pending) { - newCommand->pending = pending; - control[newCommand->code] = newCommand; - newCommand->next = firstControlCommand; - firstControlCommand = newCommand; +/// \brief Start sampling process. +void HantekDsoControl::enableSampling( bool enabled ) { + sampling = enabled; + updateSamplerateLimits(); + emit samplingStatusChanged( enabled ); } -const ControlCommand *HantekDsoControl::getCommand(ControlCode code) const { return control[(uint8_t)code]; } -void HantekDsoControl::run() { - int errorCode = 0; +unsigned HantekDsoControl::getRecordLength() const { + unsigned rawsize = getSamplesize(); + rawsize *= this->downsamplingNumber; // take multiple samples for oversampling + rawsize = grossSampleCount( rawsize ); // adjust for skipping of minimal 2048 leading samples + // printf( "getRecordLength: %d\n", rawsize ); + return rawsize; +} - // Send all pending bulk commands - BulkCommand *command = firstBulkCommand; - while (command) { - if (command->pending) { - timestampDebug(QString("Sending bulk command:%1").arg(hexDump(command->data(), command->size()))); - errorCode = bulkCommand(command); - if (errorCode < 0) { - qWarning() << "Sending bulk command failed: " << libUsbErrorString(errorCode); - emit communicationError(); - return; - } else - command->pending = false; +Dso::ErrorCode HantekDsoControl::retrieveChannelLevelData() { + // Get calibration data from EEPROM + // printf( "retrieveChannelLevelData()\n" ); + int errorCode = -1; + if ( scopeDevice->isRealHW() && specification->hasCalibrationEEPROM ) + errorCode = scopeDevice->controlRead( &controlsettings.cmdGetLimits ); + if ( errorCode < 0 ) { + // invalidate the calibration values. + memset( controlsettings.calibrationValues, 0xFF, sizeof( CalibrationValues ) ); + if ( scopeDevice->isRealHW() ) { + QString message = tr( "Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration!" ); + qWarning() << message; + emit statusMessage( message, 0 ); + emit communicationError(); + return Dso::ErrorCode::CONNECTION; + } else { + return Dso::ErrorCode::NONE; } - command = command->next; } + memcpy( controlsettings.calibrationValues, controlsettings.cmdGetLimits.data(), sizeof( CalibrationValues ) ); + // printf("HDC::cV: %lu, %d, %g\n", sizeof( CalibrationValues ), + // controlsettings.calibrationValues->off.ls.step[ 7 ][ 0 ] - 0x80, + // (controlsettings.calibrationValues->fine.ls.step[ 7 ][ 0 ] - 0x80) / 250.0 + // ); + return Dso::ErrorCode::NONE; +} - // Send all pending control commands - ControlCommand *controlCommand = firstControlCommand; - while (controlCommand) { - if (controlCommand->pending) { - timestampDebug(QString("Sending control command %1:%2") - .arg(QString::number(controlCommand->code, 16), - hexDump(controlCommand->data(), controlCommand->size()))); - - errorCode = device->controlWrite(controlCommand); - if (errorCode < 0) { - qWarning("Sending control command %2x failed: %s", (uint8_t)controlCommand->code, - libUsbErrorString(errorCode).toLocal8Bit().data()); - - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } - } else - controlCommand->pending = false; - } - controlCommand = controlCommand->next; + +void HantekDsoControl::quitSampling() { + enableSampling( false ); + capturing = false; + scopeDevice->stopSampling(); + if ( scopeDevice->isDemoDevice() ) + return; + auto controlCommand = ControlStopSampling(); + timestampDebug( QString( "Sending control command 0x%1 (Stop Sampling): %2" ) + .arg( QString::number( controlCommand.code, 16 ), + hexdecDump( controlCommand.data(), unsigned( controlCommand.size() ) ) ) ); + int errorCode = scopeDevice->controlWrite( &controlCommand ); + if ( errorCode < 0 ) { + qWarning() << "controlWrite: stop sampling failed: " << libUsbErrorString( errorCode ); + emit communicationError(); } +} - // State machine for the device communication - if (isRollMode()) { - // Roll mode - this->captureState = CAPTURE_WAITING; - bool toNextState = true; - - switch (this->rollState) { - case RollState::STARTSAMPLING: - // Don't iterate through roll mode steps when stopped - if (!this->sampling) { - toNextState = false; - break; - } - // Sampling hasn't started, update the expected sample count - expectedSampleCount = this->getSampleCount(); +void HantekDsoControl::convertRawDataToSamples() { + QReadLocker rawLocker( &raw.lock ); + activeChannels = raw.channels; + const unsigned rawSampleCount = unsigned( raw.data.size() ) / activeChannels; + if ( !rawSampleCount ) + return; + const unsigned rawOversampling = raw.oversampling; + const bool freeRunning = rawSampleCount / rawOversampling < SAMPLESIZE; // amount needed for sw trigger + const unsigned sampleCount = freeRunning ? rawSampleCount : netSampleCount( rawSampleCount ); + const unsigned resultSamples = freeRunning ? sampleCount / rawOversampling - 1 : sampleCount / rawOversampling; + const unsigned skipSamples = rawSampleCount - sampleCount; + QWriteLocker resultLocker( &result.lock ); + result.freeRunning = freeRunning; + result.tag = raw.tag; + result.samplerate = raw.samplerate / raw.oversampling; + // Prepare result buffers + result.data.resize( specification->channels ); + for ( ChannelID channelCounter = 0; channelCounter < specification->channels; ++channelCounter ) + result.data[ channelCounter ].clear(); - errorCode = bulkCommand(getCommand(BulkCode::STARTSAMPLING)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } - break; + // Convert channel data + // Channels are using their separate buffers + for ( ChannelID channel = 0; channel < activeChannels; ++channel ) { + const unsigned gainIndex = raw.gainIndex[ channel ]; + const double voltageScale = specification->voltageScale[ channel ][ gainIndex ]; + const double probeAttn = controlsettings.voltage[ channel ].probeAttn; + const double sign = controlsettings.voltage[ channel ].inverted ? -1.0 : 1.0; + + // shift + individual offset for each channel and gain + double gainCalibration = 1.0; + double voltageOffset = specification->voltageOffset[ channel ][ gainIndex ]; + if ( !bool( voltageOffset ) ) { // no config file value + // get offset value from eeprom[ 8 .. 39 and (if available) 56 .. 87] + int offsetRaw; + int offsetFine; + if ( result.samplerate < 30e6 ) { + offsetRaw = controlsettings.calibrationValues->off.ls.step[ gainIndex ][ channel ]; + offsetFine = controlsettings.calibrationValues->fine.ls.step[ gainIndex ][ channel ]; + } else { + offsetRaw = controlsettings.calibrationValues->off.hs.step[ gainIndex ][ channel ]; + offsetFine = controlsettings.calibrationValues->fine.hs.step[ gainIndex ][ channel ]; } + if ( offsetRaw && offsetRaw != 255 && offsetFine && offsetFine != 255 ) { // data valid + voltageOffset = offsetRaw + ( offsetFine - 0x80 ) / 250.0; + } else { // no offset correction + voltageOffset = 0x80; // ADC has "binary offset" format (0x80 = 0V) + } + int gain = controlsettings.calibrationValues->gain.step[ gainIndex ][ channel ]; + if ( gain && gain != 255 ) { // data valid + gainCalibration = 1.0 + ( gain - 0x80 ) / 500.0; + } + } + // Convert data from the oscilloscope and write it into the channel sample buffer + unsigned rawBufPos = 0; + if ( raw.freeRun && raw.rollMode ) // show the "new" samples on the right screen side + rawBufPos = raw.received; // start with remaining "old" samples in buffer + result.data[ channel ].resize( resultSamples ); + rawBufPos += skipSamples * activeChannels; // skip first unstable samples + result.clipped &= ~( 0x01 << channel ); // clear clipping flag + for ( unsigned index = 0; index < resultSamples; + ++index, rawBufPos += activeChannels * rawOversampling ) { // advance either by one or two blocks + if ( rawBufPos + rawOversampling * activeChannels > rawSampleCount * activeChannels ) + rawBufPos = 0; // (roll mode) show "new" samples after the "old" samples + double sample = 0.0; + for ( unsigned iii = 0; iii < rawOversampling * activeChannels; iii += activeChannels ) { + int rawSample = raw.data[ rawBufPos + channel + iii ]; // CH1/CH2/CH1/CH2 ... + if ( rawSample == 0x00 || rawSample == 0xFF ) // min or max -> clipped + result.clipped |= 0x01 << channel; + sample += double( rawSample ) - voltageOffset; + } + sample /= rawOversampling; + result.data[ channel ][ index ] = sign * sample / voltageScale * gainCalibration * probeAttn; + } + } +} - timestampDebug("Starting to capture"); - - this->_samplingStarted = true; - break; +// search for trigger point from defined point, default startPos = 0; +unsigned HantekDsoControl::searchTriggerPoint( Dso::Slope dsoSlope, unsigned int startPos ) { + int slope; + if ( dsoSlope == Dso::Slope::Positive ) + slope = 1; + else if ( dsoSlope == Dso::Slope::Negative ) + slope = -1; + else + return 0; - case RollState::ENABLETRIGGER: - errorCode = bulkCommand(getCommand(BulkCode::ENABLETRIGGER)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } + ChannelID channel = controlsettings.trigger.source; + const std::vector< double > &samples = result.data[ channel ]; + unsigned sampleCount = unsigned( samples.size() ); ///< number of available samples + // printf("searchTriggerPoint( %d, %d )\n", (int)dsoSlope, startPos ); + if ( startPos >= sampleCount ) + return 0; + double level = controlsettings.trigger.level[ channel ]; + double timeDisplay = controlsettings.samplerate.target.duration; // time for full screen width + double sampleRate = controlsettings.samplerate.current; + double samplesDisplay = timeDisplay * sampleRate; + + unsigned preTrigSamples = + startPos ? startPos : unsigned( controlsettings.trigger.position * samplesDisplay ); // samples left of trigger + unsigned postTrigSamples = + unsigned( sampleCount ) - ( unsigned( samplesDisplay ) - preTrigSamples ); // samples right of trigger + // |-----------samples-----------| // available sample + // |--disp--| // display size + // |<<<<>|--------------------| // >> = right = (disp-pre) i.e. right of trigger on screen + // |>| + // |<<<<<|????????????????????|>>| // ?? = search for trigger in this range [left,right] + + const unsigned swTriggerSampleSet = + controlsettings.trigger.smooth ? 10 : 1; // check this number of samples before/after trigger point ... + const unsigned swTriggerThreshold = + controlsettings.trigger.smooth ? 5 : 0; // ... and get at least this number below or above trigger + if ( postTrigSamples > sampleCount - 2 * ( swTriggerSampleSet + 1 ) ) + postTrigSamples = sampleCount - 2 * ( swTriggerSampleSet + 1 ); + // printf( "pre: %d, post %d\n", preTrigSamples, postTrigSamples ); + + double prev = INT_MAX * slope; + unsigned swTriggerStart = 0; + for ( unsigned int i = preTrigSamples; i < postTrigSamples; i++ ) { + if ( slope * samples[ i ] >= slope * level && slope * prev < slope * level ) { // trigger condition met + // check for the next few SampleSet samples, if they are also above/below the trigger value + unsigned int before = 0; + for ( unsigned int k = i - 1; k >= i - swTriggerSampleSet && k > 0; k-- ) { + if ( slope * samples[ k ] < slope * level ) + before++; + } + unsigned int after = 0; + for ( unsigned int k = i + 1; k <= i + swTriggerSampleSet && k < sampleCount; k++ ) { + if ( slope * samples[ k ] >= slope * level ) + after++; + } + // if at least >Threshold (=5) samples before and after trig meet the condition, set trigger + if ( before > swTriggerThreshold && after > swTriggerThreshold ) { + swTriggerStart = i; break; } - - timestampDebug("Enabling trigger"); - - break; - - case RollState::FORCETRIGGER: - errorCode = bulkCommand(getCommand(BulkCode::FORCETRIGGER)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; + } + prev = samples[ i ]; + } + return swTriggerStart; +} + + +unsigned HantekDsoControl::searchTriggeredPosition() { + static Dso::Slope nextSlope = Dso::Slope::Positive; // for alternating slope mode X + ChannelID channel = controlsettings.trigger.source; + // Trigger channel not in use + if ( !controlsettings.voltage[ channel ].used || result.data.empty() ) { + return result.triggeredPosition = 0; + } + // printf( "HDC::searchTriggeredPosition()\n" ); + triggeredPositionRaw = 0; + double pulseWidth1 = 0.0; + double pulseWidth2 = 0.0; + + size_t sampleCount = result.data[ channel ].size(); // number of available samples + double timeDisplay = controlsettings.samplerate.target.duration; // time for full screen width + double sampleRate = result.samplerate; // + double samplesDisplay = timeDisplay * controlsettings.samplerate.current; + if ( sampleCount < samplesDisplay ) // not enough samples to adjust for jitter. + return result.triggeredPosition = 0; + // search for trigger point in a range that leaves enough samples left and right of trigger for display + // find also the alternate slope after trigger point -> calculate pulse width. + if ( controlsettings.trigger.slope != Dso::Slope::Both ) { + triggeredPositionRaw = searchTriggerPoint( nextSlope = controlsettings.trigger.slope ); + if ( triggeredPositionRaw ) { // triggered -> search also following other slope (calculate pulse width) + if ( unsigned int slopePos2 = searchTriggerPoint( mirrorSlope( nextSlope ), triggeredPositionRaw ) ) { + pulseWidth1 = ( slopePos2 - triggeredPositionRaw ) / sampleRate; + if ( unsigned int slopePos3 = searchTriggerPoint( nextSlope, slopePos2 ) ) { + pulseWidth2 = ( slopePos3 - slopePos2 ) / sampleRate; } - break; } - - timestampDebug("Forcing trigger"); - - break; - - case RollState::GETDATA: { - std::vector rawData = this->getSamples(expectedSampleCount); - if (this->_samplingStarted) { - convertRawDataToSamples(rawData); - emit samplesAvailable(&result); + } + } else { // alternating trigger slope + triggeredPositionRaw = searchTriggerPoint( nextSlope ); + if ( triggeredPositionRaw ) { // triggered -> change slope + Dso::Slope thirdSlope = nextSlope; + nextSlope = mirrorSlope( nextSlope ); + if ( unsigned int slopePos2 = searchTriggerPoint( nextSlope, triggeredPositionRaw ) ) { + pulseWidth1 = ( slopePos2 - triggeredPositionRaw ) / sampleRate; + if ( unsigned int slopePos3 = searchTriggerPoint( thirdSlope, slopePos2 ) ) { + pulseWidth2 = ( slopePos3 - slopePos2 ) / sampleRate; + } } } + } - // Check if we're in single trigger mode - if (controlsettings.trigger.mode == Dso::TriggerMode::SINGLE && this->_samplingStarted) - this->enableSampling(false); + result.triggeredPosition = triggeredPositionRaw; // align trace to trigger position + result.pulseWidth1 = pulseWidth1; + result.pulseWidth2 = pulseWidth2; + // printf( "nextSlope %c, triggeredPositionRaw %d\n", "/\\"[ int( nextSlope ) ], triggeredPositionRaw ); + return result.triggeredPosition; +} - // Sampling completed, restart it when necessary - this->_samplingStarted = false; - break; +bool HantekDsoControl::provideTriggeredData() { + // printf( "HDC::provideTriggeredData()\n" ); + static DSOsamples triggeredResult; // storage for last triggered trace samples + if ( result.triggeredPosition ) { // live trace has triggered + // Use this trace and save it also + triggeredResult.data = result.data; + triggeredResult.samplerate = result.samplerate; + triggeredResult.clipped = result.clipped; + triggeredResult.triggeredPosition = result.triggeredPosition; + result.liveTrigger = true; + } else if ( controlsettings.trigger.mode == Dso::TriggerMode::NORMAL ) { // Not triggered in NORMAL mode + // Use saved trace (even if it is empty) + result.data = triggeredResult.data; + result.samplerate = triggeredResult.samplerate; + result.clipped = triggeredResult.clipped; + result.triggeredPosition = triggeredResult.triggeredPosition; + result.liveTrigger = false; // show red "TR" top left + } else { // Not triggered and not NORMAL mode + // Use the free running trace, discard history + triggeredResult.data.clear(); // discard trace + triggeredResult.triggeredPosition = 0; // not triggered + result.liveTrigger = false; // show red "TR" top left + } + return result.liveTrigger; +} - default: - timestampDebug("Roll mode state unknown"); - break; - } - // Go to next state, or restart if last state was reached - if (toNextState) this->rollState = (RollState)(((int)rollState + 1) % (int)RollState::_COUNT); - } else { - // Standard mode - this->rollState = RollState::STARTSAMPLING; - - const int lastCaptureState = this->captureState; - unsigned triggerPoint; - std::tie(captureState, triggerPoint) = this->getCaptureState(); - controlsettings.trigger.point = calculateTriggerPoint(triggerPoint); - if (this->captureState < 0) { - qWarning() << tr("Getting capture state failed: %1").arg(libUsbErrorString(this->captureState)); - emit statusMessage(tr("Getting capture state failed: %1").arg(libUsbErrorString(this->captureState)), 0); - } else if (this->captureState != lastCaptureState) - timestampDebug(QString("Capture state changed to %1").arg(this->captureState)); - - switch (this->captureState) { - case CAPTURE_READY: - case CAPTURE_READY2250: - case CAPTURE_READY5200: { - std::vector rawData = this->getSamples(expectedSampleCount); - if (this->_samplingStarted) { - convertRawDataToSamples(rawData); - emit samplesAvailable(&result); - } +/// \brief Updates the interval of the periodic thread timer. +void HantekDsoControl::updateInterval() { + // Check the current oscilloscope state everytime 25% of the time + // the buffer should be refilled (-> acquireInterval in ms) + // Use real 100% rate for demo device + int sampleInterval = int( getSamplesize() * 1000.0 / controlsettings.samplerate.current ); + // Slower update reduces CPU load but it worsens the triggering of rare events + // Display can be filled at slower rate (not faster than displayInterval) + if ( scope ) // init is done + acquireInterval = int( 1000 * scope->horizontal.acquireInterval ); + else + acquireInterval = 1; +#ifdef Q_PROCESSOR_ARM + displayInterval = 200; // update display at least every 200 ms +#else + displayInterval = 100; // update display at least every 100 ms +#endif + acquireInterval = qMin( qMax( sampleInterval, acquireInterval ), 100 ); // at least every 100 ms +} + + +/// \brief State machine for the device communication +void HantekDsoControl::stateMachine() { + static int delayDisplay = 0; // timer for display + static bool lastTriggered = false; // state of last frame + static bool skipEven = true; // even or odd frames were skipped + static unsigned lastTag = 0; + + bool triggered = false; + // we have a sample available ... + // ... that is either a new sample or we are in free run mode or a new trigger search is needed + if ( samplingStarted && raw.valid && ( raw.tag != lastTag || raw.freeRun || triggerChanged() ) ) { + lastTag = raw.tag; + convertRawDataToSamples(); // process samples, apply gain settings etc. + QWriteLocker resultLocker( &result.lock ); + if ( !result.freeRunning ) { // trigger mode != NONE + searchTriggeredPosition(); // detect trigger point + triggered = provideTriggeredData(); // present either free running or last triggered trace + } else { // free running display (uses half sample size -> double display speed for slow sample rates) + triggered = false; + result.triggeredPosition = 0; + } + } else { // start with correct calibration frequency + static bool firstTime = true; + if ( firstTime && scope ) { + setCalFreq( scope->horizontal.calfreq ); + firstTime = false; } + } + delayDisplay += qMax( acquireInterval, 1 ); + // always run the display (slowly at t=displayInterval) to allow user interaction + // ... but update immediately if new triggered data is available after untriggered + // skip an even number of frames when slope == Dso::Slope::Both + if ( ( triggered && !lastTriggered ) // show new data immediately + || ( ( delayDisplay >= displayInterval ) // or wait some time ... + && ( ( controlsettings.trigger.slope != Dso::Slope::Both ) // ... for ↗ or ↘ slope + || skipEven ) ) ) { // and drop even no. of frames + skipEven = true; // zero frames -> even + delayDisplay = 0; + timestampDebug( QString( "samplesAvailable %1" ).arg( result.tag ) ); + emit samplesAvailable( &result ); // via signal/slot -> PostProcessing::input() + } else { + skipEven = !skipEven; + } - // Check if we're in single trigger mode - if (controlsettings.trigger.mode == Dso::TriggerMode::SINGLE && this->_samplingStarted) - this->enableSampling(false); + lastTriggered = triggered; // save state - // Sampling completed, restart it when necessary - this->_samplingStarted = false; + // Stop sampling if we're in single trigger mode and have a triggered trace (txh No13) + if ( controlsettings.trigger.mode == Dso::TriggerMode::SINGLE && samplingStarted && triggeredPositionRaw > 0 ) { + enableSampling( false ); + } - // Start next capture if necessary by leaving out the break statement + // Sampling completed, restart it when necessary + samplingStarted = false; - if (!this->sampling) break; -#ifdef __has_cpp_attribute - #if __has_cpp_attribute(clang::fallthrough) - #define FALLTHROUGH [[clang::fallthrough]]; - #elif __has_cpp_attribute(fallthrough) - #define FALLTHROUGH [[fallthrough]]; - #else - #define FALLTHROUGH - #endif + if ( isSampling() ) { + // Sampling hasn't started, update the expected sample count + expectedSampleCount = this->getSampleCount(); + timestampDebug( "Starting to capture" ); + samplingStarted = true; + } + updateInterval(); // calculate new acquire timing + if ( stateMachineRunning ) { +#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 4, 0 ) ) + QTimer::singleShot( acquireInterval, this, &HantekDsoControl::stateMachine ); #else -#define FALLTHROUGH + QTimer::singleShot( acquireInterval, this, SLOT( stateMachine() ) ); #endif - else { - FALLTHROUGH - } - case CAPTURE_WAITING: - // Sampling hasn't started, update the expected sample count - expectedSampleCount = this->getSampleCount(); - - if (_samplingStarted && lastTriggerMode == controlsettings.trigger.mode) { - ++this->cycleCounter; - - if (this->cycleCounter == this->startCycle && !isRollMode()) { - // Buffer refilled completely since start of sampling, enable the - // trigger now - errorCode = bulkCommand(getCommand(BulkCode::ENABLETRIGGER)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } - break; - } - - timestampDebug("Enabling trigger"); - } else if (cycleCounter >= 8 + this->startCycle && - controlsettings.trigger.mode == Dso::TriggerMode::WAIT_FORCE) { - // Force triggering - errorCode = bulkCommand(getCommand(BulkCode::FORCETRIGGER)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } - break; - } - - timestampDebug("Forcing trigger"); - } + } +} - if (this->cycleCounter < 20 || this->cycleCounter < 4000 / cycleTime) break; - } - // Start capturing - errorCode = bulkCommand(getCommand(BulkCode::STARTSAMPLING)); - if (errorCode < 0) { - if (errorCode == LIBUSB_ERROR_NO_DEVICE) { - emit communicationError(); - return; - } - break; - } - - timestampDebug("Starting to capture"); +void HantekDsoControl::addCommand( ControlCommand *newCommand, bool pending ) { + newCommand->pending = pending; + control[ newCommand->code ] = newCommand; + newCommand->next = firstControlCommand; + firstControlCommand = newCommand; +} - this->_samplingStarted = true; - this->cycleCounter = 0; - this->startCycle = int(controlsettings.trigger.position * 1000.0 / cycleTime + 1.0); - this->lastTriggerMode = controlsettings.trigger.mode; - break; - case CAPTURE_SAMPLING: - break; - default: - break; - } - } +// sending control commands to the scope: +// format: "send CC DD DD ..." +// CC = control code, e.g. E6 (SETCALFREQ) +// DD = data, e.g. 01 = 1kHz or 69 (= 105 dec) = 50 Hz +// all CC and DD uint8_t values must consist of 2 hex encoded digits +Dso::ErrorCode HantekDsoControl::stringCommand( const QString &commandString ) { + if ( deviceNotConnected() ) + return Dso::ErrorCode::CONNECTION; + QStringList commandParts = commandString.split( ' ', QString::SkipEmptyParts ); + if ( commandParts.count() < 1 ) + return Dso::ErrorCode::PARAMETER; + if ( commandParts[ 0 ] == "send" ) { + if ( commandParts.count() < 2 ) + return Dso::ErrorCode::PARAMETER; - this->updateInterval(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - QTimer::singleShot(cycleTime, this, &HantekDsoControl::run); -#else - QTimer::singleShot(cycleTime, this, SLOT(run())); -#endif -} + uint8_t codeIndex = 0; + hexParse( commandParts[ 1 ], &codeIndex, 1 ); + QString data = commandString.section( ' ', 2, -1, QString::SectionSkipEmpty ); -int HantekDsoControl::getConnectionSpeed() const { - int errorCode; - ControlGetSpeed response; - errorCode = device->controlRead(&response); - if (errorCode < 0) return errorCode; + if ( !control[ codeIndex ] ) + return Dso::ErrorCode::UNSUPPORTED; - return response.getSpeed(); -} + QString name = ""; + if ( codeIndex >= 0xe0 && codeIndex <= 0xe6 ) + name = controlNames[ codeIndex - 0xe0 ]; -int HantekDsoControl::getPacketSize() const { - const int s = getConnectionSpeed(); - if (s == CONNECTION_FULLSPEED) - return 64; - else if (s == CONNECTION_HIGHSPEED) - return 512; - else if (s > CONNECTION_HIGHSPEED) { - qWarning() << "Unknown USB speed. Please correct source code in USBDevice::getPacketSize()"; - throw new std::runtime_error("Unknown USB speed"); - } else if (s < 0) - return s; - return 0; + ControlCommand *c = modifyCommand< ControlCommand >( ControlCode( codeIndex ) ); + hexParse( data, c->data(), unsigned( c->size() ) ); + // qDebug() << QString( "Control command 0x%1 (%2):%3" ) + // .arg( QString::number( codeIndex, 16 ), name, decDump( c->data(), unsigned( c->size() ) ) ); + if ( int( c->size() ) != commandParts.count() - 2 ) + return Dso::ErrorCode::PARAMETER; + return Dso::ErrorCode::NONE; +#if 0 + } else if ( commandParts[ 0 ] == "test" ) { // simple example for another command + if ( commandParts.count() < 2 ) + return Dso::ErrorCode::PARAMETER; + uint8_t test; + hexParse( commandParts[ 1 ], &test, 1 ); + printf( "test: 0x%02x\n", test ); + return Dso::ErrorCode::NONE; +#endif + } + return Dso::ErrorCode::UNSUPPORTED; } diff --git a/openhantek/src/hantekdso/hantekdsocontrol.h b/openhantek/src/hantekdso/hantekdsocontrol.h index 1765da4f..fc2969c5 100644 --- a/openhantek/src/hantekdso/hantekdsocontrol.h +++ b/openhantek/src/hantekdso/hantekdsocontrol.h @@ -9,26 +9,50 @@ #include "controlspecification.h" #include "dsosamples.h" #include "errorcodes.h" -#include "states.h" +#include "scopesettings.h" #include "utils/printutils.h" +#include "viewconstants.h" -#include "hantekprotocol/bulkStructs.h" #include "hantekprotocol/controlStructs.h" #include "hantekprotocol/definitions.h" +#include "dsomodel.h" + #include #include +#include +#include #include #include #include +#include + +class Capturing; +class ScopeDevice; + +struct Raw { + unsigned channels = 0; + double samplerate = 0; + unsigned oversampling = 0; + unsigned gainValue[ 2 ] = {1, 1}; // 1,2,5,10,.. + unsigned gainIndex[ 2 ] = {7, 7}; // index 0..7 + unsigned tag = 0; + bool freeRun = false; // small buffer, no trigger + bool valid = false; // samples can be processed + bool rollMode = false; // one complete buffer received, start to roll + unsigned size = 0; + unsigned received = 0; + std::vector< unsigned char > data; + mutable QReadWriteLock lock; +}; -class USBDevice; /// \brief The DsoControl abstraction layer for %Hantek USB DSOs. /// TODO Please anyone, refactor this class into smaller pieces (Separation of Concerns!). class HantekDsoControl : public QObject { Q_OBJECT + friend Capturing; public: /** @@ -39,132 +63,95 @@ class HantekDsoControl : public QObject { * if run() is called. * @param device The usb device. This object does not take ownership. */ - HantekDsoControl(USBDevice *device); + explicit HantekDsoControl( ScopeDevice *scopeDevice, const DSOModel *model ); /// \brief Cleans up ~HantekDsoControl(); - /// Call this to start the processing. This method will call itself - /// periodically from there on. - /// It is wise to move this class object to an own thread and call run from - /// there. - void run(); - - /// \brief Gets the physical channel count for this oscilloscope. - /// \return The number of physical channels. - unsigned getChannelCount() const; + /// Call this to start the processing. + /// This method will call itself periodically from there on. + /// Move this class object to an own thread and call run from there. + void stateMachine(); - /// Return the read-only device control settings. Use the set- Methods to change - /// device settings. - const Dso::ControlSettings *getDeviceSettings() const; + void stopStateMachine() { stateMachineRunning = false; } - /// \brief Get available record lengths for this oscilloscope. - /// \return The number of physical channels, empty list for continuous. - const std::vector &getAvailableRecordLengths() const; + double getSamplerate() const { return controlsettings.samplerate.current; } - /// \brief Get minimum samplerate for this oscilloscope. - /// \return The minimum samplerate for the current configuration in S/s. - double getMinSamplerate() const; - - /// \brief Get maximum samplerate for this oscilloscope. - /// \return The maximum samplerate for the current configuration in S/s. - double getMaxSamplerate() const; + static const unsigned SAMPLESIZE = 20000; + static const unsigned SAMPLESIZE_ROLL = 39 * 256; + unsigned getSamplesize() const { + if ( controlsettings.trigger.mode == Dso::TriggerMode::ROLL ) + return SAMPLESIZE_ROLL; + else + return SAMPLESIZE; + } - bool isSampling() const; + bool isSampling() const { return sampling; } /// Return the associated usb device. - const USBDevice *getDevice() const; - - /// \brief Gets the speed of the connection. - /// \return The ::ConnectionSpeed of the USB connection. - int getConnectionSpeed() const; + const ScopeDevice *getDevice() const { return scopeDevice; } - /// \brief Gets the maximum size of one packet transmitted via bulk transfer. - /// \return The maximum packet size in bytes, negative libusb error code on error. - int getPacketSize() const; + /// Return the associated scope model. + const DSOModel *getModel() const { return model; } - /// Return the last sample set - const DSOsamples &getLastSamples(); - /// \brief Sends bulk/control commands directly. + /// \brief Sends control commands directly. ///

/// Syntax:
///
- /// Bulk command: - ///

send bulk [hex data]
/// %Control command: ///
send control [hex code] [hex data]
///

/// \param command The command as string (Has to be parsed). /// \return See ::Dso::ErrorCode. - Dso::ErrorCode stringCommand(const QString &commandString); + Dso::ErrorCode stringCommand( const QString &commandString ); - void addCommand(BulkCommand *newCommand, bool pending = true); - template T *modifyCommand(Hantek::BulkCode code) { - command[(uint8_t)code]->pending = true; - return static_cast(command[(uint8_t)code]); - } - const BulkCommand *getCommand(Hantek::BulkCode code) const; + void addCommand( ControlCommand *newCommand, bool pending = true ); - void addCommand(ControlCommand *newCommand, bool pending = true); - template T *modifyCommand(Hantek::ControlCode code) { - control[(uint8_t)code]->pending = true; - return static_cast(control[(uint8_t)code]); + template < class T > T *modifyCommand( Hantek::ControlCode code ) { + control[ uint8_t( code ) ]->pending = true; + return static_cast< T * >( control[ uint8_t( code ) ] ); } - const ControlCommand *getCommand(Hantek::ControlCode code) const; + + bool hasCommand( Hantek::ControlCode code ) { return ( control[ uint8_t( code ) ] != nullptr ); } + + const ControlCommand *getCommand( Hantek::ControlCode code ) const { return control[ uint8_t( code ) ]; } + + /// \brief Stops the device. + void quitSampling(); private: - bool isRollMode() const; - bool isFastRate() const; + bool singleChannel = true; + void setSingleChannel( bool single ) { singleChannel = single; } + bool isSingleChannel() const { return singleChannel; } + bool triggerModeNONE() { return controlsettings.trigger.mode == Dso::TriggerMode::ROLL; } unsigned getRecordLength() const; - + void setDownsampling( unsigned downsampling ) { downsamplingNumber = downsampling; } Dso::ErrorCode retrieveChannelLevelData(); - /// \brief Calculated the nearest samplerate supported by the oscilloscope. - /// \param samplerate The target samplerate, that should be met as good as - /// possible. - /// \param fastRate true, if the fast rate mode is enabled. - /// \param maximum The target samplerate is the maximum allowed when true, the - /// minimum otherwise. - /// \param downsampler Pointer to where the selected downsampling factor should - /// be written. - /// \return The nearest samplerate supported, 0.0 on error. - double getBestSamplerate(double samplerate, bool fastRate = false, bool maximum = false, - unsigned *downsampler = 0) const; - /// Get the number of samples that are expected returned by the scope. /// In rolling mode this is depends on the usb speed and packet size. /// \return The total number of samples the scope should return. - unsigned getSampleCount() const; + unsigned getSampleCount() const { return isSingleChannel() ? getRecordLength() : getRecordLength() * specification->channels; } + + /// adjust for skipping of minimal 2048 leading samples + unsigned grossSampleCount( unsigned net ) const { return ( ( net + 1024 ) / 1024 + 2 ) * 1024; } + + /// calculate backwards to get multiples of 1000 (typical 20000 or 10000) + unsigned netSampleCount( unsigned gross ) const { return ( ( gross - 1024 ) / 1000 - 1 ) * 1000; } void updateInterval(); /// \brief Calculates the trigger point from the CommandGetCaptureState data. /// \param value The data value that contains the trigger point. /// \return The calculated trigger point for the given data. - static unsigned calculateTriggerPoint(unsigned value); + static unsigned calculateTriggerPoint( unsigned value ); - /// \brief Gets the current state. - /// \return The current CaptureState of the oscilloscope. - std::pair getCaptureState() const; - - /// \brief Gets sample data from the oscilloscope - std::vector getSamples(unsigned &expectedSampleCount) const; + // void capture( HantekDsoControl *hdc ); /// \brief Converts raw oscilloscope data to sample data - void convertRawDataToSamples(const std::vector &rawData); - - /// \brief Sets the size of the sample buffer without updating dependencies. - /// \param index The record length index that should be set. - /// \return The record length that has been set, 0 on error. - unsigned updateRecordLength(RecordLengthID size); - - /// \brief Sets the samplerate based on the parameters calculated by - /// Control::getBestSamplerate. - /// \param downsampler The downsampling factor. - /// \param fastRate true, if one channel uses all buffers. - /// \return The downsampling factor that has been set. - unsigned updateSamplerate(unsigned downsampler, bool fastRate); + // void convertRawDataToSamples( const std::vector< unsigned char > &rawData ); + void convertRawDataToSamples(); /// \brief Restore the samplerate/timebase targets after divider updates. void restoreTargets(); @@ -172,125 +159,164 @@ class HantekDsoControl : public QObject { /// \brief Update the minimum and maximum supported samplerate. void updateSamplerateLimits(); - private: - /// Pointers to bulk/control commands - BulkCommand *command[255] = {0}; - BulkCommand *firstBulkCommand = nullptr; - ControlCommand *control[255] = {0}; + unsigned searchTriggerPoint( Dso::Slope dsoSlope, unsigned int startPos = 0 ); + + Dso::Slope mirrorSlope( Dso::Slope slope ) { + return ( slope == Dso::Slope::Positive ? Dso::Slope::Negative : Dso::Slope::Positive ); + } + + unsigned searchTriggeredPosition(); + + bool provideTriggeredData(); + + void controlSetSamplerate( uint8_t sampleIndex ); + + /// Pointers to control commands + ControlCommand *control[ 255 ] = {nullptr}; ControlCommand *firstControlCommand = nullptr; // Communication with device - USBDevice *device; ///< The USB device for the oscilloscope - bool sampling = false; ///< true, if the oscilloscope is taking samples + ScopeDevice *scopeDevice; ///< The USB device for the oscilloscope + bool deviceNotConnected(); ///< USB status, always false for demo device + bool sampling = false; ///< true, if the oscilloscope is taking samples // Device setup + const DSOModel *model; ///< The attached scope model const Dso::ControlSpecification *specification; ///< The specifications of the device Dso::ControlSettings controlsettings; ///< The current settings of the device + const DsoSettingsScope *scope = nullptr; ///< Global scope parameters and configuations // Results + unsigned downsamplingNumber = 1; ///< Number of downsamples to reduce sample rate DSOsamples result; unsigned expectedSampleCount = 0; ///< The expected total number of samples at /// the last check before sampling started + bool capturing = false; + bool samplingStarted = false; + bool stateMachineRunning = false; + int acquireInterval = 0; + int displayInterval = 0; + unsigned triggeredPositionRaw = 0; // not triggered + unsigned activeChannels = 2; + bool newTriggerParam = false; // parameter changed -> new trigger search needed + bool triggerChanged() { + bool changed = newTriggerParam; + newTriggerParam = false; + return changed; + } + + Raw raw; + + std::vector< QString > controlNames = {"SETGAIN_CH1", "SETGAIN_CH2", "SETSAMPLERATE", "STARTSAMPLING", + "SETNUMCHANNELS", "SETCOUPLING", "SETCALFREQ"}; + + unsigned debugLevel = 0; - // State of the communication thread - int captureState = Hantek::CAPTURE_WAITING; - Hantek::RollState rollState = Hantek::RollState::STARTSAMPLING; - bool _samplingStarted = false; - Dso::TriggerMode lastTriggerMode = (Dso::TriggerMode)-1; - int cycleCounter = 0; - int startCycle = 0; - int cycleTime = 0; - - /// \brief Send a bulk command to the oscilloscope. - /// \param command The command, that should be sent. - /// \param attempts The number of attempts, that are done on timeouts. - /// \return Number of sent bytes on success, libusb error code on error. - int bulkCommand(const std::vector *command, int attempts = HANTEK_ATTEMPTS) const; +#define dprintf( level, fmt, ... ) \ + do { \ + if ( debugLevel & level ) \ + fprintf( stderr, fmt, __VA_ARGS__ ); \ + } while ( 0 ) public slots: /// \brief If sampling is disabled, no samplesAvailable() signals are send anymore, no samples /// are fetched from the device and no processing takes place. /// \param enabled Enables/Disables sampling - void enableSampling(bool enabled); + void enableSampling( bool enabled ); - /// \brief Sets the size of the oscilloscopes sample buffer. - /// \param index The record length index that should be set. - /// \return The record length that has been set, 0 on error. - Dso::ErrorCode setRecordLength(unsigned size); /// \brief Sets the samplerate of the oscilloscope. /// \param samplerate The samplerate that should be met (S/s), 0.0 to restore /// current samplerate. /// \return The samplerate that has been set, 0.0 on error. - Dso::ErrorCode setSamplerate(double samplerate = 0.0); + Dso::ErrorCode setSamplerate( double samplerate = 0.0 ); + /// \brief Sets the time duration of one aquisition by adapting the samplerate. /// \param duration The record time duration that should be met (s), 0.0 to /// restore current record time. /// \return The record time duration that has been set, 0.0 on error. - Dso::ErrorCode setRecordTime(double duration = 0.0); + Dso::ErrorCode setRecordTime( double duration = 0.0 ); /// \brief Enables/disables filtering of the given channel. /// \param channel The channel that should be set. /// \param used true if the channel should be sampled. /// \return See ::Dso::ErrorCode. - Dso::ErrorCode setChannelUsed(ChannelID channel, bool used); - /// \brief Set the coupling for the given channel. + Dso::ErrorCode setChannelUsed( ChannelID channel, bool used ); + + /// \brief Enables/disables inverting of the given channel. /// \param channel The channel that should be set. - /// \param coupling The new coupling for the channel. + /// \param used true if the channel is inverted. /// \return See ::Dso::ErrorCode. - Dso::ErrorCode setCoupling(ChannelID channel, Dso::Coupling coupling); + Dso::ErrorCode setChannelInverted( ChannelID channel, bool inverted ); + /// \brief Sets the gain for the given channel. /// Get the actual gain by specification.gainSteps[gainId] /// \param channel The channel that should be set. /// \param gain The gain that should be met (V/div). /// \return The gain that has been set, ::Dso::ErrorCode on error. - Dso::ErrorCode setGain(ChannelID channel, double gain); - /// \brief Set the offset for the given channel. - /// Get the actual offset for the channel from controlsettings.voltage[channel].offsetReal + Dso::ErrorCode setProbe( ChannelID channel, double probeAttn ); + + /// \brief Sets the probe gain for the given channel. /// \param channel The channel that should be set. - /// \param offset The new offset value (0.0 - 1.0). - Dso::ErrorCode setOffset(ChannelID channel, const double offset); + /// \param probeAttn gain of probe is set. + /// \return error code. + Dso::ErrorCode setGain( ChannelID channel, double gain ); + + /// \brief Sets the coupling for the given channel. + /// \param channel The channel that should be set. + /// \param coupling The coupling that should be set. + /// \return error code. + Dso::ErrorCode setCoupling( ChannelID channel, Dso::Coupling coupling ); /// \brief Set the trigger mode. /// \return See ::Dso::ErrorCode. - Dso::ErrorCode setTriggerMode(Dso::TriggerMode mode); + Dso::ErrorCode setTriggerMode( Dso::TriggerMode mode ); + /// \brief Set the trigger source. - /// \param special true for a special channel (EXT, ...) as trigger source. - /// \param id The number of the channel, that should be used as trigger. + /// \param id The channel that should be used as trigger. /// \return See ::Dso::ErrorCode. - Dso::ErrorCode setTriggerSource(bool special, unsigned id); + Dso::ErrorCode setTriggerSource( ChannelID channel, bool smooth ); + /// \brief Set the trigger level. /// \param channel The channel that should be set. /// \param level The new trigger level (V). - /// \return The trigger level that has been set, ::Dso::ErrorCode on error. - Dso::ErrorCode setTriggerLevel(ChannelID channel, double level); + /// \return See ::Dso::ErrorCode. + Dso::ErrorCode setTriggerLevel( ChannelID channel, double level ); + /// \brief Set the trigger slope. /// \param slope The Slope that should cause a trigger. /// \return See ::Dso::ErrorCode. - Dso::ErrorCode setTriggerSlope(Dso::Slope slope); + Dso::ErrorCode setTriggerSlope( Dso::Slope slope ); + /// \brief Set the trigger position. /// \param position The new trigger position (in s). /// \return The trigger position that has been set. - Dso::ErrorCode setPretriggerPosition(double position); - void forceTrigger(); + Dso::ErrorCode setTriggerOffset( double position ); - signals: - void samplingStatusChanged(bool enabled); ///< The oscilloscope started/stopped sampling/waiting for trigger - void statusMessage(const QString &message, int timeout); ///< Status message about the oscilloscope - void samplesAvailable(const DSOsamples *samples); ///< New sample data is available + /// \brief Sets the calibration frequency of the oscilloscope. + /// \param calfreq The calibration frequency. + /// \return The tfrequency that has been set, ::Dso::ErrorCode on error. + Dso::ErrorCode setCalFreq( double calfreq = 0.0 ); + + /// \brief Initializes the device with the current settings. + /// \param scope The settings for the oscilloscope. + void applySettings( DsoSettingsScope *scope ); - void availableRecordLengthsChanged(const std::vector &recordLengths); ///< The available record - /// lengths, empty list for + /// \brief Starts a new sampling block. + void restartSampling(); + + signals: + void samplingStatusChanged( bool enabled ); ///< The oscilloscope started/stopped sampling/waiting for trigger + void statusMessage( const QString &message, int timeout ); ///< Status message about the oscilloscope + void samplesAvailable( const DSOsamples *samples ); ///< New sample data is available /// The available samplerate range has changed - void samplerateLimitsChanged(double minimum, double maximum); + void samplerateLimitsChanged( double minimum, double maximum ); /// The available samplerate for fixed samplerate devices has changed - void samplerateSet(int mode, QList sampleSteps); + void samplerateSet( int mode, QList< double > sampleSteps ); - void recordLengthChanged(unsigned long duration); ///< The record length has changed - void recordTimeChanged(double duration); ///< The record time duration has changed - void samplerateChanged(double samplerate); ///< The samplerate has changed + void samplerateChanged( double samplerate ); ///< The samplerate has changed void communicationError() const; }; -Q_DECLARE_METATYPE(DSOsamples *) +Q_DECLARE_METATYPE( DSOsamples * ) diff --git a/openhantek/src/hantekdso/modelregistry.cpp b/openhantek/src/hantekdso/modelregistry.cpp index 4606b3e2..5d34fbca 100644 --- a/openhantek/src/hantekdso/modelregistry.cpp +++ b/openhantek/src/hantekdso/modelregistry.cpp @@ -7,6 +7,6 @@ ModelRegistry *ModelRegistry::get() { return &inst; } -void ModelRegistry::add(DSOModel *model) { supportedModels.push_back(model); } +void ModelRegistry::add( DSOModel *model ) { supportedModels.push_back( model ); } -const std::list ModelRegistry::models() const { return supportedModels; } +const std::list< DSOModel * > ModelRegistry::models() const { return supportedModels; } diff --git a/openhantek/src/hantekdso/modelregistry.h b/openhantek/src/hantekdso/modelregistry.h index f45d5195..619da5e1 100644 --- a/openhantek/src/hantekdso/modelregistry.h +++ b/openhantek/src/hantekdso/modelregistry.h @@ -1,4 +1,3 @@ - // SPDX-License-Identifier: GPL-2.0+ #pragma once @@ -6,10 +5,11 @@ #include "dsomodel.h" class ModelRegistry { -public: + public: static ModelRegistry *get(); - void add(DSOModel* model); - const std::list models() const; -private: - std::list supportedModels; + void add( DSOModel *model ); + const std::list< DSOModel * > models() const; + + private: + std::list< DSOModel * > supportedModels; }; diff --git a/openhantek/src/hantekdso/models/modelDDS120.cpp b/openhantek/src/hantekdso/models/modelDDS120.cpp new file mode 100644 index 00000000..e6d9db7d --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDDS120.cpp @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "modelDDS120.h" +#include "hantekdsocontrol.h" +#include "hantekprotocol/controlStructs.h" +#include "usb/scopedevice.h" +#include +#include +#include + +#define VERBOSE 0 + +using namespace Hantek; + +static ModelDDS120 modelInstance_120; + +static void initSpecifications( Dso::ControlSpecification &specification ) { + // we drop 2K + 480 sample values due to unreliable start of stream + // 20000 samples at 100kS/s = 200 ms gives enough to fill + // the screen two times (for pre/post trigger) at 10ms/div = 100ms/screen + // SAMPLESIZE defined in hantekdsocontrol.h + // adapt accordingly in HantekDsoControl::convertRawDataToSamples() + + // HW gain, voltage steps in V/screenheight (ranges 20,50,100,200,500,1000,2000,5000 mV) + // DDS120 has gainsteps 20x, 10x, 5x, 2x and 1x (as well as also 4x) + // Hantek has only 10x, 5x, 2x, 1x + specification.gain = { + // ID, HW gain, voltage/div + {20, 20e-3}, // 0 20 20mV/div + {20, 50e-3}, // 1 20 50mV/div + {10, 100e-3}, // 2 10 100mV/div + {5, 200e-3}, // 3 5 200mV/div + {2, 500e-3}, // 4 2 500mV/div + {1, 1.00}, // 5 1 1V/div + {1, 2.00}, // 6 1 2V/div + {1, 5.00} // 7 1 5V/div + }; + + // This data was based on testing and depends on divider. + // The sample value at the top of the screen with gain error correction + // TODO: check if 20x is possible for 1st and 2nd value + // double the values accordingly 32 -> 64 & 80 -> 160 and change 10 -> 20 in specification.gain below + // DDS120 correction factors x20: 0.80, x10: 0.80, x5: 0.77, x2: 0.86, x1: 0.83 + // theoretical voltageScales x20: 500, x10: 250, x5: 125, x2: 50, x1: 25 + specification.voltageScale[ 0 ] = {400, 400, 200, 96, 43, 21, 21, 21}; + specification.voltageScale[ 1 ] = {400, 400, 200, 96, 43, 21, 21, 21}; + // specification.voltageScale[ 0 ] = {64, 160, 160, 155, 170, 165, 330, 820}; + // specification.voltageScale[ 1 ] = {64, 160, 160, 155, 170, 165, 330, 820}; + // theoretical offset, will be corrected by individual config file + specification.voltageOffset[ 0 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + specification.voltageOffset[ 1 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // read the real calibration values from file + const char *ranges[] = {"20mV", "50mV", "100mV", "200mV", "500mV", "1000mV", "2000mV", "5000mV"}; + const char *channels[] = {"ch0", "ch1"}; + // printf( "read config file\n" ); + const unsigned RANGES = 8; + + QString Model = "modelDDS120"; + QString Organisation = "OpenHantek"; + // rename the previous *.conf to *.ini to use the ini file search also for Windows + QString calFileName = QDir::homePath() + "/.config/" + Organisation + "/" + Model; + QFile calFile( calFileName + ".conf" ); + if ( calFile.exists() ) { + qDebug() << "Renamed old mode calibration file:"; + qDebug() << calFileName + ".conf" + << "->" << calFileName + ".ini"; + calFileName += ".ini"; + calFile.rename( calFileName ); + } + + QSettings settings( QSettings::IniFormat, QSettings::UserScope, Organisation, Model ); + // Linux, Unix, macOS: "$HOME/.config/OpenHantek/modelDDS120.ini" + // Windows: "%APPDATA%\OpenHantek\modelDDS120.ini" + + settings.beginGroup( "gain" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + double calibration = settings.value( ranges[ iii ], 0.0 ).toDouble(); + if ( bool( calibration ) ) + specification.voltageScale[ ch ][ iii ] /= calibration; + } + settings.endGroup(); // channels + } + settings.endGroup(); // gain + + settings.beginGroup( "offset" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + // settings.setValue( ranges[ iii ], iii ); + // set to 0x80 if no value from conf file + int offset = settings.value( ranges[ iii ], "255" ).toInt(); + if ( offset != 255 ) // value exists in config file + specification.voltageOffset[ ch ][ iii ] = 0x80 - offset; + // printf( "%d-%d: %d %d\n", ch, iii, offset, specification.voltageOffset[ ch ][ iii ] ); + } + settings.endGroup(); // channels + } + settings.endGroup(); // offset + QSettings::setDefaultFormat( QSettings::NativeFormat ); + + + specification.samplerate.single.base = 1e6; + specification.samplerate.single.max = 30e6; + specification.samplerate.single.recordLengths = {UINT_MAX}; + specification.samplerate.multi.base = 1e6; + specification.samplerate.multi.max = 15e6; + specification.samplerate.multi.recordLengths = {UINT_MAX}; + + // This model uses the sigrok firmware that has a slightly different coding for the sample rate than my Hantek6022API version. + // 10=100k, 20=200k, 50=500k, 11=10M (Hantek: 110=100k, 120=200k, 150=500k, 10=10M) + + // 48M is unstable in 1 channel mode + // 24M, 30M and 48M are unstable in 2 channel mode + + specification.fixedSampleRates = { + // samplerate, sampleId, downsampling + {10e3, 1, 100}, // 100x downsampling from 1 MS/s! + {20e3, 2, 100}, // 100x downsampling from 2 MS/s! + {50e3, 5, 100}, // 100x downsampling from 5 MS/s! + {100e3, 8, 80}, // 80x downsampling from 8 MS/s + {200e3, 8, 40}, // 40x downsampling from 8 MS/s + {500e3, 8, 16}, // 16x downsampling from 8 MS/s + {1e6, 8, 8}, // 8x downsampling from 8 MS/s + {2e6, 8, 4}, // 4x downsampling from 8 MS/s + {5e6, 15, 3}, // 3x downsampling from 15 MS/s + {10e6, 11, 1}, // no downsampling, 11 means 10 MS/s + {15e6, 15, 1}, // no downsampling + {24e6, 24, 1}, // no downsampling + {30e6, 30, 1}, // no downsampling + {48e6, 48, 1} // no downsampling + }; + + + specification.couplings = {Dso::Coupling::DC, Dso::Coupling::AC}; + specification.hasACcoupling = true; // DDS120 has AC coupling + specification.triggerModes = { + Dso::TriggerMode::ROLL, + Dso::TriggerMode::AUTO, + Dso::TriggerMode::NORMAL, + Dso::TriggerMode::SINGLE, + }; + specification.fixedUSBinLength = 0; + // use calibration frequency steps of modified sigrok FW (<= 20 kHz) + specification.calfreqSteps = {50, 60, 100, 200, 500, 1000, 2000, 5000, 10000, 20000}; + specification.hasCalibrationEEPROM = false; +} + +static void applyRequirements_( HantekDsoControl *dsoControl ) { + dsoControl->addCommand( new ControlSetGain_CH1() ); // 0xE0 + dsoControl->addCommand( new ControlSetGain_CH2() ); // 0xE1 + dsoControl->addCommand( new ControlSetSamplerate() ); // 0xE2 + dsoControl->addCommand( new ControlStartSampling() ); // 0xE3 + dsoControl->addCommand( new ControlSetNumChannels() ); // 0xE4 + dsoControl->addCommand( new ControlSetCoupling() ); // 0xE5 + dsoControl->addCommand( new ControlSetCalFreq() ); // 0xE6 +} + +// VID/PID active VID/PID no FW FW ver FW name Scope name +// |------------| |------------| |----| |------| |------| +ModelDDS120::ModelDDS120() + : DSOModel( ID, 0x04b5, 0x0120, 0x8102, 0x8102, 0x0100, "dds120", "DDS120", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); +} + +void ModelDDS120::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } diff --git a/openhantek/src/hantekdso/models/modelDDS120.h b/openhantek/src/hantekdso/models/modelDDS120.h new file mode 100644 index 00000000..55999ad1 --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDDS120.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include "dsomodel.h" + +class HantekDsoControl; +using namespace Hantek; + + +struct ModelDDS120 : public DSOModel { + static const int ID = 0x0120; + ModelDDS120(); + void applyRequirements( HantekDsoControl *dsoControl ) const override; +}; diff --git a/openhantek/src/hantekdso/models/modelDDS120.ini b/openhantek/src/hantekdso/models/modelDDS120.ini new file mode 100644 index 00000000..664200da --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDDS120.ini @@ -0,0 +1,50 @@ +; OpenHantek calibration file +; individual calibration values for DDS120 +; copy into ~/.config/OpenHantek +; will be read by .../src/hantekdso/modelDDS120.cpp + +; multiply channel to get the correct gain +[gain] + +ch0\10mV=1.0 +ch0\20mV=1.0 +ch0\50mV=1.0 +ch0\100mV=1.0 +ch0\200mV=1.0 +ch0\500mV=1.0 +ch0\1000mV=1.0 +ch0\2000mV=1.0 +ch0\5000mV=1.0 + +ch1\10mV=1.0 +ch1\20mV=1.0 +ch1\50mV=1.0 +ch1\100mV=1.0 +ch1\200mV=1.0 +ch1\500mV=1.0 +ch1\1000mV=1.0 +ch1\2000mV=1.0 +ch1\5000mV=1.0 + +; add offset values +[offset] + +ch0\10mV=-6 +ch0\20mV=-6 +ch0\50mV=-6 +ch0\100mV=-6 +ch0\200mV=-6 +ch0\500mV=-7 +ch0\1000mV=-7 +ch0\2000mV=-7 +ch0\5000mV=-7 + +ch1\10mV=+3 +ch1\20mV=+3 +ch1\50mV=+3 +ch1\100mV=+3 +ch1\200mV=+3 +ch1\500mV=-1 +ch1\1000mV=-1 +ch1\2000mV=-1 +ch1\5000mV=-1 diff --git a/openhantek/src/hantekdso/models/modelDEMO.cpp b/openhantek/src/hantekdso/models/modelDEMO.cpp new file mode 100644 index 00000000..63da0e8e --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDEMO.cpp @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "modelDEMO.h" +#include "hantekdsocontrol.h" +#include "hantekprotocol/controlStructs.h" +#include "usb/scopedevice.h" +#include +#include +#include + +#define VERBOSE 0 + +using namespace Hantek; + +static ModelDEMO modelInstance_DEMO; + +static void initSpecifications( Dso::ControlSpecification &specification ) { + // we drop 2K + 480 sample values due to unreliable start of stream + // 20000 samples at 100kS/s = 200 ms gives enough to fill + // the screen two times (for pre/post trigger) at 10ms/div = 100ms/screen + // SAMPLESIZE defined in hantekdsocontrol.h + // adapt accordingly in HantekDsoControl::convertRawDataToSamples() + + // HW gain, voltage steps in V/div (ranges 20,50,100,200,500,1000,2000,5000 mV) + specification.gain = {{10, 20e-3}, {10, 50e-3}, {10, 100e-3}, {5, 200e-3}, {2, 500e-3}, {1, 1.00}, {1, 2.00}, {1, 5.00}}; + + // Define the scaling between ADC sample values and real input voltage + // Everything is scaled on the full screen height (8 divs) + // The voltage/div setting: 20m 50m 100m 200m 500m 1V 2V 5V + // Equivalent input voltage: 0.16V 0.4V 0.8V 1.6V 4V 8V 16V 40V + // Theoretical gain setting: x10 x10 x10 x5 x2 x1 x1 x1 + // mV / digit: 4 4 4 8 20 40 40 40 + // The sample value for full screen (8 divs) with theoretical gain setting + specification.voltageScale[ 0 ] = {250, 250, 250, 125, 50, 25, 25, 25}; + specification.voltageScale[ 1 ] = {250, 250, 250, 125, 50, 25, 25, 25}; + specification.voltageOffset[ 0 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + specification.voltageOffset[ 1 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + // Gain and offset can be corrected by individual config values from file + + // read the real calibration values from file + const char *ranges[] = {"20mV", "50mV", "100mV", "200mV", "500mV", "1000mV", "2000mV", "5000mV"}; + const char *channels[] = {"ch0", "ch1"}; + // printf( "read config file\n" ); + const unsigned RANGES = 8; + + QString Model = "modelDEMO"; + QString Organisation = "OpenHantek"; + // rename the previous *.conf to *.ini to use the ini file search also for Windows + QString calFileName = QDir::homePath() + "/.config/" + Organisation + "/" + Model; + QFile calFile( calFileName + ".conf" ); + if ( calFile.exists() ) { + qDebug() << "Renamed old mode calibration file:"; + qDebug() << calFileName + ".conf" + << "->" << calFileName + ".ini"; + calFileName += ".ini"; + calFile.rename( calFileName ); + } + + QSettings settings( QSettings::IniFormat, QSettings::UserScope, Organisation, Model ); + // Linux, Unix, macOS: "$HOME/.config/OpenHantek/modelDEMO.ini" + // Windows: "%APPDATA%\OpenHantek\modelDEMO.ini" + + settings.beginGroup( "gain" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + double gain = settings.value( ranges[ iii ], "0.0" ).toDouble(); + // printf( "ch%d %s: gain = %g\n", ch, ranges[ iii ], gain ); + if ( bool( gain ) ) + specification.voltageScale[ ch ][ iii ] /= gain; + } + settings.endGroup(); // channels + } + settings.endGroup(); // gain + + settings.beginGroup( "offset" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + // set to 0x00 if no value from conf file + int offset = settings.value( ranges[ iii ], "255" ).toInt(); + // printf( "ch%d %s: offset = %d\n", ch, ranges[ iii ], offset ); + if ( offset != 255 ) // value exists in config file + specification.voltageOffset[ ch ][ iii ] = 0x80 - offset; + } + settings.endGroup(); // channels + } + settings.endGroup(); // offset + + // Possible raw sample rates with custom fw from https://github.com/Ho-Ro/Hantek6022API + // 20k, 50k, 64k, 100k, 200k, 500k, 1M, 2M, 3M, 4M, 5M, 6M, 8M, 10M, 12M, 15M, 16M, 24M, 30M (, 48M) + // 48M is unusable in 1 channel mode due to massive USB overrun + // 24M, 30M and 48M are unusable in 2 channel mode + // these unstable settings are disabled + // Lower effective sample rates < 10 MS/s use oversampling to increase the SNR + + specification.samplerate.single.base = 1e6; + specification.samplerate.single.max = 30e6; + specification.samplerate.single.recordLengths = {UINT_MAX}; + specification.samplerate.multi.base = 1e6; + specification.samplerate.multi.max = 15e6; + specification.samplerate.multi.recordLengths = {UINT_MAX}; + + specification.fixedSampleRates = { + // samplerate, sampleId, downsampling + {100, 102, 200}, // very slow! 200x downsampling from 20 kS/s + {200, 104, 200}, // very slow! 200x downsampling from 40 kS/s + {500, 110, 200}, // very slow! 200x downsampling from 100 kS/s + {1e3, 120, 200}, // slow! 200x downsampling from 200 kS/s + {2e3, 140, 200}, // slow! 200x downsampling from 400 kS/s + {5e3, 1, 200}, // slow! 200x downsampling from 1 MS/s + {10e3, 1, 100}, // 100x downsampling from 1, 2, 5, 10 MS/s + {20e3, 2, 100}, // + {50e3, 5, 100}, // + {100e3, 10, 100}, // + {200e3, 10, 50}, // 50x, 20x 10x, 5x, 2x downsampling from 10 MS/s + {500e3, 10, 20}, // + {1e6, 10, 10}, // + {2e6, 10, 5}, // + {5e6, 10, 2}, // + {10e6, 10, 1}, // no oversampling + {12e6, 12, 1}, // + {15e6, 15, 1}, // + {24e6, 24, 1}, // + {30e6, 30, 1}, // + {48e6, 48, 1} // + }; + + // AC requires AC/DC HW mod like DDS120, enable with "cmake -D HANTEK_AC=1 .." or config option + specification.couplings = {Dso::Coupling::DC, Dso::Coupling::AC}; +#ifdef HANTEK_AC + specification.hasACcoupling = true; +#else + specification.hasACcoupling = false; +#endif + specification.triggerModes = { + Dso::TriggerMode::ROLL, + Dso::TriggerMode::AUTO, + Dso::TriggerMode::NORMAL, + Dso::TriggerMode::SINGLE, + }; + specification.fixedUSBinLength = 0; + + // calibration frequency (requires >FW0206) + specification.calfreqSteps = {50, 60, 100, 200, 500, 1e3, 2e3, 5e3, 10e3, 20e3, 50e3, 100e3}; + specification.hasCalibrationEEPROM = true; + specification.isDemoDevice = true; +} + +static void applyRequirements_( HantekDsoControl *dsoControl ) { + dsoControl->addCommand( new ControlSetGain_CH1() ); // 0xE0 + dsoControl->addCommand( new ControlSetGain_CH2() ); // 0xE1 + dsoControl->addCommand( new ControlSetSamplerate() ); // 0xE2 + dsoControl->addCommand( new ControlStartSampling() ); // 0xE3 + dsoControl->addCommand( new ControlSetNumChannels() ); // 0xE4 + dsoControl->addCommand( new ControlSetCoupling() ); // 0xE5 (no effect w/o AC/DC HW mod) + dsoControl->addCommand( new ControlSetCalFreq() ); // 0xE6 +} + + +// DEMO similar to Hantek DSO-6022BE +// +// VID/PID active VID/PID no FW FW ver FW name Scope name +// |------------| |------------| |----| |---------| |----------| +ModelDEMO::ModelDEMO() : DSOModel( ID, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, "demo", "DEMO", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); +} + +void ModelDEMO::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } diff --git a/openhantek/src/hantekdso/models/modelDEMO.h b/openhantek/src/hantekdso/models/modelDEMO.h new file mode 100644 index 00000000..624c4c36 --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDEMO.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include "dsomodel.h" + +class HantekDsoControl; +using namespace Hantek; + + +struct ModelDEMO : public DSOModel { + static const int ID = 0xDEDE; + ModelDEMO(); + void applyRequirements( HantekDsoControl *dsoControl ) const override; +}; diff --git a/openhantek/src/hantekdso/models/modelDSO2090.cpp b/openhantek/src/hantekdso/models/modelDSO2090.cpp deleted file mode 100644 index cb81bb42..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2090.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "modelDSO2090.h" -#include "hantekprotocol/bulkStructs.h" -#include "hantekprotocol/controlStructs.h" -#include "hantekdsocontrol.h" - -using namespace Hantek; - -static ModelDSO2090 modelInstance; -static ModelDSO2090A modelInstance2; - -void _applyRequirements(HantekDsoControl *dsoControl) { - dsoControl->addCommand(new BulkForceTrigger(), false); - dsoControl->addCommand(new BulkCaptureStart(), false); - dsoControl->addCommand(new BulkTriggerEnabled(), false); - dsoControl->addCommand(new BulkGetData(), false); - dsoControl->addCommand(new BulkGetCaptureState(), false); - dsoControl->addCommand(new BulkSetGain(), false); - - dsoControl->addCommand(new BulkSetTriggerAndSamplerate(), false); - dsoControl->addCommand(new ControlSetOffset(), false); - dsoControl->addCommand(new ControlSetRelays(), false); -} - -void initSpecifications(Dso::ControlSpecification& specification) { - specification.cmdSetRecordLength = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetChannels = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetSamplerate = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetTrigger = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetPretrigger = BulkCode::SETTRIGGERANDSAMPLERATE; - - specification.samplerate.single.base = 50e6; - specification.samplerate.single.max = 50e6; - specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 32768}; - specification.samplerate.multi.base = 100e6; - specification.samplerate.multi.max = 100e6; - specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 65536}; - specification.bufferDividers = { 1000 , 1 , 1 }; - specification.voltageLimit[0] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.voltageLimit[1] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.gain = { {0,0.08} , {1,0.16} , {2,0.40} , {0,0.80} , - {1,1.60} , {2,4.00} , {0,8.00} , {1,16.00} , {2,40.00} }; - specification.sampleSize = 8; - specification.specialTriggerChannels = {{"EXT", -2}, {"EXT/10", -3}}; -} - -ModelDSO2090::ModelDSO2090() : DSOModel(ID, 0x04b5, 0x2090, 0x04b4, 0x2090, "dso2090x86", "DSO-2090", - Dso::ControlSpecification(2)) { - initSpecifications(specification); -} - -void ModelDSO2090::applyRequirements(HantekDsoControl *dsoControl) const { - _applyRequirements(dsoControl); -} - -ModelDSO2090A::ModelDSO2090A() : DSOModel(ID, 0x04b5, 0x2090, 0x04b4, 0x8613, "dso2090x86", "DSO-2090", - Dso::ControlSpecification(2)) { - initSpecifications(specification); -} - -void ModelDSO2090A::applyRequirements(HantekDsoControl *dsoControl) const { - _applyRequirements(dsoControl); -} - diff --git a/openhantek/src/hantekdso/models/modelDSO2090.h b/openhantek/src/hantekdso/models/modelDSO2090.h deleted file mode 100644 index 55458918..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2090.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "dsomodel.h" - -class HantekDsoControl; -using namespace Hantek; - -struct ModelDSO2090 : public DSOModel { - static const int ID = 0x2090; - ModelDSO2090(); - void applyRequirements(HantekDsoControl* dsoControl) const override; -}; - -struct ModelDSO2090A : public DSOModel { - static const int ID = 0x2090; - ModelDSO2090A(); - void applyRequirements(HantekDsoControl* dsoControl) const override; -}; - diff --git a/openhantek/src/hantekdso/models/modelDSO2150.cpp b/openhantek/src/hantekdso/models/modelDSO2150.cpp deleted file mode 100644 index 0334eea1..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2150.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "modelDSO2150.h" -#include "hantekprotocol/bulkStructs.h" -#include "hantekprotocol/controlStructs.h" -#include "hantekdsocontrol.h" - -using namespace Hantek; - -static ModelDSO2150 modelInstance; - -ModelDSO2150::ModelDSO2150() : DSOModel(ID, 0x04b5, 0x2150, 0x04b4, 0x2150, "dso2150x86", "DSO-2150", - Dso::ControlSpecification(2)) { - specification.cmdSetRecordLength = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetChannels = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetSamplerate = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetTrigger = BulkCode::SETTRIGGERANDSAMPLERATE; - specification.cmdSetPretrigger = BulkCode::SETTRIGGERANDSAMPLERATE; - - specification.samplerate.single.base = 50e6; - specification.samplerate.single.max = 75e6; - specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 32768}; - specification.samplerate.multi.base = 100e6; - specification.samplerate.multi.max = 150e6; - specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 65536}; - specification.bufferDividers = { 1000 , 1 , 1 }; - specification.voltageLimit[0] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.voltageLimit[1] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.gain = { {0,0.08} , {1,0.16} , {2,0.40} , {0,0.80} , - {1,1.60} , {2,4.00} , {0,8.00} , {1,16.00} , {2,40.00} }; - specification.sampleSize = 8; - specification.specialTriggerChannels = {{"EXT", -2}, {"EXT/10", -3}}; -} - -void ModelDSO2150::applyRequirements(HantekDsoControl *dsoControl) const { - dsoControl->addCommand(new BulkForceTrigger(), false); - dsoControl->addCommand(new BulkCaptureStart(), false); - dsoControl->addCommand(new BulkTriggerEnabled(), false); - dsoControl->addCommand(new BulkGetData(), false); - dsoControl->addCommand(new BulkGetCaptureState(), false); - dsoControl->addCommand(new BulkSetGain(), false); - - dsoControl->addCommand(new BulkSetTriggerAndSamplerate(), false); - dsoControl->addCommand(new ControlSetOffset(), false); - dsoControl->addCommand(new ControlSetRelays(), false); -} diff --git a/openhantek/src/hantekdso/models/modelDSO2150.h b/openhantek/src/hantekdso/models/modelDSO2150.h deleted file mode 100644 index d4ff4c8c..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2150.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "dsomodel.h" - -class HantekDsoControl; -using namespace Hantek; - -struct ModelDSO2150 : public DSOModel { - static const int ID = 0x2150; - ModelDSO2150(); - virtual void applyRequirements(HantekDsoControl* dsoControl) const override; -}; diff --git a/openhantek/src/hantekdso/models/modelDSO2250.cpp b/openhantek/src/hantekdso/models/modelDSO2250.cpp deleted file mode 100644 index e562665c..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2250.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "modelDSO2250.h" -#include "hantekprotocol/bulkStructs.h" -#include "hantekprotocol/controlStructs.h" -#include "hantekdsocontrol.h" - -using namespace Hantek; - -static ModelDSO2250 modelInstance; - -ModelDSO2250::ModelDSO2250() : DSOModel(ID, 0x04b5, 0x2250, 0x04b4, 0x2250, "dso2250x86", "DSO-2250", - Dso::ControlSpecification(2)) { - specification.cmdSetRecordLength = BulkCode::DSETBUFFER; - specification.cmdSetChannels = BulkCode::BSETCHANNELS; - specification.cmdSetSamplerate = BulkCode::ESETTRIGGERORSAMPLERATE; - specification.cmdSetTrigger = BulkCode::CSETTRIGGERORSAMPLERATE; - specification.cmdSetPretrigger = BulkCode::FSETBUFFER; - - specification.samplerate.single.base = 100e6; - specification.samplerate.single.max = 100e6; - specification.samplerate.single.maxDownsampler = 65536; - specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 524288}; - specification.samplerate.multi.base = 200e6; - specification.samplerate.multi.max = 250e6; - specification.samplerate.multi.maxDownsampler = 65536; - specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 1048576}; - specification.bufferDividers = { 1000 , 1 , 1 }; - specification.voltageLimit[0] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.voltageLimit[1] = { 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 }; - specification.gain = { {0,0.08} , {2,0.16} , {3,0.40} , {0,0.80} , - {2,1.60} , {3,4.00} , {0,8.00} , {2,16.00} , {3,40.00} }; - specification.sampleSize = 8; - specification.specialTriggerChannels = {{"EXT", -2}}; -} - -void ModelDSO2250::applyRequirements(HantekDsoControl *dsoControl) const { - dsoControl->addCommand(new BulkForceTrigger(), false); - dsoControl->addCommand(new BulkCaptureStart(), false); - dsoControl->addCommand(new BulkTriggerEnabled(), false); - dsoControl->addCommand(new BulkGetData(), false); - dsoControl->addCommand(new BulkGetCaptureState(), false); - dsoControl->addCommand(new BulkSetGain(), false); - - // Instantiate additional commands for the DSO-2250 - dsoControl->addCommand(new BulkSetChannels2250(), false); - dsoControl->addCommand(new BulkSetTrigger2250(), false); - dsoControl->addCommand(new BulkSetRecordLength2250(), false); - dsoControl->addCommand(new BulkSetSamplerate2250(), false); - dsoControl->addCommand(new BulkSetBuffer2250(), false); - dsoControl->addCommand(new ControlSetOffset(), false); - dsoControl->addCommand(new ControlSetRelays(), false); -} diff --git a/openhantek/src/hantekdso/models/modelDSO2250.h b/openhantek/src/hantekdso/models/modelDSO2250.h deleted file mode 100644 index aa81b294..00000000 --- a/openhantek/src/hantekdso/models/modelDSO2250.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "dsomodel.h" - -class HantekDsoControl; -using namespace Hantek; - -struct ModelDSO2250 : public DSOModel { - static const int ID = 0x2250; - ModelDSO2250(); - void applyRequirements(HantekDsoControl* dsoControl) const override; -}; diff --git a/openhantek/src/hantekdso/models/modelDSO5200.cpp b/openhantek/src/hantekdso/models/modelDSO5200.cpp deleted file mode 100644 index 9bdef90b..00000000 --- a/openhantek/src/hantekdso/models/modelDSO5200.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "modelDSO5200.h" -#include "hantekprotocol/bulkStructs.h" -#include "hantekprotocol/controlStructs.h" -#include "hantekdsocontrol.h" - -using namespace Hantek; - -static ModelDSO5200 modelInstance; -static ModelDSO5200A modelInstance2; - -static void initSpecifications(Dso::ControlSpecification& specification) { - specification.cmdSetRecordLength = BulkCode::DSETBUFFER; - specification.cmdSetChannels = BulkCode::ESETTRIGGERORSAMPLERATE; - specification.cmdSetSamplerate = BulkCode::CSETTRIGGERORSAMPLERATE; - specification.cmdSetTrigger = BulkCode::ESETTRIGGERORSAMPLERATE; - specification.cmdSetPretrigger = BulkCode::ESETTRIGGERORSAMPLERATE; - - specification.samplerate.single.base = 100e6; - specification.samplerate.single.max = 125e6; - specification.samplerate.single.maxDownsampler = 131072; - specification.samplerate.single.recordLengths = {UINT_MAX, 10240, 14336}; - specification.samplerate.multi.base = 200e6; - specification.samplerate.multi.max = 250e6; - specification.samplerate.multi.maxDownsampler = 131072; - specification.samplerate.multi.recordLengths = {UINT_MAX, 20480, 28672}; - specification.bufferDividers = { 1000 , 1 , 1 }; - /// \todo Use calibration data to get the DSO-5200(A) sample ranges - specification.voltageLimit[0] = { 368 , 454 , 908 , 368 , 454 , 908 , 368 , 454 , 908 }; - specification.voltageLimit[1] = { 368 , 454 , 908 , 368 , 454 , 908 , 368 , 454 , 908 }; - specification.gain = { {1,0.16} , {0,0.40} , {0,0.80} , {1,1.60} , - {0,4.00} , {0,8.00} , {1,16.0} , {0,40.0} , {0,80.0} }; - specification.sampleSize = 10; - specification.specialTriggerChannels = {{"EXT", -2}, {"EXT/10", -3}}; // 3, 4 -} - -static void _applyRequirements(HantekDsoControl *dsoControl) { - dsoControl->addCommand(new BulkForceTrigger(), false); - dsoControl->addCommand(new BulkCaptureStart(), false); - dsoControl->addCommand(new BulkTriggerEnabled(), false); - dsoControl->addCommand(new BulkGetData(), false); - dsoControl->addCommand(new BulkGetCaptureState(), false); - dsoControl->addCommand(new BulkSetGain(), false); - - // Instantiate additional commands for the DSO-5200 - dsoControl->addCommand(new BulkSetSamplerate5200(), false); - dsoControl->addCommand(new BulkSetBuffer5200(), false); - dsoControl->addCommand(new BulkSetTrigger5200(), false); - dsoControl->addCommand(new ControlSetOffset(), false); - dsoControl->addCommand(new ControlSetRelays(), false); -} - -ModelDSO5200::ModelDSO5200() : DSOModel(ID, 0x04b5, 0x5200, 0x04b4, 0x5200, "dso5200x86", "DSO-5200", - Dso::ControlSpecification(2)) { - initSpecifications(specification); -} - -void ModelDSO5200::applyRequirements(HantekDsoControl *dsoControl) const { - _applyRequirements(dsoControl); -} - -ModelDSO5200A::ModelDSO5200A() : DSOModel(ID, 0x04b5, 0x520a, 0x04b4, 0x520a, "dso5200ax86", "DSO-5200A", - Dso::ControlSpecification(2)) { - initSpecifications(specification); -} - -void ModelDSO5200A::applyRequirements(HantekDsoControl *dsoControl) const { - _applyRequirements(dsoControl); -} diff --git a/openhantek/src/hantekdso/models/modelDSO5200.h b/openhantek/src/hantekdso/models/modelDSO5200.h deleted file mode 100644 index e5c81cc5..00000000 --- a/openhantek/src/hantekdso/models/modelDSO5200.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "dsomodel.h" - -class HantekDsoControl; -using namespace Hantek; - -struct ModelDSO5200 : public DSOModel { - static const int ID = 0x5200; - ModelDSO5200(); - void applyRequirements(HantekDsoControl* dsoControl) const override; -}; - -struct ModelDSO5200A : public DSOModel { - static const int ID = 0x5200; - ModelDSO5200A(); - void applyRequirements(HantekDsoControl* dsoControl) const override; -}; diff --git a/openhantek/src/hantekdso/models/modelDSO6022.cpp b/openhantek/src/hantekdso/models/modelDSO6022.cpp index 5ab11e1a..213332f2 100644 --- a/openhantek/src/hantekdso/models/modelDSO6022.cpp +++ b/openhantek/src/hantekdso/models/modelDSO6022.cpp @@ -1,67 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "modelDSO6022.h" -#include "usb/usbdevice.h" -#include "hantekprotocol/controlStructs.h" #include "hantekdsocontrol.h" +#include "hantekprotocol/controlStructs.h" +#include "usb/scopedevice.h" +#include +#include +#include + +#define VERBOSE 0 using namespace Hantek; -static ModelDSO6022BE modelInstance; -static ModelDSO6022BL modelInstance2; +static ModelDSO6022BE modelInstance_6022be; +static ModelDSO6022BL modelInstance_6022bl; + +static ModelDSO2020 modelInstance_2020; + +static void initSpecifications( Dso::ControlSpecification &specification ) { + // we drop 2K + 480 sample values due to unreliable start of stream + // 20000 samples at 100kS/s = 200 ms gives enough to fill + // the screen two times (for pre/post trigger) at 10ms/div = 100ms/screen + // SAMPLESIZE defined in hantekdsocontrol.h + // adapt accordingly in HantekDsoControl::convertRawDataToSamples() + + // HW gain, voltage steps in V/div (ranges 20,50,100,200,500,1000,2000,5000 mV) + specification.gain = {{10, 20e-3}, {10, 50e-3}, {10, 100e-3}, {5, 200e-3}, {2, 500e-3}, {1, 1.00}, {1, 2.00}, {1, 5.00}}; + + // Define the scaling between ADC sample values and real input voltage + // Everything is scaled on the full screen height (8 divs) + // The voltage/div setting: 20m 50m 100m 200m 500m 1V 2V 5V + // Equivalent input voltage: 0.16V 0.4V 0.8V 1.6V 4V 8V 16V 40V + // Theoretical gain setting: x10 x10 x10 x5 x2 x1 x1 x1 + // mV / digit: 4 4 4 8 20 40 40 40 + // The real input front end introduces a gain error + // Input divider: 100/1009 = 1% too low display + // Amplifier gain: x1 (ok), x2 (ok), x5.1 (2% too high), x10.1 (1% too high) + // Overall resulting gain: x1 1% too low, x2 1% to low, x5 1% to high, x10 ok + // The sample value for full screen (8 divs) with theoretical gain setting + // specification.voltageScale[ 0 ] = {40, 100, 200, 202, 198, 198, 396, 990}; + // specification.voltageScale[ 1 ] = {40, 100, 200, 202, 198, 198, 396, 990}; + specification.voltageScale[ 0 ] = {250, 250, 250, 126.25, 49.50, 24.75, 24.75, 24.75}; + specification.voltageScale[ 1 ] = {250, 250, 250, 126.25, 49.50, 24.75, 24.75, 24.75}; + specification.voltageOffset[ 0 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + specification.voltageOffset[ 1 ] = {0, 0, 0, 0, 0, 0, 0, 0}; + // Gain and offset can be corrected by individual config values from EEPROM or file + + // read the real calibration values from file + const char *ranges[] = {"20mV", "50mV", "100mV", "200mV", "500mV", "1000mV", "2000mV", "5000mV"}; + const char *channels[] = {"ch0", "ch1"}; + // printf( "read config file\n" ); + const unsigned RANGES = 8; -static void initSpecifications(Dso::ControlSpecification& specification) { - // 6022xx do not support any bulk commands - specification.useControlNoBulk = true; - specification.isSoftwareTriggerDevice = true; - specification.isFixedSamplerateDevice = true; - specification.supportsCaptureState = false; - specification.supportsOffset = false; - specification.supportsCouplingRelays = false; + QString Model = "modelDSO6022"; + QString Organisation = "OpenHantek"; + // rename the previous *.conf to *.ini to use the ini file search also for Windows + QString calFileName = QDir::homePath() + "/.config/" + Organisation + "/" + Model; + QFile calFile( calFileName + ".conf" ); + if ( calFile.exists() ) { + qDebug() << "Renamed old mode calibration file:"; + qDebug() << calFileName + ".conf" + << "->" << calFileName + ".ini"; + calFileName += ".ini"; + calFile.rename( calFileName ); + } + + QSettings settings( QSettings::IniFormat, QSettings::UserScope, Organisation, Model ); + // Linux, Unix, macOS: "$HOME/.config/OpenHantek/modelDSO6022.ini" + // Windows: "%APPDATA%\OpenHantek\modelDSO6022.ini" + // qDebug() << settings.fileName(); + + settings.beginGroup( "gain" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + double gain = settings.value( ranges[ iii ], "0.0" ).toDouble(); + // printf( "ch%d %s: gain = %g\n", ch, ranges[ iii ], gain ); + if ( bool( gain ) ) + specification.voltageScale[ ch ][ iii ] /= gain; + } + settings.endGroup(); // channels + } + settings.endGroup(); // gain + + settings.beginGroup( "offset" ); + for ( unsigned ch = 0; ch < 2; ch++ ) { + settings.beginGroup( channels[ ch ] ); + for ( unsigned iii = 0; iii < RANGES; iii++ ) { + // set to 0x00 if no value from conf file + int offset = settings.value( ranges[ iii ], "255" ).toInt(); + // printf( "ch%d %s: offset = %d\n", ch, ranges[ iii ], offset ); + if ( offset != 255 ) // value exists in config file + specification.voltageOffset[ ch ][ iii ] = 0x80 - offset; + } + settings.endGroup(); // channels + } + settings.endGroup(); // offset + QSettings::setDefaultFormat( QSettings::NativeFormat ); + + // Possible raw sample rates with custom fw from https://github.com/Ho-Ro/Hantek6022API + // 20k, 50k, 64k, 100k, 200k, 500k, 1M, 2M, 3M, 4M, 5M, 6M, 8M, 10M, 12M, 15M, 16M, 24M, 30M (, 48M) + // 48M is unusable in 1 channel mode due to massive USB overrun + // 24M, 30M and 48M are unusable in 2 channel mode + // these unstable settings are disabled + // Lower effective sample rates < 10 MS/s use oversampling to increase the SNR specification.samplerate.single.base = 1e6; - specification.samplerate.single.max = 48e6; - specification.samplerate.single.maxDownsampler = 10; - specification.samplerate.single.recordLengths = {UINT_MAX, 10240}; + specification.samplerate.single.max = 30e6; + specification.samplerate.single.recordLengths = {UINT_MAX}; specification.samplerate.multi.base = 1e6; - specification.samplerate.multi.max = 48e6; - specification.samplerate.multi.maxDownsampler = 10; - specification.samplerate.multi.recordLengths = {UINT_MAX, 20480}; - specification.bufferDividers = { 1000 , 1 , 1 }; - // This data was based on testing and depends on Divider. - specification.voltageLimit[0] = { 25 , 51 , 103 , 206 , 412 , 196 , 392 , 784 , 1000 }; - specification.voltageLimit[1] = { 25 , 51 , 103 , 206 , 412 , 196 , 392 , 784 , 1000 }; - // Divider. Tested and calculated results are different! - specification.gain = { {10,0.08} , {10,0.16} , {10,0.40} , {10,0.80} , - {10,1.60} , {2,4.00} , {2,8.00} , {2,16.00} , {1,40.00} }; - specification.fixedSampleRates = { {10,1e5} , {20,2e5} , {50,5e5} , {1,1e6} , {2,2e6} , {4,4e6} , {8,8e6} , - {16,16e6} , {24,24e6} , {48,48e6} }; - specification.sampleSize = 8; - - specification.couplings = {Dso::Coupling::DC}; - specification.triggerModes = {Dso::TriggerMode::HARDWARE_SOFTWARE, Dso::TriggerMode::SINGLE}; - specification.fixedUSBinLength = 16384; + specification.samplerate.multi.max = 15e6; + specification.samplerate.multi.recordLengths = {UINT_MAX}; + + specification.fixedSampleRates = { + // samplerate, sampleId, downsampling + {100, 102, 200}, // very slow! 200x downsampling from 20 kS/s + {200, 104, 200}, // very slow! 200x downsampling from 40 kS/s + {500, 110, 200}, // very slow! 200x downsampling from 100 kS/s + {1e3, 120, 200}, // slow! 200x downsampling from 200 kS/s + {2e3, 140, 200}, // slow! 200x downsampling from 400 kS/s + {5e3, 1, 200}, // slow! 200x downsampling from 1 MS/s + {10e3, 1, 100}, // 100x downsampling from 1, 2, 5, 10 MS/s + {20e3, 2, 100}, // + {50e3, 5, 100}, // + {100e3, 10, 100}, // + {200e3, 10, 50}, // 50x, 20x 10x, 5x, 2x downsampling from 10 MS/s + {500e3, 10, 20}, // + {1e6, 10, 10}, // + {2e6, 10, 5}, // + {5e6, 10, 2}, // + {10e6, 10, 1}, // no oversampling + {12e6, 12, 1}, // + {15e6, 15, 1}, // + {24e6, 24, 1}, // + {30e6, 30, 1}, // + {48e6, 48, 1} // + }; + + // AC requires AC/DC HW mod like DDS120, enable with "cmake -D HANTEK_AC=1 .." or config option + specification.couplings = {Dso::Coupling::DC, Dso::Coupling::AC}; +#ifdef HANTEK_AC + specification.hasACcoupling = true; +#else + specification.hasACcoupling = false; +#endif + specification.triggerModes = { + Dso::TriggerMode::ROLL, + Dso::TriggerMode::AUTO, + Dso::TriggerMode::NORMAL, + Dso::TriggerMode::SINGLE, + }; + specification.fixedUSBinLength = 512; + + // calibration frequency (requires >FW0206) + specification.calfreqSteps = {50, 60, 100, 200, 500, 1e3, 2e3, 5e3, 10e3, 20e3, 50e3, 100e3}; + specification.hasCalibrationEEPROM = true; +} + +static void applyRequirements_( HantekDsoControl *dsoControl ) { + dsoControl->addCommand( new ControlSetGain_CH1() ); // 0xE0 + dsoControl->addCommand( new ControlSetGain_CH2() ); // 0xE1 + dsoControl->addCommand( new ControlSetSamplerate() ); // 0xE2 + dsoControl->addCommand( new ControlStartSampling() ); // 0xE3 + dsoControl->addCommand( new ControlSetNumChannels() ); // 0xE4 + dsoControl->addCommand( new ControlSetCoupling() ); // 0xE5 (no effect w/o AC/DC HW mod) + dsoControl->addCommand( new ControlSetCalFreq() ); // 0xE6 } -void applyRequirements_(HantekDsoControl *dsoControl) { - dsoControl->addCommand(new ControlAcquireHardData()); - dsoControl->addCommand(new ControlSetTimeDIV()); - dsoControl->addCommand(new ControlSetVoltDIV_CH2()); - dsoControl->addCommand(new ControlSetVoltDIV_CH1()); + +// Hantek DSO-6022BE (this is the base model) +// +// VID/PID active VID/PID no FW FW ver FW name Scope name +// |------------| |------------| |----| |---------| |----------| +ModelDSO6022BE::ModelDSO6022BE() + : DSOModel( ID, 0x04b5, 0x6022, 0x04b4, 0x6022, 0x0207, "dso6022be", "DSO-6022BE", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); } -ModelDSO6022BE::ModelDSO6022BE() : DSOModel(ID, 0x04b5, 0x6022, 0x04b4, 0x6022, "dso6022be", "DSO-6022BE", - Dso::ControlSpecification(2)) { - initSpecifications(specification); +void ModelDSO6022BE::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } + + +// Hantek DSO-6022BL (scope or logic analyzer) +ModelDSO6022BL::ModelDSO6022BL() + : DSOModel( ID, 0x04b5, 0x602a, 0x04b4, 0x602a, 0x0207, "dso6022bl", "DSO-6022BL", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); } -void ModelDSO6022BE::applyRequirements(HantekDsoControl *dsoControl) const { - applyRequirements_(dsoControl); +void ModelDSO6022BL::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } + + +// Voltcraft DSO-2020 USB Oscilloscope (HW is identical to 6022) +// Scope starts up as model DS-2020 (VID/PID = 04b4/2020) but loads 6022BE firmware and looks like a 6022BE +ModelDSO2020::ModelDSO2020() + : DSOModel( ID, 0x04b5, 0x6022, 0x04b4, 0x2020, 0x0207, "dso6022be", "DSO-2020", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); } -ModelDSO6022BL::ModelDSO6022BL() : DSOModel(ID, 0x04b5, 0x602a, 0x04b4, 0x602a, "dso6022bl", "DSO-6022BL", - Dso::ControlSpecification(2)) { - initSpecifications(specification); +void ModelDSO2020::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } + + +// two test cases with simple EZUSB board (LCsoft) without EEPROM or with Saleae VID/PID in EEPROM +// after loading the FW they look like a 6022BE (without useful sample values as Port B and D are left open) +// LCSOFT_TEST_BOARD is #defined/#undefined in modelDSO6022.h + +#ifdef LCSOFT_TEST_BOARD + +static ModelEzUSB modelInstance_EzUSB; +static ModelSaleae modelInstance_Saleae; + + +// LCSOFT without EEPROM reports EzUSB VID/PID +ModelEzUSB::ModelEzUSB() + : DSOModel( ID, 0x04b5, 0x6022, 0x04b4, 0x8613, 0x0207, "dso6022be", "LCsoft-EzUSB", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); + specification.hasCalibrationEEPROM = false; // (big) EEPROM, disabled by address jumper } -void ModelDSO6022BL::applyRequirements(HantekDsoControl *dsoControl) const { - applyRequirements_(dsoControl); +void ModelEzUSB::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } + + +// Saleae VID/PID in EEPROM +ModelSaleae::ModelSaleae() + : DSOModel( ID, 0x04b5, 0x6022, 0x0925, 0x3881, 0x0207, "dso6022be", "LCsoft-Saleae", Dso::ControlSpecification( 2 ) ) { + initSpecifications( specification ); + specification.hasCalibrationEEPROM = false; // we have a big EEPROM } + + +void ModelSaleae::applyRequirements( HantekDsoControl *dsoControl ) const { applyRequirements_( dsoControl ); } + +#endif diff --git a/openhantek/src/hantekdso/models/modelDSO6022.h b/openhantek/src/hantekdso/models/modelDSO6022.h index b83b850d..8afb1a36 100644 --- a/openhantek/src/hantekdso/models/modelDSO6022.h +++ b/openhantek/src/hantekdso/models/modelDSO6022.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "dsomodel.h" @@ -5,14 +7,47 @@ class HantekDsoControl; using namespace Hantek; + struct ModelDSO6022BE : public DSOModel { static const int ID = 0x6022; ModelDSO6022BE(); - virtual void applyRequirements(HantekDsoControl* dsoControl) const override; + void applyRequirements( HantekDsoControl *dsoControl ) const override; }; + struct ModelDSO6022BL : public DSOModel { - static const int ID = 0x6022; + static const int ID = 0x602a; ModelDSO6022BL(); - virtual void applyRequirements(HantekDsoControl* dsoControl) const override; + void applyRequirements( HantekDsoControl *dsoControl ) const override; +}; + + +// Voltcraft DSO-2020 USB Oscilloscope +struct ModelDSO2020 : public DSOModel { + static const int ID = 0x6022; + ModelDSO2020(); + void applyRequirements( HantekDsoControl *dsoControl ) const override; }; + + +// two test cases with simple EZUSB board (LCsoft) without EEPROM or with Saleae VID/PID in EEPROM +// after loading the FW they look like a 6022BE (without useful sample values as Port B and D are left open) +// LCSOFT_TEST_BOARD is also used in modelDSO6022.cpp + +// #define LCSOFT_TEST_BOARD + +#ifdef LCSOFT_TEST_BOARD + +struct ModelEzUSB : public DSOModel { + static const int ID = 0x6022; + ModelEzUSB(); + void applyRequirements( HantekDsoControl *dsoControl ) const override; +}; + +struct ModelSaleae : public DSOModel { + static const int ID = 0x6022; + ModelSaleae(); + void applyRequirements( HantekDsoControl *dsoControl ) const override; +}; + +#endif diff --git a/openhantek/src/hantekdso/models/modelDSO6022.ini b/openhantek/src/hantekdso/models/modelDSO6022.ini new file mode 100644 index 00000000..207297ae --- /dev/null +++ b/openhantek/src/hantekdso/models/modelDSO6022.ini @@ -0,0 +1,50 @@ +; OpenHantek calibration file +; individual calibration values for Hantek6022BE +; copy into ~/.config/OpenHantek +; will be read by .../src/hantekdso/modelDSO6022.cpp + +; multiply channel to get the correct gain +[gain] + +ch0\10mV=1.0 +ch0\20mV=1.0 +ch0\50mV=1.0 +ch0\100mV=1.0 +ch0\200mV=1.0 +ch0\500mV=1.0 +ch0\1000mV=1.0 +ch0\2000mV=1.0 +ch0\5000mV=1.0 + +ch1\10mV=1.0 +ch1\20mV=1.0 +ch1\50mV=1.0 +ch1\100mV=1.0 +ch1\200mV=1.0 +ch1\500mV=1.0 +ch1\1000mV=1.0 +ch1\2000mV=1.0 +ch1\5000mV=1.0 + +; add offset values +[offset] + +ch0\10mV=-6 +ch0\20mV=-6 +ch0\50mV=-6 +ch0\100mV=-6 +ch0\200mV=-6 +ch0\500mV=-7 +ch0\1000mV=-7 +ch0\2000mV=-7 +ch0\5000mV=-7 + +ch1\10mV=+3 +ch1\20mV=+3 +ch1\50mV=+3 +ch1\100mV=+3 +ch1\200mV=+3 +ch1\500mV=-1 +ch1\1000mV=-1 +ch1\2000mV=-1 +ch1\5000mV=-1 diff --git a/openhantek/src/hantekdso/states.h b/openhantek/src/hantekdso/states.h deleted file mode 100644 index a60af63a..00000000 --- a/openhantek/src/hantekdso/states.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -namespace Hantek { - -////////////////////////////////////////////////////////////////////////////// -/// \enum RollState -/// \brief The states of the roll cycle (Since capture state isn't valid). -enum class RollState : int { - STARTSAMPLING = 0, ///< Start sampling - ENABLETRIGGER = 1, ///< Enable triggering - FORCETRIGGER = 2, ///< Force triggering - GETDATA = 3, ///< Request sample data - - _COUNT // Used for mod operator -}; - -////////////////////////////////////////////////////////////////////////////// -/// \enum CaptureState hantek/types.h -/// \brief The different capture states which the oscilloscope returns. -enum CaptureState { - CAPTURE_WAITING = 0, ///< The scope is waiting for a trigger event - CAPTURE_SAMPLING = 1, ///< The scope is sampling data after triggering - CAPTURE_READY = 2, ///< Sampling data is available (DSO-2090/DSO-2150) - CAPTURE_READY2250 = 3, ///< Sampling data is available (DSO-2250) - CAPTURE_READY5200 = 7, ///< Sampling data is available (DSO-5200/DSO-5200A) - CAPTURE_ERROR = 1000 -}; - -} diff --git a/openhantek/src/hantekprotocol/bulkStructs.cpp b/openhantek/src/hantekprotocol/bulkStructs.cpp deleted file mode 100644 index 62603948..00000000 --- a/openhantek/src/hantekprotocol/bulkStructs.cpp +++ /dev/null @@ -1,679 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include "bulkcode.h" -#include "bulkStructs.h" -#include "definitions.h" - -namespace Hantek { - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetFilter -/// \brief Sets the data array to the default values. -BulkSetFilter::BulkSetFilter() : BulkCommand(BulkCode::SETFILTER, 8) { this->init(); } - -/// \brief Sets the FilterByte to the given value. -/// \param channel1 true if channel 1 is filtered. -/// \param channel2 true if channel 2 is filtered. -/// \param trigger true if trigger is filtered. -BulkSetFilter::BulkSetFilter(bool channel1, bool channel2, bool trigger) : BulkCommand(BulkCode::SETFILTER, 8) { - this->init(); - - this->setChannel(0, channel1); - this->setChannel(1, channel2); - this->setTrigger(trigger); -} - -/// \brief Gets the filtering state of one channel. -/// \param channel The channel whose filtering state should be returned. -/// \return The filtering state of the channel. -bool BulkSetFilter::getChannel(ChannelID channel) { - FilterBits *filterBits = (FilterBits *)&(data()[2]); - if (channel == 0) - return filterBits->channel1 == 1; - else - return filterBits->channel2 == 1; -} - -/// \brief Enables/disables filtering of one channel. -/// \param channel The channel that should be set. -/// \param filtered true if the channel should be filtered. -void BulkSetFilter::setChannel(ChannelID channel, bool filtered) { - FilterBits *filterBits = (FilterBits *)&(data()[2]); - if (channel == 0) - filterBits->channel1 = filtered ? 1 : 0; - else - filterBits->channel2 = filtered ? 1 : 0; -} - -/// \brief Gets the filtering state for the trigger. -/// \return The filtering state of the trigger. -bool BulkSetFilter::getTrigger() { return ((FilterBits *)&(data()[2]))->trigger == 1; } - -/// \brief Enables/disables filtering for the trigger. -/// \param filtered true if the trigger should be filtered. -void BulkSetFilter::setTrigger(bool filtered) { - FilterBits *filterBits = (FilterBits *)&(data()[2]); - - filterBits->trigger = filtered ? 1 : 0; -} - -/// \brief Initialize the array to the needed values. -void BulkSetFilter::init() { - data()[0] =(uint8_t) BulkCode::SETFILTER; - data()[1] = 0x0f; -} - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetTriggerAndSamplerate -/// \brief Sets the data array to the default values. -BulkSetTriggerAndSamplerate::BulkSetTriggerAndSamplerate() : BulkCommand(BulkCode::SETTRIGGERANDSAMPLERATE, 12) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param downsampler The Downsampler value. -/// \param triggerPosition The trigger position value. -/// \param triggerSource The trigger source id (Tsr1). -/// \param recordLength The record length id (Tsr1). -/// \param samplerateId The samplerateId value (Tsr1). -/// \param downsamplingMode The downsamplingMode value (Tsr1). -/// \param usedChannels The enabled channels (Tsr2). -/// \param fastRate The fastRate state (Tsr2). -/// \param triggerSlope The triggerSlope value (Tsr2). -BulkSetTriggerAndSamplerate::BulkSetTriggerAndSamplerate(uint16_t downsampler, uint32_t triggerPosition, - uint8_t triggerSource, uint8_t recordLength, - uint8_t samplerateId, bool downsamplingMode, - uint8_t usedChannels, bool fastRate, uint8_t triggerSlope) - : BulkCommand(BulkCode::SETTRIGGERANDSAMPLERATE, 12) { - this->init(); - - this->setTriggerSource(triggerSource); - this->setRecordLength(recordLength); - this->setSamplerateId(samplerateId); - this->setDownsamplingMode(downsamplingMode); - this->setUsedChannels(usedChannels); - this->setFastRate(fastRate); - this->setTriggerSlope(triggerSlope); - this->setDownsampler(downsampler); - this->setTriggerPosition(triggerPosition); -} - -/// \brief Get the triggerSource value in Tsr1Bits. -/// \return The triggerSource value. -uint8_t BulkSetTriggerAndSamplerate::getTriggerSource() { return ((Tsr1Bits *)&(data()[2]))->triggerSource; } - -/// \brief Set the triggerSource in Tsr1Bits to the given value. -/// \param value The new triggerSource value. -void BulkSetTriggerAndSamplerate::setTriggerSource(uint8_t value) { - ((Tsr1Bits *)&(data()[2]))->triggerSource = value; -} - -/// \brief Get the recordLength value in Tsr1Bits. -/// \return The ::RecordLengthId value. -uint8_t BulkSetTriggerAndSamplerate::getRecordLength() { return ((Tsr1Bits *)&(data()[2]))->recordLength; } - -/// \brief Set the recordLength in Tsr1Bits to the given value. -/// \param value The new ::RecordLengthId value. -void BulkSetTriggerAndSamplerate::setRecordLength(uint8_t value) { - ((Tsr1Bits *)&(data()[2]))->recordLength = value; -} - -/// \brief Get the samplerateId value in Tsr1Bits. -/// \return The samplerateId value. -uint8_t BulkSetTriggerAndSamplerate::getSamplerateId() { return ((Tsr1Bits *)&(data()[2]))->samplerateId; } - -/// \brief Set the samplerateId in Tsr1Bits to the given value. -/// \param value The new samplerateId value. -void BulkSetTriggerAndSamplerate::setSamplerateId(uint8_t value) { - ((Tsr1Bits *)&(data()[2]))->samplerateId = value; -} - -/// \brief Get the downsamplerMode value in Tsr1Bits. -/// \return The downsamplerMode value. -bool BulkSetTriggerAndSamplerate::getDownsamplingMode() { - return ((Tsr1Bits *)&(data()[2]))->downsamplingMode == 1; -} - -/// \brief Set the downsamplerMode in Tsr1Bits to the given value. -/// \param downsampling The new downsamplerMode value. -void BulkSetTriggerAndSamplerate::setDownsamplingMode(bool downsampling) { - ((Tsr1Bits *)&(data()[2]))->downsamplingMode = downsampling ? 1 : 0; -} - -/// \brief Get the usedChannels value in Tsr2Bits. -/// \return The usedChannels value. -uint8_t BulkSetTriggerAndSamplerate::getUsedChannels() { return ((Tsr2Bits *)&(data()[3]))->usedChannels; } - -/// \brief Set the usedChannels in Tsr2Bits to the given value. -/// \param value The new usedChannels value. -void BulkSetTriggerAndSamplerate::setUsedChannels(uint8_t value) { - ((Tsr2Bits *)&(data()[3]))->usedChannels = value; -} - -/// \brief Get the fastRate state in Tsr2Bits. -/// \return The fastRate state. -bool BulkSetTriggerAndSamplerate::getFastRate() { return ((Tsr2Bits *)&(data()[3]))->fastRate == 1; } - -/// \brief Set the fastRate in Tsr2Bits to the given state. -/// \param fastRate The new fastRate state. -void BulkSetTriggerAndSamplerate::setFastRate(bool fastRate) { - ((Tsr2Bits *)&(data()[3]))->fastRate = fastRate ? 1 : 0; -} - -/// \brief Get the triggerSlope value in Tsr2Bits. -/// \return The triggerSlope value. -uint8_t BulkSetTriggerAndSamplerate::getTriggerSlope() { return ((Tsr2Bits *)&(data()[3]))->triggerSlope; } - -/// \brief Set the triggerSlope in Tsr2Bits to the given value. -/// \param slope The new triggerSlope value. -void BulkSetTriggerAndSamplerate::setTriggerSlope(uint8_t slope) { - ((Tsr2Bits *)&(data()[3]))->triggerSlope = slope; -} - -/// \brief Get the Downsampler value. -/// \return The Downsampler value. -uint16_t BulkSetTriggerAndSamplerate::getDownsampler() { - return (uint16_t)data()[4] | ((uint16_t)data()[5] << 8); -} - -/// \brief Set the Downsampler to the given value. -/// \param downsampler The new Downsampler value. -void BulkSetTriggerAndSamplerate::setDownsampler(uint16_t downsampler) { - data()[4] = (uint8_t)downsampler; - data()[5] = (uint8_t)(downsampler >> 8); -} - -/// \brief Get the TriggerPosition value. -/// \return The horizontal trigger position. -uint32_t BulkSetTriggerAndSamplerate::getTriggerPosition() { - return (uint32_t)data()[6] | ((uint32_t)data()[7] << 8) | ((uint32_t)data()[10] << 16); -} - -/// \brief Set the TriggerPosition to the given value. -/// \param position The new horizontal trigger position. -void BulkSetTriggerAndSamplerate::setTriggerPosition(uint32_t position) { - data()[6] = (uint8_t)position; - data()[7] = (uint8_t)(position >> 8); - data()[10] = (uint8_t)(position >> 16); -} - -/// \brief Initialize the array to the needed values. -void BulkSetTriggerAndSamplerate::init() { data()[0] = (uint8_t) BulkCode::SETTRIGGERANDSAMPLERATE; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkForceTrigger -/// \brief Sets the data array to needed values. -BulkForceTrigger::BulkForceTrigger() : BulkCommand(BulkCode::FORCETRIGGER, 2) { data()[0] = (uint8_t) BulkCode::FORCETRIGGER; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkCaptureStart -/// \brief Sets the data array to needed values. -BulkCaptureStart::BulkCaptureStart() : BulkCommand(BulkCode::STARTSAMPLING, 2) { data()[0] = (uint8_t) BulkCode::STARTSAMPLING; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkTriggerEnabled -/// \brief Sets the data array to needed values. -BulkTriggerEnabled::BulkTriggerEnabled() : BulkCommand(BulkCode::ENABLETRIGGER, 2) { data()[0] = (uint8_t) BulkCode::ENABLETRIGGER; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkGetData -/// \brief Sets the data array to needed values. -BulkGetData::BulkGetData() : BulkCommand(BulkCode::GETDATA, 2) { data()[0] = (uint8_t) BulkCode::GETDATA; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkGetCaptureState -/// \brief Sets the data array to needed values. -BulkGetCaptureState::BulkGetCaptureState() : BulkCommand(BulkCode::GETCAPTURESTATE, 2) { data()[0] = (uint8_t) BulkCode::GETCAPTURESTATE; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkResponseGetCaptureState -/// \brief Initializes the array. -BulkResponseGetCaptureState::BulkResponseGetCaptureState() : BulkCommand(BulkCode::GETCAPTURESTATE_RESPONSE, 512) {} - -/// \brief Gets the capture state. -/// \return The CaptureState of the oscilloscope. -CaptureState BulkResponseGetCaptureState::getCaptureState() { return (CaptureState)data()[0]; } - -/// \brief Gets the trigger point. -/// \return The trigger point for the captured samples. -unsigned int BulkResponseGetCaptureState::getTriggerPoint() { - return data()[2] | (data()[3] << 8) | (data()[1] << 16); -} - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetGain -/// \brief Sets the data array to needed values. -BulkSetGain::BulkSetGain() : BulkCommand(BulkCode::SETGAIN, 8) { this->init(); } - -/// \brief Sets the gain to the given values. -/// \param channel1 The gain value for channel 1. -/// \param channel2 The gain value for channel 2. -BulkSetGain::BulkSetGain(uint8_t channel1, uint8_t channel2) : BulkCommand(BulkCode::SETGAIN, 8) { - this->init(); - - this->setGain(0, channel1); - this->setGain(1, channel2); -} - -/// \brief Get the gain for the given channel. -/// \param channel The channel whose gain should be returned. -/// \returns The gain value. -uint8_t BulkSetGain::getGain(ChannelID channel) { - GainBits *gainBits = (GainBits *)&(data()[2]); - if (channel == 0) - return gainBits->channel1; - else - return gainBits->channel2; -} - -/// \brief Set the gain for the given channel. -/// \param channel The channel that should be set. -/// \param value The new gain value for the channel. -void BulkSetGain::setGain(ChannelID channel, uint8_t value) { - GainBits *gainBits = (GainBits *)&(data()[2]); - if (channel == 0) - gainBits->channel1 = value; - else - gainBits->channel2 = value; -} - -/// \brief Initialize the array to the needed values. -void BulkSetGain::init() { data()[0] = (uint8_t)BulkCode::SETGAIN; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetLogicalData -/// \brief Sets the data array to needed values. -BulkSetLogicalData::BulkSetLogicalData() : BulkCommand(BulkCode::SETLOGICALDATA, 8) { this->init(); } - -/// \brief Sets the data to the given value. -/// \param data The data byte. -BulkSetLogicalData::BulkSetLogicalData(uint8_t data) : BulkCommand(BulkCode::SETLOGICALDATA, 8) { - this->init(); - - this->setData(data); -} - -/// \brief Gets the data. -/// \returns The data byte. -uint8_t BulkSetLogicalData::getData() { return data()[2]; } - -/// \brief Sets the data to the given value. -/// \param data The new data byte. -void BulkSetLogicalData::setData(uint8_t d) { data()[2] = d; } - -/// \brief Initialize the array to the needed values. -void BulkSetLogicalData::init() { data()[0] = (uint8_t)BulkCode::SETLOGICALDATA; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkGetLogicalData -/// \brief Sets the data array to needed values. -BulkGetLogicalData::BulkGetLogicalData() : BulkCommand(BulkCode::GETLOGICALDATA, 2) { data()[0] = (uint8_t)BulkCode::GETLOGICALDATA; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetFilter2250 -/// \brief Sets the data array to needed values. -BulkSetChannels2250::BulkSetChannels2250() : BulkCommand(BulkCode::BSETCHANNELS, 4) { this->init(); } - -/// \brief Sets the used channels. -/// \param usedChannels The UsedChannels value. -BulkSetChannels2250::BulkSetChannels2250(uint8_t usedChannels) : BulkCommand(BulkCode::BSETCHANNELS, 4) { - this->init(); - - this->setUsedChannels(usedChannels); -} - -/// \brief Get the UsedChannels value -/// \return The UsedChannels value. -uint8_t BulkSetChannels2250::getUsedChannels() { return data()[2]; } - -/// \brief Set the UsedChannels to the given value. -/// \param value The new UsedChannels value. -void BulkSetChannels2250::setUsedChannels(uint8_t value) { data()[2] = value; } - -/// \brief Initialize the array to the needed values. -void BulkSetChannels2250::init() { data()[0] = (uint8_t)BulkCode::BSETCHANNELS; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetTrigger2250 -/// \brief Sets the data array to needed values. -BulkSetTrigger2250::BulkSetTrigger2250() : BulkCommand(BulkCode::CSETTRIGGERORSAMPLERATE, 8) { this->init(); } - -/// \brief Sets the used channels. -/// \param triggerSource The trigger source id (CTriggerBits). -/// \param triggerSlope The triggerSlope value (CTriggerBits). -BulkSetTrigger2250::BulkSetTrigger2250(uint8_t triggerSource, uint8_t triggerSlope) : BulkCommand(BulkCode::CSETTRIGGERORSAMPLERATE, 8) { - this->init(); - - this->setTriggerSource(triggerSource); - this->setTriggerSlope(triggerSlope); -} - -/// \brief Get the triggerSource value in CTriggerBits. -/// \return The triggerSource value. -uint8_t BulkSetTrigger2250::getTriggerSource() { return ((CTriggerBits *)&(data()[2]))->triggerSource; } - -/// \brief Set the triggerSource in CTriggerBits to the given value. -/// \param value The new triggerSource value. -void BulkSetTrigger2250::setTriggerSource(uint8_t value) { ((CTriggerBits *)&(data()[2]))->triggerSource = value; } - -/// \brief Get the triggerSlope value in CTriggerBits. -/// \return The triggerSlope value. -uint8_t BulkSetTrigger2250::getTriggerSlope() { return ((CTriggerBits *)&(data()[2]))->triggerSlope; } - -/// \brief Set the triggerSlope in CTriggerBits to the given value. -/// \param slope The new triggerSlope value. -void BulkSetTrigger2250::setTriggerSlope(uint8_t slope) { ((CTriggerBits *)&(data()[2]))->triggerSlope = slope; } - -/// \brief Initialize the array to the needed values. -void BulkSetTrigger2250::init() { data()[0] = (uint8_t)BulkCode::CSETTRIGGERORSAMPLERATE; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetSamplerate5200 -/// \brief Sets the data array to the default values. -BulkSetSamplerate5200::BulkSetSamplerate5200() : BulkCommand(BulkCode::CSETTRIGGERORSAMPLERATE, 6) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param samplerateSlow The SamplerateSlow value. -/// \param samplerateFast The SamplerateFast value. -BulkSetSamplerate5200::BulkSetSamplerate5200(uint16_t samplerateSlow, uint8_t samplerateFast) : BulkCommand(BulkCode::CSETTRIGGERORSAMPLERATE, 6) { - this->init(); - - this->setSamplerateFast(samplerateFast); - this->setSamplerateSlow(samplerateSlow); -} - -/// \brief Get the SamplerateFast value. -/// \return The SamplerateFast value. -uint8_t BulkSetSamplerate5200::getSamplerateFast() { return data()[4]; } - -/// \brief Set the SamplerateFast to the given value. -/// \param value The new SamplerateFast value. -void BulkSetSamplerate5200::setSamplerateFast(uint8_t value) { data()[4] = value; } - -/// \brief Get the SamplerateSlow value. -/// \return The SamplerateSlow value. -uint16_t BulkSetSamplerate5200::getSamplerateSlow() { - return (uint16_t)data()[2] | ((uint16_t)data()[3] << 8); -} - -/// \brief Set the SamplerateSlow to the given value. -/// \param samplerate The new SamplerateSlow value. -void BulkSetSamplerate5200::setSamplerateSlow(uint16_t samplerate) { - data()[2] = (uint8_t)samplerate; - data()[3] = (uint8_t)(samplerate >> 8); -} - -/// \brief Initialize the array to the needed values. -void BulkSetSamplerate5200::init() { data()[0] = (uint8_t)BulkCode::CSETTRIGGERORSAMPLERATE; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetBuffer2250 -/// \brief Sets the data array to the default values. -BulkSetRecordLength2250::BulkSetRecordLength2250() : BulkCommand(BulkCode::DSETBUFFER, 4) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param recordLength The ::RecordLengthId value. -BulkSetRecordLength2250::BulkSetRecordLength2250(uint8_t recordLength) : BulkCommand(BulkCode::DSETBUFFER, 4) { - this->init(); - - this->setRecordLength(recordLength); -} - -/// \brief Get the ::RecordLengthId value. -/// \return The ::RecordLengthId value. -uint8_t BulkSetRecordLength2250::getRecordLength() { return data()[2]; } - -/// \brief Set the ::RecordLengthId to the given value. -/// \param value The new ::RecordLengthId value. -void BulkSetRecordLength2250::setRecordLength(uint8_t value) { data()[2] = value; } - -/// \brief Initialize the array to the needed values. -void BulkSetRecordLength2250::init() { data()[0] = (uint8_t)BulkCode::DSETBUFFER; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetBuffer5200 -/// \brief Sets the data array to the default values. -BulkSetBuffer5200::BulkSetBuffer5200() : BulkCommand(BulkCode::DSETBUFFER, 10) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param triggerPositionPre The TriggerPositionPre value. -/// \param triggerPositionPost The TriggerPositionPost value. -/// \param usedPre The TriggerPositionUsedPre value. -/// \param usedPost The TriggerPositionUsedPost value. -/// \param recordLength The ::RecordLengthId value. -BulkSetBuffer5200::BulkSetBuffer5200(uint16_t triggerPositionPre, uint16_t triggerPositionPost, DTriggerPositionUsed usedPre, - DTriggerPositionUsed usedPost, uint8_t recordLength) - : BulkCommand(BulkCode::DSETBUFFER, 10) { - this->init(); - - this->setTriggerPositionPre(triggerPositionPre); - this->setTriggerPositionPost(triggerPositionPost); - this->setUsedPre(usedPre); - this->setUsedPost(usedPost); - this->setRecordLength(recordLength); -} - -/// \brief Get the TriggerPositionPre value. -/// \return The TriggerPositionPre value. -uint16_t BulkSetBuffer5200::getTriggerPositionPre() { - return (uint16_t)data()[2] | ((uint16_t)data()[3] << 8); -} - -/// \brief Set the TriggerPositionPre to the given value. -/// \param position The new TriggerPositionPre value. -void BulkSetBuffer5200::setTriggerPositionPre(uint16_t position) { - data()[2] = (uint8_t)position; - data()[3] = (uint8_t)(position >> 8); -} - -/// \brief Get the TriggerPositionPost value. -/// \return The TriggerPositionPost value. -uint16_t BulkSetBuffer5200::getTriggerPositionPost() { - return (uint16_t)data()[6] | ((uint16_t)data()[7] << 8); -} - -/// \brief Set the TriggerPositionPost to the given value. -/// \param position The new TriggerPositionPost value. -void BulkSetBuffer5200::setTriggerPositionPost(uint16_t position) { - data()[6] = (uint8_t)position; - data()[7] = (uint8_t)(position >> 8); -} - -/// \brief Get the TriggerPositionUsedPre value. -/// \return The ::DTriggerPositionUsed value for the pre position. -uint8_t BulkSetBuffer5200::getUsedPre() { return data()[4]; } - -/// \brief Set the TriggerPositionUsedPre to the given value. -/// \param value The new ::DTriggerPositionUsed value for the pre position. -void BulkSetBuffer5200::setUsedPre(DTriggerPositionUsed value) { data()[4] = (uint8_t) value; } - -/// \brief Get the TriggerPositionUsedPost value. -/// \return The ::DTriggerPositionUsed value for the post position. -DTriggerPositionUsed BulkSetBuffer5200::getUsedPost() { return (DTriggerPositionUsed) ((DBufferBits *)&(data()[8]))->triggerPositionUsed; } - -/// \brief Set the TriggerPositionUsedPost to the given value. -/// \param value The new ::DTriggerPositionUsed value for the post position. -void BulkSetBuffer5200::setUsedPost(DTriggerPositionUsed value) { ((DBufferBits *)&(data()[8]))->triggerPositionUsed = (uint8_t) value; } - -/// \brief Get the recordLength value in DBufferBits. -/// \return The ::RecordLengthId value. -uint8_t BulkSetBuffer5200::getRecordLength() { return ((DBufferBits *)&(data()[8]))->recordLength; } - -/// \brief Set the recordLength in DBufferBits to the given value. -/// \param value The new ::RecordLengthId value. -void BulkSetBuffer5200::setRecordLength(uint8_t value) { ((DBufferBits *)&(data()[8]))->recordLength = value; } - -/// \brief Initialize the array to the needed values. -void BulkSetBuffer5200::init() { - data()[0] = (uint8_t)BulkCode::DSETBUFFER; - data()[5] = 0xff; - data()[9] = 0xff; -} - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetSamplerate2250 -/// \brief Sets the data array to the default values. -BulkSetSamplerate2250::BulkSetSamplerate2250() : BulkCommand(BulkCode::ESETTRIGGERORSAMPLERATE, 8) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param fastRate The fastRate state (ESamplerateBits). -/// \param downsampling The downsampling state (ESamplerateBits). -/// \param samplerate The Samplerate value. -BulkSetSamplerate2250::BulkSetSamplerate2250(bool fastRate, bool downsampling, uint16_t samplerate) - : BulkCommand(BulkCode::ESETTRIGGERORSAMPLERATE, 8) { - this->init(); - - this->setFastRate(fastRate); - this->setDownsampling(downsampling); - this->setSamplerate(samplerate); -} - -/// \brief Get the fastRate state in ESamplerateBits. -/// \return The fastRate state. -bool BulkSetSamplerate2250::getFastRate() { return ((ESamplerateBits *)&(data()[2]))->fastRate == 1; } - -/// \brief Set the fastRate in ESamplerateBits to the given state. -/// \param fastRate The new fastRate state. -void BulkSetSamplerate2250::setFastRate(bool fastRate) { - ((ESamplerateBits *)&(data()[2]))->fastRate = fastRate ? 1 : 0; -} - -/// \brief Get the downsampling state in ESamplerateBits. -/// \return The downsampling state. -bool BulkSetSamplerate2250::getDownsampling() { return ((ESamplerateBits *)&(data()[2]))->downsampling == 1; } - -/// \brief Set the downsampling in ESamplerateBits to the given state. -/// \param downsampling The new downsampling state. -void BulkSetSamplerate2250::setDownsampling(bool downsampling) { - ((ESamplerateBits *)&(data()[2]))->downsampling = downsampling ? 1 : 0; -} - -/// \brief Get the Samplerate value. -/// \return The Samplerate value. -uint16_t BulkSetSamplerate2250::getSamplerate() { return (uint16_t)data()[4] | ((uint16_t)data()[5] << 8); } - -/// \brief Set the Samplerate to the given value. -/// \param samplerate The new Samplerate value. -void BulkSetSamplerate2250::setSamplerate(uint16_t samplerate) { - data()[4] = (uint8_t)samplerate; - data()[5] = (uint8_t)(samplerate >> 8); -} - -/// \brief Initialize the array to the needed values. -void BulkSetSamplerate2250::init() { data()[0] = (uint8_t)BulkCode::ESETTRIGGERORSAMPLERATE; } - -////////////////////////////////////////////////////////////////////////////// -// class BulkSetTrigger5200 -/// \brief Sets the data array to the default values. -BulkSetTrigger5200::BulkSetTrigger5200() : BulkCommand(BulkCode::ESETTRIGGERORSAMPLERATE, 8) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param triggerSource The trigger source id. -/// \param usedChannels The enabled channels. -/// \param fastRate The fastRate state. -/// \param triggerSlope The triggerSlope value. -/// \param triggerPulse The triggerPulse value. -BulkSetTrigger5200::BulkSetTrigger5200(uint8_t triggerSource, uint8_t usedChannels, bool fastRate, uint8_t triggerSlope, - uint8_t triggerPulse) - : BulkCommand(BulkCode::ESETTRIGGERORSAMPLERATE, 8) { - this->init(); - - this->setTriggerSource(triggerSource); - this->setUsedChannels(usedChannels); - this->setFastRate(fastRate); - this->setTriggerSlope(triggerSlope); - this->setTriggerPulse(triggerPulse); -} - -/// \brief Get the triggerSource value in ETsrBits. -/// \return The ::TriggerSource value. -uint8_t BulkSetTrigger5200::getTriggerSource() { return ((ETsrBits *)&(data()[2]))->triggerSource; } - -/// \brief Set the triggerSource in ETsrBits to the given value. -/// \param value The new ::TriggerSource value. -void BulkSetTrigger5200::setTriggerSource(uint8_t value) { ((ETsrBits *)&(data()[2]))->triggerSource = value; } - -/// \brief Get the usedChannels value in ETsrBits. -/// \return The ::UsedChannels value. -uint8_t BulkSetTrigger5200::getUsedChannels() { return ((ETsrBits *)&(data()[2]))->usedChannels; } - -/// \brief Set the usedChannels in ETsrBits to the given value. -/// \param value The new ::UsedChannels value. -void BulkSetTrigger5200::setUsedChannels(uint8_t value) { ((ETsrBits *)&(data()[2]))->usedChannels = value; } - -/// \brief Get the fastRate state in ETsrBits. -/// \return The fastRate state (Already inverted). -bool BulkSetTrigger5200::getFastRate() { return ((ETsrBits *)&(data()[2]))->fastRate == 0; } - -/// \brief Set the fastRate in ETsrBits to the given state. -/// \param fastRate The new fastRate state (Automatically inverted). -void BulkSetTrigger5200::setFastRate(bool fastRate) { ((ETsrBits *)&(data()[2]))->fastRate = fastRate ? 0 : 1; } - -/// \brief Get the triggerSlope value in ETsrBits. -/// \return The triggerSlope value. -uint8_t BulkSetTrigger5200::getTriggerSlope() { return ((ETsrBits *)&(data()[2]))->triggerSlope; } - -/// \brief Set the triggerSlope in ETsrBits to the given value. -/// \param slope The new triggerSlope value. -void BulkSetTrigger5200::setTriggerSlope(uint8_t slope) { ((ETsrBits *)&(data()[2]))->triggerSlope = slope; } - -/// \brief Get the triggerPulse state in ETsrBits. -/// \return The triggerPulse state. -bool BulkSetTrigger5200::getTriggerPulse() { return ((ETsrBits *)&(data()[2]))->triggerPulse == 1; } - -/// \brief Set the triggerPulse in ETsrBits to the given state. -/// \param pulse The new triggerPulse state. -void BulkSetTrigger5200::setTriggerPulse(bool pulse) { ((ETsrBits *)&(data()[2]))->triggerPulse = pulse ? 1 : 0; } - -/// \brief Initialize the array to the needed values. -void BulkSetTrigger5200::init() { - data()[0] = (uint8_t)BulkCode::ESETTRIGGERORSAMPLERATE; - data()[4] = 0x02; -} - -////////////////////////////////////////////////////////////////////////////// -/// \class BulkSetBuffer2250 hantek/types.h -/// \brief The DSO-2250 BulkCode::FSETBUFFER builder. -/// \brief Sets the data array to the default values. -BulkSetBuffer2250::BulkSetBuffer2250() : BulkCommand(BulkCode::FSETBUFFER, 10) { this->init(); } - -/// \brief Sets the data bytes to the specified values. -/// \param triggerPositionPre The TriggerPositionPre value. -/// \param triggerPositionPost The TriggerPositionPost value. -BulkSetBuffer2250::BulkSetBuffer2250(uint32_t triggerPositionPre, uint32_t triggerPositionPost) - : BulkCommand(BulkCode::FSETBUFFER, 12) { - this->init(); - - this->setTriggerPositionPre(triggerPositionPre); - this->setTriggerPositionPost(triggerPositionPost); -} - -/// \brief Get the TriggerPositionPost value. -/// \return The TriggerPositionPost value. -uint32_t BulkSetBuffer2250::getTriggerPositionPost() { - return (uint32_t)data()[2] | ((uint32_t)data()[3] << 8) | ((uint32_t)data()[4] << 16); -} - -/// \brief Set the TriggerPositionPost to the given value. -/// \param position The new TriggerPositionPost value. -void BulkSetBuffer2250::setTriggerPositionPost(uint32_t position) { - data()[2] = (uint8_t)position; - data()[3] = (uint8_t)(position >> 8); - data()[4] = (uint8_t)(position >> 16); -} - -/// \brief Get the TriggerPositionPre value. -/// \return The TriggerPositionPre value. -uint32_t BulkSetBuffer2250::getTriggerPositionPre() { - return (uint32_t)data()[6] | ((uint16_t)data()[7] << 8) | ((uint16_t)data()[8] << 16); -} - -/// \brief Set the TriggerPositionPre to the given value. -/// \param position The new TriggerPositionPre value. -void BulkSetBuffer2250::setTriggerPositionPre(uint32_t position) { - data()[6] = (uint8_t)position; - data()[7] = (uint8_t)(position >> 8); - data()[8] = (uint8_t)(position >> 16); -} - -/// \brief Initialize the array to the needed values. -void BulkSetBuffer2250::init() { data()[0] = (uint8_t)BulkCode::FSETBUFFER; } -} diff --git a/openhantek/src/hantekprotocol/bulkStructs.h b/openhantek/src/hantekprotocol/bulkStructs.h deleted file mode 100644 index 5db367f0..00000000 --- a/openhantek/src/hantekprotocol/bulkStructs.h +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include - -#include - -#include "types.h" -#include "definitions.h" -#include "states.h" -#include "bulkcommand.h" - -namespace Hantek { - -/// \brief The BULK::SETFILTER builder. -class BulkSetFilter : public BulkCommand { - public: - BulkSetFilter(); - BulkSetFilter(bool channel1, bool channel2, bool trigger); - - bool getChannel(ChannelID channel); - void setChannel(ChannelID channel, bool filtered); - bool getTrigger(); - void setTrigger(bool filtered); - - private: - void init(); -}; - -/// \brief The BulkCode::SETTRIGGERANDSAMPLERATE builder. -class BulkSetTriggerAndSamplerate : public BulkCommand { - public: - BulkSetTriggerAndSamplerate(); - BulkSetTriggerAndSamplerate(uint16_t downsampler, uint32_t triggerPosition, uint8_t triggerSource = 0, - uint8_t recordLength = 0, uint8_t samplerateId = 0, bool downsamplingMode = true, - uint8_t usedChannels = 0, bool fastRate = false, uint8_t triggerSlope = 0); - - uint8_t getTriggerSource(); - void setTriggerSource(uint8_t value); - uint8_t getRecordLength(); - void setRecordLength(uint8_t value); - uint8_t getSamplerateId(); - void setSamplerateId(uint8_t value); - bool getDownsamplingMode(); - void setDownsamplingMode(bool downsampling); - uint8_t getUsedChannels(); - void setUsedChannels(uint8_t value); - bool getFastRate(); - void setFastRate(bool fastRate); - uint8_t getTriggerSlope(); - void setTriggerSlope(uint8_t slope); - uint16_t getDownsampler(); - void setDownsampler(uint16_t downsampler); - uint32_t getTriggerPosition(); - void setTriggerPosition(uint32_t position); - - private: - void init(); -}; - -/// \brief The BulkCode::FORCETRIGGER builder. -class BulkForceTrigger : public BulkCommand { - public: - BulkForceTrigger(); -}; - -/// \brief The BULK_CAPTURESTART builder. -class BulkCaptureStart : public BulkCommand { - public: - BulkCaptureStart(); -}; - -/// \brief The BULK_TRIGGERENABLED builder. -class BulkTriggerEnabled : public BulkCommand { - public: - BulkTriggerEnabled(); -}; - -/// \brief The BulkCode::GETDATA builder. -class BulkGetData : public BulkCommand { - public: - BulkGetData(); -}; - -/// \brief The BulkCode::GETCAPTURESTATE builder. -class BulkGetCaptureState : public BulkCommand { - public: - BulkGetCaptureState(); -}; - -/// \brief The parser for the BulkCode::GETCAPTURESTATE response. -class BulkResponseGetCaptureState : public BulkCommand { - public: - BulkResponseGetCaptureState(); - - CaptureState getCaptureState(); - unsigned int getTriggerPoint(); -}; - -/// \brief The BulkCode::SETGAIN builder. -class BulkSetGain : public BulkCommand { - public: - BulkSetGain(); - BulkSetGain(uint8_t channel1, uint8_t channel2); - - uint8_t getGain(ChannelID channel); - void setGain(ChannelID channel, uint8_t value); - - private: - void init(); -}; - -/// \brief The BulkCode::SETLOGICALDATA builder. -class BulkSetLogicalData : public BulkCommand { - public: - BulkSetLogicalData(); - BulkSetLogicalData(uint8_t data); - - uint8_t getData(); - void setData(uint8_t data); - - private: - void init(); -}; - -/// \brief The BulkCode::GETLOGICALDATA builder. -class BulkGetLogicalData : public BulkCommand { - public: - BulkGetLogicalData(); -}; - -/// \brief The DSO-2250 BULK_BSETFILTER builder. -class BulkSetChannels2250 : public BulkCommand { - public: - BulkSetChannels2250(); - BulkSetChannels2250(uint8_t usedChannels); - - uint8_t getUsedChannels(); - void setUsedChannels(uint8_t value); - - private: - void init(); -}; - -/// \brief The DSO-2250 BulkCode::CSETTRIGGERORSAMPLERATE builder. -class BulkSetTrigger2250 : public BulkCommand { - public: - BulkSetTrigger2250(); - BulkSetTrigger2250(uint8_t triggerSource, uint8_t triggerSlope); - - uint8_t getTriggerSource(); - void setTriggerSource(uint8_t value); - uint8_t getTriggerSlope(); - void setTriggerSlope(uint8_t slope); - - private: - void init(); -}; - -/// \brief The DSO-5200/DSO-5200A BulkCode::CSETTRIGGERORSAMPLERATE builder. -class BulkSetSamplerate5200 : public BulkCommand { - public: - BulkSetSamplerate5200(); - BulkSetSamplerate5200(uint16_t samplerateSlow, uint8_t samplerateFast); - - uint8_t getSamplerateFast(); - void setSamplerateFast(uint8_t value); - uint16_t getSamplerateSlow(); - void setSamplerateSlow(uint16_t samplerate); - - private: - void init(); -}; - -/// \brief The DSO-2250 BulkCode::DSETBUFFER builder. -class BulkSetRecordLength2250 : public BulkCommand { - public: - BulkSetRecordLength2250(); - BulkSetRecordLength2250(uint8_t recordLength); - - uint8_t getRecordLength(); - void setRecordLength(uint8_t value); - - private: - void init(); -}; - -/// \brief The DSO-5200/DSO-5200A BulkCode::DSETBUFFER builder. -class BulkSetBuffer5200 : public BulkCommand { - public: - BulkSetBuffer5200(); - BulkSetBuffer5200(uint16_t triggerPositionPre, uint16_t triggerPositionPost, DTriggerPositionUsed usedPre = DTriggerPositionUsed::OFF, - DTriggerPositionUsed usedPost = DTriggerPositionUsed::OFF, uint8_t recordLength = 0); - - uint16_t getTriggerPositionPre(); - void setTriggerPositionPre(uint16_t value); - uint16_t getTriggerPositionPost(); - void setTriggerPositionPost(uint16_t value); - uint8_t getUsedPre(); - void setUsedPre(DTriggerPositionUsed value); - DTriggerPositionUsed getUsedPost(); - void setUsedPost(DTriggerPositionUsed value); - uint8_t getRecordLength(); - void setRecordLength(uint8_t value); - - private: - void init(); -}; - -/// \brief The DSO-2250 BulkCode::ESETTRIGGERORSAMPLERATE builder. -class BulkSetSamplerate2250 : public BulkCommand { - public: - BulkSetSamplerate2250(); - BulkSetSamplerate2250(bool fastRate, bool downsampling = false, uint16_t samplerate = 0); - - bool getFastRate(); - void setFastRate(bool fastRate); - bool getDownsampling(); - void setDownsampling(bool downsampling); - uint16_t getSamplerate(); - void setSamplerate(uint16_t samplerate); - - private: - void init(); -}; - -/// \brief The DSO-5200/DSO-5200A BulkCode::ESETTRIGGERORSAMPLERATE builder. -class BulkSetTrigger5200 : public BulkCommand { - public: - BulkSetTrigger5200(); - BulkSetTrigger5200(uint8_t triggerSource, uint8_t usedChannels, bool fastRate = false, uint8_t triggerSlope = 0, - uint8_t triggerPulse = 0); - - uint8_t getTriggerSource(); - void setTriggerSource(uint8_t value); - uint8_t getUsedChannels(); - void setUsedChannels(uint8_t value); - bool getFastRate(); - void setFastRate(bool fastRate); - uint8_t getTriggerSlope(); - void setTriggerSlope(uint8_t slope); - bool getTriggerPulse(); - void setTriggerPulse(bool pulse); - - private: - void init(); -}; - -/// \brief The DSO-2250 BulkCode::FSETBUFFER builder. -class BulkSetBuffer2250 : public BulkCommand { - public: - BulkSetBuffer2250(); - BulkSetBuffer2250(uint32_t triggerPositionPre, uint32_t triggerPositionPost); - - uint32_t getTriggerPositionPost(); - void setTriggerPositionPost(uint32_t value); - uint32_t getTriggerPositionPre(); - void setTriggerPositionPre(uint32_t value); - - private: - void init(); -}; -} diff --git a/openhantek/src/hantekprotocol/bulkcode.h b/openhantek/src/hantekprotocol/bulkcode.h deleted file mode 100644 index 8224fba0..00000000 --- a/openhantek/src/hantekprotocol/bulkcode.h +++ /dev/null @@ -1,465 +0,0 @@ -#pragma once - -#include - -namespace Hantek { - -/// \brief All supported bulk commands. -/// Indicies given in square brackets specify byte numbers in little endian -/// format. -/// SETFILTER [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// This command sets channel and trigger filter: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x000x00FilterBits0x000x000x000x000x00
-///

-/// -/// This command is used by the official %Hantek software, but doesn't seem -/// to be used by the device. -/// -/// -/// SETTRIGGERANDSAMPLERATE [::MODEL_DSO2090, ::MODEL_DSO2150] -///

-/// This command sets trigger and timebase: -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x010x00Tsr1BitsTsr2BitsDownsampler[0]Downsampler[1]
-/// -/// -/// -/// -/// -/// -/// -/// -/// -///
TriggerPosition[0]TriggerPosition[1]0x000x00TriggerPosition[2]0x00
-///

-///

-/// The samplerate is set relative to the base samplerate by a divider or to -/// a maximum samplerate.
-/// This divider is set by Tsr1Bits.samplerateId for values up to 5 with to -/// the following values: -/// -/// -/// -/// -/// -/// -/// -///
Tsr1Bits.samplerateId0123
SamplerateMaxBaseBase / -/// 2Base / 5
-/// For higher divider values, the value can be set using the 16-bit value -/// in the two Downsampler bytes. The value of Downsampler is given by:
-/// Downsampler = 1comp((Base / Samplerate / 2) - 2)
-/// The Base samplerate is 50 MS/s for the DSO-2090 and DSO-2150. The Max -/// samplerate is also 50 MS/s for the DSO-2090 and 75 MS/s for the -/// DSO-2150.
-/// When using fast rate mode the Base and Max samplerate is twice as fast. -/// When Tsr1Bits.recordLength is 0 (Roll mode) the sampling rate is divided -/// by 1000. -///

-///

-/// The TriggerPosition sets the position of the pretrigger in samples. The -/// left side (0 %) is 0x77660 when using the small buffer and 0x78000 when -/// using the large buffer. -///

-/// -/// FORCETRIGGER [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// This command forces triggering: -/// -/// -/// -/// -/// -///
0x020x00
-///

-/// -/// -/// STARTSAMPLING [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// This command starts to capture data: -/// -/// -/// -/// -/// -///
0x030x00
-///

-/// -/// -/// ENABLETRIGGER [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// This command sets the trigger: -/// -/// -/// -/// -/// -///
0x040x00
-///

-/// -/// -/// GETDATA [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// This command reads data from the hardware: -/// -/// -/// -/// -/// -///
0x050x00
-///

-///

-/// The oscilloscope returns the sample data, that will be split if it's -/// larger than the IN endpoint packet length: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
Sample[0]...Sample[511]
Sample[512]...Sample[1023]
Sample[1024]...
-/// Because of the 10 bit data model, the DSO-5200 transmits the two extra -/// bits for each sample afterwards: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
Extra[0] << 2 | Extra[1]0Extra[2] << 2 | Extra[3]0...Extra[510] << 2 | Extra[511]0
Extra[512] << 2 | Extra[513]...
-///

-/// -/// GETCAPTURESTATE [::MODEL_DSO2090, ::MODEL_DSO2150, ::MODEL_DSO2250, ::MODEL_DSO5200, ::MODEL_DSO5200A] -///

-/// This command checks the capture state: -/// -/// -/// -/// -/// -///
0x060x00
-///

-///

-/// The oscilloscope returns it's capture state and the trigger point. Not -/// sure about this, looks like 248 16-bit words with nearly constant -/// values. These can be converted to the start address of the data in the -/// buffer (See Hantek::Control::calculateTriggerPoint): -/// -/// -/// -/// -/// -/// -/// -/// -///
::CaptureState0x00TriggerPoint[0]TriggerPoint[1]...
-///

-/// -/// SETGAIN [::MODEL_DSO2090, ::MODEL_DSO2150, ::MODEL_DSO2250, ::MODEL_DSO5200, ::MODEL_DSO5200A] -///

-/// This command sets the gain: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x070x00GainBits0x000x000x000x000x00
-/// It is usually used in combination with ::CONTROL_SETRELAYS. -///

-/// -/// SETLOGICALDATA [] -///

-/// This command sets the logical data (Not used in official %Hantek software): -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x080x00Data | 0x010x000x000x000x000x00
-///

-/// -/// GETLOGICALDATA [] -///

-/// This command reads the logical data (Not used in official %Hantek -/// software): -/// -/// -/// -/// -/// -///
0x090x00
-///

-///

-/// The oscilloscope returns the logical data, which contains valid data in -/// the first byte although it is 64 or 512 bytes long: -/// -/// -/// -/// -/// -///
Data...
-///

-/// -/// BSETCHANNELS BulkSetChannels2250 [::MODEL_DSO2250] -///

-/// This command sets the activated channels for the DSO-2250: -/// -/// -/// -/// -/// -/// -/// -///
0x0b0x00BUsedChannels0x00
-///

-/// -/// CSETTRIGGERORSAMPLERATE BulkSetTrigger2250 [::MODEL_DSO2250] -///

-/// This command sets the trigger source for the DSO-2250: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0c0x00CTriggerBits0x000x000x000x000x00
-///

-/// -/// BulkSetSamplerate5200 [::MODEL_DSO5200, ::MODEL_DSO5200A] -///

-/// This command sets the sampling rate for the DSO-5200: -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0c0x00SamplerateSlow[0]SamplerateSlow[1]SamplerateFast0x00
-///

-///

-/// The samplerate is set relative to the maximum sample rate by a divider -/// that is set in SamplerateFast and the 16-bit value in the two -/// SamplerateSlow bytes.
-/// Without using fast rate mode, the samplerate is:
-/// Samplerate = SamplerateMax / (2comp(SamplerateSlow) * 2 + 4 - -/// SamplerateFast)
-/// SamplerateBase is 100 MS/s for the DSO-5200 in normal mode and 200 MS/s -/// in fast rate mode, the modifications regarding record length are the the -/// same that apply for the DSO-2090. The maximum samplerate is 125 MS/s in -/// normal mode and 250 MS/s in fast rate mode, and is reached by setting -/// SamplerateSlow = 0 and SamplerateFast = 4. -///

-/// -/// DSETBUFFER BulkSetRecordLength2250 [::MODEL_DSO2250] -///

-/// This command sets the record length for the DSO-2250: -/// -/// -/// -/// -/// -/// -/// -///
0x0d0x00::RecordLengthId0x00
-///

-/// -/// BulkSetBuffer5200 [::MODEL_DSO5200, ::MODEL_DSO5200A] -///

-/// This command sets the trigger position and record length for the -/// DSO-5200: -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0d0x00TriggerPositionPre[0]TriggerPositionPre[1]::DTriggerPositionUsed
-/// -/// -/// -/// -/// -/// -/// -/// -///
0xffTriggerPositionPost[0]TriggerPositionPost[1]DBufferBits0xff
-///

-///

-/// The TriggerPositionPre and TriggerPositionPost values set the pretrigger -/// position. Both values have a range from 0xd7ff (0xc7ff for 14 kiS -/// buffer) to 0xfffe. On the left side (0 %) the TriggerPositionPre value -/// is minimal, on the right side (100 %) it is maximal. The -/// TriggerPositionPost value is maximal for 0 % and minimal for 100%. -///

-/// -/// ESETTRIGGERORSAMPLERATE BulkSetSamplerate2250 [::MODEL_DSO2250] -///

-/// This command sets the samplerate: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0e0x00ESamplerateBits0x00Samplerate[0]Samplerate[1]0x000x00
-///

-///

-/// The downsampler can be activated by setting ESamplerateBits.downsampling -/// = 1. If this is the case, the value of Downsampler is given by:
-/// Downsampler = 1comp((Base / Samplerate) - 2)
-/// Base is 100 MS/s for the DSO-2250 in standard mode and 200 MS/s in fast -/// rate mode, the modifications regarding record length are the the same -/// that apply for the DSO-2090. The maximum samplerate is 125 MS/s in -/// standard mode and 250 MS/s in fast rate mode and is achieved by setting -/// ESamplerateBits.downsampling = 0. -///

-/// -/// ESETTRIGGERORSAMPLERATE BulkSetTrigger5200 [::MODEL_DSO5200, ::MODEL_DSO5200A] -///

-/// This command sets the channel and trigger settings: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0e0x00ETsrBits0x000x000x000x000x00
-///

-/// -/// FSETBUFFER BulkSetBuffer2250 [::MODEL_DSO2250] -///

-/// This command sets the trigger position and buffer configuration for the -/// DSO-2250: -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0f0x00TriggerPositionPost[0]TriggerPositionPost[1]TriggerPositionPost[2]0x00
-/// -/// -/// -/// -/// -/// -/// -/// -/// -///
TriggerPositionPre[0]TriggerPositionPre[1]TriggerPositionPre[2]0x000x000x00
-///

-///

-/// The TriggerPositionPre and TriggerPositionPost values set the pretrigger -/// position. Both values have a range from 0x7d800 (0x00000 for 512 kiS -/// buffer) to 0x7ffff. On the left side (0 %) the TriggerPositionPre value -/// is minimal, on the right side (100 %) it is maximal. The -/// TriggerPositionPost value is maximal for 0 % and minimal for 100%. -///

-/// -/// AUNKNOWN [] -///

-/// This command isn't used for any supported model: -/// -/// -/// -/// -/// -///
0x0a...
-///

-///


-enum class BulkCode : uint8_t { - SETFILTER = 0x00, - SETTRIGGERANDSAMPLERATE = 0x01, - FORCETRIGGER = 0x02, - STARTSAMPLING = 0x03, - ENABLETRIGGER = 0x04, - GETDATA = 0x05, - GETCAPTURESTATE = 0x06, - SETGAIN = 0x07, - SETLOGICALDATA = 0x08, - GETLOGICALDATA = 0x09, - AUNKNOWN = 0x0a, - BSETCHANNELS = 0x0b, - CSETTRIGGERORSAMPLERATE = 0x0c, - DSETBUFFER = 0x0d, - ESETTRIGGERORSAMPLERATE = 0x0e, - FSETBUFFER = 0x0f, - - GETCAPTURESTATE_RESPONSE=0xfe, - INVALID=0xff -}; - -} diff --git a/openhantek/src/hantekprotocol/bulkcommand.cpp b/openhantek/src/hantekprotocol/bulkcommand.cpp deleted file mode 100644 index dcb44f06..00000000 --- a/openhantek/src/hantekprotocol/bulkcommand.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "bulkcommand.h" - -BulkCommand::BulkCommand(Hantek::BulkCode code, unsigned size): std::vector(size), code(code) {} diff --git a/openhantek/src/hantekprotocol/bulkcommand.h b/openhantek/src/hantekprotocol/bulkcommand.h deleted file mode 100644 index 27155f1c..00000000 --- a/openhantek/src/hantekprotocol/bulkcommand.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include - -namespace Hantek { -enum class BulkCode : uint8_t; -} - -class BulkCommand : public std::vector { -protected: - BulkCommand(Hantek::BulkCode code, unsigned size); -public: - Hantek::BulkCode code; - bool pending = false; - BulkCommand* next = nullptr; -}; diff --git a/openhantek/src/hantekprotocol/controlStructs.cpp b/openhantek/src/hantekprotocol/controlStructs.cpp index d4f130ad..f200cefb 100644 --- a/openhantek/src/hantekprotocol/controlStructs.cpp +++ b/openhantek/src/hantekprotocol/controlStructs.cpp @@ -3,135 +3,74 @@ #include #include "controlStructs.h" -#include "controlvalue.h" #include "definitions.h" namespace Hantek { -ControlBeginCommand::ControlBeginCommand(BulkIndex index) - : ControlCommand(Hantek::ControlCode::CONTROL_BEGINCOMMAND, 10) { - data()[0] = 0x0f; - data()[1] = (uint8_t)index; -} - -ControlGetSpeed::ControlGetSpeed() : ControlCommand(Hantek::ControlCode::CONTROL_GETSPEED, 10) {} - -ConnectionSpeed ControlGetSpeed::getSpeed() { return (ConnectionSpeed)data()[0]; } -ControlSetOffset::ControlSetOffset() : ControlCommand(ControlCode::CONTROL_SETOFFSET, 17) {} +ControlSetGain_CH1::ControlSetGain_CH1() : ControlCommand( ControlCode::CONTROL_SETGAIN_CH1, 2 ) { this->setGainCH1( 1, 7 ); } -ControlSetOffset::ControlSetOffset(uint16_t channel1, uint16_t channel2, uint16_t trigger) - : ControlCommand(ControlCode::CONTROL_SETOFFSET, 17) { - this->setChannel(0, channel1); - this->setChannel(1, channel2); - this->setTrigger(trigger); +void ControlSetGain_CH1::setGainCH1( uint8_t gain, uint8_t index ) { + data()[ 0 ] = gain; + data()[ 1 ] = index; } -uint16_t ControlSetOffset::getChannel(ChannelID channel) { - if (channel == 0) - return ((data()[0] & 0x0f) << 8) | data()[1]; - else - return ((data()[2] & 0x0f) << 8) | data()[3]; -} -void ControlSetOffset::setChannel(ChannelID channel, uint16_t offset) { - if (channel == 0) { - data()[0] = (uint8_t)(offset >> 8); - data()[1] = (uint8_t)offset; - } else { - data()[2] = (uint8_t)(offset >> 8); - data()[3] = (uint8_t)offset; - } +ControlSetGain_CH2::ControlSetGain_CH2() : ControlCommand( ControlCode::CONTROL_SETGAIN_CH2, 2 ) { this->setGainCH2( 1, 7 ); } + +void ControlSetGain_CH2::setGainCH2( uint8_t gain, uint8_t index ) { + data()[ 0 ] = gain; + data()[ 1 ] = index; } -uint16_t ControlSetOffset::getTrigger() { return ((data()[4] & 0x0f) << 8) | data()[5]; } -void ControlSetOffset::setTrigger(uint16_t level) { - data()[4] = (uint8_t)(level >> 8); - data()[5] = (uint8_t)level; +ControlSetSamplerate::ControlSetSamplerate() : ControlCommand( ControlCode::CONTROL_SETSAMPLERATE, 2 ) { + this->setSamplerate( 1, 7 ); } -ControlSetRelays::ControlSetRelays(bool ch1Below1V, bool ch1Below100mV, bool ch1CouplingDC, bool ch2Below1V, - bool ch2Below100mV, bool ch2CouplingDC, bool triggerExt) - : ControlCommand(ControlCode::CONTROL_SETRELAYS, 17) { - this->setBelow1V(0, ch1Below1V); - this->setBelow100mV(0, ch1Below100mV); - this->setCoupling(0, ch1CouplingDC); - this->setBelow1V(1, ch2Below1V); - this->setBelow100mV(1, ch2Below100mV); - this->setCoupling(1, ch2CouplingDC); - this->setTrigger(triggerExt); +void ControlSetSamplerate::setSamplerate( uint8_t id, uint8_t index ) { + data()[ 0 ] = id; + data()[ 1 ] = index; } -bool ControlSetRelays::getBelow1V(ChannelID channel) { - if (channel == 0) - return (data()[1] & 0x04) == 0x00; - else - return (data()[4] & 0x20) == 0x00; -} -void ControlSetRelays::setBelow1V(ChannelID channel, bool below) { - if (channel == 0) - data()[1] = below ? 0xfb : 0x04; - else - data()[4] = below ? 0xdf : 0x20; +ControlSetNumChannels::ControlSetNumChannels() : ControlCommand( ControlCode::CONTROL_SETNUMCHANNELS, 1 ) { + this->setNumChannels( 2 ); } -bool ControlSetRelays::getBelow100mV(ChannelID channel) { - if (channel == 0) - return (data()[2] & 0x08) == 0x00; - else - return (data()[5] & 0x40) == 0x00; -} +void ControlSetNumChannels::setNumChannels( uint8_t val ) { data()[ 0 ] = val; } -void ControlSetRelays::setBelow100mV(ChannelID channel, bool below) { - if (channel == 0) - data()[2] = below ? 0xf7 : 0x08; - else - data()[5] = below ? 0xbf : 0x40; -} -bool ControlSetRelays::getCoupling(ChannelID channel) { - if (channel == 0) - return (data()[3] & 0x02) == 0x00; - else - return (data()[6] & 0x10) == 0x00; -} +ControlStartSampling::ControlStartSampling() : ControlCommand( ControlCode::CONTROL_STARTSAMPLING, 1 ) { data()[ 0 ] = 0x01; } -void ControlSetRelays::setCoupling(ChannelID channel, bool dc) { - if (channel == 0) - data()[3] = dc ? 0xfd : 0x02; - else - data()[6] = dc ? 0xef : 0x10; -} -bool ControlSetRelays::getTrigger() { return (data()[7] & 0x01) == 0x00; } +ControlStopSampling::ControlStopSampling() : ControlCommand( ControlCode::CONTROL_STARTSAMPLING, 1 ) { data()[ 0 ] = 0x00; } -void ControlSetRelays::setTrigger(bool ext) { data()[7] = ext ? 0xfe : 0x01; } -ControlSetVoltDIV_CH1::ControlSetVoltDIV_CH1() : ControlCommand(ControlCode::CONTROL_SETVOLTDIV_CH1, 1) { - this->setDiv(5); +ControlGetLimits::ControlGetLimits() : ControlCommand( ControlCode::CONTROL_GETEEPROM, sizeof( CalibrationValues ) ) { + value = uint8_t( 8 ); // get calibration values from EEPROM offset 8 + data()[ 0 ] = 0x01; } -void ControlSetVoltDIV_CH1::setDiv(uint8_t val) { data()[0] = val; } -ControlSetVoltDIV_CH2::ControlSetVoltDIV_CH2() : ControlCommand(ControlCode::CONTROL_SETVOLTDIV_CH2, 1) { - this->setDiv(5); +ControlSetCalFreq::ControlSetCalFreq() : ControlCommand( ControlCode::CONTROL_SETCALFREQ, 1 ) { + this->setCalFreq( 1 ); // 1kHz } -void ControlSetVoltDIV_CH2::setDiv(uint8_t val) { data()[0] = val; } - -ControlSetTimeDIV::ControlSetTimeDIV() : ControlCommand(ControlCode::CONTROL_SETTIMEDIV, 1) { this->setDiv(1); } +void ControlSetCalFreq::setCalFreq( uint8_t val ) { data()[ 0 ] = val; } -void ControlSetTimeDIV::setDiv(uint8_t val) { data()[0] = val; } -ControlAcquireHardData::ControlAcquireHardData() : ControlCommand(ControlCode::CONTROL_ACQUIIRE_HARD_DATA, 1) { - data()[0] = 0x01; +ControlSetCoupling::ControlSetCoupling() + : ControlCommand( ControlCode::CONTROL_SETCOUPLING, 1 ), ch1Coupling( 0x01 ), ch2Coupling( 0x10 ) { + data()[ 0 ] = 0x11; } -ControlGetLimits::ControlGetLimits(size_t channels) - : ControlCommand(ControlCode::CONTROL_VALUE, sizeof(OffsetsPerGainStep)*channels) { - value = (uint8_t)ControlValue::VALUE_OFFSETLIMITS; - data()[0] = 0x01; -} +void ControlSetCoupling::setCoupling( ChannelID channel, bool dc ) { + if ( channel == 0 ) + ch1Coupling = dc ? 0x01 : 0x00; + else + ch2Coupling = dc ? 0x10 : 0x00; + data()[ 0 ] = 0xFF & ( ch2Coupling | ch1Coupling ); } + +} // namespace Hantek diff --git a/openhantek/src/hantekprotocol/controlStructs.h b/openhantek/src/hantekprotocol/controlStructs.h index 55aab565..601229b5 100644 --- a/openhantek/src/hantekprotocol/controlStructs.h +++ b/openhantek/src/hantekprotocol/controlStructs.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "controlcode.h" @@ -9,126 +11,50 @@ #include namespace Hantek { -struct OffsetsPerGainStep; +struct CalibrationValues; -/// \enum BulkIndex -/// \brief Can be set by CONTROL_BEGINCOMMAND, maybe it allows multiple commands -/// at the same time? -enum BulkIndex { - COMMANDINDEX_0 = 0x03, ///< Used most of the time - COMMANDINDEX_1 = 0x0a, - COMMANDINDEX_2 = 0x09, - COMMANDINDEX_3 = 0x01, ///< Used for ::BulkCode::SETTRIGGERANDSAMPLERATE sometimes - COMMANDINDEX_4 = 0x02, - COMMANDINDEX_5 = 0x08 -}; -/// \class ControlBeginCommand -class ControlBeginCommand : public ControlCommand { - public: - /// \brief Sets the command index to the given value. - /// \param index The CommandIndex for the command. - ControlBeginCommand(BulkIndex index = COMMANDINDEX_0); +struct ControlSetGain_CH1 : public ControlCommand { + ControlSetGain_CH1(); + void setGainCH1( uint8_t gain, uint8_t index ); }; -/// \brief The CONTROL_GETSPEED parser. -class ControlGetSpeed : public ControlCommand { - public: - ControlGetSpeed(); - /// \brief Gets the speed of the connection. - /// \return The speed level of the USB connection. - ConnectionSpeed getSpeed(); +struct ControlSetGain_CH2 : public ControlCommand { + ControlSetGain_CH2(); + void setGainCH2( uint8_t gain, uint8_t index ); }; -struct ControlSetOffset : public ControlCommand { - ControlSetOffset(); - /// \brief Sets the offsets to the given values. - /// \param channel1 The offset for channel 1. - /// \param channel2 The offset for channel 2. - /// \param trigger The offset for ext. trigger. - ControlSetOffset(uint16_t channel1, uint16_t channel2, uint16_t trigger); - - /// \brief Get the offset for the given channel. - /// \param channel The channel whose offset should be returned. - /// \return The channel offset value. - uint16_t getChannel(ChannelID channel); - /// \brief Set the offset for the given channel. - /// \param channel The channel that should be set. - /// \param offset The new channel offset value. - void setChannel(ChannelID channel, uint16_t offset); - /// \brief Get the trigger level. - /// \return The trigger level value. - uint16_t getTrigger(); - /// \brief Set the trigger level. - /// \param level The new trigger level value. - void setTrigger(uint16_t level); +struct ControlSetSamplerate : public ControlCommand { + ControlSetSamplerate(); + void setSamplerate( uint8_t sampleId, uint8_t index ); }; -struct ControlSetRelays : public ControlCommand { - /// \brief Sets all relay states. - /// \param ch1Below1V Sets the state of the Channel 1 below 1 V relay. - /// \param ch1Below100mV Sets the state of the Channel 1 below 100 mV relay. - /// \param ch1CouplingDC Sets the state of the Channel 1 coupling relay. - /// \param ch2Below1V Sets the state of the Channel 2 below 1 V relay. - /// \param ch2Below100mV Sets the state of the Channel 2 below 100 mV relay. - /// \param ch2CouplingDC Sets the state of the Channel 2 coupling relay. - /// \param triggerExt Sets the state of the external trigger relay. - ControlSetRelays(bool ch1Below1V = false, bool ch1Below100mV = false, bool ch1CouplingDC = false, - bool ch2Below1V = false, bool ch2Below100mV = false, bool ch2CouplingDC = false, - bool triggerExt = false); - - /// \brief Get the below 1 V relay state for the given channel. - /// \param channel The channel whose relay state should be returned. - /// \return true, if the gain of the channel is below 1 V. - bool getBelow1V(ChannelID channel); - /// \brief Set the below 1 V relay for the given channel. - /// \param channel The channel that should be set. - /// \param below true, if the gain of the channel should be below 1 V. - void setBelow1V(ChannelID channel, bool below); - /// \brief Get the below 1 V relay state for the given channel. - /// \param channel The channel whose relay state should be returned. - /// \return true, if the gain of the channel is below 1 V. - bool getBelow100mV(ChannelID channel); - /// \brief Set the below 100 mV relay for the given channel. - /// \param channel The channel that should be set. - /// \param below true, if the gain of the channel should be below 100 mV. - void setBelow100mV(ChannelID channel, bool below); - /// \brief Get the coupling relay state for the given channel. - /// \param channel The channel whose relay state should be returned. - /// \return true, if the coupling of the channel is DC. - bool getCoupling(ChannelID channel); - /// \brief Set the coupling relay for the given channel. - /// \param channel The channel that should be set. - /// \param dc true, if the coupling of the channel should be DC. - void setCoupling(ChannelID channel, bool dc); - /// \brief Get the external trigger relay state. - /// \return true, if the trigger is external (EXT-Connector). - bool getTrigger(); - /// \brief Set the external trigger relay. - /// \param ext true, if the trigger should be external (EXT-Connector). - void setTrigger(bool ext); +struct ControlSetNumChannels : public ControlCommand { + ControlSetNumChannels(); + void setNumChannels( uint8_t val ); }; -struct ControlSetVoltDIV_CH1 : public ControlCommand { - ControlSetVoltDIV_CH1(); - void setDiv(uint8_t val); +struct ControlStartSampling : public ControlCommand { + ControlStartSampling(); }; -struct ControlSetVoltDIV_CH2 : public ControlCommand { - ControlSetVoltDIV_CH2(); - void setDiv(uint8_t val); +struct ControlStopSampling : public ControlCommand { + ControlStopSampling(); }; -struct ControlSetTimeDIV : public ControlCommand { - ControlSetTimeDIV(); - void setDiv(uint8_t val); +struct ControlGetLimits : public ControlCommand { + ControlGetLimits(); }; -struct ControlAcquireHardData : public ControlCommand { - ControlAcquireHardData(); +struct ControlSetCalFreq : public ControlCommand { + ControlSetCalFreq(); + void setCalFreq( uint8_t val ); }; -struct ControlGetLimits : public ControlCommand { - ControlGetLimits(size_t channels); +struct ControlSetCoupling : public ControlCommand { + ControlSetCoupling(); + void setCoupling( ChannelID channel, bool dc ); + uint8_t ch1Coupling, ch2Coupling; }; -} + +} // namespace Hantek diff --git a/openhantek/src/hantekprotocol/controlcode.h b/openhantek/src/hantekprotocol/controlcode.h index 3653f9cc..dee2238b 100644 --- a/openhantek/src/hantekprotocol/controlcode.h +++ b/openhantek/src/hantekprotocol/controlcode.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include @@ -5,118 +7,31 @@ namespace Hantek { /// \brief All supported control commands. - -/// CONTROL_VALUE [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A, MODEL_DSO6022] -///

-/// The 0xa2 control read/write command gives access to a ControlValue. -///

+/// \brief All supported control commands. +/// 0xE0 CONTROL_SETVOLTDIV_CH1 CH1 voltage div setting (6022BE/BL) +/// +/// 0xE1 CONTROL_SETVOLTDIV_CH2 CH2 voltage div setting (6022BE/BL) /// -/// CONTROL_GETSPEED [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A, MODEL_DSO6022] -///

-/// The 0xb2 control read command gets the speed level of the USB -/// connection: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
ConnectionSpeed0x000x000x000x000x000x000x000x000x00
-///

+/// CONTROL_SETTIMEDIV Time divisor setting (6022BE/BL) +/// +/// CONTROL_ACQUIIRE_HARD_DATA Request sample data (6022BE/BL) /// -/// CONTROL_BEGINCOMMAND [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// The 0xb3 control write command is sent before any bulk command: -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x0fBulkIndexBulkIndexBulkIndex0x000x000x000x000x000x00
-///

+/// | Oscilloscope Command | bRequest Value | Other Notes | +/// |----------------------|----------------|------------------------------------------------------------------------| +/// | Set CH0 voltage range| 0xE0 | Possible values: 1,2,5,10 (5V, 2.5V, 1V, 500mV). | +/// | Set CH1 voltage range| 0xE1 | Possible values: 1,2,5,10 (5V, 2.5V, 1V, 500mV). | +/// | Set Sampling Rate | 0xE2 | Possible values: 48, 30, 24, 16, 8, 4, 1 (MHz) and 50,20,10 (*10kHz). | +/// | Trigger Oscilloscope | 0xE3 | Possible values: 0: start sampling, 1: stop sampling | +/// | Set channel count | 0xE4 | Possible values are 1 (CH1 only) and 2 (CH1 and CH2) | +/// | Set Calibration Freq | 0xE6 | Possible values: 1, 2, 5, 10, 20, 50, 100 (kHz) | +/// | | | Possible values: 105 (50 Hz), 110 (100 Hz), 120 (200 Hz), 150 (500 Hz) | +/// | Read/Write Firmware | 0xA0 | Read or write the scope firmware. Must be done on scope initialization | /// -/// CONTROL_SETOFFSET [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// The 0xb4 control write command sets the channel offsets: -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
Ch1Offset[1]Ch1Offset[0]Ch2Offset[1]Ch2Offset[0]TriggerOffset[1]TriggerOffset[0]
-/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x000x000x000x000x000x000x000x000x000x000x00
-///

+/// The 0xEx requests are sent with value 0x00. +/// The calibration commands are sent with value 0x08 (offset into eeprom). +/// The value for R/W command is dependent on the Cypress protocol for interacting with the firmware. /// -/// CONTROL_SETRELAYS [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// The 0xb5 control write command sets the internal relays: -/// -/// -/// -/// -/// -/// -/// -///
0x000x04 ^ (Ch1Gain < 1 V)0x08 ^ (Ch1Gain < 100 mV)0x02 ^ (Ch1Coupling == DC)
-/// -/// -/// -/// -/// -/// -/// -///
0x20 ^ (Ch2Gain < 1 V)0x40 ^ (Ch2Gain < 100 mV)0x10 ^ (Ch2Coupling == DC)0x01 ^ (Trigger == EXT)
-/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
0x000x000x000x000x000x000x000x000x00
-///

-///

-/// The limits are <= instead of < for the 10 bit models, since those -/// support voltages up to 10 V. -///

+/// A bulk read from end point 0x86 reads the current contents of the FIFO, which the ADC is filling. /// /// CONTROL_SETVOLTDIV_CH1 CH1 voltage div setting (6022BE/BL) /// @@ -125,16 +40,16 @@ namespace Hantek { /// CONTROL_SETTIMEDIV Time divisor setting (6022BE/BL) /// /// CONTROL_ACQUIIRE_HARD_DATA Request sample data (6022BE/BL) + enum class ControlCode : uint8_t { - CONTROL_VALUE = 0xa2, - CONTROL_GETSPEED = 0xb2, - CONTROL_BEGINCOMMAND = 0xb3, - CONTROL_SETOFFSET = 0xb4, - CONTROL_SETRELAYS = 0xb5, - CONTROL_SETVOLTDIV_CH1 = 0xe0, - CONTROL_SETVOLTDIV_CH2 = 0xe1, - CONTROL_SETTIMEDIV = 0xe2, - CONTROL_ACQUIIRE_HARD_DATA = 0xe3 + CONTROL_GETEEPROM = 0xa2, + CONTROL_SETGAIN_CH1 = 0xe0, + CONTROL_SETGAIN_CH2 = 0xe1, + CONTROL_SETSAMPLERATE = 0xe2, + CONTROL_STARTSAMPLING = 0xe3, + CONTROL_SETNUMCHANNELS = 0xe4, + CONTROL_SETCOUPLING = 0xe5, // DC/AC not possible without hw modification on Hantek 6022, but implemented on Sainsmart DS120 + CONTROL_SETCALFREQ = 0xe6 }; -} +} // namespace Hantek diff --git a/openhantek/src/hantekprotocol/controlcommand.cpp b/openhantek/src/hantekprotocol/controlcommand.cpp index b2a33411..8783c08e 100644 --- a/openhantek/src/hantekprotocol/controlcommand.cpp +++ b/openhantek/src/hantekprotocol/controlcommand.cpp @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "controlcommand.h" -ControlCommand::ControlCommand(Hantek::ControlCode code, unsigned size): std::vector(size), code((uint8_t)code) {} +ControlCommand::ControlCommand( Hantek::ControlCode code, unsigned size ) + : std::vector< uint8_t >( size ), code( uint8_t( code ) ) {} diff --git a/openhantek/src/hantekprotocol/controlcommand.h b/openhantek/src/hantekprotocol/controlcommand.h index e2d97f75..531e8111 100644 --- a/openhantek/src/hantekprotocol/controlcommand.h +++ b/openhantek/src/hantekprotocol/controlcommand.h @@ -9,12 +9,14 @@ namespace Hantek { enum class ControlCode : uint8_t; } -class ControlCommand : public std::vector { -protected: - ControlCommand(Hantek::ControlCode code, unsigned size); -public: +class ControlCommand : public std::vector< uint8_t > { + + protected: + ControlCommand( Hantek::ControlCode code, unsigned size ); + + public: bool pending = false; uint8_t code; uint8_t value = 0; - ControlCommand* next = nullptr; + ControlCommand *next = nullptr; }; diff --git a/openhantek/src/hantekprotocol/controlvalue.h b/openhantek/src/hantekprotocol/controlvalue.h deleted file mode 100644 index 5b3699ef..00000000 --- a/openhantek/src/hantekprotocol/controlvalue.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -namespace Hantek { - -/// \brief All supported values for control commands. -/// VALUE_OFFSETLIMITS [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// Value 0x08 is the calibration data for the channels offsets. It holds the -/// offset value for the top and bottom of the scope screen for every gain -/// step on every channel. The data is stored as a three-dimensional array:
-/// channelLevels[channel][GainId][LevelOffset] -///

-///


-/// -/// VALUE_DEVICEADDRESS [MODEL_DSO2090, MODEL_DSO2150, MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// Value 0x0a is the address of the device. It has a length of one byte. -///

-///


-/// -/// VALUE_FASTRATECALIBRATION [MODEL_DSO2250, MODEL_DSO5200, MODEL_DSO5200A] -///

-/// Value 0x60 is the calibration data for the fast rate mode on the DSO-2250, -/// DSO-5200 and DSO-5200A. It's used to correct the level differences between -/// the two merged channels to avoid deterministic noise. -///

-///


-/// -/// VALUE_ETSCORRECTION [MODEL_DSO5200, MODEL_DSO5200A] -///

-/// Value 0x70 contains correction values for the ETS functionality of the -/// DSO-5200 and DSO-5200A. -///

-///


-enum class ControlValue : uint8_t { - VALUE_OFFSETLIMITS = 0x08, - VALUE_DEVICEADDRESS = 0x0a, - VALUE_FASTRATECALIBRATION = 0x60, - VALUE_ETSCORRECTION = 0x70 -}; - -} - diff --git a/openhantek/src/hantekprotocol/definitions.h b/openhantek/src/hantekprotocol/definitions.h index 11b5f6ed..874cd07e 100644 --- a/openhantek/src/hantekprotocol/definitions.h +++ b/openhantek/src/hantekprotocol/definitions.h @@ -6,118 +6,46 @@ #include #include -#define HANTEK_GAIN_STEPS 9 +#define HANTEK_GAIN_STEPS 8 namespace Hantek { /// \enum UsedChannels /// \brief The enabled channels. enum class UsedChannels : uint8_t { + USED_NONE, ///< No channels are activated USED_CH1, ///< Only channel 1 is activated USED_CH2, ///< Only channel 2 is activated USED_CH1CH2, ///< Channel 1 and 2 are both activated - USED_NONE, ///< No channels are activated - - // The enabled channels for the DSO-2250. - BUSED_CH1 = USED_CH1, - BUSED_NONE = USED_CH2, - BUSED_CH1CH2 = USED_CH1CH2, - BUSED_CH2 = USED_NONE -}; - -/// \enum DTriggerPositionUsed hantek/types.h -/// \brief The trigger position states for the 0x0d command. -enum class DTriggerPositionUsed: uint8_t { - OFF = 0, ///< Used for Roll mode - ON = 7 ///< Used for normal operation -}; - -#pragma pack(push, 1) -struct Offset { - unsigned short start = 0x0000; - unsigned short end = 0xffff; -}; - -struct OffsetsPerGainStep { - Offset step[HANTEK_GAIN_STEPS]; -}; - -/// \struct FilterBits -/// \brief The bits for BULK::SETFILTER. -struct FilterBits { - uint8_t channel1 : 1; ///< Set to true when channel 1 isn't used - uint8_t channel2 : 1; ///< Set to true when channel 2 isn't used - uint8_t trigger : 1; ///< Set to true when trigger isn't used - uint8_t reserved : 5; ///< Unused bits -}; - - -/// \struct GainBits -/// \brief The gain bits for BulkCode::SETGAIN. -struct GainBits { - uint8_t channel1 : 2; ///< Gain for CH1, 0 = 1e* V, 1 = 2e*, 2 = 5e* - uint8_t channel2 : 2; ///< Gain for CH1, 0 = 1e* V, 1 = 2e*, 2 = 5e* - uint8_t reserved : 4; ///< Unused bits }; +#pragma pack( push, 1 ) -/// \struct Tsr1Bits -/// \brief Trigger and samplerate bits (Byte 1). -struct Tsr1Bits { - uint8_t triggerSource : 2; ///< The trigger source, see Hantek::TriggerSource - uint8_t recordLength : 3; ///< See ::RecordLengthId - uint8_t samplerateId : 2; ///< Samplerate ID when downsampler is disabled - uint8_t downsamplingMode : 1; ///< true, if Downsampler is used -}; - +// The strct reflects the offset layout in EEPROM +// All values are offset binariy bytes (0 -> 0x80) +// The 1st 32 bytes are already factory calibration values +// 16 byte offset at low speed: CH0@20mV, CH1@20mV, CH0@50mV,...,CH1@5V +// 16 byte offset at high speed: CH0@20mV, CH1@20mV, CH0@50mV,...,CH1@5V +// The next 16 bytes are written by python calibration tool +// 16 byte gain: CH0@20mV, CH1@20mV, CH0@50mV,...,CH1@5V +// The next 32 bytes are an optional fractional part of offset (*250) +// 16 byte offset (ls) fractional part: CH0@20mV, CH1@20mV, CH0@50mV,...,CH1@5V +// 16 byte offset (hs) fractional part: CH0@20mV, CH1@20mV, CH0@50mV,...,CH1@5V -/// \struct Tsr2Bits -/// \brief Trigger and samplerate bits (Byte 2). -struct Tsr2Bits { - uint8_t usedChannels : 2; ///< Used channels, see Hantek::UsedChannels - uint8_t fastRate : 1; ///< true, if one channels uses all buffers - uint8_t triggerSlope : 1; ///< The trigger slope, see Dso::Slope, inverted - /// when Tsr1Bits.samplerateFast is uneven - uint8_t reserved : 4; ///< Unused bits +struct Steps { + uint8_t step[ HANTEK_GAIN_STEPS ][ 2 ]; }; - -/// \struct CTriggerBits -/// \brief Trigger bits for 0x0c command. -struct CTriggerBits { - uint8_t triggerSource : 2; ///< The trigger source, see Hantek::TriggerSource - uint8_t triggerSlope : 1; ///< The trigger slope, see Dso::Slope - uint8_t reserved : 5; ///< Unused bits +struct Offsets { + Steps ls; + Steps hs; }; - -/// \struct DBufferBits -/// \brief Buffer mode bits for 0x0d command. -struct DBufferBits { - uint8_t triggerPositionUsed : 3; ///< See ::DTriggerPositionUsed - uint8_t recordLength : 3; ///< See ::RecordLengthId - uint8_t reserved : 2; ///< Unused bits -}; - - -/// \struct ESamplerateBits -/// \brief Samplerate bits for DSO-2250 0x0e command. -struct ESamplerateBits { - uint8_t fastRate : 1; ///< false, if one channels uses all buffers - uint8_t downsampling : 1; ///< true, if the downsampler is activated - uint8_t reserved : 4; ///< Unused bits -}; - - -/// \struct ETsrBits -/// \brief Trigger and samplerate bits for DSO-5200/DSO-5200A 0x0e command. -struct ETsrBits { - uint8_t fastRate : 1; ///< false, if one channels uses all buffers - uint8_t usedChannels : 2; ///< Used channels, see Hantek::UsedChannels - uint8_t triggerSource : 2; ///< The trigger source, see Hantek::TriggerSource - uint8_t triggerSlope : 2; ///< The trigger slope, see Dso::Slope - uint8_t triggerPulse : 1; ///< Pulses are causing trigger events +struct CalibrationValues { + Offsets off; + Steps gain; + Offsets fine; }; -#pragma pack(pop) +#pragma pack( pop ) -} +} // namespace Hantek diff --git a/openhantek/src/iconfont/QtAwesome.cpp b/openhantek/src/iconfont/QtAwesome.cpp index 78405e88..d0e3e04f 100644 --- a/openhantek/src/iconfont/QtAwesome.cpp +++ b/openhantek/src/iconfont/QtAwesome.cpp @@ -16,138 +16,127 @@ QtAwesome *iconFont = new QtAwesome(); -/// The font-awesome icon painter -class QtAwesomeCharIconPainter : public QtAwesomeIconPainter { +QtAwesomeIconPainter::~QtAwesomeIconPainter() {} - protected: - QStringList optionKeysForModeAndState(const QString &key, QIcon::Mode mode, QIcon::State state) { - QString modePostfix; - switch (mode) { - case QIcon::Disabled: - modePostfix = "-disabled"; - break; - case QIcon::Active: - modePostfix = "-active"; - break; - case QIcon::Selected: - modePostfix = "-selected"; - break; - case QIcon::Normal: - break; - } +QStringList QtAwesomeCharIconPainter::optionKeysForModeAndState( const QString &key, QIcon::Mode mode, QIcon::State state ) { + QString modePostfix; + switch ( mode ) { + case QIcon::Disabled: + modePostfix = "-disabled"; + break; + case QIcon::Active: + modePostfix = "-active"; + break; + case QIcon::Selected: + modePostfix = "-selected"; + break; + case QIcon::Normal: + break; + } - QString statePostfix; - if (state == QIcon::Off) { statePostfix = "-off"; } + QString statePostfix; + if ( state == QIcon::Off ) { + statePostfix = "-off"; + } - // the keys that need to bet tested: key-mode-state | key-mode | key-state | key - QStringList result; - if (!modePostfix.isEmpty()) { - if (!statePostfix.isEmpty()) { result.push_back(key + modePostfix + statePostfix); } - result.push_back(key + modePostfix); + // the keys that need to bet tested: key-mode-state | key-mode | key-state | key + QStringList result; + if ( !modePostfix.isEmpty() ) { + if ( !statePostfix.isEmpty() ) { + result.push_back( key + modePostfix + statePostfix ); } - if (!statePostfix.isEmpty()) { result.push_back(key + statePostfix); } - - return result; + result.push_back( key + modePostfix ); + } + if ( !statePostfix.isEmpty() ) { + result.push_back( key + statePostfix ); } - QVariant optionValueForModeAndState(const QString &baseKey, QIcon::Mode mode, QIcon::State state, - const QVariantMap &options) { - foreach (QString key, optionKeysForModeAndState(baseKey, mode, state)) { - if (options.contains(key)) return options.value(key); - } - return options.value(baseKey); + return result; +} + +QVariant QtAwesomeCharIconPainter::optionValueForModeAndState( const QString &baseKey, QIcon::Mode mode, QIcon::State state, + const QVariantMap &options ) { + foreach ( QString key, optionKeysForModeAndState( baseKey, mode, state ) ) { + if ( options.contains( key ) ) + return options.value( key ); } + return options.value( baseKey ); +} - public: - virtual void paint(QtAwesome *awesome, QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state, - const QVariantMap &options) { - Q_UNUSED(mode); - Q_UNUSED(state); - Q_UNUSED(options); - painter->save(); +void QtAwesomeCharIconPainter::paint( QtAwesome *awesome, QPainter *painter, const QRect &rect, QIcon::Mode mode, + QIcon::State state, const QVariantMap &options ) { + Q_UNUSED( mode ); + Q_UNUSED( state ); + Q_UNUSED( options ); - // set the default options - QColor color = optionValueForModeAndState("color", mode, state, options).value(); - QString text = optionValueForModeAndState("text", mode, state, options).toString(); + painter->save(); - if (text.isEmpty() || color.red()==0) { - qWarning() << "empty for" << mode << state << options; - } + // set the default options + QColor color = optionValueForModeAndState( "color", mode, state, options ).value< QColor >(); + QString text = optionValueForModeAndState( "text", mode, state, options ).toString(); - painter->setPen(color); + if ( text.isEmpty() || color.red() == 0 ) { + qWarning() << "empty for" << mode << state << options; + } - // add some 'padding' around the icon - int drawSize = qRound(rect.height() * options.value("scale-factor").toFloat()); + painter->setPen( color ); - painter->setFont(awesome->font(drawSize)); - painter->drawText(rect, text, QTextOption(Qt::AlignCenter | Qt::AlignVCenter)); + // add some 'padding' around the icon + int drawSize = qRound( rect.height() * options.value( "scale-factor" ).toFloat() ); - painter->restore(); + painter->setFont( awesome->font( drawSize ) ); + painter->drawText( rect, text, QTextOption( Qt::AlignCenter | Qt::AlignVCenter ) ); - QVariant var = options.value("anim"); - QtAwesomeAnimation *anim = var.value(); - if (anim) { anim->setup(*painter, rect); } + painter->restore(); + QVariant var = options.value( "anim" ); + QtAwesomeAnimation *anim = var.value< QtAwesomeAnimation * >(); + if ( anim ) { + anim->setup( *painter, rect ); } -}; +} //--------------------------------------------------------------------------------------- /// The painter icon engine. -class QtAwesomeIconPainterIconEngine : public QIconEngine { - - public: - QtAwesomeIconPainterIconEngine(QtAwesome *awesome, QtAwesomeIconPainter *painter, const QVariantMap &options) - : awesomeRef_(awesome), iconPainterRef_(painter), options_(options) {} +QtAwesomeIconPainterIconEngine::QtAwesomeIconPainterIconEngine( QtAwesome *awesome, QtAwesomeIconPainter *painter, + const QVariantMap &options ) + : awesomeRef_( awesome ), iconPainterRef_( painter ), options_( options ) {} - virtual ~QtAwesomeIconPainterIconEngine() {} +QtAwesomeIconPainterIconEngine::~QtAwesomeIconPainterIconEngine() {} - QtAwesomeIconPainterIconEngine *clone() const override { - return new QtAwesomeIconPainterIconEngine(awesomeRef_, iconPainterRef_, options_); - } +QtAwesomeIconPainterIconEngine *QtAwesomeIconPainterIconEngine::clone() const { + return new QtAwesomeIconPainterIconEngine( awesomeRef_, iconPainterRef_, options_ ); +} - virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override { - Q_UNUSED(mode); - Q_UNUSED(state); - iconPainterRef_->paint(awesomeRef_, painter, rect, mode, state, options_); - } +void QtAwesomeIconPainterIconEngine::paint( QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state ) { + Q_UNUSED( mode ); + Q_UNUSED( state ); + iconPainterRef_->paint( awesomeRef_, painter, rect, mode, state, options_ ); +} - virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override { - QPixmap pm(size); - pm.fill(Qt::transparent); // we need transparency - { - QPainter p(&pm); - paint(&p, QRect(QPoint(0, 0), size), mode, state); - } - return pm; +QPixmap QtAwesomeIconPainterIconEngine::pixmap( const QSize &size, QIcon::Mode mode, QIcon::State state ) { + QPixmap pm( size ); + pm.fill( Qt::transparent ); // we need transparency + { + QPainter p( &pm ); + paint( &p, QRect( QPoint( 0, 0 ), size ), mode, state ); } - //#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - // virtual QList availableSizes(QIcon::Mode mode, QIcon::State state) const override { - // Q_UNUSED(mode); - // Q_UNUSED(state); - // QList sizes = {QSize(16, 16), QSize(32, 32), QSize(64, 64), - // QSize(128, 128), QSize(256, 256), QSize(512, 512)}; - // return sizes; - // } - //#endif - private: - QtAwesome *awesomeRef_; ///< a reference to the QtAwesome instance - QtAwesomeIconPainter *iconPainterRef_; ///< a reference to the icon painter - QVariantMap options_; ///< the options for this icon painter -}; + return pm; +} //--------------------------------------------------------------------------------------- /// The default icon colors -QtAwesome::QtAwesome(QObject *parent) : QObject(parent), namedCodepoints_() { +QtAwesome::QtAwesome( QObject *parent ) : QObject( parent ), namedCodepoints_() { // initialize the default options - setDefaultOption("color", QColor(50, 50, 50)); - setDefaultOption("color-off", QColor(50, 50, 50)); - setDefaultOption("color-disabled", QColor(70, 70, 70, 60)); - setDefaultOption("color-active", QColor(10, 10, 10)); - setDefaultOption("color-selected", QColor(10, 10, 10)); - setDefaultOption("scale-factor", 1.0); + setDefaultOption( "color", QColor( 50, 50, 50 ) ); + setDefaultOption( "color-off", QColor( 50, 50, 50 ) ); + setDefaultOption( "color-disabled", QColor( 70, 70, 70, 60 ) ); + setDefaultOption( "color-active", QColor( 10, 10, 10 ) ); + setDefaultOption( "color-selected", QColor( 10, 10, 10 ) ); + setDefaultOption( "scale-factor", 1.0 ); fontIconPainter_ = new QtAwesomeCharIconPainter(); } @@ -155,11 +144,11 @@ QtAwesome::QtAwesome(QObject *parent) : QObject(parent), namedCodepoints_() { QtAwesome::~QtAwesome() { delete fontIconPainter_; // delete errorIconPainter_; - qDeleteAll(painterMap_); + qDeleteAll( painterMap_ ); } /// initializes the QtAwesome icon factory with the given fontname -void QtAwesome::init(const QString &fontname) { fontName_ = fontname; } +void QtAwesome::init( const QString &fontname ) { fontName_ = fontname; } struct FANameIcon { const char *name; @@ -960,23 +949,23 @@ bool QtAwesome::initFontAwesome() { static int fontAwesomeFontId = -1; // only load font-awesome once - if (fontAwesomeFontId < 0) { + if ( fontAwesomeFontId < 0 ) { // load the font file - QFile res(":/images/fontawesome-4.7.0.ttf"); - if (!res.open(QIODevice::ReadOnly)) { + QFile res( ":/images/fontawesome-4.7.0.ttf" ); + if ( !res.open( QIODevice::ReadOnly ) ) { qDebug() << "Font awesome font could not be loaded!"; return false; } - QByteArray fontData(res.readAll()); + QByteArray fontData( res.readAll() ); res.close(); // fetch the given font - fontAwesomeFontId = QFontDatabase::addApplicationFontFromData(fontData); + fontAwesomeFontId = QFontDatabase::addApplicationFontFromData( fontData ); } - QStringList loadedFontFamilies = QFontDatabase::applicationFontFamilies(fontAwesomeFontId); - if (!loadedFontFamilies.empty()) { - fontName_ = loadedFontFamilies.at(0); + QStringList loadedFontFamilies = QFontDatabase::applicationFontFamilies( fontAwesomeFontId ); + if ( !loadedFontFamilies.empty() ) { + fontName_ = loadedFontFamilies.at( 0 ); } else { qDebug() << "Font awesome font is empty?!"; fontAwesomeFontId = -1; // restore the font-awesome id @@ -984,30 +973,30 @@ bool QtAwesome::initFontAwesome() { } // intialize the map - QHash &m = namedCodepoints_; - for (unsigned i = 0; i < sizeof(faNameIconArray) / sizeof(FANameIcon); ++i) { - m.insert(faNameIconArray[i].name, faNameIconArray[i].icon); + QHash< QString, int > &m = namedCodepoints_; + for ( unsigned i = 0; i < sizeof( faNameIconArray ) / sizeof( FANameIcon ); ++i ) { + m.insert( faNameIconArray[ i ].name, faNameIconArray[ i ].icon ); } return true; } -void QtAwesome::addNamedCodepoint(const QString &name, int codePoint) { namedCodepoints_.insert(name, codePoint); } +void QtAwesome::addNamedCodepoint( const QString &name, int codePoint ) { namedCodepoints_.insert( name, codePoint ); } /// Sets a default option. These options are passed on to the icon painters -void QtAwesome::setDefaultOption(const QString &name, const QVariant &value) { defaultOptions_.insert(name, value); } +void QtAwesome::setDefaultOption( const QString &name, const QVariant &value ) { defaultOptions_.insert( name, value ); } /// Returns the default option for the given name -QVariant QtAwesome::defaultOption(const QString &name) { return defaultOptions_.value(name); } +QVariant QtAwesome::defaultOption( const QString &name ) { return defaultOptions_.value( name ); } // internal helper method to merge to option amps -static QVariantMap mergeOptions(const QVariantMap &defaults, const QVariantMap &override) { +static QVariantMap mergeOptions( const QVariantMap &defaults, const QVariantMap &override ) { QVariantMap result = defaults; - if (!override.isEmpty()) { - QMapIterator itr(override); - while (itr.hasNext()) { + if ( !override.isEmpty() ) { + QMapIterator< QString, QVariant > itr( override ); + while ( itr.hasNext() ) { itr.next(); - result.insert(itr.key(), itr.value()); + result.insert( itr.key(), itr.value() ); } } return result; @@ -1017,16 +1006,21 @@ static QVariantMap mergeOptions(const QVariantMap &defaults, const QVariantMap & /// /// awesome->icon( icon_group ) /// -QIcon QtAwesome::icon(int character, const QVariantMap &options) { +QIcon QtAwesome::icon( int character, const QVariantMap &options ) { // create a merged QVariantMap to have default options and icon-specific options - QVariantMap optionMap = mergeOptions(defaultOptions_, options); - if (!optionMap.contains("text")) optionMap.insert("text", QChar(character)); - if (!optionMap.contains("text-active")) optionMap.insert("text-active", QChar(character)); - if (!optionMap.contains("text-selected")) optionMap.insert("text-selected", QChar(character)); - if (!optionMap.contains("text-disabled")) optionMap.insert("text-disabled", QChar(character)); - if (!optionMap.contains("text-off")) optionMap.insert("text-off", QChar(character)); + QVariantMap optionMap = mergeOptions( defaultOptions_, options ); + if ( !optionMap.contains( "text" ) ) + optionMap.insert( "text", QChar( character ) ); + if ( !optionMap.contains( "text-active" ) ) + optionMap.insert( "text-active", QChar( character ) ); + if ( !optionMap.contains( "text-selected" ) ) + optionMap.insert( "text-selected", QChar( character ) ); + if ( !optionMap.contains( "text-disabled" ) ) + optionMap.insert( "text-disabled", QChar( character ) ); + if ( !optionMap.contains( "text-off" ) ) + optionMap.insert( "text-off", QChar( character ) ); - return icon(fontIconPainter_, optionMap); + return icon( fontIconPainter_, optionMap ); } /// Creates an icon with the given name @@ -1035,30 +1029,34 @@ QIcon QtAwesome::icon(int character, const QVariantMap &options) { /// prefix /// @param name the name of the icon /// @param options extra option to pass to the icon renderer -QIcon QtAwesome::icon(const QString &name, const QVariantMap &options) { +QIcon QtAwesome::icon( const QString &name, const QVariantMap &options ) { // when it's a named codepoint - if (namedCodepoints_.count(name)) { return icon(namedCodepoints_.value(name), options); } + if ( namedCodepoints_.count( name ) ) { + return icon( namedCodepoints_.value( name ), options ); + } // create a merged QVariantMap to have default options and icon-specific options - QVariantMap optionMap = mergeOptions(defaultOptions_, options); + QVariantMap optionMap = mergeOptions( defaultOptions_, options ); // this method first tries to retrieve the icon - QtAwesomeIconPainter *painter = painterMap_.value(name); - if (!painter) { return QIcon(); } + QtAwesomeIconPainter *painter = painterMap_.value( name ); + if ( !painter ) { + return QIcon(); + } - return icon(painter, optionMap); + return icon( painter, optionMap ); } /// Create a dynamic icon by simlpy supplying a painter object /// The ownership of the painter is NOT transfered. /// @param painter a dynamic painter that is going to paint the icon /// @param optionmap the options to pass to the painter -QIcon QtAwesome::icon(QtAwesomeIconPainter *painter, const QVariantMap &optionMap) { +QIcon QtAwesome::icon( QtAwesomeIconPainter *painter, const QVariantMap &optionMap ) { // Warning, when you use memoryleak detection. You should turn it of for the next call // QIcon's placed in gui items are often cached and not deleted when my memory-leak detection checks for leaks. // I'm not sure if it's a Qt bug or something I do wrong - QtAwesomeIconPainterIconEngine *engine = new QtAwesomeIconPainterIconEngine(this, painter, optionMap); - return QIcon(engine); + QtAwesomeIconPainterIconEngine *engine = new QtAwesomeIconPainterIconEngine( this, painter, optionMap ); + return QIcon( engine ); } /// Adds a named icon-painter to the QtAwesome icon map @@ -1066,9 +1064,9 @@ QIcon QtAwesome::icon(QtAwesomeIconPainter *painter, const QVariantMap &optionMa /// /// @param name the name of the icon /// @param painter the icon painter to add for this name -void QtAwesome::give(const QString &name, QtAwesomeIconPainter *painter) { - delete painterMap_.value(name); // delete the old one - painterMap_.insert(name, painter); +void QtAwesome::give( const QString &name, QtAwesomeIconPainter *painter ) { + delete painterMap_.value( name ); // delete the old one + painterMap_.insert( name, painter ); } /// Creates/Gets the icon font with a given size in pixels. This can be usefull to use a label for displaying icons @@ -1076,8 +1074,8 @@ void QtAwesome::give(const QString &name, QtAwesomeIconPainter *painter) { /// /// QLabel* label = new QLabel( QChar( icon_group ) ); /// label->setFont( awesome->font(16) ) -QFont QtAwesome::font(int size) { - QFont font(fontName_); - font.setPixelSize(size); +QFont QtAwesome::font( int size ) { + QFont font( fontName_ ); + font.setPixelSize( size ); return font; } diff --git a/openhantek/src/iconfont/QtAwesome.h b/openhantek/src/iconfont/QtAwesome.h index 30c3f68d..c8102c81 100644 --- a/openhantek/src/iconfont/QtAwesome.h +++ b/openhantek/src/iconfont/QtAwesome.h @@ -19,843 +19,46 @@ #include -/// A list of all icon-names with the codepoint (unicode-value) on the right -/// You can use the names on the page http://fortawesome.github.io/Font-Awesome/design.html -namespace fa { - enum icon { - fa_500px = 0xf26e, - addressbook = 0xf2b9, - addressbooko = 0xf2ba, - addresscard = 0xf2bb, - addresscardo = 0xf2bc, - adjust = 0xf042, - adn = 0xf170, - aligncenter = 0xf037, - alignjustify = 0xf039, - alignleft = 0xf036, - alignright = 0xf038, - amazon = 0xf270, - ambulance = 0xf0f9, - americansignlanguageinterpreting = 0xf2a3, - anchor = 0xf13d, - android = 0xf17b, - angellist = 0xf209, - angledoubledown = 0xf103, - angledoubleleft = 0xf100, - angledoubleright = 0xf101, - angledoubleup = 0xf102, - angledown = 0xf107, - angleleft = 0xf104, - angleright = 0xf105, - angleup = 0xf106, - apple = 0xf179, - archive = 0xf187, - areachart = 0xf1fe, - arrowcircledown = 0xf0ab, - arrowcircleleft = 0xf0a8, - arrowcircleodown = 0xf01a, - arrowcircleoleft = 0xf190, - arrowcircleoright = 0xf18e, - arrowcircleoup = 0xf01b, - arrowcircleright = 0xf0a9, - arrowcircleup = 0xf0aa, - arrowdown = 0xf063, - arrowleft = 0xf060, - arrowright = 0xf061, - arrowup = 0xf062, - arrows = 0xf047, - arrowsalt = 0xf0b2, - arrowsh = 0xf07e, - arrowsv = 0xf07d, - aslinterpreting = 0xf2a3, - assistivelisteningsystems = 0xf2a2, - asterisk = 0xf069, - at = 0xf1fa, - audiodescription = 0xf29e, - automobile = 0xf1b9, - backward = 0xf04a, - balancescale = 0xf24e, - ban = 0xf05e, - bandcamp = 0xf2d5, - bank = 0xf19c, - barchart = 0xf080, - barcharto = 0xf080, - barcode = 0xf02a, - bars = 0xf0c9, - bath = 0xf2cd, - bathtub = 0xf2cd, - battery = 0xf240, - battery0 = 0xf244, - battery1 = 0xf243, - battery2 = 0xf242, - battery3 = 0xf241, - battery4 = 0xf240, - batteryempty = 0xf244, - batteryfull = 0xf240, - batteryhalf = 0xf242, - batteryquarter = 0xf243, - batterythreequarters = 0xf241, - bed = 0xf236, - beer = 0xf0fc, - behance = 0xf1b4, - behancesquare = 0xf1b5, - bell = 0xf0f3, - bello = 0xf0a2, - bellslash = 0xf1f6, - bellslasho = 0xf1f7, - bicycle = 0xf206, - binoculars = 0xf1e5, - birthdaycake = 0xf1fd, - bitbucket = 0xf171, - bitbucketsquare = 0xf172, - bitcoin = 0xf15a, - blacktie = 0xf27e, - blind = 0xf29d, - bluetooth = 0xf293, - bluetoothb = 0xf294, - bold = 0xf032, - bolt = 0xf0e7, - bomb = 0xf1e2, - book = 0xf02d, - bookmark = 0xf02e, - bookmarko = 0xf097, - braille = 0xf2a1, - briefcase = 0xf0b1, - btc = 0xf15a, - bug = 0xf188, - building = 0xf1ad, - buildingo = 0xf0f7, - bullhorn = 0xf0a1, - bullseye = 0xf140, - bus = 0xf207, - buysellads = 0xf20d, - cab = 0xf1ba, - calculator = 0xf1ec, - calendar = 0xf073, - calendarchecko = 0xf274, - calendarminuso = 0xf272, - calendaro = 0xf133, - calendarpluso = 0xf271, - calendartimeso = 0xf273, - camera = 0xf030, - cameraretro = 0xf083, - car = 0xf1b9, - caretdown = 0xf0d7, - caretleft = 0xf0d9, - caretright = 0xf0da, - caretsquareodown = 0xf150, - caretsquareoleft = 0xf191, - caretsquareoright = 0xf152, - caretsquareoup = 0xf151, - caretup = 0xf0d8, - cartarrowdown = 0xf218, - cartplus = 0xf217, - cc = 0xf20a, - ccamex = 0xf1f3, - ccdinersclub = 0xf24c, - ccdiscover = 0xf1f2, - ccjcb = 0xf24b, - ccmastercard = 0xf1f1, - ccpaypal = 0xf1f4, - ccstripe = 0xf1f5, - ccvisa = 0xf1f0, - certificate = 0xf0a3, - chain = 0xf0c1, - chainbroken = 0xf127, - check = 0xf00c, - checkcircle = 0xf058, - checkcircleo = 0xf05d, - checksquare = 0xf14a, - checksquareo = 0xf046, - chevroncircledown = 0xf13a, - chevroncircleleft = 0xf137, - chevroncircleright = 0xf138, - chevroncircleup = 0xf139, - chevrondown = 0xf078, - chevronleft = 0xf053, - chevronright = 0xf054, - chevronup = 0xf077, - child = 0xf1ae, - chrome = 0xf268, - circle = 0xf111, - circleo = 0xf10c, - circleonotch = 0xf1ce, - circlethin = 0xf1db, - clipboard = 0xf0ea, - clocko = 0xf017, - clone = 0xf24d, - close = 0xf00d, - cloud = 0xf0c2, - clouddownload = 0xf0ed, - cloudupload = 0xf0ee, - cny = 0xf157, - code = 0xf121, - codefork = 0xf126, - codepen = 0xf1cb, - codiepie = 0xf284, - coffee = 0xf0f4, - cog = 0xf013, - cogs = 0xf085, - columns = 0xf0db, - comment = 0xf075, - commento = 0xf0e5, - commenting = 0xf27a, - commentingo = 0xf27b, - comments = 0xf086, - commentso = 0xf0e6, - compass = 0xf14e, - compress = 0xf066, - connectdevelop = 0xf20e, - contao = 0xf26d, - copy = 0xf0c5, - copyright = 0xf1f9, - creativecommons = 0xf25e, - creditcard = 0xf09d, - creditcardalt = 0xf283, - crop = 0xf125, - crosshairs = 0xf05b, - css3 = 0xf13c, - cube = 0xf1b2, - cubes = 0xf1b3, - cut = 0xf0c4, - cutlery = 0xf0f5, - dashboard = 0xf0e4, - dashcube = 0xf210, - database = 0xf1c0, - deaf = 0xf2a4, - deafness = 0xf2a4, - dedent = 0xf03b, - delicious = 0xf1a5, - desktop = 0xf108, - deviantart = 0xf1bd, - diamond = 0xf219, - digg = 0xf1a6, - dollar = 0xf155, - dotcircleo = 0xf192, - download = 0xf019, - dribbble = 0xf17d, - driverslicense = 0xf2c2, - driverslicenseo = 0xf2c3, - dropbox = 0xf16b, - drupal = 0xf1a9, - edge = 0xf282, - edit = 0xf044, - eercast = 0xf2da, - eject = 0xf052, - ellipsish = 0xf141, - ellipsisv = 0xf142, - empire = 0xf1d1, - envelope = 0xf0e0, - envelopeo = 0xf003, - envelopeopen = 0xf2b6, - envelopeopeno = 0xf2b7, - envelopesquare = 0xf199, - envira = 0xf299, - eraser = 0xf12d, - etsy = 0xf2d7, - eur = 0xf153, - euro = 0xf153, - exchange = 0xf0ec, - exclamation = 0xf12a, - exclamationcircle = 0xf06a, - exclamationtriangle = 0xf071, - expand = 0xf065, - expeditedssl = 0xf23e, - externallink = 0xf08e, - externallinksquare = 0xf14c, - eye = 0xf06e, - eyeslash = 0xf070, - eyedropper = 0xf1fb, - fa = 0xf2b4, - facebook = 0xf09a, - facebookf = 0xf09a, - facebookofficial = 0xf230, - facebooksquare = 0xf082, - fastbackward = 0xf049, - fastforward = 0xf050, - fax = 0xf1ac, - feed = 0xf09e, - female = 0xf182, - fighterjet = 0xf0fb, - file = 0xf15b, - filearchiveo = 0xf1c6, - fileaudioo = 0xf1c7, - filecodeo = 0xf1c9, - fileexcelo = 0xf1c3, - fileimageo = 0xf1c5, - filemovieo = 0xf1c8, - fileo = 0xf016, - filepdfo = 0xf1c1, - filephotoo = 0xf1c5, - filepictureo = 0xf1c5, - filepowerpointo = 0xf1c4, - filesoundo = 0xf1c7, - filetext = 0xf15c, - filetexto = 0xf0f6, - filevideoo = 0xf1c8, - filewordo = 0xf1c2, - filezipo = 0xf1c6, - fileso = 0xf0c5, - film = 0xf008, - filter = 0xf0b0, - fire = 0xf06d, - fireextinguisher = 0xf134, - firefox = 0xf269, - firstorder = 0xf2b0, - flag = 0xf024, - flagcheckered = 0xf11e, - flago = 0xf11d, - flash = 0xf0e7, - flask = 0xf0c3, - flickr = 0xf16e, - floppyo = 0xf0c7, - folder = 0xf07b, - foldero = 0xf114, - folderopen = 0xf07c, - folderopeno = 0xf115, - font = 0xf031, - fontawesome = 0xf2b4, - fonticons = 0xf280, - fortawesome = 0xf286, - forumbee = 0xf211, - forward = 0xf04e, - foursquare = 0xf180, - freecodecamp = 0xf2c5, - frowno = 0xf119, - futbolo = 0xf1e3, - gamepad = 0xf11b, - gavel = 0xf0e3, - gbp = 0xf154, - ge = 0xf1d1, - gear = 0xf013, - gears = 0xf085, - genderless = 0xf22d, - getpocket = 0xf265, - gg = 0xf260, - ggcircle = 0xf261, - gift = 0xf06b, - git = 0xf1d3, - gitsquare = 0xf1d2, - github = 0xf09b, - githubalt = 0xf113, - githubsquare = 0xf092, - gitlab = 0xf296, - gittip = 0xf184, - glass = 0xf000, - glide = 0xf2a5, - glideg = 0xf2a6, - globe = 0xf0ac, - google = 0xf1a0, - googleplus = 0xf0d5, - googlepluscircle = 0xf2b3, - googleplusofficial = 0xf2b3, - googleplussquare = 0xf0d4, - googlewallet = 0xf1ee, - graduationcap = 0xf19d, - gratipay = 0xf184, - grav = 0xf2d6, - group = 0xf0c0, - hsquare = 0xf0fd, - hackernews = 0xf1d4, - handgrabo = 0xf255, - handlizardo = 0xf258, - handodown = 0xf0a7, - handoleft = 0xf0a5, - handoright = 0xf0a4, - handoup = 0xf0a6, - handpapero = 0xf256, - handpeaceo = 0xf25b, - handpointero = 0xf25a, - handrocko = 0xf255, - handscissorso = 0xf257, - handspocko = 0xf259, - handstopo = 0xf256, - handshakeo = 0xf2b5, - hardofhearing = 0xf2a4, - hashtag = 0xf292, - hddo = 0xf0a0, - header = 0xf1dc, - headphones = 0xf025, - heart = 0xf004, - hearto = 0xf08a, - heartbeat = 0xf21e, - history = 0xf1da, - home = 0xf015, - hospitalo = 0xf0f8, - hotel = 0xf236, - hourglass = 0xf254, - hourglass1 = 0xf251, - hourglass2 = 0xf252, - hourglass3 = 0xf253, - hourglassend = 0xf253, - hourglasshalf = 0xf252, - hourglasso = 0xf250, - hourglassstart = 0xf251, - houzz = 0xf27c, - html5 = 0xf13b, - icursor = 0xf246, - idbadge = 0xf2c1, - idcard = 0xf2c2, - idcardo = 0xf2c3, - ils = 0xf20b, - image = 0xf03e, - imdb = 0xf2d8, - inbox = 0xf01c, - indent = 0xf03c, - industry = 0xf275, - info = 0xf129, - infocircle = 0xf05a, - inr = 0xf156, - instagram = 0xf16d, - institution = 0xf19c, - internetexplorer = 0xf26b, - intersex = 0xf224, - ioxhost = 0xf208, - italic = 0xf033, - joomla = 0xf1aa, - jpy = 0xf157, - jsfiddle = 0xf1cc, - key = 0xf084, - keyboardo = 0xf11c, - krw = 0xf159, - language = 0xf1ab, - laptop = 0xf109, - lastfm = 0xf202, - lastfmsquare = 0xf203, - leaf = 0xf06c, - leanpub = 0xf212, - legal = 0xf0e3, - lemono = 0xf094, - leveldown = 0xf149, - levelup = 0xf148, - lifebouy = 0xf1cd, - lifebuoy = 0xf1cd, - lifering = 0xf1cd, - lifesaver = 0xf1cd, - lightbulbo = 0xf0eb, - linechart = 0xf201, - link = 0xf0c1, - linkedin = 0xf0e1, - linkedinsquare = 0xf08c, - linode = 0xf2b8, - fa_linux = 0xf17c, - list = 0xf03a, - listalt = 0xf022, - listol = 0xf0cb, - listul = 0xf0ca, - locationarrow = 0xf124, - lock = 0xf023, - longarrowdown = 0xf175, - longarrowleft = 0xf177, - longarrowright = 0xf178, - longarrowup = 0xf176, - lowvision = 0xf2a8, - magic = 0xf0d0, - magnet = 0xf076, - mailforward = 0xf064, - mailreply = 0xf112, - mailreplyall = 0xf122, - male = 0xf183, - map = 0xf279, - mapmarker = 0xf041, - mapo = 0xf278, - mappin = 0xf276, - mapsigns = 0xf277, - mars = 0xf222, - marsdouble = 0xf227, - marsstroke = 0xf229, - marsstrokeh = 0xf22b, - marsstrokev = 0xf22a, - maxcdn = 0xf136, - meanpath = 0xf20c, - medium = 0xf23a, - medkit = 0xf0fa, - meetup = 0xf2e0, - meho = 0xf11a, - mercury = 0xf223, - microchip = 0xf2db, - microphone = 0xf130, - microphoneslash = 0xf131, - minus = 0xf068, - minuscircle = 0xf056, - minussquare = 0xf146, - minussquareo = 0xf147, - mixcloud = 0xf289, - mobile = 0xf10b, - mobilephone = 0xf10b, - modx = 0xf285, - money = 0xf0d6, - moono = 0xf186, - mortarboard = 0xf19d, - motorcycle = 0xf21c, - mousepointer = 0xf245, - music = 0xf001, - navicon = 0xf0c9, - neuter = 0xf22c, - newspapero = 0xf1ea, - objectgroup = 0xf247, - objectungroup = 0xf248, - odnoklassniki = 0xf263, - odnoklassnikisquare = 0xf264, - opencart = 0xf23d, - openid = 0xf19b, - opera = 0xf26a, - optinmonster = 0xf23c, - outdent = 0xf03b, - pagelines = 0xf18c, - paintbrush = 0xf1fc, - paperplane = 0xf1d8, - paperplaneo = 0xf1d9, - paperclip = 0xf0c6, - paragraph = 0xf1dd, - paste = 0xf0ea, - pause = 0xf04c, - pausecircle = 0xf28b, - pausecircleo = 0xf28c, - paw = 0xf1b0, - paypal = 0xf1ed, - pencil = 0xf040, - pencilsquare = 0xf14b, - pencilsquareo = 0xf044, - percent = 0xf295, - phone = 0xf095, - phonesquare = 0xf098, - photo = 0xf03e, - pictureo = 0xf03e, - piechart = 0xf200, - piedpiper = 0xf2ae, - piedpiperalt = 0xf1a8, - piedpiperpp = 0xf1a7, - pinterest = 0xf0d2, - pinterestp = 0xf231, - pinterestsquare = 0xf0d3, - plane = 0xf072, - play = 0xf04b, - playcircle = 0xf144, - playcircleo = 0xf01d, - plug = 0xf1e6, - plus = 0xf067, - pluscircle = 0xf055, - plussquare = 0xf0fe, - plussquareo = 0xf196, - podcast = 0xf2ce, - poweroff = 0xf011, - print = 0xf02f, - producthunt = 0xf288, - puzzlepiece = 0xf12e, - qq = 0xf1d6, - qrcode = 0xf029, - question = 0xf128, - questioncircle = 0xf059, - questioncircleo = 0xf29c, - quora = 0xf2c4, - quoteleft = 0xf10d, - quoteright = 0xf10e, - ra = 0xf1d0, - random = 0xf074, - ravelry = 0xf2d9, - rebel = 0xf1d0, - recycle = 0xf1b8, - reddit = 0xf1a1, - redditalien = 0xf281, - redditsquare = 0xf1a2, - refresh = 0xf021, - registered = 0xf25d, - remove = 0xf00d, - renren = 0xf18b, - reorder = 0xf0c9, - repeat = 0xf01e, - reply = 0xf112, - replyall = 0xf122, - resistance = 0xf1d0, - retweet = 0xf079, - rmb = 0xf157, - road = 0xf018, - rocket = 0xf135, - rotateleft = 0xf0e2, - rotateright = 0xf01e, - rouble = 0xf158, - rss = 0xf09e, - rsssquare = 0xf143, - rub = 0xf158, - ruble = 0xf158, - rupee = 0xf156, - s15 = 0xf2cd, - safari = 0xf267, - save = 0xf0c7, - scissors = 0xf0c4, - scribd = 0xf28a, - search = 0xf002, - searchminus = 0xf010, - searchplus = 0xf00e, - sellsy = 0xf213, - send = 0xf1d8, - sendo = 0xf1d9, - server = 0xf233, - share = 0xf064, - sharealt = 0xf1e0, - sharealtsquare = 0xf1e1, - sharesquare = 0xf14d, - sharesquareo = 0xf045, - shekel = 0xf20b, - sheqel = 0xf20b, - shield = 0xf132, - ship = 0xf21a, - shirtsinbulk = 0xf214, - shoppingbag = 0xf290, - shoppingbasket = 0xf291, - shoppingcart = 0xf07a, - shower = 0xf2cc, - signin = 0xf090, - signlanguage = 0xf2a7, - signout = 0xf08b, - signal = 0xf012, - signing = 0xf2a7, - simplybuilt = 0xf215, - sitemap = 0xf0e8, - skyatlas = 0xf216, - skype = 0xf17e, - slack = 0xf198, - sliders = 0xf1de, - slideshare = 0xf1e7, - smileo = 0xf118, - snapchat = 0xf2ab, - snapchatghost = 0xf2ac, - snapchatsquare = 0xf2ad, - snowflakeo = 0xf2dc, - soccerballo = 0xf1e3, - sort = 0xf0dc, - sortalphaasc = 0xf15d, - sortalphadesc = 0xf15e, - sortamountasc = 0xf160, - sortamountdesc = 0xf161, - sortasc = 0xf0de, - sortdesc = 0xf0dd, - sortdown = 0xf0dd, - sortnumericasc = 0xf162, - sortnumericdesc = 0xf163, - sortup = 0xf0de, - soundcloud = 0xf1be, - spaceshuttle = 0xf197, - spinner = 0xf110, - spoon = 0xf1b1, - spotify = 0xf1bc, - square = 0xf0c8, - squareo = 0xf096, - stackexchange = 0xf18d, - stackoverflow = 0xf16c, - star = 0xf005, - starhalf = 0xf089, - starhalfempty = 0xf123, - starhalffull = 0xf123, - starhalfo = 0xf123, - staro = 0xf006, - steam = 0xf1b6, - steamsquare = 0xf1b7, - stepbackward = 0xf048, - stepforward = 0xf051, - stethoscope = 0xf0f1, - stickynote = 0xf249, - stickynoteo = 0xf24a, - stop = 0xf04d, - stopcircle = 0xf28d, - stopcircleo = 0xf28e, - streetview = 0xf21d, - strikethrough = 0xf0cc, - stumbleupon = 0xf1a4, - stumbleuponcircle = 0xf1a3, - subscript = 0xf12c, - subway = 0xf239, - suitcase = 0xf0f2, - suno = 0xf185, - superpowers = 0xf2dd, - superscript = 0xf12b, - support = 0xf1cd, - table = 0xf0ce, - tablet = 0xf10a, - tachometer = 0xf0e4, - tag = 0xf02b, - tags = 0xf02c, - tasks = 0xf0ae, - taxi = 0xf1ba, - telegram = 0xf2c6, - television = 0xf26c, - tencentweibo = 0xf1d5, - terminal = 0xf120, - textheight = 0xf034, - textwidth = 0xf035, - th = 0xf00a, - thlarge = 0xf009, - thlist = 0xf00b, - themeisle = 0xf2b2, - thermometer = 0xf2c7, - thermometer0 = 0xf2cb, - thermometer1 = 0xf2ca, - thermometer2 = 0xf2c9, - thermometer3 = 0xf2c8, - thermometer4 = 0xf2c7, - thermometerempty = 0xf2cb, - thermometerfull = 0xf2c7, - thermometerhalf = 0xf2c9, - thermometerquarter = 0xf2ca, - thermometerthreequarters = 0xf2c8, - thumbtack = 0xf08d, - thumbsdown = 0xf165, - thumbsodown = 0xf088, - thumbsoup = 0xf087, - thumbsup = 0xf164, - ticket = 0xf145, - times = 0xf00d, - timescircle = 0xf057, - timescircleo = 0xf05c, - timesrectangle = 0xf2d3, - timesrectangleo = 0xf2d4, - tint = 0xf043, - toggledown = 0xf150, - toggleleft = 0xf191, - toggleoff = 0xf204, - toggleon = 0xf205, - toggleright = 0xf152, - toggleup = 0xf151, - trademark = 0xf25c, - train = 0xf238, - transgender = 0xf224, - transgenderalt = 0xf225, - trash = 0xf1f8, - trasho = 0xf014, - tree = 0xf1bb, - trello = 0xf181, - tripadvisor = 0xf262, - trophy = 0xf091, - truck = 0xf0d1, - fa_try = 0xf195, - tty = 0xf1e4, - tumblr = 0xf173, - tumblrsquare = 0xf174, - turkishlira = 0xf195, - tv = 0xf26c, - twitch = 0xf1e8, - twitter = 0xf099, - twittersquare = 0xf081, - umbrella = 0xf0e9, - underline = 0xf0cd, - undo = 0xf0e2, - universalaccess = 0xf29a, - university = 0xf19c, - unlink = 0xf127, - unlock = 0xf09c, - unlockalt = 0xf13e, - unsorted = 0xf0dc, - upload = 0xf093, - usb = 0xf287, - usd = 0xf155, - user = 0xf007, - usercircle = 0xf2bd, - usercircleo = 0xf2be, - usermd = 0xf0f0, - usero = 0xf2c0, - userplus = 0xf234, - usersecret = 0xf21b, - usertimes = 0xf235, - users = 0xf0c0, - vcard = 0xf2bb, - vcardo = 0xf2bc, - venus = 0xf221, - venusdouble = 0xf226, - venusmars = 0xf228, - viacoin = 0xf237, - viadeo = 0xf2a9, - viadeosquare = 0xf2aa, - videocamera = 0xf03d, - vimeo = 0xf27d, - vimeosquare = 0xf194, - vine = 0xf1ca, - vk = 0xf189, - volumecontrolphone = 0xf2a0, - volumedown = 0xf027, - volumeoff = 0xf026, - volumeup = 0xf028, - warning = 0xf071, - wechat = 0xf1d7, - weibo = 0xf18a, - weixin = 0xf1d7, - whatsapp = 0xf232, - wheelchair = 0xf193, - wheelchairalt = 0xf29b, - wifi = 0xf1eb, - wikipediaw = 0xf266, - windowclose = 0xf2d3, - windowcloseo = 0xf2d4, - windowmaximize = 0xf2d0, - windowminimize = 0xf2d1, - windowrestore = 0xf2d2, - windows = 0xf17a, - won = 0xf159, - wordpress = 0xf19a, - wpbeginner = 0xf297, - wpexplorer = 0xf2de, - wpforms = 0xf298, - wrench = 0xf0ad, - xing = 0xf168, - xingsquare = 0xf169, - ycombinator = 0xf23b, - ycombinatorsquare = 0xf1d4, - yahoo = 0xf19e, - yc = 0xf23b, - ycsquare = 0xf1d4, - yelp = 0xf1e9, - yen = 0xf157, - yoast = 0xf2b1, - youtube = 0xf167, - youtubeplay = 0xf16a, - youtubesquare = 0xf166 - }; -} - - - //--------------------------------------------------------------------------------------- class QtAwesomeIconPainter; /// The main class for managing icons /// This class requires a 2-phase construction. You must first create the class and then initialize it via an init* method -class QtAwesome : public QObject -{ -Q_OBJECT - -public: +class QtAwesome : public QObject { + Q_OBJECT - explicit QtAwesome(QObject *parent = 0); + public: + explicit QtAwesome( QObject *parent = nullptr ); virtual ~QtAwesome(); - void init( const QString& fontname ); + void init( const QString &fontname ); bool initFontAwesome(); - void addNamedCodepoint( const QString& name, int codePoint ); - QHash namedCodePoints() { return namedCodepoints_; } + void addNamedCodepoint( const QString &name, int codePoint ); + QHash< QString, int > namedCodePoints() { return namedCodepoints_; } - void setDefaultOption( const QString& name, const QVariant& value ); - QVariant defaultOption( const QString& name ); + void setDefaultOption( const QString &name, const QVariant &value ); + QVariant defaultOption( const QString &name ); - QIcon icon( int character, const QVariantMap& options = QVariantMap() ); - QIcon icon( const QString& name, const QVariantMap& options = QVariantMap() ); - QIcon icon(QtAwesomeIconPainter* painter, const QVariantMap& optionMap = QVariantMap() ); + QIcon icon( int character, const QVariantMap &options = QVariantMap() ); + QIcon icon( const QString &name, const QVariantMap &options = QVariantMap() ); + QIcon icon( QtAwesomeIconPainter *painter, const QVariantMap &optionMap = QVariantMap() ); - void give( const QString& name, QtAwesomeIconPainter* painter ); + void give( const QString &name, QtAwesomeIconPainter *painter ); QFont font( int size ); /// Returns the font-name that is used as icon-map - QString fontName() { return fontName_ ; } + QString fontName() { return fontName_; } -private: - QString fontName_; ///< The font name used for this map - QHash namedCodepoints_; ///< A map with names mapped to code-points + private: + QString fontName_; ///< The font name used for this map + QHash< QString, int > namedCodepoints_; ///< A map with names mapped to code-points - QHash painterMap_; ///< A map of custom painters - QVariantMap defaultOptions_; ///< The default icon options - QtAwesomeIconPainter* fontIconPainter_; ///< A special painter fo painting codepoints + QHash< QString, QtAwesomeIconPainter * > painterMap_; ///< A map of custom painters + QVariantMap defaultOptions_; ///< The default icon options + QtAwesomeIconPainter *fontIconPainter_; ///< A special painter fo painting codepoints }; @@ -864,15 +67,856 @@ Q_OBJECT /// The QtAwesomeIconPainter is a specialized painter for painting icons /// your can implement an iconpainter to create custom font-icon code -class QtAwesomeIconPainter -{ -public: - virtual ~QtAwesomeIconPainter() {} - virtual void paint( QtAwesome* awesome, QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state, const QVariantMap& options ) = 0; +class QtAwesomeIconPainter { + public: + virtual ~QtAwesomeIconPainter(); + virtual void paint( QtAwesome *awesome, QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state, + const QVariantMap &options ) = 0; +}; + + +/// The font-awesome icon painter +class QtAwesomeCharIconPainter : public QtAwesomeIconPainter { + protected: + QStringList optionKeysForModeAndState( const QString &key, QIcon::Mode mode, QIcon::State state ); + + QVariant optionValueForModeAndState( const QString &baseKey, QIcon::Mode mode, QIcon::State state, const QVariantMap &options ); + + public: + virtual void paint( QtAwesome *awesome, QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state, + const QVariantMap &options ); +}; + + +/// The painter icon engine. +class QtAwesomeIconPainterIconEngine : public QIconEngine { + public: + QtAwesomeIconPainterIconEngine( QtAwesome *awesome, QtAwesomeIconPainter *painter, const QVariantMap &options ); + // : awesomeRef_( awesome ), iconPainterRef_( painter ), options_( options ) + + ~QtAwesomeIconPainterIconEngine() override; + + QtAwesomeIconPainterIconEngine *clone() const override; + + void paint( QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state ) override; + + QPixmap pixmap( const QSize &size, QIcon::Mode mode, QIcon::State state ) override; + +#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 6, 0 ) ) + QList< QSize > availableSizes( QIcon::Mode mode, QIcon::State state ) const override { + Q_UNUSED( mode ); + Q_UNUSED( state ); + QList< QSize > sizes = {QSize( 16, 16 ), QSize( 32, 32 ), QSize( 64, 64 ), + QSize( 128, 128 ), QSize( 256, 256 ), QSize( 512, 512 )}; + return sizes; + } +#endif + + private: + QtAwesome *awesomeRef_; ///< a reference to the QtAwesome instance + QtAwesomeIconPainter *iconPainterRef_; ///< a reference to the icon painter + QVariantMap options_; ///< the options for this icon painter }; -Q_DECLARE_METATYPE(QtAwesomeAnimation*) -extern QtAwesome* iconFont; +Q_DECLARE_METATYPE( QtAwesomeAnimation * ) + +extern QtAwesome *iconFont; + +//--------------------------------------------------------------------------------------- + +/// A list of all icon-names with the codepoint (unicode-value) on the right +/// You can use the names on the page http://fortawesome.github.io/Font-Awesome/design.html +namespace fa { +enum icon { + fa_500px = 0xf26e, + addressbook = 0xf2b9, + addressbooko = 0xf2ba, + addresscard = 0xf2bb, + addresscardo = 0xf2bc, + adjust = 0xf042, + adn = 0xf170, + aligncenter = 0xf037, + alignjustify = 0xf039, + alignleft = 0xf036, + alignright = 0xf038, + amazon = 0xf270, + ambulance = 0xf0f9, + americansignlanguageinterpreting = 0xf2a3, + anchor = 0xf13d, + android = 0xf17b, + angellist = 0xf209, + angledoubledown = 0xf103, + angledoubleleft = 0xf100, + angledoubleright = 0xf101, + angledoubleup = 0xf102, + angledown = 0xf107, + angleleft = 0xf104, + angleright = 0xf105, + angleup = 0xf106, + apple = 0xf179, + archive = 0xf187, + areachart = 0xf1fe, + arrowcircledown = 0xf0ab, + arrowcircleleft = 0xf0a8, + arrowcircleodown = 0xf01a, + arrowcircleoleft = 0xf190, + arrowcircleoright = 0xf18e, + arrowcircleoup = 0xf01b, + arrowcircleright = 0xf0a9, + arrowcircleup = 0xf0aa, + arrowdown = 0xf063, + arrowleft = 0xf060, + arrowright = 0xf061, + arrowup = 0xf062, + arrows = 0xf047, + arrowsalt = 0xf0b2, + arrowsh = 0xf07e, + arrowsv = 0xf07d, + aslinterpreting = 0xf2a3, + assistivelisteningsystems = 0xf2a2, + asterisk = 0xf069, + at = 0xf1fa, + audiodescription = 0xf29e, + automobile = 0xf1b9, + backward = 0xf04a, + balancescale = 0xf24e, + ban = 0xf05e, + bandcamp = 0xf2d5, + bank = 0xf19c, + barchart = 0xf080, + barcharto = 0xf080, + barcode = 0xf02a, + bars = 0xf0c9, + bath = 0xf2cd, + bathtub = 0xf2cd, + battery = 0xf240, + battery0 = 0xf244, + battery1 = 0xf243, + battery2 = 0xf242, + battery3 = 0xf241, + battery4 = 0xf240, + batteryempty = 0xf244, + batteryfull = 0xf240, + batteryhalf = 0xf242, + batteryquarter = 0xf243, + batterythreequarters = 0xf241, + bed = 0xf236, + beer = 0xf0fc, + behance = 0xf1b4, + behancesquare = 0xf1b5, + bell = 0xf0f3, + bello = 0xf0a2, + bellslash = 0xf1f6, + bellslasho = 0xf1f7, + bicycle = 0xf206, + binoculars = 0xf1e5, + birthdaycake = 0xf1fd, + bitbucket = 0xf171, + bitbucketsquare = 0xf172, + bitcoin = 0xf15a, + blacktie = 0xf27e, + blind = 0xf29d, + bluetooth = 0xf293, + bluetoothb = 0xf294, + bold = 0xf032, + bolt = 0xf0e7, + bomb = 0xf1e2, + book = 0xf02d, + bookmark = 0xf02e, + bookmarko = 0xf097, + braille = 0xf2a1, + briefcase = 0xf0b1, + btc = 0xf15a, + bug = 0xf188, + building = 0xf1ad, + buildingo = 0xf0f7, + bullhorn = 0xf0a1, + bullseye = 0xf140, + bus = 0xf207, + buysellads = 0xf20d, + cab = 0xf1ba, + calculator = 0xf1ec, + calendar = 0xf073, + calendarchecko = 0xf274, + calendarminuso = 0xf272, + calendaro = 0xf133, + calendarpluso = 0xf271, + calendartimeso = 0xf273, + camera = 0xf030, + cameraretro = 0xf083, + car = 0xf1b9, + caretdown = 0xf0d7, + caretleft = 0xf0d9, + caretright = 0xf0da, + caretsquareodown = 0xf150, + caretsquareoleft = 0xf191, + caretsquareoright = 0xf152, + caretsquareoup = 0xf151, + caretup = 0xf0d8, + cartarrowdown = 0xf218, + cartplus = 0xf217, + cc = 0xf20a, + ccamex = 0xf1f3, + ccdinersclub = 0xf24c, + ccdiscover = 0xf1f2, + ccjcb = 0xf24b, + ccmastercard = 0xf1f1, + ccpaypal = 0xf1f4, + ccstripe = 0xf1f5, + ccvisa = 0xf1f0, + certificate = 0xf0a3, + chain = 0xf0c1, + chainbroken = 0xf127, + check = 0xf00c, + checkcircle = 0xf058, + checkcircleo = 0xf05d, + checksquare = 0xf14a, + checksquareo = 0xf046, + chevroncircledown = 0xf13a, + chevroncircleleft = 0xf137, + chevroncircleright = 0xf138, + chevroncircleup = 0xf139, + chevrondown = 0xf078, + chevronleft = 0xf053, + chevronright = 0xf054, + chevronup = 0xf077, + child = 0xf1ae, + chrome = 0xf268, + circle = 0xf111, + circleo = 0xf10c, + circleonotch = 0xf1ce, + circlethin = 0xf1db, + clipboard = 0xf0ea, + clocko = 0xf017, + clone = 0xf24d, + close = 0xf00d, + cloud = 0xf0c2, + clouddownload = 0xf0ed, + cloudupload = 0xf0ee, + cny = 0xf157, + code = 0xf121, + codefork = 0xf126, + codepen = 0xf1cb, + codiepie = 0xf284, + coffee = 0xf0f4, + cog = 0xf013, + cogs = 0xf085, + columns = 0xf0db, + comment = 0xf075, + commento = 0xf0e5, + commenting = 0xf27a, + commentingo = 0xf27b, + comments = 0xf086, + commentso = 0xf0e6, + compass = 0xf14e, + compress = 0xf066, + connectdevelop = 0xf20e, + contao = 0xf26d, + copy = 0xf0c5, + copyright = 0xf1f9, + creativecommons = 0xf25e, + creditcard = 0xf09d, + creditcardalt = 0xf283, + crop = 0xf125, + crosshairs = 0xf05b, + css3 = 0xf13c, + cube = 0xf1b2, + cubes = 0xf1b3, + cut = 0xf0c4, + cutlery = 0xf0f5, + dashboard = 0xf0e4, + dashcube = 0xf210, + database = 0xf1c0, + deaf = 0xf2a4, + deafness = 0xf2a4, + dedent = 0xf03b, + delicious = 0xf1a5, + desktop = 0xf108, + deviantart = 0xf1bd, + diamond = 0xf219, + digg = 0xf1a6, + dollar = 0xf155, + dotcircleo = 0xf192, + download = 0xf019, + dribbble = 0xf17d, + driverslicense = 0xf2c2, + driverslicenseo = 0xf2c3, + dropbox = 0xf16b, + drupal = 0xf1a9, + edge = 0xf282, + edit = 0xf044, + eercast = 0xf2da, + eject = 0xf052, + ellipsish = 0xf141, + ellipsisv = 0xf142, + empire = 0xf1d1, + envelope = 0xf0e0, + envelopeo = 0xf003, + envelopeopen = 0xf2b6, + envelopeopeno = 0xf2b7, + envelopesquare = 0xf199, + envira = 0xf299, + eraser = 0xf12d, + etsy = 0xf2d7, + eur = 0xf153, + euro = 0xf153, + exchange = 0xf0ec, + exclamation = 0xf12a, + exclamationcircle = 0xf06a, + exclamationtriangle = 0xf071, + expand = 0xf065, + expeditedssl = 0xf23e, + externallink = 0xf08e, + externallinksquare = 0xf14c, + eye = 0xf06e, + eyeslash = 0xf070, + eyedropper = 0xf1fb, + fa = 0xf2b4, + facebook = 0xf09a, + facebookf = 0xf09a, + facebookofficial = 0xf230, + facebooksquare = 0xf082, + fastbackward = 0xf049, + fastforward = 0xf050, + fax = 0xf1ac, + feed = 0xf09e, + female = 0xf182, + fighterjet = 0xf0fb, + file = 0xf15b, + filearchiveo = 0xf1c6, + fileaudioo = 0xf1c7, + filecodeo = 0xf1c9, + fileexcelo = 0xf1c3, + fileimageo = 0xf1c5, + filemovieo = 0xf1c8, + fileo = 0xf016, + filepdfo = 0xf1c1, + filephotoo = 0xf1c5, + filepictureo = 0xf1c5, + filepowerpointo = 0xf1c4, + filesoundo = 0xf1c7, + filetext = 0xf15c, + filetexto = 0xf0f6, + filevideoo = 0xf1c8, + filewordo = 0xf1c2, + filezipo = 0xf1c6, + fileso = 0xf0c5, + film = 0xf008, + filter = 0xf0b0, + fire = 0xf06d, + fireextinguisher = 0xf134, + firefox = 0xf269, + firstorder = 0xf2b0, + flag = 0xf024, + flagcheckered = 0xf11e, + flago = 0xf11d, + flash = 0xf0e7, + flask = 0xf0c3, + flickr = 0xf16e, + floppyo = 0xf0c7, + folder = 0xf07b, + foldero = 0xf114, + folderopen = 0xf07c, + folderopeno = 0xf115, + font = 0xf031, + fontawesome = 0xf2b4, + fonticons = 0xf280, + fortawesome = 0xf286, + forumbee = 0xf211, + forward = 0xf04e, + foursquare = 0xf180, + freecodecamp = 0xf2c5, + frowno = 0xf119, + futbolo = 0xf1e3, + gamepad = 0xf11b, + gavel = 0xf0e3, + gbp = 0xf154, + ge = 0xf1d1, + gear = 0xf013, + gears = 0xf085, + genderless = 0xf22d, + getpocket = 0xf265, + gg = 0xf260, + ggcircle = 0xf261, + gift = 0xf06b, + git = 0xf1d3, + gitsquare = 0xf1d2, + github = 0xf09b, + githubalt = 0xf113, + githubsquare = 0xf092, + gitlab = 0xf296, + gittip = 0xf184, + glass = 0xf000, + glide = 0xf2a5, + glideg = 0xf2a6, + globe = 0xf0ac, + google = 0xf1a0, + googleplus = 0xf0d5, + googlepluscircle = 0xf2b3, + googleplusofficial = 0xf2b3, + googleplussquare = 0xf0d4, + googlewallet = 0xf1ee, + graduationcap = 0xf19d, + gratipay = 0xf184, + grav = 0xf2d6, + group = 0xf0c0, + hsquare = 0xf0fd, + hackernews = 0xf1d4, + handgrabo = 0xf255, + handlizardo = 0xf258, + handodown = 0xf0a7, + handoleft = 0xf0a5, + handoright = 0xf0a4, + handoup = 0xf0a6, + handpapero = 0xf256, + handpeaceo = 0xf25b, + handpointero = 0xf25a, + handrocko = 0xf255, + handscissorso = 0xf257, + handspocko = 0xf259, + handstopo = 0xf256, + handshakeo = 0xf2b5, + hardofhearing = 0xf2a4, + hashtag = 0xf292, + hddo = 0xf0a0, + header = 0xf1dc, + headphones = 0xf025, + heart = 0xf004, + hearto = 0xf08a, + heartbeat = 0xf21e, + history = 0xf1da, + home = 0xf015, + hospitalo = 0xf0f8, + hotel = 0xf236, + hourglass = 0xf254, + hourglass1 = 0xf251, + hourglass2 = 0xf252, + hourglass3 = 0xf253, + hourglassend = 0xf253, + hourglasshalf = 0xf252, + hourglasso = 0xf250, + hourglassstart = 0xf251, + houzz = 0xf27c, + html5 = 0xf13b, + icursor = 0xf246, + idbadge = 0xf2c1, + idcard = 0xf2c2, + idcardo = 0xf2c3, + ils = 0xf20b, + image = 0xf03e, + imdb = 0xf2d8, + inbox = 0xf01c, + indent = 0xf03c, + industry = 0xf275, + info = 0xf129, + infocircle = 0xf05a, + inr = 0xf156, + instagram = 0xf16d, + institution = 0xf19c, + internetexplorer = 0xf26b, + intersex = 0xf224, + ioxhost = 0xf208, + italic = 0xf033, + joomla = 0xf1aa, + jpy = 0xf157, + jsfiddle = 0xf1cc, + key = 0xf084, + keyboardo = 0xf11c, + krw = 0xf159, + language = 0xf1ab, + laptop = 0xf109, + lastfm = 0xf202, + lastfmsquare = 0xf203, + leaf = 0xf06c, + leanpub = 0xf212, + legal = 0xf0e3, + lemono = 0xf094, + leveldown = 0xf149, + levelup = 0xf148, + lifebouy = 0xf1cd, + lifebuoy = 0xf1cd, + lifering = 0xf1cd, + lifesaver = 0xf1cd, + lightbulbo = 0xf0eb, + linechart = 0xf201, + link = 0xf0c1, + linkedin = 0xf0e1, + linkedinsquare = 0xf08c, + linode = 0xf2b8, + fa_linux = 0xf17c, + list = 0xf03a, + listalt = 0xf022, + listol = 0xf0cb, + listul = 0xf0ca, + locationarrow = 0xf124, + lock = 0xf023, + longarrowdown = 0xf175, + longarrowleft = 0xf177, + longarrowright = 0xf178, + longarrowup = 0xf176, + lowvision = 0xf2a8, + magic = 0xf0d0, + magnet = 0xf076, + mailforward = 0xf064, + mailreply = 0xf112, + mailreplyall = 0xf122, + male = 0xf183, + map = 0xf279, + mapmarker = 0xf041, + mapo = 0xf278, + mappin = 0xf276, + mapsigns = 0xf277, + mars = 0xf222, + marsdouble = 0xf227, + marsstroke = 0xf229, + marsstrokeh = 0xf22b, + marsstrokev = 0xf22a, + maxcdn = 0xf136, + meanpath = 0xf20c, + medium = 0xf23a, + medkit = 0xf0fa, + meetup = 0xf2e0, + meho = 0xf11a, + mercury = 0xf223, + microchip = 0xf2db, + microphone = 0xf130, + microphoneslash = 0xf131, + minus = 0xf068, + minuscircle = 0xf056, + minussquare = 0xf146, + minussquareo = 0xf147, + mixcloud = 0xf289, + mobile = 0xf10b, + mobilephone = 0xf10b, + modx = 0xf285, + money = 0xf0d6, + moono = 0xf186, + mortarboard = 0xf19d, + motorcycle = 0xf21c, + mousepointer = 0xf245, + music = 0xf001, + navicon = 0xf0c9, + neuter = 0xf22c, + newspapero = 0xf1ea, + objectgroup = 0xf247, + objectungroup = 0xf248, + odnoklassniki = 0xf263, + odnoklassnikisquare = 0xf264, + opencart = 0xf23d, + openid = 0xf19b, + opera = 0xf26a, + optinmonster = 0xf23c, + outdent = 0xf03b, + pagelines = 0xf18c, + paintbrush = 0xf1fc, + paperplane = 0xf1d8, + paperplaneo = 0xf1d9, + paperclip = 0xf0c6, + paragraph = 0xf1dd, + paste = 0xf0ea, + pause = 0xf04c, + pausecircle = 0xf28b, + pausecircleo = 0xf28c, + paw = 0xf1b0, + paypal = 0xf1ed, + pencil = 0xf040, + pencilsquare = 0xf14b, + pencilsquareo = 0xf044, + percent = 0xf295, + phone = 0xf095, + phonesquare = 0xf098, + photo = 0xf03e, + pictureo = 0xf03e, + piechart = 0xf200, + piedpiper = 0xf2ae, + piedpiperalt = 0xf1a8, + piedpiperpp = 0xf1a7, + pinterest = 0xf0d2, + pinterestp = 0xf231, + pinterestsquare = 0xf0d3, + plane = 0xf072, + play = 0xf04b, + playcircle = 0xf144, + playcircleo = 0xf01d, + plug = 0xf1e6, + plus = 0xf067, + pluscircle = 0xf055, + plussquare = 0xf0fe, + plussquareo = 0xf196, + podcast = 0xf2ce, + poweroff = 0xf011, + print = 0xf02f, + producthunt = 0xf288, + puzzlepiece = 0xf12e, + qq = 0xf1d6, + qrcode = 0xf029, + question = 0xf128, + questioncircle = 0xf059, + questioncircleo = 0xf29c, + quora = 0xf2c4, + quoteleft = 0xf10d, + quoteright = 0xf10e, + ra = 0xf1d0, + random = 0xf074, + ravelry = 0xf2d9, + rebel = 0xf1d0, + recycle = 0xf1b8, + reddit = 0xf1a1, + redditalien = 0xf281, + redditsquare = 0xf1a2, + refresh = 0xf021, + registered = 0xf25d, + remove = 0xf00d, + renren = 0xf18b, + reorder = 0xf0c9, + repeat = 0xf01e, + reply = 0xf112, + replyall = 0xf122, + resistance = 0xf1d0, + retweet = 0xf079, + rmb = 0xf157, + road = 0xf018, + rocket = 0xf135, + rotateleft = 0xf0e2, + rotateright = 0xf01e, + rouble = 0xf158, + rss = 0xf09e, + rsssquare = 0xf143, + rub = 0xf158, + ruble = 0xf158, + rupee = 0xf156, + s15 = 0xf2cd, + safari = 0xf267, + save = 0xf0c7, + scissors = 0xf0c4, + scribd = 0xf28a, + search = 0xf002, + searchminus = 0xf010, + searchplus = 0xf00e, + sellsy = 0xf213, + send = 0xf1d8, + sendo = 0xf1d9, + server = 0xf233, + share = 0xf064, + sharealt = 0xf1e0, + sharealtsquare = 0xf1e1, + sharesquare = 0xf14d, + sharesquareo = 0xf045, + shekel = 0xf20b, + sheqel = 0xf20b, + shield = 0xf132, + ship = 0xf21a, + shirtsinbulk = 0xf214, + shoppingbag = 0xf290, + shoppingbasket = 0xf291, + shoppingcart = 0xf07a, + shower = 0xf2cc, + signin = 0xf090, + signlanguage = 0xf2a7, + signout = 0xf08b, + signal = 0xf012, + signing = 0xf2a7, + simplybuilt = 0xf215, + sitemap = 0xf0e8, + skyatlas = 0xf216, + skype = 0xf17e, + slack = 0xf198, + sliders = 0xf1de, + slideshare = 0xf1e7, + smileo = 0xf118, + snapchat = 0xf2ab, + snapchatghost = 0xf2ac, + snapchatsquare = 0xf2ad, + snowflakeo = 0xf2dc, + soccerballo = 0xf1e3, + sort = 0xf0dc, + sortalphaasc = 0xf15d, + sortalphadesc = 0xf15e, + sortamountasc = 0xf160, + sortamountdesc = 0xf161, + sortasc = 0xf0de, + sortdesc = 0xf0dd, + sortdown = 0xf0dd, + sortnumericasc = 0xf162, + sortnumericdesc = 0xf163, + sortup = 0xf0de, + soundcloud = 0xf1be, + spaceshuttle = 0xf197, + spinner = 0xf110, + spoon = 0xf1b1, + spotify = 0xf1bc, + square = 0xf0c8, + squareo = 0xf096, + stackexchange = 0xf18d, + stackoverflow = 0xf16c, + star = 0xf005, + starhalf = 0xf089, + starhalfempty = 0xf123, + starhalffull = 0xf123, + starhalfo = 0xf123, + staro = 0xf006, + steam = 0xf1b6, + steamsquare = 0xf1b7, + stepbackward = 0xf048, + stepforward = 0xf051, + stethoscope = 0xf0f1, + stickynote = 0xf249, + stickynoteo = 0xf24a, + stop = 0xf04d, + stopcircle = 0xf28d, + stopcircleo = 0xf28e, + streetview = 0xf21d, + strikethrough = 0xf0cc, + stumbleupon = 0xf1a4, + stumbleuponcircle = 0xf1a3, + subscript = 0xf12c, + subway = 0xf239, + suitcase = 0xf0f2, + suno = 0xf185, + superpowers = 0xf2dd, + superscript = 0xf12b, + support = 0xf1cd, + table = 0xf0ce, + tablet = 0xf10a, + tachometer = 0xf0e4, + tag = 0xf02b, + tags = 0xf02c, + tasks = 0xf0ae, + taxi = 0xf1ba, + telegram = 0xf2c6, + television = 0xf26c, + tencentweibo = 0xf1d5, + terminal = 0xf120, + textheight = 0xf034, + textwidth = 0xf035, + th = 0xf00a, + thlarge = 0xf009, + thlist = 0xf00b, + themeisle = 0xf2b2, + thermometer = 0xf2c7, + thermometer0 = 0xf2cb, + thermometer1 = 0xf2ca, + thermometer2 = 0xf2c9, + thermometer3 = 0xf2c8, + thermometer4 = 0xf2c7, + thermometerempty = 0xf2cb, + thermometerfull = 0xf2c7, + thermometerhalf = 0xf2c9, + thermometerquarter = 0xf2ca, + thermometerthreequarters = 0xf2c8, + thumbtack = 0xf08d, + thumbsdown = 0xf165, + thumbsodown = 0xf088, + thumbsoup = 0xf087, + thumbsup = 0xf164, + ticket = 0xf145, + times = 0xf00d, + timescircle = 0xf057, + timescircleo = 0xf05c, + timesrectangle = 0xf2d3, + timesrectangleo = 0xf2d4, + tint = 0xf043, + toggledown = 0xf150, + toggleleft = 0xf191, + toggleoff = 0xf204, + toggleon = 0xf205, + toggleright = 0xf152, + toggleup = 0xf151, + trademark = 0xf25c, + train = 0xf238, + transgender = 0xf224, + transgenderalt = 0xf225, + trash = 0xf1f8, + trasho = 0xf014, + tree = 0xf1bb, + trello = 0xf181, + tripadvisor = 0xf262, + trophy = 0xf091, + truck = 0xf0d1, + fa_try = 0xf195, + tty = 0xf1e4, + tumblr = 0xf173, + tumblrsquare = 0xf174, + turkishlira = 0xf195, + tv = 0xf26c, + twitch = 0xf1e8, + twitter = 0xf099, + twittersquare = 0xf081, + umbrella = 0xf0e9, + underline = 0xf0cd, + undo = 0xf0e2, + universalaccess = 0xf29a, + university = 0xf19c, + unlink = 0xf127, + unlock = 0xf09c, + unlockalt = 0xf13e, + unsorted = 0xf0dc, + upload = 0xf093, + usb = 0xf287, + usd = 0xf155, + user = 0xf007, + usercircle = 0xf2bd, + usercircleo = 0xf2be, + usermd = 0xf0f0, + usero = 0xf2c0, + userplus = 0xf234, + usersecret = 0xf21b, + usertimes = 0xf235, + users = 0xf0c0, + vcard = 0xf2bb, + vcardo = 0xf2bc, + venus = 0xf221, + venusdouble = 0xf226, + venusmars = 0xf228, + viacoin = 0xf237, + viadeo = 0xf2a9, + viadeosquare = 0xf2aa, + videocamera = 0xf03d, + vimeo = 0xf27d, + vimeosquare = 0xf194, + vine = 0xf1ca, + vk = 0xf189, + volumecontrolphone = 0xf2a0, + volumedown = 0xf027, + volumeoff = 0xf026, + volumeup = 0xf028, + warning = 0xf071, + wechat = 0xf1d7, + weibo = 0xf18a, + weixin = 0xf1d7, + whatsapp = 0xf232, + wheelchair = 0xf193, + wheelchairalt = 0xf29b, + wifi = 0xf1eb, + wikipediaw = 0xf266, + windowclose = 0xf2d3, + windowcloseo = 0xf2d4, + windowmaximize = 0xf2d0, + windowminimize = 0xf2d1, + windowrestore = 0xf2d2, + windows = 0xf17a, + won = 0xf159, + wordpress = 0xf19a, + wpbeginner = 0xf297, + wpexplorer = 0xf2de, + wpforms = 0xf298, + wrench = 0xf0ad, + xing = 0xf168, + xingsquare = 0xf169, + ycombinator = 0xf23b, + ycombinatorsquare = 0xf1d4, + yahoo = 0xf19e, + yc = 0xf23b, + ycsquare = 0xf1d4, + yelp = 0xf1e9, + yen = 0xf157, + yoast = 0xf2b1, + youtube = 0xf167, + youtubeplay = 0xf16a, + youtubesquare = 0xf166 +}; +} + #endif // QTAWESOME_H diff --git a/openhantek/src/iconfont/QtAwesomeAnim.cpp b/openhantek/src/iconfont/QtAwesomeAnim.cpp index 733df513..5e85e244 100644 --- a/openhantek/src/iconfont/QtAwesomeAnim.cpp +++ b/openhantek/src/iconfont/QtAwesomeAnim.cpp @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "QtAwesomeAnim.h" #include @@ -6,25 +8,25 @@ #include #include -QtAwesomeAnimation::QtAwesomeAnimation(QWidget *parentWidget, int interval, double step) - : parentWidgetRef_(parentWidget), timer_(0), interval_(interval), step_(step), angle_(0.0f) {} +QtAwesomeAnimation::QtAwesomeAnimation( QWidget *parentWidget, int interval, double step ) + : parentWidgetRef_( parentWidget ), timer_( nullptr ), interval_( interval ), step_( step ), angle_( 0.0 ) {} -void QtAwesomeAnimation::setup(QPainter &painter, const QRect &rect) { +void QtAwesomeAnimation::setup( QPainter &painter, const QRect &rect ) { // first time set the timer - if (!timer_) { + if ( !timer_ ) { timer_ = new QTimer(); - connect(timer_, SIGNAL(timeout()), this, SLOT(update())); - timer_->start(interval_); + connect( timer_, SIGNAL( timeout() ), this, SLOT( update() ) ); + timer_->start( interval_ ); } else { QPen pen = painter.pen(); - pen.setWidth(2); - pen.setColor(QColor(Qt::gray)); - painter.setPen(pen); - double val = 1 + sin(angle_) / 2; - if (val >= 0.5) - painter.drawArc(rect, 0 * 16, 16 * (360 - (val - 0.5) * 2 * 360)); + pen.setWidth( 2 ); + pen.setColor( QColor( Qt::gray ) ); + painter.setPen( pen ); + double val = 1 + sin( angle_ ) / 2; + if ( val >= 0.5 ) + painter.drawArc( rect, 0 * 16, int( 16 * ( 360 - ( val - 0.5 ) * 2 * 360 ) ) ); else - painter.drawArc(rect, 0 * 16, 16 * (val * 2) * 360); + painter.drawArc( rect, 0 * 16, int( 16 * ( val * 2 ) * 360 ) ); } } diff --git a/openhantek/src/iconfont/QtAwesomeAnim.h b/openhantek/src/iconfont/QtAwesomeAnim.h index 5192fc88..80006474 100644 --- a/openhantek/src/iconfont/QtAwesomeAnim.h +++ b/openhantek/src/iconfont/QtAwesomeAnim.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #ifndef QTAWESOMEANIMATION_H #define QTAWESOMEANIMATION_H @@ -11,25 +13,23 @@ class QWidget; /// /// Basic Animation Support for QtAwesome (Inspired by https://github.com/spyder-ide/qtawesome) /// -class QtAwesomeAnimation : public QObject -{ -Q_OBJECT +class QtAwesomeAnimation : public QObject { + Q_OBJECT -public: - QtAwesomeAnimation( QWidget* parentWidget, int interval=20, double step=0.01); + public: + QtAwesomeAnimation( QWidget *parentWidget, int interval = 20, double step = 0.01 ); - void setup( QPainter& painter, const QRect& rect ); + void setup( QPainter &painter, const QRect &rect ); -public slots: + public slots: void update(); -private: - QWidget* parentWidgetRef_; - QTimer* timer_; + private: + QWidget *parentWidgetRef_; + QTimer *timer_; int interval_; double step_; double angle_; - }; diff --git a/openhantek/src/main.cpp b/openhantek/src/main.cpp index f5f3b5a8..abeb4e64 100644 --- a/openhantek/src/main.cpp +++ b/openhantek/src/main.cpp @@ -7,19 +7,33 @@ #include #include #include - +#ifdef Q_OS_LINUX +#include +#include +#endif #include +#ifdef Q_OS_FREEBSD +#include +// FreeBSD doesn't have libusb_setlocale() +#define libusb_setlocale( x ) (void)0 +#else #include +#endif +#ifdef Q_OS_WIN +#include +#endif #include // Settings -#include "settings.h" +#include "dsosettings.h" #include "viewconstants.h" +#include "viewsettings.h" // DSO core logic +#include "capturing.h" #include "dsomodel.h" #include "hantekdsocontrol.h" -#include "usb/usbdevice.h" +#include "usb/scopedevice.h" // Post processing #include "post/graphgenerator.h" @@ -31,8 +45,11 @@ #include "exporting/exportcsv.h" #include "exporting/exporterprocessor.h" #include "exporting/exporterregistry.h" +// legacy img and pdf export is replaced by MainWindow::screenshot() +#ifdef LEGACYEXPORT #include "exporting/exportimage.h" #include "exporting/exportprint.h" +#endif // GUI #include "iconfont/QtAwesome.h" @@ -42,173 +59,258 @@ // OpenGL setup #include "glscope.h" +#include "models/modelDEMO.h" + #ifndef VERSION #error "You need to run the cmake buildsystem!" #endif +#include "OH_VERSION.h" -using namespace Hantek; -/// \brief Initialize the device with the current settings. -void applySettingsToDevice(HantekDsoControl *dsoControl, DsoSettingsScope *scope, - const Dso::ControlSpecification *spec) { - bool mathUsed = scope->anyUsed(spec->channels); - for (ChannelID channel = 0; channel < spec->channels; ++channel) { - dsoControl->setCoupling(channel, scope->coupling(channel, spec)); - dsoControl->setGain(channel, scope->gain(channel) * DIVS_VOLTAGE); - dsoControl->setOffset(channel, (scope->voltage[channel].offset / DIVS_VOLTAGE) + 0.5); - dsoControl->setTriggerLevel(channel, scope->voltage[channel].trigger); - dsoControl->setChannelUsed(channel, mathUsed | scope->anyUsed(channel)); - } +using namespace Hantek; - if (scope->horizontal.samplerateSource == DsoSettingsScopeHorizontal::Samplerrate) - dsoControl->setSamplerate(scope->horizontal.samplerate); - else - dsoControl->setRecordTime(scope->horizontal.timebase * DIVS_TIME); - - if (dsoControl->getAvailableRecordLengths().empty()) - dsoControl->setRecordLength(scope->horizontal.recordLength); - else { - auto recLenVec = dsoControl->getAvailableRecordLengths(); - ptrdiff_t index = std::distance(recLenVec.begin(), - std::find(recLenVec.begin(), recLenVec.end(), scope->horizontal.recordLength)); - dsoControl->setRecordLength(index < 0 ? 1 : (unsigned)index); - } - dsoControl->setTriggerMode(scope->trigger.mode); - dsoControl->setPretriggerPosition(scope->trigger.position * scope->horizontal.timebase * DIVS_TIME); - dsoControl->setTriggerSlope(scope->trigger.slope); - dsoControl->setTriggerSource(scope->trigger.special, scope->trigger.source); -} /// \brief Initialize resources and translations and show the main window. -int main(int argc, char *argv[]) { +int main( int argc, char *argv[] ) { //////// Set application information //////// - QCoreApplication::setOrganizationName("OpenHantek"); - QCoreApplication::setOrganizationDomain("www.openhantek.org"); - QCoreApplication::setApplicationName("OpenHantek"); - QCoreApplication::setApplicationVersion(VERSION); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + QCoreApplication::setOrganizationName( "OpenHantek" ); + QCoreApplication::setOrganizationDomain( "openhantek.org" ); + QCoreApplication::setApplicationName( "OpenHantek6022" ); + QCoreApplication::setApplicationVersion( VERSION ); + QCoreApplication::setAttribute( Qt::AA_UseHighDpiPixmaps, true ); +#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 6, 0 ) ) + QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling, true ); +#endif + +#ifdef Q_OS_WIN + // close "extra" console window but if started from cmd.exe use this console + if ( FreeConsole() && AttachConsole( ATTACH_PARENT_PROCESS ) ) { + freopen( "CONOUT$", "w", stdout ); + freopen( "CONOUT$", "w", stderr ); + } #endif - bool useGles = false; + bool demoMode = false; + bool useGLES = false; + bool useGLSL120 = false; + bool useGLSL150 = false; + bool useLocale = true; + int fontSize = 0; { - QCoreApplication parserApp(argc, argv); + QCoreApplication parserApp( argc, argv ); QCommandLineParser p; p.addHelpOption(); p.addVersionOption(); - QCommandLineOption useGlesOption("useGLES", QCoreApplication::tr("Use OpenGL ES instead of OpenGL")); - p.addOption(useGlesOption); - p.process(parserApp); - useGles = p.isSet(useGlesOption); + QCommandLineOption demoModeOption( {"d", "demoMode"}, "Demo mode without scope HW" ); + p.addOption( demoModeOption ); + QCommandLineOption useGlesOption( {"e", "useGLES"}, "Use OpenGL ES instead of OpenGL" ); + p.addOption( useGlesOption ); + QCommandLineOption useGLSL120Option( "useGLSL120", "Force OpenGL SL version 1.20" ); + p.addOption( useGLSL120Option ); + QCommandLineOption useGLSL150Option( "useGLSL150", "Force OpenGL SL version 1.50" ); + p.addOption( useGLSL150Option ); + QCommandLineOption intOption( {"i", "international"}, "Show the international interface, do not translate" ); + p.addOption( intOption ); + QCommandLineOption fontsizeOption( {"f", "fontsize"}, "Set a different font size", "fontsize" ); + p.addOption( fontsizeOption ); + p.process( parserApp ); + demoMode = p.isSet( demoModeOption ); + useGLES = p.isSet( useGlesOption ); + fontSize = p.value( "fontsize" ).toInt(); + useGLSL120 = p.isSet( useGLSL120Option ); + useGLSL150 = p.isSet( useGLSL150Option ); + useLocale = !p.isSet( intOption ); } - GlScope::fixOpenGLversion(useGles ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL); +#ifdef Q_PROCESSOR_ARM + // HACK: Raspberry Pi crashes with OpenGL, use always OpenGLES + useGLES = true; +#endif + + GlScope::useQSurfaceFormat( useGLES ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL ); + if ( useGLSL120 ) + GlScope::useOpenGLSLversion( 120 ); + else if ( useGLSL150 ) + GlScope::useOpenGLSLversion( 150 ); + QApplication openHantekApplication( argc, argv ); + +#ifdef Q_OS_LINUX + // Qt5 linux default + // ("Breeze", "Windows", "Fusion") + // with package qt5-style-plugins + // ("Breeze", "bb10dark", "bb10bright", "cleanlooks", "gtk2", "cde", "motif", "plastique", "Windows", "Fusion") + openHantekApplication.setStyle( QStyleFactory::create( "Fusion" ) ); // smaller widgets allow stacking of all docks +#endif - QApplication openHantekApplication(argc, argv); +#ifdef __linux_rt__ + // try to set realtime priority to improve USB allocation + // this works if the user is member of a realtime group, e.g. audio: + // 1. set limits in /etc/security/limits.d: + // @audio - rtprio 99 + // 2. add user to the group, e.g. audio: + // usermod -a -G audio + // or set the limits only for your user in /etc/security/limits.d: + // - rtprio 99 + struct sched_param schedParam; + schedParam.sched_priority = 49; // set RT priority level 50 + sched_setscheduler( 0, SCHED_FIFO, &schedParam ); // and RT FIFO scheduler + // but ignore any error if user has no realtime rights +#endif //////// Load translations //////// QTranslator qtTranslator; - if (qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - openHantekApplication.installTranslator(&qtTranslator); - QTranslator openHantekTranslator; - if (openHantekTranslator.load(QLocale(), QLatin1String("openhantek"), QLatin1String("_"), - QLatin1String(":/translations"))) { - openHantekApplication.installTranslator(&openHantekTranslator); + if ( useLocale && QLocale::system().name() != "en_US" ) { // somehow Qt on MacOS uses the german translation for en_US?! + if ( qtTranslator.load( "qt_" + QLocale::system().name(), QLibraryInfo::location( QLibraryInfo::TranslationsPath ) ) ) { + openHantekApplication.installTranslator( &qtTranslator ); + } + if ( openHantekTranslator.load( QLocale(), QLatin1String( "openhantek" ), QLatin1String( "_" ), + QLatin1String( ":/translations" ) ) ) { + openHantekApplication.installTranslator( &openHantekTranslator ); + } + } + + if ( fontSize ) { + QFont f = openHantekApplication.font(); + f.setPointSize( fontSize ); + openHantekApplication.setFont( f ); + openHantekApplication.setFont( f, "QWidget" ); // on some systems the 2nd argument is required } //////// Find matching usb devices //////// libusb_context *context = nullptr; - int error = libusb_init(&context); - if (error) { - SelectSupportedDevice().showLibUSBFailedDialogModel(error); - return -1; - } - std::unique_ptr device = SelectSupportedDevice().showSelectDeviceModal(context); - QString errorMessage; - if (device == nullptr || !device->connectDevice(errorMessage)) { - libusb_exit(context); - return -1; + std::unique_ptr< ScopeDevice > scopeDevice = nullptr; + + if ( !demoMode ) { + int error = libusb_init( &context ); + if ( error ) { + SelectSupportedDevice().showLibUSBFailedDialogModel( error ); + return -1; + } + if ( useLocale ) // localize USB error messages, supported: "en", "nl", "fr", "ru" + libusb_setlocale( QLocale::system().name().toLocal8Bit().constData() ); + + // SelectSupportedDevive returns a real device unless demoMode is true + scopeDevice = SelectSupportedDevice().showSelectDeviceModal( context ); + if ( scopeDevice && scopeDevice->isDemoDevice() ) { + demoMode = true; + libusb_exit( context ); // stop all USB activities + context = nullptr; + } else { + QString errorMessage; + if ( scopeDevice == nullptr || !scopeDevice->connectDevice( errorMessage ) ) { + libusb_exit( context ); // clean USB + if ( !errorMessage.isEmpty() ) + qCritical() << errorMessage; + return -1; + } + } + } else { + scopeDevice = std::unique_ptr< ScopeDevice >( new ScopeDevice() ); } + // Here we have either a connected scope device or a demo device w/o hardware + const DSOModel *model = scopeDevice->getModel(); + //////// Create DSO control object and move it to a separate thread //////// QThread dsoControlThread; - dsoControlThread.setObjectName("dsoControlThread"); - HantekDsoControl dsoControl(device.get()); - dsoControl.moveToThread(&dsoControlThread); - QObject::connect(&dsoControlThread, &QThread::started, &dsoControl, &HantekDsoControl::run); - QObject::connect(&dsoControl, &HantekDsoControl::communicationError, QCoreApplication::instance(), - &QCoreApplication::quit); - QObject::connect(device.get(), &USBDevice::deviceDisconnected, QCoreApplication::instance(), - &QCoreApplication::quit); + dsoControlThread.setObjectName( "dsoControlThread" ); + HantekDsoControl dsoControl( scopeDevice ? scopeDevice.get() : nullptr, model ); + dsoControl.moveToThread( &dsoControlThread ); + QObject::connect( &dsoControlThread, &QThread::started, &dsoControl, &HantekDsoControl::stateMachine ); + QObject::connect( &dsoControl, &HantekDsoControl::communicationError, QCoreApplication::instance(), &QCoreApplication::quit ); + + if ( scopeDevice ) + QObject::connect( scopeDevice.get(), &ScopeDevice::deviceDisconnected, QCoreApplication::instance(), + &QCoreApplication::quit ); + + const Dso::ControlSpecification *spec = model->spec(); //////// Create settings object //////// - DsoSettings settings(device->getModel()->spec()); + DsoSettings settings( spec ); //////// Create exporters //////// - ExporterRegistry exportRegistry(device->getModel()->spec(), &settings); - + ExporterRegistry exportRegistry( spec, &settings ); ExporterCSV exporterCSV; - ExporterImage exportImage; - ExporterPrint exportPrint; - - ExporterProcessor samplesToExportRaw(&exportRegistry); - - exportRegistry.registerExporter(&exporterCSV); - exportRegistry.registerExporter(&exportImage); - exportRegistry.registerExporter(&exportPrint); + ExporterProcessor samplesToExportRaw( &exportRegistry ); + exportRegistry.registerExporter( &exporterCSV ); //////// Create post processing objects //////// QThread postProcessingThread; - postProcessingThread.setObjectName("postProcessingThread"); - PostProcessing postProcessing(settings.scope.countChannels()); + postProcessingThread.setObjectName( "postProcessingThread" ); + PostProcessing postProcessing( settings.scope.countChannels() ); - SpectrumGenerator spectrumGenerator(&settings.scope, &settings.post); - MathChannelGenerator mathchannelGenerator(&settings.scope, device->getModel()->spec()->channels); - GraphGenerator graphGenerator(&settings.scope, device->getModel()->spec()->isSoftwareTriggerDevice); + SpectrumGenerator spectrumGenerator( &settings.scope, &settings.post ); + MathChannelGenerator mathchannelGenerator( &settings.scope, spec->channels ); + GraphGenerator graphGenerator( &settings.scope ); - postProcessing.registerProcessor(&samplesToExportRaw); - postProcessing.registerProcessor(&mathchannelGenerator); - postProcessing.registerProcessor(&spectrumGenerator); - postProcessing.registerProcessor(&graphGenerator); + postProcessing.registerProcessor( &samplesToExportRaw ); + postProcessing.registerProcessor( &mathchannelGenerator ); + postProcessing.registerProcessor( &spectrumGenerator ); + postProcessing.registerProcessor( &graphGenerator ); - postProcessing.moveToThread(&postProcessingThread); - QObject::connect(&dsoControl, &HantekDsoControl::samplesAvailable, &postProcessing, &PostProcessing::input); - QObject::connect(&postProcessing, &PostProcessing::processingFinished, &exportRegistry, &ExporterRegistry::input, - Qt::DirectConnection); + postProcessing.moveToThread( &postProcessingThread ); + QObject::connect( &dsoControl, &HantekDsoControl::samplesAvailable, &postProcessing, &PostProcessing::input ); + QObject::connect( &postProcessing, &PostProcessing::processingFinished, &exportRegistry, &ExporterRegistry::input, + Qt::DirectConnection ); //////// Create main window //////// iconFont->initFontAwesome(); - MainWindow openHantekMainWindow(&dsoControl, &settings, &exportRegistry); - QObject::connect(&postProcessing, &PostProcessing::processingFinished, &openHantekMainWindow, - &MainWindow::showNewData); - QObject::connect(&exportRegistry, &ExporterRegistry::exporterProgressChanged, &openHantekMainWindow, - &MainWindow::exporterProgressChanged); - QObject::connect(&exportRegistry, &ExporterRegistry::exporterStatusChanged, &openHantekMainWindow, - &MainWindow::exporterStatusChanged); + MainWindow openHantekMainWindow( &dsoControl, &settings, &exportRegistry ); + QObject::connect( &postProcessing, &PostProcessing::processingFinished, &openHantekMainWindow, &MainWindow::showNewData ); + QObject::connect( &exportRegistry, &ExporterRegistry::exporterProgressChanged, &openHantekMainWindow, + &MainWindow::exporterProgressChanged ); + QObject::connect( &exportRegistry, &ExporterRegistry::exporterStatusChanged, &openHantekMainWindow, + &MainWindow::exporterStatusChanged ); openHantekMainWindow.show(); - applySettingsToDevice(&dsoControl, &settings.scope, device->getModel()->spec()); - //////// Start DSO thread and go into GUI main loop - dsoControl.enableSampling(true); + dsoControl.enableSampling( true ); postProcessingThread.start(); dsoControlThread.start(); - int res = openHantekApplication.exec(); + Capturing capturing( &dsoControl ); + capturing.start(); + + int appStatus = openHantekApplication.exec(); + + //////// Application closed, clean up step by step //////// + + std::cout << std::unitbuf; // enable automatic flushing + std::cout << "OpenHantek6022 "; - //////// Clean up //////// + dsoControl.quitSampling(); // send USB control command, stop bulk transfer + + // stop the capturing thread + unsigned waitForCapturing = unsigned( 2000 * dsoControl.getSamplesize() / dsoControl.getSamplerate() ); + if ( waitForCapturing < 10000 ) // minimum 10 s + waitForCapturing = 10000; + capturing.requestInterruption(); + capturing.wait( waitForCapturing ); + + std::cout << "has "; + + // now quit the data acquisition thread + // wait 2 * record time (delay is ms) for dso to finish + unsigned waitForDso = unsigned( 2000 * dsoControl.getSamplesize() / dsoControl.getSamplerate() ); + if ( waitForDso < 10000 ) // minimum 10 s + waitForDso = 10000; dsoControlThread.quit(); - dsoControlThread.wait(10000); + dsoControlThread.wait( waitForDso ); + std::cout << "stopped "; + // next stop the data processing + postProcessing.stop(); postProcessingThread.quit(); - postProcessingThread.wait(10000); + postProcessingThread.wait( 10000 ); + std::cout << "after "; - if (context && device != nullptr) { - device.reset(); // causes libusb_close(), which must be called before libusb_exit() - libusb_exit(context); - } + // finally shut down the libUSB communication + if ( scopeDevice ) + scopeDevice.reset(); // destroys unique_pointer, causes libusb_close(), must be called before libusb_exit() + if ( context ) + libusb_exit( context ); + std::cout << openHantekMainWindow.elapsedTime.elapsed() / 1000 << " s\n"; - return res; + return appStatus; } diff --git a/openhantek/src/mainwindow.cpp b/openhantek/src/mainwindow.cpp index 411195b0..ade202f2 100644 --- a/openhantek/src/mainwindow.cpp +++ b/openhantek/src/mainwindow.cpp @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "mainwindow.h" #include "iconfont/QtAwesome.h" #include "ui_mainwindow.h" @@ -15,312 +17,513 @@ #include "exporting/exporterinterface.h" #include "exporting/exporterregistry.h" #include "hantekdsocontrol.h" -#include "usb/usbdevice.h" +#include "usb/scopedevice.h" #include "viewconstants.h" -#include "settings.h" +#include "dsosettings.h" +#include #include -#include +#include #include - -MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, ExporterRegistry *exporterRegistry, - QWidget *parent) - : QMainWindow(parent), ui(new Ui::MainWindow), mSettings(settings), exporterRegistry(exporterRegistry) { - ui->setupUi(this); - ui->actionSave->setIcon(iconFont->icon(fa::save)); - ui->actionAbout->setIcon(iconFont->icon(fa::questioncircle)); - ui->actionOpen->setIcon(iconFont->icon(fa::folderopen)); - ui->actionSampling->setIcon(iconFont->icon(fa::pause, - {std::make_pair("text-selected-off", QChar(fa::play)), - std::make_pair("text-off", QChar(fa::play)), - std::make_pair("text-active-off", QChar(fa::play))})); - ui->actionSettings->setIcon(iconFont->icon(fa::gear)); - ui->actionManualCommand->setIcon(iconFont->icon(fa::edit)); - ui->actionDigital_phosphor->setIcon(QIcon(":/images/digitalphosphor.svg")); - ui->actionZoom->setIcon(iconFont->icon(fa::crop)); - ui->actionCursors->setIcon(iconFont->icon(fa::crosshairs)); +#include +#include +#include + +#include "OH_VERSION.h" + +MainWindow::MainWindow( HantekDsoControl *dsoControl, DsoSettings *settings, ExporterRegistry *exporterRegistry, QWidget *parent ) + : QMainWindow( parent ), ui( new Ui::MainWindow ), dsoSettings( settings ), exporterRegistry( exporterRegistry ) { + // suppress nasty warnings, e.g. "kf5.kio.core: Invalid URL ..." or "qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow) ..." + QLoggingCategory::setFilterRules( "kf5.kio.core=false\nqt.qpa.xcb=false" ); + QVariantMap colorMap; + QString iconPath = QString( ":/images/" ); + if ( QPalette().color( QPalette::Window ).lightness() < 128 ) { // automatic light/dark icon switch + iconPath += "darktheme/"; // select top window icons accordingly + colorMap.insert( "color-off", QColor( 208, 208, 208 ) ); // light grey normal + colorMap.insert( "color-active", QColor( 255, 255, 255 ) ); // white when selected + } + elapsedTime.start(); + ui->setupUi( this ); + iconPause = QIcon( iconPath + "pause.svg" ); + iconPlay = QIcon( iconPath + "play.svg" ); + ui->actionSampling->setIcon( iconPause ); + QList< QKeySequence > shortcuts; // provide multiple shortcuts for ui->actionSampling. + // 1st entry in list is shown as shortcut in menu +#ifdef Q_OS_WIN + shortcuts << QKeySequence( Qt::Key::Key_S ); // WIN: can be grabbed by buttons, e.g. CH1 +#endif + shortcuts << QKeySequence( Qt::Key::Key_Space ); + shortcuts << QKeySequence( Qt::Key::Key_Pause ); +#ifndef Q_OS_WIN + shortcuts << QKeySequence( Qt::Key::Key_S ); // else put this shortcut at the end of the list +#endif + ui->actionSampling->setShortcuts( shortcuts ); + ui->actionRefresh->setIcon( QIcon( iconPath + "refresh.svg" ) ); + ui->actionRefresh->setShortcut( Qt::Key::Key_R ); + ui->actionPhosphor->setIcon( QIcon( iconPath + "phosphor.svg" ) ); + ui->actionPhosphor->setShortcut( Qt::Key::Key_P ); + ui->actionHistogram->setIcon( QIcon( iconPath + "histogram.svg" ) ); + ui->actionHistogram->setShortcut( Qt::Key::Key_H ); + ui->actionZoom->setIcon( QIcon( iconPath + "zoom.svg" ) ); + ui->actionZoom->setShortcut( Qt::Key::Key_Z ); + ui->actionMeasure->setIcon( QIcon( iconPath + "measure.svg" ) ); + ui->actionMeasure->setShortcut( Qt::Key::Key_M ); + ui->actionOpen->setIcon( iconFont->icon( fa::folderopen, colorMap ) ); + ui->actionSave->setIcon( iconFont->icon( fa::save, colorMap ) ); + ui->actionSettings->setIcon( iconFont->icon( fa::gear, colorMap ) ); + ui->actionManualCommand->setIcon( iconFont->icon( fa::edit, colorMap ) ); + ui->actionUserManual->setIcon( iconFont->icon( fa::filepdfo, colorMap ) ); + ui->actionACmodification->setIcon( iconFont->icon( fa::filepdfo, colorMap ) ); + ui->actionAbout->setIcon( iconFont->icon( fa::questioncircle, colorMap ) ); // Window title - setWindowIcon(QIcon(":openhantek.png")); - setWindowTitle( - tr("OpenHantek - Device %1 - Renderer %2") - .arg(QString::fromStdString(dsoControl->getDevice()->getModel()->name)) - .arg(QSurfaceFormat::defaultFormat().renderableType() == QSurfaceFormat::OpenGL ? "OpenGL" : "OpenGL ES")); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - setDockOptions(dockOptions() | QMainWindow::GroupedDragging); + setWindowIcon( QIcon( ":/images/OpenHantek.svg" ) ); + setWindowTitle( dsoControl->getDevice()->isRealHW() + ? tr( "OpenHantek6022 (%1) - Device %2 (FW%3)" ) + .arg( QString::fromStdString( VERSION ), QString::fromStdString( dsoControl->getModel()->name ) ) + .arg( dsoControl->getDevice()->getFwVersion(), 4, 16, QChar( '0' ) ) + : tr( "OpenHantek6022 (%1) - " ).arg( QString::fromStdString( VERSION ) ) + tr( "Demo Mode" ) ); + +#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 6, 0 ) ) + setDockOptions( dockOptions() | QMainWindow::GroupedDragging ); #endif - - for (auto *exporter : *exporterRegistry) { - QAction *action = new QAction(exporter->icon(), exporter->name(), this); - action->setCheckable(exporter->type() == ExporterInterface::Type::ContinousExport); - connect(action, &QAction::triggered, [exporter, exporterRegistry](bool checked) { - exporterRegistry->setExporterEnabled( - exporter, exporter->type() == ExporterInterface::Type::ContinousExport ? checked : true); - }); - ui->menuExport->addAction(action); + QAction *action; + action = new QAction( iconFont->icon( fa::camera, colorMap ), tr( "Screenshot .." ), this ); + action->setToolTip( "Make a screenshot of the program window" ); + connect( action, &QAction::triggered, [this]() { screenShot( SCREENSHOT ); } ); + ui->menuExport->addAction( action ); + + action = new QAction( iconFont->icon( fa::camera, colorMap ), tr( "Hardcopy .." ), this ); + action->setToolTip( "Make a (printable) hardcopy of the display" ); + connect( action, &QAction::triggered, [this]() { + dsoWidget->switchToPrintColors(); + QTimer::singleShot( 20, [this]() { screenShot( HARDCOPY ); } ); + } ); + ui->menuExport->addAction( action ); + + action = new QAction( iconFont->icon( fa::print, colorMap ), tr( "Print screen .." ), this ); + action->setToolTip( "Send the hardcopy to the printer" ); + connect( action, &QAction::triggered, [this]() { + dsoWidget->switchToPrintColors(); + QTimer::singleShot( 20, [this]() { screenShot( PRINTER ); } ); + } ); + ui->menuExport->addAction( action ); + + ui->menuExport->addSeparator(); + + for ( auto *exporter : *exporterRegistry ) { + action = new QAction( iconFont->icon( exporter->faIcon(), colorMap ), exporter->name(), this ); + action->setCheckable( exporter->type() == ExporterInterface::Type::ContinousExport ); + connect( action, &QAction::triggered, [exporter, exporterRegistry]( bool checked ) { + exporterRegistry->setExporterEnabled( exporter, + exporter->type() == ExporterInterface::Type::ContinousExport ? checked : true ); + } ); + ui->menuExport->addAction( action ); } - DsoSettingsScope *scope = &(mSettings->scope); - const Dso::ControlSpecification *spec = dsoControl->getDevice()->getModel()->spec(); + DsoSettingsScope *scope = &( dsoSettings->scope ); + const Dso::ControlSpecification *spec = dsoControl->getModel()->spec(); registerDockMetaTypes(); // Docking windows // Create dock windows before the dso widget, they fix messed up settings - HorizontalDock *horizontalDock; - TriggerDock *triggerDock; - SpectrumDock *spectrumDock; - VoltageDock *voltageDock; - horizontalDock = new HorizontalDock(scope, this); - triggerDock = new TriggerDock(scope, spec, this); - spectrumDock = new SpectrumDock(scope, this); - voltageDock = new VoltageDock(scope, spec, this); - - addDockWidget(Qt::RightDockWidgetArea, horizontalDock); - addDockWidget(Qt::RightDockWidgetArea, triggerDock); - addDockWidget(Qt::RightDockWidgetArea, voltageDock); - addDockWidget(Qt::RightDockWidgetArea, spectrumDock); - - restoreGeometry(mSettings->mainWindowGeometry); - restoreState(mSettings->mainWindowState); + + VoltageDock *voltageDock = new VoltageDock( scope, spec, this ); + HorizontalDock *horizontalDock = new HorizontalDock( scope, spec, this ); + TriggerDock *triggerDock = new TriggerDock( scope, spec, this ); + SpectrumDock *spectrumDock = new SpectrumDock( scope, this ); + + addDockWidget( Qt::RightDockWidgetArea, voltageDock ); + addDockWidget( Qt::RightDockWidgetArea, horizontalDock ); + addDockWidget( Qt::RightDockWidgetArea, triggerDock ); + addDockWidget( Qt::RightDockWidgetArea, spectrumDock ); + + restoreGeometry( dsoSettings->mainWindowGeometry ); + restoreState( dsoSettings->mainWindowState ); // Central oszilloscope widget - dsoWidget = new DsoWidget(&mSettings->scope, &mSettings->view, spec); - setCentralWidget(dsoWidget); + dsoWidget = new DsoWidget( &dsoSettings->scope, &dsoSettings->view, spec ); + setCentralWidget( dsoWidget ); // Command field inside the status bar - QLineEdit *commandEdit = new QLineEdit(this); + commandEdit = new QLineEdit( this ); commandEdit->hide(); - statusBar()->addPermanentWidget(commandEdit, 1); + statusBar()->addPermanentWidget( commandEdit, 1 ); - connect(ui->actionManualCommand, &QAction::toggled, [this, commandEdit](bool checked) { - commandEdit->setVisible(checked); - if (checked) commandEdit->setFocus(); - }); + connect( ui->actionManualCommand, &QAction::toggled, [this]( bool checked ) { + commandEdit->setVisible( checked ); + if ( checked ) + commandEdit->setFocus(); + } ); - connect(commandEdit, &QLineEdit::returnPressed, [this, commandEdit, dsoControl]() { - Dso::ErrorCode errorCode = dsoControl->stringCommand(commandEdit->text()); + connect( commandEdit, &QLineEdit::returnPressed, [this, dsoControl]() { + Dso::ErrorCode errorCode = dsoControl->stringCommand( commandEdit->text() ); commandEdit->clear(); - this->ui->actionManualCommand->setChecked(false); - if (errorCode != Dso::ErrorCode::NONE) statusBar()->showMessage(tr("Invalid command"), 3000); - }); + this->ui->actionManualCommand->setChecked( false ); + if ( errorCode != Dso::ErrorCode::NONE ) + statusBar()->showMessage( tr( "Invalid command" ), 3000 ); + } ); // Connect general signals - connect(dsoControl, &HantekDsoControl::statusMessage, statusBar(), &QStatusBar::showMessage); + connect( dsoControl, &HantekDsoControl::statusMessage, statusBar(), &QStatusBar::showMessage ); // Connect signals to DSO controller and widget - connect(horizontalDock, &HorizontalDock::samplerateChanged, [dsoControl, this]() { - dsoControl->setSamplerate(mSettings->scope.horizontal.samplerate); - this->dsoWidget->updateSamplerate(mSettings->scope.horizontal.samplerate); - }); - connect(horizontalDock, &HorizontalDock::timebaseChanged, [dsoControl, this]() { - dsoControl->setRecordTime(mSettings->scope.horizontal.timebase * DIVS_TIME); - this->dsoWidget->updateTimebase(mSettings->scope.horizontal.timebase); - }); - connect(horizontalDock, &HorizontalDock::frequencybaseChanged, dsoWidget, &DsoWidget::updateFrequencybase); - connect(horizontalDock, &HorizontalDock::recordLengthChanged, - [dsoControl](unsigned long recordLength) { dsoControl->setRecordLength(recordLength); }); - - connect(dsoControl, &HantekDsoControl::recordTimeChanged, - [this, settings, horizontalDock, dsoControl](double duration) { - if (settings->scope.horizontal.samplerateSource == DsoSettingsScopeHorizontal::Samplerrate && - settings->scope.horizontal.recordLength != UINT_MAX) { - // The samplerate was set, let's adapt the timebase accordingly - settings->scope.horizontal.timebase = horizontalDock->setTimebase(duration / DIVS_TIME); - } - - // The trigger position should be kept at the same place but the timebase has - // changed - dsoControl->setPretriggerPosition(settings->scope.trigger.position * - settings->scope.horizontal.timebase * DIVS_TIME); - - this->dsoWidget->updateTimebase(settings->scope.horizontal.timebase); - }); - connect(dsoControl, &HantekDsoControl::samplerateChanged, [this, horizontalDock](double samplerate) { - if (mSettings->scope.horizontal.samplerateSource == DsoSettingsScopeHorizontal::Duration && - mSettings->scope.horizontal.recordLength != UINT_MAX) { - // The timebase was set, let's adapt the samplerate accordingly - mSettings->scope.horizontal.samplerate = samplerate; - horizontalDock->setSamplerate(samplerate); - dsoWidget->updateSamplerate(samplerate); - } - }); - - connect(triggerDock, &TriggerDock::modeChanged, dsoControl, &HantekDsoControl::setTriggerMode); - connect(triggerDock, &TriggerDock::modeChanged, dsoWidget, &DsoWidget::updateTriggerMode); - connect(triggerDock, &TriggerDock::sourceChanged, dsoControl, &HantekDsoControl::setTriggerSource); - connect(triggerDock, &TriggerDock::sourceChanged, dsoWidget, &DsoWidget::updateTriggerSource); - connect(triggerDock, &TriggerDock::slopeChanged, dsoControl, &HantekDsoControl::setTriggerSlope); - connect(triggerDock, &TriggerDock::slopeChanged, dsoWidget, &DsoWidget::updateTriggerSlope); - connect(dsoWidget, &DsoWidget::triggerPositionChanged, dsoControl, &HantekDsoControl::setPretriggerPosition); - connect(dsoWidget, &DsoWidget::triggerLevelChanged, dsoControl, &HantekDsoControl::setTriggerLevel); - - auto usedChanged = [this, dsoControl, spec](ChannelID channel, bool used) { - if (channel >= (unsigned int)mSettings->scope.voltage.size()) return; - -// if (!used) dsoWidget-> - bool mathUsed = mSettings->scope.anyUsed(spec->channels); + connect( horizontalDock, &HorizontalDock::samplerateChanged, [dsoControl, this]() { + dsoControl->setSamplerate( dsoSettings->scope.horizontal.samplerate ); + this->dsoWidget->updateSamplerate( dsoSettings->scope.horizontal.samplerate ); + } ); + connect( horizontalDock, &HorizontalDock::timebaseChanged, [dsoControl, this]() { + dsoControl->setRecordTime( dsoSettings->scope.horizontal.timebase * DIVS_TIME ); + this->dsoWidget->updateTimebase( dsoSettings->scope.horizontal.timebase ); + } ); + connect( spectrumDock, &SpectrumDock::frequencybaseChanged, + [this]( double frequencybase ) { this->dsoWidget->updateFrequencybase( frequencybase ); } ); + connect( dsoControl, &HantekDsoControl::samplerateChanged, [this, horizontalDock, spectrumDock]( double samplerate ) { + // The timebase was set, let's adapt the samplerate accordingly + // printf( "mainwindow::samplerateChanged( %g )\n", samplerate ); + dsoSettings->scope.horizontal.samplerate = samplerate; + horizontalDock->setSamplerate( samplerate ); + spectrumDock->setSamplerate( samplerate ); // mind the Nyquest frequency + dsoWidget->updateSamplerate( samplerate ); + } ); + connect( horizontalDock, &HorizontalDock::calfreqChanged, + [dsoControl, this]() { dsoControl->setCalFreq( dsoSettings->scope.horizontal.calfreq ); } ); + connect( horizontalDock, &HorizontalDock::formatChanged, + [this]( Dso::GraphFormat format ) { ui->actionHistogram->setEnabled( format == Dso::GraphFormat::TY ); } ); + + connect( triggerDock, &TriggerDock::modeChanged, dsoControl, &HantekDsoControl::setTriggerMode ); + connect( triggerDock, &TriggerDock::modeChanged, dsoWidget, &DsoWidget::updateTriggerMode ); + connect( triggerDock, &TriggerDock::modeChanged, [this]( Dso::TriggerMode mode ) { + ui->actionRefresh->setEnabled( Dso::TriggerMode::ROLL == mode && dsoSettings->scope.horizontal.samplerate < 10e3 ); + } ); + connect( dsoControl, &HantekDsoControl::samplerateChanged, [this]( double samplerate ) { + ui->actionRefresh->setEnabled( Dso::TriggerMode::ROLL == dsoSettings->scope.trigger.mode && samplerate < 10e3 ); + } ); + connect( triggerDock, &TriggerDock::sourceChanged, dsoControl, &HantekDsoControl::setTriggerSource ); + connect( triggerDock, &TriggerDock::sourceChanged, dsoWidget, &DsoWidget::updateTriggerSource ); + connect( triggerDock, &TriggerDock::slopeChanged, dsoControl, &HantekDsoControl::setTriggerSlope ); + connect( triggerDock, &TriggerDock::slopeChanged, dsoWidget, &DsoWidget::updateTriggerSlope ); + connect( dsoWidget, &DsoWidget::triggerPositionChanged, dsoControl, &HantekDsoControl::setTriggerOffset ); + connect( dsoWidget, &DsoWidget::triggerLevelChanged, dsoControl, &HantekDsoControl::setTriggerLevel ); + + auto usedChanged = [this, dsoControl, spec]( ChannelID channel ) { + if ( channel >= dsoSettings->scope.voltage.size() ) + return; + + bool mathUsed = dsoSettings->scope.anyUsed( spec->channels ); // Normal channel, check if voltage/spectrum or math channel is used - if (channel < spec->channels) dsoControl->setChannelUsed(channel, mathUsed | mSettings->scope.anyUsed(channel)); + if ( channel < spec->channels ) + dsoControl->setChannelUsed( channel, mathUsed | dsoSettings->scope.anyUsed( channel ) ); // Math channel, update all channels - else if (channel == spec->channels) { - for (ChannelID c = 0; c < spec->channels; ++c) - dsoControl->setChannelUsed(c, mathUsed | mSettings->scope.anyUsed(c)); + else if ( channel == spec->channels ) { + for ( ChannelID c = 0; c < spec->channels; ++c ) + dsoControl->setChannelUsed( c, mathUsed | dsoSettings->scope.anyUsed( c ) ); } }; - connect(voltageDock, &VoltageDock::usedChanged, usedChanged); - connect(spectrumDock, &SpectrumDock::usedChanged, usedChanged); - - connect(voltageDock, &VoltageDock::couplingChanged, dsoControl, &HantekDsoControl::setCoupling); - connect(voltageDock, &VoltageDock::couplingChanged, dsoWidget, &DsoWidget::updateVoltageCoupling); - connect(voltageDock, &VoltageDock::modeChanged, dsoWidget, &DsoWidget::updateMathMode); - connect(voltageDock, &VoltageDock::gainChanged, [this, dsoControl, spec](ChannelID channel, double gain) { - if (channel >= spec->channels) return; - - dsoControl->setGain(channel, mSettings->scope.gain(channel) * DIVS_VOLTAGE); - }); - connect(voltageDock, &VoltageDock::gainChanged, dsoWidget, &DsoWidget::updateVoltageGain); - connect(dsoWidget, &DsoWidget::offsetChanged, [this, dsoControl, spec](ChannelID channel) { - if (channel >= spec->channels) return; - dsoControl->setOffset(channel, (mSettings->scope.voltage[channel].offset / DIVS_VOLTAGE) + 0.5); - }); - - connect(voltageDock, &VoltageDock::usedChanged, dsoWidget, &DsoWidget::updateVoltageUsed); - connect(spectrumDock, &SpectrumDock::usedChanged, dsoWidget, &DsoWidget::updateSpectrumUsed); - connect(spectrumDock, &SpectrumDock::magnitudeChanged, dsoWidget, &DsoWidget::updateSpectrumMagnitude); + connect( voltageDock, &VoltageDock::usedChanged, usedChanged ); + connect( spectrumDock, &SpectrumDock::usedChanged, usedChanged ); + + connect( voltageDock, &VoltageDock::modeChanged, dsoWidget, &DsoWidget::updateMathMode ); + connect( voltageDock, &VoltageDock::gainChanged, [dsoControl, spec]( ChannelID channel, double gain ) { + if ( channel >= spec->channels ) + return; + dsoControl->setGain( channel, gain ); + } ); + connect( voltageDock, &VoltageDock::probeAttnChanged, [dsoControl, spec]( ChannelID channel, double probeAttn ) { + if ( channel >= spec->channels ) + return; + dsoControl->setProbe( channel, probeAttn ); + } ); + connect( voltageDock, &VoltageDock::invertedChanged, [dsoControl, spec]( ChannelID channel, bool inverted ) { + if ( channel >= spec->channels ) + return; + dsoControl->setChannelInverted( channel, inverted ); + } ); + connect( voltageDock, &VoltageDock::couplingChanged, dsoWidget, &DsoWidget::updateVoltageCoupling ); + connect( voltageDock, &VoltageDock::couplingChanged, [dsoControl, spec]( ChannelID channel, Dso::Coupling coupling ) { + if ( channel >= spec->channels ) + return; + dsoControl->setCoupling( channel, coupling ); + } ); + connect( voltageDock, &VoltageDock::gainChanged, dsoWidget, &DsoWidget::updateVoltageGain ); + connect( voltageDock, &VoltageDock::usedChanged, dsoWidget, &DsoWidget::updateVoltageUsed ); + connect( spectrumDock, &SpectrumDock::usedChanged, dsoWidget, &DsoWidget::updateSpectrumUsed ); + connect( spectrumDock, &SpectrumDock::magnitudeChanged, dsoWidget, &DsoWidget::updateSpectrumMagnitude ); // Started/stopped signals from oscilloscope - connect(dsoControl, &HantekDsoControl::samplingStatusChanged, [this, dsoControl](bool enabled) { - QSignalBlocker blocker(this->ui->actionSampling); - if (enabled) { - this->ui->actionSampling->setText(tr("&Stop")); - this->ui->actionSampling->setStatusTip(tr("Stop the oscilloscope")); + connect( dsoControl, &HantekDsoControl::samplingStatusChanged, [this]( bool enabled ) { + QSignalBlocker blocker( this->ui->actionSampling ); + if ( enabled ) { + this->ui->actionSampling->setIcon( this->iconPause ); + this->ui->actionSampling->setText( tr( "Stop" ) ); + this->ui->actionSampling->setStatusTip( tr( "Stop the oscilloscope" ) ); } else { - this->ui->actionSampling->setText(tr("&Start")); - this->ui->actionSampling->setStatusTip(tr("Start the oscilloscope")); + this->ui->actionSampling->setIcon( this->iconPlay ); + this->ui->actionSampling->setText( tr( "Start" ) ); + this->ui->actionSampling->setStatusTip( tr( "Start the oscilloscope" ) ); } - this->ui->actionSampling->setChecked(enabled); - }); - connect(this->ui->actionSampling, &QAction::triggered, dsoControl, &HantekDsoControl::enableSampling); - this->ui->actionSampling->setChecked(dsoControl->isSampling()); - - connect(dsoControl, &HantekDsoControl::availableRecordLengthsChanged, horizontalDock, - &HorizontalDock::setAvailableRecordLengths); - connect(dsoControl, &HantekDsoControl::samplerateLimitsChanged, horizontalDock, - &HorizontalDock::setSamplerateLimits); - connect(dsoControl, &HantekDsoControl::samplerateSet, horizontalDock, &HorizontalDock::setSamplerateSteps); - - connect(ui->actionOpen, &QAction::triggered, [this]() { - QString fileName = QFileDialog::getOpenFileName(this, tr("Open file"), "", tr("Settings (*.ini)")); - if (!fileName.isEmpty()) { - if (mSettings->setFilename(fileName)) { mSettings->load(); } + this->ui->actionSampling->setChecked( enabled ); + } ); + connect( this->ui->actionSampling, &QAction::triggered, dsoControl, &HantekDsoControl::enableSampling ); + this->ui->actionSampling->setChecked( dsoControl->isSampling() ); + + connect( this->ui->actionRefresh, &QAction::triggered, dsoControl, &HantekDsoControl::restartSampling ); + + connect( dsoControl, &HantekDsoControl::samplerateLimitsChanged, horizontalDock, &HorizontalDock::setSamplerateLimits ); + connect( dsoControl, &HantekDsoControl::samplerateSet, horizontalDock, &HorizontalDock::setSamplerateSteps ); + + // Load settings to GUI + connect( this, &MainWindow::settingsLoaded, voltageDock, &VoltageDock::loadSettings ); + connect( this, &MainWindow::settingsLoaded, horizontalDock, &HorizontalDock::loadSettings ); + connect( this, &MainWindow::settingsLoaded, spectrumDock, &SpectrumDock::loadSettings ); + connect( this, &MainWindow::settingsLoaded, triggerDock, &TriggerDock::loadSettings ); + connect( this, &MainWindow::settingsLoaded, dsoControl, &HantekDsoControl::applySettings ); + connect( this, &MainWindow::settingsLoaded, dsoWidget, &DsoWidget::updateSlidersSettings ); + + connect( ui->actionOpen, &QAction::triggered, [this, spec]() { + QString fileName = QFileDialog::getOpenFileName( this, tr( "Open file" ), "", tr( "Settings (*.conf)" ) ); + if ( !fileName.isEmpty() ) { + if ( dsoSettings->setFilename( fileName ) ) { + dsoSettings->load(); + } + emit settingsLoaded( &dsoSettings->scope, spec ); + + dsoWidget->updateTimebase( dsoSettings->scope.horizontal.timebase ); + + for ( ChannelID channel = 0; channel < spec->channels; ++channel ) { + this->dsoWidget->updateVoltageUsed( channel, dsoSettings->scope.voltage[ channel ].used ); + this->dsoWidget->updateSpectrumUsed( channel, dsoSettings->scope.spectrum[ channel ].used ); + } } - }); - - connect(ui->actionSave, &QAction::triggered, [this]() { - mSettings->mainWindowGeometry = saveGeometry(); - mSettings->mainWindowState = saveState(); - mSettings->save(); - }); - - connect(ui->actionSave_as, &QAction::triggered, [this]() { - QString fileName = QFileDialog::getSaveFileName(this, tr("Save settings"), "", tr("Settings (*.ini)")); - if (fileName.isEmpty()) return; - mSettings->mainWindowGeometry = saveGeometry(); - mSettings->mainWindowState = saveState(); - mSettings->setFilename(fileName); - mSettings->save(); - }); - - connect(ui->actionExit, &QAction::triggered, this, &QWidget::close); - - connect(ui->actionSettings, &QAction::triggered, [this]() { - mSettings->mainWindowGeometry = saveGeometry(); - mSettings->mainWindowState = saveState(); - - DsoConfigDialog *configDialog = new DsoConfigDialog(this->mSettings, this); - configDialog->setModal(true); + } ); + + connect( ui->actionSave, &QAction::triggered, [this]() { + dsoSettings->mainWindowGeometry = saveGeometry(); + dsoSettings->mainWindowState = saveState(); + dsoSettings->save(); + } ); + + connect( ui->actionSave_as, &QAction::triggered, [this]() { + QString fileName = QFileDialog::getSaveFileName( this, tr( "Save settings" ), "", tr( "Settings (*.conf)" ) ); + if ( fileName.isEmpty() ) + return; + if ( !fileName.endsWith( ".conf" ) ) + fileName.append( ".conf" ); + dsoSettings->mainWindowGeometry = saveGeometry(); + dsoSettings->mainWindowState = saveState(); + dsoSettings->setFilename( fileName ); + dsoSettings->save(); + } ); + + connect( ui->actionExit, &QAction::triggered, this, &QWidget::close ); + + connect( ui->actionSettings, &QAction::triggered, [this]() { + dsoSettings->mainWindowGeometry = saveGeometry(); + dsoSettings->mainWindowState = saveState(); + + DsoConfigDialog *configDialog = new DsoConfigDialog( this->dsoSettings, this ); + configDialog->setModal( true ); configDialog->show(); - }); + } ); + + connect( this->ui->actionPhosphor, &QAction::toggled, [this]( bool enabled ) { + dsoSettings->view.digitalPhosphor = enabled; + + if ( dsoSettings->view.digitalPhosphor ) + this->ui->actionPhosphor->setStatusTip( tr( "Disable fading of previous graphs" ) ); + else + this->ui->actionPhosphor->setStatusTip( tr( "Enable fading of previous graphs" ) ); + } ); + this->ui->actionPhosphor->setChecked( dsoSettings->view.digitalPhosphor ); - connect(this->ui->actionDigital_phosphor, &QAction::toggled, [this](bool enabled) { - mSettings->view.digitalPhosphor = enabled; + connect( ui->actionHistogram, &QAction::toggled, [this]( bool enabled ) { + dsoSettings->scope.histogram = enabled; - if (mSettings->view.digitalPhosphor) - this->ui->actionDigital_phosphor->setStatusTip(tr("Disable fading of previous graphs")); + if ( dsoSettings->scope.histogram ) + this->ui->actionHistogram->setStatusTip( tr( "Hide histogram" ) ); else - this->ui->actionDigital_phosphor->setStatusTip(tr("Enable fading of previous graphs")); - }); - this->ui->actionDigital_phosphor->setChecked(mSettings->view.digitalPhosphor); + this->ui->actionHistogram->setStatusTip( tr( "Show histogram" ) ); + } ); + ui->actionHistogram->setChecked( dsoSettings->scope.histogram ); + ui->actionHistogram->setEnabled( scope->horizontal.format == Dso::GraphFormat::TY ); - connect(ui->actionZoom, &QAction::toggled, [this](bool enabled) { - mSettings->view.zoom = enabled; + connect( ui->actionZoom, &QAction::toggled, [this]( bool enabled ) { + dsoSettings->view.zoom = enabled; - if (mSettings->view.zoom) - this->ui->actionZoom->setStatusTip(tr("Hide magnified scope")); + if ( dsoSettings->view.zoom ) + this->ui->actionZoom->setStatusTip( tr( "Hide magnified scope" ) ); else - this->ui->actionZoom->setStatusTip(tr("Show magnified scope")); + this->ui->actionZoom->setStatusTip( tr( "Show magnified scope" ) ); - this->dsoWidget->updateZoom(enabled); - }); - ui->actionZoom->setChecked(mSettings->view.zoom); + this->dsoWidget->updateZoom( enabled ); + } ); + ui->actionZoom->setChecked( dsoSettings->view.zoom ); - connect(ui->actionCursors, &QAction::toggled, [this](bool enabled) { - mSettings->view.cursorsVisible = enabled; + connect( ui->actionMeasure, &QAction::toggled, [this]( bool enabled ) { + dsoSettings->view.cursorsVisible = enabled; - if (mSettings->view.cursorsVisible) - this->ui->actionCursors->setStatusTip(tr("Hide measurements")); + if ( dsoSettings->view.cursorsVisible ) + this->ui->actionMeasure->setStatusTip( tr( "Hide measurements" ) ); else - this->ui->actionCursors->setStatusTip(tr("Show measurements")); + this->ui->actionMeasure->setStatusTip( tr( "Show measurements" ) ); + + this->dsoWidget->updateCursorGrid( enabled ); + } ); + ui->actionMeasure->setChecked( dsoSettings->view.cursorsVisible ); - this->dsoWidget->updateCursorGrid(enabled); - }); - ui->actionCursors->setChecked(mSettings->view.cursorsVisible); + connect( ui->actionUserManual, &QAction::triggered, []() { + QString usrManualPath( USR_MANUAL_PATH ); + QFile userManual( usrManualPath ); + if ( userManual.exists() ) + QDesktopServices::openUrl( QUrl( "file://" + usrManualPath ) ); + else + QDesktopServices::openUrl( + QUrl( "https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf" ) ); + } ); + + connect( ui->actionACmodification, &QAction::triggered, []() { + QString ACmodPath( AC_MODIFICATION_PATH ); + QFile ACmod( ACmodPath ); + if ( ACmod.exists() ) + QDesktopServices::openUrl( QUrl( "file://" + ACmodPath ) ); + else + QDesktopServices::openUrl( + QUrl( "https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/HANTEK6022_AC_Modification.pdf" ) ); + } ); - connect(ui->actionAbout, &QAction::triggered, [this]() { + connect( ui->actionAbout, &QAction::triggered, [this]() { QMessageBox::about( - this, tr("About OpenHantek %1").arg(VERSION), - tr("

This is a open source software for Hantek USB oscilloscopes.

" - "

Copyright © 2010, 2011 Oliver Haag
oliver.haag@gmail.com

" - "

Copyright © 2012-2017 OpenHantek community
" - "https://github.com/OpenHantek/openhantek

")); - - }); - - if (mSettings->scope.horizontal.samplerateSource == DsoSettingsScopeHorizontal::Samplerrate) - dsoWidget->updateSamplerate(mSettings->scope.horizontal.samplerate); - else - dsoWidget->updateTimebase(mSettings->scope.horizontal.timebase); - - for (ChannelID channel = 0; channel < spec->channels; ++channel) { - this->dsoWidget->updateVoltageUsed(channel, mSettings->scope.voltage[channel].used); - this->dsoWidget->updateSpectrumUsed(channel, mSettings->scope.spectrum[channel].used); + this, tr( "About OpenHantek6022 (%1)" ).arg( VERSION ), + tr( "

Open source software for Hantek6022 USB oscilloscopes

" + "

Maintainer: Martin Homuth-Rosemann

" + "

Copyright © 2010, 2011 Oliver Haag

" + "

Copyright © 2012-2020 OpenHantek community
" + "https://github.com/OpenHantek

" + "

Open source firmware copyright © 2019-2020 Ho-Ro
" + "https://github.com/Ho-Ro/Hantek6022API

" ) + + tr( "

Running since %1 seconds.

" ).arg( elapsedTime.elapsed() / 1000 ) ); + } ); + + emit settingsLoaded( &dsoSettings->scope, spec ); + + dsoWidget->updateTimebase( dsoSettings->scope.horizontal.timebase ); + + for ( ChannelID channel = 0; channel < spec->channels; ++channel ) { + this->dsoWidget->updateVoltageUsed( channel, dsoSettings->scope.voltage[ channel ].used ); + this->dsoWidget->updateSpectrumUsed( channel, dsoSettings->scope.spectrum[ channel ].used ); } } MainWindow::~MainWindow() { delete ui; } -void MainWindow::showNewData(std::shared_ptr data) { dsoWidget->showNew(data); } +void MainWindow::showNewData( std::shared_ptr< PPresult > newData ) { dsoWidget->showNew( newData ); } -void MainWindow::exporterStatusChanged(const QString &exporterName, const QString &status) { - ui->statusbar->showMessage(tr("%1: %2").arg(exporterName).arg(status)); +void MainWindow::exporterStatusChanged( const QString &exporterName, const QString &status ) { + ui->statusbar->showMessage( tr( "%1: %2" ).arg( exporterName, status ) ); } void MainWindow::exporterProgressChanged() { exporterRegistry->checkForWaitingExporters(); } +// make screenshot (type == SCREENSHOT) from the complete program window with screen colors ... +// ... or a printable hardcopy (type == HARDCOPY) from the scope widget only ... +// ... with printer colors and scaled to double height for zoomed screens ... +// ... or send this hardcopy directly to the printer (type == PRINTER). +void MainWindow::screenShot( screenshotType_t screenshotType ) { + auto activeWindow = screenshotType == SCREENSHOT ? qApp->activeWindow() : dsoWidget; + QPixmap screenshot( activeWindow->size() ); + QDateTime now = QDateTime::currentDateTime(); + QString docName = now.toString( tr( "yyyy-MM-dd hh:mm:ss" ) ); + QString fileName = now.toString( tr( "yyyyMMdd_hhmmss" ) ); + commandEdit->setText( docName ); // show date in bottom line + commandEdit->setVisible( true ); + activeWindow->render( &screenshot ); // take the screenshot + commandEdit->clear(); + commandEdit->setVisible( false ); + dsoWidget->restoreScreenColors(); + + int sw = screenshot.width(); + int sh = screenshot.height(); + if ( screenshotType != SCREENSHOT && dsoSettings->view.zoom && dsoSettings->view.zoomImage ) { + screenshot = screenshot.scaled( sw, sh *= 2 ); // make double height + } + + // here we have a screeshot, now handle the different destinations. + QPrinter printer( QPrinter::HighResolution ); +#if ( QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 ) ) + printer.setPdfVersion( QPrinter::PdfVersion_A1b ); +#endif + printer.setPaperSize( QPrinter::A4 ); + printer.setPageMargins( 20, 20, 20, 20, QPrinter::Millimeter ); + printer.setOrientation( sw > sh ? QPrinter::Landscape : QPrinter::Portrait ); + printer.setCreator( QCoreApplication::applicationName() ); + printer.setDocName( docName ); + + if ( screenshotType != PRINTER ) { // ask for a filename + QStringList filters; + fileName += ".png"; + filters << tr( "Image (*.png *.jpg)" ) << tr( "Portable Document Format (*.pdf)" ); + QFileDialog fileDialog( this, tr( "Save screenshot" ), fileName, filters.join( ";;" ) ); + fileDialog.setAcceptMode( QFileDialog::AcceptSave ); + if ( fileDialog.exec() != QDialog::Accepted ) + return; + + fileName = fileDialog.selectedFiles().first(); + if ( filters.indexOf( fileDialog.selectedNameFilter() ) == 0 ) { // save as image + screenshot.save( fileName ); + return; + } + + // else create a *.pdf with a scaled and centered image + printer.setOutputFormat( QPrinter::PdfFormat ); + printer.setOutputFileName( fileName ); + // supports screen resolution up to about 9600 x 9600 pixel + int resolution = 75; + printer.setResolution( resolution ); + int pw = printer.pageRect().width(); + int ph = printer.pageRect().height(); + int scale = qMin( pw / sw, ph / sh ); + while ( scale < 2 && resolution < 1200 ) { + resolution *= 2; + printer.setResolution( resolution ); + pw = printer.pageRect().width(); + ph = printer.pageRect().height(); + scale = qMin( pw / sw, ph / sh ); + } + } else { // Show the printing dialog + printer.setDocName( fileName + ".pdf" ); + QPrintDialog dialog( &printer ); + dialog.setWindowTitle( tr( "Print oscillograph" ) ); + if ( dialog.exec() != QDialog::Accepted ) { + return; + } + } + // send the pixmap to *.pdf or printer + int pw = printer.pageRect().width(); + int ph = printer.pageRect().height(); + int scale = qMin( pw / sw, ph / sh ); + + // qDebug() << sw << sh << pw << ph << scale << resolution; + if ( scale < 1 ) + qDebug() << "Screenshot size too big, page will be cropped"; + else if ( scale > 1 ) // upscale accordingly + screenshot = screenshot.scaled( sw *= scale, sh *= scale ); + printer.newPage(); + QPainter p( &printer ); + p.drawPixmap( ( pw - sw ) / 2, ( ph - sh ) / 2, screenshot ); // center the picture + p.end(); +} + /// \brief Save the settings before exiting. /// \param event The close event that should be handled. -void MainWindow::closeEvent(QCloseEvent *event) { - if (mSettings->alwaysSave) { - mSettings->mainWindowGeometry = saveGeometry(); - mSettings->mainWindowState = saveState(); - mSettings->save(); +void MainWindow::closeEvent( QCloseEvent *event ) { + if ( dsoSettings->alwaysSave ) { + dsoSettings->mainWindowGeometry = saveGeometry(); + dsoSettings->mainWindowState = saveState(); + dsoSettings->save(); } - - QMainWindow::closeEvent(event); + QMainWindow::closeEvent( event ); } diff --git a/openhantek/src/mainwindow.h b/openhantek/src/mainwindow.h index d17b7ab8..6f0f3d7c 100644 --- a/openhantek/src/mainwindow.h +++ b/openhantek/src/mainwindow.h @@ -1,8 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "post/ppresult.h" +#include +#include #include #include +#include "scopesettings.h" + class SpectrumGenerator; class HantekDsoControl; class DsoSettings; @@ -24,24 +30,37 @@ class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow(HantekDsoControl *dsoControl, DsoSettings *mSettings, ExporterRegistry *exporterRegistry, - QWidget *parent = 0); - ~MainWindow(); + explicit MainWindow( HantekDsoControl *dsoControl, DsoSettings *dsoSettings, ExporterRegistry *exporterRegistry, + QWidget *parent = nullptr ); + ~MainWindow() override; + QElapsedTimer elapsedTime; + public slots: - void showNewData(std::shared_ptr data); - void exporterStatusChanged(const QString &exporterName, const QString &status); + void showNewData( std::shared_ptr< PPresult > newData ); + void exporterStatusChanged( const QString &exporterName, const QString &status ); void exporterProgressChanged(); protected: - void closeEvent(QCloseEvent *event) override; + void closeEvent( QCloseEvent *event ) override; private: Ui::MainWindow *ui; + QIcon iconPause; + QIcon iconPlay; + QLineEdit *commandEdit; // Central widgets DsoWidget *dsoWidget; // Settings used for the whole program - DsoSettings *mSettings; + DsoSettings *dsoSettings; ExporterRegistry *exporterRegistry; + + // Taking screenshots + enum screenshotType_t { SCREENSHOT, HARDCOPY, PRINTER }; + screenshotType_t screenshotType; + void screenShot( screenshotType_t screenshotType = SCREENSHOT ); + + signals: + void settingsLoaded( DsoSettingsScope *scope, const Dso::ControlSpecification *spec ); }; diff --git a/openhantek/src/mainwindow.ui b/openhantek/src/mainwindow.ui index ae006798..8ed3ad7b 100644 --- a/openhantek/src/mainwindow.ui +++ b/openhantek/src/mainwindow.ui @@ -20,7 +20,7 @@ 0 0 800 - 28 + 30 @@ -37,11 +37,14 @@ &View - - - + - + + + + + + @@ -49,17 +52,19 @@ - + &Help + + - Export + &Export @@ -74,24 +79,23 @@ toolBar - TopToolBarArea + LeftToolBarArea false - - - - - - + + + + + - Open layout + &Open settings .. Ctrl+O @@ -99,7 +103,7 @@ - Save layout + &Save settings Ctrl+S @@ -107,20 +111,50 @@ - Save as ... + Save settings &as .. - Exit + &Exit + + + Ctrl+Q + + + + + true + + + S&ampling + + + Space + + + + + false + + + &Refresh + + + + + true + + + &Phosphor - + true - Digital phosphor + &Histogram @@ -128,7 +162,15 @@ true - Zoom + &Zoom + + + + + true + + + &Measure @@ -141,25 +183,19 @@ Toolbars - + - About + &User Manual - + - Settings + &About .. - - - true - + - Sampling - - - Space + &Settings .. @@ -167,15 +203,12 @@ true - Manual command + &Manual command - - - true - + - Cursors + AC &Modification diff --git a/openhantek/src/post/graphgenerator.cpp b/openhantek/src/post/graphgenerator.cpp index 4d04e773..db9bd1e3 100644 --- a/openhantek/src/post/graphgenerator.cpp +++ b/openhantek/src/post/graphgenerator.cpp @@ -3,178 +3,214 @@ #include #include #include +#include -#include "post/graphgenerator.h" -#include "post/ppresult.h" -#include "post/softwaretrigger.h" +#include "graphgenerator.h" #include "hantekdso/controlspecification.h" +#include "ppresult.h" #include "scopesettings.h" #include "utils/printutils.h" #include "viewconstants.h" +#include "viewsettings.h" -static const SampleValues &useSpecSamplesOf(ChannelID channel, const PPresult *result, - const DsoSettingsScope *scope) { + +static const SampleValues &useSpecSamplesOf( ChannelID channel, const PPresult *result, const DsoSettingsScope *scope ) { static SampleValues emptyDefault; - if (!scope->spectrum[channel].used || !result->data(channel)) return emptyDefault; - return result->data(channel)->spectrum; + if ( !scope->spectrum[ channel ].used || !result->data( channel ) ) + return emptyDefault; + return result->data( channel )->spectrum; } -static const SampleValues &useVoltSamplesOf(ChannelID channel, const PPresult *result, - const DsoSettingsScope *scope) { + +static const SampleValues &useVoltSamplesOf( ChannelID channel, const PPresult *result, const DsoSettingsScope *scope ) { static SampleValues emptyDefault; - if (!scope->voltage[channel].used || !result->data(channel)) return emptyDefault; - return result->data(channel)->voltage; + if ( !scope->voltage[ channel ].used || !result->data( channel ) ) + return emptyDefault; + return result->data( channel )->voltage; } -GraphGenerator::GraphGenerator(const DsoSettingsScope *scope, bool isSoftwareTriggerDevice) - : scope(scope), isSoftwareTriggerDevice(isSoftwareTriggerDevice) {} -bool GraphGenerator::isReady() const { return ready; } +GraphGenerator::GraphGenerator( const DsoSettingsScope *scope /*, const DsoSettingsView *view */ ) + : scope( scope ) /*, view(view) */ { + // printf( "GraphGenerator::GraphGenerator()\n" ); +} + -void GraphGenerator::generateGraphsTYvoltage(PPresult *result) { - unsigned preTrigSamples = 0; - unsigned postTrigSamples = 0; - unsigned swTriggerStart = 0; +void GraphGenerator::process( PPresult *data ) { + // printf( "GraphGenerator::process()\n" ); + if ( scope->horizontal.format == Dso::GraphFormat::TY ) { + ready = true; + generateGraphsTYvoltage( data ); + generateGraphsTYspectrum( data ); + } else + generateGraphsXY( data ); +} - // check trigger point for software trigger - if (isSoftwareTriggerDevice && scope->trigger.source < result->channelCount()) - std::tie(preTrigSamples, postTrigSamples, swTriggerStart) = SoftwareTrigger::compute(result, scope); - result->softwareTriggerTriggered = postTrigSamples > preTrigSamples; - result->vaChannelVoltage.resize(scope->voltage.size()); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - ChannelGraph &target = result->vaChannelVoltage[channel]; - const SampleValues &samples = useVoltSamplesOf(channel, result, scope); +void GraphGenerator::generateGraphsTYvoltage( PPresult *result ) { + // printf( "GraphGenerator::generateGraphsTYvoltage()\n" ); + result->vaChannelVoltage.resize( scope->voltage.size() ); + result->vaChannelHistogram.resize( scope->voltage.size() ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + ChannelGraph &graphVoltage = result->vaChannelVoltage[ channel ]; + ChannelGraph &graphHistogram = result->vaChannelHistogram[ channel ]; + const SampleValues &samples = useVoltSamplesOf( channel, result, scope ); // Check if this channel is used and available at the data analyzer - if (samples.sample.empty()) { + if ( samples.sample.empty() ) { // Delete all vector arrays - target.clear(); + graphVoltage.clear(); + graphHistogram.clear(); continue; } - // Check if the sample count has changed - size_t sampleCount = samples.sample.size(); - if (sampleCount > 500000) { - qWarning() << "Sample count too high!"; - throw new std::runtime_error("Sample count too high!"); - } - sampleCount -= (swTriggerStart - preTrigSamples); - size_t neededSize = sampleCount * 2; - - // Set size directly to avoid reallocations - target.reserve(neededSize); - - // What's the horizontal distance between sampling points? - float horizontalFactor = (float)(samples.interval / scope->horizontal.timebase); - // Fill vector array - std::vector::const_iterator dataIterator = samples.sample.begin(); - const float gain = (float)scope->gain(channel); - const float offset = (float)scope->voltage[channel].offset; - const float invert = scope->voltage[channel].inverted ? -1.0f : 1.0f; - - std::advance(dataIterator, swTriggerStart - preTrigSamples); + // time distance between sampling points + double horizontalFactor = ( samples.interval / scope->horizontal.timebase ); + // printf( "hF: %g\n", horizontalFactor ); + unsigned dotsOnScreen = unsigned( ceil( DIVS_TIME / horizontalFactor ) ); + unsigned preTrigSamples = unsigned( scope->trigger.offset * dotsOnScreen ); + // align displayed trace with trigger mark on screen ... + // ... also if trig pos or time/div was changed on a "frozen" or single trace + int leftmostSample = int( result->triggeredPosition ); + if ( leftmostSample ) // adjust position if triggered, else start from sample[0] + leftmostSample -= preTrigSamples; // shift samples to show a stable trace + int leftmostPosition = 0; // start position on display + if ( leftmostSample < 0 ) { // trig pos or time/div was increased + leftmostPosition = -leftmostSample; // trace can't start on left margin + leftmostSample = 0; // show as much as we have on left side + } - for (unsigned int position = 0; position < sampleCount; ++position) { - target.push_back(QVector3D(position * horizontalFactor - DIVS_TIME / 2, - (float)*(dataIterator++) / gain * invert + offset, 0.0)); + const unsigned binsPerDiv = 50; + + // Set size directly to avoid reallocations (n+1 dots to display n lines) + graphVoltage.reserve( ++dotsOnScreen ); + graphHistogram.reserve( int( 2 * ( binsPerDiv * DIVS_VOLTAGE ) ) ); + + const double gain = scope->gain( channel ); + const double offset = scope->voltage[ channel ].offset; + + auto sampleIterator = samples.sample.cbegin() + leftmostSample; // -> visible samples + auto sampleEnd = samples.sample.cend(); + // printf("samples: %lu, dotsOnScreen: %d\n", samples.sample.size(), dotsOnScreen); + graphVoltage.clear(); // remove all previous dots and fill in new trace + graphHistogram.clear(); // remove all previous dots and fill in new trace + unsigned bins[ int( binsPerDiv * DIVS_VOLTAGE ) ] = {0}; + for ( unsigned int position = unsigned( leftmostPosition ); position < dotsOnScreen && sampleIterator < sampleEnd; + ++position, ++sampleIterator ) { + double x = double( MARGIN_LEFT + position * horizontalFactor ); + double y = *sampleIterator / gain + offset; + if ( !scope->histogram ) { // show complete trace + graphVoltage.push_back( QVector3D( float( x ), float( y ), 0.0f ) ); + } else { // histogram replaces trace in rightmost div + int bin = int( round( binsPerDiv * ( y + DIVS_VOLTAGE / 2 ) ) ); + if ( bin > 0 && bin < binsPerDiv * DIVS_VOLTAGE ) // if trace is on screen + ++bins[ bin ]; // count value + if ( x < MARGIN_RIGHT - 1.1 ) { // show trace unless in last div + 10% margin + graphVoltage.push_back( QVector3D( float( x ), float( y ), 0.0f ) ); + } + } + } + if ( ( scope->horizontal.format == Dso::GraphFormat::TY ) && scope->histogram ) { // scale and display the histogram + double max = 0; // find max histo count + for ( int bin = 0; bin < binsPerDiv * DIVS_VOLTAGE; ++bin ) { + if ( bins[ bin ] > max ) { + max = bins[ bin ]; + } + } + for ( int bin = 0; bin < binsPerDiv * DIVS_VOLTAGE; ++bin ) { + if ( bins[ bin ] ) { // show bar (= start and end point) if value exists + double y = double( bin ) / binsPerDiv - DIVS_VOLTAGE / 2 - double( channel ) / binsPerDiv / 2; + graphHistogram.push_back( QVector3D( float( MARGIN_RIGHT ), float( y ), 0 ) ); + graphHistogram.push_back( QVector3D( float( MARGIN_RIGHT - bins[ bin ] / max ), float( y ), 0 ) ); + } + } } } } -void GraphGenerator::generateGraphsTYspectrum(PPresult *result) { + +void GraphGenerator::generateGraphsTYspectrum( PPresult *result ) { + // printf( "GraphGenerator::generateGraphsTYspectrum()\n" ); ready = true; - result->vaChannelSpectrum.resize(scope->spectrum.size()); - for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { - ChannelGraph &target = result->vaChannelSpectrum[channel]; - const SampleValues &samples = useSpecSamplesOf(channel, result, scope); + result->vaChannelSpectrum.resize( scope->spectrum.size() ); + for ( ChannelID channel = 0; channel < scope->voltage.size(); ++channel ) { + ChannelGraph &graphSpectrum = result->vaChannelSpectrum[ channel ]; + const SampleValues &samples = useSpecSamplesOf( channel, result, scope ); // Check if this channel is used and available at the data analyzer - if (samples.sample.empty()) { + if ( samples.sample.empty() ) { // Delete all vector arrays - target.clear(); + graphSpectrum.clear(); continue; } // Check if the sample count has changed size_t sampleCount = samples.sample.size(); - if (sampleCount > 500000) { - qWarning() << "Sample count too high!"; - throw new std::runtime_error("Sample count too high!"); - } size_t neededSize = sampleCount * 2; // Set size directly to avoid reallocations - target.reserve(neededSize); + graphSpectrum.reserve( neededSize ); // What's the horizontal distance between sampling points? - float horizontalFactor = (float)(samples.interval / scope->horizontal.frequencybase); + double horizontalFactor = samples.interval / scope->horizontal.frequencybase; // Fill vector array - std::vector::const_iterator dataIterator = samples.sample.begin(); - const float magnitude = (float)scope->spectrum[channel].magnitude; - const float offset = (float)scope->spectrum[channel].offset; + std::vector< double >::const_iterator dataIterator = samples.sample.begin(); + const double magnitude = scope->spectrum[ channel ].magnitude; + const double offset = scope->spectrum[ channel ].offset; - for (unsigned int position = 0; position < sampleCount; ++position) { - target.push_back(QVector3D(position * horizontalFactor - DIVS_TIME / 2, - (float)*(dataIterator++) / magnitude + offset, 0.0)); + for ( unsigned int position = 0; position < sampleCount; ++position ) { + graphSpectrum.push_back( QVector3D( float( position * horizontalFactor - DIVS_TIME / 2 ), + float( *( dataIterator++ ) / magnitude + offset ), 0.0f ) ); } } } -void GraphGenerator::process(PPresult *data) { - if (scope->horizontal.format == Dso::GraphFormat::TY) { - ready = true; - generateGraphsTYspectrum(data); - generateGraphsTYvoltage(data); - } else - generateGraphsXY(data, scope); -} -void GraphGenerator::generateGraphsXY(PPresult *result, const DsoSettingsScope *scope) { - result->vaChannelVoltage.resize(scope->voltage.size()); +void GraphGenerator::generateGraphsXY( PPresult *result ) { + // puts("generateGraphXY()"); + result->vaChannelVoltage.resize( scope->voltage.size() ); // Delete all spectrum graphs - for (ChannelGraph &data : result->vaChannelSpectrum) data.clear(); + for ( ChannelGraph &data : result->vaChannelSpectrum ) + data.clear(); // Generate voltage graphs for pairs of channels - for (ChannelID channel = 0; channel < scope->voltage.size(); channel += 2) { + for ( ChannelID channel = 0; channel < scope->voltage.size(); channel += 2 ) { // We need pairs of channels. - if (channel + 1 == scope->voltage.size()) { - result->vaChannelVoltage[channel].clear(); + if ( channel + 1 == scope->voltage.size() ) { + result->vaChannelVoltage[ channel ].clear(); continue; } const ChannelID xChannel = channel; const ChannelID yChannel = channel + 1; - const SampleValues &xSamples = useVoltSamplesOf(xChannel, result, scope); - const SampleValues &ySamples = useVoltSamplesOf(yChannel, result, scope); + const SampleValues &xSamples = useVoltSamplesOf( xChannel, result, scope ); + const SampleValues &ySamples = useVoltSamplesOf( yChannel, result, scope ); // The channels need to be active - if (!xSamples.sample.size() || !ySamples.sample.size()) { - result->vaChannelVoltage[channel].clear(); - result->vaChannelVoltage[channel + 1].clear(); + if ( !xSamples.sample.size() || !ySamples.sample.size() ) { + result->vaChannelVoltage[ xChannel ].clear(); + result->vaChannelVoltage[ yChannel ].clear(); continue; } // Check if the sample count has changed - const size_t sampleCount = std::min(xSamples.sample.size(), ySamples.sample.size()); - ChannelGraph &drawLines = result->vaChannelVoltage[channel]; - drawLines.reserve(sampleCount * 2); + const size_t sampleCount = std::min( xSamples.sample.size(), ySamples.sample.size() ); + ChannelGraph &graphXY = result->vaChannelVoltage[ yChannel ]; // color of y channel + graphXY.reserve( sampleCount * 2 ); // Fill vector array - std::vector::const_iterator xIterator = xSamples.sample.begin(); - std::vector::const_iterator yIterator = ySamples.sample.begin(); - const double xGain = scope->gain(xChannel); - const double yGain = scope->gain(yChannel); - const double xOffset = scope->voltage[xChannel].offset; - const double yOffset = scope->voltage[yChannel].offset; - const double xInvert = scope->voltage[xChannel].inverted ? -1.0 : 1.0; - const double yInvert = scope->voltage[yChannel].inverted ? -1.0 : 1.0; - - for (unsigned int position = 0; position < sampleCount; ++position) { - drawLines.push_back(QVector3D((float)(*(xIterator++) / xGain * xInvert + xOffset), - (float)(*(yIterator++) / yGain * yInvert + yOffset), 0.0)); + std::vector< double >::const_iterator xIterator = xSamples.sample.begin(); + std::vector< double >::const_iterator yIterator = ySamples.sample.begin(); + const double xGain = scope->gain( xChannel ); + const double yGain = scope->gain( yChannel ); + const double xOffset = ( scope->trigger.offset - 0.5 ) * DIVS_TIME; + const double yOffset = scope->voltage[ yChannel ].offset; + + for ( unsigned int position = 0; position < sampleCount; ++position ) { + graphXY.push_back( + QVector3D( float( *( xIterator++ ) / xGain + xOffset ), float( *( yIterator++ ) / yGain + yOffset ), 0.0 ) ); } } } diff --git a/openhantek/src/post/graphgenerator.h b/openhantek/src/post/graphgenerator.h index 1642a896..fb21d08a 100644 --- a/openhantek/src/post/graphgenerator.h +++ b/openhantek/src/post/graphgenerator.h @@ -12,6 +12,7 @@ #include "processor.h" struct DsoSettingsScope; +struct DsoSettingsView; class PPresult; namespace Dso { struct ControlSpecification; @@ -22,21 +23,15 @@ class GraphGenerator : public QObject, public Processor { Q_OBJECT public: - GraphGenerator(const DsoSettingsScope *scope, bool isSoftwareTriggerDevice); - void generateGraphsXY(PPresult *result, const DsoSettingsScope *scope); - - bool isReady() const; + GraphGenerator( const DsoSettingsScope *scope ); private: - void generateGraphsTYvoltage(PPresult *result); - void generateGraphsTYspectrum(PPresult *result); + void generateGraphsTYvoltage( PPresult *result ); + void generateGraphsTYspectrum( PPresult *result ); + void generateGraphsXY( PPresult *result ); - private: bool ready = false; const DsoSettingsScope *scope; - const bool isSoftwareTriggerDevice; - // Processor interface - private: - virtual void process(PPresult *) override; + void process( PPresult *data ) override; }; diff --git a/openhantek/src/post/mathchannelgenerator.cpp b/openhantek/src/post/mathchannelgenerator.cpp index 233fa494..9a082344 100644 --- a/openhantek/src/post/mathchannelgenerator.cpp +++ b/openhantek/src/post/mathchannelgenerator.cpp @@ -1,47 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "mathchannelgenerator.h" -#include "scopesettings.h" -#include "post/postprocessingsettings.h" #include "enums.h" +#include "post/postprocessingsettings.h" +#include "scopesettings.h" + +MathChannelGenerator::MathChannelGenerator( const DsoSettingsScope *scope, unsigned physicalChannels ) + : physicalChannels( physicalChannels ), scope( scope ) {} -MathChannelGenerator::MathChannelGenerator(const DsoSettingsScope *scope, unsigned physicalChannels) - : physicalChannels(physicalChannels), scope(scope) {} MathChannelGenerator::~MathChannelGenerator() {} -void MathChannelGenerator::process(PPresult *result) { - bool channelsHaveData = !result->data(0)->voltage.sample.empty() && !result->data(1)->voltage.sample.empty(); - if (!channelsHaveData) return; - for (ChannelID channel = physicalChannels; channel < result->channelCount(); ++channel) { - DataChannel *const channelData = result->modifyData(channel); +void MathChannelGenerator::process( PPresult *result ) { + // printf( "MathChannelGenerator::process\n" ); + // Math channel enabled? + if ( !scope->voltage[ physicalChannels ].used && !scope->spectrum[ physicalChannels ].used ) + return; - // Math channel enabled? - if (!scope->voltage[channel].used && !scope->spectrum[channel].used) continue; + DataChannel *const channelData = result->modifiableData( physicalChannels ); + std::vector< double > &resultData = channelData->voltage.sample; + const double sign = scope->voltage[ physicalChannels ].inverted ? -1.0 : 1.0; + + if ( Dso::getMathMode( scope->voltage[ physicalChannels ] ) < Dso::MathMode::AC_CH1 ) { // binary operations + if ( result->data( 0 )->voltage.sample.empty() || result->data( 1 )->voltage.sample.empty() ) + return; + // Resize the sample vector + resultData.resize( std::min( result->data( 0 )->voltage.sample.size(), result->data( 1 )->voltage.sample.size() ) ); // Set sampling interval - channelData->voltage.interval = result->data(0)->voltage.interval; + channelData->voltage.interval = result->data( 0 )->voltage.interval; + // Calculate values and write them into the sample buffer + std::vector< double >::const_iterator ch1Iterator = result->data( 0 )->voltage.sample.begin(); + std::vector< double >::const_iterator ch2Iterator = result->data( 1 )->voltage.sample.begin(); + double ( *calculate )( double, double ); + + switch ( Dso::getMathMode( scope->voltage[ physicalChannels ] ) ) { + case Dso::MathMode::ADD_CH1_CH2: + calculate = []( double val1, double val2 ) -> double { return val1 + val2; }; + break; + case Dso::MathMode::SUB_CH2_FROM_CH1: + calculate = []( double val1, double val2 ) -> double { return val1 - val2; }; + break; + case Dso::MathMode::SUB_CH1_FROM_CH2: + calculate = []( double val1, double val2 ) -> double { return val2 - val1; }; + break; + case Dso::MathMode::MUL_CH1_CH2: + // multiply e.g. voltage and current (measured with a 1 ohm shunt) to get momentary power + calculate = []( double val1, double val2 ) -> double { return val1 * val2; }; + break; + default: + calculate = []( double, double ) -> double { return 0.0; }; + break; + } + for ( auto it = resultData.begin(), end = resultData.end(); it != end; ++it ) { + *it = sign * calculate( *ch1Iterator++, *ch2Iterator++ ); + } + } else { // unary operators (calculate "AC coupling") + unsigned src = 0; + if ( Dso::getMathMode( scope->voltage[ physicalChannels ] ) == Dso::MathMode::AC_CH1 ) + src = 0; + else if ( Dso::getMathMode( scope->voltage[ physicalChannels ] ) == Dso::MathMode::AC_CH2 ) + src = 1; // Resize the sample vector - std::vector &resultData = channelData->voltage.sample; - resultData.resize(std::min(result->data(0)->voltage.sample.size(), result->data(1)->voltage.sample.size())); + resultData.resize( result->data( src )->voltage.sample.size() ); + // Set sampling interval + channelData->voltage.interval = result->data( src )->voltage.interval; - // Calculate values and write them into the sample buffer - std::vector::const_iterator ch1Iterator = result->data(0)->voltage.sample.begin(); - std::vector::const_iterator ch2Iterator = result->data(1)->voltage.sample.begin(); - for (std::vector::iterator it = resultData.begin(); it != resultData.end(); ++it) { - switch (Dso::getMathMode(scope->voltage[physicalChannels])) { - case Dso::MathMode::ADD_CH1_CH2: - *it = *ch1Iterator + *ch2Iterator; - break; - case Dso::MathMode::SUB_CH2_FROM_CH1: - *it = *ch1Iterator - *ch2Iterator; - break; - case Dso::MathMode::SUB_CH1_FROM_CH2: - *it = *ch2Iterator - *ch1Iterator; - break; - } - ++ch1Iterator; - ++ch2Iterator; + // calculate DC component of channel... + double average = 0; + for ( auto srcIt = result->data( src )->voltage.sample.begin(), srcEnd = result->data( src )->voltage.sample.end(); + srcIt != srcEnd; ++srcIt ) { + average += *srcIt; + } + average /= result->data( src )->voltage.sample.size(); + + // ... and remove DC component to get AC + auto srcIt = result->data( src )->voltage.sample.begin(); + for ( auto dstIt = resultData.begin(), dstEnd = resultData.end(); dstIt != dstEnd; ++srcIt, ++dstIt ) { + *dstIt = sign * ( *srcIt - average ); } } } diff --git a/openhantek/src/post/mathchannelgenerator.h b/openhantek/src/post/mathchannelgenerator.h index 7195e15a..2a69dcb6 100644 --- a/openhantek/src/post/mathchannelgenerator.h +++ b/openhantek/src/post/mathchannelgenerator.h @@ -7,13 +7,14 @@ struct DsoSettingsScope; class PPresult; -class MathChannelGenerator : public Processor -{ -public: - MathChannelGenerator(const DsoSettingsScope *scope, unsigned physicalChannels); - virtual ~MathChannelGenerator(); - virtual void process(PPresult *) override; -private: +class MathChannelGenerator : public Processor { + + public: + MathChannelGenerator( const DsoSettingsScope *scope, unsigned physicalChannels ); + ~MathChannelGenerator() override; + void process( PPresult * ) override; + + private: const unsigned physicalChannels; const DsoSettingsScope *scope; }; diff --git a/openhantek/src/post/postprocessing.cpp b/openhantek/src/post/postprocessing.cpp index b78f7b33..57b7234c 100644 --- a/openhantek/src/post/postprocessing.cpp +++ b/openhantek/src/post/postprocessing.cpp @@ -1,29 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "postprocessing.h" -PostProcessing::PostProcessing(unsigned channelCount) : channelCount(channelCount) { - qRegisterMetaType>(); +PostProcessing::PostProcessing( unsigned channelCount ) : channelCount( channelCount ) { + qRegisterMetaType< std::shared_ptr< PPresult > >(); } -void PostProcessing::registerProcessor(Processor *processor) { processors.push_back(processor); } -void PostProcessing::convertData(const DSOsamples *source, PPresult *destination) { - QReadLocker locker(&source->lock); +void PostProcessing::registerProcessor( Processor *processor ) { processors.push_back( processor ); } + - for (ChannelID channel = 0; channel < source->data.size(); ++channel) { - const std::vector &rawChannelData = source->data.at(channel); +void PostProcessing::convertData( const DSOsamples *source, PPresult *destination ) { + // printf( "PostProcessing::convertData()\n" ); + QReadLocker locker( &source->lock ); + if ( source->triggeredPosition ) { + destination->softwareTriggerTriggered = source->liveTrigger; + destination->triggeredPosition = source->triggeredPosition; + destination->pulseWidth1 = source->pulseWidth1; + destination->pulseWidth2 = source->pulseWidth2; + } else { + destination->softwareTriggerTriggered = false; + destination->triggeredPosition = 0; + destination->pulseWidth1 = 0; + destination->pulseWidth2 = 0; + } - if (rawChannelData.empty()) { continue; } + for ( ChannelID channel = 0; channel < source->data.size(); ++channel ) { + const std::vector< double > &rawChannelData = source->data.at( channel ); - DataChannel *const channelData = destination->modifyData(channel); + if ( rawChannelData.empty() ) { + continue; + } + DataChannel *const channelData = destination->modifiableData( channel ); channelData->voltage.interval = 1.0 / source->samplerate; channelData->voltage.sample = rawChannelData; + // printf( "PP CH%d: %d\n", channel+1, source->clipped ); + channelData->valid = !( source->clipped & ( 0x01 << channel ) ); } + destination->tag = source->tag; } -void PostProcessing::input(const DSOsamples *data) { - currentData.reset(new PPresult(channelCount)); - convertData(data, currentData.get()); - for (Processor *p : processors) p->process(currentData.get()); - std::shared_ptr res = std::move(currentData); - emit processingFinished(res); + +void PostProcessing::input( const DSOsamples *data ) { + if ( data && processing ) { + // printf( "PostProcessing::input( %d )\n", data->tag ); + currentData.reset( new PPresult( channelCount ) ); // start with a fresh data structure + convertData( data, currentData.get() ); // copy all relevant data over + for ( Processor *p : processors ) // feed it into the PP chain + p->process( currentData.get() ); + std::shared_ptr< PPresult > res = std::move( currentData ); + emit processingFinished( res ); + } } diff --git a/openhantek/src/post/postprocessing.h b/openhantek/src/post/postprocessing.h index 56e4f7ea..596f744c 100644 --- a/openhantek/src/post/postprocessing.h +++ b/openhantek/src/post/postprocessing.h @@ -9,6 +9,7 @@ #include #include +#include struct DsoSettingsScope; @@ -19,34 +20,39 @@ struct DsoSettingsScope; */ class PostProcessing : public QObject { Q_OBJECT + public: - PostProcessing(unsigned channelCount); + explicit PostProcessing( unsigned channelCount ); /** * Adds a new processor that is called when a new input arrived. The order of the processors is * imporant. The first added processor will be called first. This class does not take ownership * of the processors. * @param processor */ - void registerProcessor(Processor *processor); + void registerProcessor( Processor *processor ); + void stop() { processing = false; } private: /// A new `PPresult` is created for each new input. We need to know the channel size. const unsigned channelCount; /// The list of processors. Processors are not memory managed by this class. - std::vector processors; + std::vector< Processor * > processors; /// - std::unique_ptr currentData; - static void convertData(const DSOsamples *source, PPresult *destination); + std::unique_ptr< PPresult > currentData; + static void convertData( const DSOsamples *source, PPresult *destination ); + bool processing = true; + public slots: /** * Start processing new data. The actual data may be processed in another thread if you have moved * this class object into another thread. * @param data */ - void input(const DSOsamples *data); -signals: - void processingFinished(std::shared_ptr result); + void input( const DSOsamples *data ); + + signals: + void processingFinished( std::shared_ptr< PPresult > result ); }; -Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE( std::shared_ptr< PPresult > ) diff --git a/openhantek/src/post/postprocessingsettings.cpp b/openhantek/src/post/postprocessingsettings.cpp index 7fef5f95..2a8c926b 100644 --- a/openhantek/src/post/postprocessingsettings.cpp +++ b/openhantek/src/post/postprocessingsettings.cpp @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "postprocessingsettings.h" #include @@ -5,23 +7,31 @@ namespace Dso { -Enum MathModeEnum; -Enum WindowFunctionEnum; +Enum< Dso::MathMode, Dso::MathMode::ADD_CH1_CH2, Dso::MathMode::AC_CH2 > MathModeEnum; +Enum< Dso::WindowFunction, Dso::WindowFunction::RECTANGULAR, Dso::WindowFunction::FLATTOP > WindowFunctionEnum; /// \brief Return string representation of the given math mode. /// \param mode The ::MathMode that should be returned as string. /// \return The string that should be used in labels etc. -QString mathModeString(MathMode mode) { - switch (mode) { +QString mathModeString( MathMode mode ) { + switch ( mode ) { case MathMode::ADD_CH1_CH2: - return QCoreApplication::tr("CH1 + CH2"); + return QCoreApplication::tr( "CH1 + CH2" ); case MathMode::SUB_CH2_FROM_CH1: - return QCoreApplication::tr("CH1 - CH2"); + return QCoreApplication::tr( "CH1 - CH2" ); case MathMode::SUB_CH1_FROM_CH2: - return QCoreApplication::tr("CH2 - CH1"); + return QCoreApplication::tr( "CH2 - CH1" ); + case MathMode::MUL_CH1_CH2: + return QCoreApplication::tr( "CH1 * CH2" ); + case MathMode::AC_CH1: + return QCoreApplication::tr( "CH1 AC" ); + case MathMode::AC_CH2: + return QCoreApplication::tr( "CH2 AC" ); } return QString(); } + +#if 0 /// \brief Return string representation of the given dft window function. /// \param window The ::WindowFunction that should be returned as string. /// \return The string that should be used in labels etc. @@ -60,4 +70,6 @@ QString windowFunctionString(WindowFunction window) { } return QString(); } -} +#endif + +} // namespace Dso diff --git a/openhantek/src/post/postprocessingsettings.h b/openhantek/src/post/postprocessingsettings.h index 0ed3d4f1..459b54da 100644 --- a/openhantek/src/post/postprocessingsettings.h +++ b/openhantek/src/post/postprocessingsettings.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "utils/enumclass.h" @@ -6,11 +8,10 @@ namespace Dso { /// \enum MathMode /// \brief The different math modes for the math-channel. -enum class MathMode : unsigned { ADD_CH1_CH2, SUB_CH2_FROM_CH1, SUB_CH1_FROM_CH2 }; -extern Enum MathModeEnum; +enum class MathMode : unsigned { ADD_CH1_CH2, SUB_CH2_FROM_CH1, SUB_CH1_FROM_CH2, MUL_CH1_CH2, AC_CH1, AC_CH2 }; +extern Enum< Dso::MathMode, Dso::MathMode::ADD_CH1_CH2, Dso::MathMode::AC_CH2 > MathModeEnum; -template -inline MathMode getMathMode(T& t) { return (MathMode)t.couplingOrMathIndex; } +template < class T > inline MathMode getMathMode( T &t ) { return MathMode( t.couplingOrMathIndex ); } /// \enum WindowFunction /// \brief The supported window functions. @@ -33,17 +34,17 @@ enum class WindowFunction : int { BLACKMANNUTTALL, ///< Blackman-Nuttall window FLATTOP ///< Flat top window }; -extern Enum WindowFunctionEnum; +extern Enum< Dso::WindowFunction, Dso::WindowFunction::RECTANGULAR, Dso::WindowFunction::FLATTOP > WindowFunctionEnum; -QString mathModeString(MathMode mode); -QString windowFunctionString(WindowFunction window); -} +QString mathModeString( MathMode mode ); +// QString windowFunctionString(WindowFunction window); +} // namespace Dso -Q_DECLARE_METATYPE(Dso::MathMode) -Q_DECLARE_METATYPE(Dso::WindowFunction) +Q_DECLARE_METATYPE( Dso::MathMode ) +Q_DECLARE_METATYPE( Dso::WindowFunction ) struct DsoSettingsPostProcessing { - Dso::WindowFunction spectrumWindow = Dso::WindowFunction::HANN; ///< Window function for DFT - double spectrumReference = 0.0; ///< Reference level for spectrum in dBm - double spectrumLimit = -20.0; ///< Minimum magnitude of the spectrum (Avoids peaks) + Dso::WindowFunction spectrumWindow = Dso::WindowFunction::HAMMING; ///< Window function for DFT + double spectrumReference = 0.0; ///< Reference level for spectrum in dBu + double spectrumLimit = -60.0; ///< Minimum magnitude of the spectrum (Avoids peaks) }; diff --git a/openhantek/src/post/ppresult.cpp b/openhantek/src/post/ppresult.cpp index 435adf4d..128c2e46 100644 --- a/openhantek/src/post/ppresult.cpp +++ b/openhantek/src/post/ppresult.cpp @@ -4,31 +4,16 @@ #include #include -PPresult::PPresult(unsigned int channelCount) { analyzedData.resize(channelCount); } +PPresult::PPresult( unsigned int channelCount ) { analyzedData.resize( channelCount ); } -const DataChannel *PPresult::data(ChannelID channel) const { - if (channel >= this->analyzedData.size()) return 0; - - return &this->analyzedData[(size_t)channel]; +const DataChannel *PPresult::data( ChannelID channel ) const { + if ( channel >= this->analyzedData.size() ) + return nullptr; + return &this->analyzedData[ channel ]; } -DataChannel *PPresult::modifyData(ChannelID channel) { return &this->analyzedData[(size_t)channel]; } - -unsigned int PPresult::sampleCount() const { return (unsigned)analyzedData[0].voltage.sample.size(); } - -unsigned int PPresult::channelCount() const { return (unsigned)analyzedData.size(); } +DataChannel *PPresult::modifiableData( ChannelID channel ) { return &this->analyzedData[ channel ]; } -double DataChannel::computeAmplitude() const { - if (voltage.sample.empty()) return 0.0; - double minimalVoltage, maximalVoltage; - minimalVoltage = maximalVoltage = voltage.sample[0]; +unsigned int PPresult::sampleCount() const { return unsigned( analyzedData[ 0 ].voltage.sample.size() ); } - for (unsigned int position = 1; position < voltage.sample.size(); ++position) { - if (voltage.sample[position] < minimalVoltage) - minimalVoltage = voltage.sample[position]; - else if (voltage.sample[position] > maximalVoltage) - maximalVoltage = voltage.sample[position]; - } - - return maximalVoltage - minimalVoltage; -} +unsigned int PPresult::channelCount() const { return unsigned( analyzedData.size() ); } diff --git a/openhantek/src/post/ppresult.h b/openhantek/src/post/ppresult.h index 16b80221..c36aaf09 100644 --- a/openhantek/src/post/ppresult.h +++ b/openhantek/src/post/ppresult.h @@ -2,50 +2,63 @@ #pragma once -#include #include +#include -#include #include "hantekprotocol/types.h" +#include /// \brief Struct for a array of sample values. struct SampleValues { - std::vector sample; ///< Vector holding the sampling data - double interval = 0.0; ///< The interval between two sample values + std::vector< double > sample; ///< Vector holding the sampling data + double interval = 0.0; ///< The interval between two sample values }; /// \brief Struct for the analyzed data. struct DataChannel { - SampleValues voltage; ///< The time-domain voltage levels (V) - SampleValues spectrum; ///< The frequency-domain power levels (dB) - - double frequency = 0.0; ///< The frequency of the signal - // Calculate peak-to-peak voltage - double computeAmplitude() const; + SampleValues voltage; ///< The time-domain voltage levels (V) + SampleValues spectrum; ///< The frequency-domain power levels (dB) + bool valid = true; ///< Not clipped, distorted, dropouts etc. + double vpp = 0.0; ///< The peak-to-peak voltage of the _displayed_ part of trace + double rms = 0.0; ///< The DC + AC rms value of the signal = sqrt( dc * dc + acc * ac ) + double dc = 0.0; ///< The DC bias of the signal + double ac = 0.0; ///< The AC rms value of the signal + double dB = 0.0; ///< The AC rms value as dB (dBV or other depending on config) + double frequency = 0.0; ///< The frequency of the signal + double pulseWidth1 = 0.0; ///< The width of the triggered pulse + double pulseWidth2 = 0.0; ///< The width of the following pulse }; -typedef std::vector ChannelGraph; -typedef std::vector ChannelsGraphs; +typedef std::vector< QVector3D > ChannelGraph; +typedef std::vector< ChannelGraph > ChannelsGraphs; /// Post processing results class PPresult { public: - PPresult(unsigned int channelCount); + explicit PPresult( unsigned int channelCount ); - /// \brief Returns the analyzed data. + /// \brief Returns the analyzed data (RO). /// \param channel Channel, whose data should be returned. - const DataChannel *data(ChannelID channel) const; - /// \brief Returns the analyzed data. The data structure can be modifed. + const DataChannel *data( ChannelID channel ) const; + /// \brief Returns the analyzed data (RW). The data structure can be modifed. /// \param channel Channel, whose data should be returned. - DataChannel *modifyData(ChannelID channel); + DataChannel *modifiableData( ChannelID channel ); /// \return The maximum sample count of the last analyzed data. This assumes there is at least one channel. unsigned int sampleCount() const; unsigned int channelCount() const; + /// sw trigger status bool softwareTriggerTriggered = false; + /// skip samples at start of channel to get triggered trace on screen + unsigned triggeredPosition = 0; ///< Not triggered + double pulseWidth1 = 0.0; ///< The width of the triggered pulse + double pulseWidth2 = 0.0; ///< The width of the following pulse + unsigned tag; ///< track individual sample blocks (debug support) ChannelsGraphs vaChannelSpectrum; ChannelsGraphs vaChannelVoltage; + ChannelsGraphs vaChannelHistogram; + private: - std::vector analyzedData; ///< The analyzed data for each channel + std::vector< DataChannel > analyzedData; ///< The analyzed data for each channel }; diff --git a/openhantek/src/post/processor.cpp b/openhantek/src/post/processor.cpp new file mode 100644 index 00000000..28ad167f --- /dev/null +++ b/openhantek/src/post/processor.cpp @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "processor.h" + +Processor::~Processor() {} diff --git a/openhantek/src/post/processor.h b/openhantek/src/post/processor.h index 71da37b8..deee7f0b 100644 --- a/openhantek/src/post/processor.h +++ b/openhantek/src/post/processor.h @@ -1,8 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "ppresult.h" class Processor { -public: - virtual void process(PPresult*) = 0; + public: + virtual ~Processor(); + virtual void process( PPresult * ) = 0; }; diff --git a/openhantek/src/post/readme.md b/openhantek/src/post/readme.md index 4d03070f..7fbf8e44 100644 --- a/openhantek/src/post/readme.md +++ b/openhantek/src/post/readme.md @@ -1,9 +1,9 @@ # Content This directory contains post processing algorithms, namely -* SoftwareTrigger: Determines a steady point, is used by GraphGenerator, -* GraphGenerator: Applies all user settings (gain, offset, trigger point) and produces vertices, +* SpectrumGenerator: calculates signal frequency by auto correlation, applies window and calculates DFT spectrum, * MathChannelGenerator: Creates a math channel on top of the pysical channels +* GraphGenerator: Applies all user settings (gain, offset, trigger point) and produces vertices, # Dependency * Files in this directory depend on structs in the `hantekprotocol` folder. diff --git a/openhantek/src/post/softwaretrigger.cpp b/openhantek/src/post/softwaretrigger.cpp deleted file mode 100644 index 19ecc964..00000000 --- a/openhantek/src/post/softwaretrigger.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "post/softwaretrigger.h" -#include "post/ppresult.h" -#include "scopesettings.h" -#include "viewconstants.h" -#include "utils/printutils.h" - -SoftwareTrigger::PrePostStartTriggerSamples SoftwareTrigger::compute(const PPresult *data, - const DsoSettingsScope *scope) -{ - unsigned int preTrigSamples = 0; - unsigned int postTrigSamples = 0; - unsigned int swTriggerStart = 0; - ChannelID channel = scope->trigger.source; - - // Trigger channel not in use - if (!scope->voltage[channel].used || !data->data(channel) || - data->data(channel)->voltage.sample.empty()) - return PrePostStartTriggerSamples(preTrigSamples, postTrigSamples, swTriggerStart); - - const std::vector& samples = data->data(channel)->voltage.sample; - double level = scope->voltage[channel].trigger; - size_t sampleCount = samples.size(); - double timeDisplay = scope->horizontal.timebase * DIVS_TIME; - double samplesDisplay = timeDisplay * scope->horizontal.samplerate; - - if (samplesDisplay >= sampleCount) { - // For sure not enough samples to adjust for jitter. - // Following options exist: - // 1: Decrease sample rate - // 2: Change trigger mode to auto - // 3: Ignore samples - // For now #3 is chosen - timestampDebug(QString("Too few samples to make a steady " - "picture. Decrease sample rate")); - return PrePostStartTriggerSamples(preTrigSamples, postTrigSamples, swTriggerStart); - } - preTrigSamples = (unsigned)(scope->trigger.position * samplesDisplay); - postTrigSamples = (unsigned)sampleCount - ((unsigned)samplesDisplay - preTrigSamples); - - double prev; - bool (*opcmp)(double,double,double); - bool (*smplcmp)(double,double); - if (scope->trigger.slope == Dso::Slope::Positive) { - prev = INT_MAX; - opcmp = [](double value, double level, double prev) { return value > level && prev <= level;}; - smplcmp = [](double sampleK, double value) { return sampleK >= value;}; - } else { - prev = INT_MIN; - opcmp = [](double value, double level, double prev) { return value < level && prev >= level;}; - smplcmp = [](double sampleK, double value) { return sampleK < value;}; - } - - for (unsigned int i = preTrigSamples; i < postTrigSamples; i++) { - double value = samples[i]; - if (opcmp(value, level, prev)) { - unsigned rising = 0; - for (unsigned int k = i + 1; k < i + scope->trigger.swTriggerSampleSet && k < sampleCount; k++) { - if (smplcmp(samples[k], value)) { rising++; } - } - if (rising > scope->trigger.swTriggerThreshold) { - swTriggerStart = i; - break; - } - } - prev = value; - } - if (swTriggerStart == 0) { - timestampDebug(QString("Trigger not asserted. Data ignored")); - preTrigSamples = 0; // preTrigSamples may never be greater than swTriggerStart - postTrigSamples = 0; - } - return PrePostStartTriggerSamples(preTrigSamples, postTrigSamples, swTriggerStart); -} diff --git a/openhantek/src/post/softwaretrigger.h b/openhantek/src/post/softwaretrigger.h deleted file mode 100644 index 0ed730a2..00000000 --- a/openhantek/src/post/softwaretrigger.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include -struct DsoSettingsScope; -class PPresult; - - -/** - * Contains software trigger algorithms. At the moment this works on the analysed data of the - * DataAnalyser class. - * TODO Should work on the raw data within HantekDsoControl - */ -class SoftwareTrigger { - public: - typedef std::tuple PrePostStartTriggerSamples; - /** - * @brief Computes a software trigger point. - * @param data Analysed data from the - * @param scope Scope settings - * @return Returns a tuple of positions [preTrigger, postTrigger, startTrigger] - */ - static PrePostStartTriggerSamples compute(const PPresult *data, const DsoSettingsScope *scope); -}; diff --git a/openhantek/src/post/spectrumgenerator.cpp b/openhantek/src/post/spectrumgenerator.cpp index 48dde97b..da968a62 100644 --- a/openhantek/src/post/spectrumgenerator.cpp +++ b/openhantek/src/post/spectrumgenerator.cpp @@ -1,227 +1,337 @@ // SPDX-License-Identifier: GPL-2.0+ -#define _USE_MATH_DEFINES #include #include #include #include -#include - +#include "ppresult.h" #include "spectrumgenerator.h" +#include -#include "glscope.h" -#include "settings.h" +#include "dsosettings.h" #include "utils/printutils.h" +#include "viewconstants.h" /// \brief Analyzes the data from the dso. -SpectrumGenerator::SpectrumGenerator(const DsoSettingsScope *scope, const DsoSettingsPostProcessing *postprocessing) - : scope(scope), postprocessing(postprocessing) {} +SpectrumGenerator::SpectrumGenerator( const DsoSettingsScope *scope, const DsoSettingsPostProcessing *postprocessing ) + : scope( scope ), postprocessing( postprocessing ) {} + SpectrumGenerator::~SpectrumGenerator() { - if (lastWindowBuffer) fftw_free(lastWindowBuffer); + if ( lastWindowBuffer ) + fftw_free( lastWindowBuffer ); } -void SpectrumGenerator::process(PPresult *result) { + +void SpectrumGenerator::process( PPresult *result ) { // Calculate frequencies and spectrums - for (ChannelID channel = 0; channel < result->channelCount(); ++channel) { - DataChannel *const channelData = result->modifyData(channel); - if (channelData->voltage.sample.empty()) { + for ( ChannelID channel = 0; channel < result->channelCount(); ++channel ) { + DataChannel *const channelData = result->modifiableData( channel ); + + if ( channelData->voltage.sample.empty() ) { // Clear unused channels channelData->spectrum.interval = 0; channelData->spectrum.sample.clear(); continue; } - // Calculate new window + // scale all windows to display 1 Veff as 0 dBu reference level. size_t sampleCount = channelData->voltage.sample.size(); - if (!lastWindowBuffer || lastWindow != postprocessing->spectrumWindow || lastRecordLength != sampleCount) { - if (lastWindowBuffer) fftw_free(lastWindowBuffer); - lastWindowBuffer = fftw_alloc_real(sampleCount); - lastRecordLength = (unsigned)sampleCount; + if ( !lastWindowBuffer || lastWindow != postprocessing->spectrumWindow || lastRecordLength != sampleCount ) { + if ( lastWindowBuffer ) + fftw_free( lastWindowBuffer ); + lastWindowBuffer = fftw_alloc_real( sampleCount ); + lastRecordLength = unsigned( sampleCount ); unsigned int windowEnd = lastRecordLength - 1; lastWindow = postprocessing->spectrumWindow; - - switch (postprocessing->spectrumWindow) { + double weight = 0.0; // calculate area under window fkt + switch ( postprocessing->spectrumWindow ) { case Dso::WindowFunction::HAMMING: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.54 - 0.46 * cos(2.0 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 0.54 - 0.46 * cos( 2.0 * M_PI * windowPosition / windowEnd ); break; case Dso::WindowFunction::HANN: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.5 * (1.0 - cos(2.0 * M_PI * windowPosition / windowEnd)); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = + 0.5 * ( 1.0 - cos( 2.0 * M_PI * windowPosition / windowEnd ) ); break; case Dso::WindowFunction::COSINE: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = sin(M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = sin( M_PI * windowPosition / windowEnd ); break; case Dso::WindowFunction::LANCZOS: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) { - double sincParameter = (2.0 * windowPosition / windowEnd - 1.0) * M_PI; - if (sincParameter == 0) - *(lastWindowBuffer + windowPosition) = 1; + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) { + double sincParameter = ( 2.0 * windowPosition / windowEnd - 1.0 ) * M_PI; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + + if ( sincParameter == 0 ) + weight += *( lastWindowBuffer + windowPosition ) = 1; else - *(lastWindowBuffer + windowPosition) = sin(sincParameter) / sincParameter; + weight += *( lastWindowBuffer + windowPosition ) = sin( sincParameter ) / sincParameter; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } break; case Dso::WindowFunction::BARTLETT: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = - 2.0 / windowEnd * (windowEnd / 2 - std::abs((double)(windowPosition - windowEnd / 2.0))); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = + 2.0 / windowEnd * ( windowEnd / 2 - std::abs( double( windowPosition - windowEnd / 2.0 ) ) ); break; case Dso::WindowFunction::TRIANGULAR: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = - 2.0 / lastRecordLength * - (lastRecordLength / 2 - std::abs((double)(windowPosition - windowEnd / 2.0))); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = + 2.0 / lastRecordLength * ( lastRecordLength / 2 - std::abs( double( windowPosition - windowEnd / 2.0 ) ) ); break; case Dso::WindowFunction::GAUSS: { - double sigma = 0.4; - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = - exp(-0.5 * pow(((windowPosition - windowEnd / 2) / (sigma * windowEnd / 2)), 2)); + const double sigma = 0.5; + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) { + double w = ( double( windowPosition ) - lastRecordLength / 2.0 ) / ( sigma * lastRecordLength / 2.0 ); + w *= w; + weight += *( lastWindowBuffer + windowPosition ) = exp( -w ); + } } break; case Dso::WindowFunction::BARTLETTHANN: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.62 - - 0.48 * std::abs((double)(windowPosition / windowEnd - 0.5)) - - 0.38 * cos(2.0 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = + 0.62 - 0.48 * std::abs( double( windowPosition / windowEnd - 0.5 ) ) - + 0.38 * cos( 2.0 * M_PI * windowPosition / windowEnd ); break; case Dso::WindowFunction::BLACKMAN: { double alpha = 0.16; - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = (1 - alpha) / 2 - - 0.5 * cos(2.0 * M_PI * windowPosition / windowEnd) + - alpha / 2 * cos(4.0 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = ( 1 - alpha ) / 2 - + 0.5 * cos( 2.0 * M_PI * windowPosition / windowEnd ) + + alpha / 2 * cos( 4.0 * M_PI * windowPosition / windowEnd ); } break; // case Dso::WindowFunction::WINDOW_KAISER: - // TODO WINDOW_KAISER - // double alpha = 3.0; - // for(unsigned int windowPosition = 0; windowPosition < - // lastRecordLength; ++windowPosition) - //*(window + windowPosition) = ; - // break; + // TODO WINDOW_KAISER + // corr = dB( 0 ); + // double alpha = 3.0; + // for(unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) + // weight += *(window + windowPosition) = ...; + // break; case Dso::WindowFunction::NUTTALL: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.355768 - - 0.487396 * cos(2 * M_PI * windowPosition / windowEnd) + - 0.144232 * cos(4 * M_PI * windowPosition / windowEnd) - - 0.012604 * cos(6 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 0.355768 - + 0.487396 * cos( 2 * M_PI * windowPosition / windowEnd ) + + 0.144232 * cos( 4 * M_PI * windowPosition / windowEnd ) - + 0.012604 * cos( 6 * M_PI * windowPosition / windowEnd ); break; case Dso::WindowFunction::BLACKMANHARRIS: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.35875 - - 0.48829 * cos(2 * M_PI * windowPosition / windowEnd) + - 0.14128 * cos(4 * M_PI * windowPosition / windowEnd) - - 0.01168 * cos(6 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 0.35875 - + 0.48829 * cos( 2 * M_PI * windowPosition / windowEnd ) + + 0.14128 * cos( 4 * M_PI * windowPosition / windowEnd ) - + 0.01168 * cos( 6 * M_PI * windowPosition / windowEnd ); break; case Dso::WindowFunction::BLACKMANNUTTALL: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 0.3635819 - - 0.4891775 * cos(2 * M_PI * windowPosition / windowEnd) + - 0.1365995 * cos(4 * M_PI * windowPosition / windowEnd) - - 0.0106411 * cos(6 * M_PI * windowPosition / windowEnd); + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 0.3635819 - + 0.4891775 * cos( 2 * M_PI * windowPosition / windowEnd ) + + 0.1365995 * cos( 4 * M_PI * windowPosition / windowEnd ) - + 0.0106411 * cos( 6 * M_PI * windowPosition / windowEnd ); break; - case Dso::WindowFunction::FLATTOP: - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 1.0 - 1.93 * cos(2 * M_PI * windowPosition / windowEnd) + - 1.29 * cos(4 * M_PI * windowPosition / windowEnd) - - 0.388 * cos(6 * M_PI * windowPosition / windowEnd) + - 0.032 * cos(8 * M_PI * windowPosition / windowEnd); + case Dso::WindowFunction::FLATTOP: // wikipedia.de + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 1.0 - 1.93 * cos( 2 * M_PI * windowPosition / windowEnd ) + + 1.29 * cos( 4 * M_PI * windowPosition / windowEnd ) - + 0.388 * cos( 6 * M_PI * windowPosition / windowEnd ) + + 0.028 * cos( 8 * M_PI * windowPosition / windowEnd ); break; default: // Dso::WINDOW_RECTANGULAR - for (unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition) - *(lastWindowBuffer + windowPosition) = 1.0; + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + weight += *( lastWindowBuffer + windowPosition ) = 1.0; } + // weight is the area below the window function + weight = lastRecordLength / weight; // normalise all windows equal to the rectangular window + + // DFT transforms a 1V sin(ωt) signal to 1 = 0 dB, RMS = 0.707 V = sqrt(0.5) V (-3dBV) + // If we want to scale to 0 dBu = 0 dBm @ 600 Ω, RMS = 0.775V = sqrt(1 mW * 600 Ω) + // we must scale by sqrt(0.5/0.6) = -2.2 dB + weight *= sqrt( 0.5 ); // scale display to 0 dBV -> 1V RMS = 0dB + // printf( "window %u, weight %g\n", (unsigned)postprocessing->spectrumWindow, weight ); + // scale the windowed samples + for ( unsigned int windowPosition = 0; windowPosition < lastRecordLength; ++windowPosition ) + *( lastWindowBuffer + windowPosition ) *= weight; } // Set sampling interval channelData->spectrum.interval = 1.0 / channelData->voltage.interval / sampleCount; // Number of real/complex samples - unsigned int dftLength = sampleCount / 2; + unsigned int dftLength = unsigned( sampleCount ) / 2; // Reallocate memory for samples if the sample count has changed - channelData->spectrum.sample.resize(sampleCount); + channelData->spectrum.sample.resize( sampleCount ); // Create sample buffer and apply window - std::unique_ptr windowedValues = std::unique_ptr(new double[sampleCount]); - - for (unsigned int position = 0; position < sampleCount; ++position) - windowedValues[position] = lastWindowBuffer[position] * channelData->voltage.sample[position]; - - { - // Do discrete real to half-complex transformation - /// \todo Check if record length is multiple of 2 - /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm - fftw_plan fftPlan = fftw_plan_r2r_1d(sampleCount, windowedValues.get(), - &channelData->spectrum.sample.front(), FFTW_R2HC, FFTW_ESTIMATE); - fftw_execute(fftPlan); - fftw_destroy_plan(fftPlan); + std::unique_ptr< double[] > windowedValues = std::unique_ptr< double[] >( new double[ sampleCount ] ); + + // calculate the peak-to-peak value of the displayed part of trace + double min = INT_MAX; + double max = INT_MIN; + // TODO: adapt triggerPosition (left = tP - preTrig; right = left + dotsOnScreen) + double horizontalFactor = result->data( channel )->voltage.interval / scope->horizontal.timebase; + unsigned dotsOnScreen = unsigned( DIVS_TIME / horizontalFactor + 0.99 ); // round up + unsigned preTrigSamples = unsigned( scope->trigger.offset * dotsOnScreen ); + int left = int( result->triggeredPosition ) - int( preTrigSamples ); // 1st sample to show + int right = left + int( dotsOnScreen ); // last sample to show + if ( left < 0 ) // trig pos or time/div was increased + left = 0; // show as much as we have on left side + // unsigned right = result->triggerPosition + DIVS_TIME * scope->horizontal.timebase / channelData->voltage.interval; + if ( right >= int( sampleCount ) ) + right = int( sampleCount ) - 1; + for ( int position = left; // left side of trace + position <= right; // right side + ++position ) { + if ( channelData->voltage.sample[ unsigned( position ) ] < min ) + min = channelData->voltage.sample[ unsigned( position ) ]; + if ( channelData->voltage.sample[ unsigned( position ) ] > max ) + max = channelData->voltage.sample[ unsigned( position ) ]; } + channelData->vpp = max - min; + // printf( "dots = %d, Vpp = %g\n", dots, channelData->vpp ); + + // calculate the average value + double dc = 0.0; + auto voltageIterator = channelData->voltage.sample.begin(); + for ( unsigned int position = 0; position < sampleCount; ++position ) { + dc += *voltageIterator++; + } + dc /= sampleCount; + channelData->dc = dc; + + // now strip DC bias, calculate rms of AC component and apply window for fft to AC component + double ac2 = 0.0; + voltageIterator = channelData->voltage.sample.begin(); + for ( unsigned int position = 0; position < sampleCount; ++position ) { + double ac_sample = *voltageIterator++ - dc; + ac2 += ac_sample * ac_sample; + windowedValues[ position ] = lastWindowBuffer[ position ] * ac_sample; + } + ac2 /= sampleCount; + channelData->ac = sqrt( ac2 ); // rms of AC component + channelData->rms = sqrt( dc * dc + ac2 ); // total rms = U eff + channelData->dB = 10.0 * log10( ac2 ) - postprocessing->spectrumReference; + channelData->pulseWidth1 = result->pulseWidth1; + channelData->pulseWidth2 = result->pulseWidth2; + + // Do discrete real to half-complex transformation + /// \todo Check if record length is multiple of 2 + /// \todo Reuse plan and use FFTW_MEASURE to get fastest algorithm + + fftw_plan fftPlan; + fftPlan = fftw_plan_r2r_1d( int( sampleCount ), windowedValues.get(), &channelData->spectrum.sample.front(), FFTW_R2HC, + FFTW_ESTIMATE ); + fftw_execute( fftPlan ); + fftw_destroy_plan( fftPlan ); // Do an autocorrelation to get the frequency of the signal - std::unique_ptr conjugateComplex = std::move(windowedValues); + // fft: f(t) ⊶ F(ω); calculate power spectrum |F(ω)|² + // ifft: F(ω) ∙ F(ω) ⊷ f(t) ⊗ f(t) (convolution of f(t) with f(t), i.e. autocorrelation) + // HORO: + // This is quite inaccurate at high frequencies due to the used algorithm: + // as we do a autocorrelation the resolution at high frequencies is limited by voltagestep interval + // e.g. at 6 MHz sampled with 30 MS/s we get correlation at time shift + // of either 6 or 5 or 4 samples -> 30 MHz / 6 = 5.0 MHz ; 30 / 5 = 6.0 ; 30 / 4 = 7.5 + // in these cases use spectrum instead if peak position is too small. + + // create a copy of powerSpectrum because hc2r iDFT destroys spectrum input + const double norm = 1.0 / dftLength / dftLength; + std::unique_ptr< double[] > powerSpectrum = std::move( windowedValues ); - // Real values unsigned int position; - double correctionFactor = 1.0 / dftLength / dftLength; - conjugateComplex[0] = (channelData->spectrum.sample[0] * channelData->spectrum.sample[0]) * correctionFactor; - for (position = 1; position < dftLength; ++position) - conjugateComplex[position] = - (channelData->spectrum.sample[position] * channelData->spectrum.sample[position] + - channelData->spectrum.sample[sampleCount - position] * - channelData->spectrum.sample[sampleCount - position]) * - correctionFactor; + // correct the (half-)complex values in spectrum (1st part real forward), (2nd part imag backwards) -> magnitude + auto fwd = channelData->spectrum.sample.begin(); // forward iterator + auto rev = channelData->spectrum.sample.rbegin(); // reverse iterator + // convert complex to magnitude square in place (*fwd) and into copy (powerSpectrum[]) + *fwd = *fwd * *fwd; + powerSpectrum[ 0 ] = *fwd * norm; + ++fwd; // spectrum[0] is only real + for ( position = 1; position < dftLength; ++position ) { + *fwd = ( *fwd * *fwd + *rev * *rev ); + powerSpectrum[ position ] = *fwd * norm; + ++fwd; + ++rev; + } + *fwd = *fwd * *fwd; + powerSpectrum[ position ] = *fwd * norm; + ++fwd; // Complex values, all zero for autocorrelation - conjugateComplex[dftLength] = - (channelData->spectrum.sample[dftLength] * channelData->spectrum.sample[dftLength]) * correctionFactor; - for (++position; position < sampleCount; ++position) conjugateComplex[position] = 0; + for ( ++position; position < sampleCount; ++position ) { + powerSpectrum[ position ] = 0; + } + // skip mirrored 2nd half of result spectrum + channelData->spectrum.sample.resize( dftLength + 1 ); - // Do half-complex to real inverse transformation - std::unique_ptr correlation = std::unique_ptr(new double[sampleCount]); - fftw_plan fftPlan = - fftw_plan_r2r_1d(sampleCount, conjugateComplex.get(), correlation.get(), FFTW_HC2R, FFTW_ESTIMATE); - fftw_execute(fftPlan); - fftw_destroy_plan(fftPlan); + // Do half-complex to real inverse transformation -> autocorrelation + std::unique_ptr< double[] > correlation = std::unique_ptr< double[] >( new double[ sampleCount ] ); + fftPlan = fftw_plan_r2r_1d( int( sampleCount ), powerSpectrum.get(), correlation.get(), FFTW_HC2R, FFTW_ESTIMATE ); + fftw_execute( fftPlan ); + fftw_destroy_plan( fftPlan ); // Get the frequency from the correlation results - double minimumCorrelation = correlation[0]; - double peakCorrelation = 0; - unsigned int peakPosition = 0; - - for (unsigned int position = 1; position < sampleCount / 2; ++position) { - if (correlation[position] > peakCorrelation && correlation[position] > minimumCorrelation * 2) { - peakCorrelation = correlation[position]; - peakPosition = position; - } else if (correlation[position] < minimumCorrelation) - minimumCorrelation = correlation[position]; + unsigned int peakCorrPos = 0; + double minCorr = 0; + double maxCorr = 0; + unsigned maxCorrPos = 0; + // search from right to left for a max and remember this if a following min corr (<0) is found + for ( position = unsigned( sampleCount ) / 2; position > 1; --position ) { // go down to get leftmost peak (= max freq) + if ( correlation[ position ] > maxCorr ) { // find (local) max + maxCorr = correlation[ position ]; + maxCorrPos = position; + minCorr = 0; // reset minimum to start new min search + // printf( "max %d: %g\n", position, maxCorr ); + } else if ( correlation[ position ] < minCorr ) { // search for local min + minCorr = correlation[ position ]; + maxCorr = 0; // reset max to start new max seach + peakCorrPos = maxCorrPos; + // printf( "min %d: %g\n", position, minCorr ); + } } - correlation.reset(nullptr); - - // Calculate the frequency in Hz - if (peakPosition) - channelData->frequency = 1.0 / (channelData->voltage.interval * peakPosition); - else - channelData->frequency = 0; - - // Finally calculate the real spectrum if we want it - if (scope->spectrum[channel].used) { - // Convert values into dB (Relative to the reference level) - double offset = 60 - postprocessing->spectrumReference - 20 * log10(dftLength); - double offsetLimit = postprocessing->spectrumLimit - postprocessing->spectrumReference; - for (std::vector::iterator spectrumIterator = channelData->spectrum.sample.begin(); - spectrumIterator != channelData->spectrum.sample.end(); ++spectrumIterator) { - double value = 20 * log10(fabs(*spectrumIterator)) + offset; - - // Check if this value has to be limited - if (offsetLimit > value) value = offsetLimit; - - *spectrumIterator = value; + correlation.reset( nullptr ); + + // Finally calculate the real spectrum (it's also used for frequency display) + // Convert values into dB (Relative to the reference level 0 dBV = 1V eff) + double offset = -postprocessing->spectrumReference - 20 * log10( dftLength ); + double offsetLimit = postprocessing->spectrumLimit - postprocessing->spectrumReference; + double peakSpectrum = offsetLimit; // get a start value for peak search + unsigned int peakFreqPos = 0; // initial position of max spectrum peak + position = 0; + for ( auto spectrumIterator = channelData->spectrum.sample.begin(), spectrumEnd = channelData->spectrum.sample.end(); + spectrumIterator != spectrumEnd; ++spectrumIterator, ++position ) { + // spectrum is power spectrum, but show amplitude spectrum -> 10 * log... + double value = 10 * log10( *spectrumIterator ) + offset; + // Check if this value has to be limited + if ( value < offsetLimit ) + value = offsetLimit; + *spectrumIterator = value; + // detect frequency peak + if ( value > peakSpectrum ) { + peakSpectrum = value; + peakFreqPos = position; } } + + // Calculate both peak frequencies (correlation and spectrum) in Hz + double pF = channelData->spectrum.interval * peakFreqPos; + double pC = 1.0 / ( channelData->voltage.interval * peakCorrPos ); + // printf( "pF %u: %d %g\n", channel, peakFreqPos, pF ); + // printf( "pC %u: %d %g\n", channel, peakCorrPos, pC ); + if ( peakFreqPos > peakCorrPos // use frequency result if it is more granular than correlation + || peakFreqPos > 100 // or at least if it is granular enough (+- 1% resolution) + || peakCorrPos < 100 || peakCorrPos > sampleCount / 4 ) { // or if correlation is out of safe range + channelData->frequency = pF; + } else { // otherwise fall back to correlation + channelData->frequency = pC; + } } } diff --git a/openhantek/src/post/spectrumgenerator.h b/openhantek/src/post/spectrumgenerator.h index d0f4710d..4b521b17 100644 --- a/openhantek/src/post/spectrumgenerator.h +++ b/openhantek/src/post/spectrumgenerator.h @@ -8,10 +8,10 @@ #include #include -#include "ppresult.h" #include "dsosamples.h" -#include "utils/printutils.h" #include "postprocessingsettings.h" +#include "ppresult.h" +#include "utils/printutils.h" #include "processor.h" @@ -22,15 +22,17 @@ struct DsoSettingsScope; /// Calculates the spectrum and various data about the signal and saves the /// time-/frequencysteps between two values. class SpectrumGenerator : public Processor { + public: - SpectrumGenerator(const DsoSettingsScope* scope, const DsoSettingsPostProcessing* postprocessing); - virtual ~SpectrumGenerator(); - virtual void process(PPresult *data) override; + SpectrumGenerator( const DsoSettingsScope *scope, const DsoSettingsPostProcessing *postprocessing ); + ~SpectrumGenerator() override; private: - const DsoSettingsScope* scope; - const DsoSettingsPostProcessing* postprocessing; - unsigned int lastRecordLength = 0; ///< The record length of the previously analyzed data - Dso::WindowFunction lastWindow = (Dso::WindowFunction)-1; ///< The previously used dft window function + const DsoSettingsScope *scope; + const DsoSettingsPostProcessing *postprocessing; + unsigned int lastRecordLength = 0; ///< The record length of the previously analyzed data + Dso::WindowFunction lastWindow = Dso::WindowFunction( -1 ); ///< The previously used dft window function double *lastWindowBuffer = nullptr; + // Processor interface + void process( PPresult *data ) override; }; diff --git a/openhantek/src/scopesettings.h b/openhantek/src/scopesettings.h index c46ef2ba..b20393aa 100644 --- a/openhantek/src/scopesettings.h +++ b/openhantek/src/scopesettings.h @@ -2,26 +2,20 @@ #pragma once -#include #include +#include #include "hantekdso/controlspecification.h" #include "hantekdso/enums.h" #include "hantekprotocol/definitions.h" +#include "viewconstants.h" #include -#define MARKER_COUNT 2 ///< Number of markers -#define MARKER_STEP (DIVS_TIME / 100.0) /// \brief Holds the cursor parameters struct DsoSettingsScopeCursor { - enum CursorShape { - NONE, - HORIZONTAL, - VERTICAL, - RECTANGULAR - } shape = NONE; - QPointF pos[MARKER_COUNT] = {{-1.0, -1.0}, {1.0, 1.0}}; ///< Position in div + enum CursorShape { NONE, HORIZONTAL, VERTICAL, RECTANGULAR } shape = NONE; + QPointF pos[ 2 ] = {{-1.0, -1.0}, {1.0, 1.0}}; ///< Position in div }; /// \brief Holds the settings for the horizontal axis. @@ -31,29 +25,33 @@ struct DsoSettingsScopeHorizontal { DsoSettingsScopeCursor cursor; unsigned int recordLength = 0; ///< Sample count - - /// TODO Use ControlSettingsSamplerateTarget - double timebase = 1e-3; ///< Timebase in s/div + double timebase = 1e-3; ///< Timebase in s/div + double maxTimebase = 1; ///< Allow very slow timebases 0.1 ... 10.0 s/div +#ifdef Q_PROCESSOR_ARM + // RPi: Not more often than every 10 ms + double acquireInterval = 0.010; ///< Minimal time between captured frames +#else + // other PC: Not more often than every 1 ms + double acquireInterval = 0.001; ///< Minimal time between captured frames +#endif double samplerate = 1e6; ///< The samplerate of the oscilloscope in S - enum SamplerateSource { Samplerrate, Duration } samplerateSource = Samplerrate; + double calfreq = 1e3; ///< The frequency of the calibration output }; /// \brief Holds the settings for the trigger. /// TODO Use ControlSettingsTrigger struct DsoSettingsScopeTrigger { - Dso::TriggerMode mode = Dso::TriggerMode::HARDWARE_SOFTWARE; ///< Automatic, normal or single trigger - double position = 0.0; ///< Horizontal position for pretrigger - Dso::Slope slope = Dso::Slope::Positive; ///< Rising or falling edge causes trigger - unsigned int source = 0; ///< Channel that is used as trigger source - bool special = false; ///< true if the trigger source is not a standard channel - unsigned swTriggerThreshold = 7; ///< Software trigger, threshold - unsigned swTriggerSampleSet = 11; ///< Software trigger, sample set + Dso::TriggerMode mode = Dso::TriggerMode::AUTO; ///< Automatic, normal or single trigger + double offset = 0.5; ///< Horizontal position for pretrigger (middle of screen) + Dso::Slope slope = Dso::Slope::Positive; ///< Rising or falling edge causes trigger + unsigned int source = 0; ///< Channel that is used as trigger source + bool smooth = false; ///< Don't trigger on glitches }; /// \brief Base for DsoSettingsScopeSpectrum and DsoSettingsScopeVoltage struct DsoSettingsScopeChannel { - QString name; ///< Name of this channel - bool used = false; ///< true if the channel is turned on + QString name; ///< Name of this channel + bool used = false; ///< true if the channel is turned on DsoSettingsScopeCursor cursor; }; @@ -71,30 +69,38 @@ struct DsoSettingsScopeVoltage : public DsoSettingsScopeChannel { unsigned gainStepIndex = 6; ///< The vertical resolution in V/div (default = 1.0) unsigned couplingOrMathIndex = 0; ///< Different index: coupling for real- and mode for math-channels bool inverted = false; ///< true if the channel is inverted (mirrored on cross-axis) + double probeAttn = 1.0; ///< attenuation of probe }; /// \brief Holds the settings for the oscilloscope. struct DsoSettingsScope { - std::vector gainSteps = {1e-2, 2e-2, 5e-2, 1e-1, 2e-1, - 5e-1, 1e0, 2e0, 5e0}; ///< The selectable voltage gain steps in V/div - std::vector spectrum; ///< Spectrum analysis settings - std::vector voltage; ///< Settings for the normal graphs - DsoSettingsScopeHorizontal horizontal; ///< Settings for the horizontal axis - DsoSettingsScopeTrigger trigger; ///< Settings for the trigger - - double gain(unsigned channel) const { return gainSteps[voltage[channel].gainStepIndex]; } - bool anyUsed(ChannelID channel) { return voltage[channel].used | spectrum[channel].used; } - - Dso::Coupling coupling(ChannelID channel, const Dso::ControlSpecification *deviceSpecification) const { - return deviceSpecification->couplings[voltage[channel].couplingOrMathIndex]; + std::vector< double > gainSteps = {2e-2, 5e-2, 1e-1, 2e-1, 5e-1, 1e0, 2e0, 5e0}; ///< The selectable voltage gain steps in V/div + std::vector< DsoSettingsScopeSpectrum > spectrum; ///< Spectrum analysis settings + std::vector< DsoSettingsScopeVoltage > voltage; ///< Settings for the normal graphs + DsoSettingsScopeHorizontal horizontal; ///< Settings for the horizontal axis + DsoSettingsScopeTrigger trigger; ///< Settings for the trigger + + bool histogram = false; + bool hasACcoupling = false; + bool hasACmodification = false; + + double gain( unsigned channel ) const { return gainSteps[ voltage[ channel ].gainStepIndex ] * voltage[ channel ].probeAttn; } + + bool anyUsed( ChannelID channel ) { return voltage[ channel ].used | spectrum[ channel ].used; } + + Dso::Coupling coupling( ChannelID channel, const Dso::ControlSpecification *deviceSpecification ) const { + return deviceSpecification->couplings[ voltage[ channel ].couplingOrMathIndex ]; } // Channels, including math channels - unsigned countChannels() const { return (unsigned)voltage.size(); } + unsigned countChannels() const { return unsigned( voltage.size() ); } - double getMarker(unsigned int marker) const { - return marker < MARKER_COUNT ? horizontal.cursor.pos[marker].x() : 0.0; + double getMarker( unsigned int marker ) const { + double x = qBound( MARGIN_LEFT, marker < 2 ? horizontal.cursor.pos[ marker ].x() : 0.0, MARGIN_RIGHT ); + return x; } - void setMarker(unsigned int marker, double value) { - if (marker < MARKER_COUNT) horizontal.cursor.pos[marker].setX(value); + + void setMarker( unsigned int marker, double value ) { + if ( marker < 2 ) + horizontal.cursor.pos[ marker ].setX( value ); } }; diff --git a/openhantek/src/selectdevice/devicelistentry.h b/openhantek/src/selectdevice/devicelistentry.h index a544919e..aae44638 100644 --- a/openhantek/src/selectdevice/devicelistentry.h +++ b/openhantek/src/selectdevice/devicelistentry.h @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0+ + #pragma once +#include "usb/scopedevice.h" #include -#include "usb/usbdevice.h" /** * Represents an entry in the {@link DevicesListModel}. @@ -10,10 +11,11 @@ struct DeviceListEntry { UniqueUSBid id; QString name; - bool canConnect; - bool needFirmware; + bool canConnect = false; + bool needFirmware = false; QString errorMessage; QString getStatus() const { - return errorMessage.size()? errorMessage : (canConnect?"Ready":(needFirmware?"Firmware upload":"Cannot connect")); + return errorMessage.size() ? errorMessage + : ( canConnect ? "Ready" : ( needFirmware ? "Firmware upload" : "Cannot connect" ) ); } }; diff --git a/openhantek/src/selectdevice/deviceslistmodel.cpp b/openhantek/src/selectdevice/deviceslistmodel.cpp index 3ae3438b..1e015db9 100644 --- a/openhantek/src/selectdevice/deviceslistmodel.cpp +++ b/openhantek/src/selectdevice/deviceslistmodel.cpp @@ -1,83 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "deviceslistmodel.h" +#include "dsomodel.h" #include "usb/finddevices.h" #include "usb/uploadFirmware.h" -#include "dsomodel.h" #include +#include -DevicesListModel::DevicesListModel(FindDevices *findDevices) :findDevices(findDevices) {} +DevicesListModel::DevicesListModel( FindDevices *findDevices ) : findDevices( findDevices ) {} -int DevicesListModel::rowCount(const QModelIndex &) const -{ - return (int)entries.size(); -} +int DevicesListModel::rowCount( const QModelIndex & ) const { return int( entries.size() ); } -int DevicesListModel::columnCount(const QModelIndex &) const -{ - return 2; -} +int DevicesListModel::columnCount( const QModelIndex & ) const { return 2; } -QVariant DevicesListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Vertical) - return QAbstractTableModel::headerData(section, orientation, role); - if (role == Qt::DisplayRole) { - switch(section) { - case 0: return tr("Devicename"); - case 1: return tr("Status"); - default: return QVariant(); +QVariant DevicesListModel::headerData( int section, Qt::Orientation orientation, int role ) const { + if ( orientation == Qt::Vertical ) + return QAbstractTableModel::headerData( section, orientation, role ); + if ( role == Qt::DisplayRole ) { + switch ( section ) { + case 0: + return QObject::tr( "Devicename" ); + case 1: + return QObject::tr( "Status" ); + default: + return QVariant(); } } - return QAbstractTableModel::headerData(section, orientation, role); + return QAbstractTableModel::headerData( section, orientation, role ); } -QVariant DevicesListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) return QVariant(); - const unsigned row = (unsigned)index.row(); - if (role==Qt::UserRole) return QVariant::fromValue(entries[row].id); - else if (role==Qt::UserRole+1) return QVariant::fromValue(entries[row].canConnect); - else if (role==Qt::UserRole+2) return QVariant::fromValue(entries[row].needFirmware); +QVariant DevicesListModel::data( const QModelIndex &index, int role ) const { + if ( !index.isValid() ) + return QVariant(); + const unsigned row = unsigned( index.row() ); + if ( role == Qt::UserRole ) + return QVariant::fromValue( entries[ row ].id ); + if ( role == Qt::UserRole + 1 ) + return QVariant::fromValue( entries[ row ].canConnect ); + if ( role == Qt::UserRole + 2 ) + return QVariant::fromValue( entries[ row ].needFirmware ); + if ( role == Qt::UserRole + 3 ) + return QVariant::fromValue( entries[ row ].errorMessage ); - if (role == Qt::DisplayRole) { - if (index.column() == 0) { - return entries[row].name; - } else if (index.column() == 1) { - return entries[row].getStatus(); + if ( role == Qt::DisplayRole ) { + if ( index.column() == 0 ) { + return entries[ row ].name; + } else if ( index.column() == 1 ) { + return entries[ row ].getStatus(); } } - if (role == Qt::BackgroundRole) { - if (entries[row].canConnect) return QColor(Qt::darkGreen).lighter(); - else if (entries[row].needFirmware) return QColor(Qt::yellow).lighter(); + if ( role == Qt::BackgroundRole ) { + if ( entries[ row ].canConnect ) + return QColor( Qt::darkGreen ).lighter(); + else if ( entries[ row ].needFirmware ) + return QColor( Qt::yellow ).lighter(); } return QVariant(); } -void DevicesListModel::updateDeviceList() -{ +void DevicesListModel::updateDeviceList() { beginResetModel(); entries.clear(); endResetModel(); - const FindDevices::DeviceList* devices = findDevices->getDevices(); - beginInsertRows(QModelIndex(),0,(int)devices->size()); - for (auto &i : *devices) { + const FindDevices::DeviceList *devices = findDevices->getDevices(); + beginInsertRows( QModelIndex(), 0, int( devices->size() ) ); + for ( auto &i : *devices ) { DeviceListEntry entry; - entry.name= QString::fromStdString(i.second->getModel()->name); + entry.name = QString::fromStdString( i.second->getModel()->name ); entry.id = i.first; - if (i.second->needsFirmware()) { + if ( i.second->needsFirmware() ) { UploadFirmware uf; - if (!uf.startUpload(i.second.get())) { + if ( !uf.startUpload( i.second.get() ) ) { entry.errorMessage = uf.getErrorMessage(); - } - entry.needFirmware = true; - } else if (i.second->connectDevice(entry.errorMessage)) { + entry.needFirmware = false; // trigger error message + } else + entry.needFirmware = true; + } else if ( i.second->connectDevice( entry.errorMessage ) ) { entry.canConnect = true; i.second->disconnectFromDevice(); } else { entry.canConnect = false; } - entries.push_back(entry); + entries.push_back( entry ); } endInsertRows(); } diff --git a/openhantek/src/selectdevice/deviceslistmodel.h b/openhantek/src/selectdevice/deviceslistmodel.h index 764512d3..0131942f 100644 --- a/openhantek/src/selectdevice/deviceslistmodel.h +++ b/openhantek/src/selectdevice/deviceslistmodel.h @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0+ + #pragma once -#include #include "devicelistentry.h" +#include class FindDevices; @@ -10,16 +11,17 @@ class FindDevices; * Provides a Model for the Qt Model/View concept. The {@see FindDevices} is required * to update the list of available devices. */ -class DevicesListModel: public QAbstractTableModel { -public: - DevicesListModel(FindDevices* findDevices); +class DevicesListModel : public QAbstractTableModel { + public: + explicit DevicesListModel( FindDevices *findDevices ); // QAbstractItemModel interface - virtual int rowCount(const QModelIndex &parent) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual QVariant data(const QModelIndex &index, int role) const override; + int rowCount( const QModelIndex &parent ) const override; + int columnCount( const QModelIndex &parent ) const override; + QVariant headerData( int section, Qt::Orientation orientation, int role ) const override; + QVariant data( const QModelIndex &index, int role ) const override; void updateDeviceList(); -private: - std::vector entries; - FindDevices* findDevices; + + private: + std::vector< DeviceListEntry > entries; + FindDevices *findDevices; }; diff --git a/openhantek/src/selectdevice/newdevicemodelfromexisting.cpp b/openhantek/src/selectdevice/newdevicemodelfromexisting.cpp deleted file mode 100644 index 013e6adc..00000000 --- a/openhantek/src/selectdevice/newdevicemodelfromexisting.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "newdevicemodelfromexisting.h" - -#include "dsomodel.h" -#include "modelregistry.h" -#include "rawdeviceslistmodel.h" - -#include -#include -#include -#include - -NewDeviceModelFromExisting::NewDeviceModelFromExisting(QWidget *parent) : - QDialog(parent), - ui(new Ui::NewDeviceModelFromExisting) -{ - ui->setupUi(this); - connect(ui->checkBox, &QCheckBox::stateChanged,[this](int state) { - ui->stackedWidget->setCurrentIndex(state==Qt::Checked ? 0: 1); - }); - ui->checkBox->setCheckState(Qt::Checked); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - QStringList supportedModelsList; - for (const DSOModel* model: ModelRegistry::get()->models()) { - supportedModelsList.append(QString::fromStdString(model->name)); - } - - QStringListModel* model = new QStringListModel(this); - model->setStringList(supportedModelsList); - ui->cmbTemplateModel->setModel(model); - - RawDevicesListModel* deviceListModel = new RawDevicesListModel(context, this); - ui->cmbUSBdevices->setModel(deviceListModel); - deviceListModel->updateDeviceList(); - - connect(ui->btnRefresh, &QPushButton::clicked, [this,deviceListModel] { - deviceListModel->updateDeviceList(); - }); - - connect(ui->cmbUSBdevices, static_cast(&QComboBox::currentIndexChanged), [this](int index) { - if (index == -1) { - ui->stackedWidget->setCurrentIndex(2); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - ui->checkBox->setEnabled(false); - return; - } - if (ui->cmbUSBdevices->currentData(RawDevicesListModel::AccessRole).toBool()) { - ui->checkBox->setEnabled(true); - ui->modelname->setText(ui->cmbUSBdevices->currentData(RawDevicesListModel::DeviceNameRole).toString()); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->modelname->text().size()); - ui->stackedWidget->setCurrentIndex(ui->checkBox->isChecked() ? 0: 1); - } else { - ui->checkBox->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - ui->stackedWidget->setCurrentIndex(3); - } - }); - - if (deviceListModel->rowCount(QModelIndex())) { - ui->cmbUSBdevices->setCurrentIndex(0); - } -} - -void NewDeviceModelFromExisting::setUSBcontext(libusb_context *context) -{ - this->context = context; -} - -RawDeviceListEntry *NewDeviceModelFromExisting::getSelectedEntry() -{ - return (RawDeviceListEntry*) ui->cmbUSBdevices->currentData(RawDevicesListModel::EntryPointerRole).value(); -} - -void NewDeviceModelFromExisting::accept() -{ - QMessageBox::information(this,tr("Sorry"),tr("This is not yet implemented!")); - QDialog::accept(); -} diff --git a/openhantek/src/selectdevice/newdevicemodelfromexisting.h b/openhantek/src/selectdevice/newdevicemodelfromexisting.h deleted file mode 100644 index 1ab128d6..00000000 --- a/openhantek/src/selectdevice/newdevicemodelfromexisting.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -#pragma once - -#include -#include -#include "ui_newdevicemodelfromexisting.h" -#include "rawdevicelistentry.h" - -namespace Ui { -class NewDeviceModelFromExisting; -} - -struct libusb_context; - -class NewDeviceModelFromExisting : public QDialog -{ - Q_OBJECT - -public: - explicit NewDeviceModelFromExisting(QWidget *parent = 0); - void setUSBcontext(libusb_context* context); - RawDeviceListEntry* getSelectedEntry(); -private: - std::unique_ptr ui; - libusb_context* context = nullptr; - - // QDialog interface -public slots: - virtual void accept() override; -}; diff --git a/openhantek/src/selectdevice/newdevicemodelfromexisting.ui b/openhantek/src/selectdevice/newdevicemodelfromexisting.ui deleted file mode 100644 index eb90c6e1..00000000 --- a/openhantek/src/selectdevice/newdevicemodelfromexisting.ui +++ /dev/null @@ -1,274 +0,0 @@ - - - NewDeviceModelFromExisting - - - - 0 - 0 - 442 - 437 - - - - New device from template - - - - - - Select USB device - - - - QLayout::SetMinimumSize - - - - - - - - Refresh - - - - - - - This is usually indicated by a light (red flashing) - - - This is usually indicated by a light (red flashing) - - - Firmware is uploaded already* - - - - - - - - - - 0 - - - 1 - - - - - - - - 0 - 0 - - - - Template selection - - - - - - - 0 - 0 - - - - Select the existing model that should be used as template for your device. You may need to restart OpenHantek and try different models until it works for you. - - - true - - - - - - - - - - Please enter a model name - - - - - - - - - - - - - - Firmware files - - - - - - Please select the firmware files in hex format, extracted from the windows driver for example. You need to open this dialog again after the firmware has been uploaded. - - - true - - - - - - - QLayout::SetMinimumSize - - - - - Firmware - - - - - - - Loader - - - - - - - - - - - - - - 0 - 0 - - - - ... - - - false - - - - - - - - 0 - 0 - - - - ... - - - - - - - - - - - - - - - - No USB devices found or your operating system prohibited enumerating devices. - - - true - - - - - - - - - - - No access granted for the selected USB device. Your operating system may prohibit access. On Windows you might need to install a generic driver first. On Linux you need to install an udev rule to grant access to your currently logged in user. - - - true - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - NewDeviceModelFromExisting - accept() - - - 227 - 324 - - - 157 - 274 - - - - - buttonBox - rejected() - NewDeviceModelFromExisting - reject() - - - 260 - 337 - - - 286 - 274 - - - - - diff --git a/openhantek/src/selectdevice/rawdevicelistentry.h b/openhantek/src/selectdevice/rawdevicelistentry.h index 46c8a31a..7121e55d 100644 --- a/openhantek/src/selectdevice/rawdevicelistentry.h +++ b/openhantek/src/selectdevice/rawdevicelistentry.h @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ + #pragma once -#include -#include "usb/usbdevice.h" #include "dsomodel.h" +#include "usb/scopedevice.h" +#include /** * Represents an entry in the {@link DevicesListModel}. */ @@ -11,7 +12,7 @@ struct RawDeviceListEntry { long productId; long vendorId; bool access; - DSOModel* baseModel=nullptr; + DSOModel *baseModel = nullptr; QString devicename; QString deviceinfo; }; diff --git a/openhantek/src/selectdevice/rawdeviceslistmodel.cpp b/openhantek/src/selectdevice/rawdeviceslistmodel.cpp index 8b30aa0c..0f89c245 100644 --- a/openhantek/src/selectdevice/rawdeviceslistmodel.cpp +++ b/openhantek/src/selectdevice/rawdeviceslistmodel.cpp @@ -1,80 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "rawdeviceslistmodel.h" +#include "dsomodel.h" #include "usb/finddevices.h" #include "usb/uploadFirmware.h" -#include "dsomodel.h" #include -RawDevicesListModel::RawDevicesListModel(libusb_context *context, QObject *parent) : QAbstractTableModel(parent), context(context) {} +RawDevicesListModel::RawDevicesListModel( libusb_context *context, QObject *parent ) + : QAbstractTableModel( parent ), context( context ) {} -int RawDevicesListModel::rowCount(const QModelIndex &) const -{ - return (int)entries.size(); -} +int RawDevicesListModel::rowCount( const QModelIndex & ) const { return int( entries.size() ); } -int RawDevicesListModel::columnCount(const QModelIndex &) const -{ - return 1; -} +int RawDevicesListModel::columnCount( const QModelIndex & ) const { return 1; } -QVariant RawDevicesListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) return QVariant(); - const unsigned row = (unsigned)index.row(); - if (role==ProductIDRole) return QVariant::fromValue(entries[row].productId); - else if (role==VendorIDRole) return QVariant::fromValue(entries[row].vendorId); - else if (role==AccessRole) return QVariant::fromValue(entries[row].access); - else if (role==DeviceNameRole) return QVariant::fromValue(entries[row].devicename); - else if (role==EntryPointerRole) return QVariant::fromValue((void*)&entries[row]); - else if (role==Qt::DisplayRole) return QVariant::fromValue(entries[row].deviceinfo); +QVariant RawDevicesListModel::data( const QModelIndex &index, int role ) const { + if ( !index.isValid() ) + return QVariant(); + const unsigned row = unsigned( index.row() ); + if ( role == ProductIDRole ) + return QVariant::fromValue( entries[ row ].productId ); + else if ( role == VendorIDRole ) + return QVariant::fromValue( entries[ row ].vendorId ); + else if ( role == AccessRole ) + return QVariant::fromValue( entries[ row ].access ); + else if ( role == DeviceNameRole ) + return QVariant::fromValue( entries[ row ].devicename ); + else if ( role == EntryPointerRole ) + return QVariant::fromValue( (void *)( &entries[ row ] ) ); + else if ( role == Qt::DisplayRole ) + return QVariant::fromValue( entries[ row ].deviceinfo ); return QVariant(); } -QString readUSBdescriptor(libusb_device_handle *handle, uint8_t index) { - unsigned char string[255]; - int ret = libusb_get_string_descriptor_ascii(handle, index, string, sizeof(string)); - if (ret > 0) - return QString::fromLatin1((char*)string, ret).trimmed(); +QString readUSBdescriptor( libusb_device_handle *handle, uint8_t index ) { + unsigned char string[ 255 ]; + int ret = libusb_get_string_descriptor_ascii( handle, index, string, sizeof( string ) ); + if ( ret > 0 ) + return QString::fromLatin1( reinterpret_cast< char * >( string ), ret ).trimmed(); else return QString(); } -void RawDevicesListModel::updateDeviceList() -{ +void RawDevicesListModel::updateDeviceList() { beginResetModel(); entries.clear(); endResetModel(); libusb_device **deviceList; - ssize_t deviceCount = libusb_get_device_list(context, &deviceList); - beginInsertRows(QModelIndex(),0,(int)deviceCount); + ssize_t deviceCount = libusb_get_device_list( context, &deviceList ); + beginInsertRows( QModelIndex(), 0, int( deviceCount ) ); - for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) { - libusb_device *device = deviceList[deviceIterator]; + for ( ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator ) { + libusb_device *device = deviceList[ deviceIterator ]; RawDeviceListEntry entry; // Get device descriptor struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(device, &descriptor); + libusb_get_device_descriptor( device, &descriptor ); entry.productId = descriptor.idProduct; entry.vendorId = descriptor.idVendor; - libusb_device_handle *handle = NULL; - int ret = libusb_open(device, &handle); - if (ret != LIBUSB_SUCCESS) { + libusb_device_handle *handle = nullptr; + int ret = libusb_open( device, &handle ); + if ( ret != LIBUSB_SUCCESS ) { entry.access = false; - entry.deviceinfo = tr("%1:%2 - No access").arg(entry.vendorId,0,16).arg(entry.productId,0,16); + entry.deviceinfo = QObject::tr( "%1:%2 - No access" ).arg( entry.vendorId, 0, 16 ).arg( entry.productId, 0, 16 ); } else { entry.access = true; - entry.devicename = readUSBdescriptor(handle, descriptor.iProduct); - entry.deviceinfo = tr("%1:%2 (%3 - %4)").arg(entry.vendorId,0,16).arg(entry.productId,0,16) - .arg(entry.devicename).arg(readUSBdescriptor(handle, descriptor.iManufacturer)); - libusb_close(handle); + entry.devicename = readUSBdescriptor( handle, descriptor.iProduct ); + entry.deviceinfo = QObject::tr( "%1:%2 (%3 - %4)" ) + .arg( entry.vendorId, 0, 16 ) + .arg( entry.productId, 0, 16 ) + .arg( entry.devicename, readUSBdescriptor( handle, descriptor.iManufacturer ) ); + libusb_close( handle ); } - entries.push_back(entry); + entries.push_back( entry ); } - libusb_free_device_list(deviceList, true); + libusb_free_device_list( deviceList, true ); endInsertRows(); } diff --git a/openhantek/src/selectdevice/rawdeviceslistmodel.h b/openhantek/src/selectdevice/rawdeviceslistmodel.h index b2ccd3bc..30a96d0c 100644 --- a/openhantek/src/selectdevice/rawdeviceslistmodel.h +++ b/openhantek/src/selectdevice/rawdeviceslistmodel.h @@ -1,31 +1,33 @@ // SPDX-License-Identifier: GPL-2.0+ + #pragma once -#include #include "rawdevicelistentry.h" -#include "usb/usbdevice.h" +#include "usb/scopedevice.h" +#include /** * Provides a Model for the Qt Model/View concept. The {@see FindDevices} is required * to update the list of available devices. */ -class RawDevicesListModel: public QAbstractTableModel { -public: - RawDevicesListModel(libusb_context *context, QObject *parent = 0); +class RawDevicesListModel : public QAbstractTableModel { + public: + RawDevicesListModel( libusb_context *context, QObject *parent = nullptr ); // QAbstractItemModel interface - virtual int rowCount(const QModelIndex &parent) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual QVariant data(const QModelIndex &index, int role) const override; + int rowCount( const QModelIndex &parent ) const override; + int columnCount( const QModelIndex &parent ) const override; + QVariant data( const QModelIndex &index, int role ) const override; void updateDeviceList(); enum Roles { - ProductIDRole = Qt::UserRole+0, - VendorIDRole = Qt::UserRole+1, - AccessRole = Qt::UserRole+2, - DeviceNameRole = Qt::UserRole+3, - EntryPointerRole = Qt::UserRole+4 + ProductIDRole = Qt::UserRole + 0, + VendorIDRole = Qt::UserRole + 1, + AccessRole = Qt::UserRole + 2, + DeviceNameRole = Qt::UserRole + 3, + EntryPointerRole = Qt::UserRole + 4 }; -private: - std::vector entries; + + private: + std::vector< RawDeviceListEntry > entries; libusb_context *context; }; diff --git a/openhantek/src/selectdevice/selectsupporteddevice.cpp b/openhantek/src/selectdevice/selectsupporteddevice.cpp index 5d2300f7..a404b882 100644 --- a/openhantek/src/selectdevice/selectsupporteddevice.cpp +++ b/openhantek/src/selectdevice/selectsupporteddevice.cpp @@ -1,120 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include "selectsupporteddevice.h" -#include #include #include +#include #include -#include "usb/uploadFirmware.h" -#include "usb/finddevices.h" -#include "dsomodel.h" #include "devicelistentry.h" #include "deviceslistmodel.h" -#include "newdevicemodelfromexisting.h" +#include "dsomodel.h" #include "modelregistry.h" +#include "usb/finddevices.h" +#include "usb/uploadFirmware.h" +#include "viewconstants.h" -SelectSupportedDevice::SelectSupportedDevice(QWidget *parent) : - QDialog(parent), - ui(new Ui::SelectSupportedDevice) -{ - ui->setupUi(this); - newDeviceFromExistingDialog = new NewDeviceModelFromExisting(this); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - qRegisterMetaType("UniqueUSBid"); - connect(ui->buttonBox, &QDialogButtonBox::accepted, [this]() { - if (ui->cmbDevices->currentIndex()!=-1) { - selectedDevice = ui->cmbDevices->currentData(Qt::UserRole).value(); +SelectSupportedDevice::SelectSupportedDevice( QWidget *parent ) : QDialog( parent ), ui( new Ui::SelectSupportedDevice ) { + ui->setupUi( this ); + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + qRegisterMetaType< UniqueUSBid >( "UniqueUSBid" ); + connect( ui->buttonBox, &QDialogButtonBox::accepted, [this]() { + if ( ui->cmbDevices->currentIndex() != -1 ) { + selectedDevice = ui->cmbDevices->currentData( Qt::UserRole ).value< UniqueUSBid >(); } QCoreApplication::instance()->quit(); - }); - connect(ui->buttonBox, &QDialogButtonBox::helpRequested, [this]() { - QDesktopServices::openUrl(QUrl("https://github.com/OpenHantek/openhantek#openhantek--")); - }); - connect(ui->btnAddDevice, &QPushButton::clicked, [this]() { - newDeviceFromExistingDialog->setModal(true); - newDeviceFromExistingDialog->show(); - }); + } ); + connect( ui->buttonBox, &QDialogButtonBox::helpRequested, []() { + QString usrManualPath( USR_MANUAL_PATH ); + QFile userManual( usrManualPath ); + if ( userManual.exists() ) + QDesktopServices::openUrl( QUrl( "file://" + usrManualPath ) ); + else + QDesktopServices::openUrl( + QUrl( "https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf" ) ); + } ); + connect( ui->btnDemoMode, &QPushButton::clicked, [this]() { demoModeClicked = true; } ); } -std::unique_ptr SelectSupportedDevice::showSelectDeviceModal(libusb_context *context) -{ - newDeviceFromExistingDialog->setUSBcontext(context); - std::unique_ptr findDevices = std::unique_ptr(new FindDevices(context)); - std::unique_ptr model = std::unique_ptr(new DevicesListModel(findDevices.get())); - ui->cmbDevices->setModel(model.get()); - connect(ui->cmbDevices, static_cast(&QComboBox::currentIndexChanged), [this](int index) { - if (index == -1) { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); +std::unique_ptr< ScopeDevice > SelectSupportedDevice::showSelectDeviceModal( libusb_context *context ) { + // newDeviceFromExistingDialog->setUSBcontext(context); + std::unique_ptr< FindDevices > findDevices = std::unique_ptr< FindDevices >( new FindDevices( context ) ); + std::unique_ptr< DevicesListModel > model = std::unique_ptr< DevicesListModel >( new DevicesListModel( findDevices.get() ) ); + ui->cmbDevices->setModel( model.get() ); + connect( ui->cmbDevices, static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ), [this]( int index ) { + if ( index == -1 ) { + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); return; } - if (ui->cmbDevices->currentData(Qt::UserRole+1).toBool()) { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - ui->labelReadyState->setText(tr("Device ready to use")); + if ( ui->cmbDevices->currentData( Qt::UserRole + 1 ).toBool() ) { // canConnect + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( true ); + ui->labelReadyState->setText( + tr( "


The device is ready for use.

Please observe the " + "" + "user manual for safe operation.

" ) ); } else { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - if (ui->cmbDevices->currentData(Qt::UserRole+2).toBool()) { - ui->labelReadyState->setText(tr("Upload in progress...")); - } else { - ui->labelReadyState->setText(tr("Connection failed!")); + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + if ( ui->cmbDevices->currentData( Qt::UserRole + 2 ).toBool() ) { // needFirmware + ui->labelReadyState->setText( tr( + "

Upload in progress ...

" + "

If the upload takes more than 30 s, please close this window
and restart the program!

" ) ); + } else { // something went wrong, inform user + ui->labelReadyState->setText( tr( "


Connection failed!

" ) + + ui->cmbDevices->currentData( Qt::UserRole + 3 ).toString() ); } } - }); + } ); - QString messageNoDevices = tr("

OpenHantek did not find any compatible devices.

" - "

" - "Don't forget to switch your device into oscilloscope mode if it has multiple modes.

" - ); - #if defined(Q_OS_WIN) - messageNoDevices += tr("

Please make sure you have installed the windows usb driver correctly

"); - #elif defined(Q_OS_LINUX) - QFile file("/lib/udev/rules.d/60-hantek.rules"); - if (!file.exists()) { - messageNoDevices += tr("

Please make sure you have copied the udev rules file to %1 for correct USB access permissions.

").arg(file.fileName()); - } - #else - #endif - messageNoDevices += tr("

Visit the build and run instruction " - "website for help.

"); + QString messageNoDevices = tr( "

OpenHantek6022 is searching for compatible devices ...

" + "

" + "Don't forget to switch your device into oscilloscope mode if it has multiple modes.

" ); +#if defined( Q_OS_WIN ) + messageNoDevices += tr( "

Please make sure you have installed the windows usb driver correctly

" ); +#elif defined( Q_OS_LINUX ) + QFile libRules( "/lib/udev/rules.d/60-hantek.rules" ); + QFile etcRules( "/etc/udev/rules.d/60-hantek.rules" ); + if ( !libRules.exists() && !etcRules.exists() ) { + messageNoDevices += + tr( "

Please make sure you have copied the udev rules file to %1 for correct USB access permissions.

" ) + .arg( libRules.fileName() ); + } +#endif + messageNoDevices += + tr( "

Visit the build and run instruction " + "website for help.

" ); + messageNoDevices += tr( "

Even without a device you can explore the program's function. " + "Just press the Demo Mode button below.

" ); updateSupportedDevices(); QTimer timer; - timer.setInterval(1000); - connect(&timer, &QTimer::timeout, [this, &model, &findDevices, &messageNoDevices]() { - if (findDevices->updateDeviceList()) + timer.setInterval( 1000 ); + connect( &timer, &QTimer::timeout, [this, &model, &findDevices, &messageNoDevices]() { + if ( findDevices->updateDeviceList() ) { // searching... model->updateDeviceList(); - if (model->rowCount(QModelIndex())) { - ui->cmbDevices->setCurrentIndex(0); + } + if ( model->rowCount( QModelIndex() ) ) { // device ready + ui->cmbDevices->setCurrentIndex( 0 ); + // HACK: "click()" the "OK" button (if enabled) to start the scope automatically + if ( ui->buttonBox->button( QDialogButtonBox::Ok )->isEnabled() ) { // if scope is ready to run + ui->buttonBox->button( QDialogButtonBox::Ok )->click(); // start it without user activity + } } else { - ui->labelReadyState->setText(messageNoDevices); + ui->labelReadyState->setText( messageNoDevices ); } - }); + } ); timer.start(); - QCoreApplication::sendEvent(&timer, new QTimerEvent(timer.timerId())); // immediate timer event + QCoreApplication::sendEvent( &timer, new QTimerEvent( timer.timerId() ) ); // immediate timer event show(); QCoreApplication::instance()->exec(); timer.stop(); close(); - - return findDevices->takeDevice(selectedDevice); + if ( demoModeClicked ) + return std::unique_ptr< ScopeDevice >( new ScopeDevice() ); + return findDevices->takeDevice( selectedDevice ); } -void SelectSupportedDevice::showLibUSBFailedDialogModel(int error) -{ - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - ui->btnAddDevice->setEnabled(false); - ui->labelReadyState->setText(tr("Can't initalize USB: %1").arg(libUsbErrorString(error))); +void SelectSupportedDevice::showLibUSBFailedDialogModel( int error ) { + ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + ui->labelReadyState->setText( tr( "Can't initalize USB: %1" ).arg( libUsbErrorString( error ) ) ); show(); QCoreApplication::instance()->exec(); close(); } -void SelectSupportedDevice::updateSupportedDevices() -{ +void SelectSupportedDevice::updateSupportedDevices() { QString devices; - for (const DSOModel* model: ModelRegistry::get()->models()) { - devices.append(QString::fromStdString(model->name)).append(" "); + for ( const DSOModel *model : ModelRegistry::get()->models() ) { + devices.append( QString::fromStdString( model->name ) ).append( " " ); } - ui->labelSupportedDevices->setText(devices); + ui->labelSupportedDevices->setText( devices ); } diff --git a/openhantek/src/selectdevice/selectsupporteddevice.h b/openhantek/src/selectdevice/selectsupporteddevice.h index eb248697..dfa6eda4 100644 --- a/openhantek/src/selectdevice/selectsupporteddevice.h +++ b/openhantek/src/selectdevice/selectsupporteddevice.h @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0+ + #pragma once #include "ui_selectsupporteddevice.h" #include +#include +#include "usb/scopedevice.h" #include -#include "usb/usbdevice.h" struct libusb_context; -class NewDeviceModelFromExisting; /** * Offers the user a device selection dialog. If you call any of the -Modal methods, @@ -19,18 +20,18 @@ class NewDeviceModelFromExisting; * An example to get a user selected device: * std::unique_ptr device = SelectDevice().showSelectDeviceModal(context); */ -class SelectSupportedDevice : public QDialog -{ +class SelectSupportedDevice : public QDialog { Q_OBJECT -public: - explicit SelectSupportedDevice(QWidget *parent = 0); - std::unique_ptr showSelectDeviceModal(libusb_context *context); - void showLibUSBFailedDialogModel(int error); -private: + public: + explicit SelectSupportedDevice( QWidget *parent = nullptr ); + std::unique_ptr< ScopeDevice > showSelectDeviceModal( libusb_context *context ); + void showLibUSBFailedDialogModel( int error ); + + private: void updateDeviceList(); void updateSupportedDevices(); - std::unique_ptr ui; + std::unique_ptr< Ui::SelectSupportedDevice > ui; UniqueUSBid selectedDevice = 0; - NewDeviceModelFromExisting* newDeviceFromExistingDialog; + bool demoModeClicked = false; }; diff --git a/openhantek/src/selectdevice/selectsupporteddevice.ui b/openhantek/src/selectdevice/selectsupporteddevice.ui index 0910964d..653fa658 100644 --- a/openhantek/src/selectdevice/selectsupporteddevice.ui +++ b/openhantek/src/selectdevice/selectsupporteddevice.ui @@ -6,7 +6,7 @@ 0 0 - 550 + 600 400 @@ -15,99 +15,68 @@ - - - QTabWidget::Rounded - - - 0 - - - - Supported device - - - - - - - 0 - 0 - - - - Devices: - - - - - - - ... - - - true - - - - - - - - - - ... - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Try unsupported - - - - - - <html><head/><body><p>Your device might not directly be supported by OpenHantek. But it might be compatible to one of the existing devices. Help us to identify those devices and report back on our <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#0000ff;">issue tracker</span></a>.</p></body></html> - - - Qt::AutoText - - - true - - - true - - - - - - - Add new device from template - - - - - + + + + + + + 0 + 0 + + + + Devices: + + + + + + + ... + + + true + + + + + + + ... + + + true + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Demo Mode + + + + @@ -131,8 +100,8 @@ accept() - 257 - 302 + 263 + 393 157 @@ -147,8 +116,8 @@ reject() - 312 - 302 + 318 + 393 286 @@ -156,5 +125,21 @@ + + btnDemoMode + clicked() + SelectSupportedDevice + accept() + + + 232 + 332 + + + -10 + 316 + + + diff --git a/openhantek/src/settings.cpp b/openhantek/src/settings.cpp deleted file mode 100644 index d7f1d476..00000000 --- a/openhantek/src/settings.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include -#include -#include -#include - -#include "settings.h" - -#include "dsowidget.h" - -/// \brief Set the number of channels. -/// \param channels The new channel count, that will be applied to lists. -DsoSettings::DsoSettings(const Dso::ControlSpecification* deviceSpecification) { - // Add new channels to the list - while (scope.spectrum.size() < deviceSpecification->channels) { - // Spectrum - DsoSettingsScopeSpectrum newSpectrum; - newSpectrum.name = QApplication::tr("SP%1").arg(scope.spectrum.size()+1); - scope.spectrum.push_back(newSpectrum); - - // Voltage - DsoSettingsScopeVoltage newVoltage; - newVoltage.name = QApplication::tr("CH%1").arg(scope.voltage.size()+1); - scope.voltage.push_back(newVoltage); - - view.screen.voltage.push_back(QColor::fromHsv((int)(scope.spectrum.size()-1) * 60, 0xff, 0xff)); - view.screen.spectrum.push_back(view.screen.voltage.back().lighter()); - view.print.voltage.push_back(view.screen.voltage.back().darker(120)); - view.print.spectrum.push_back(view.screen.voltage.back().darker()); - } - - DsoSettingsScopeSpectrum newSpectrum; - newSpectrum.name = QApplication::tr("SPM"); - scope.spectrum.push_back(newSpectrum); - - DsoSettingsScopeVoltage newVoltage; - newVoltage.couplingOrMathIndex = (unsigned)Dso::MathMode::ADD_CH1_CH2; - newVoltage.name = QApplication::tr("MATH"); - scope.voltage.push_back(newVoltage); - - view.screen.voltage.push_back(QColor(0x7f, 0x7f, 0x7f, 0xff)); - view.screen.spectrum.push_back(view.screen.voltage.back().lighter()); - view.print.voltage.push_back(view.screen.voltage.back()); - view.print.spectrum.push_back(view.print.voltage.back().darker()); - - - load(); -} - -bool DsoSettings::setFilename(const QString &filename) { - std::unique_ptr local = std::unique_ptr(new QSettings(filename, QSettings::IniFormat)); - if (local->status() != QSettings::NoError) { - qWarning() << "Could not change the settings file to " << filename; - return false; - } - store.swap(local); - return true; -} - -void DsoSettings::load() { - // General options - store->beginGroup("options"); - if (store->contains("alwaysSave")) alwaysSave = store->value("alwaysSave").toBool(); - store->endGroup(); - - store->beginGroup("exporting"); - if (store->contains("imageSize")) exporting.imageSize = store->value("imageSize").toSize(); - store->endGroup(); - - // Oscilloscope settings - store->beginGroup("scope"); - // Horizontal axis - store->beginGroup("horizontal"); - if (store->contains("format")) scope.horizontal.format = (Dso::GraphFormat)store->value("format").toInt(); - if (store->contains("frequencybase")) - scope.horizontal.frequencybase = store->value("frequencybase").toDouble(); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - QString name; - name = QString("marker%1").arg(marker); - if (store->contains(name)) scope.setMarker(marker, store->value(name).toDouble()); - } - if (store->contains("timebase")) scope.horizontal.timebase = store->value("timebase").toDouble(); - if (store->contains("recordLength")) scope.horizontal.recordLength = store->value("recordLength").toUInt(); - if (store->contains("samplerate")) scope.horizontal.samplerate = store->value("samplerate").toDouble(); - if (store->contains("samplerateSet")) scope.horizontal.samplerateSource = (DsoSettingsScopeHorizontal::SamplerateSource)store->value("samplerateSet").toInt(); - store->endGroup(); - // Trigger - store->beginGroup("trigger"); - if (store->contains("mode")) scope.trigger.mode = (Dso::TriggerMode)store->value("mode").toUInt(); - if (store->contains("position")) scope.trigger.position = store->value("position").toDouble(); - if (store->contains("slope")) scope.trigger.slope = (Dso::Slope)store->value("slope").toUInt(); - if (store->contains("source")) scope.trigger.source = store->value("source").toUInt(); - if (store->contains("special")) scope.trigger.special = store->value("special").toInt(); - store->endGroup(); - // Spectrum - for (ChannelID channel = 0; channel < scope.spectrum.size(); ++channel) { - store->beginGroup(QString("spectrum%1").arg(channel)); - if (store->contains("magnitude")) - scope.spectrum[channel].magnitude = store->value("magnitude").toDouble(); - if (store->contains("offset")) scope.spectrum[channel].offset = store->value("offset").toDouble(); - if (store->contains("used")) scope.spectrum[channel].used = store->value("used").toBool(); - store->beginGroup("cursor"); - if (store->contains("shape")) scope.spectrum[channel].cursor.shape = - DsoSettingsScopeCursor::CursorShape(store->value("shape").toUInt()); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - QString name; - name = QString("x%1").arg(marker); - if (store->contains(name)) scope.spectrum[channel].cursor.pos[marker].setX(store->value(name).toDouble()); - name = QString("y%1").arg(marker); - if (store->contains(name)) scope.spectrum[channel].cursor.pos[marker].setY(store->value(name).toDouble()); - } - store->endGroup(); - store->endGroup(); - } - // Vertical axis - for (ChannelID channel = 0; channel < scope.voltage.size(); ++channel) { - store->beginGroup(QString("vertical%1").arg(channel)); - if (store->contains("gainStepIndex")) scope.voltage[channel].gainStepIndex = store->value("gainStepIndex").toUInt(); - if (store->contains("couplingOrMathIndex")) scope.voltage[channel].couplingOrMathIndex = - store->value("couplingOrMathIndex").toUInt(); - if (store->contains("inverted")) scope.voltage[channel].inverted = store->value("inverted").toBool(); - if (store->contains("offset")) scope.voltage[channel].offset = store->value("offset").toDouble(); - if (store->contains("trigger")) scope.voltage[channel].trigger = store->value("trigger").toDouble(); - if (store->contains("used")) scope.voltage[channel].used = store->value("used").toBool(); - store->beginGroup("cursor"); - if (store->contains("shape")) scope.voltage[channel].cursor.shape = - DsoSettingsScopeCursor::CursorShape(store->value("shape").toUInt()); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - QString name; - name = QString("x%1").arg(marker); - if (store->contains(name)) scope.voltage[channel].cursor.pos[marker].setX(store->value(name).toDouble()); - name = QString("y%1").arg(marker); - if (store->contains(name)) scope.voltage[channel].cursor.pos[marker].setY(store->value(name).toDouble()); - } - store->endGroup(); - store->endGroup(); - } - - // Post processing - if (store->contains("spectrumLimit")) post.spectrumLimit = store->value("spectrumLimit").toDouble(); - if (store->contains("spectrumReference")) - post.spectrumReference = store->value("spectrumReference").toDouble(); - if (store->contains("spectrumWindow")) - post.spectrumWindow = (Dso::WindowFunction)store->value("spectrumWindow").toInt(); - store->endGroup(); - - // View - store->beginGroup("view"); - // Colors - store->beginGroup("color"); - DsoSettingsColorValues *colors; - for (int mode = 0; mode < 2; ++mode) { - if (mode == 0) { - colors = &view.screen; - store->beginGroup("screen"); - } else { - colors = &view.print; - store->beginGroup("print"); - } - - if (store->contains("axes")) colors->axes = store->value("axes").value(); - if (store->contains("background")) colors->background = store->value("background").value(); - if (store->contains("border")) colors->border = store->value("border").value(); - if (store->contains("grid")) colors->grid = store->value("grid").value(); - if (store->contains("markers")) colors->markers = store->value("markers").value(); - for (ChannelID channel = 0; channel < scope.spectrum.size(); ++channel) { - QString key = QString("spectrum%1").arg(channel); - if (store->contains(key)) colors->spectrum[channel] = store->value(key).value(); - } - if (store->contains("text")) colors->text = store->value("text").value(); - for (ChannelID channel = 0; channel < scope.voltage.size(); ++channel) { - QString key = QString("voltage%1").arg(channel); - if (store->contains(key)) colors->voltage[channel] = store->value(key).value(); - } - store->endGroup(); - } - store->endGroup(); - // Other view settings - if (store->contains("digitalPhosphor")) view.digitalPhosphor = store->value("digitalPhosphor").toBool(); - if (store->contains("interpolation")) - view.interpolation = (Dso::InterpolationMode)store->value("interpolation").toInt(); - if (store->contains("screenColorImages")) view.screenColorImages = store->value("screenColorImages").toBool(); - if (store->contains("zoom")) view.zoom = store->value("zoom").toBool(); - if (store->contains("cursorGridPosition")) - view.cursorGridPosition = (Qt::ToolBarArea)store->value("cursorGridPosition").toUInt(); - if (store->contains("cursorsVisible")) view.cursorsVisible = store->value("cursorsVisible").toBool(); - store->endGroup(); - - store->beginGroup("window"); - mainWindowGeometry = store->value("geometry").toByteArray(); - mainWindowState = store->value("state").toByteArray(); - store->endGroup(); -} - -void DsoSettings::save() { - // Main window layout and other general options - store->beginGroup("options"); - store->setValue("alwaysSave", alwaysSave); - store->endGroup(); - - store->beginGroup("exporting"); - store->setValue("imageSize", exporting.imageSize); - store->endGroup(); - - // Oszilloskope settings - store->beginGroup("scope"); - // Horizontal axis - store->beginGroup("horizontal"); - store->setValue("format", scope.horizontal.format); - store->setValue("frequencybase", scope.horizontal.frequencybase); - for (int marker = 0; marker < MARKER_COUNT; ++marker) - store->setValue(QString("marker%1").arg(marker), scope.getMarker(marker)); - store->setValue("timebase", scope.horizontal.timebase); - store->setValue("recordLength", scope.horizontal.recordLength); - store->setValue("samplerate", scope.horizontal.samplerate); - store->setValue("samplerateSet", (int)scope.horizontal.samplerateSource); - store->endGroup(); - // Trigger - store->beginGroup("trigger"); - store->setValue("mode", (unsigned)scope.trigger.mode); - store->setValue("position", scope.trigger.position); - store->setValue("slope", (unsigned)scope.trigger.slope); - store->setValue("source", scope.trigger.source); - store->setValue("special", scope.trigger.special); - store->endGroup(); - // Spectrum - for (ChannelID channel = 0; channel < scope.spectrum.size(); ++channel) { - store->beginGroup(QString("spectrum%1").arg(channel)); - store->setValue("magnitude", scope.spectrum[channel].magnitude); - store->setValue("offset", scope.spectrum[channel].offset); - store->setValue("used", scope.spectrum[channel].used); - store->beginGroup("cursor"); - store->setValue("shape", scope.spectrum[channel].cursor.shape); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - QString name; - name = QString("x%1").arg(marker); - store->setValue(name, scope.spectrum[channel].cursor.pos[marker].x()); - name = QString("y%1").arg(marker); - store->setValue(name, scope.spectrum[channel].cursor.pos[marker].y()); - } - store->endGroup(); - store->endGroup(); - } - // Vertical axis - for (ChannelID channel = 0; channel < scope.voltage.size(); ++channel) { - store->beginGroup(QString("vertical%1").arg(channel)); - store->setValue("gainStepIndex", scope.voltage[channel].gainStepIndex); - store->setValue("couplingOrMathIndex", scope.voltage[channel].couplingOrMathIndex); - store->setValue("inverted", scope.voltage[channel].inverted); - store->setValue("offset", scope.voltage[channel].offset); - store->setValue("trigger", scope.voltage[channel].trigger); - store->setValue("used", scope.voltage[channel].used); - store->beginGroup("cursor"); - store->setValue("shape", scope.voltage[channel].cursor.shape); - for (int marker = 0; marker < MARKER_COUNT; ++marker) { - QString name; - name = QString("x%1").arg(marker); - store->setValue(name, scope.voltage[channel].cursor.pos[marker].x()); - name = QString("y%1").arg(marker); - store->setValue(name, scope.voltage[channel].cursor.pos[marker].y()); - } - store->endGroup(); - store->endGroup(); - } - - // Post processing - store->setValue("spectrumLimit", post.spectrumLimit); - store->setValue("spectrumReference", post.spectrumReference); - store->setValue("spectrumWindow", (int)post.spectrumWindow); - store->endGroup(); - - // View - store->beginGroup("view"); - // Colors - - store->beginGroup("color"); - DsoSettingsColorValues *colors; - for (int mode = 0; mode < 2; ++mode) { - if (mode == 0) { - colors = &view.screen; - store->beginGroup("screen"); - } else { - colors = &view.print; - store->beginGroup("print"); - } - - store->setValue("axes", colors->axes.name(QColor::HexArgb)); - store->setValue("background", colors->background.name(QColor::HexArgb)); - store->setValue("border", colors->border.name(QColor::HexArgb)); - store->setValue("grid", colors->grid.name(QColor::HexArgb)); - store->setValue("markers", colors->markers.name(QColor::HexArgb)); - for (ChannelID channel = 0; channel < scope.spectrum.size(); ++channel) - store->setValue(QString("spectrum%1").arg(channel), colors->spectrum[channel].name(QColor::HexArgb)); - store->setValue("text", colors->text.name(QColor::HexArgb)); - for (ChannelID channel = 0; channel < scope.voltage.size(); ++channel) - store->setValue(QString("voltage%1").arg(channel), colors->voltage[channel].name(QColor::HexArgb)); - store->endGroup(); - } - store->endGroup(); - - // Other view settings - store->setValue("digitalPhosphor", view.digitalPhosphor); - store->setValue("interpolation", view.interpolation); - store->setValue("screenColorImages", view.screenColorImages); - store->setValue("zoom", view.zoom); - store->setValue("cursorGridPosition", view.cursorGridPosition); - store->setValue("cursorsVisible", view.cursorsVisible); - store->endGroup(); - - store->beginGroup("window"); - store->setValue("geometry", mainWindowGeometry); - store->setValue("state", mainWindowState); - store->endGroup(); -} diff --git a/openhantek/src/settings.h b/openhantek/src/settings.h deleted file mode 100644 index 17b2eff7..00000000 --- a/openhantek/src/settings.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include -#include -#include - -#include "exporting/exportsettings.h" -#include "post/postprocessingsettings.h" -#include "scopesettings.h" -#include "viewsettings.h" - -/// \brief Holds the settings of the program. -class DsoSettings { - public: - explicit DsoSettings(const Dso::ControlSpecification *deviceSpecification); - bool setFilename(const QString &filename); - - DsoSettingsExport exporting; ///< General options of the program - DsoSettingsScope scope; ///< All oscilloscope related settings - DsoSettingsView view; ///< All view related settings - DsoSettingsPostProcessing post; ///< All post processing related settings - bool alwaysSave = true; ///< Always save the settings on exit - - QByteArray mainWindowGeometry; ///< Geometry of the main window - QByteArray mainWindowState; ///< State of docking windows and toolbars - - /// \brief Read the settings from the last session or another file. - void load(); - - /// \brief Save the settings to the harddisk. - void save(); - - private: - std::unique_ptr store = std::unique_ptr(new QSettings); -}; diff --git a/openhantek/src/usb/ezusb.cpp b/openhantek/src/usb/ezusb.cpp index 1d8c6417..cff2cda9 100644 --- a/openhantek/src/usb/ezusb.cpp +++ b/openhantek/src/usb/ezusb.cpp @@ -20,18 +20,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#define _CRT_SECURE_NO_WARNINGS + #include #include #include #include #include +// No Qt header loaded -> do not use Q_OS_FREEBSD, use __FreeBSD__ +#ifdef __FreeBSD__ +#include +#else #include +#endif #include "ezusb.h" -#define logerror(...) fprintf(stderr, __VA_ARGS__) +#define logerror( ... ) fprintf( stderr, __VA_ARGS__ ) /* * This file contains functions for uploading firmware into Cypress @@ -48,17 +53,18 @@ * The Cypress FX parts are largely compatible with the Anchorchip ones. */ -int verbose = 1; +int verbose = 0; /* * return true if [addr,addr+len] includes external RAM * for Anchorchips EZ-USB or Cypress EZ-USB FX */ -static bool fx_is_external(uint32_t addr, size_t len) { +static bool fx_is_external( uint32_t addr, size_t len ) { /* with 8KB RAM, 0x0000-0x1b3f can be written * we can't tell if it's a 4KB device here */ - if (addr <= 0x1b3f) return ((addr + len) > 0x1b40); + if ( addr <= 0x1b3f ) + return ( ( addr + len ) > 0x1b40 ); /* there may be more RAM; unclear if we can write it. * some bulk buffers may be unused, 0x1b3f-0x1f3f @@ -71,13 +77,14 @@ static bool fx_is_external(uint32_t addr, size_t len) { * return true if [addr,addr+len] includes external RAM * for Cypress EZ-USB FX2 */ -static bool fx2_is_external(uint32_t addr, size_t len) { +static bool fx2_is_external( uint32_t addr, size_t len ) { /* 1st 8KB for data/code, 0x0000-0x1fff */ - if (addr <= 0x1fff) return ((addr + len) > 0x2000); + if ( addr <= 0x1fff ) + return ( ( addr + len ) > 0x2000 ); /* and 512 for data, 0xe000-0xe1ff */ - else if (addr >= 0xe000 && addr <= 0xe1ff) - return ((addr + len) > 0xe200); + else if ( addr >= 0xe000 && addr <= 0xe1ff ) + return ( ( addr + len ) > 0xe200 ); /* otherwise, it's certainly external */ else @@ -88,13 +95,14 @@ static bool fx2_is_external(uint32_t addr, size_t len) { * return true if [addr,addr+len] includes external RAM * for Cypress EZ-USB FX2LP */ -static bool fx2lp_is_external(uint32_t addr, size_t len) { +static bool fx2lp_is_external( uint32_t addr, size_t len ) { /* 1st 16KB for data/code, 0x0000-0x3fff */ - if (addr <= 0x3fff) return ((addr + len) > 0x4000); + if ( addr <= 0x3fff ) + return ( ( addr + len ) > 0x4000 ); /* and 512 for data, 0xe000-0xe1ff */ - else if (addr >= 0xe000 && addr <= 0xe1ff) - return ((addr + len) > 0xe200); + else if ( addr >= 0xe000 && addr <= 0xe1ff ) + return ( ( addr + len ) > 0xe200 ); /* otherwise, it's certainly external */ else @@ -116,82 +124,86 @@ static bool fx2lp_is_external(uint32_t addr, size_t len) { /* * Issues the specified vendor-specific write request. */ -static int ezusb_write(libusb_device_handle *device, const char *label, uint8_t opcode, uint32_t addr, - const unsigned char *data, size_t len) { +static int ezusb_write( libusb_device_handle *device, const char *label, uint8_t opcode, uint32_t addr, const unsigned char *data, + size_t len ) { int status; - if (verbose > 1) logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len); - status = libusb_control_transfer(device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - opcode, addr & 0xFFFF, addr >> 16, (unsigned char *)data, (uint16_t)len, 1000); - if (status != (signed)len) { - if (status < 0) - logerror("%s: %s\n", label, libusb_error_name(status)); + if ( verbose > 1 ) + logerror( "%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, unsigned( len ), unsigned( len ) ); + status = libusb_control_transfer( device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, opcode, + addr & 0xFFFF, addr >> 16, const_cast< unsigned char * >( data ), uint16_t( len ), 1000 ); + if ( status != signed( len ) ) { + if ( status < 0 ) + logerror( "%s: %s\n", label, libusb_error_name( status ) ); else - logerror("%s ==> %d\n", label, status); + logerror( "%s ==> %d\n", label, status ); } - return (status < 0) ? -EIO : 0; + return ( status < 0 ) ? -EIO : 0; } /* * Issues the specified vendor-specific read request. */ -static int ezusb_read(libusb_device_handle *device, const char *label, uint8_t opcode, uint32_t addr, - const unsigned char *data, size_t len) { +static int ezusb_read( libusb_device_handle *device, const char *label, uint8_t opcode, uint32_t addr, const unsigned char *data, + size_t len ) { int status; - if (verbose > 1) logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len); - status = libusb_control_transfer(device, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - opcode, addr & 0xFFFF, addr >> 16, (unsigned char *)data, (uint16_t)len, 1000); - if (status != (signed)len) { - if (status < 0) - logerror("%s: %s\n", label, libusb_error_name(status)); + if ( verbose > 1 ) + logerror( "%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, unsigned( len ), unsigned( len ) ); + status = libusb_control_transfer( device, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, opcode, + addr & 0xFFFF, addr >> 16, const_cast< unsigned char * >( data ), uint16_t( len ), 1000 ); + if ( status != signed( len ) ) { + if ( status < 0 ) + logerror( "%s: %s\n", label, libusb_error_name( status ) ); else - logerror("%s ==> %d\n", label, status); + logerror( "%s ==> %d\n", label, status ); } - return (status < 0) ? -EIO : 0; + return ( status < 0 ) ? -EIO : 0; } /* * Modifies the CPUCS register to stop or reset the CPU. * Returns false on error. */ -static bool ezusb_cpucs(libusb_device_handle *device, uint32_t addr, bool doRun) { +static bool ezusb_cpucs( libusb_device_handle *device, uint32_t addr, bool doRun ) { int status; uint8_t data = doRun ? 0x00 : 0x01; - if (verbose) logerror("%s\n", data ? "stop CPU" : "reset CPU"); - status = libusb_control_transfer(device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - RW_INTERNAL, addr & 0xFFFF, addr >> 16, &data, 1, 1000); - if ((status != 1) && - /* We may get an I/O error from libusb as the device disappears */ - ((!doRun) || (status != LIBUSB_ERROR_IO))) { + if ( verbose ) + logerror( "%s\n", data ? "stop CPU" : "reset CPU" ); + status = libusb_control_transfer( device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + RW_INTERNAL, addr & 0xFFFF, addr >> 16, &data, 1, 1000 ); + if ( ( status != 1 ) && + /* We may get an I/O error from libusb as the device disappears */ + ( ( !doRun ) || ( status != LIBUSB_ERROR_IO ) ) ) { const char *mesg = "can't modify CPUCS"; - if (status < 0) - logerror("%s: %s\n", mesg, libusb_error_name(status)); + if ( status < 0 ) + logerror( "%s: %s\n", mesg, libusb_error_name( status ) ); else - logerror("%s\n", mesg); + logerror( "%s\n", mesg ); return false; } else return true; } /* - * Send an FX3 jumpt to address command + * Send an FX3 jump to address command * Returns false on error. */ -static bool ezusb_fx3_jump(libusb_device_handle *device, uint32_t addr) { +static bool ezusb_fx3_jump( libusb_device_handle *device, uint32_t addr ) { int status; - if (verbose) logerror("transfer execution to Program Entry at 0x%08x\n", addr); - status = libusb_control_transfer(device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - RW_INTERNAL, addr & 0xFFFF, addr >> 16, NULL, 0, 1000); + if ( verbose ) + logerror( "transfer execution to Program Entry at 0x%08x\n", addr ); + status = libusb_control_transfer( device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + RW_INTERNAL, addr & 0xFFFF, addr >> 16, nullptr, 0, 1000 ); /* We may get an I/O error from libusb as the device disappears */ - if ((status != 0) && (status != LIBUSB_ERROR_IO)) { + if ( ( status != 0 ) && ( status != LIBUSB_ERROR_IO ) ) { const char *mesg = "failed to send jump command"; - if (status < 0) - logerror("%s: %s\n", mesg, libusb_error_name(status)); + if ( status < 0 ) + logerror( "%s: %s\n", mesg, libusb_error_name( status ) ); else - logerror("%s\n", mesg); + logerror( "%s\n", mesg ); return false; } else return true; @@ -215,9 +227,9 @@ static bool ezusb_fx3_jump(libusb_device_handle *device, uint32_t addr) { * Caller is responsible for halting CPU as needed, such as when * overwriting a second stage loader. */ -static int parse_ihex(FILE *image, void *context, bool (*is_external)(uint32_t addr, size_t len), - int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len)) { - unsigned char data[1023]; +static int parse_ihex( FILE *image, void *context, bool ( *is_external )( uint32_t addr, size_t len ), + int ( *poke )( void *context, uint32_t addr, bool external, const unsigned char *data, size_t len ) ) { + unsigned char data[ 1023 ]; uint32_t data_addr = 0; size_t data_len = 0; int rc; @@ -234,100 +246,108 @@ static int parse_ihex(FILE *image, void *context, bool (*is_external)(uint32_t a * Note that EEPROM segments max out at 1023 bytes; the upload protocol * allows segments of up to 64 KBytes (more than a loader could handle). */ - for (;;) { - char buf[512], *cp; + for ( ;; ) { + char buf[ 512 ], *cp; char tmp, type; size_t len; unsigned idx, off; - cp = fgets(buf, sizeof(buf), image); - if (cp == NULL) { - logerror("EOF without EOF record!\n"); + cp = fgets( buf, sizeof( buf ), image ); + if ( cp == nullptr ) { + logerror( "EOF without EOF record!\n" ); break; } /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */ - if (buf[0] == '#') continue; + if ( buf[ 0 ] == '#' ) + continue; - if (buf[0] != ':') { - logerror("not an ihex record: %s", buf); + if ( buf[ 0 ] != ':' ) { + logerror( "not an ihex record: %s", buf ); return -2; } /* ignore any newline */ - cp = strchr(buf, '\n'); - if (cp) *cp = 0; + cp = strchr( buf, '\n' ); + if ( cp ) + *cp = 0; - if (verbose >= 3) logerror("** LINE: %s\n", buf); + if ( verbose >= 3 ) + logerror( "** LINE: %s\n", buf ); /* Read the length field (up to 16 bytes) */ - tmp = buf[3]; - buf[3] = 0; - len = strtoul(buf + 1, NULL, 16); - buf[3] = tmp; + tmp = buf[ 3 ]; + buf[ 3 ] = 0; + len = strtoul( buf + 1, nullptr, 16 ); + buf[ 3 ] = tmp; /* Read the target offset (address up to 64KB) */ - tmp = buf[7]; - buf[7] = 0; - off = (int)strtoul(buf + 3, NULL, 16); - buf[7] = tmp; + tmp = buf[ 7 ]; + buf[ 7 ] = 0; + off = unsigned( strtoul( buf + 3, nullptr, 16 ) ); + buf[ 7 ] = tmp; /* Initialize data_addr */ - if (first_line) { + if ( first_line ) { data_addr = off; first_line = 0; } /* Read the record type */ - tmp = buf[9]; - buf[9] = 0; - type = (char)strtoul(buf + 7, NULL, 16); - buf[9] = tmp; + tmp = buf[ 9 ]; + buf[ 9 ] = 0; + type = char( strtoul( buf + 7, nullptr, 16 ) ); + buf[ 9 ] = tmp; /* If this is an EOF record, then make it so. */ - if (type == 1) { - if (verbose >= 2) logerror("EOF on hexfile\n"); + if ( type == 1 ) { + if ( verbose >= 2 ) + logerror( "EOF on hexfile\n" ); break; } - if (type != 0) { - logerror("unsupported record type: %u\n", type); + if ( type != 0 ) { + logerror( "unsupported record type: %d\n", type ); return -3; } - if ((len * 2) + 11 > strlen(buf)) { - logerror("record too short?\n"); + if ( ( len * 2 ) + 11 > strlen( buf ) ) { + logerror( "record too short?\n" ); return -4; } /* flush the saved data if it's not contiguous, - * or when we've buffered as much as we can. - */ - if (data_len != 0 && (off != (data_addr + data_len) - /* || !merge */ - || (data_len + len) > sizeof(data))) { - if (is_external) external = is_external(data_addr, data_len); - rc = poke(context, data_addr, external, data, data_len); - if (rc < 0) return -1; + * or when we've buffered as much as we can. + */ + if ( data_len != 0 && ( off != ( data_addr + data_len ) + /* || !merge */ + || ( data_len + len ) > sizeof( data ) ) ) { + if ( is_external ) + external = is_external( data_addr, data_len ); + rc = poke( context, data_addr, external, data, data_len ); + if ( rc < 0 ) + return -1; data_addr = off; data_len = 0; } /* append to saved data, flush later */ - for (idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2) { - tmp = cp[2]; - cp[2] = 0; - data[data_len + idx] = (uint8_t)strtoul(cp, NULL, 16); - cp[2] = tmp; + for ( idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2 ) { + tmp = cp[ 2 ]; + cp[ 2 ] = 0; + data[ data_len + idx ] = uint8_t( strtoul( cp, nullptr, 16 ) ); + cp[ 2 ] = tmp; } data_len += len; } /* flush any data remaining */ - if (data_len != 0) { - if (is_external) external = is_external(data_addr, data_len); - rc = poke(context, data_addr, external, data, data_len); - if (rc < 0) return -1; + if ( data_len != 0 ) { + if ( is_external ) + external = is_external( data_addr, data_len ); + rc = poke( context, data_addr, external, data, data_len ); + if ( rc < 0 ) + return -1; } return 0; } @@ -351,33 +371,36 @@ struct ram_poke_context { #define RETRY_LIMIT 5 -static int ram_poke(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len) { - struct ram_poke_context *ctx = (struct ram_poke_context *)context; +static int ram_poke( void *context, uint32_t addr, bool external, const unsigned char *data, size_t len ) { + struct ram_poke_context *ctx = static_cast< struct ram_poke_context * >( context ); int rc; unsigned retry = 0; - switch (ctx->mode) { + switch ( ctx->mode ) { case internal_only: /* CPU should be stopped */ - if (external) { - logerror("can't write %u bytes external memory at 0x%08x\n", (unsigned)len, addr); + if ( external ) { + logerror( "can't write %u bytes external memory at 0x%08x\n", unsigned( len ), addr ); return -EINVAL; } break; case skip_internal: /* CPU must be running */ - if (!external) { - if (verbose >= 2) { logerror("SKIP on-chip RAM, %u bytes at 0x%08x\n", (unsigned)len, addr); } + if ( !external ) { + if ( verbose >= 2 ) { + logerror( "SKIP on-chip RAM, %u bytes at 0x%08x\n", unsigned( len ), addr ); + } return 0; } break; case skip_external: /* CPU should be stopped */ - if (external) { - if (verbose >= 2) { logerror("SKIP external RAM, %u bytes at 0x%08x\n", (unsigned)len, addr); } + if ( external ) { + if ( verbose >= 2 ) { + logerror( "SKIP external RAM, %u bytes at 0x%08x\n", unsigned( len ), addr ); + } return 0; } break; case _undef: - default: - logerror("bug\n"); + logerror( "bug\n" ); return -EDOM; } @@ -387,10 +410,11 @@ static int ram_poke(void *context, uint32_t addr, bool external, const unsigned /* Retry this till we get a real error. Control messages are not * NAKed (just dropped) so time out means is a real problem. */ - while ((rc = ezusb_write(ctx->device, external ? "write external" : "write on-chip", - external ? RW_MEMORY : RW_INTERNAL, addr, data, len)) < 0 && - retry < RETRY_LIMIT) { - if (rc != LIBUSB_ERROR_TIMEOUT) break; + while ( ( rc = ezusb_write( ctx->device, external ? "write external" : "write on-chip", external ? RW_MEMORY : RW_INTERNAL, + addr, data, len ) ) < 0 && + retry < RETRY_LIMIT ) { + if ( rc != LIBUSB_ERROR_TIMEOUT ) + break; retry += 1; } return rc; @@ -400,108 +424,113 @@ static int ram_poke(void *context, uint32_t addr, bool external, const unsigned * Load a Cypress Image file into target RAM. * See http://www.cypress.com/?docID=41351 (AN76405 PDF) for more info. */ -static int fx3_load_ram(libusb_device_handle *device, const char *path) { +static int fx3_load_ram( libusb_device_handle *device, const char *path ) { uint32_t dCheckSum, dExpectedCheckSum, dAddress, i, dLen, dLength; uint32_t *dImageBuf; - unsigned char *bBuf, hBuf[4], blBuf[4], rBuf[4096]; + unsigned char *bBuf, hBuf[ 4 ], blBuf[ 4 ], rBuf[ 4096 ]; FILE *image; int ret = 0; - image = fopen(path, "rb"); - if (image == NULL) { - logerror("unable to open '%s' for input\n", path); + image = fopen( path, "rb" ); + if ( image == nullptr ) { + logerror( "unable to open '%s' for input\n", path ); return -2; - } else if (verbose) - logerror("open firmware image %s for RAM upload\n", path); + } else if ( verbose ) + logerror( "open firmware image %s for RAM upload\n", path ); // Read header - if (fread(hBuf, sizeof(char), sizeof(hBuf), image) != sizeof(hBuf)) { - logerror("could not read image header"); + if ( fread( hBuf, sizeof( char ), sizeof( hBuf ), image ) != sizeof( hBuf ) ) { + logerror( "could not read image header" ); ret = -3; goto exit; } // check "CY" signature byte and format - if ((hBuf[0] != 'C') || (hBuf[1] != 'Y')) { - logerror("image doesn't have a CYpress signature\n"); + if ( ( hBuf[ 0 ] != 'C' ) || ( hBuf[ 1 ] != 'Y' ) ) { + logerror( "image doesn't have a CYpress signature\n" ); ret = -3; goto exit; } // Check bImageType - switch (hBuf[3]) { + switch ( hBuf[ 3 ] ) { case 0xB0: - if (verbose) logerror("normal FW binary %s image with checksum\n", (hBuf[2] & 0x01) ? "data" : "executable"); + if ( verbose ) + logerror( "normal FW binary %s image with checksum\n", ( hBuf[ 2 ] & 0x01 ) ? "data" : "executable" ); break; case 0xB1: - logerror("security binary image is not currently supported\n"); + logerror( "security binary image is not currently supported\n" ); ret = -3; goto exit; case 0xB2: - logerror("VID:PID image is not currently supported\n"); + logerror( "VID:PID image is not currently supported\n" ); ret = -3; goto exit; default: - logerror("invalid image type 0x%02X\n", hBuf[3]); + logerror( "invalid image type 0x%02X\n", hBuf[ 3 ] ); ret = -3; goto exit; } // Read the bootloader version - if (verbose) { - if ((ezusb_read(device, "read bootloader version", RW_INTERNAL, 0xFFFF0020, blBuf, 4) < 0)) { - logerror("Could not read bootloader version\n"); + if ( verbose ) { + if ( ( ezusb_read( device, "read bootloader version", RW_INTERNAL, 0xFFFF0020, blBuf, 4 ) < 0 ) ) { + logerror( "Could not read bootloader version\n" ); ret = -8; goto exit; } - logerror("FX3 bootloader version: 0x%02X%02X%02X%02X\n", blBuf[3], blBuf[2], blBuf[1], blBuf[0]); + logerror( "FX3 bootloader version: 0x%02X%02X%02X%02X\n", blBuf[ 3 ], blBuf[ 2 ], blBuf[ 1 ], blBuf[ 0 ] ); } dCheckSum = 0; - if (verbose) logerror("writing image...\n"); - while (1) { - if ((fread(&dLength, sizeof(uint32_t), 1, image) != 1) || // read dLength - (fread(&dAddress, sizeof(uint32_t), 1, image) != 1)) { // read dAddress - logerror("could not read image"); + if ( verbose ) + logerror( "writing image...\n" ); + while ( 1 ) { + if ( ( fread( &dLength, sizeof( uint32_t ), 1, image ) != 1 ) || // read dLength + ( fread( &dAddress, sizeof( uint32_t ), 1, image ) != 1 ) ) { // read dAddress + logerror( "could not read image" ); ret = -3; goto exit; } - if (dLength == 0) break; // done + if ( dLength == 0 ) + break; // done // coverity[tainted_data] - dImageBuf = (uint32_t *)calloc(dLength, sizeof(uint32_t)); - if (dImageBuf == NULL) { - logerror("could not allocate buffer for image chunk\n"); + dImageBuf = static_cast< uint32_t * >( calloc( dLength, sizeof( uint32_t ) ) ); + if ( dImageBuf == nullptr ) { + logerror( "could not allocate buffer for image chunk\n" ); ret = -4; goto exit; } // read sections - if (fread(dImageBuf, sizeof(uint32_t), dLength, image) != dLength) { - logerror("could not read image"); - free(dImageBuf); + if ( fread( dImageBuf, sizeof( uint32_t ), dLength, image ) != dLength ) { + logerror( "could not read image" ); + free( dImageBuf ); ret = -3; goto exit; } - for (i = 0; i < dLength; i++) dCheckSum += dImageBuf[i]; + for ( i = 0; i < dLength; i++ ) + dCheckSum += dImageBuf[ i ]; dLength <<= 2; // convert to Byte length - bBuf = (unsigned char *)dImageBuf; + bBuf = reinterpret_cast< unsigned char * >( dImageBuf ); - while (dLength > 0) { + while ( dLength > 0 ) { dLen = 4096; // 4K max - if (dLen > dLength) dLen = dLength; - if ((ezusb_write(device, "write firmware", RW_INTERNAL, dAddress, bBuf, dLen) < 0) || - (ezusb_read(device, "read firmware", RW_INTERNAL, dAddress, rBuf, dLen) < 0)) { - logerror("R/W error\n"); - free(dImageBuf); + if ( dLen > dLength ) + dLen = dLength; + if ( ( ezusb_write( device, "write firmware", RW_INTERNAL, dAddress, bBuf, dLen ) < 0 ) || + ( ezusb_read( device, "read firmware", RW_INTERNAL, dAddress, rBuf, dLen ) < 0 ) ) { + logerror( "R/W error\n" ); + free( dImageBuf ); ret = -5; goto exit; } // Verify data: rBuf with bBuf - for (i = 0; i < dLen; i++) { - if (rBuf[i] != bBuf[i]) { - logerror("verify error"); - free(dImageBuf); + for ( i = 0; i < dLen; i++ ) { + if ( rBuf[ i ] != bBuf[ i ] ) { + logerror( "verify error" ); + free( dImageBuf ); ret = -6; goto exit; } @@ -511,21 +540,23 @@ static int fx3_load_ram(libusb_device_handle *device, const char *path) { bBuf += dLen; dAddress += dLen; } - free(dImageBuf); + free( dImageBuf ); } // read pre-computed checksum data - if ((fread(&dExpectedCheckSum, sizeof(uint32_t), 1, image) != 1) || (dCheckSum != dExpectedCheckSum)) { - logerror("checksum error\n"); + if ( ( fread( &dExpectedCheckSum, sizeof( uint32_t ), 1, image ) != 1 ) || ( dCheckSum != dExpectedCheckSum ) ) { + logerror( "checksum error\n" ); ret = -7; goto exit; } // transfer execution to Program Entry - if (!ezusb_fx3_jump(device, dAddress)) { ret = -6; } + if ( !ezusb_fx3_jump( device, dAddress ) ) { + ret = -6; + } exit: - fclose(image); + fclose( image ); return ret; } @@ -543,26 +574,27 @@ static int fx3_load_ram(libusb_device_handle *device, const char *path) { * memory is written, expecting a second stage loader to have already * been loaded. Then file is re-parsed and on-chip memory is written. */ -int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int stage) { +int ezusb_load_ram( libusb_device_handle *device, const char *path, int fx_type, int stage ) { FILE *image; uint32_t cpucs_addr; - bool (*is_external)(uint32_t off, size_t len); + bool ( *is_external )( uint32_t off, size_t len ); struct ram_poke_context ctx; int status; int ret = 0; - if (fx_type == FX_TYPE_FX3) return fx3_load_ram(device, path); + if ( fx_type == FX_TYPE_FX3 ) + return fx3_load_ram( device, path ); - image = fopen(path, "rb"); - if (image == NULL) { - logerror("%s: unable to open for input.\n", path); + image = fopen( path, "rb" ); + if ( image == nullptr ) { + logerror( "%s: unable to open for input.\n", path ); return -2; - } else if (verbose > 1) - logerror("open firmware image %s for RAM upload\n", path); + } else if ( verbose > 1 ) + logerror( "open firmware image %s for RAM upload\n", path ); /* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */ - switch (fx_type) { + switch ( fx_type ) { case FX_TYPE_FX2LP: cpucs_addr = 0xe600; is_external = fx2lp_is_external; @@ -578,11 +610,11 @@ int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, } /* use only first stage loader? */ - if (stage == 0) { + if ( stage == 0 ) { ctx.mode = internal_only; /* if required, halt the CPU while we overwrite its code/data */ - if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) { + if ( cpucs_addr && !ezusb_cpucs( device, cpucs_addr, false ) ) { ret = -1; goto exit; } @@ -592,49 +624,51 @@ int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, ctx.mode = skip_internal; /* let CPU run; overwrite the 2nd stage loader later */ - if (verbose) logerror("2nd stage: write external memory\n"); + if ( verbose ) + logerror( "2nd stage: write external memory\n" ); } /* scan the image, first (maybe only) time */ ctx.device = device; ctx.total = ctx.count = 0; - status = parse_ihex(image, &ctx, is_external, ram_poke); - if (status < 0) { - logerror("unable to upload %s\n", path); + status = parse_ihex( image, &ctx, is_external, ram_poke ); + if ( status < 0 ) { + logerror( "unable to upload %s\n", path ); ret = status; goto exit; } /* second part of 2nd stage: rescan */ - if (stage) { + if ( stage ) { ctx.mode = skip_external; /* if needed, halt the CPU while we overwrite the 1st stage loader */ - if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) { + if ( cpucs_addr && !ezusb_cpucs( device, cpucs_addr, false ) ) { ret = -1; goto exit; } /* at least write the interrupt vectors (at 0x0000) for reset! */ - rewind(image); - if (verbose) logerror("2nd stage: write on-chip memory\n"); - status = parse_ihex(image, &ctx, is_external, ram_poke); - if (status < 0) { - logerror("unable to completely upload %s\n", path); + rewind( image ); + if ( verbose ) + logerror( "2nd stage: write on-chip memory\n" ); + status = parse_ihex( image, &ctx, is_external, ram_poke ); + if ( status < 0 ) { + logerror( "unable to completely upload %s\n", path ); ret = status; goto exit; } } - if (verbose && (ctx.count != 0)) { - logerror("... WROTE: %d bytes, %d segments, avg %d\n", (int)ctx.total, (int)ctx.count, - (int)(ctx.total / ctx.count)); + if ( verbose && ( ctx.count != 0 ) ) { + logerror( "... WROTE: %d bytes, %d segments, avg %d\n", int( ctx.total ), int( ctx.count ), int( ctx.total / ctx.count ) ); } /* if required, reset the CPU so it runs what we just uploaded */ - if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, true)) ret = -1; + if ( cpucs_addr && !ezusb_cpucs( device, cpucs_addr, true ) ) + ret = -1; exit: - fclose(image); + fclose( image ); return ret; } diff --git a/openhantek/src/usb/ezusb.h b/openhantek/src/usb/ezusb.h index 63679f3c..5047bbf6 100644 --- a/openhantek/src/usb/ezusb.h +++ b/openhantek/src/usb/ezusb.h @@ -16,7 +16,7 @@ * 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 . + * along with this program. If not, see . */ #include @@ -34,7 +34,7 @@ struct libusb_device_handle; * * The target processor is reset at the end of this upload. */ -extern int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int stage); +extern int ezusb_load_ram( libusb_device_handle *device, const char *path, int fx_type, int stage ); /* Verbosity level (default 1). Can be increased or decreased with options v/q */ diff --git a/openhantek/src/usb/finddevices.cpp b/openhantek/src/usb/finddevices.cpp index 67b338ed..8175378e 100644 --- a/openhantek/src/usb/finddevices.cpp +++ b/openhantek/src/usb/finddevices.cpp @@ -7,74 +7,91 @@ #include #include -#include #include "ezusb.h" #include "utils/printutils.h" +#include +#ifdef Q_OS_FREEBSD +#include +#else #include +#endif #include "modelregistry.h" -FindDevices::FindDevices(libusb_context *context) : context(context) {} -// Iterate through all usb devices +FindDevices::FindDevices( libusb_context *context ) : context( context ) {} + + +// Iterate all devices on USB and keep track of all supported scopes int FindDevices::updateDeviceList() { libusb_device **deviceList; - ssize_t deviceCount = libusb_get_device_list(context, &deviceList); - if (deviceCount < 0) { - return (int) deviceCount; + ssize_t deviceCount = libusb_get_device_list( context, &deviceList ); + if ( deviceCount < 0 ) { + return int( deviceCount ); } ++findIteration; int changes = 0; - for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) { - libusb_device *device = deviceList[deviceIterator]; + for ( ssize_t deviceIndex = 0; deviceIndex < deviceCount; ++deviceIndex ) { + libusb_device *device = deviceList[ deviceIndex ]; // Get device descriptor struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(device, &descriptor); + libusb_get_device_descriptor( device, &descriptor ); + + if ( 0x1d6b == descriptor.idVendor ) // skip linux foundation devices, e.g. usb root hubs + continue; - DeviceList::const_iterator inList = devices.find(USBDevice::computeUSBdeviceID(device)); + const UniqueUSBid USBid = ScopeDevice::computeUSBdeviceID( device ); - if (inList != devices.end()) { - inList->second->setFindIteration(findIteration); + DeviceList::const_iterator inList = devices.find( USBid ); + if ( inList != devices.end() ) { // already in list, update heartbeat only + inList->second->setFindIteration( findIteration ); continue; } - - for (DSOModel* model : ModelRegistry::get()->models()) { + // else check against all supported models for match + for ( DSOModel *model : ModelRegistry::get()->models() ) { // Check VID and PID for firmware flashed devices bool supported = descriptor.idVendor == model->vendorID && descriptor.idProduct == model->productID; // Devices without firmware have different VID/PIDs supported |= descriptor.idVendor == model->vendorIDnoFirmware && descriptor.idProduct == model->productIDnoFirmware; - if (supported) { + if ( supported ) { // put matching device into list ++changes; - devices[USBDevice::computeUSBdeviceID(device)] = std::unique_ptr(new USBDevice(model, device, findIteration)); + // printf( "+ %016lX %s\n", USBid, model->name.c_str() ); + devices[ USBid ] = std::unique_ptr< ScopeDevice >( new ScopeDevice( model, device, findIteration ) ); + break; // stop after 1st supported model (there can be more models with identical VID/PID) } } } // Remove non existing devices - for (DeviceList::iterator it=devices.begin();it!=devices.end();) { - if (it->second->getFindIteration() != findIteration) { + for ( DeviceList::iterator it = devices.begin(); it != devices.end(); ) { + if ( it->second->getFindIteration() != findIteration ) { // heartbeat not up to date, no more on the bus ++changes; - it = devices.erase(it); + // printf( "- %016lX\n", it->first ); + it = devices.erase( it ); // it points to next entry } else { ++it; } } +#if defined Q_OS_FREEBSD + libusb_free_device_list( deviceList, false ); // free the list but don't unref the devices +#else + // TODO check if this crashes on MacOSX, Windows + // TODO check if change true -> false solves it + // move it up by appending " || defined __YOUR_OS__" to the line "#if defined ..." + libusb_free_device_list( deviceList, true ); // linux and some other systems unref also the USB devices +#endif + return changes; // report number of all detected bus changes (added + removed devices) +} - libusb_free_device_list(deviceList, true); - return changes; -} +const FindDevices::DeviceList *FindDevices::getDevices() { return &devices; } -const FindDevices::DeviceList* FindDevices::getDevices() -{ - return &devices; -} -std::unique_ptr FindDevices::takeDevice(UniqueUSBid id) -{ - DeviceList::iterator i = devices.find(id); - if (i==devices.end()) return nullptr; - return std::move(i->second); +std::unique_ptr< ScopeDevice > FindDevices::takeDevice( UniqueUSBid id ) { + DeviceList::iterator it = devices.find( id ); + if ( it == devices.end() ) + return nullptr; + return std::move( it->second ); } diff --git a/openhantek/src/usb/finddevices.h b/openhantek/src/usb/finddevices.h index e0799f3d..b3f916f8 100644 --- a/openhantek/src/usb/finddevices.h +++ b/openhantek/src/usb/finddevices.h @@ -3,11 +3,11 @@ #pragma once #include -#include -#include #include +#include +#include -#include "usbdevice.h" +#include "scopedevice.h" struct libusb_context; @@ -20,10 +20,10 @@ struct libusb_context; * * Do not close the given usb context before this class object is destroyed. */ -class FindDevices{ +class FindDevices { public: - typedef std::map> DeviceList; - FindDevices(libusb_context *context); + typedef std::map< UniqueUSBid, std::unique_ptr< ScopeDevice > > DeviceList; + explicit FindDevices( libusb_context *context ); /// Updates the device list. To clear the list, just dispose this object /// \return If negative it represents a libusb error code otherwise the amount of updates int updateDeviceList(); @@ -33,7 +33,8 @@ class FindDevices{ * @param id The unique usb id for the current bus layout * @return A shared reference to the */ - std::unique_ptr takeDevice(UniqueUSBid id); + std::unique_ptr< ScopeDevice > takeDevice( UniqueUSBid id ); + private: libusb_context *context; ///< The usb context used for this device DeviceList devices; diff --git a/openhantek/src/usb/scopedevice.cpp b/openhantek/src/usb/scopedevice.cpp new file mode 100644 index 00000000..094379c0 --- /dev/null +++ b/openhantek/src/usb/scopedevice.cpp @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include + +#include "scopedevice.h" + +#include "hantekdso/dsomodel.h" +// #include "hantekprotocol/bulkStructs.h" +#include "hantekprotocol/controlStructs.h" + +#include + +// Returns a constant QString with a short description of the given error code, +// this description is intended for displaying to the end user and will be +// in the language set by libusb_setlocale(). +// Supported languages: +// libusb-1.0.21 (Win): "en", "nl", "fr", "ru" +// libusb-1.0.22 (Linux): "en", "nl", "fr", "ru" +// libusb-1.0.23 (MacOSX): "en", "nl", "fr", "ru", "de", "hu" +const QString libUsbErrorString( int error ) { return QString( libusb_strerror( libusb_error( error ) ) ); } + + +UniqueUSBid ScopeDevice::computeUSBdeviceID( libusb_device *device ) { + // Returns a 64-bit value that uniquely identifies a device on the bus + // bus/ports define a constant plug position + // VID/FW changes with FW upload + // bpppppppVVVVFFFF + // ^^^^-- Firmware version (16bit)(can change after upload of new FW over old FW) + // ^^^^------ Vendor ID (16 bit)(changes with FW upload to device w/o FW) + // ^^^^^^^---------- USB ports tree (as shown by "lsusb -t"), max 7 ports, port = 1..15, 0 = none + // ^----------------- USB bus, bus = 1..15 + + // Get device descriptor + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor( device, &descriptor ); + // collect values and arrange them + UniqueUSBid uid = libusb_get_bus_number( device ) & 0x0F; // typically no more than 15 busses + const int treeSize = 7; // port tree max size is 7 + uint8_t ports[ treeSize ]; + int nPorts = libusb_get_port_numbers( device, ports, sizeof( ports ) ); + for ( int iii = 0; iii < treeSize; ++iii ) { + uid <<= 4; + if ( iii < nPorts ) + uid |= ports[ iii ] & 0x0F; + } + uid <<= 16; + uid |= descriptor.idVendor; + uid <<= 16; + uid |= descriptor.bcdDevice; + return uid; +} + + +ScopeDevice::ScopeDevice( DSOModel *model, libusb_device *device, unsigned findIteration ) + : model( model ), device( device ), findIteration( findIteration ), uniqueUSBdeviceID( computeUSBdeviceID( device ) ) { + libusb_ref_device( device ); + libusb_get_device_descriptor( device, &descriptor ); +} + + +ScopeDevice::ScopeDevice() : model( new ModelDEMO ), device( nullptr ), uniqueUSBdeviceID( 0 ), realHW( false ) {} + + +bool ScopeDevice::connectDevice( QString &errorMessage ) { + if ( needsFirmware() ) + return false; + if ( isConnected() ) + return true; + + // Open device + int errorCode = libusb_open( device, &( handle ) ); + if ( errorCode != LIBUSB_SUCCESS ) { + handle = nullptr; + errorMessage = QCoreApplication::translate( "", "Couldn't open device: %1" ).arg( libUsbErrorString( errorCode ) ); + return false; + } + + // Find and claim interface + errorCode = LIBUSB_ERROR_NOT_FOUND; + libusb_config_descriptor *configDescriptor; + libusb_get_config_descriptor( device, 0, &configDescriptor ); + for ( int interfaceIndex = 0; interfaceIndex < int( configDescriptor->bNumInterfaces ); ++interfaceIndex ) { + const libusb_interface *pInterface = &configDescriptor->interface[ interfaceIndex ]; + if ( pInterface->num_altsetting < 1 ) + continue; + + const libusb_interface_descriptor *interfaceDescriptor = &pInterface->altsetting[ 0 ]; + if ( interfaceDescriptor->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && interfaceDescriptor->bInterfaceSubClass == 0 && + interfaceDescriptor->bInterfaceProtocol == 0 ) { + errorCode = claimInterface( interfaceDescriptor ); + break; + } + } + + libusb_free_config_descriptor( configDescriptor ); + + if ( errorCode != LIBUSB_SUCCESS ) { + errorMessage = QString( "%1 (%2:%3)" ) + .arg( libUsbErrorString( errorCode ) ) + .arg( libusb_get_bus_number( device ), 3, 10, QLatin1Char( '0' ) ) + .arg( libusb_get_device_address( device ), 3, 10, QLatin1Char( '0' ) ); + return false; + } + disconnected = false; + return true; +} + + +ScopeDevice::~ScopeDevice() { + disconnectFromDevice(); +#if defined Q_OS_WIN + if ( device != nullptr ) + libusb_unref_device( device ); + device = nullptr; +#endif +} + + +int ScopeDevice::claimInterface( const libusb_interface_descriptor *interfaceDescriptor ) { + int errorCode = libusb_claim_interface( this->handle, interfaceDescriptor->bInterfaceNumber ); + if ( errorCode < 0 ) + return errorCode; + + nInterface = interfaceDescriptor->bInterfaceNumber; + + // Check the maximum endpoint packet size + const libusb_endpoint_descriptor *endpointDescriptor; + this->outPacketLength = 0; + this->inPacketLength = 0; + for ( int endpoint = 0; endpoint < interfaceDescriptor->bNumEndpoints; ++endpoint ) { + endpointDescriptor = &( interfaceDescriptor->endpoint[ endpoint ] ); + if ( endpointDescriptor->bEndpointAddress == HANTEK_EP_OUT ) { + this->outPacketLength = endpointDescriptor->wMaxPacketSize; + } else if ( endpointDescriptor->bEndpointAddress == HANTEK_EP_IN ) { + this->inPacketLength = endpointDescriptor->wMaxPacketSize; + } + } + return LIBUSB_SUCCESS; +} + + +void ScopeDevice::disconnectFromDevice() { + disconnected = true; + if ( !device ) + return; + + if ( this->handle ) { + // Release claimed interface + if ( nInterface != -1 ) + libusb_release_interface( this->handle, nInterface ); + nInterface = -1; + + // Close device handle + libusb_close( this->handle ); + } + this->handle = nullptr; + +#if !defined Q_OS_WIN + libusb_unref_device( device ); +#endif + emit deviceDisconnected(); +} + + +bool ScopeDevice::isConnected() { return isDemoDevice() || ( !disconnected && this->handle != nullptr ); } + + +bool ScopeDevice::needsFirmware() { + return this->descriptor.idProduct != model->productID || this->descriptor.idVendor != model->vendorID || + this->descriptor.bcdDevice < model->firmwareVersion; +} + + +int ScopeDevice::bulkTransfer( unsigned char endpoint, const unsigned char *data, unsigned int length, int attempts, + unsigned int timeout ) { + if ( !this->handle ) + return LIBUSB_ERROR_NO_DEVICE; + + int errorCode = LIBUSB_ERROR_TIMEOUT; + int transferred = 0; + for ( int attempt = 0; ( attempt < attempts || attempts == -1 ) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt ) + errorCode = libusb_bulk_transfer( this->handle, endpoint, const_cast< unsigned char * >( data ), int( length ), + &transferred, timeout ); + + if ( errorCode == LIBUSB_ERROR_NO_DEVICE ) + disconnectFromDevice(); + if ( errorCode < 0 ) + return errorCode; + else + return transferred; +} + + +int ScopeDevice::bulkReadMulti( unsigned char *data, unsigned length, bool captureSmallBlocks, unsigned &received, int attempts ) { + if ( !handle || disconnected ) + return LIBUSB_ERROR_NO_DEVICE; + int retCode = 0; + // printf("USBDevice::bulkReadMulti( %d )\n", length ); + if ( captureSmallBlocks ) { + // slow data is read in smaller chunks to enable quick screen update + const unsigned packetLength = 512 * 78; // 100 blocks for one screen width of 40000 + retCode = int( packetLength ); + unsigned int packet; + received = 0; + for ( packet = 0; received < length && retCode == int( packetLength ); ++packet ) { + if ( hasStopped() ) + break; + retCode = bulkTransfer( HANTEK_EP_IN, data + packet * packetLength, qMin( length - unsigned( received ), packetLength ), + attempts, HANTEK_TIMEOUT_MULTI * 10 ); + if ( retCode > 0 ) + received += unsigned( retCode ); + } + // printf( "total packets: %d, received: %d\n", packet, received ); + if ( received > 0 ) + retCode = int( received ); + return retCode; + } else { + // more stable if fast data is read as one big block (up to 4 MB) + if ( hasStopped() ) + return 0; + retCode = bulkTransfer( HANTEK_EP_IN, data, length, attempts, HANTEK_TIMEOUT_MULTI * length / inPacketLength ); + if ( retCode < 0 ) + received = 0; + else + received = unsigned( retCode ); + stopTransfer = false; + return retCode; + } +} + + +int ScopeDevice::controlTransfer( unsigned char type, unsigned char request, unsigned char *data, unsigned int length, int value, + int index, int attempts ) { + if ( !handle || disconnected ) + return LIBUSB_ERROR_NO_DEVICE; + + int errorCode = LIBUSB_ERROR_TIMEOUT; + // printf( "controlTransfer type %x request %x data[0] %d length %d value %d index %d attempts %d\n", + // type, request, data[0], length, value, index, attempts ); + + for ( int attempt = 0; ( attempt < attempts || attempts == -1 ) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt ) + errorCode = libusb_control_transfer( handle, type, request, uint16_t( value ), uint16_t( index ), data, uint16_t( length ), + HANTEK_TIMEOUT ); + + if ( errorCode == LIBUSB_ERROR_NO_DEVICE ) + disconnectFromDevice(); + return errorCode; +} diff --git a/openhantek/src/usb/scopedevice.h b/openhantek/src/usb/scopedevice.h new file mode 100644 index 00000000..7ee098f6 --- /dev/null +++ b/openhantek/src/usb/scopedevice.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#pragma once + +#include +#include +#include +#include + +#ifdef Q_OS_FREEBSD +#include +#else +#include +#endif +#include + +#include "models/modelDEMO.h" +#include "usbdevicedefinitions.h" + +class DSOModel; + +typedef uint64_t UniqueUSBid; + + +/// \brief Returns string representation for libusb errors. +/// \param error The error code. +/// \return String explaining the error. +const QString libUsbErrorString( int error ); + + +/// \brief This class handles the USB communication with an usb device that has +/// one in and one out endpoint. +class ScopeDevice : public QObject { + Q_OBJECT + + public: + explicit ScopeDevice( DSOModel *model, libusb_device *device, unsigned findIteration = 0 ); + explicit ScopeDevice(); + ScopeDevice( const ScopeDevice & ) = delete; + ~ScopeDevice(); + bool connectDevice( QString &errorMessage ); + void disconnectFromDevice(); + + /// \brief Check if the oscilloscope is connected. + /// \return true, if a connection is up. + bool isConnected(); + + /// \brief Distinguish between real hw or demo device + bool isRealHW() const { return realHW; } + bool isDemoDevice() const { return !realHW; } + + /// \brief Stop a long running (interruptable) bulk transfer + void stopSampling() { stopTransfer = true; } + + bool hasStopped() { + bool stopped = stopTransfer; + stopTransfer = false; + return stopped; + } + + /** + * @return Return true if this device needs a firmware first + */ + bool needsFirmware(); + + /** + * @return Return device version as unsigned int + */ + inline unsigned int getFwVersion() const { return this->descriptor.bcdDevice; } + + /** + * Keep track of the find iteration on which this device was found + * @param iteration The new iteration value + */ + inline void setFindIteration( unsigned iteration ) { findIteration = iteration; } + inline unsigned getFindIteration() const { return findIteration; } + + /// \brief Bulk transfer to/from the oscilloscope. + /// \param endpoint Endpoint number, also sets the direction of the transfer. + /// \param data Buffer for the sent/received data. + /// \param length The length of the packet. + /// \param attempts The number of attempts, that are done on timeouts. + /// \param timeout The timeout in ms. + /// \return Number of transferred bytes on success, libusb error code on error. + int bulkTransfer( unsigned char endpoint, const unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS, + unsigned int timeout = HANTEK_TIMEOUT ); + + /// \brief Bulk write to the oscilloscope. + /// \param data Buffer for the sent/received data. + /// \param length The length of the packet. + /// \param attempts The number of attempts, that are done on timeouts. + /// \return Number of sent bytes on success, libusb error code on error. + inline int bulkWrite( const unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS ) { + return bulkTransfer( HANTEK_EP_OUT, data, length, attempts ); + } + + /// \brief Bulk read from the oscilloscope. + /// \param data Buffer for the sent/received data. + /// \param length The length of the packet. + /// \param attempts The number of attempts, that are done on timeouts. + /// \return Number of received bytes on success, libusb error code on error. + template < class T > inline int bulkRead( const T *command, int attempts = HANTEK_ATTEMPTS ) { + return bulkTransfer( HANTEK_EP_IN, command->data(), command->size(), attempts ); + } + + /// \brief Multi packet bulk read from the oscilloscope. + /// \param data Buffer for the sent/received data. + /// \param length The length of data contained in the packets. + /// \param captureSmallBlocks Capture many small blocks instread of one big block (faster gui update) + /// \param received The amount of already captured samples + /// \param attempts The number of attempts, that are done on timeouts. + /// \return Number of received bytes on success, libusb error code on error. + int bulkReadMulti( unsigned char *data, unsigned length, bool captureSmallBlocks, unsigned &received, + int attempts = HANTEK_ATTEMPTS_MULTI ); + + /// \brief Control transfer to the oscilloscope. + /// \param type The request type, also sets the direction of the transfer. + /// \param request The request field of the packet. + /// \param data Buffer for the sent/received data. + /// \param length The length field of the packet. + /// \param value The value field of the packet. + /// \param index The index field of the packet. + /// \param attempts The number of attempts, that are done on timeouts. + /// \return Number of transferred bytes on success, libusb error code on error. + int controlTransfer( unsigned char type, unsigned char request, unsigned char *data, unsigned int length, int value, int index, + int attempts = HANTEK_ATTEMPTS ); + + /// \brief Control write to the oscilloscope. + /// \param command Buffer for the sent/received data. + /// \return Number of sent bytes on success, libusb error code on error. + template < class T > inline int controlWrite( const T *command ) { + return controlTransfer( LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, uint8_t( command->code ), + const_cast< unsigned char * >( command->data() ), unsigned( command->size() ), command->value, 0, + HANTEK_ATTEMPTS ); + } + + /// \brief Control read to the oscilloscope. + /// \param command Buffer for the sent/received data. + /// \return Number of received bytes on success, libusb error code on error. + template < class T > inline int controlRead( const T *command ) { + return controlTransfer( LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, uint8_t( command->code ), + const_cast< unsigned char * >( command->data() ), unsigned( command->size() ), command->value, 0, + HANTEK_ATTEMPTS ); + } + + /** + * @return Returns the raw libusb device + */ + inline libusb_device *getUSBDevice() const { return device; } + + /** + * @return Return the unique usb device id {@link USBDevice::computeUSBdeviceID()}. + */ + inline UniqueUSBid getUniqueUSBDeviceID() const { return uniqueUSBdeviceID; } + /** + * ID built from bus, port, VID, PID and FW version + */ + static UniqueUSBid computeUSBdeviceID( libusb_device *device ); + + /// \brief Get the oscilloscope model. + /// \return The ::Model of the connected Hantek DSO. + inline const DSOModel *getModel() const { return model; } + /** + * Usually a maximum packet length for in and outgoing packets is determined + * by the underlying implementation and usb specification. E.g. the roll buffer + * mode uses the maximum in length for transfer. Some devices do not support + * that much data though and need an artification restriction. + */ + inline void overwriteInPacketLength( unsigned len ) { inPacketLength = len; } + + protected: + int claimInterface( const libusb_interface_descriptor *interfaceDescriptor ); + + // Device model data + DSOModel *model; + + // Libusb specific variables + struct libusb_device_descriptor descriptor; + libusb_device *device; ///< The USB handle for the oscilloscope + libusb_device_handle *handle = nullptr; + unsigned findIteration; + const UniqueUSBid uniqueUSBdeviceID; + int nInterface; + unsigned outPacketLength; ///< Packet length for the OUT endpoint + unsigned inPacketLength; ///< Packet length for the IN endpoint + + private: + bool realHW = true; + bool stopTransfer = false; + bool disconnected = true; + + signals: + void deviceDisconnected(); ///< The device has been disconnected +}; diff --git a/openhantek/src/usb/uploadFirmware.cpp b/openhantek/src/usb/uploadFirmware.cpp index 8ac9db0e..5c07c0e4 100644 --- a/openhantek/src/usb/uploadFirmware.cpp +++ b/openhantek/src/usb/uploadFirmware.cpp @@ -4,73 +4,73 @@ #include #include #include +#ifdef Q_OS_FREEBSD +#include +#else #include +#endif #include #include "ezusb.h" +#include "scopedevice.h" #include "uploadFirmware.h" -#include "usbdevice.h" #include "dsomodel.h" -#define TR(str) QCoreApplication::translate("UploadFirmware", str) +#define TR( str ) ( QString( "UploadFirmware: " ) + QCoreApplication::translate( "UploadFirmware", str ) ) -bool UploadFirmware::startUpload(USBDevice *device) { - if (device->isConnected() || !device->needsFirmware()) return false; +bool UploadFirmware::startUpload( ScopeDevice *scopeDevice ) { + if ( scopeDevice->isConnected() || !scopeDevice->needsFirmware() ) + return false; // Open device libusb_device_handle *handle; - int errorCode = libusb_open(device->getRawDevice(), &handle); - if (errorCode != LIBUSB_SUCCESS) { + int status = libusb_open( scopeDevice->getUSBDevice(), &handle ); + if ( status != LIBUSB_SUCCESS ) { handle = nullptr; - errorMessage = TR("Couldn't open device: %1").arg(libUsbErrorString(errorCode)); + errorMessage = TR( "Couldn't open device: %1" ).arg( libUsbErrorString( status ) ); return false; } // Write firmware from resources to temp files QFile firmwareRes( - QString(":/firmware/%1-firmware.hex").arg(QString::fromStdString(device->getModel()->firmwareToken))); - auto temp_firmware_path = std::unique_ptr(QTemporaryFile::createNativeFile(firmwareRes)); - if (!temp_firmware_path) return false; + QString( ":/firmware/%1-firmware.hex" ).arg( QString::fromStdString( scopeDevice->getModel()->firmwareToken ) ) ); + auto temp_firmware_path = std::unique_ptr< QTemporaryFile >( QTemporaryFile::createNativeFile( firmwareRes ) ); + if ( !temp_firmware_path ) + return false; temp_firmware_path->open(); - QFile loaderRes(QString(":/firmware/%1-loader.hex").arg(QString::fromStdString(device->getModel()->firmwareToken))); - auto temp_loader_path = std::unique_ptr(QTemporaryFile::createNativeFile(loaderRes)); - if (!temp_loader_path) return false; - temp_loader_path->open(); - /* We need to claim the first interface */ - libusb_set_auto_detach_kernel_driver(handle, 1); - int status = libusb_claim_interface(handle, 0); - if (status != LIBUSB_SUCCESS) { - errorMessage = TR("libusb_claim_interface() failed: %1").arg(libusb_error_name(status)); - libusb_close(handle); +#ifdef Q_OS_LINUX + // Detach kernel driver, reported to lead to an error on FreeBSD, MacOSX and Windows + status = libusb_set_auto_detach_kernel_driver( handle, 1 ); + if ( status != LIBUSB_SUCCESS && status != LIBUSB_ERROR_NOT_SUPPORTED ) { + errorMessage = TR( "libusb_set_auto_detach_kernel_driver() failed: %1" ).arg( libusb_error_name( status ) ); + libusb_close( handle ); return false; } +#endif - // Write loader - status = ezusb_load_ram(handle, temp_loader_path->fileName().toUtf8().constData(), FX_TYPE_FX2, 0); - - if (status != LIBUSB_SUCCESS) { - errorMessage = TR("Writing the loader firmware failed: %1").arg(libusb_error_name(status)); - libusb_release_interface(handle, 0); - libusb_close(handle); + // We need to claim the first interface (num=0) + status = libusb_claim_interface( handle, 0 ); + if ( status != LIBUSB_SUCCESS ) { + errorMessage = TR( "libusb_claim_interface() failed: %1" ).arg( libusb_error_name( status ) ); + libusb_close( handle ); return false; } - // Write firmware - status = ezusb_load_ram(handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2, 1); - - if (status != LIBUSB_SUCCESS) { - errorMessage = TR("Writing the main firmware failed: %1").arg(libusb_error_name(status)); - libusb_release_interface(handle, 0); - libusb_close(handle); + // Write firmware into internal RAM using first stage loader built into EZ-USB hardware + status = ezusb_load_ram( handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2LP, 0 ); + if ( status != LIBUSB_SUCCESS ) { + errorMessage = TR( "Writing the main firmware failed: %1" ).arg( libusb_error_name( status ) ); + libusb_release_interface( handle, 0 ); + libusb_close( handle ); return false; } - libusb_release_interface(handle, 0); - libusb_close(handle); + + status = libusb_release_interface( handle, 0 ); + libusb_close( handle ); return status == LIBUSB_SUCCESS; } const QString &UploadFirmware::getErrorMessage() const { return errorMessage; } - diff --git a/openhantek/src/usb/uploadFirmware.h b/openhantek/src/usb/uploadFirmware.h index 1986f85e..afa66f9f 100644 --- a/openhantek/src/usb/uploadFirmware.h +++ b/openhantek/src/usb/uploadFirmware.h @@ -4,7 +4,7 @@ #include -class USBDevice; +class ScopeDevice; /** * Extracts the firmware from the applications resources, and uploads the @@ -12,8 +12,9 @@ class USBDevice; */ class UploadFirmware { public: - bool startUpload(USBDevice *device); + bool startUpload( ScopeDevice *scopeDevice ); const QString &getErrorMessage() const; + private: QString errorMessage; }; diff --git a/openhantek/src/usb/usbdevice.cpp b/openhantek/src/usb/usbdevice.cpp deleted file mode 100644 index bdda8d44..00000000 --- a/openhantek/src/usb/usbdevice.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include -#include -#include - -#include "usbdevice.h" - -#include "hantekdso/dsomodel.h" -#include "hantekprotocol/bulkStructs.h" -#include "hantekprotocol/controlStructs.h" - -#include - -QString libUsbErrorString(int error) { - switch (error) { - case LIBUSB_SUCCESS: - return QCoreApplication::tr("Success (no error)"); - case LIBUSB_ERROR_IO: - return QCoreApplication::tr("Input/output error"); - case LIBUSB_ERROR_INVALID_PARAM: - return QCoreApplication::tr("Invalid parameter"); - case LIBUSB_ERROR_ACCESS: - return QCoreApplication::tr("Access denied (insufficient permissions)"); - case LIBUSB_ERROR_NO_DEVICE: - return QCoreApplication::tr("No such device (it may have been disconnected)"); - case LIBUSB_ERROR_NOT_FOUND: - return QCoreApplication::tr("Entity not found"); - case LIBUSB_ERROR_BUSY: - return QCoreApplication::tr("Resource busy"); - case LIBUSB_ERROR_TIMEOUT: - return QCoreApplication::tr("Operation timed out"); - case LIBUSB_ERROR_OVERFLOW: - return QCoreApplication::tr("Overflow"); - case LIBUSB_ERROR_PIPE: - return QCoreApplication::tr("Pipe error"); - case LIBUSB_ERROR_INTERRUPTED: - return QCoreApplication::tr("System call interrupted (perhaps due to signal)"); - case LIBUSB_ERROR_NO_MEM: - return QCoreApplication::tr("Insufficient memory"); - case LIBUSB_ERROR_NOT_SUPPORTED: - return QCoreApplication::tr("Operation not supported or unimplemented on this platform"); - default: - return QCoreApplication::tr("Other error"); - } -} - -UniqueUSBid USBDevice::computeUSBdeviceID(libusb_device *device) { - UniqueUSBid v = 0; - libusb_get_port_numbers(device, (uint8_t *)&v, sizeof(v)); - return v; -} - -USBDevice::USBDevice(DSOModel *model, libusb_device *device, unsigned findIteration) - : model(model), device(device), findIteration(findIteration), uniqueUSBdeviceID(computeUSBdeviceID(device)) { - libusb_ref_device(device); - libusb_get_device_descriptor(device, &descriptor); -} - -bool USBDevice::connectDevice(QString &errorMessage) { - if (needsFirmware()) return false; - if (isConnected()) return true; - - // Open device - int errorCode = libusb_open(device, &(handle)); - if (errorCode != LIBUSB_SUCCESS) { - handle = nullptr; - errorMessage = QCoreApplication::translate("", "Couldn't open device: %1").arg(libUsbErrorString(errorCode)); - return false; - } - - // Find and claim interface - errorCode = LIBUSB_ERROR_NOT_FOUND; - libusb_config_descriptor *configDescriptor; - libusb_get_config_descriptor(device, 0, &configDescriptor); - for (int interfaceIndex = 0; interfaceIndex < (int)configDescriptor->bNumInterfaces; ++interfaceIndex) { - const libusb_interface *interface = &configDescriptor->interface[interfaceIndex]; - if (interface->num_altsetting < 1) continue; - - const libusb_interface_descriptor *interfaceDescriptor = &interface->altsetting[0]; - if (interfaceDescriptor->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && - interfaceDescriptor->bInterfaceSubClass == 0 && interfaceDescriptor->bInterfaceProtocol == 0 && - interfaceDescriptor->bNumEndpoints == 2) { - errorCode = claimInterface(interfaceDescriptor, HANTEK_EP_OUT, HANTEK_EP_IN); - break; - } - } - - libusb_free_config_descriptor(configDescriptor); - - if (errorCode != LIBUSB_SUCCESS) { - errorMessage = QString("%1 (%2:%3)") - .arg(libUsbErrorString(errorCode)) - .arg(libusb_get_bus_number(device), 3, 10, QLatin1Char('0')) - .arg(libusb_get_device_address(device), 3, 10, QLatin1Char('0')); - return false; - } - - return true; -} - -USBDevice::~USBDevice() { - disconnectFromDevice(); -#if defined(_WIN32) || defined(_WIN64) - if (device != nullptr) libusb_unref_device(device); - device = nullptr; -#endif -} - -int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn) { - int errorCode = libusb_claim_interface(this->handle, interfaceDescriptor->bInterfaceNumber); - if (errorCode < 0) { return errorCode; } - - interface = interfaceDescriptor->bInterfaceNumber; - - // Check the maximum endpoint packet size - const libusb_endpoint_descriptor *endpointDescriptor; - this->outPacketLength = 0; - this->inPacketLength = 0; - for (int endpoint = 0; endpoint < interfaceDescriptor->bNumEndpoints; ++endpoint) { - endpointDescriptor = &(interfaceDescriptor->endpoint[endpoint]); - if (endpointDescriptor->bEndpointAddress == endpointOut) { - this->outPacketLength = endpointDescriptor->wMaxPacketSize; - } else if (endpointDescriptor->bEndpointAddress == endPointIn) { - this->inPacketLength = endpointDescriptor->wMaxPacketSize; - } - } - return LIBUSB_SUCCESS; -} - -void USBDevice::disconnectFromDevice() { - if (!device) return; - - if (this->handle) { - // Release claimed interface - if (this->interface != -1) libusb_release_interface(this->handle, this->interface); - this->interface = -1; - - // Close device handle - libusb_close(this->handle); - } - this->handle = nullptr; - -#if !defined(_WIN32) || !defined(_WIN64) - libusb_unref_device(device); -#endif - - emit deviceDisconnected(); -} - -bool USBDevice::isConnected() { return this->handle != 0; } - -bool USBDevice::needsFirmware() { - return this->descriptor.idProduct != model->productID || this->descriptor.idVendor != model->vendorID; -} - -int USBDevice::bulkTransfer(unsigned char endpoint, const unsigned char *data, unsigned int length, int attempts, - unsigned int timeout) { - if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; - - int errorCode = LIBUSB_ERROR_TIMEOUT; - int transferred = 0; - for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) - errorCode = - libusb_bulk_transfer(this->handle, endpoint, (unsigned char *)data, (int)length, &transferred, timeout); - - if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice(); - if (errorCode < 0) - return errorCode; - else - return transferred; -} - -int USBDevice::bulkReadMulti(unsigned char *data, unsigned length, int attempts) { - if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; - - int errorCode = this->inPacketLength; - unsigned int packet, received = 0; - for (packet = 0; received < length && errorCode == this->inPacketLength; ++packet) { - errorCode = this->bulkTransfer(HANTEK_EP_IN, data + packet * this->inPacketLength, - qMin(length - received, (unsigned int)this->inPacketLength), attempts, - HANTEK_TIMEOUT_MULTI); - if (errorCode > 0) received += (unsigned)errorCode; - } - - if (received > 0) - return (int)received; - else - return errorCode; -} - -int USBDevice::controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned int length, - int value, int index, int attempts) { - if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; - - int errorCode = LIBUSB_ERROR_TIMEOUT; - for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) - errorCode = libusb_control_transfer(this->handle, type, request, value, index, data, length, HANTEK_TIMEOUT); - - if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice(); - return errorCode; -} - - - - diff --git a/openhantek/src/usb/usbdevice.h b/openhantek/src/usb/usbdevice.h deleted file mode 100644 index 23126a1f..00000000 --- a/openhantek/src/usb/usbdevice.h +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#pragma once - -#include -#include -#include -#include - -#include "usbdevicedefinitions.h" - -class DSOModel; - -typedef unsigned long UniqueUSBid; - - -/// \brief Returns string representation for libusb errors. -/// \param error The error code. -/// \return String explaining the error. -QString libUsbErrorString(int error); - -/// \brief This class handles the USB communication with an usb device that has -/// one in and one out endpoint. -class USBDevice : public QObject { - Q_OBJECT - - public: - explicit USBDevice(DSOModel* model, libusb_device *device, unsigned findIteration = 0); - USBDevice(const USBDevice&) = delete; - ~USBDevice(); - bool connectDevice(QString &errorMessage); - void disconnectFromDevice(); - - /// \brief Check if the oscilloscope is connected. - /// \return true, if a connection is up. - bool isConnected(); - - /** - * @return Return true if this device needs a firmware first - */ - bool needsFirmware(); - - /** - * Keep track of the find iteration on which this device was found - * @param iteration The new iteration value - */ - inline void setFindIteration(unsigned iteration) { findIteration = iteration; } - inline unsigned getFindIteration() const { return findIteration; } - - /// \brief Bulk transfer to/from the oscilloscope. - /// \param endpoint Endpoint number, also sets the direction of the transfer. - /// \param data Buffer for the sent/recieved data. - /// \param length The length of the packet. - /// \param attempts The number of attempts, that are done on timeouts. - /// \param timeout The timeout in ms. - /// \return Number of transferred bytes on success, libusb error code on - /// error. - int bulkTransfer(unsigned char endpoint, const unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS, - unsigned int timeout = HANTEK_TIMEOUT); - - /// \brief Bulk write to the oscilloscope. - /// \param data Buffer for the sent/recieved data. - /// \param length The length of the packet. - /// \param attempts The number of attempts, that are done on timeouts. - /// \return Number of sent bytes on success, libusb error code on error. - inline int bulkWrite(const unsigned char *data, unsigned int length, int attempts = HANTEK_ATTEMPTS) { - return bulkTransfer(HANTEK_EP_OUT, data, length, attempts); - } - - /// \brief Bulk read from the oscilloscope. - /// \param data Buffer for the sent/recieved data. - /// \param length The length of the packet. - /// \param attempts The number of attempts, that are done on timeouts. - /// \return Number of received bytes on success, libusb error code on error. - template - inline int bulkRead(const T *command, int attempts = HANTEK_ATTEMPTS) { - return bulkTransfer(HANTEK_EP_IN, command->data(), command->size(), attempts); - } - - /// \brief Multi packet bulk read from the oscilloscope. - /// \param data Buffer for the sent/recieved data. - /// \param length The length of data contained in the packets. - /// \param attempts The number of attempts, that are done on timeouts. - /// \return Number of received bytes on success, libusb error code on error. - int bulkReadMulti(unsigned char *data, unsigned length, int attempts = HANTEK_ATTEMPTS_MULTI); - - /// \brief Control transfer to the oscilloscope. - /// \param type The request type, also sets the direction of the transfer. - /// \param request The request field of the packet. - /// \param data Buffer for the sent/recieved data. - /// \param length The length field of the packet. - /// \param value The value field of the packet. - /// \param index The index field of the packet. - /// \param attempts The number of attempts, that are done on timeouts. - /// \return Number of transferred bytes on success, libusb error code on error. - int controlTransfer(unsigned char type, unsigned char request, unsigned char *data, unsigned int length, int value, - int index, int attempts = HANTEK_ATTEMPTS); - - /// \brief Control write to the oscilloscope. - /// \param command Buffer for the sent/recieved data. - /// \return Number of sent bytes on success, libusb error code on error. - template - inline int controlWrite(const T *command) { - return controlTransfer(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, (uint8_t)command->code, - (unsigned char *)command->data(), (unsigned)command->size(), command->value, 0, - HANTEK_ATTEMPTS); - } - - /// \brief Control read to the oscilloscope. - /// \param command Buffer for the sent/recieved data. - /// \return Number of received bytes on success, libusb error code on error. - template - inline int controlRead(const T *command) { - return controlTransfer(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, (uint8_t)command->code, - (unsigned char *)command->data(), (unsigned)command->size(), command->value, 0, - HANTEK_ATTEMPTS); - } - - /** - * @return Returns the raw libusb device - */ - inline libusb_device *getRawDevice() const { return device; } - - /** - * @return Return the unique usb device id {@link USBDevice::computeUSBdeviceID()}. - */ - inline unsigned long getUniqueUSBDeviceID() const { return uniqueUSBdeviceID; } - /** - * The USB bus is organised in a tree hierarchy. A device is connected to a port on a bus device, - * which is connected to a port on another bus device etc up to the root usb device. - * - * The USB 3.0 standard allows up to 7 levels with 256 devices on each level (1 Byte). We generate - * a unique number for the connected device. - */ - static UniqueUSBid computeUSBdeviceID(libusb_device *device); - - /// \brief Get the oscilloscope model. - /// \return The ::Model of the connected Hantek DSO. - inline const DSOModel *getModel() const { return model; } - /** - * Usually a maximaum packet length for in and outgoing packets is determined - * by the underlying implementation and usb specification. E.g. the roll buffer - * mode uses the maximum in length for transfer. Some devices do not support - * that much data though and need an artification restriction. - */ - inline void overwriteInPacketLength(int len) { inPacketLength = len; } - protected: - int claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn); - - // Device model data - DSOModel* model; - - // Libusb specific variables - struct libusb_device_descriptor descriptor; - libusb_device *device; ///< The USB handle for the oscilloscope - libusb_device_handle *handle = nullptr; - unsigned findIteration; - const unsigned long uniqueUSBdeviceID; - int interface; - int outPacketLength; ///< Packet length for the OUT endpoint - int inPacketLength; ///< Packet length for the IN endpoint - signals: - void deviceDisconnected(); ///< The device has been disconnected -}; diff --git a/openhantek/src/usb/usbdevicedefinitions.h b/openhantek/src/usb/usbdevicedefinitions.h index 92580c36..919a020a 100644 --- a/openhantek/src/usb/usbdevicedefinitions.h +++ b/openhantek/src/usb/usbdevicedefinitions.h @@ -3,7 +3,7 @@ #pragma once #define HANTEK_TIMEOUT 500 ///< Timeout for USB transfers in ms -#define HANTEK_TIMEOUT_MULTI 100 ///< Timeout for multi packet USB transfers in ms +#define HANTEK_TIMEOUT_MULTI 500 ///< Timeout for multi packet USB transfers in ms #define HANTEK_ATTEMPTS 3 ///< The number of transfer attempts #define HANTEK_ATTEMPTS_MULTI 1 ///< The number of multi packet transfer attempts diff --git a/openhantek/src/utils/enumclass.h b/openhantek/src/utils/enumclass.h index 7ba33a58..b529528d 100644 --- a/openhantek/src/utils/enumclass.h +++ b/openhantek/src/utils/enumclass.h @@ -2,46 +2,27 @@ #pragma once -template< typename T, T first, T last > -class Enum -{ -public: - class Iterator - { - public: - Iterator( int value ) : - m_value( value ) - { } - - T operator*( void ) const - { - return (T)m_value; - } - - void operator++( void ) - { - ++m_value; - } - - bool operator!=( Iterator rhs ) - { - return m_value != rhs.m_value; - } - - private: - int m_value; - }; +template < typename T, T first, T last > class Enum { + public: + class Iterator { + public: + Iterator( int value ) : m_value( value ) {} + T operator*(void)const { return T( m_value ); } + + void operator++( void ) { ++m_value; } + + bool operator!=( Iterator rhs ) { return m_value != rhs.m_value; } + + private: + int m_value; + }; }; -template< typename T, T first, T last > -typename Enum::Iterator begin( Enum ) -{ - return typename Enum::Iterator( (int)first ); +template < typename T, T first, T last > typename Enum< T, first, last >::Iterator begin( Enum< T, first, last > ) { + return typename Enum< T, first, last >::Iterator( int( first ) ); } -template< typename T, T first, T last > -typename Enum::Iterator end( Enum ) -{ - return typename Enum::Iterator( ((int)last) + 1 ); +template < typename T, T first, T last > typename Enum< T, first, last >::Iterator end( Enum< T, first, last > ) { + return typename Enum< T, first, last >::Iterator( int( last ) + 1 ); } diff --git a/openhantek/src/utils/printutils.cpp b/openhantek/src/utils/printutils.cpp index 52d9d01c..92cb64a6 100644 --- a/openhantek/src/utils/printutils.cpp +++ b/openhantek/src/utils/printutils.cpp @@ -6,105 +6,105 @@ #include #include -#include - #include "utils/printutils.h" -QString valueToString(double value, Unit unit, int precision) { - char format = (precision < 0) ? 'g' : 'f'; +QString valueToString( double value, Unit unit, int precision ) { + char format = ( precision < 0 ) ? 'g' : 'f'; - switch (unit) { + switch ( unit ) { case UNIT_VOLTS: { // Voltage string representation - int logarithm = floor(log10(fabs(value))); - if (fabs(value) < 1e-3) - return QApplication::tr("%L1 µV").arg(value / 1e-6, 0, format, - (precision <= 0) ? precision - : qBound(0, precision - 7 - logarithm, precision)); - else if (fabs(value) < 1.0) - return QApplication::tr("%L1 mV").arg(value / 1e-3, 0, format, - (precision <= 0) ? precision : (precision - 4 - logarithm)); + int logarithm = int( floor( log10( fabs( value ) ) ) ); + if ( fabs( value ) < 1e-3 ) + return QApplication::tr( "%L1 µV" ) + .arg( value / 1e-6, 0, format, ( precision <= 0 ) ? precision : qBound( 0, precision - 7 - logarithm, precision ) ); + else if ( fabs( value ) < 1.0 ) + return QApplication::tr( "%L1 mV" ) + .arg( value / 1e-3, 0, format, ( precision <= 0 ) ? precision : ( precision - 4 - logarithm ) ); else - return QApplication::tr("%L1 V").arg(value, 0, format, - (precision <= 0) ? precision : qMax(0, precision - 1 - logarithm)); + return QApplication::tr( "%L1 V" ).arg( value, 0, format, + ( precision <= 0 ) ? precision : qMax( 0, precision - 1 - logarithm ) ); } case UNIT_DECIBEL: // Power level string representation - return QApplication::tr("%L1 dB").arg( - value, 0, format, - (precision <= 0) ? precision : qBound(0, precision - 1 - (int)floor(log10(fabs(value))), precision)); + return QApplication::tr( "%L1 dB" ) + .arg( value, 0, format, + ( precision <= 0 ) ? precision : qBound( 0, precision - 1 - int( floor( log10( fabs( value ) ) ) ), precision ) ); case UNIT_SECONDS: // Time string representation - if (fabs(value) < 1e-9) - return QApplication::tr("%L1 ps").arg( - value / 1e-12, 0, format, - (precision <= 0) ? precision : qBound(0, precision - 13 - (int)floor(log10(fabs(value))), precision)); - else if (fabs(value) < 1e-6) - return QApplication::tr("%L1 ns").arg(value / 1e-9, 0, format, - (precision <= 0) ? precision - : (precision - 10 - (int)floor(log10(fabs(value))))); - else if (fabs(value) < 1e-3) - return QApplication::tr("%L1 µs").arg(value / 1e-6, 0, format, - (precision <= 0) ? precision - : (precision - 7 - (int)floor(log10(fabs(value))))); - else if (fabs(value) < 1.0) - return QApplication::tr("%L1 ms").arg(value / 1e-3, 0, format, - (precision <= 0) ? precision - : (precision - 4 - (int)floor(log10(fabs(value))))); - else if (fabs(value) < 60) - return QApplication::tr("%L1 s").arg( - value, 0, format, (precision <= 0) ? precision : (precision - 1 - (int)floor(log10(fabs(value))))); - else if (fabs(value) < 3600) - return QApplication::tr("%L1 min").arg( - value / 60, 0, format, (precision <= 0) ? precision : (precision - 1 - (int)floor(log10(value / 60)))); + if ( fabs( value ) < 1e-9 ) + return QApplication::tr( "%L1 ps" ) + .arg( value / 1e-12, 0, format, + ( precision <= 0 ) ? precision + : qBound( 0, precision - 13 - int( floor( log10( fabs( value ) ) ) ), precision ) ); + else if ( fabs( value ) < 1e-6 ) + return QApplication::tr( "%L1 ns" ) + .arg( value / 1e-9, 0, format, + ( precision <= 0 ) ? precision : ( precision - 10 - int( floor( log10( fabs( value ) ) ) ) ) ); + else if ( fabs( value ) < 1e-3 ) + return QApplication::tr( "%L1 µs" ) + .arg( value / 1e-6, 0, format, + ( precision <= 0 ) ? precision : ( precision - 7 - int( floor( log10( fabs( value ) ) ) ) ) ); + else if ( fabs( value ) < 1.0 ) + return QApplication::tr( "%L1 ms" ) + .arg( value / 1e-3, 0, format, + ( precision <= 0 ) ? precision : ( precision - 4 - int( floor( log10( fabs( value ) ) ) ) ) ); + else if ( fabs( value ) < 60 ) + return QApplication::tr( "%L1 s" ).arg( + value, 0, format, ( precision <= 0 ) ? precision : ( precision - 1 - int( floor( log10( fabs( value ) ) ) ) ) ); + else if ( fabs( value ) < 3600 ) + return QApplication::tr( "%L1 min" ) + .arg( value / 60, 0, format, + ( precision <= 0 ) ? precision : ( precision - 1 - int( floor( log10( value / 60 ) ) ) ) ); else - return QApplication::tr("%L1 h").arg( + return QApplication::tr( "%L1 h" ).arg( value / 3600, 0, format, - (precision <= 0) ? precision : qMax(0, precision - 1 - (int)floor(log10(value / 3600)))); + ( precision <= 0 ) ? precision : qMax( 0, precision - 1 - int( floor( log10( value / 3600 ) ) ) ) ); case UNIT_HERTZ: { // Frequency string representation - int logarithm = floor(log10(fabs(value))); - if (fabs(value) < 1e3) - return QApplication::tr("%L1 Hz").arg( - value, 0, format, (precision <= 0) ? precision : qBound(0, precision - 1 - logarithm, precision)); - else if (fabs(value) < 1e6) - return QApplication::tr("%L1 kHz").arg(value / 1e3, 0, format, - (precision <= 0) ? precision : precision + 2 - logarithm); - else if (fabs(value) < 1e9) - return QApplication::tr("%L1 MHz").arg(value / 1e6, 0, format, - (precision <= 0) ? precision : precision + 5 - logarithm); + int logarithm = int( floor( log10( fabs( value ) ) ) ); + if ( fabs( value ) < 1e3 ) + return QApplication::tr( "%L1 Hz" ) + .arg( value, 0, format, ( precision <= 0 ) ? precision : qBound( 0, precision - 1 - logarithm, precision ) ); + else if ( fabs( value ) < 1e6 ) + return QApplication::tr( "%L1 kHz" ) + .arg( value / 1e3, 0, format, ( precision <= 0 ) ? precision : precision + 2 - logarithm ); + else if ( fabs( value ) < 1e9 ) + return QApplication::tr( "%L1 MHz" ) + .arg( value / 1e6, 0, format, ( precision <= 0 ) ? precision : precision + 5 - logarithm ); else - return QApplication::tr("%L1 GHz").arg(value / 1e9, 0, format, - (precision <= 0) ? precision : qMax(0, precision + 8 - logarithm)); + return QApplication::tr( "%L1 GHz" ) + .arg( value / 1e9, 0, format, ( precision <= 0 ) ? precision : qMax( 0, precision + 8 - logarithm ) ); } case UNIT_SAMPLES: { // Sample count string representation - int logarithm = floor(log10(fabs(value))); - if (fabs(value) < 1e3) - return QApplication::tr("%L1 S").arg( - value, 0, format, (precision <= 0) ? precision : qBound(0, precision - 1 - logarithm, precision)); - else if (fabs(value) < 1e6) - return QApplication::tr("%L1 kS").arg(value / 1e3, 0, format, - (precision <= 0) ? precision : precision + 2 - logarithm); - else if (fabs(value) < 1e9) - return QApplication::tr("%L1 MS").arg(value / 1e6, 0, format, - (precision <= 0) ? precision : precision + 5 - logarithm); + int logarithm = int( floor( log10( fabs( value ) ) ) ); + if ( fabs( value ) < 1e3 ) + return QApplication::tr( "%L1 S" ).arg( + value, 0, format, ( precision <= 0 ) ? precision : qBound( 0, precision - 1 - logarithm, precision ) ); + else if ( fabs( value ) < 1e6 ) + return QApplication::tr( "%L1 kS" ) + .arg( value / 1e3, 0, format, ( precision <= 0 ) ? precision : precision + 2 - logarithm ); + else if ( fabs( value ) < 1e9 ) + return QApplication::tr( "%L1 MS" ) + .arg( value / 1e6, 0, format, ( precision <= 0 ) ? precision : precision + 5 - logarithm ); else - return QApplication::tr("%L1 GS").arg(value / 1e9, 0, format, - (precision <= 0) ? precision : qMax(0, precision + 8 - logarithm)); + return QApplication::tr( "%L1 GS" ) + .arg( value / 1e9, 0, format, ( precision <= 0 ) ? precision : qMax( 0, precision + 8 - logarithm ) ); } default: return QString(); } } -double stringToValue(const QString &text, Unit unit, bool *ok) { +double stringToValue( const QString &text, Unit unit, bool *ok ) { // Check if the text is empty int totalSize = text.size(); - if (!totalSize) { - if (ok) *ok = false; + if ( !totalSize ) { + if ( ok ) + *ok = false; return 0.0; } @@ -113,36 +113,40 @@ double stringToValue(const QString &text, Unit unit, bool *ok) { QLocale locale; bool decimalFound = false; bool exponentFound = false; - if (text[valueSize] == locale.negativeSign()) ++valueSize; - for (; valueSize < text.size(); ++valueSize) { - QChar character = text[valueSize]; + if ( text[ valueSize ] == locale.negativeSign() ) + ++valueSize; + for ( ; valueSize < text.size(); ++valueSize ) { + QChar character = text[ valueSize ]; - if (character.isDigit()) { - } else if (character == locale.decimalPoint() && decimalFound == false && exponentFound == false) { + if ( character.isDigit() ) { + } else if ( character == locale.decimalPoint() && decimalFound == false && exponentFound == false ) { decimalFound = true; - } else if (character == locale.exponential() && exponentFound == false) { + } else if ( character == locale.exponential() && exponentFound == false ) { exponentFound = true; - if (text[valueSize + 1] == locale.negativeSign()) ++valueSize; + if ( text[ valueSize + 1 ] == locale.negativeSign() ) + ++valueSize; } else { break; } } - QString valueString = text.left(valueSize); + QString valueString = text.left( valueSize ); bool valueOk = false; - double value = valueString.toDouble(&valueOk); - if (!valueOk) { - if (ok) *ok = false; + double value = valueString.toDouble( &valueOk ); + if ( !valueOk ) { + if ( ok ) + *ok = false; return value; } - QString unitString = text.right(text.size() - valueSize).trimmed(); + QString unitString = text.right( text.size() - valueSize ).trimmed(); - if (ok) *ok = true; - switch (unit) { + if ( ok ) + *ok = true; + switch ( unit ) { case UNIT_VOLTS: { // Voltage string decoding - if (unitString.startsWith("µ")) + if ( unitString.startsWith( "µ" ) ) // my return value * 1e-6; - else if (unitString.startsWith('m')) + else if ( unitString.startsWith( 'm' ) ) return value * 1e-3; else return value; @@ -153,75 +157,91 @@ double stringToValue(const QString &text, Unit unit, bool *ok) { case UNIT_SECONDS: // Time string decoding - if (unitString.startsWith('p')) + if ( unitString.startsWith( 'p' ) ) return value * 1e-12; - else if (unitString.startsWith('n')) + else if ( unitString.startsWith( 'n' ) ) return value * 1e-9; - else if (unitString.startsWith("µ")) + else if ( unitString.startsWith( "µ" ) ) // my return value * 1e-6; - else if (unitString.startsWith("min")) + else if ( unitString.startsWith( "min" ) ) return value * 60; - else if (unitString.startsWith('m')) + else if ( unitString.startsWith( 'm' ) ) return value * 1e-3; - else if (unitString.startsWith('h')) + else if ( unitString.startsWith( 'h' ) ) return value * 3600; else return value; case UNIT_HERTZ: // Frequency string decoding - if (unitString.startsWith('k')) + if ( unitString.startsWith( 'k' ) ) return value * 1e3; - else if (unitString.startsWith('M')) + else if ( unitString.startsWith( 'M' ) ) return value * 1e6; - else if (unitString.startsWith('G')) + else if ( unitString.startsWith( 'G' ) ) return value * 1e9; else return value; case UNIT_SAMPLES: // Sample count string decoding - if (unitString.startsWith('k')) + if ( unitString.startsWith( 'k' ) ) return value * 1e3; - else if (unitString.startsWith('M')) + else if ( unitString.startsWith( 'M' ) ) return value * 1e6; - else if (unitString.startsWith('G')) + else if ( unitString.startsWith( 'G' ) ) return value * 1e9; else return value; default: - if (ok) *ok = false; + if ( ok ) + *ok = false; return value; } } -QString hexDump(unsigned char *data, unsigned int length) { +QString hexDump( unsigned char *data, unsigned int length ) { QString dumpString, byteString; + for ( unsigned int index = 0; index < length; ++index ) + dumpString.append( QString( "0x%1 " ).arg( data[ index ], 2, 16, QChar( '0' ) ) ); + return dumpString; +} - for (unsigned int index = 0; index < length; ++index) dumpString.append(byteString.sprintf(" %02x", data[index])); +QString decDump( unsigned char *data, unsigned int length ) { + QString dumpString, byteString; + for ( unsigned int index = 0; index < length; ++index ) + dumpString.append( QString( "%1 " ).arg( data[ index ] ) ); + return dumpString; +} +QString hexdecDump( unsigned char *data, unsigned int length ) { + QString dumpString, byteString; + for ( unsigned int index = 0; index < length; ++index ) + dumpString.append( QString( "0x%1 (%2) " ).arg( data[ index ], 2, 16, QChar( '0' ) ).arg( data[ index ] ) ); return dumpString; } -unsigned int hexParse(const QString dump, unsigned char *data, unsigned int length) { +unsigned int hexParse( const QString dump, uint8_t *data, unsigned int length ) { QString dumpString = dump; - dumpString.remove(' '); + dumpString.remove( ' ' ); QString byteString; unsigned int index; - for (index = 0; index < length; ++index) { - byteString = dumpString.mid(index * 2, 2); + for ( index = 0; index < length; ++index ) { + byteString = dumpString.mid( int( index ) * 2, 2 ); // Check if we reached the end of the string - if (byteString.isNull()) break; + if ( byteString.isNull() ) + break; // Check for parsing errors bool ok; - unsigned char byte = (unsigned char)byteString.toUShort(&ok, 16); - if (!ok) break; + uint8_t byte = uint8_t( byteString.toUShort( &ok, 16 ) ); + if ( !ok ) + break; - data[index] = byte; + data[ index ] = byte; } return index; diff --git a/openhantek/src/utils/printutils.h b/openhantek/src/utils/printutils.h index b3ee5a54..f5504dac 100644 --- a/openhantek/src/utils/printutils.h +++ b/openhantek/src/utils/printutils.h @@ -17,7 +17,7 @@ enum Unit { UNIT_VOLTS, UNIT_DECIBEL, UNIT_SECONDS, UNIT_HERTZ, UNIT_SAMPLES, UN /// \param unit The unit for the value. /// \param precision Significant digits, 0 for integer, -1 for auto. /// \return String with the value and unit. -QString valueToString(double value, Unit unit, int precision = -1); +QString valueToString( double value, Unit unit, int precision = -1 ); /// \brief Converts string containing value and (prefix+)unit to double /// (Counterpart to valueToString). @@ -25,27 +25,29 @@ QString valueToString(double value, Unit unit, int precision = -1); /// \param unit The base unit of the value. /// \param ok Pointer to a success-flag, true on success, false on error. /// \return Decoded value. -double stringToValue(const QString &text, Unit unit, bool *ok = 0); +double stringToValue( const QString &text, Unit unit, bool *ok = nullptr ); -/// \brief Returns the hex dump for the given data. +/// \brief Returns the hex or decimal dump for the given data. /// \param data Pointer to the data bytes that should be dumped. /// \param length The length of the data array in bytes. -/// \return String with the hex dump of the data. -QString hexDump(unsigned char *data, unsigned int length); +/// \return String with the dump of the data. +QString hexDump( unsigned char *data, unsigned int length ); +QString decDump( unsigned char *data, unsigned int length ); +QString hexdecDump( unsigned char *data, unsigned int length ); /// \brief Returns the hex dump for the given data. /// \param dump The string with the hex dump of the data. /// \param data Pointer to the address where the data bytes should be saved. /// \param length The maximum length of the data array in bytes. /// \return The length of the saved data. -unsigned int hexParse(const QString dump, unsigned char *data, unsigned int length); +unsigned int hexParse( const QString dump, unsigned char *data, unsigned int length ); /// \brief Print debug information with timestamp. /// \param text Text that will be output via qDebug. -#ifdef DEBUG -inline void timestampDebug(const QString& text) { - qDebug("%s: %s", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().constData(), text.toLatin1().constData()); +#ifdef TIMESTAMPDEBUG +inline void timestampDebug( const QString &text ) { + qDebug( "%s: %s", QTime::currentTime().toString( "hh:mm:ss.zzz" ).toLatin1().constData(), text.toLatin1().constData() ); } #else -#define timestampDebug(ARG) +#define timestampDebug( ARG ) #endif diff --git a/openhantek/src/viewconstants.h b/openhantek/src/viewconstants.h index d26ee759..6eced6c6 100644 --- a/openhantek/src/viewconstants.h +++ b/openhantek/src/viewconstants.h @@ -2,6 +2,22 @@ #pragma once -#define DIVS_TIME 10.0f ///< Number of horizontal screen divs -#define DIVS_VOLTAGE 8.0f ///< Number of vertical screen divs +#define DIVS_TIME 10.0 ///< Number of horizontal screen divs +#define DIVS_VOLTAGE 8.0 ///< Number of vertical screen divs #define DIVS_SUB 5 ///< Number of sub-divisions per div + +#define MARGIN_LEFT ( -DIVS_TIME / 2.0 ) +#define MARGIN_RIGHT ( DIVS_TIME / 2.0 ) +#define MARGIN_TOP ( DIVS_VOLTAGE / 2.0 ) +#define MARGIN_BOTTOM ( -DIVS_VOLTAGE / 2.0 ) + +#define MARKER_STEP ( DIVS_TIME / 100.0 ) + +// where are the (local) documents? +#ifdef __FreeBSD__ +#define USR_MANUAL_PATH "/usr/local/share/doc/openhantek/OpenHantek6022_User_Manual.pdf" +#define AC_MODIFICATION_PATH "/usr/local/share/doc/openhantek/HANTEK6022_AC_Modification.pdf" +#else +#define USR_MANUAL_PATH "/usr/share/doc/openhantek/OpenHantek6022_User_Manual.pdf" +#define AC_MODIFICATION_PATH "/usr/share/doc/openhantek/HANTEK6022_AC_Modification.pdf" +#endif diff --git a/openhantek/src/viewsettings.h b/openhantek/src/viewsettings.h index c72d63aa..957c0527 100644 --- a/openhantek/src/viewsettings.h +++ b/openhantek/src/viewsettings.h @@ -14,38 +14,38 @@ /// \struct DsoSettingsColorValues /// \brief Holds the color values for the oscilloscope screen. struct DsoSettingsColorValues { - QColor axes; ///< X- and Y-axis and subdiv lines on them - QColor background; ///< The scope background - QColor border; ///< The border of the scope screen - QColor grid; ///< The color of the grid - QColor markers; ///< The color of the markers - QColor text; ///< The default text color - std::vector spectrum; ///< The colors of the spectrum graphs - std::vector voltage; ///< The colors of the voltage graphs + QColor axes; ///< X- and Y-axis and subdiv lines on them + QColor background; ///< The scope background + QColor border; ///< The border of the scope screen + QColor grid; ///< The color of the grid + QColor markers; ///< The color of the markers + QColor text; ///< The default text color + std::vector< QColor > spectrum; ///< The colors of the spectrum graphs + std::vector< QColor > voltage; ///< The colors of the voltage graphs }; //////////////////////////////////////////////////////////////////////////////// /// \struct DsoSettingsView /// \brief Holds all view settings. struct DsoSettingsView { - DsoSettingsColorValues screen = {QColor(0xff, 0xff, 0xff, 0x7f), QColor(0x00, 0x00, 0x00, 0xff), - QColor(0xff, 0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff, 0x3f), - QColor(0xff, 0xff, 0xff, 0xbf), QColor(0xff, 0xff, 0xff, 0xff), - std::vector(), std::vector()}; - DsoSettingsColorValues print = {QColor(0x00, 0x00, 0x00, 0xbf), QColor(0x00, 0x00, 0x00, 0x00), - QColor(0x00, 0x00, 0x00, 0xff), QColor(0x00, 0x00, 0x00, 0x7f), - QColor(0x00, 0x00, 0x00, 0xef), QColor(0x00, 0x00, 0x00, 0xff), - std::vector(), std::vector()}; + DsoSettingsColorValues screen = {QColor( 0x7f, 0x7f, 0x7f, 0xff ), QColor( 0x00, 0x00, 0x00, 0xff ), // axes, background + QColor( 0xff, 0xff, 0xff, 0xff ), QColor( 0xc0, 0xc0, 0xc0, 0xff ), // border, grid + QColor( 0xc0, 0xc0, 0xc0, 0xff ), QColor( 0xff, 0xff, 0xff, 0xff ), // markers, text + std::vector< QColor >(), std::vector< QColor >()}; // spectrum, voltage + DsoSettingsColorValues print = {QColor( 0x40, 0x40, 0x40, 0xff ), QColor( 0xff, 0xff, 0xff, 0xff ), // axes, background + QColor( 0x00, 0x00, 0x00, 0xff ), QColor( 0x40, 0x40, 0x40, 0xff ), // border, grid + QColor( 0x40, 0x40, 0x40, 0xff ), QColor( 0x00, 0x00, 0x00, 0xff ), // markers, text + std::vector< QColor >(), std::vector< QColor >()}; // spectrum, voltage bool antialiasing = true; ///< Antialiasing for the graphs bool digitalPhosphor = false; ///< true slowly fades out the previous graphs unsigned digitalPhosphorDepth = 8; ///< Number of channels shown at one time Dso::InterpolationMode interpolation = Dso::INTERPOLATION_LINEAR; ///< Interpolation mode for the graph - bool screenColorImages = false; ///< true exports images with screen colors + bool printerColorImages = true; ///< Exports images with screen colors + bool zoomImage = true; ///< Export zoomed images with double height bool zoom = false; ///< true if the magnified scope is enabled Qt::ToolBarArea cursorGridPosition = Qt::RightToolBarArea; bool cursorsVisible = false; + DsoSettingsColorValues *colors = &screen; - unsigned digitalPhosphorDraws() const { - return digitalPhosphor ? digitalPhosphorDepth : 1; - } + unsigned digitalPhosphorDraws() const { return digitalPhosphor ? digitalPhosphorDepth : 1; } }; diff --git a/openhantek/src/widgets/colorbox.cpp b/openhantek/src/widgets/colorbox.cpp index faacf3e0..21814fe8 100644 --- a/openhantek/src/widgets/colorbox.cpp +++ b/openhantek/src/widgets/colorbox.cpp @@ -30,12 +30,11 @@ //////////////////////////////////////////////////////////////////////////////// // class ColorBox /// \brief Initializes the widget. -/// \param color Initial color value. +/// \param color_ Initial color value. /// \param parent The parent widget. -ColorBox::ColorBox(QColor color, QWidget *parent) : QPushButton(parent) { - this->setColor(color); - - connect(this, &QAbstractButton::clicked, this, &ColorBox::waitForColor); +ColorBox::ColorBox( QColor color_, QWidget *parent ) : QPushButton( parent ) { + setColor( color_ ); + connect( this, &QAbstractButton::clicked, this, &ColorBox::waitForColor ); } /// \brief Cleans up the widget. @@ -43,24 +42,22 @@ ColorBox::~ColorBox() {} /// \brief Get the current color. /// \return The current color as QColor. -const QColor ColorBox::getColor() { return this->color; } +const QColor ColorBox::getColor() { return color; } /// \brief Sets the color. -/// \param color The new color. -void ColorBox::setColor(QColor color) { - this->color = color; - this->setText(QString("#%1").arg((unsigned int)this->color.rgba(), 8, 16, QChar('0'))); - this->setPalette(QPalette(this->color)); - - emit colorChanged(this->color); +/// \param newColor The new color. +void ColorBox::setColor( QColor newColor ) { + color = newColor; + setText( QString( "#%1" ).arg( unsigned( color.rgba() ), 8, 16, QChar( '0' ) ) ); + setPalette( QPalette( color ) ); + emit colorChanged( color ); } /// \brief Wait for the color dialog and apply chosen color. void ColorBox::waitForColor() { - this->setFocus(); - this->setDown(true); - - QColor color = QColorDialog::getColor(this->color, this, 0, QColorDialog::ShowAlphaChannel); - - if (color.isValid()) this->setColor(color); + setFocus(); + setDown( true ); + QColor newColor = QColorDialog::getColor( color, this, nullptr, QColorDialog::ShowAlphaChannel ); + if ( newColor.isValid() ) + setColor( newColor ); } diff --git a/openhantek/src/widgets/colorbox.h b/openhantek/src/widgets/colorbox.h index 8519fed9..49ff5dbc 100644 --- a/openhantek/src/widgets/colorbox.h +++ b/openhantek/src/widgets/colorbox.h @@ -10,18 +10,18 @@ class ColorBox : public QPushButton { Q_OBJECT public: - ColorBox(QColor color, QWidget *parent = 0); + ColorBox( QColor color, QWidget *parent = nullptr ); ~ColorBox(); const QColor getColor(); public slots: - void setColor(QColor color); + void setColor( QColor color ); void waitForColor(); private: QColor color; signals: - void colorChanged(QColor color); ///< The color has been changed + void colorChanged( QColor color ); ///< The color has been changed }; diff --git a/openhantek/src/widgets/datagrid.cpp b/openhantek/src/widgets/datagrid.cpp index bb38f0a2..86b8f863 100644 --- a/openhantek/src/widgets/datagrid.cpp +++ b/openhantek/src/widgets/datagrid.cpp @@ -2,43 +2,41 @@ #include "datagrid.h" +#include #include #include #include -#include -DataGrid::DataGrid(QWidget *parent) : QGroupBox(parent) -{ +DataGrid::DataGrid( QWidget *parent ) : QGroupBox( parent ) { cursorsLayout = new QGridLayout(); - cursorsLayout->setSpacing(5); + cursorsLayout->setSpacing( 5 ); cursorsSelectorGroup = new QButtonGroup(); - cursorsSelectorGroup->setExclusive(true); + cursorsSelectorGroup->setExclusive( true ); - connect(cursorsSelectorGroup, - static_cast(&QButtonGroup::buttonPressed), [this] (int index) { - emit itemSelected(index); - }); + connect( cursorsSelectorGroup, static_cast< void ( QButtonGroup::* )( int ) >( &QButtonGroup::buttonPressed ), + [this]( unsigned index ) { emit itemSelected( index ); } ); - setLayout(cursorsLayout); - setFixedWidth(180); + setLayout( cursorsLayout ); + setFixedWidth( 180 ); } DataGrid::CursorInfo::CursorInfo() { selector = new QPushButton(); - selector->setCheckable(true); + selector->setCheckable( true ); shape = new QPushButton(); deltaXLabel = new QLabel(); - deltaXLabel->setAlignment(Qt::AlignRight); + deltaXLabel->setAlignment( Qt::AlignRight ); deltaYLabel = new QLabel(); - deltaYLabel->setAlignment(Qt::AlignRight); + deltaYLabel->setAlignment( Qt::AlignRight ); } -void DataGrid::CursorInfo::configure(const QString &text, const QColor &bgColor, const QColor &fgColor) { - palette.setColor(QPalette::Background, bgColor); - palette.setColor(QPalette::WindowText, fgColor); +void DataGrid::CursorInfo::configure( const QString &text, const QColor &bgColor, const QColor &fgColor ) { + palette.setColor( QPalette::Background, bgColor ); + palette.setColor( QPalette::WindowText, fgColor ); - selector->setText(text); - selector->setStyleSheet(QString(R"( + selector->setText( text ); + selector->setStyleSheet( + QString( R"( QPushButton { color: %2; background-color: %1; @@ -52,75 +50,74 @@ void DataGrid::CursorInfo::configure(const QString &text, const QColor &bgColor, color: %3; border: 1px dotted %2; } - )").arg(bgColor.name(QColor::HexArgb)) - .arg(fgColor.name(QColor::HexArgb)) - .arg(fgColor.darker().name(QColor::HexArgb))); + )" ) + .arg( bgColor.name( QColor::HexArgb ), fgColor.name( QColor::HexArgb ), fgColor.darker().name( QColor::HexArgb ) ) ); - shape->setStyleSheet(QString(R"( + shape->setStyleSheet( QString( R"( QPushButton { color: %2; background-color: %1; border: none } - )").arg(bgColor.name(QColor::HexArgb)) - .arg(fgColor.name(QColor::HexArgb))); + )" ) + .arg( bgColor.name( QColor::HexArgb ), fgColor.name( QColor::HexArgb ) ) ); - deltaXLabel->setPalette(palette); - deltaYLabel->setPalette(palette); + deltaXLabel->setPalette( palette ); + deltaYLabel->setPalette( palette ); } -void DataGrid::setBackgroundColor(const QColor &bgColor) { +void DataGrid::setBackgroundColor( const QColor &bgColor ) { backgroundColor = bgColor; - for (auto it : items) { - it.configure(it.selector->text(), bgColor, it.palette.color(QPalette::WindowText)); + for ( auto it : items ) { + it.configure( it.selector->text(), bgColor, it.palette.color( QPalette::WindowText ) ); } } -void DataGrid::configureItem(unsigned index, const QColor &fgColor) { - if (index < items.size()) { - items[index].configure(items[index].selector->text(), backgroundColor, fgColor); +void DataGrid::configureItem( unsigned index, const QColor &fgColor ) { + if ( index < items.size() ) { + items[ index ].configure( items[ index ].selector->text(), backgroundColor, fgColor ); } } -unsigned DataGrid::addItem(const QString &text, const QColor &fgColor) { - unsigned index = items.size(); - items.resize(index + 1); +int DataGrid::addItem( const QString &text, const QColor &fgColor ) { + int index = int( items.size() ); + items.resize( ulong( index + 1 ) ); - CursorInfo& info = items.at(index); - info.configure(text, backgroundColor, fgColor); - cursorsSelectorGroup->addButton(info.selector, index); + CursorInfo &info = items.at( ulong( index ) ); + info.configure( text, backgroundColor, fgColor ); + cursorsSelectorGroup->addButton( info.selector, index ); - connect(info.shape, &QPushButton::clicked, [this, index] () { - emit itemUpdated(index); - }); + connect( info.shape, &QPushButton::clicked, [this, index]() { emit itemUpdated( unsigned( index ) ); } ); - cursorsLayout->addWidget(info.selector, 3 * index, 0); - cursorsLayout->addWidget(info.shape, 3 * index, 1); - cursorsLayout->addWidget(info.deltaXLabel, 3 * index + 1, 0); - cursorsLayout->addWidget(info.deltaYLabel, 3 * index + 1, 1); - cursorsLayout->setRowMinimumHeight(3 * index + 2, 10); - cursorsLayout->setRowStretch(3 * index, 0); - cursorsLayout->setRowStretch(3 * index + 3, 1); + cursorsLayout->addWidget( info.selector, 3 * index, 0 ); + cursorsLayout->addWidget( info.shape, 3 * index, 1 ); + cursorsLayout->addWidget( info.deltaXLabel, 3 * index + 1, 0 ); + cursorsLayout->addWidget( info.deltaYLabel, 3 * index + 1, 1 ); + cursorsLayout->setRowMinimumHeight( 3 * index + 2, 10 ); + cursorsLayout->setRowStretch( 3 * index, 0 ); + cursorsLayout->setRowStretch( 3 * index + 3, 1 ); return index; } -void DataGrid::updateInfo(unsigned index, bool visible, const QString &strShape, const QString &strX, const QString &strY) { - if (index >= items.size()) return; - CursorInfo &info = items.at(index); - info.selector->setEnabled(visible); - if (visible) { - info.shape->setText(strShape); - info.deltaXLabel->setText(strX); - info.deltaYLabel->setText(strY); +void DataGrid::updateInfo( unsigned index, bool visible, const QString &strShape, const QString &strX, const QString &strY ) { + if ( index >= items.size() ) + return; + CursorInfo &info = items.at( index ); + info.selector->setEnabled( visible ); + if ( visible ) { + info.shape->setText( strShape ); + info.deltaXLabel->setText( strX ); + info.deltaYLabel->setText( strY ); } else { - info.shape->setText(QString()); - info.deltaXLabel->setText(QString()); - info.deltaYLabel->setText(QString()); + info.shape->setText( QString() ); + info.deltaXLabel->setText( QString() ); + info.deltaYLabel->setText( QString() ); } } -void DataGrid::selectItem(unsigned index) { - if (index >= items.size()) return; - items[index].selector->setChecked(true); +void DataGrid::selectItem( unsigned index ) { + if ( index >= items.size() ) + return; + items[ index ].selector->setChecked( true ); } diff --git a/openhantek/src/widgets/datagrid.h b/openhantek/src/widgets/datagrid.h index b5169bae..761ffd22 100644 --- a/openhantek/src/widgets/datagrid.h +++ b/openhantek/src/widgets/datagrid.h @@ -10,39 +10,39 @@ class QButtonGroup; class QLabel; class QGridLayout; -class DataGrid : public QGroupBox -{ +class DataGrid : public QGroupBox { Q_OBJECT -public: - explicit DataGrid(QWidget *parent = nullptr); + + public: + explicit DataGrid( QWidget *parent = nullptr ); struct CursorInfo { - QPalette palette; ///< The widget's palette - QPushButton *selector; ///< The name of the channel - QPushButton *shape; ///< The cursor shape - QLabel *deltaXLabel; ///< The horizontal distance between cursors - QLabel *deltaYLabel; ///< The vertical distance between cursors + QPalette palette; ///< The widget's palette + QPushButton *selector; ///< The name of the channel + QPushButton *shape; ///< The cursor shape + QLabel *deltaXLabel; ///< The horizontal distance between cursors + QLabel *deltaYLabel; ///< The vertical distance between cursors CursorInfo(); - void configure(const QString &text, const QColor &bgColor, const QColor &fgColor); + void configure( const QString &text, const QColor &bgColor, const QColor &fgColor ); }; - unsigned addItem(const QString &text, const QColor &fgColor); - void setBackgroundColor(const QColor &bgColor); - void configureItem(unsigned index, const QColor &fgColor); - void updateInfo(unsigned index, bool visible, const QString &strShape = QString(), - const QString &strX = QString(), const QString &strY = QString()); + int addItem( const QString &text, const QColor &fgColor ); + void setBackgroundColor( const QColor &bgColor ); + void configureItem( unsigned index, const QColor &fgColor ); + void updateInfo( unsigned index, bool visible, const QString &strShape = QString(), const QString &strX = QString(), + const QString &strY = QString() ); -signals: - void itemSelected(unsigned index); - void itemUpdated(unsigned index); + signals: + void itemSelected( unsigned index ); + void itemUpdated( unsigned index ); -public slots: - void selectItem(unsigned index); + public slots: + void selectItem( unsigned index ); -private: + private: QColor backgroundColor; QButtonGroup *cursorsSelectorGroup; QGridLayout *cursorsLayout; - std::vector items; + std::vector< CursorInfo > items; }; diff --git a/openhantek/src/widgets/levelslider.cpp b/openhantek/src/widgets/levelslider.cpp index f2890a80..54fa3df5 100644 --- a/openhantek/src/widgets/levelslider.cpp +++ b/openhantek/src/widgets/levelslider.cpp @@ -34,15 +34,15 @@ /// \brief Initializes the slider container. /// \param direction The side on which the sliders are shown. /// \param parent The parent widget. -LevelSlider::LevelSlider(Qt::ArrowType direction, QWidget *parent) : QWidget(parent) { - QFont font = this->font(); - font.setPointSize(font.pointSize() * 0.8); - this->setFont(font); +LevelSlider::LevelSlider( Qt::ArrowType direction, QWidget *parent ) : QWidget( parent ) { + // Set pixel values based on the current dpi scaling + needleWidth = ( int( 0.55 * fontMetrics().height() ) ) + 1; // always an odd number + sliderWidth = ( int( 1.2 * fontMetrics().height() ) ); - this->pressedSlider = -1; + pressedSlider = -1; calculateWidth(); - setDirection(direction); + setDirection( direction ); } /// \brief Cleans up the widget. @@ -50,23 +50,24 @@ LevelSlider::~LevelSlider() {} /// \brief Return the margin before the slider. /// \return The margin the Slider has at the top/left. -int LevelSlider::preMargin() const { return this->_preMargin; } +int LevelSlider::preMargin() const { return _preMargin; } /// \brief Return the margin after the slider. /// \return The margin the Slider has at the bottom/right. -int LevelSlider::postMargin() const { return this->_postMargin; } +int LevelSlider::postMargin() const { return _postMargin; } /// \brief Add a new slider to the slider container. /// \param index The index where the slider should be inserted, 0 to append. /// \return The index of the slider, -1 on error. -int LevelSlider::addSlider(int index) { return this->addSlider("", index); } +int LevelSlider::addSlider( int index ) { return addSlider( "", index ); } /// \brief Add a new slider to the slider container. /// \param text The text that will be shown next to the slider. /// \param index The index where the slider should be inserted, 0 to append. /// \return The index of the slider, -1 on error. -int LevelSlider::addSlider(const QString& text, int index) { - if (index < -1) return -1; +int LevelSlider::addSlider( const QString &text, int index ) { + if ( index < -1 ) + return -1; LevelSliderParameters *parameters = new LevelSliderParameters; parameters->color = Qt::white; @@ -75,13 +76,13 @@ int LevelSlider::addSlider(const QString& text, int index) { parameters->value = 0x00; parameters->visible = false; - if (index == -1) { - this->slider.append(parameters); - index = this->slider.count() - 1; + if ( index == -1 ) { + slider.append( parameters ); + index = slider.count() - 1; } else - this->slider.insert(index, parameters); + slider.insert( index, parameters ); - this->setText(index, text); + setText( index, text ); return index; } @@ -89,17 +90,18 @@ int LevelSlider::addSlider(const QString& text, int index) { /// \brief Remove a slider from the slider container. /// \param index The index of the slider that should be removed. /// \return The index of the removed slider, -1 on error. -int LevelSlider::removeSlider(int index) { - if (index < -1) return -1; +int LevelSlider::removeSlider( int index ) { + if ( index < -1 ) + return -1; - if (index == -1) { - this->slider.removeLast(); - index = this->slider.count(); + if ( index == -1 ) { + slider.removeLast(); + index = slider.count(); } else { - this->slider.removeAt(index); + slider.removeAt( index ); } - this->calculateWidth(); + calculateWidth(); return index; } @@ -107,50 +109,54 @@ int LevelSlider::removeSlider(int index) { /// \brief Size hint for the widget. /// \return The recommended size for the widget. QSize LevelSlider::sizeHint() const { - if (this->_direction == Qt::RightArrow || this->_direction == Qt::LeftArrow) - return QSize(this->sliderWidth, 16); + if ( _direction == Qt::RightArrow || _direction == Qt::LeftArrow ) + return QSize( sliderWidth, 16 ); else - return QSize(16, this->sliderWidth); + return QSize( 16, sliderWidth ); } /// \brief Return the color of a slider. /// \param index The index of the slider whose color should be returned. /// \return The current color of the slider. -const QColor LevelSlider::color(int index) const { - if (index < 0 || index >= this->slider.count()) return Qt::black; +const QColor LevelSlider::color( int index ) const { + if ( index < 0 || index >= slider.count() ) + return Qt::black; - return this->slider[index]->color; + return slider[ index ]->color; } /// \brief Set the color of the slider. /// \param index The index of the slider whose color should be set. /// \param color The new color for the slider. /// \return The index of the slider, -1 on error. -void LevelSlider::setColor(unsigned index, QColor color) { - if (index >= (unsigned)this->slider.count()) return; +void LevelSlider::setColor( unsigned index, QColor color ) { + if ( int( index ) >= slider.count() ) + return; - this->slider[(int)index]->color = color; - this->repaint(); + slider[ int( index ) ]->color = color; + repaint(); } /// \brief Return the text shown beside a slider. /// \param index The index of the slider whose text should be returned. /// \return The current text of the slider. -const QString LevelSlider::text(int index) const { - if (index < 0 || index >= this->slider.count()) return QString(); +const QString LevelSlider::text( int index ) const { + if ( index < 0 || index >= slider.count() ) + return QString(); - return this->slider[index]->text; + return slider[ index ]->text; } /// \brief Set the text for a slider. /// \param index The index of the slider whose text should be set. /// \param text The text shown next to the slider. /// \return The index of the slider, -1 on error. -int LevelSlider::setText(int index, const QString &text) { - if (index < 0 || index >= this->slider.count()) return -1; +int LevelSlider::setText( int index, const QString &text ) { + if ( index < 0 || index >= slider.count() ) + return -1; - this->slider[index]->text = text; - this->calculateWidth(); + slider[ index ]->text = text; + calculateWidth(); return index; } @@ -158,199 +164,217 @@ int LevelSlider::setText(int index, const QString &text) { /// \brief Return the visibility of a slider. /// \param index The index of the slider whose visibility should be returned. /// \return true if the slider is visible, false if it's hidden. -bool LevelSlider::visible(int index) const { - if (index < 0 || index >= this->slider.count()) return false; +bool LevelSlider::visible( int index ) const { + if ( index < 0 || index >= slider.count() ) + return false; - return this->slider[index]->visible; + return slider[ index ]->visible; } /// \brief Set the visibility of a slider. /// \param index The index of the slider whose visibility should be set. /// \param visible true to show the slider, false to hide it. /// \return The index of the slider, -1 on error. -void LevelSlider::setIndexVisible(unsigned index, bool visible) { - if (index >= (unsigned)this->slider.count()) return; +void LevelSlider::setIndexVisible( unsigned index, bool visible ) { + if ( int( index ) >= slider.count() ) + return; - this->slider[(int)index]->visible = visible; - this->repaint(); + slider[ int( index ) ]->visible = visible; + repaint(); } /// \brief Return the minimal value of the sliders. /// \return The value a slider has at the bottommost/leftmost position. -double LevelSlider::minimum(int index) const { - if (index < 0 || index >= this->slider.count()) return -1; +double LevelSlider::minimum( int index ) const { + if ( index < 0 || index >= slider.count() ) + return -1; - return this->slider[index]->minimum; + return slider[ index ]->minimum; } /// \brief Return the maximal value of the sliders. /// \return The value a slider has at the topmost/rightmost position. -double LevelSlider::maximum(int index) const { - if (index < 0 || index >= this->slider.count()) return -1; +double LevelSlider::maximum( int index ) const { + if ( index < 0 || index >= slider.count() ) + return -1; - return this->slider[index]->maximum; + return slider[ index ]->maximum; } -/// \brief Set the maximal value of the sliders. +/// \brief Set the min-max values of the sliders, correct the value if changed. /// \param index The index of the slider whose limits should be set. /// \param minimum The value a slider has at the bottommost/leftmost position. /// \param maximum The value a slider has at the topmost/rightmost position. -/// \return -1 on error, fixValue result on success. -void LevelSlider::setLimits(int index, double minimum, double maximum) { - if (index < 0 || index >= this->slider.count()) return; - - this->slider[index]->minimum = minimum; - this->slider[index]->maximum = maximum; - this->fixValue(index); - this->calculateRect(index); - this->repaint(); +void LevelSlider::setLimits( int index, double minimum, double maximum ) { + if ( index < 0 || index >= slider.count() ) + return; + double lastValue = slider[ index ]->value; + slider[ index ]->minimum = minimum; + slider[ index ]->maximum = maximum; + fixValue( index ); + calculateRect( index ); + repaint(); +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if ( lastValue != slider[ index ]->value ) + emit valueChanged( index, slider[ index ]->value ); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } /// \brief Return the step width of the sliders. /// \param index The index of the slider whose step width should be returned. /// \return The distance between the selectable slider positions. -double LevelSlider::step(int index) const { - if (index < 0 || index >= this->slider.count()) return -1; +double LevelSlider::step( int index ) const { + if ( index < 0 || index >= slider.count() ) + return -1; - return this->slider[index]->step; + return slider[ index ]->step; } /// \brief Set the step width of the sliders. /// \param index The index of the slider whose step width should be set. /// \param step The distance between the selectable slider positions. /// \return The new step width. -double LevelSlider::setStep(int index, double step) { - if (index < 0 || index >= this->slider.count()) return -1; +double LevelSlider::setStep( int index, double step ) { + if ( index < 0 || index >= slider.count() ) + return -1; - if (step > 0) this->slider[index]->step = step; + if ( step > 0 ) + slider[ index ]->step = step; - return this->slider[index]->step; + return slider[ index ]->step; } /// \brief Return the current position of a slider. /// \param index The index of the slider whose value should be returned. /// \return The value of the slider. -double LevelSlider::value(int index) const { - if (index < 0 || index >= this->slider.count()) return -1; +double LevelSlider::value( int index ) const { + if ( index < 0 || index >= slider.count() ) + return -1; - return this->slider[index]->value; + return slider[ index ]->value; } /// \brief Set the current position of a slider. /// \param index The index of the slider whose value should be set. /// \param value The new value of the slider. /// \return The new value of the slider. -void LevelSlider::setValue(int index, double value) { - if (index < 0 || index >= this->slider.count()) return; +void LevelSlider::setValue( int index, double value ) { + if ( index < 0 || index >= slider.count() ) + return; // Apply new value - this->slider[index]->value = value; - this->fixValue(index); + slider[ index ]->value = value; + fixValue( index ); - this->calculateRect(index); - this->repaint(); + calculateRect( index ); + repaint(); - if (this->pressedSlider < 0) emit valueChanged(index, value); + if ( pressedSlider < 0 ) + emit valueChanged( index, value ); } /// \brief Return the direction of the sliders. /// \return The side on which the sliders are shown. -Qt::ArrowType LevelSlider::direction() const { return this->_direction; } +Qt::ArrowType LevelSlider::direction() const { return _direction; } /// \brief Set the direction of the sliders. /// \param direction The side on which the sliders are shown. /// \return The index of the direction, -1 on error. -int LevelSlider::setDirection(Qt::ArrowType direction) { - if (direction < Qt::UpArrow || direction > Qt::RightArrow) return -1; +int LevelSlider::setDirection( Qt::ArrowType direction ) { + if ( direction < Qt::UpArrow || direction > Qt::RightArrow ) + return -1; - this->_direction = direction; + _direction = direction; - if (this->_direction == Qt::RightArrow || this->_direction == Qt::LeftArrow) { - this->_preMargin = this->fontMetrics().lineSpacing(); - this->_postMargin = 3; + if ( _direction == Qt::RightArrow || _direction == Qt::LeftArrow ) { + _preMargin = fontMetrics().lineSpacing(); + _postMargin = ( needleWidth ); } else { - this->_preMargin = this->fontMetrics().averageCharWidth() * 3; - this->_postMargin = 3; + _preMargin = fontMetrics().averageCharWidth() * 5; + _postMargin = (needleWidth)*2; } - return this->_direction; + return _direction; } /// \brief Move the slider if it's pressed. /// \param event The mouse event that should be handled. -void LevelSlider::mouseMoveEvent(QMouseEvent *event) { - if (this->pressedSlider < 0) { +void LevelSlider::mouseMoveEvent( QMouseEvent *event ) { + if ( pressedSlider < 0 ) { event->ignore(); return; } // Get new value double value; - if (this->_direction == Qt::RightArrow || this->_direction == Qt::LeftArrow) - value = this->slider[pressedSlider]->maximum - - (this->slider[pressedSlider]->maximum - this->slider[pressedSlider]->minimum) * - ((double)event->y() - this->_preMargin + 0.5) / - (this->height() - this->_preMargin - this->_postMargin - 1); + if ( _direction == Qt::RightArrow || _direction == Qt::LeftArrow ) + value = slider[ pressedSlider ]->maximum - ( slider[ pressedSlider ]->maximum - slider[ pressedSlider ]->minimum ) * + ( double( event->y() ) - _preMargin + 0.5 ) / + ( height() - _preMargin - _postMargin - 1 ); else - value = this->slider[pressedSlider]->minimum + - (this->slider[pressedSlider]->maximum - this->slider[pressedSlider]->minimum) * - ((double)event->x() - this->_preMargin + 0.5) / - (this->width() - this->_preMargin - this->_postMargin - 1); + value = slider[ pressedSlider ]->minimum + ( slider[ pressedSlider ]->maximum - slider[ pressedSlider ]->minimum ) * + ( double( event->x() ) - _preMargin + 0.5 ) / + ( width() - _preMargin - _postMargin - 1 ); // Move the slider - if (event->modifiers() & Qt::AltModifier) + if ( event->modifiers() & Qt::AltModifier ) // Alt allows every position - this->setValue(this->pressedSlider, value); + setValue( pressedSlider, value ); else // Set to nearest possible position - this->setValue(this->pressedSlider, - floor(value / this->slider[pressedSlider]->step + 0.5) * this->slider[pressedSlider]->step); + setValue( pressedSlider, floor( value / slider[ pressedSlider ]->step + 0.5 ) * slider[ pressedSlider ]->step ); - emit valueChanged(pressedSlider, slider[pressedSlider]->value); + emit valueChanged( pressedSlider, slider[ pressedSlider ]->value, true ); event->accept(); } /// \brief Prepare slider for movement if the left mouse button is pressed. /// \param event The mouse event that should be handled. -void LevelSlider::mousePressEvent(QMouseEvent *event) { - if (!(event->button() & Qt::LeftButton)) { +void LevelSlider::mousePressEvent( QMouseEvent *event ) { + if ( !( event->button() & Qt::LeftButton ) ) { event->ignore(); return; } - this->pressedSlider = -1; - for (int sliderId = 0; sliderId < this->slider.count(); ++sliderId) { - if (this->slider[sliderId]->visible && this->slider[sliderId]->rect.contains(event->pos())) { - this->pressedSlider = sliderId; + pressedSlider = -1; + for ( int sliderId = 0; sliderId < slider.count(); ++sliderId ) { + if ( slider[ sliderId ]->visible && slider[ sliderId ]->rect.contains( event->pos() ) ) { + pressedSlider = sliderId; break; } } + if ( pressedSlider >= 0 ) + emit valueChanged( pressedSlider, slider[ pressedSlider ]->value, true ); // Accept event if a slider was pressed - event->setAccepted(this->pressedSlider >= 0); + event->setAccepted( pressedSlider >= 0 ); } /// \brief Movement is done if the left mouse button is released. /// \param event The mouse event that should be handled. -void LevelSlider::mouseReleaseEvent(QMouseEvent *event) { - if (!(event->button() & Qt::LeftButton) || this->pressedSlider == -1) { +void LevelSlider::mouseReleaseEvent( QMouseEvent *event ) { + if ( !( event->button() & Qt::LeftButton ) || pressedSlider == -1 ) { event->ignore(); return; } - - emit valueChanged(this->pressedSlider, this->slider[this->pressedSlider]->value); - this->pressedSlider = -1; + emit valueChanged( pressedSlider, slider[ pressedSlider ]->value, false ); + pressedSlider = -1; event->accept(); } /// \brief Paint the widget. /// \param event The paint event that should be handled. -void LevelSlider::paintEvent(QPaintEvent *event) { - QPainter painter(this); +void LevelSlider::paintEvent( QPaintEvent *event ) { + QPainter painter( this ); Qt::Alignment alignment; - switch (this->_direction) { + switch ( _direction ) { case Qt::LeftArrow: alignment = Qt::AlignLeft | Qt::AlignBottom; break; @@ -364,81 +388,79 @@ void LevelSlider::paintEvent(QPaintEvent *event) { alignment = Qt::AlignRight | Qt::AlignBottom; } - QList::iterator slider = this->slider.end(); - while (slider != this->slider.begin()) { - --slider; + QList< LevelSliderParameters * >::iterator sliderIt = slider.end(); + while ( sliderIt != slider.begin() ) { + --sliderIt; - if (!(*slider)->visible) continue; + if ( !( *sliderIt )->visible ) + continue; - painter.setPen((*slider)->color); + painter.setPen( ( *sliderIt )->color ); - if ((*slider)->text.isEmpty()) { - QVector needlePoints; - QRect& sRect = (*slider)->rect; - const int W = this->sliderWidth; + if ( ( *sliderIt )->text.isEmpty() ) { + QVector< QPoint > needlePoints; + QRect &needleRect = ( *sliderIt )->rect; + const int peak = 1; // distance from slider to the tip of the needle + const int shoulder = peak + needleWidth / 2; // distance from slider to the straight part of the needle - switch (this->_direction) { + switch ( _direction ) { case Qt::LeftArrow: - needlePoints << QPoint(sRect.left() + 4, sRect.top() ) - << QPoint(sRect.left() + 1, sRect.top() + 3) - << QPoint(sRect.left() + 4, sRect.top() + 6) - << QPoint(sRect.left() + W, sRect.top() + 6) - << QPoint(sRect.left() + W, sRect.top() ); + needlePoints << QPoint( needleRect.left() + shoulder, needleRect.top() ) + << QPoint( needleRect.left() + peak, needleRect.top() + needleWidth / 2 ) + << QPoint( needleRect.left() + shoulder, needleRect.bottom() ) + << QPoint( needleRect.right(), needleRect.bottom() ) << QPoint( needleRect.right(), needleRect.top() ); break; case Qt::UpArrow: - needlePoints << QPoint(sRect.left(), sRect.top() + 4) - << QPoint(sRect.left() + 3, sRect.top() + 1) - << QPoint(sRect.left() + 6, sRect.top() + 4) - << QPoint(sRect.left() + 6, sRect.top() + W) - << QPoint(sRect.left(), sRect.top() + W); + needlePoints << QPoint( needleRect.left(), needleRect.top() + shoulder ) + << QPoint( needleRect.left() + needleWidth / 2, needleRect.top() + peak ) + << QPoint( needleRect.right(), needleRect.top() + shoulder ) + << QPoint( needleRect.right(), needleRect.bottom() ) + << QPoint( needleRect.left(), needleRect.bottom() ); break; case Qt::DownArrow: - needlePoints << QPoint(sRect.left(), sRect.top() + W - 5) - << QPoint(sRect.left() + 3, sRect.top() + W - 2) - << QPoint(sRect.left() + 6, sRect.top() + W - 5) - << QPoint(sRect.left() + 6, sRect.top() ) - << QPoint(sRect.left(), sRect.top() ); + needlePoints << QPoint( needleRect.left(), needleRect.bottom() - shoulder ) + << QPoint( needleRect.left() + needleWidth / 2, needleRect.bottom() - peak ) + << QPoint( needleRect.right(), needleRect.bottom() - shoulder ) + << QPoint( needleRect.right(), needleRect.top() ) << QPoint( needleRect.left(), needleRect.top() ); break; case Qt::RightArrow: - needlePoints << QPoint(sRect.left() + W - 5, sRect.top() ) - << QPoint(sRect.left() + W - 2, sRect.top() + 3) - << QPoint(sRect.left() + W - 5, sRect.top() + 6) - << QPoint(sRect.left(), sRect.top() + 6) - << QPoint(sRect.left(), sRect.top() ); + needlePoints << QPoint( needleRect.right() - shoulder, needleRect.top() ) + << QPoint( needleRect.right() - peak, needleRect.top() + needleWidth / 2 ) + << QPoint( needleRect.right() - shoulder, needleRect.bottom() ) + << QPoint( needleRect.left(), needleRect.bottom() ) << QPoint( needleRect.left(), needleRect.top() ); break; default: break; } - painter.setBrush(QBrush((*slider)->color, isEnabled() ? Qt::SolidPattern : Qt::NoBrush)); - painter.drawPolygon(QPolygon(needlePoints)); - painter.setBrush(Qt::NoBrush); + painter.setBrush( QBrush( ( *sliderIt )->color, isEnabled() ? Qt::SolidPattern : Qt::NoBrush ) ); + painter.drawPolygon( QPolygon( needlePoints ) ); + painter.setBrush( Qt::NoBrush ); } else { // Get rect for text and draw needle - QRect textRect = (*slider)->rect; - if (this->_direction == Qt::UpArrow || this->_direction == Qt::DownArrow) { - textRect.setRight(textRect.right() - 1); - if (this->_direction == Qt::UpArrow) { - textRect.setTop(textRect.top() + 1); - painter.drawLine((*slider)->rect.right(), 0, (*slider)->rect.right(), 7); + QRect textRect = ( *sliderIt )->rect; + if ( _direction == Qt::UpArrow || _direction == Qt::DownArrow ) { + textRect.setRight( textRect.right() - 1 ); + if ( _direction == Qt::UpArrow ) { + textRect.setTop( textRect.top() + 1 ); + painter.drawLine( ( *sliderIt )->rect.right(), 0, ( *sliderIt )->rect.right(), 7 ); } else { - textRect.setBottom(textRect.bottom() - 1); - painter.drawLine((*slider)->rect.right(), this->sliderWidth - 8, (*slider)->rect.right(), - this->sliderWidth - 1); + textRect.setBottom( textRect.bottom() - 1 ); + painter.drawLine( ( *sliderIt )->rect.right(), sliderWidth - 8, ( *sliderIt )->rect.right(), sliderWidth - 1 ); } } else { - textRect.setBottom(textRect.bottom() - 1); - if (this->_direction == Qt::LeftArrow) { - textRect.setLeft(textRect.left() + 1); - painter.drawLine(0, (*slider)->rect.bottom(), 7, (*slider)->rect.bottom()); + textRect.setBottom( textRect.bottom() - 1 ); + if ( _direction == Qt::LeftArrow ) { + textRect.setLeft( textRect.left() + 1 ); + painter.drawLine( 0, ( *sliderIt )->rect.bottom(), 7, ( *sliderIt )->rect.bottom() ); } else { - textRect.setRight(textRect.right() - 1); - painter.drawLine(this->sliderWidth - 8, (*slider)->rect.bottom(), this->sliderWidth - 1, - (*slider)->rect.bottom()); + textRect.setRight( textRect.right() - 1 ); + painter.drawLine( sliderWidth - 8, ( *sliderIt )->rect.bottom(), sliderWidth - 1, + ( *sliderIt )->rect.bottom() ); } } // Draw text - painter.drawText(textRect, alignment, (*slider)->text); + painter.drawText( textRect, int( alignment ), ( *sliderIt )->text ); } } @@ -447,125 +469,119 @@ void LevelSlider::paintEvent(QPaintEvent *event) { /// \brief Resize the widget and adapt the slider positions. /// \param event The resize event that should be handled. -void LevelSlider::resizeEvent(QResizeEvent *event) { - Q_UNUSED(event); +void LevelSlider::resizeEvent( QResizeEvent *event ) { + Q_UNUSED( event ); - for (int sliderId = 0; sliderId < this->slider.count(); ++sliderId) this->calculateRect(sliderId); + for ( int sliderId = 0; sliderId < slider.count(); ++sliderId ) + calculateRect( sliderId ); - this->repaint(); + repaint(); } /// \brief Calculate the drawing area for the slider for it's current value. /// \param sliderId The id of the slider whose rect should be calculated. /// \return The calculated rect. -QRect LevelSlider::calculateRect(int sliderId) { +QRect LevelSlider::calculateRect( int sliderId ) { // Is it a vertical slider? - if (this->_direction == Qt::RightArrow || this->_direction == Qt::LeftArrow) { + if ( _direction == Qt::RightArrow || _direction == Qt::LeftArrow ) { // Is it a triangular needle? - if (this->slider[sliderId]->text.isEmpty()) { - this->slider[sliderId]->rect = - QRect(0, // Start at the left side - // The needle should be center-aligned, 0.5 pixel offset for - // exact pixelization - (long)((double)(this->height() - this->_preMargin - this->_postMargin - 1) * - (this->slider[sliderId]->maximum - this->slider[sliderId]->value) / - (this->slider[sliderId]->maximum - this->slider[sliderId]->minimum) + - 0.5f) + - this->_preMargin - 3, - this->sliderWidth, // Fill the whole width - 7 // The needle is 7 px wide - ); + if ( slider[ sliderId ]->text.isEmpty() ) { + slider[ sliderId ]->rect = QRect( + 0, // Start at the left side + // The needle should be center-aligned, 0.5 pixel offset for + // exact pixelization + int( ( height() - _preMargin - _postMargin - 1 ) * ( slider[ sliderId ]->maximum - slider[ sliderId ]->value ) / + ( slider[ sliderId ]->maximum - slider[ sliderId ]->minimum ) + + 0.5 ) + + _preMargin - ( needleWidth / 2 ), + sliderWidth, // Fill the whole width + needleWidth // one needle width high + ); } // Or a thin needle with text? else { - this->slider[sliderId]->rect = - QRect(0, // Start at the left side - // The needle is at the bottom, the text above it, 0.5 pixel - // offset for exact pixelization - (long)((double)(this->height() - this->_preMargin - this->_postMargin - 1) * - (this->slider[sliderId]->maximum - this->slider[sliderId]->value) / - (this->slider[sliderId]->maximum - this->slider[sliderId]->minimum) + - 0.5f), - this->sliderWidth, // Fill the whole width - this->preMargin() + 1 // Use the full margin - ); + slider[ sliderId ]->rect = QRect( + 0, // Start at the left side + // The needle is at the bottom, the text above it, 0.5 pixel + // offset for exact pixelization + int( ( height() - _preMargin - _postMargin - 1 ) * ( slider[ sliderId ]->maximum - slider[ sliderId ]->value ) / + ( slider[ sliderId ]->maximum - slider[ sliderId ]->minimum ) + + 0.5 ), + sliderWidth, // Fill the whole width + preMargin() + 1 // Use the full margin + ); } } // Or a horizontal slider? else { // Is it a triangular needle? - if (this->slider[sliderId]->text.isEmpty()) { - this->slider[sliderId]->rect = QRect( + if ( slider[ sliderId ]->text.isEmpty() ) { + slider[ sliderId ]->rect = QRect( // The needle should be center-aligned, 0.5 pixel offset for exact // pixelization - (long)((double)(this->width() - this->_preMargin - this->_postMargin - 1) * - (this->slider[sliderId]->value - this->slider[sliderId]->minimum) / - (this->slider[sliderId]->maximum - this->slider[sliderId]->minimum) + - 0.5f) + - this->_preMargin - 3, - 0, // Start at the top - 7, // The needle is 7 px wide - this->sliderWidth // Fill the whole height - ); + int( ( width() - _preMargin - _postMargin - 1 ) * ( slider[ sliderId ]->value - slider[ sliderId ]->minimum ) / + ( slider[ sliderId ]->maximum - slider[ sliderId ]->minimum ) + + 0.5 ) + + _preMargin - ( needleWidth / 2 ), + 0, // Start at the top + needleWidth, + sliderWidth // As high as the slider + ); } // Or a thin needle with text? else { - int sliderLength = this->fontMetrics().size(0, this->slider[sliderId]->text).width() + 2; - this->slider[sliderId]->rect = QRect( + int sliderLength = fontMetrics().size( 0, slider[ sliderId ]->text ).width() + 2; + slider[ sliderId ]->rect = QRect( // The needle is at the right side, the text before it, 0.5 pixel // offset for exact pixelization - (long)((double)(this->width() - this->_preMargin - this->_postMargin - 1) * - (this->slider[sliderId]->value - this->slider[sliderId]->minimum) / - (this->slider[sliderId]->maximum - this->slider[sliderId]->minimum) + - 0.5f) + - this->_preMargin - sliderLength + 1, - 0, // Start at the top - sliderLength, // The width depends on the text - this->sliderWidth // Fill the whole height - ); + int( ( width() - _preMargin - _postMargin - 1 ) * ( slider[ sliderId ]->value - slider[ sliderId ]->minimum ) / + ( slider[ sliderId ]->maximum - slider[ sliderId ]->minimum ) + + 0.5 ) + + _preMargin - sliderLength + 1, + 0, // Start at the top + sliderLength, // The width depends on the text + sliderWidth // Fill the whole height + ); } } - return this->slider[sliderId]->rect; + return slider[ sliderId ]->rect; } /// \brief Search for the widest slider element. /// \return The calculated width of the slider. int LevelSlider::calculateWidth() { - // At least 12 px for the needles - this->sliderWidth = 12; - // Is it a vertical slider? - if (this->_direction == Qt::RightArrow || this->_direction == Qt::LeftArrow) { - for (QList::iterator slider = this->slider.begin(); slider != this->slider.end(); - ++slider) { - int sliderWidth = this->fontMetrics().size(0, (*slider)->text).width(); - if (sliderWidth > this->sliderWidth) this->sliderWidth = sliderWidth; + if ( _direction == Qt::RightArrow || _direction == Qt::LeftArrow ) { + for ( QList< LevelSliderParameters * >::iterator sliderIt = slider.begin(); sliderIt != slider.end(); ++sliderIt ) { + int newSliderWidth = fontMetrics().size( 0, ( *sliderIt )->text ).width(); + if ( newSliderWidth > sliderWidth ) + sliderWidth = newSliderWidth; } } // Or a horizontal slider? else { - for (QList::iterator slider = this->slider.begin(); slider != this->slider.end(); - ++slider) { - int sliderWidth = this->fontMetrics().size(0, (*slider)->text).height(); - if (sliderWidth > this->sliderWidth) this->sliderWidth = sliderWidth; + for ( QList< LevelSliderParameters * >::iterator sliderIt = slider.begin(); sliderIt != slider.end(); ++sliderIt ) { + int newSliderWidth = fontMetrics().size( 0, ( *sliderIt )->text ).height(); + if ( newSliderWidth > sliderWidth ) + sliderWidth = newSliderWidth; } } - return this->sliderWidth; + return sliderWidth; } /// \brief Fix the value if it's outside the limits. /// \param index The index of the slider who should be fixed. -/// \return 0 when ok, -1 on error, 1 when increased and 2 when decreased. -void LevelSlider::fixValue(int index) { - if (index < 0 || index >= this->slider.count()) return; - - double lowest = qMin(this->slider[index]->minimum, this->slider[index]->maximum); - double highest = qMax(this->slider[index]->minimum, this->slider[index]->maximum); - if (this->slider[index]->value < lowest) { - this->slider[index]->value = lowest; - } else if (this->slider[index]->value > highest) { - this->slider[index]->value = highest; +void LevelSlider::fixValue( int index ) { + if ( index < 0 || index >= slider.count() ) + return; + + double lowest = qMin( slider[ index ]->minimum, slider[ index ]->maximum ); + double highest = qMax( slider[ index ]->minimum, slider[ index ]->maximum ); + if ( slider[ index ]->value < lowest ) { + slider[ index ]->value = lowest; + } else if ( slider[ index ]->value > highest ) { + slider[ index ]->value = highest; } } diff --git a/openhantek/src/widgets/levelslider.h b/openhantek/src/widgets/levelslider.h index 2c6e1d47..6f4ead08 100644 --- a/openhantek/src/widgets/levelslider.h +++ b/openhantek/src/widgets/levelslider.h @@ -29,7 +29,7 @@ class LevelSlider : public QWidget { Q_OBJECT public: - LevelSlider(Qt::ArrowType direction = Qt::RightArrow, QWidget *parent = 0); + LevelSlider( Qt::ArrowType direction = Qt::RightArrow, QWidget *parent = nullptr ); ~LevelSlider(); QSize sizeHint() const; @@ -37,51 +37,52 @@ class LevelSlider : public QWidget { int preMargin() const; int postMargin() const; - int addSlider(int index = -1); - int addSlider(const QString &text, int index = -1); - int removeSlider(int index = -1); + int addSlider( int index = -1 ); + int addSlider( const QString &text, int index = -1 ); + int removeSlider( int index = -1 ); // Parameters for a specific slider - const QColor color(int index) const; - void setColor(unsigned index, QColor color); - const QString text(int index) const; - int setText(int index, const QString &text); - bool visible(int index) const; - void setIndexVisible(unsigned index, bool visible); - - double minimum(int index) const; - double maximum(int index) const; - void setLimits(int index, double minimum, double maximum); - double step(int index) const; - double setStep(int index, double step); - double value(int index) const; - void setValue(int index, double value); + const QColor color( int index ) const; + void setColor( unsigned index, QColor color ); + const QString text( int index ) const; + int setText( int index, const QString &text ); + bool visible( int index ) const; + void setIndexVisible( unsigned index, bool visible ); + + double minimum( int index ) const; + double maximum( int index ) const; + void setLimits( int index, double minimum, double maximum ); + double step( int index ) const; + double setStep( int index, double step ); + double value( int index ) const; + void setValue( int index, double value ); // Parameters for all sliders Qt::ArrowType direction() const; - int setDirection(Qt::ArrowType direction); + int setDirection( Qt::ArrowType direction ); protected: - void mouseMoveEvent(QMouseEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent( QMouseEvent *event ); + void mousePressEvent( QMouseEvent *event ); + void mouseReleaseEvent( QMouseEvent *event ); - void paintEvent(QPaintEvent *event); - void resizeEvent(QResizeEvent *event); + void paintEvent( QPaintEvent *event ); + void resizeEvent( QResizeEvent *event ); - QRect calculateRect(int sliderId); + QRect calculateRect( int sliderId ); int calculateWidth(); - void fixValue(int index); + void fixValue( int index ); - QList slider; ///< The parameters for each slider - int pressedSlider; ///< The currently pressed (moved) slider - int sliderWidth; ///< The slider width (dimension orthogonal to the sliding - /// direction) + QList< LevelSliderParameters * > slider; ///< The parameters for each slider + int pressedSlider; ///< The currently pressed (moved) slider + int sliderWidth; ///< The slider width (dimension orthogonal to the sliding + /// direction) + int needleWidth; ///< Width of the needle (parallel to sliding direction) Qt::ArrowType _direction; ///< The direction the sliders point to int _preMargin; ///< The margin before the minimum slider position int _postMargin; ///< The margin after the maximum slider position signals: - void valueChanged(int index, double value); ///< The value of a slider has changed + void valueChanged( int index, double value, bool pressed = false ); ///< The value of a slider has changed }; diff --git a/openhantek/src/widgets/sispinbox.cpp b/openhantek/src/widgets/sispinbox.cpp index db849604..5bacbab1 100644 --- a/openhantek/src/widgets/sispinbox.cpp +++ b/openhantek/src/widgets/sispinbox.cpp @@ -32,15 +32,15 @@ // class OpenHantekMainWindow /// \brief Initializes the SiSpinBox internals. /// \param parent The parent widget. -SiSpinBox::SiSpinBox(QWidget *parent) : QDoubleSpinBox(parent) { this->init(); } +SiSpinBox::SiSpinBox( QWidget *parent ) : QDoubleSpinBox( parent ) { this->init(); } /// \brief Initializes the SiSpinBox, allowing the user to choose the unit. /// \param unit The unit shown for the value in the spin box. /// \param parent The parent widget. -SiSpinBox::SiSpinBox(Unit unit, QWidget *parent) : QDoubleSpinBox(parent) { +SiSpinBox::SiSpinBox( Unit unit, QWidget *parent ) : QDoubleSpinBox( parent ) { this->init(); - this->setUnit(unit); + this->setUnit( unit ); this->setBackground(); } @@ -52,129 +52,136 @@ SiSpinBox::~SiSpinBox() {} /// \param input The content of the text box. /// \param pos The position of the cursor in the text box. /// \return Validity of the current text. -QValidator::State SiSpinBox::validate(QString &input, int &pos) const { - Q_UNUSED(pos); +QValidator::State SiSpinBox::validate( QString &input, int &pos ) const { + Q_UNUSED( pos ); bool ok; - double value = stringToValue(input, this->unit, &ok); + double value = stringToValue( input, this->unit, &ok ); - if (!ok) return QValidator::Invalid; + if ( !ok ) + return QValidator::Invalid; - if (input == this->textFromValue(value)) return QValidator::Acceptable; + if ( input == this->textFromValue( value ) ) + return QValidator::Acceptable; return QValidator::Intermediate; } /// \brief Parse value from input text. /// \param text The content of the text box. /// \return Value in base unit. -double SiSpinBox::valueFromText(const QString &text) const { return stringToValue(text, this->unit); } +double SiSpinBox::valueFromText( const QString &text ) const { return stringToValue( text, this->unit ); } /// \brief Get string representation of value. /// \param val Value in base unit. /// \return String representation containing value and (prefix+)unit. -QString SiSpinBox::textFromValue(double val) const { return valueToString(val, this->unit, -1) + this->unitPostfix; } +QString SiSpinBox::textFromValue( double val ) const { return valueToString( val, this->unit, -1 ) + this->unitPostfix; } /// \brief Fixes the text after the user finished changing it. /// \param input The content of the text box. -void SiSpinBox::fixup(QString &input) const { +void SiSpinBox::fixup( QString &input ) const { bool ok; - double value = stringToValue(input, this->unit, &ok); + double value = stringToValue( input, this->unit, &ok ); - if (!ok) value = this->value(); + if ( !ok ) + value = this->value(); - input = this->textFromValue(value); + input = this->textFromValue( value ); } /// \brief Increase/decrease the values in fixed steps. -/// \param steps The number of steps, positive means increase. -void SiSpinBox::stepBy(int steps) { - double stepsSpan = this->steps.last() / this->steps.first(); - int stepsCount = this->steps.size() - 1; - double value = 0; - - // Skip if we are already at a limit or if steps is null - if (steps == 0 || (steps < 0 && this->value() <= this->minimum()) || - (steps > 0 && this->value() >= this->maximum())) { +/// \param doStep The number of steps, positive means increase. +void SiSpinBox::stepBy( int doStep ) { + // Skip if we are already at a limit or if doStep is null + if ( doStep == 0 || ( doStep < 0 && this->value() <= this->minimum() ) || ( doStep > 0 && this->value() >= this->maximum() ) ) { return; } - - if (!this->steppedTo) { // No step done directly before this one, so we need - // to check where we are - // Get how often the steps have to be fully ran through - int stepsFully = (this->mode == 0) ? (int)floor(log(this->value() / this->steps.first()) / log(stepsSpan)) : 0; - // And now the remaining multiple - double stepMultiple = - (this->mode == 0) ? this->value() / pow(stepsSpan, stepsFully) : this->value() / this->minimum(); - // Now get the neighbours of the current value from our steps list - int remainingSteps = 0; - for (; remainingSteps <= stepsCount; ++remainingSteps) { - if (this->steps[remainingSteps] > stepMultiple) break; + int stepsCount = this->steps.size() - 1; + if ( 0 == mode ) { // this is a regular 1/2/5.. spinbox + double stepsSpan = this->steps.last() / this->steps.first(); + double value = 0; + if ( !this->steppedTo ) { // No step done directly before this one, + // ... so we need to check where we are + // Get how often the steps have to be fully ran through + int stepsFully = int( floor( log( this->value() / this->steps.first() ) / log( stepsSpan ) ) ); + // And now the remaining multiple + double stepMultiple = this->value() / pow( stepsSpan, stepsFully ); + // Now get the neighbours of the current value from our steps list + int remainingSteps = 0; + for ( ; remainingSteps <= stepsCount; ++remainingSteps ) { + if ( this->steps[ remainingSteps ] > stepMultiple ) + break; + } + if ( remainingSteps > 0 ) // Shouldn't happen, but double may have rounding errors + --remainingSteps; + this->stepId = stepsFully * stepsCount + remainingSteps; + // We need to do one step less down if we are inbetween two of them since + // our step is lower than the value + if ( doStep < 0 && this->steps[ remainingSteps ] < stepMultiple ) + ++this->stepId; } - if (remainingSteps > 0) // Shouldn't happen, but double may have rounding errors - --remainingSteps; - this->stepId = stepsFully * stepsCount + remainingSteps; - // We need to do one step less down if we are inbetween two of them since - // our step is lower than the value - if (steps < 0 && this->steps[remainingSteps] < stepMultiple) ++this->stepId; - } - - int subStep = steps / abs(steps); - for (int i = 0; i != steps; i += subStep) { - this->stepId += subStep; - if (!this->mode) { + int subStep = doStep / abs( doStep ); + for ( int i = 0; i != doStep; i += subStep ) { + this->stepId += subStep; int stepsId = this->stepId % stepsCount; - if (stepsId < 0) stepsId += stepsCount; - value = pow(stepsSpan, floor((double)this->stepId / stepsCount)) * this->steps[stepsId]; - } else { - stepId = std::min(std::max(stepId, 0), stepsCount); - value = this->steps[stepId]; + if ( stepsId < 0 ) + stepsId += stepsCount; + value = pow( stepsSpan, floor( double( this->stepId ) / stepsCount ) ) * this->steps[ stepsId ]; + if ( value <= this->minimum() || value >= this->maximum() ) + break; } - if (value <= this->minimum() || value >= this->maximum()) break; + this->setValue( value ); + this->steppedTo = true; + } else { // irregular spinbox, e.g. sample rate ..10/12/15/24/30.., check always + stepId = 0; + for ( auto it = steps.cbegin(); it != steps.end(); ++it, ++stepId ) + if ( abs( this->value() - *it ) < 1e-12 ) // found + break; + // apply the requested up or down step(s) with bound check + stepId = qBound( 0, stepId + doStep, stepsCount ); + setValue( steps[ stepId ] ); } - this->setValue(value); - value = this->value(); - this->steppedTo = true; } /// \brief Set the unit for this spin box. -/// \param unit The unit shown for the value in the spin box. +/// \param newUnit The unit shown for the value in the spin box. /// \return true on success, false on invalid unit. -bool SiSpinBox::setUnit(Unit unit) { - if ((unsigned int)unit >= UNIT_COUNT) return false; +bool SiSpinBox::setUnit( Unit newUnit ) { + if ( newUnit >= UNIT_COUNT ) + return false; - this->unit = unit; + this->unit = newUnit; return true; } /// \brief Set the unit postfix for this spin box. /// \param postfix the string shown after the unit in the spin box. -void SiSpinBox::setUnitPostfix(const QString &postfix) { this->unitPostfix = postfix; } +void SiSpinBox::setUnitPostfix( const QString &postfix ) { this->unitPostfix = postfix; } /// \brief Set the steps the spin box will take. -/// \param steps The steps, will be extended with the ratio from the start after +/// \param newSteps The steps, will be extended with the ratio from the start after /// the last element. -void SiSpinBox::setSteps(const QList &steps) { this->steps = steps; } +void SiSpinBox::setSteps( const QList< double > &newSteps ) { this->steps = newSteps; } /// \brief Set the mode. /// \param mode The mode, the value 0 will have fixed interval, otherwise the /// value will have interval within steps itself. -void SiSpinBox::setMode(const int mode) { this->mode = mode; } +void SiSpinBox::setMode( const int newMode ) { this->mode = newMode; } /// \brief Generic initializations. void SiSpinBox::init() { - setMinimum(1e-12); - setMaximum(1e12); - setValue(1.0); - setDecimals(DBL_MAX_10_EXP + DBL_DIG); // Disable automatic rounding - setFocusPolicy(Qt::NoFocus); + setMinimum( 1e-12 ); + setMaximum( 1e12 ); + setValue( 1.0 ); + setDecimals( DBL_MAX_10_EXP + DBL_DIG ); // Disable automatic rounding + setFocusPolicy( Qt::NoFocus ); steps << 1.0 << 2.0 << 5.0 << 10.0; steppedTo = false; stepId = 0; mode = 0; - connect(this, static_cast(&QDoubleSpinBox::valueChanged), - this, &SiSpinBox::resetSteppedTo); + connect( this, static_cast< void ( QDoubleSpinBox::* )( double ) >( &QDoubleSpinBox::valueChanged ), this, + &SiSpinBox::resetSteppedTo ); } /// \brief Resets the ::steppedTo flag after the value has been changed. @@ -183,6 +190,6 @@ void SiSpinBox::resetSteppedTo() { this->steppedTo = false; } // fix Dark mode background introduced with MacOS 10.24 (mojave) void SiSpinBox::setBackground() { QPalette palette; - QColor background = palette.color(QPalette::Window); - this->setStyleSheet("background-color: " + background.name()); + QColor background = palette.color( QPalette::Window ); + this->setStyleSheet( "background-color: " + background.name() ); } diff --git a/openhantek/src/widgets/sispinbox.h b/openhantek/src/widgets/sispinbox.h index cd098ba6..a37fdd51 100644 --- a/openhantek/src/widgets/sispinbox.h +++ b/openhantek/src/widgets/sispinbox.h @@ -15,28 +15,28 @@ class SiSpinBox : public QDoubleSpinBox { Q_OBJECT public: - explicit SiSpinBox(QWidget *parent = 0); - SiSpinBox(Unit unit, QWidget *parent = 0); + explicit SiSpinBox( QWidget *parent = nullptr ); + SiSpinBox( Unit unit, QWidget *parent = nullptr ); ~SiSpinBox(); - QValidator::State validate(QString &input, int &pos) const; - double valueFromText(const QString &text) const; - QString textFromValue(double val) const; - void fixup(QString &input) const; - void stepBy(int steps); - bool setUnit(Unit unit); - void setUnitPostfix(const QString &postfix); - void setSteps(const QList &steps); - void setMode(const int mode); + QValidator::State validate( QString &input, int &pos ) const; + double valueFromText( const QString &text ) const; + QString textFromValue( double val ) const; + void fixup( QString &input ) const; + void stepBy( int steps ); + bool setUnit( Unit unit ); + void setUnitPostfix( const QString &postfix ); + void setSteps( const QList< double > &steps ); + void setMode( const int mode ); private: void init(); void setBackground(); - Unit unit; ///< The SI unit used for this spin box - QString unitPostfix; ///< Shown after the unit - QList steps; ///< The steps, begins from start after last element - int mode; ///< The mode, fixed or constant + Unit unit; ///< The SI unit used for this spin box + QString unitPostfix; ///< Shown after the unit + QList< double > steps; ///< The steps, begins from start after last element + int mode; ///< The mode, fixed or constant bool steppedTo; ///< true, if the current value was reached using stepBy int stepId; ///< The index of the last step reached using stepBy diff --git a/openhantek/translations/CMakeLists.txt b/openhantek/translations/CMakeLists.txt deleted file mode 100644 index 329d6adb..00000000 --- a/openhantek/translations/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1) - -find_package(Qt5LinguistTools QUIET) - -if (NOT Qt5LinguistTools_FOUND) - message(STATUS "Will not build translations! Please install Qt5LinguistTools") - # Copy empty translations.qrc file to not make the build fail because of this. - configure_file("${CMAKE_CURRENT_LIST_DIR}/translations.qrc" "${CMAKE_BINARY_DIR}/translations.qrc" @ONLY) - return() -endif() - -qt5_create_translation(TRANSLATION_BIN_FILES "${CMAKE_CURRENT_LIST_DIR}") - -# We prepare the translations.qrc file and insert all available compiled translation files now. -set(QRC_ITEMS "") -foreach(TRANSLATION_FILE ${TRANSLATION_BIN_FILES}) - get_filename_component(FILENAME "${TRANSLATION_FILE}" NAME) - set(QRC_ITEMS "${QRC_ITEMS}\n${TRANSLATION_FILE}") -endforeach() -configure_file("${CMAKE_CURRENT_LIST_DIR}/translations.qrc" "${CMAKE_BINARY_DIR}/translations.qrc" @ONLY) -QT5_ADD_RESOURCES(TRANSLATION_QRC "${CMAKE_BINARY_DIR}/translations.qrc") diff --git a/openhantek/translations/Translation_HowTo.md b/openhantek/translations/Translation_HowTo.md new file mode 100644 index 00000000..36cfb6ed --- /dev/null +++ b/openhantek/translations/Translation_HowTo.md @@ -0,0 +1,48 @@ +# OpenHantek6022 Translation + +The build system simplifies the localization process by automatic preparation of translations. +All strings in the source code that should be translated must marked with the function `tr()`, instead of +`QLabel("Some text")` use `QLabel(tr("Some text"))`. +All texts marked in this way will be collected automatically during a build into a /translation source/ file. +This file can be translated manually afterwards. The translated texts will be automatically integrated +into the program during the next build and are available to the user depending on his locale. +Changed program text will be detected during the next build and the translation source will be updated accordingly. +Text without translation is displayed in the (English) original. + +If a translation exists for your language, it will be used automatically. + +To use OpenHantek6022 with the original English text, call it with `LANG=C OpenHantek`. + +## Quick HowTo + +### Add a new language + +Go to the translation directory `openhantek/translations`. + +Add your new language to the `set(TS_FILES ...` line of `Translations.cmake`, e.g. `openhantek_fr.ts` for French. +``` +################################### +# +# Add more languages here +# +set(TS_FILES translations/openhantek_de.ts translations/openhantek_fr.ts) +# +################################### + +``` +Go to the `build` directory and call `make -j2` +During the build process all translatable text will be added/updated in all TS_FILES from above. + +### Translate into your language + +Go to the translation directory `openhantek/translations`. +Start translating the original (English) text using linguist, unprocessed strings remain untranslated: + +`linguist openhantek_fr.ts` + + +### Create a binary with new localization + +Go to the `build` directory and call `make -j2` + + diff --git a/openhantek/translations/Translations.cmake b/openhantek/translations/Translations.cmake new file mode 100644 index 00000000..c08ebb52 --- /dev/null +++ b/openhantek/translations/Translations.cmake @@ -0,0 +1,27 @@ +################################### +# +# Add more languages here +# +set(TS_FILES translations/openhantek_de.ts translations/openhantek_es.ts translations/openhantek_fr.ts translations/openhantek_it.ts translations/openhantek_pt.ts translations/openhantek_ru.ts) +# +################################### + + +# Find the QtWidgets library +find_package(Qt5 REQUIRED COMPONENTS LinguistTools) + +# defines files with translatable strings. +set(INPUT ${SRC} ${HEADERS} ${UI}) + +# prepares 'lupdate' to update ts files and also 'lcreate' to build qm files. +qt5_create_translation(QM_FILES ${INPUT} ${TS_FILES}) + +# prepare the translations.qrc file and insert all available compiled translation files now. +set(QRC_ITEMS "") +foreach(QM_FILE ${QM_FILES}) + get_filename_component(FILENAME "${QM_FILE}" NAME) + set(QRC_ITEMS "${QRC_ITEMS}\n${QM_FILE}") +endforeach() +configure_file("${CMAKE_CURRENT_LIST_DIR}/translations.qrc.template" "${CMAKE_BINARY_DIR}/translations.qrc" @ONLY) + +QT5_ADD_RESOURCES(TRANSLATION_QRC "${CMAKE_BINARY_DIR}/translations.qrc") diff --git a/openhantek/translations/openhantek_de.ts b/openhantek/translations/openhantek_de.ts index 62394b1b..b81deba8 100644 --- a/openhantek/translations/openhantek_de.ts +++ b/openhantek/translations/openhantek_de.ts @@ -1,1132 +1,1765 @@ - + + + + + + Couldn't open device: %1 + Konnte Gerät %1 nicht öffnen + + DsoConfigAnalysisPage - Rectangular - Rechteck + Rechteck - Hamming - Hamming + Hamming - Hann - Hann + Hann - Cosine - Cosinus + Cosinus - Lanczos - Lanczos + Lanczos - Bartlett - Bartlett + Bartlett - Triangular - Dreieck + Dreieck - Gauss - Gauss + Gauss - Bartlett-Hann - Bartlett-Hann + Bartlett-Hann - Blackman - Blackman + Blackman - Nuttall - Nuttall + Nuttall - Blackman-Harris - Blackman-Harris + Blackman-Harris - Blackman-Nuttall - Blackman-Nuttall + Blackman-Nuttall - Flat top - Flat Top + Flat Top - - Window function - Fensterfunktion + <b>Window function</b> + Fensterfunktion - - Reference level - Referenzpegel + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Referenzpegel</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV - - - dBm - dBm + dBV + dBV - - Minimum magnitude - Minimaler Pegel + <b>Minimum magnitude</b> + Minimaler Pegel - Spectrum - Spektrum + Spektrum DsoConfigColorsPage - + Axes Achsen - + Background Hintergrund - + Border Rahmen - + Grid Gitternetz - + Markers Marker - + Text Text - + + Export hardcopy images or pdf files with printer colors + Exportiere Hardcopy-Bilder oder Hardcopy-PDF-Files mit Drucker-Farben + + + Export images with screen colors + Exportiere Bilder mit Bildschirm-Farben + + + + Screen and Print Colors + Bildschirm- und Drucker-Farben + + + Screen Bildschirm - + <hr width="100%"/> + <hr width="100%"/> + + + + Print + Drucker + + + + Channel Kanal - + + Spectrum Spektrum - - - Graph - Graph - DsoConfigDialog - + Settings Einstellungen - + &Ok &Ok - + &Apply &Anwenden - + &Cancel &Abbrechen - - Analysis - Analyse + + Spectrum + Spektrum - - Colors - Farben + File + Datei - - Files - Dateien + + Colors + Farben - + Scope Oszilloskop - DsoConfigFilesPage + DsoConfigFilePage - - Save default settings on exit - Standardeinstellung beim Beenden speichern + Export Images with Screen Colors + Exportiere Bilder mit Bildschirm-Farben - - Save default settings now - Standardeinstellungen jetzt speichern + Image width + Bildbreite - - Configuration - Konfiguration + Image height + Bildhöhe - - Image width - Bildbreite + Export images with screen colors + Exportiere Bilder mit Bildschirm-Farben - - Image height - Bildhöhe + Take a screenshot of the display area only + Screenshot nur vom Display-Bereich + + + Export zoomed screen in double height + Exportiere gezoomtes Display in doppleter Höhe - Export - Exportieren + Export + + + Save default settings on exit + Standardeinstellung beim Beenden speichern + + + Save default settings now + Standardeinstellungen jetzt speichern + + + Configuration + Konfiguration DsoConfigScopePage - + Off Aus - + Linear Linear - - Sinc - Sinc + Timebase + Zeitbasis - - Antialiasing - Antialiasing + Set slowest possible timebase +(GUI may become very unresponsible!) + Langsamste mögliche Zeitbasis +(Das Programm kann sehr zäh reagieren!) - - Interpolation - Interpolation + Minimal time between captured frames +(Longer times reduce the CPU load) + Minimale Zeit zwischen Datenblöcken +(Längere Zeiten reduzieren die CPU-Last) - - Digital phosphor depth - Stufen für digitalen Phosphor + Set slowest possible timebase<br/>(<b>GUI may become very unresponsible!</b>) + Langsamste mögliche Zeitbasis<br/>(<b>Das Programm kann sehr zäh reagieren!</b>) - - Graph - Graph + + Set slowest possible timebase + Langsamste mögliche Zeitbasis - - - DsoWidget - - Zoom x%L1 - Zoom x%L1 + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + Minimale Zeit zwischen Datenblöcken<br/>(Längere Zeiten reduzieren die CPU-Last) - - - - - - - /div - /div + + Horizontal + Horizontal - - Portable Document Format (*.pdf) - Portables Dokumentenformat (*.pdf) + + Interpolation + Interpolation - - PostScript (*.ps) - PostScript (*.ps) + + Digital phosphor depth + Stufen für digitalen Phosphor - - Export file... - Datei exportieren... + + Graph + Graph - - Marker 1/2 - Marker 1/2 + + Position + Position - - %L1% - %L1% + + Left + Links - - %1 %2 %3 %4 - %1 %2 %3 %4 + + Right + Rechts - - /s - /s + + Cursors + Cursor - - Image (*.png *.xpm *.jpg) - Bild (*.png *.xpm *.jpg) + + Export zoomed screen in double height + Exportiere gezoomtes Display in doppelter Höhe - - Comma-Separated Values (*.csv) - Kommagetrennte Werte (*.csv) + + Export + Export - - - Exporter - - Print oscillograph - Oszillograph drucken + + Scope has hardware modification for AC coupling (restart needed to apply the change) + Das Gerät hat Hardware-Modifikation für AC-Kopplung (Neustart erforderlich) - - %L1% - %L1% + + Save settings on exit + Einstellungen beim Beenden speichern - - %1 %2 %3 %4 - %1 %2 %3 %4 + + Apply default settings after next restart + Standardeinstellungen beim nächsten Start anwenden - - %1 S - %1 S + + Save settings now + Einstellungen jetzt speichern - - /s - /s + Save default settings on exit + Standardeinstellung beim Beenden speichern - - - - - - - /div - /div + Apply default settings after restart + Standardeinstellungen beim nächsten Start anwenden - - Zoom x%L1 - Zoom x%L1 + Save default settings now + Standardeinstellungen jetzt speichern - - Marker 1/2 - Marker 1/2 + + Configuration + Konfiguration - Hantek::Control + DsoConfigSpectrumPage - - EXT - EXT + + Rectangular + Rechteck - - EXT/10 - EXT/10 + + Hamming + Hamming - - The device has been disconnected - Die Verbindung zum Gerät wurde getrennt + + Hann + Hann - - Unknown model - Unbekanntes Modell + + Cosine + Cosinus - - Couldn't get channel level data from oscilloscope - Konnte Kanalpegeldaten des Oszilloskops nicht lesen + + Lanczos + Lanczos - - - Hantek::Device - - Can't search for Hantek oscilloscopes: %1 - Suche nach Hantek Oszilloskopen fehlgeschlagen: %1 + + Bartlett + Bartlett - - - Failed to claim interface %1 of device %2: %3 - Anforderung der Schnittstelle %1 des Gerätes %2 fehlgeschlagen: %3 + + Triangular + Dreieck - - - Device found: Hantek %1 (%2) - Gerät gefunden: Hantek %1 (%2) + + Gauss + Gauss - - Couldn't open device %1 - Konnte Gerät %1 nicht öffnen + + Bartlett-Hann + Bartlett-Hann - - - No Hantek oscilloscope found - Keine Hantek Oszilloskope gefunden + + Blackman + Blackman - - - Failed to get device list: %1 - Abrufen der Geräteliste fehlgeschlagen: %1 + + Nuttall + Nuttall - - Couldn't open device %1: %2 - Konnte Gerät %1 nicht öffnen: %2 + + Blackman-Harris + Blackman-Harris - - - HorizontalDock - - Horizontal - Horizontal + + Blackman-Nuttall + Blackman-Nuttall - - Samplerate - Samplerate + + Flat top + Flat Top - - Timebase - Zeitbasis + + <b>Window function</b> + <b>Fensterfunktion<b> - - Frequencybase - Frequenzbasis + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Referenzpegel</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV - - Record length - Satzlänge + + + dBV + dBV - - Format - Format + + <b>Minimum magnitude</b> + <b>Minimaler Pegel</b> - - - Roll - Rollen + + Spectrum + Spektrum - OpenHantekMainWindow + DsoSettings - - OpenHantek - OpenHantek + + SP%1 + SP%1 - - &Open... - &Öffnen... + + CH%1 + CH%1 - - Ctrl+O - Strg+O + + SPM + SPM - - Open saved settings - Gespeicherte Einstellungen öffnen + + MATH + MATH + + + DsoWidget - - &Save - &Speichern + + + + + + + /div + /div - - - Ctrl+S - Strg+S + + TR + TR - - Save the current settings - Aktuelle Einstellungen speichern + + Markers + Marker - - Save &as... - Speichern &unter... + + + ON + EIN - - Save the current settings to another file - Aktuelle Einstellungen in anderer Datei speichern + + + OFF + AUS - - &Print... - &Drucken... + + Markers + Merker - - Ctrl+P - Strg+P + + Time: + Zeit: - - Print the oscilloscope screen - Den Oscilloskopbildschirm drucken + + Frequency: + Frequenz: - - &Export as... - &Exportieren als... + + Zoom x%L1 + Zoom x%L1 - - Ctrl+E - Strg+E + + Zoom --- + Zoom --- - - Export the oscilloscope data to a file - Die Oszilloskopdaten in eine Datei exportieren + + %L1% + %L1% - - E&xit - &Beenden + + %1 %2 %3 %4 %5 + %1 %2 %3 %4 %5 - - Ctrl+Q - Strg+Q + + /s + /s - - Exit the application - Anwendung beenden + + on screen + angezeigt - - &Settings - &Einstellungen + + pp + pp - - Configure the oscilloscope - Das Oszilloskop konfigurieren + + rms + rms + + + ExporterRegistry - - Send command - Befehl senden + + Data saved + Daten gespeichert - - Shift+C - Shift+C + + No data exported + Keine Daten exportiert + + + GlScope - - &Docking windows - &Dockfenster + + System does not support OpenGL Shading Language (GLSL) + System unterstützt nicht OpenGL Shading Language (GLSL) - - &Toolbars - &Werkzeugleisten + + OpenGL init called twice! + OpenGL init wurde doppelt aufgerufen! - - - Settings (*.ini) - Einstellungen (*.ini) + + Failed to compile OpenGL shader programs. + + Fehler beim Übersetzen der OpenGL Shader-Programme. + - - Save settings - Einstellungen speichern + + Failed to link/bind OpenGL shader programs. + + Fehler beim Linken der OpenGL Shader-Programme. + - - &Stop - &Stop + + Failed to locate shader variable. + Kann Shader-Variable nicht lokalisieren. + + + HantekDsoControl - - <p>This is a open source software for Hantek USB oscilloscopes.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> - <p>Dies ist ein Open-Source Programm für Hantek USB Oszilloskope.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> + Couldn't get calibration data from oscilloscope + Konnte Kalibrierdaten des Oszilloskops nicht lesen - - Invalid command - Ungültiger Befehl + Couldn't get channel level data from oscilloscope + Konnte Kanalpegeldaten des Oszilloskops nicht lesen - - Space - Leertaste + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + Konnte Kalibrierdaten des Oszilloskops nicht lesen, benutze eine config-Datei für die Kalibrierung! + + + HorizontalDock - - Stop the oscilloscope - Das Oszilloskop anhalten + + Horizontal + Horizontal - - Digital &phosphor - Digitaler &Phosphor + + Samplerate + Samplerate - - &Zoom - &Zoom + + /s + /s - - &About - &Über + + Timebase + Zeitbasis - - Show information about this program - Zeige Informationen über dieses Programm + Frequencybase + Frequenzbasis - - About &Qt - Über &Qt + + Format + Format - - Show the Qt library's About box - Zeige Dialog zur Qt Bibliothek + + Calibration out + Kalibriersignal + + + LegacyExportDrawer - - &File - &Datei + %L1% + %L1% - - &View - &Ansicht + %1 %2 %3 %4 %5 + %1 %2 %3 %4 %5 - - &Oscilloscope - &Oszilloskop + %1 S on screen + %1 S angezeigt - - &Help - &Hilfe + /s + /s - - File - Datei + /div + /div - - Oscilloscope - Oszilloskop + pp + pp - - View - Ansicht + rms + rms - - Ready - Bereit + Zoom x%L1 + Zoom x%L1 - - Open file - Datei öffnen + Marker 1/2 + Marker 1/2 - - &Start - &Start + &Print .. + &Drucken .. - - Start the oscilloscope - Startet das Oszilloskop + Print oscillograph + Oszillogramm drucken - - Disable fading of previous graphs - Nachleuchten von vorigen Graphen deaktivieren + Export &Image/PDF .. + Exportiere &Graphik/PDF .. - - Enable fading of previous graphs - Nachleuchten von vorigen Graphen aktivieren + Portable Document Format (*.pdf) + Portables Dokumentenformat (*.pdf) - - Hide magnified scope - Vergrößerte Anzeige ausblenden + Image (*.png *.jpg) + Bild (*.png *.jpg) - - Show magnified scope - Vergrößerte Anzeige anzeigen + Save image + Bild speichern - - About OpenHantek %1 - *ber OpenHantek %1 + + Export &CSV .. + Exportiere &CSV .. - - - QApplication - - Success (no error) - Erfolgreich (Kein Fehler) + + Comma-Separated Values (*.csv) + Kommagetrennte Werte (*.csv) - - Input/output error - Ein-/Ausgabe Fehler + + Save CSV + CSV speichern - - Invalid parameter - Ungültiger Parameter + SP%1 + SP%1 - - Access denied (insufficient permissions) - Zugriff verweigert (Unzureichende Berechtigungen) + CH%1 + CH%1 - - No such device (it may have been disconnected) - Gerät nicht vorhanden (Möglicherweise wurde es abgesteckt) + SPM + SPM - - Entity not found - Datensatz nicht gefunden + MATH + MATH + + + MainWindow - - Resource busy - Quelle belegt + + MainWindow + Hauptfenster - - Operation timed out - Zeitüberschreitung + + &File + &Datei - - Overflow - Überlauf + + &View + &Ansicht - - Pipe error - Leitungsfehler + + &Oscilloscope + &Oszilloskop - - System call interrupted (perhaps due to signal) - Systemaufruf unterbrochen (Möglicherweise aufgrund eines Signals) + + &Export + &Export - - Insufficient memory - Unzureichender Speicher + + Save settings &as .. + Einstellungen speichern &als .. - - Operation not supported or unimplemented on this platform - Vorgang auf diesem System nicht unterstützt oder nicht implementiert + + &Exit + &Exit - - Other error - Anderer Fehler + + S&ampling + &Abtastung - - %L1 µV - %L1 µV + + &Refresh + &Refresh + + + + &Phosphor + &Phosphor + + + + &Histogram + &Histogram + + + + &Zoom + + + + + &Measure + &Messen + + + + &User Manual + &Benutzerhandbuch + + + + &About .. + &Über .. + + + + &Settings .. + &Einstellungen .. + + + + &Manual command + &Manuelles Kommando + + + + AC &Modification + AC &Modifikation + + + Manual &command + Manuelles &Kommando + + + + &Help + &Hilfe + + + Export + Export + + + + toolBar + Werkzeugleiste + + + + &Open settings .. + Einstellungen &laden .. + + + + Ctrl+O + Strg+O + + + + &Save settings + Einstellungen &speichern + + + + Ctrl+S + Strg+S + + + Digital Phosphor + Digitaler Phosphor + + + Open settings .. + Eistellungen laden .. + + + Save settings as .. + Einstellungen speichern als .. + + + Exit + Beenden + + + + Ctrl+Q + Strg+Q + + + Sampling + Abtastung + + + + Space + Leertaste + + + Digital phosphor + Digitaler Phosphor + + + Histogram + Histogramm + + + Zoom + Zoom + + + Measure + Messen + + + + Docking windows + Platzierbare Fenster + + + + Toolbars + Werkzeugleisten + + + User Manual + Benutzerhandbuch + + + About .. + Über .. + + + Settings .. + Einstellungen .. + + + Manual command + Manuelles Kommando + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + OpenHantek6022 (%1) - Device %2 (FW%3) + + + Screenshot.. + Screenshot .. + + + + Invalid command + Ungültiger Befehl + + + + Stop the oscilloscope + Das Oszilloskop anhalten + + + + Stop + Stop + + + OpenHantek6022 (%1) - Device %2 + OpenHantek6022 (%1) - Device %2 + + + + Demo Mode + Demo-Modus + + + + OpenHantek6022 (%1) - + + + + + Screenshot .. + Screenshot .. + + + + Hardcopy .. + Hardcopy .. + + + + Print screen .. + Drucken .. + + + + Start + Start + + + + Start the oscilloscope + Startet das Oszilloskop + + + + Open file + Datei öffnen + + + + + Settings (*.conf) + Einstellungen (*.conf) + + + Settings (*.ini) + Einstellungen (*.ini) + + + + Save settings + Einstellungen speichern + + + + Disable fading of previous graphs + Nachleuchten von vorigen Graphen deaktivieren + + + + Enable fading of previous graphs + Nachleuchten von vorigen Graphen aktivieren + + + + Hide histogram + Histogramm ausblenden + + + + Show histogram + Histogramm einblenden + + + + Hide magnified scope + Vergrößerte Anzeige ausblenden + + + + Show magnified scope + Vergrößerte Anzeige anzeigen + + + + Hide measurements + Messwerte ausblenden - + + Show measurements + Messwerte anzeigen + + + + About OpenHantek6022 (%1) + Über OpenHantek6022 (%1) + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + <p>Open-Source-Software für Hantek6022 USB Oszilloskope</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open-Source-Firmware - Copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + <p>Running since %1 seconds.</p> + <p>Laufzeit %1 Sekunden.</p> + + + + %1: %2 + %1: %2 + + + + yyyy-MM-dd hh:mm:ss + + + + + yyyyMMdd_hhmmss + + + + + Image (*.png *.jpg) + Bild (*.png *.jpg) + + + + Portable Document Format (*.pdf) + Portables Dokumentenformat (*.pdf) + + + + Save screenshot + Screenshot speichen + + + + Print oscillograph + Oszillogramm drucken + + + + NewDeviceModelFromExisting + + New device from template + Neues Gerät aus Vorlage + + + Select USB device + mindestens zwei Leerzeichen am Ende, damit der Text nicht abgeschnitten dargestellt wird (?) + USB-Gerät auswählen + + + Refresh + Erneuern + + + This is usually indicated by a light (red flashing) + Dies wird üblicherweise durch eine rot-blinkende LED angezeigt + + + Firmware is uploaded already* + Firmware ist bereits geladen* + + + Template selection + Vorlage auswählen + + + Select the existing model that should be used as template for your device. You may need to restart OpenHantek and try different models until it works for you. + Bitte ein Modell auswählen, das als Vorlage verwendet werden soll. Möglicherweise muss dies mehrfach mit unterschiedlichen Modellen probiert werden. + + + Please enter a model name + Bitte einen Modell-Namen eingeben + + + Firmware files + Firmware-Dateien + + + Please select the firmware files in hex format, extracted from the windows driver for example. You need to open this dialog again after the firmware has been uploaded. + Bitte eine Firmware im hex-Format auswählen, die z.B. aus dem Windows-Treiber extrahiert wurde. Dieser Dialog muss nach dem Laden der Firmware erneut geöffnet werden. + + + Firmware + Firmware + + + Loader + Loader + + + ... + ... + + + No USB devices found or your operating system prohibited enumerating devices. + Kein USB-Gerät gefunden oder das Betriebssystem erlaubt keinen Zugriff darauf. + + + No access granted for the selected USB device. Your operating system may prohibit access. On Windows you might need to install a generic driver first. On Linux you need to install an udev rule to grant access to your currently logged in user. + Kein Zugriff auf das USB-Gerät. Das Betriebssystem blockiert möglicherweise den Zugriff. Für Windows muss eventuell zuerst ein Treiber geladen werden. Für Linux ist eine udev-Datei erforderlich, die den Zugriff auf das Gerät erlaubt. + + + Sorry + Sorry + + + This is not yet implemented! + Dies ist noch nicht unterstützt! + + + + QApplication + + %L1 mV %L1 mV - + %L1 V %L1 V - + %L1 dB %L1 dB - + %L1 ps %L1 ps - + %L1 ns %L1 ns - - %L1 µs - %L1 µs - - - + %L1 ms %L1 ms - + %L1 s %L1 s - + + %L1 µV + %L1 µV + + + + %L1 µs + %L1 µs + + + %L1 min %L1 min - + %L1 h %L1 h - + %L1 Hz %L1 Hz - + %L1 kHz %L1 kHz - + %L1 MHz %L1 MHz - + %L1 GHz %L1 GHz - + %L1 S %L1 S - + %L1 kS %L1 kS - + %L1 MS %L1 MS - + %L1 GS %L1 GS - CH%1 - CH%1 + CH%1 - SP%1 - SP%1 + SP%1 - MATH - MATH + MATH - SPM - SPM + SPM + + + + QCoreApplication + + Export CSV .. + Exportiere CSV .. + + + Export &CSV .. + Exportiere &CSV .. + + + Comma-Separated Values (*.csv) + Kommagetrennte Werte (*.csv) + + + Export Image/PDF .. + Exportiere Graphik/PDF .. + + + Export &Image/PDF .. + Exportiere &Graphik/PDF .. + + + Portable Document Format (*.pdf) + Portables Dokumentenformat (*.pdf) + + + Image (*.png *.jpg) + Bild (*.png *.jpg) + + + Export file .. + Datei exportieren .. + + + Print .. + Drucken .. + + + &Print .. + &Drucken .. + + + Print oscillograph + Oszillogramm drucken - Voltage - Spannung + Spannung - Spectrum - Spektrum + Spektrum - + T - Y T - Y - + X - Y X - Y - + AC AC - + DC DC - + GND GND - + None + Kein + + + + Auto + Auto + + + + Normal + Normal + + + + Roll + Rollen + + + + Single + Einzel + + + Off + Aus + + + Linear + Linear + + + Demo mode without scope HW + Demo-Modus ohne Hardware + + + Use OpenGL ES instead of OpenGL + Benutze OpenGL ES anstelle von OpenGL + + + CH1 + CH2 CH1 + CH2 - + CH1 - CH2 CH1 - CH2 - + CH2 - CH1 CH2 - CH1 - - Auto - Auto + + CH1 * CH2 + CH1 * CH2 - - Normal - Normal + + CH1 AC + CH1 AC - - Single - Einzel + + CH2 AC + CH2 AC - + Rectangular Rechteck - + Hamming Hamming - + Hann Hann - + Cosine Cosinus - + Lanczos Lanczos - + Bartlett Bartlett - + Triangular Dreieck - + Gauss Gauss - + Bartlett-Hann Bartlett-Hann - + Blackman Blackman - + Nuttall Nuttall - + Blackman-Harris Blackman-Harris - + Blackman-Nuttall Blackman-Nuttall - + Flat top Flat Top - - Off - Aus + Success (no error) + Erfolgreich (Kein Fehler) - - Linear - Linear + Input/output error + Ein-/Ausgabe Fehler + + + Invalid parameter + Ungültiger Parameter - - Sinc - Sinc + Access denied (insufficient permissions) + Zugriff verweigert (Unzureichende Berechtigungen) + + + No such device (it may have been disconnected) + Gerät nicht vorhanden (Möglicherweise wurde es abgesteckt) + + + Entity not found + Datensatz nicht gefunden + + + Resource busy + Quelle belegt + + + Operation timed out + Zeitüberschreitung + + + Overflow + Überlauf + + + Pipe error + Leitungsfehler + + + System call interrupted (perhaps due to signal) + Systemaufruf unterbrochen (Möglicherweise aufgrund eines Signals) + + + Insufficient memory + Unzureichender Speicher + + + Operation not supported or unimplemented on this platform + Vorgang auf diesem System nicht unterstützt oder nicht implementiert + + + Other error + Anderer Fehler + + + + QObject + + + %1:%2 - No access + %1:%2 - Kein Zugriff + + + + %1:%2 (%3 - %4) + %1:%2 (%3 - %4) + + + + Devicename + Gerätename + + + + Status + Status + + + + SelectSupportedDevice + + + Select device + Gerät auswählen + + + Supported device + Unterstützte Geräte + + + + Devices: + Geräte: + + + + + ... + ... + + + + Demo Mode + Demo-Modus + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>Das Gerät ist bereit.</b></p><p>Das <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>Benutzerhandbuch</a> gibt Hinweise für den sicheren Gebrauch.</p> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Firmware wird geladen...</p><p><b>Sollte dies länger als 30 s dauern, bitte abbrechen <br/>und das Programm neu starten!</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>Verbindungsfehler!</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>OpenHantek6022 sucht nach kompatiblen Geräten ...</p><p><img align='right' height='150' src='qrc:///switch_6022BL.png'>Bitte das Gerät in den Oszilloskop-Modus schalten, falls es mehrere Funktionen besitzt.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <p>Bitte sicherstellen, dass der korrekte Windows-Treiber installiert ist.</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <p>Bitte sicherstellen, dass eine udev-Regel nach <b>%1</b> kopiert wurde um den USB-Zugriff zu ermöglichen.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p><a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>Informationen</a> zur Erstellung des Programms.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/><p>Die Funktionen des Programms können auch ohne Gerät im <b>Demo-Modus</b> erkundet werden.</p> + + + + Can't initalize USB: %1 + Kann USB nicht initialisieren: %1 SpectrumDock - + Spectrum Spektrum + + + Frequencybase + Frequenzbasis + TriggerDock - + Trigger Trigger - + CH%1 CH%1 - + + CH%1 smooth + CH%1 geglättet + + + Mode Modus - + Slope Flanke - + Source Quelle @@ -1134,9 +1767,33 @@ VoltageDock - + Voltage Spannung + + + CH&%1 + CH&%1 + + + + MA&TH + MA&TH + + + &MATH + &MATH + + + + Invert + Invertiert + + + + x + + diff --git a/openhantek/translations/openhantek_es.ts b/openhantek/translations/openhantek_es.ts new file mode 100644 index 00000000..2924ecb6 --- /dev/null +++ b/openhantek/translations/openhantek_es.ts @@ -0,0 +1,1650 @@ + + + + + + + + Couldn't open device: %1 + No se pudo abrir dispositivo: %1 + + + + DsoConfigAnalysisPage + + Cosine + Coseno + + + <b>Window function</b> + <b>Función de ventana</b> + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Nivel de referencia</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + + + <b>Minimum magnitude</b> + <b>Magnitud mínima</b> + + + Spectrum + Espectro + + + + DsoConfigColorsPage + + + Screen + Pantalla + + + + Print + Impresión + + + + Axes + Ejes + + + + Background + Fondo + + + + Border + Borde + + + + Grid + Rejilla + + + + Markers + Marcadores + + + + Text + Texto + + + + + Channel + Canal + + + + + Spectrum + Espectro + + + + Export hardcopy images or pdf files with printer colors + Exportar imágenes impresas o archivos pdf con los colores de la impresora + + + Export images with screen colors + Exportar imágenes con los colores de la pantalla + + + + Screen and Print Colors + Pantalla y colores de impresión + + + + DsoConfigDialog + + + Settings + Configuración + + + + &Ok + &Aceptar + + + + &Apply + &Aplicar + + + + &Cancel + &Cancelar + + + + Spectrum + Espectro + + + + Scope + Osciloscopio + + + + Colors + Colores + + + File + Archivo + + + + DsoConfigFilePage + + Export Images with Screen Colors + Exportar imágenes con los colores de la pantalla + + + Image width + Ancho de la imagen + + + Image height + Altura de la imagen + + + Export images with screen colors + Exportar imágenes con los colores de la pantalla + + + Take a screenshot of the display area only + Haga una captura de pantalla de la zona de visualización solamente + + + Export zoomed screen in double height + Exportar la pantalla con zoom en doble altura + + + Export + Exportar + + + Save default settings on exit + Guardar configuración predeterminada al salir + + + Save default settings now + Guardar configuración predeterminada ahora + + + Configuration + Configuración + + + + DsoConfigScopePage + + + Off + Desactivada + + + + Linear + Lineal + + + Timebase + Base de tiempos + + + Set slowest possible timebase +(GUI may become very unresponsible!) + Establecer la base de tiempo más lenta posible +(¡El GUI puede volverse muy poco responsable!) + + + Minimal time between captured frames +(Longer times reduce the CPU load) + El tiempo mínimo entre los cuadros capturados +(Los tiempos más largos reducen la carga de la CPU) + + + Set slowest possible timebase<br/>(<b>GUI may become very unresponsible!</b>) + Establecer la base de tiempo más lenta posible<br/>(<b>¡El GUI puede volverse muy poco responsable!</b>) + + + + Set slowest possible timebase + Establecer la base de tiempo más lenta posible + + + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + El tiempo mínimo entre los cuadros capturados<br/>(Los tiempos más largos reducen la carga de la CPU) + + + + Horizontal + Horizontal + + + + Interpolation + Interpolación + + + + Digital phosphor depth + Profundidad del fósforo digital + + + + Graph + Gráfico + + + + Position + Posición + + + + Left + Izquierda + + + + Right + Derecha + + + + Cursors + Cursores + + + + Export zoomed screen in double height + Exportar la pantalla con zoom en doble altura + + + + Export + Exportar + + + + Scope has hardware modification for AC coupling (restart needed to apply the change) + Scope tiene una modificación de hardware para el acoplamiento de CA (se necesita reiniciar para aplicar el cambio) + + + + Save settings on exit + Guardar configuración al salir + + + + Apply default settings after next restart + Aplicar la configuración predeterminada después del reinicio + + + + Save settings now + Guardar configuración ahora + + + Save default settings on exit + Guardar configuración predeterminada al salir + + + Apply default settings after restart + Aplicar la configuración predeterminada después del reinicio + + + Save default settings now + Guardar configuración predeterminada ahora + + + + Configuration + Configuración + + + + DsoConfigSpectrumPage + + + Rectangular + + + + + Hamming + + + + + Hann + + + + + Cosine + Coseno + + + + Lanczos + + + + + Bartlett + + + + + Triangular + + + + + Gauss + + + + + Bartlett-Hann + + + + + Blackman + + + + + Nuttall + + + + + Blackman-Harris + + + + + Blackman-Nuttall + + + + + Flat top + + + + + <b>Window function</b> + <b>Función de ventana</b> + + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Nivel de referencia</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + + + + + dBV + + + + + <b>Minimum magnitude</b> + <b>Magnitud mínima</b> + + + + Spectrum + Espectro + + + + DsoSettings + + + SP%1 + + + + + CH%1 + + + + + SPM + + + + + MATH + + + + + DsoWidget + + + TR + + + + + Markers + Marcadores + + + + + ON + + + + + + OFF + + + + + Markers + Marcadores + + + + Time: + Tiempo: + + + + Frequency: + Frecuencia: + + + + Zoom x%L1 + + + + + Zoom --- + + + + + + + + + + /div + + + + + %L1% + + + + + %1 %2 %3 %4 %5 + + + + + /s + + + + + on screen + en pantalla + + + + pp + pp + + + + rms + rms + + + + ExporterRegistry + + + Data saved + Datos guardados + + + + No data exported + Ningún dato exportado + + + + GlScope + + + System does not support OpenGL Shading Language (GLSL) + El sistema no tiene OpenGL Shading Language (GLSL) + + + + OpenGL init called twice! + ¡OpenGL init se ha llamado dos veces! + + + + Failed to compile OpenGL shader programs. + + Ha fallado al compilar programas OpenGL shader. + + + + + Failed to link/bind OpenGL shader programs. + + Ha fallado al enlazar programas OpenGL shader. + + + + + Failed to locate shader variable. + Ha fallado al localizar la variable shader. + + + + HantekDsoControl + + Couldn't get calibration data from oscilloscope + No se pudieron obtener los datos de calibración del osciloscopio + + + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + No se pudieron obtener los datos de calibración del osciloscopio. ¡Usa un archivo de configuración para la calibración! + + + + HorizontalDock + + + Horizontal + Horizontal + + + + Samplerate + Muestreo + + + + /s + /s + + + + Timebase + Base de tiempos + + + Frequencybase + Frecuencia base + + + + Format + Formato + + + + Calibration out + Salida calibración + + + + LegacyExportDrawer + + %1 S on screen + %1 S en pantalla + + + /s + /s + + + pp + pp + + + rms + rms + + + Marker 1/2 + Marcador 1/2 + + + &Print .. + Im&primir .. + + + Print oscillograph + Imprimir oscilógrafo + + + Export &Image/PDF .. + Exportar &imagen/PDF .. + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Image (*.png *.jpg) + Imagen (*.png *.jpg) + + + Save image + Guardar imagen + + + + Export &CSV .. + Exportar &CSV .. + + + + Comma-Separated Values (*.csv) + Valores separados por coma (*.csv) + + + + Save CSV + Guardar CSV + + + + MainWindow + + + MainWindow + + + + + &File + &Archivo + + + + &View + &Ver + + + + &Oscilloscope + &Osciloscopio + + + + &Export + &Exportar + + + + &Open settings .. + &Abrir configuración .. + + + + &Save settings + &Guardar configuración + + + + Save settings &as .. + Guardar configuración &como .. + + + + &Exit + &Salir + + + + S&ampling + M&uestreo + + + + &Refresh + &Actualizar + + + + &Phosphor + &Fósforo + + + + &Histogram + &Histograma + + + + &Zoom + &Zoom + + + + &Measure + &Medidas + + + + &User Manual + &Manual del usuario + + + + &About .. + &Acerca de .. + + + + &Settings .. + &Configuración .. + + + + &Manual command + Orden &manual + + + + AC &Modification + AC &Modificación + + + Manual &command + Orden &manual + + + + &Help + A&yuda + + + Export + Exportar + + + + toolBar + + + + Open settings .. + Abrir configuración... + + + + Ctrl+O + + + + + Save settings + Guardar configuración + + + + Ctrl+S + + + + Save settings as .. + Guardar configuración como... + + + Exit + Salir + + + + Ctrl+Q + + + + Sampling + Muestreo + + + + Space + + + + Digital phosphor + Fósforo digital + + + Histogram + Histograma + + + Measure + Medidas + + + + Docking windows + Ventanas acoplables + + + + Toolbars + Barra de herramientas + + + User Manual + Manual del usuario + + + About .. + Acerca de... + + + Settings .. + Configuración... + + + Manual command + Orden manual + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + OpenHantek6022 (%1) - Dispositivo %2 (FW%3) + + + OpenHantek6022 (%1) - Device %2 + OpenHantek6022 (%1) - Dispositivo %2 + + + + Demo Mode + Modo de demostración + + + + OpenHantek6022 (%1) - + + + + + Screenshot .. + Captura de pantalla .. + + + + Hardcopy .. + Hardcopy .. + + + + Print screen .. + Imprimir oscilógrafo .. + + + + Invalid command + Orden no válida + + + + Stop + Parar + + + + Stop the oscilloscope + Parar el osciloscopio + + + + Start + Iniciar + + + + Start the oscilloscope + Iniciar el osciloscopio + + + + Open file + Abrir archivo + + + + + Settings (*.conf) + Configuración (*.conf) + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + Settings (*.ini) + Configuración (*.ini) + + + + Disable fading of previous graphs + Desactivar desvanecimiento de los gráficos anteriores + + + + Enable fading of previous graphs + Activar desvanecimiento de los gráficos anteriores + + + + Hide histogram + Ocultar histograma + + + + Show histogram + Mostrar histograma + + + + Hide magnified scope + Ocultar osciloscopio aumentado + + + + Show magnified scope + Mostrar osciloscopio aumentado + + + + Hide measurements + Ocultar medidas + + + + Show measurements + Mostrar medidas + + + + About OpenHantek6022 (%1) + Acerca de OpenHantek6022 (%1) + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + <p>Software de código abierto para osciloscopios USB Hantek6022</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Firmware de código abierto copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + <p>Running since %1 seconds.</p> + <p>El programa ha estado funcionando durante %1 segundos.</p> + + + + %1: %2 + + + + + yyyy-MM-dd hh:mm:ss + yyyy-MM-dd hh:mm:ss + + + + yyyyMMdd_hhmmss + yyyy-MM-dd hh:mm:ss + + + + Image (*.png *.jpg) + Imagen (*.png *.jpg) + + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + + Save screenshot + Guardar captura de pantalla + + + + Print oscillograph + Imprimir oscilógrafo + + + + NewDeviceModelFromExisting + + New device from template + Nuevo dispositivo desde plantilla + + + Select USB device + Seleccionar dispositivo USB + + + Refresh + Actualizar + + + This is usually indicated by a light (red flashing) + Se indica generalmente con una luz (roja intermitente) + + + Firmware is uploaded already* + El firmware ya está cargado* + + + Template selection + Selección de la plantilla + + + Select the existing model that should be used as template for your device. You may need to restart OpenHantek and try different models until it works for you. + Seleccionar el modelo existente que debiera ser usado como plantilla para su dispositivo. Puede necesitar reiniciar OpenHantek y probar modelos diferentes hasta que funcione. + + + Please enter a model name + Introduzca el nombre del modelo + + + Firmware files + Archivos del firmware + + + Please select the firmware files in hex format, extracted from the windows driver for example. You need to open this dialog again after the firmware has been uploaded. + Seleccione los archivos del firmware en formato hex, extraídos del controlador de windows por ejemplo. Necesita abrir este diálogo otra vez después de que el firmware haya sido cargado. + + + Firmware + Firmware + + + Loader + Cargador + + + No USB devices found or your operating system prohibited enumerating devices. + Ningún dispositivo USB encontrado o su sistema operativo no permite enumerar dispositivos. + + + No access granted for the selected USB device. Your operating system may prohibit access. On Windows you might need to install a generic driver first. On Linux you need to install an udev rule to grant access to your currently logged in user. + Acceso no permitido para el dispositivo USB seleccionado. Su sistema operativo puede haber impedido el acceso. En Windows puede que primero necesite instalar un controlador genérico. En Linux necesita instalar una regla udev para permitir el acceso al usuario actual. + + + Sorry + Lo siento + + + This is not yet implemented! + ¡Todavía no implementado! + + + + QApplication + + + %L1 µV + + + + + %L1 mV + + + + + %L1 V + + + + + %L1 dB + + + + + %L1 ps + + + + + %L1 ns + + + + + %L1 µs + + + + + %L1 ms + + + + + %L1 s + + + + + %L1 min + + + + + %L1 h + + + + + %L1 Hz + + + + + %L1 kHz + + + + + %L1 MHz + + + + + %L1 GHz + + + + + %L1 S + + + + + %L1 kS + + + + + %L1 MS + + + + + %L1 GS + + + + + QCoreApplication + + Success (no error) + Éxito (ningún error) + + + Input/output error + Error de entrada/salida + + + Invalid parameter + Parámetro no válido + + + Access denied (insufficient permissions) + Acceso denegado (permisos insuficientes) + + + No such device (it may have been disconnected) + No tal dispositivo (puede haya sido desconectado) + + + Entity not found + Entidad no encontrada + + + Resource busy + Recurso ocupado + + + Operation timed out + Operación expirada + + + Overflow + Desbordamiento + + + System call interrupted (perhaps due to signal) + Llamada del sistema interrumpida (quizás debido a una señal) + + + Insufficient memory + Memoria insuficiente + + + Operation not supported or unimplemented on this platform + Operación no soportada o sin implementar en esta plataforma + + + Other error + Otro error + + + + CH1 + CH2 + + + + + CH1 - CH2 + + + + + CH2 - CH1 + + + + + CH1 * CH2 + + + + + CH1 AC + + + + + CH2 AC + + + + + Rectangular + + + + + Hamming + + + + + Hann + + + + + Cosine + Coseno + + + + Lanczos + + + + + Bartlett + + + + + Triangular + + + + + Gauss + + + + + Bartlett-Hann + + + + + Blackman + + + + + Nuttall + + + + + Blackman-Harris + + + + + Blackman-Nuttall + + + + + Flat top + + + + Use OpenGL ES instead of OpenGL + Usar OpenGL ES en vez de OpenGL + + + Voltage + Voltaje + + + Spectrum + Espectro + + + + T - Y + + + + + X - Y + + + + + AC + + + + + DC + + + + + GND + + + + + Auto + + + + + Normal + + + + + Roll + Rodar + + + + Single + Simple + + + Linear + Lineal + + + Print .. + Imprimir... + + + &Print .. + Im&primir .. + + + Print oscillograph + Imprimir oscilógrafo + + + Export Image/PDF .. + Exportar imagen/PDF... + + + Export &Image/PDF .. + Exportar &imagen/PDF .. + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Image (*.png *.jpg) + Imagen (*.png *.jpg) + + + Export file .. + Exportar archivo .. + + + Export CSV .. + Exportar CSV... + + + Export &CSV .. + Exportar &CSV .. + + + Comma-Separated Values (*.csv) + Valores separados por coma (*.csv) + + + + QObject + + + %1:%2 - No access + %1:%2 - Sin acceso + + + + %1:%2 (%3 - %4) + + + + + Devicename + Nombre del dispositivo + + + + Status + Estado + + + + SelectSupportedDevice + + + Select device + Seleccionar dispositivo + + + Supported device + Dispositivo compatible + + + + Devices: + Dispositivos: + + + + + ... + + + + + Demo Mode + Modo de demostración + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>El dispositivo está listo para su uso.</b></p><p>Lea con detenimiento el <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>manual del usuario</a> para una operacion segura.</p> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Carga en progreso ...</p><p><b>Si la carga durase más de 30 s, cierre esta ventana <br/>y reinicie el programa.</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>Ha fallado la conexión.</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>OpenHantek6022 está buscando dispositivos compatibles...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>No olvide cambiar su dispositivo al modo osciloscopio si tiene múltiples modos.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <p>Asegúrese de que ha instalado el controlador USB de Windows correctamente</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <p>Asegúrese de que ha copiado las reglas udev en <b>%1</b> para permisos de acceso USB correctos.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p>Visite las instrucciones de compilación y ejecución en el <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>sitio web</a> para más ayuda.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/><p>Incluso sin un dispositivo puedes explorar la función del programa, sólo tienes que pulsar el botón <b>Modo de demostración</b>.</p> + + + + Can't initalize USB: %1 + No se puede inicializar USB: %1 + + + + SpectrumDock + + + Spectrum + Espectro + + + + Frequencybase + Frecuencia base + + + + TriggerDock + + + Trigger + Disparador + + + + CH%1 + + + + + CH%1 smooth + CH%1 liso + + + + Mode + Modo + + + + Slope + Flanco + + + + Source + Fuente + + + + VoltageDock + + + Voltage + Voltaje + + + + CH&%1 + + + + + MA&TH + + + + + Invert + Invertir + + + + x + + + + diff --git a/openhantek/translations/openhantek_fr.ts b/openhantek/translations/openhantek_fr.ts new file mode 100644 index 00000000..d941af76 --- /dev/null +++ b/openhantek/translations/openhantek_fr.ts @@ -0,0 +1,1782 @@ + + + + + + + + Couldn't open device: %1 + Impossible d'ouvrir le périphérique : %1 + + + + DsoConfigAnalysisPage + + Rectangular + Rectangulaire + + + Hamming + Hamming + + + Hann + Hann + + + Cosine + Cosinus + + + Lanczos + Lanczos + + + Bartlett + Bartlett + + + Triangular + Triangulaire + + + Gauss + Gauss + + + Bartlett-Hann + Bartlett-Hann + + + Blackman + Blackman + + + Nuttall + Nuttall + + + Blackman-Harris + Blackman-Harris + + + Blackman-Nuttall + Blackman-Nuttall + + + Flat top + Flat Top + + + <b>Window function</b> + <b>Fenêtrage</b> + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Niveau de réference</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + + + <b>Minimum magnitude</b> + <b>Amplitude minimum</b> + + + Spectrum + Spectre + + + + DsoConfigColorsPage + + + Screen + Ecran + + + + Print + Impression + + + + Axes + Axes + + + + Background + Arrière-plan + + + + Border + Bordure + + + + Grid + Grille + + + + Markers + Repères + + + + Text + Texte + + + + + Channel + Canal + + + + + Spectrum + Spectre + + + + Export hardcopy images or pdf files with printer colors + Exporter des images sur papier ou des fichiers pdf aux couleurs de l'imprimante + + + Export images with screen colors + Exporter images avec couleurs de l'écran + + + + Screen and Print Colors + Couleurs à l'écran et à l'impression + + + + DsoConfigDialog + + + Settings + Réglages + + + + &Ok + + + + + &Apply + &Appliquer + + + + &Cancel + A&nnuler + + + + Spectrum + Spectre + + + + Scope + Oscilloscope + + + + Colors + Couleurs + + + File + Fichier + + + + DsoConfigFilePage + + Export Images with Screen Colors + Exporter images avec couleurs de l'écran + + + Image width + Largeur d'image + + + Image height + Hauteur d'image + + + Export images with screen colors + Exporter images avec couleurs de l'écran + + + Take a screenshot of the display area only + Prenez une capture d'écran de la zone d'affichage uniquement + + + Export zoomed screen in double height + Exporter l'écran zoomé en double hauteur + + + Export + Exporter + + + Save default settings on exit + Enregistrer les réglages en quittant + + + Save default settings now + Enregistrer les réglages maintenant + + + Configuration + Configuration + + + + DsoConfigScopePage + + + Off + + + + + Linear + Linéaire + + + Timebase + Base temps + + + Set slowest possible timebase +(GUI may become very unresponsible!) + Fixer la base de temps la plus lente possible +(L'interface graphique peut devenir très irresponsable !) + + + Minimal time between captured frames +(Longer times reduce the CPU load) + Temps minimal entre les images capturées +(Des durées plus longues réduisent la charge du CPU) + + + Set slowest possible timebase<br/>(<b>GUI may become very unresponsible!</b>) + Fixer la base de temps la plus lente possible<br/>(<b>L'interface graphique peut devenir très irresponsable !</b>) + + + + Set slowest possible timebase + Fixer la base de temps la plus lente possible + + + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + Temps minimal entre les images capturées<br/>(Des durées plus longues réduisent la charge du CPU) + + + + Horizontal + Horizontal + + + + Interpolation + Interpolation + + + + Digital phosphor depth + Profondeur de phosphore numérique + + + + Graph + Graphe + + + + Position + Position + + + + Left + Gauche + + + + Right + Droite + + + + Cursors + Curseurs + + + + Export zoomed screen in double height + Exporter l'écran zoomé en double hauteur + + + + Export + Exporter + + + + Scope has hardware modification for AC coupling (restart needed to apply the change) + L'oscilloscope a subi une modification hardware pour le couplage AC (redémarrage nécessaire pour appliquer la modification) + + + + Save settings on exit + Enregistrer les réglages en quittant + + + + Apply default settings after next restart + Appliquer les paramètres par défaut après le redémarrage + + + + Save settings now + Enregistrer les réglages maintenant + + + Save default settings on exit + Enregistrer les réglages en quittant + + + Apply default settings after restart + Appliquer les paramètres par défaut après le redémarrage + + + Save default settings now + Enregistrer les réglages maintenant + + + + Configuration + Configuration + + + + DsoConfigSpectrumPage + + + Rectangular + Rectangulaire + + + + Hamming + Hamming + + + + Hann + Hann + + + + Cosine + Cosinus + + + + Lanczos + Lanczos + + + + Bartlett + Bartlett + + + + Triangular + Triangulaire + + + + Gauss + Gauss + + + + Bartlett-Hann + Bartlett-Hann + + + + Blackman + Blackman + + + + Nuttall + Nuttall + + + + Blackman-Harris + Blackman-Harris + + + + Blackman-Nuttall + Blackman-Nuttall + + + + Flat top + Flat Top + + + + <b>Window function</b> + <b>Fenêtrage</b> + + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Niveau de réference</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + + + + + dBV + + + + + <b>Minimum magnitude</b> + <b>Amplitude minimum</b> + + + + Spectrum + Spectre + + + + DsoSettings + + + SP%1 + + + + + CH%1 + + + + + SPM + SPM + + + + MATH + MATH + + + + DsoWidget + + + TR + D + + + + Markers + Repères + + + + + + + + + /div + /div + + + + + ON + + + + + + OFF + + + + + Markers + Repères + + + + Time: + Temps : + + + + Frequency: + Fréquence : + + + + Zoom x%L1 + + + + + Zoom --- + + + + + %L1% + + + + + %1 %2 %3 %4 %5 + + + + + /s + /s + + + + on screen + à l'écran + + + + pp + pp + + + + rms + rms + + + + ExporterRegistry + + + Data saved + Données sauvegardées + + + + No data exported + Aucune donnée exportée + + + + GlScope + + + System does not support OpenGL Shading Language (GLSL) + Le système ne supporte pas OpenGL Shading Language (GLSL) + + + + OpenGL init called twice! + Double initialisation d'OpenGL ! + + + + Failed to compile OpenGL shader programs. + + Echec de compilation des programmes d'ombrage OpenGL. + + + + + Failed to link/bind OpenGL shader programs. + + Echec de création des liens des programmes d'ombrage OpenGL. + + + + + Failed to locate shader variable. + Echec de localisation des variables d'ombrage. + + + + HantekDsoControl + + Couldn't get channel level data from oscilloscope + Impossible d'obtenir les données de niveau du canal depuis l'oscilloscope + + + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + Impossible d'obtenir les données de niveau du canal depuis l'oscilloscope. Utilisez un fichier de configuration pour la calibration ! + + + + HorizontalDock + + + Horizontal + Horizontal + + + + Samplerate + Taux d'éch. + + + + /s + /s + + + + Timebase + Base temps + + + Frequencybase + Base fréq. + + + + Format + Format + + + + Calibration out + Etalonnage + + + + LegacyExportDrawer + + %1 S on screen + %1 E à l'écran + + + /s + /s + + + /div + /div + + + pp + pp + + + rms + rms + + + Marker 1/2 + Repère 1/2 + + + &Print .. + Im&primer .. + + + Print oscillograph + Imprimer l'oscillographe + + + Export &Image/PDF .. + Exporter &image/PDF .. + + + Image (*.png *.jpg) + Image (*.png *.jpg) + + + Save image + Enregistrer image + + + + Export &CSV .. + Exporter &CSV .. + + + + Comma-Separated Values (*.csv) + + + + + Save CSV + Enregistrer CSV + + + SPM + SPM + + + + MainWindow + + + MainWindow + + + + + &File + &Fichier + + + + &View + &Affichage + + + + &Oscilloscope + &Oscilloscope + + + + &Export + &Exporter + + + + &Open settings .. + &Ouvrir une configuration .. + + + + &Save settings + &Enregistrer les réglages + + + + Save settings &as .. + Enregistrer la configuration &sous .. + + + + &Exit + &Quitter + + + + S&ampling + E&chantillonnage + + + + &Refresh + &Rafraichir + + + + &Phosphor + + + + + &Histogram + + + + + &Zoom + + + + + &Measure + + + + + &User Manual + &Manuel utilisateur + + + + &About .. + &A propos .. + + + + &Settings .. + &Réglages .. + + + + &Manual command + &Ligne de commande + + + + AC &Modification + AC &Modification + + + Manual &command + &Ligne de commande + + + + &Help + A&ide + + + Export + Exporter + + + + toolBar + Barre d'outils + + + Open layout + &Ouvrir une configuration... + + + + Ctrl+O + Ctrl+O + + + Save layout + &Enregistrer la configuration + + + + Ctrl+S + Ctrl+E + + + Save as ... + Enregistrer la configuration &sous... + + + Open settings .. + &Ouvrir une configuration .. + + + Save settings as .. + Enregistrer la configuration &sous .. + + + Exit + &Quitter + + + + Ctrl+Q + Ctrl+Q + + + Sampling + Echantillonnage + + + + Space + Espace + + + Digital phosphor + &Phosphore numérique + + + Histogram + Histogramme + + + Zoom + &Zoom + + + Measure + &Mesures + + + + Docking windows + Fenêtres ancrables + + + + Toolbars + Barres d'outils + + + User Manual + &Manuel utilisateur... + + + About .. + &A propos... + + + Settings .. + &Réglages... + + + About + &A propos... + + + Settings + &Réglages... + + + Manual command + &Ligne de commande + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + OpenHantek6022 (%1) - %2 (FW%3) + + + OpenHantek6022 (%1) - Device %2 + OpenHantek6022 (%1) - %2 + + + + Demo Mode + Mode Démo + + + + OpenHantek6022 (%1) - + + + + + Screenshot .. + Capture d'écran .. + + + + Hardcopy .. + Copie papier .. + + + + Print screen .. + Imprimer l'oscillographe .. + + + + Invalid command + Commande invalide + + + + Stop + Arrêter + + + + Stop the oscilloscope + Arrêter l'oscilloscope + + + + Start + Démarrer + + + + Start the oscilloscope + Démarrer l'oscilloscope + + + + Open file + Ouvrir une configuration + + + + + Settings (*.conf) + Réglages (*.conf) + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + Settings (*.ini) + Réglages (*.ini) + + + + Save settings + Enregistrer les réglages + + + + Disable fading of previous graphs + Désactiver l'estompage des graphes précédents + + + + Enable fading of previous graphs + Activer l'estompage des graphes précédents + + + + Hide histogram + Cacher l’histogramme + + + + Show histogram + Montrer l’histogramme + + + + Hide magnified scope + Cacher la vue agrandie + + + + Show magnified scope + Montrer la vue agrandie + + + + Hide measurements + Cacher les mesures + + + + Show measurements + Montrer les mesures + + + + About OpenHantek6022 (%1) + A propos de OpenHantek6022 (%1) + + + + <p>Running since %1 seconds.</p> + <p>En cours depuis %1 secondes.</p> + + + + %1: %2 + + + + + yyyy-MM-dd hh:mm:ss + + + + + yyyyMMdd_hhmmss + + + + + Portable Document Format (*.pdf) + + + + + Save screenshot + Enregistrer la capture d'écran + + + + Print oscillograph + Imprimer l'oscillographe + + + + Image (*.png *.jpg) + Image (*.png *.jpg) + + + + NewDeviceModelFromExisting + + New device from template + Nouveau périphérique depuis un modèle + + + Select USB device + Sélectionner un périphérique USB + + + Refresh + Rafraichir + + + This is usually indicated by a light (red flashing) + Ceci est généralement indiqué pour une lumière (rouge clignotante) + + + Firmware is uploaded already* + Le firmware est déjà téléchargé* + + + Template selection + Sélection du modèle + + + Select the existing model that should be used as template for your device. You may need to restart OpenHantek and try different models until it works for you. + Selectionner un appareil existant qui pourrait servir de modèle pour votre appareil. Il peut être nécessaire de relancer le logiciel et d'essayer différents modèles. + + + Please enter a model name + Entrer un nom de modèle + + + Firmware files + Fichiers de firmware + + + Please select the firmware files in hex format, extracted from the windows driver for example. You need to open this dialog again after the firmware has been uploaded. + Selectionner les fichiers au format héxadécimal, extraits du pilote Windows par exemple. Il peut être nécessaire de réouvrir cette fenêtre après le téléchargement du firmware. + + + Loader + Chargeur + + + No USB devices found or your operating system prohibited enumerating devices. + Aucun périphérique USB n'a été trouvé ou votre système d'exploitation interdit l'énumération des périphériques. + + + No access granted for the selected USB device. Your operating system may prohibit access. On Windows you might need to install a generic driver first. On Linux you need to install an udev rule to grant access to your currently logged in user. + Aucun accès n'est autorisé pour le périphérique USB sélectionné. Votre système d'exploitation peut interdire cet accès. Sous Windows, vous devrez peut-être d'abord installer un pilote générique. Sous Linux, vous devez installer une règle udev pour autoriser l'accès pour l'utilisateur courant. + + + Sorry + Désolé + + + This is not yet implemented! + Ceci n'est pas encore développé ! + + + + QApplication + + + %L1 µV + + + + + %L1 mV + + + + + %L1 V + + + + + %L1 dB + + + + + %L1 ps + + + + + %L1 ns + + + + + %L1 µs + + + + + %L1 ms + + + + + %L1 s + + + + + %L1 min + + + + + %L1 h + + + + + %L1 Hz + + + + + %L1 kHz + + + + + %L1 MHz + + + + + %L1 GHz + + + + + %L1 S + %L1 E + + + + %L1 kS + %L1 kE + + + + %L1 MS + %L1 ME + + + + %L1 GS + %L1 GE + + + SPM + SPM + + + + QCoreApplication + + Success (no error) + Réussi (pas d'erreur) + + + Input/output error + Erreur d'entrée/sortie + + + Invalid parameter + Paramètre invalide + + + Access denied (insufficient permissions) + Accès refusé (permissions insuffisantes) + + + No such device (it may have been disconnected) + Aucun périphérique (il peut avoir été déconnecté) + + + Entity not found + Entité non trouvée + + + Resource busy + Ressource occupée + + + Operation timed out + Opération trop longue + + + Overflow + Dépassement de capacité + + + Pipe error + Erreur de pipe + + + System call interrupted (perhaps due to signal) + Appel système interrompu (peut-être à cause du signal) + + + Insufficient memory + Mémoire insuffisante + + + Operation not supported or unimplemented on this platform + Opération non supportée sur ce système + + + Other error + Autre erreur + + + + CH1 + CH2 + + + + + CH1 - CH2 + + + + + CH2 - CH1 + + + + + CH1 * CH2 + CH1 x CH2 + + + + CH1 AC + + + + + CH2 AC + + + + + Rectangular + Rectangulaire + + + + Hamming + Hamming + + + + Hann + Hann + + + + Cosine + Cosinus + + + + Lanczos + Lanczos + + + + Bartlett + Bartlett + + + + Triangular + Triangulaire + + + + Gauss + Gauss + + + + Bartlett-Hann + Bartlett-Hann + + + + Blackman + Blackman + + + + Nuttall + Nuttall + + + + Blackman-Harris + Blackman-Harris + + + + Blackman-Nuttall + Blackman-Nuttall + + + + Flat top + Flat Top + + + Demo mode without scope HW + Mode démo sans équipement + + + Use OpenGL ES instead of OpenGL + Utilisez OpenGL ES à la place de OpenGL + + + Voltage + Tension + + + Spectrum + Spectre + + + + T - Y + + + + + X - Y + + + + + AC + + + + + DC + + + + + GND + + + + None + Aucun + + + + Auto + + + + + Normal + + + + + Roll + Rouler + + + + Single + Unique + + + Linear + Linéaire + + + Print + &Imprimer... + + + Print .. + Imprimer .. + + + &Print .. + Im&primer .. + + + Print oscillograph + Imprimer l'oscillographe + + + Export Image/PDF + Exporter i&mage/PDF... + + + Export Image/PDF .. + Exporter image/PDF .. + + + Export &Image/PDF .. + Exporter &image/PDF .. + + + Export file .. + Exporter un fichier .. + + + Export file... + Exporter un fichier... + + + Export CSV + Exporter &CSV... + + + Export CSV .. + Exporter CSV .. + + + Export &CSV .. + Exporter &CSV .. + + + + QObject + + + %1:%2 - No access + %1:%2 - aucun accès + + + + %1:%2 (%3 - %4) + + + + + Devicename + Nom de l'appareil + + + + Status + Etat + + + + SelectSupportedDevice + + + Select device + Sélectionner l'appareil + + + Supported device + Appareil supporté + + + + Devices: + Appareils : + + + + + ... + ... + + + + Demo Mode + Mode Démo + + + Try unsupported + Essayez non supporté + + + <html><head/><body><p>Your device might not directly be supported by OpenHantek6022. But it might be compatible to one of the existing devices. Help us to identify those devices and report back on our <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#0000ff;">issue tracker</span></a>.</p></body></html> + Il se peut que votre appareil ne soit pas directement pris en charge par OpenHantek6022. Mais il pourrait être compatible avec l'un des appareils existants. Aidez-nous à identifier ces appareils et rapportez-nous sur notre <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration : souligné ; color:#000000ff ;">issue tracker</span></a>.</p></body></html> + + + Add new device from template + Ajouter un nouvel appareil à partir du template + + + <br/><p>The device is ready for use.</p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/><p>L'appareil est prêt à l'emploi.</p><p>Veuillez observer le <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>mode d'emploi</a> pour un fonctionnement sûr.</p> + + + <p>Upload in progress ...</p><p>If the upload takes more than 30 s, please close this window <br/>and restart the program!</p> + <p>Téléchargement en cours...</p><p>Si le téléchargement dure plus de 30 s, fermez cette fenêtre <br/>et redémarrez le programme!</p> + + + Connection failed! + La connexion a échoué! + + + <br/><p><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/><p><b>L'appareil est prêt à l'emploi.</b></p><p>Veuillez observer le <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>mode d'emploi</a> pour un fonctionnement sûr.</p> + + + <p><br/><b>Connection failed!</p> + <p><br/><b>La connexion a échoué!</p> + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>L'appareil est prêt à l'emploi.</b></p><p>Veuillez lire le <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>mode d'emploi</a> pour un fonctionnement sûr.</p> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Téléchargement en cours...</p><p><b>S'il dure plus de 30 s, fermez cette fenêtre <br/>et redémarrez le programme !</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>La connexion a échoué !</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>OpenHantek6022 recherche des périphériques compatibles...</p><p><img align='right' height='200' src='qrc:////switch_6022BL.png'>N'oubliez pas de passer votre appareil en mode oscilloscope s'il a plusieurs modes.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <p>Assurez-vous d'avoir installé correctement le pilote USB de Windows</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <p>Assurez-vous d'avoir copié le fichier de règles udev dans <b>%1</b> pour des permissions d'accès USB correctes.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p>Visitez la page d'instructions <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>build and run</a> pour de l'aide.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/><p>Même sans appareil, vous pouvez explorer la fonction du programme, il suffit d'appuyer sur le bouton <b>Mode Démo</b>.</p> + + + + Can't initalize USB: %1 + Impossible d'initialiser l'USB: %1 + + + + SpectrumDock + + + Spectrum + Spectre + + + + Frequencybase + Base fréq. + + + + TriggerDock + + + Trigger + Déclenchement + + + + CH%1 + + + + + CH%1 smooth + CH%1 lisse + + + + Mode + Mode + + + + Slope + Pente + + + + Source + Source + + + + VoltageDock + + + Voltage + Tension + + + + CH&%1 + + + + + MA&TH + + + + &MATH + &MATH + + + + Invert + Inverser + + + x10 + x10 + + + + x + + + + diff --git a/openhantek/translations/openhantek_it.ts b/openhantek/translations/openhantek_it.ts new file mode 100644 index 00000000..d9058a0d --- /dev/null +++ b/openhantek/translations/openhantek_it.ts @@ -0,0 +1,1319 @@ + + + + + + + + Couldn't open device: %1 + + + + + DsoConfigColorsPage + + + Screen + + + + + Print + + + + + Axes + + + + + Background + + + + + Border + + + + + Grid + + + + + Markers + + + + + Text + + + + + + Channel + + + + + + Spectrum + + + + + Export hardcopy images or pdf files with printer colors + Esportazione di immagini cartacee o file pdf con colori della stampante + + + + Screen and Print Colors + Colori dello schermo e della stampante + + + + DsoConfigDialog + + + Settings + + + + + &Ok + + + + + &Apply + + + + + &Cancel + + + + + Spectrum + + + + + Scope + + + + + Colors + + + + + DsoConfigScopePage + + + Off + + + + + Linear + + + + Set slowest possible timebase +(GUI may become very unresponsible!) + Impostare la base dei tempi più lenta possibile +(L'interfaccia grafica può diventare molto irresponsabile!) + + + Minimal time between captured frames +(Longer times reduce the CPU load) + Tempo minimo tra i fotogrammi catturati +(Tempi più lunghi riducono il carico della CPU) + + + Set slowest possible timebase<br/>(<b>GUI may become very unresponsible!</b>) + Impostare la base dei tempi più lenta possibile<br/>(<b>L'interfaccia grafica può diventare molto irresponsabile!</b>) + + + + Set slowest possible timebase + Impostare la base dei tempi più lenta possibile + + + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + Tempo minimo tra i fotogrammi catturati<br/>(Tempi più lunghi riducono il carico della CPU) + + + + Horizontal + + + + + Interpolation + + + + + Digital phosphor depth + + + + + Graph + + + + + Position + Posizione + + + + Left + A sinistra + + + + Right + A destra + + + + Cursors + + + + + Export zoomed screen in double height + Esportazione dello schermo ingrandito in doppia altezza + + + + Export + Esportazione + + + + Scope has hardware modification for AC coupling (restart needed to apply the change) + L'oscilloscopio ha una modifica hardware per l'accoppiamento AC (è necessario riavviare per applicare la modifica) + + + + Save settings on exit + Salvare le impostazioni all'uscita + + + + Apply default settings after next restart + + + + + Save settings now + Salvare ora le impostazioni + + + Save default settings on exit + Salvare le impostazioni predefinite all'uscita + + + Apply default settings after restart + Applicare le impostazioni predefinite dopo il riavvio + + + Save default settings now + Salvare ora le impostazioni predefinite + + + + Configuration + Configurazione + + + + DsoConfigSpectrumPage + + + Rectangular + + + + + Hamming + + + + + Hann + + + + + Cosine + + + + + Lanczos + + + + + Bartlett + + + + + Triangular + + + + + Gauss + + + + + Bartlett-Hann + + + + + Blackman + + + + + Nuttall + + + + + Blackman-Harris + + + + + Blackman-Nuttall + + + + + Flat top + + + + + <b>Window function</b> + + + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + + + + + + dBV + + + + + <b>Minimum magnitude</b> + + + + + Spectrum + + + + + DsoSettings + + + SP%1 + + + + + CH%1 + + + + + SPM + + + + + MATH + + + + + DsoWidget + + + TR + + + + + Markers + + + + + + + + + + /div + + + + + + ON + + + + + + OFF + + + + + Markers + + + + + Time: + + + + + Frequency: + + + + + Zoom x%L1 + + + + + Zoom --- + + + + + %L1% + + + + + %1 %2 %3 %4 %5 + + + + + /s + + + + + on screen + + + + + pp + + + + + rms + + + + + ExporterRegistry + + + Data saved + + + + + No data exported + + + + + GlScope + + + System does not support OpenGL Shading Language (GLSL) + + + + + OpenGL init called twice! + + + + + Failed to compile OpenGL shader programs. + + + + + + Failed to link/bind OpenGL shader programs. + + + + + + Failed to locate shader variable. + + + + + HantekDsoControl + + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + Impossibile ottenere i dati di calibrazione dall'EEPROM dell'oscilloscopio. Utilizzare un file di configurazione per la calibrazione! + + + + HorizontalDock + + + Horizontal + + + + + Samplerate + + + + + /s + + + + + Timebase + + + + + Format + + + + + Calibration out + + + + + LegacyExportDrawer + + + Export &CSV .. + Esportazione &CSV .. + + + + Comma-Separated Values (*.csv) + Valori separati da virgola (*.csv) + + + + Save CSV + Salvare CSV + + + + + MainWindow + + + MainWindow + + + + + &File + + + + + &View + + + + + &Oscilloscope + + + + + Save settings &as .. + + + + + &Exit + + + + + S&ampling + + + + + &Phosphor + + + + + &Histogram + + + + + &Zoom + + + + + &Measure + + + + + &User Manual + + + + + &About .. + + + + + &Settings .. + + + + + &Manual command + + + + + AC &Modification + + + + + toolBar + + + + + &Help + + + + + &Export + + + + + &Open settings .. + + + + + Ctrl+O + + + + + &Save settings + + + + + Ctrl+S + + + + + Ctrl+Q + + + + + Space + + + + + &Refresh + + + + + Docking windows + + + + + Toolbars + + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + + + + + Demo Mode + modalità demo + + + + OpenHantek6022 (%1) - + + + + + Screenshot .. + + + + + Hardcopy .. + + + + + Print screen .. + + + + + Invalid command + + + + + Stop + + + + + Stop the oscilloscope + + + + + Start + + + + + Start the oscilloscope + + + + + Open file + + + + + + Settings (*.conf) + + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + + Save settings + + + + + Disable fading of previous graphs + + + + + Enable fading of previous graphs + + + + + Hide histogram + + + + + Show histogram + + + + + Hide magnified scope + + + + + Show magnified scope + + + + + Hide measurements + + + + + Show measurements + + + + + About OpenHantek6022 (%1) + + + + + <p>Running since %1 seconds.</p> + + + + + %1: %2 + + + + + yyyy-MM-dd hh:mm:ss + + + + + yyyyMMdd_hhmmss + + + + + Portable Document Format (*.pdf) + + + + + Save screenshot + + + + + Print oscillograph + + + + + Image (*.png *.jpg) + + + + + NewDeviceModelFromExisting + + ... + ... + + + + QApplication + + + %L1 µV + + + + + %L1 mV + + + + + %L1 V + + + + + %L1 dB + + + + + %L1 ps + + + + + %L1 ns + + + + + %L1 µs + + + + + %L1 ms + + + + + %L1 s + + + + + %L1 min + + + + + %L1 h + + + + + %L1 Hz + + + + + %L1 kHz + + + + + %L1 MHz + + + + + %L1 GHz + + + + + %L1 S + + + + + %L1 kS + + + + + %L1 MS + + + + + %L1 GS + + + + + QCoreApplication + + + CH1 + CH2 + + + + + CH1 - CH2 + + + + + CH2 - CH1 + + + + + CH1 * CH2 + + + + + CH1 AC + + + + + CH2 AC + + + + + Rectangular + + + + + Hamming + + + + + Hann + + + + + Cosine + + + + + Lanczos + + + + + Bartlett + + + + + Triangular + + + + + Gauss + + + + + Bartlett-Hann + + + + + Blackman + + + + + Nuttall + + + + + Blackman-Harris + + + + + Blackman-Nuttall + + + + + Flat top + + + + + T - Y + + + + + X - Y + + + + + AC + + + + + DC + + + + + GND + + + + + Roll + + + + + Auto + + + + + Normal + + + + + Single + + + + + QObject + + + %1:%2 - No access + + + + + %1:%2 (%3 - %4) + + + + + Devicename + + + + + Status + + + + + SelectSupportedDevice + + + Select device + Seleziona apparecchio + + + Supported device + Apparecchio supportato + + + + Devices: + Apparecchi: + + + + + ... + ... + + + + Demo Mode + modalità demo + + + Try unsupported + Prova non supportato + + + <html><head/><body><p>Your device might not directly be supported by OpenHantek6022. But it might be compatible to one of the existing devices. Help us to identify those devices and report back on our <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#0000ff;">issue tracker</span></a>.</p></body></html> + <html><head/><body><p>Il vostro dispositivo potrebbe non essere supportato direttamente da OpenHantek6022. Ma potrebbe essere compatibile con uno dei dispositivi esistenti. Aiutaci a identificare questi dispositivi e riportaci sul nostro <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#000000ff;">issue tracker</span></a>.</p></p></body></html> + + + Add new device from template + Aggiungere un nuovo dispositivo dal template + + + <br/><p>The device is ready for use.</p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/><p>Il dispositivo è pronto all'uso.</p><p>Per un funzionamento sicuro, osservare il <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>manuale d'uso.</a> + + + <p>Upload in progress ...</p><p>If the upload takes more than 30 s, please close this window <br/>and restart the program!</p> + <p>Carica in corso ...</p><p><p>Se il caricamento richiede più di 30 secondi, chiudi questa finestra <br/>e riavvia il programma!</p> + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p><p>In this case, please unplug other USB devices on the same bus!<br/>You can check this under Linux with: <pre>lsusb; lsusb -t</pre></p> + <p>Carica in corso ...</p><p><p>Se il caricamento richiede più di 30 secondi, chiudi questa finestra <br/>e riavvia il programma!</p> + + + Connection failed! + Il collegamento è fallito! + + + <br/><p><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/><p><b>Il dispositivo è pronto all'uso.</b></p><p>Per un funzionamento sicuro, osservare il <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>manuale d'uso.</a> + + + <p><br/><b>Connection failed!</p> + <p><br/><b>Il collegamento è fallito!</p> + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>Il dispositivo è pronto all'uso.</b></p><p>Per un funzionamento sicuro, osservare il <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>manuale d'uso.</a> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Upload in corso ...</p><p><b>Se il caricamento richiede più di 30 s, si prega di chiudere questa finestra <br/>e riavviare il programma!</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>Il collegamento è fallito!</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>ApertoHantek6022 è alla ricerca di dispositivi compatibili...</p><p><img align='right' height='200' src='qrc:////switch_6022BL.png'>Non dimenticare di passare il dispositivo in modalità oscilloscopio se ha più modalità.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <p>Assicurati di aver installato correttamente il driver usb di Windows.</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <p>Assicurati di aver copiato il file delle regole udev in <b>%1</b> per ottenere le corrette autorizzazioni di accesso USB.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p>Visitare l'istruzione di compilazione ed esecuzione <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>sito web site</a> per help.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/><p>Anche senza un device è possibile esplorare la funzione del programma, basta premere il pulsante <b>modalità demo</b>.</p> + + + + Can't initalize USB: %1 + Non può initalizzare USB: %1 + + + + SpectrumDock + + + Spectrum + + + + + Frequencybase + + + + + TriggerDock + + + Trigger + + + + + CH%1 + + + + + CH%1 smooth + + + + + Mode + + + + + Slope + + + + + Source + + + + + VoltageDock + + + Voltage + + + + + CH&%1 + + + + + MA&TH + + + + + Invert + + + + + x + + + + diff --git a/openhantek/translations/openhantek_pt.ts b/openhantek/translations/openhantek_pt.ts index 39ca3623..e537f989 100644 --- a/openhantek/translations/openhantek_pt.ts +++ b/openhantek/translations/openhantek_pt.ts @@ -1,1150 +1,1638 @@ - + + + + + + Couldn't open device: %1 + + + DsoConfigAnalysisPage - Rectangular - Retangular + Retangular - Hamming - Hamming + Hamming - Hann - Hann + Hann - Cosine - Cosseno + Cosseno - Lanczos - Lanczos + Lanczos - Bartlett - Bartlett + Bartlett - Triangular - Triangular + Triangular - Gauss - Gauss + Gauss - Bartlett-Hann - Bartlett-Hann + Bartlett-Hann - Blackman - Blackman + Blackman - Nuttall - Nuttall + Nuttall - Blackman-Harris - Blackman-Harris + Blackman-Harris - Blackman-Nuttall - Blackman-Nuttall + Blackman-Nuttall - Flat top - Flat Top - - - - Window function - Função de Janela - - - - Reference level - Nivel de referência - - - - - dBm - dBm + Flat Top - - Minimum magnitude - Magnitude minima - - - Spectrum - Espectro + Espectro DsoConfigColorsPage - + Axes Eixos - + Background Fundo - + Border Borda - + Grid Grade - + Markers Marcadores - + Text Texto - + Export images with screen colors + Exportar imagens com cores de tela + + + + Screen and Print Colors + Cores de tela e impressão + + + Screen Tela - + + Print + Impressão + + + + Channel Canal - + + Spectrum Espectro - - Graph - Gráficos + + Export hardcopy images or pdf files with printer colors + Exportar imagens impressas em papel ou arquivos pdf com cores de impressora DsoConfigDialog - + Settings Configurações - + &Ok &Ok - + &Apply &Aplicar - + &Cancel &Cancelar - - Analysis - Análise + + Spectrum + Espectro - - Colors - Cores + File + Arquivo - - Files - Arquivos + + Colors + Cores - + Scope Osciloscópio - DsoConfigFilesPage + DsoConfigFilePage - - Save default settings on exit - Salvar configuração padrão ao sair + Export Images with Screen Colors + Exportar imagens com cores de tela - - Save default settings now - Salvar configuração padrão agora + Image width + Largura da imagem - - Configuration - Configuração + Image height + Altura da imagem - - Image width - Largura da imagem + Scale Screenshots too + Telas de Escala também - - Image height - Altura da imagem + Export images with screen colors + Exportar imagens com cores de tela + + + Take a screenshot of the display area only + Tire uma screenshot apenas da área de exibição + + + Export zoomed screen in double height + Exportar uma tela com zoom em altura dupla - Export - Exportar + Exportar + + + Save default settings on exit + Salvar configuração padrão ao sair + + + Save default settings now + Salvar configuração padrão agora + + + Configuration + Configuração DsoConfigScopePage - + Off Desligado - + Linear Linear - Sinc - Sinc + Sinc - - Antialiasing - Anti-serrilhamento + Timebase + Tempo base + + + Set slowest possible timebase +(GUI may become very unresponsible!) + Definir a base de tempo mais lenta possível +(O GUI pode se tornar muito pouco responsável!) + + + Minimal time between captured frames +(Longer times reduce the CPU load) + Tempo mínimo entre os frames capturados +(Tempos mais longos reduzem a carga da CPU) + + + Set slowest possible timebase<br/>(<b>GUI may become very unresponsible!</b>) + Definir a base de tempo mais lenta possível<br/>(<b>O GUI pode se tornar muito pouco responsável!</b>) + + + + Set slowest possible timebase + Definir a base de tempo mais lenta possível + + + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + Tempo mínimo entre os frames capturados<br/>(Tempos mais longos reduzem a carga da CPU) + + + + Horizontal + Horizontal - + Interpolation Interpolação - + Digital phosphor depth Profundidade digital - + Graph Gráfico - - - DsoWidget - - Zoom x%L1 - Zoom x%L1 + + Position + Posição - - - - - - - /div - /div + + Left + Esquerda - - Portable Document Format (*.pdf) - Portables Documenten Format (*.pdf) + + Right + Direita - - PostScript (*.ps) - PostScript (*.ps) + + Cursors + Cursores - - Export file... - Exportar arquivo... + + Export zoomed screen in double height + Exportar uma tela com zoom em altura dupla - - Marker 1/2 - Construtor 1/2 + + Export + Exportar - - %L1% - %L1% + + Scope has hardware modification for AC coupling (restart needed to apply the change) + O osciloscópio tem modificação de hardware para acoplamento AC (reinício necessário para aplicar a mudança) - - %1 %2 %3 %4 - %1 %2 %3 %4 + + Save settings on exit + Salvar configuração ao sair - - /s - /s + + Apply default settings after next restart + Aplicar as configurações padrão após o reinício - - %1 S - %1 S + + Save settings now + Salvar configuração agora - - Image (*.png *.xpm *.jpg) - Imagem (*.png *.xpm *.jpg) + Save default settings on exit + Salvar configuração padrão ao sair - - Comma-Separated Values (*.csv) - Separado por ponto e vírgula (*.csv) + Apply default settings after restart + Aplicar as configurações padrão após o reinício - - - Exporter - - Print oscillograph - Impressão + Save default settings now + Salvar configuração padrão agora - - %L1% - %L1% + + Configuration + Configuração + + + DsoConfigSpectrumPage - - %1 %2 %3 %4 - %1 %2 %3 %4 + + Rectangular + Retangular - - %1 S - %1 S + + Hamming + Hamming - - /s - /s + + Hann + Hann - - - - - - - /div - /div + + Cosine + Cosseno - - Zoom x%L1 - Aproximação x%L1 + + Lanczos + Lanczos - - Marker 1/2 - Construtor 1/2 + + Bartlett + Bartlett - - - Hantek::Control - - EXT - EXT + + Triangular + Triangular - - EXT/10 - EXT/10 + + Gauss + Gauss - - The device has been disconnected - O dispositivo esta desconectado + + Bartlett-Hann + Bartlett-Hann - - Unknown model - Modelo desconhecido + + Blackman + Blackman - - Couldn't get channel level data from oscilloscope - Não foi possível obter dados do canal do osciloscópio + + Nuttall + Nuttall + + + + Blackman-Harris + Blackman-Harris - - - Hantek::Device - - Can't search for Hantek oscilloscopes: %1 - Não é possivel procurar pelo osciloscópio Hantek: %1 + + Blackman-Nuttall + Blackman-Nuttall - - - Failed to claim interface %1 of device %2: %3 - Falha ao acessar a interface %1 do dispositivo %2: %3 + + Flat top + Flat Top - - - Device found: Hantek %1 (%2) - Dispositivo encontrado Hantek %1 (%2) + + <b>Window function</b> + - - Couldn't open device %1 - Não foi possivel acessar o dispositivo %1 + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + - - - No Hantek oscilloscope found - Nenhum osciloscópio Hantek encontrado + + + dBV + - - - Failed to get device list: %1 - Falha ao obter a lista de dispositivos: %1 + + <b>Minimum magnitude</b> + - - Couldn't open device %1: %2 - Não foi possível abrir o dispositivo %1: %2 + + Spectrum + Espectro - HorizontalDock + DsoSettings - - Horizontal - Horizontal + + SP%1 + SP%1 - - Timebase - Tempo base + + CH%1 + CH%1 - - Frequencybase - Frequência base + + SPM + SPC - - Format - Formato + + MATH + MATH - OpenHantekMainWindow + DsoWidget - - OpenHantek - OpenHantek + Zoom x%L1 + Zoom x%L1 - - &Open... - &Abrir... + + + + + + + /div + /div - - Ctrl+O - Ctrl+O + + TR + - - Open saved settings - Abrir configuração salva + + Markers + Marcadores - - &Save - &Salvar + + + ON + - - - Ctrl+S - Ctrl+S + + + OFF + - - Save the current settings - Salvar configuração atual + + Markers + Marcadores - - Save &as... - Salvar &como... + + Time: + Tempo: - - Save the current settings to another file - Salvar atual configuração em outro arquivo + + Frequency: + Frequência: - - &Print... - &Imprimir... + + Zoom x%L1 + Zoom x%L1 - - Ctrl+P - Ctrl+P + + Zoom --- + - - Print the oscilloscope screen - Imprimir a tela do osciloscópio + + %L1% + %L1% - - &Export as... - &Exportar como... + + %1 %2 %3 %4 %5 + - - Ctrl+E - Ctrl+E + + on screen + - - Export the oscilloscope data to a file - Exportar dados do osciloscópio para um arquivo + + pp + - - E&xit - &Sair + + rms + - - Ctrl+Q - Ctrl+Q + %L1 + %L1% {1?} - - Exit the application - Sair do programa + %1 %2 %3 %4 %5 + %1 %2 %3 %4 %5 {1 ?} {2 ?} {3 ?} - - &Settings - &Configurações + %1 %2 %3 %4 + %1 %2 %3 %4 - - Configure the oscilloscope - Configurar o osciloscópio + + /s + /s - - Send command - Enviar comando + %L1 S + %L1 S + + + ExporterRegistry - - Shift+C - Shift+C + + Data saved + Dados salvos - - &Docking windows - &Janelas flutuantes + + No data exported + Nenhum dado exportado + + + GlScope - - &Toolbars - &Ferramentas + + System does not support OpenGL Shading Language (GLSL) + - - &Record length - &Tamanho do registro + + OpenGL init called twice! + - - - Settings (*.ini) - Configurações (*.ini) + + Failed to compile OpenGL shader programs. + + - - Save settings - Salvar configurações + + Failed to link/bind OpenGL shader programs. + + - - &Stop - &Parar + + Failed to locate shader variable. + + + + HantekDsoControl - - <p>This is a open source software for Hantek USB oscilloscopes.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> - <p>Este é um programa Open-Source para osciloscópios USB Hantek.</p><p>Copyright &copy; 2010, 2011 Oliver Haag &lt;oliver.haag@gmail.com&gt;</p> + Couldn't get channel level data from oscilloscope + Não foi possível obter dados do canal do osciloscópio - - Invalid command - Comando inválido + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + Não foi possível obter dados de calibração a partir da EEPROM do osciloscópio. Use um arquivo de configuração para a calibração! + + + HorizontalDock - - Space - Espaço + + Horizontal + Horizontal - - Stop the oscilloscope - Parar osciloscópio + + Samplerate + - - &Small - &Pequeno + + /s + /s - - 10240 Samples - 10240 Amostras + + Timebase + Tempo base - - &Large - &Grande + Frequencybase + Frequência base - - 32768 Samples - 32768 Amostras + + Format + Formato - - Digital &phosphor - &Intensidade Digital + + Calibration out + + + + LegacyExportDrawer - - &Zoom - &Zoom + %L1% + %L1% - - &About - &Sobre + %1 %2 %3 %4 + %1 %2 %3 %4 - - Show information about this program - Mostrar informações sobre este programa + %1 %2 %3 %4 %5 + %1 %2 %3 %4 %5 - - About &Qt - Sobre o &Qt + %1 S + %1 S - - Show the Qt library's About box - Mostrar a mensagem sobre o Qt + /s + /s - - &File - &Arquivo + /div + /div - - &View - &Ver + Zoom x%L1 + Zoom x%L1 - - &Oscilloscope - &Osciloscópio + Marker 1/2 + Construtor 1/2 - &Buffer size - &Tamanho do buffer + &Print .. + &Impressão .. - - &Help - &Ajuda + Print oscillograph + Impressão - - File - Arquivo + Image (*.png *.jpg) + Imagem (*.png *.jpg) - - Oscilloscope - Oscilosópio + + Export &CSV .. + Exportar &CSV .. - - View - Ver + + Comma-Separated Values (*.csv) + Separado por ponto e vírgula (*.csv) - - Ready - Pronto + + Save CSV + Salvar CSV - - Open file - Abrir arquivo + SP%1 + SP%1 - - &Start - &Iniciar + CH%1 + CH%1 - - Start the oscilloscope - Iniciar osciloscópio + SPM + SPC - - Disable fading of previous graphs - Desabilitar desaparecimento de gráficos anteriores + MATH + MATH + + + MainWindow - - Enable fading of previous graphs - Habilitar desaparecimento de gráficos anteriores + + MainWindow + - - Hide magnified scope - Esconder/Ampliar osciloscópio + + &File + &Arquivo - - Show magnified scope - Mostrar osciloscópio ampliado + + &View + &Ver - - About OpenHantek %1 - *Sobre o OpenHantek %1 + + &Oscilloscope + &Osciloscópio - - - QApplication - - Success (no error) - Sucesso (sem erro) + + &Export + &Exportar - - Input/output error - Erro na entrada/saida + + Save settings &as .. + - - Invalid parameter - Parametro inválido + + &Exit + - - Access denied (insufficient permissions) - Acesso negado (permissões insuficientes) + + S&ampling + - - No such device (it may have been disconnected) - Não encontrado o dispositivo (pode estar desconectado) + + &Phosphor + - - Entity not found - Entidade não encontrada + + &Histogram + - - Resource busy - Recurso ocupado + + &Zoom + - - Operation timed out - Operação expirou + + &Measure + - - Overflow - Transbordamento + + &User Manual + - - Pipe error - Erro de redirecionamento + + &About .. + - - System call interrupted (perhaps due to signal) - Chamada de sistema interrompida(talvez, devido ao sinal) + + &Settings .. + &Configurações .. - - Insufficient memory - Memória insuficiente + + &Manual command + - - Operation not supported or unimplemented on this platform - Operação não suportada ou não implementada nesta plataforma + + AC &Modification + - - Other error - Outro erro + + &Help + &Ajuda - - %L1 µV - %L1 µV + Export + Exportar + + + + toolBar + + + + + &Open settings .. + + + + + Ctrl+O + Ctrl+O + + + + &Save settings + &Salvar configurações + + + + Ctrl+S + Ctrl+S + + + + Ctrl+Q + Ctrl+Q + + + + Space + Espaço + + + + &Refresh + + + + + Docking windows + + + + + Toolbars + + + + Settings + Configurações + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + + + + + Demo Mode + Demo Mode + + + + OpenHantek6022 (%1) - + + + + + Invalid command + Comando inválido + + + &Stop + &Parar + + + + Stop the oscilloscope + Parar osciloscópio + + + &Start + &Iniciar + + + + Screenshot .. + + + + + Hardcopy .. + + + + + Print screen .. + + + + + Stop + + + + + Start + + + + + Start the oscilloscope + Iniciar osciloscópio + + + + Open file + Abrir arquivo + + + + + Settings (*.conf) + Configurações (*.conf) + + + Settings (*.ini) + Configurações (*.ini) - + + Save settings + Salvar configurações + + + + Disable fading of previous graphs + Desabilitar desaparecimento de gráficos anteriores + + + + Enable fading of previous graphs + Habilitar desaparecimento de gráficos anteriores + + + + Hide histogram + Esconder histogram + + + + Show histogram + Mostrar histogram + + + + Hide magnified scope + Esconder/Ampliar osciloscópio + + + + Show magnified scope + Mostrar osciloscópio ampliado + + + + Hide measurements + + + + + Show measurements + + + + + About OpenHantek6022 (%1) + + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + + <p>Running since %1 seconds.</p> + + + + + %1: %2 + + + + + yyyy-MM-dd hh:mm:ss + + + + + yyyyMMdd_hhmmss + + + + + Image (*.png *.jpg) + Imagem (*.png *.jpg) + + + + Portable Document Format (*.pdf) + + + + + Save screenshot + + + + + Print oscillograph + Impressão + + + + NewDeviceModelFromExisting + + ... + ... + + + + QApplication + + %L1 mV %L1 mV - + %L1 V %L1 V - + %L1 dB %L1 dB - + %L1 ps %L1 ps - + %L1 ns %L1 ns - - %L1 µs - %L1 µs - - - + %L1 ms %L1 ms - + %L1 s %L1 s - + + %L1 µV + + + + + %L1 µs + + + + %L1 min %L1 min - + %L1 h %L1 h - + %L1 Hz %L1 Hz - + %L1 kHz %L1 kHz - + %L1 MHz %L1 MHz - + %L1 GHz %L1 GHz - + %L1 S %L1 S - + %L1 kS %L1 kS - + %L1 MS %L1 MS - + %L1 GS %L1 GS - CH%1 - CH%1 + CH%1 - SP%1 - SP%1 + SP%1 - MATH - MATH + MATH - SPM - SPC + SPC + + + + QCoreApplication + + Comma-Separated Values (*.csv) + Separado por ponto e vírgula (*.csv) + + + Export file... + Exportar arquivo... + + + Image (*.png *.jpg) + Imagem (*.png *.jpg) + + + Print oscillograph + Impressão - Voltage - Voltagem + Voltagem - Spectrum - Espectro + Espectro - + T - Y T - Y - + X - Y X - Y - + AC AC - + DC DC - + GND GND - + + Roll + + + + None + Nada + + + + Auto + Auto + + + + Normal + Normal + + + + Single + Simples + + + Off + Desligado + + + Linear + Linear + + + Sinc + Sinc + + + Use OpenGL ES instead of OpenGL + Usar OpenGL ES em vez de OpenGL + + + CH1 + CH2 CH1 + CH2 - + CH1 - CH2 CH1 - CH2 - + CH2 - CH1 CH2 - CH1 - - Auto - Auto + + CH1 * CH2 + CH1 * CH2 - - Normal - Normal + + CH1 AC + CH1 AC - - Single - Simples + + CH2 AC + CH2 AC - + Rectangular Retangular - + Hamming Hamming - + Hann Hann - + Cosine Cosseno - + Lanczos Lanczos - + Bartlett Bartlett - + Triangular Triangular - + Gauss Gauss - + Bartlett-Hann Bartlett-Hann - + Blackman Blackman - + Nuttall Nuttall - + Blackman-Harris Blackman-Harris - + Blackman-Nuttall Blackman-Nuttall - + Flat top Flat Top - - Off - Desligado + Success (no error) + Sucesso (sem erro) - - Linear - Linear + Input/output error + Erro na entrada/saida - - Sinc - Sinc + Invalid parameter + Parametro inválido + + + Access denied (insufficient permissions) + Acesso negado (permissões insuficientes) + + + No such device (it may have been disconnected) + Não encontrado o dispositivo (pode estar desconectado) + + + Entity not found + Entidade não encontrada + + + Resource busy + Recurso ocupado + + + Operation timed out + Operação expirou + + + Overflow + Transbordamento + + + Pipe error + Erro de redirecionamento + + + System call interrupted (perhaps due to signal) + Chamada de sistema interrompida(talvez, devido ao sinal) + + + Insufficient memory + Memória insuficiente + + + Operation not supported or unimplemented on this platform + Operação não suportada ou não implementada nesta plataforma + + + Other error + Outro erro + + + + QObject + + + %1:%2 - No access + + + + + %1:%2 (%3 - %4) + + + + + Devicename + + + + + Status + + + + + SelectSupportedDevice + + + Select device + Selecione o dispositivo + + + Supported device + Dispositivo suportado + + + + Devices: + Dispositivos: + + + + + ... + ... + + + + Demo Mode + Demo Mode + + + Try unsupported + Tente não suportado + + + <html><head/><body><p>Your device might not directly be supported by OpenHantek6022. But it might be compatible to one of the existing devices. Help us to identify those devices and report back on our <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#0000ff;">issue tracker</span></a>.</p></body></html> + <html><head/><body><p> Seu dispositivo pode não ser suportado diretamente pelo OpenHantek6022. Mas ele pode ser compatível com um dos dispositivos existentes. Ajude-nos a identificar esses dispositivos e a relatar a nossa <a href="https://github.com/OpenHantek/openhantek/issues"><span style=" text-decoration: underline; color:#0000ff;">issue tracker</span></a>.</p></body></html> + + + Add new device from template + Adicionar novo dispositivo a partir do modelo + + + Connection failed! + Conexão falhou! + + + <br/><p>The device is ready for use.</p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/>><p>O dispositivo está pronto para uso.</p><p>Por favor, observe o <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'> manual do usuário</a> para uma operação segura.</p> + + + <p>Upload in progress ...</p><p>If the upload takes more than 30 s, please close this window <br/>and restart the program!</p> + <p>Upload em andamento ...</p><p>>Se o upload demorar mais de 30 s, feche esta janela <br/>e reinicie o programa!</p> + + + <br/><p><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <br/>><p><b>O dispositivo está pronto para uso.</b></p><p>Por favor, observe o <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'> manual do usuário</a> para uma operação segura.</p> + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p><p>In this case, please unplug other USB devices on the same bus!<br/>You can check this under Linux with: <pre>lsusb; lsusb -t</pre></p> + <p>Upload em andamento ...</p><p>><b>Se o upload demorar mais de 30 s, feche esta janela <br/>e reinicie o programa!</b></p> + + + <p><br/><b>Connection failed!</p> + <p><br/><b>Conexão falhou!</p> + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>O dispositivo está pronto para uso.</b></p><p>Por favor, observe o <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'> manual do usuário</a> para uma operação segura.</p> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Upload em andamento ...</p><p>b>Se o upload demorar mais de 30 s, por favor feche esta janela <br/> e reinicie o programa!</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>Conexão falhou!</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>OpenHantek6022 está procurando por dispositivos compatíveis ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Não se esqueça de mudar seu dispositivo no modo osciloscópio se ele tiver vários modos.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <Por favor, certifique-se de que instalou o driver usb do Windows corretamente</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <Por favor, tenha certeza de que você copiou o arquivo de regras do udev para <b>%1</b> para obter as permissões de acesso USB corretas.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p>Visita a instrução de compilação e execução <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> para ajuda.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/>>p>Even sem um dispositivo você pode explorar a função do programa, basta pressionar o botão <b>Demo Mode</b> button.</p> + + + + Can't initalize USB: %1 + Não é possível initalizar o USB: %1 SpectrumDock - + Spectrum Espectro + + + Frequencybase + Frequência base + TriggerDock - + Trigger Gatilho - + CH%1 CH%1 - + + CH%1 smooth + CH%1 alisado + + + Mode Modo - + Slope Declive - + Source Fonte @@ -1152,9 +1640,29 @@ VoltageDock - + Voltage Voltagem + + + CH&%1 + + + + + MA&TH + + + + + Invert + + + + + x + + diff --git a/openhantek/translations/openhantek_ru.ts b/openhantek/translations/openhantek_ru.ts new file mode 100644 index 00000000..b2ac072a --- /dev/null +++ b/openhantek/translations/openhantek_ru.ts @@ -0,0 +1,1345 @@ + + + + + + + + Couldn't open device: %1 + Невозможно открыть устройство: %1 + + + + DsoConfigColorsPage + + + Axes + Оси + + + + Background + Фон + + + + Border + Рамка + + + + Grid + Сетка + + + + Markers + Маркеры + + + + Text + Текст + + + + Export hardcopy images or pdf files with printer colors + Экспорт изображений или pdf файлов с цветами принтера + + + + Screen and Print Colors + Цвета Экрана и Печати + + + + Screen + Экран + + + <hr width="100%"/> + <hr width="100%"/> + + + + Print + Печать + + + + + Channel + Канал + + + + + Spectrum + Спектр + + + + DsoConfigDialog + + + Settings + Настройки + + + + &Ok + &Ok + + + + &Apply + &Применить + + + + &Cancel + &Отмена + + + + Spectrum + Спектр + + + File + Файл + + + + Colors + Цвета + + + + Scope + Осциллограф + + + + DsoConfigScopePage + + + Off + Выкл + + + + Linear + Линейная + + + Timebase + Время/Деление + + + + Set slowest possible timebase + Установите максимальное значение Время/Деление + + + + Minimal time between captured frames<br/>(Longer times reduce the CPU load) + Минимальное время между захватами кадров<br/>(Больше времени - меньше загрузка процессора) + + + + Horizontal + Развёртка + + + + Interpolation + Интерполяция + + + + Digital phosphor depth + Глубина цифрового люминофора + + + + Graph + График + + + + Position + Положение + + + + Left + Слева + + + + Right + Справа + + + + Cursors + Курсоры + + + + Export zoomed screen in double height + Экспорт увеличенного экрана с двойной высотой + + + + Export + Экспорт + + + + Scope has hardware modification for AC coupling (restart needed to apply the change) + Осциллограф аппаратно модифицирован под переменный ток (для активации требуется перезапуск) + + + + Save settings on exit + Сохранять настройки при выходе + + + + Apply default settings after next restart + Применить настройки по умолчанию после перезапуска + + + + Save settings now + Сохранить текущие настройки + + + + Configuration + Конфигурация + + + + DsoConfigSpectrumPage + + + Rectangular + Прямоугольное + + + + Hamming + Хэмминга + + + + Hann + Ханна + + + + Cosine + Косинус + + + + Lanczos + Ланцоша + + + + Bartlett + Барлетта + + + + Triangular + Треугольное + + + + Gauss + Гаусса + + + + Bartlett-Hann + Барлетта-Ханна + + + + Blackman + Блэкмана + + + + Nuttall + Наталла + + + + Blackman-Harris + Блэкмана-Харриса + + + + Blackman-Nuttall + Блэкмана-Наталла + + + + Flat top + С плоской вершиной + + + + <b>Window function</b> + <b>Окно функции</b> + + + + <b>Reference level</b><br/>0 dBu = -2.2 dBV<br/>0 dBm (@600 &Omega;) = -2.2 dBV<br/>0 dBm (@50 &Omega;) = -13 dBV + <b>Опорный уровень</b><br/>0 дБн = -2.2 дБВ<br/>0 дБм (@600 &Omega;) = -2.2 дБВ<br/>0 дБм (@50 &Omega;) = -13 дБВ + + + + + dBV + дБВ + + + + <b>Minimum magnitude</b> + <b>Минимальная величина</b> + + + + Spectrum + Спектр + + + + DsoSettings + + + SP%1 + Спектр %1 + + + + CH%1 + Канал %1 + + + + SPM + Сп.Мат. + + + + MATH + Матем. + + + + DsoWidget + + + + + + + + /div + /дел + + + + TR + + + + + Markers + Маркеры + + + + + ON + ВКЛ + + + + + OFF + ВЫКЛ + + + + Markers + Маркеры + + + + Time: + t: + + + + Frequency: + f: + + + + Zoom x%L1 + Увел.x%L1 + + + + Zoom --- + Увел.--- + + + + %L1% + %L1% + + + + %1 %2 %3 %4 %5 + %1 %2 %3 %4 %5 + + + + /s + + + + + on screen + на экране + + + + pp + пп + + + + rms + скз + + + + ExporterRegistry + + + Data saved + Данные сохранены + + + + No data exported + Данные не экспортированы + + + + GlScope + + + System does not support OpenGL Shading Language (GLSL) + Система не поддерживает язык шейдеров OpenGL (GLSL) + + + + OpenGL init called twice! + OpenGL инициализация вызывается дважды! + + + + Failed to compile OpenGL shader programs. + + Не удалось скомпилировать шейдерные программы OpenGL. + + + + + Failed to link/bind OpenGL shader programs. + + Не удалось связать/привязать шейдерные программы OpenGL. + + + + + Failed to locate shader variable. + Не удалось найти переменную шейдера. + + + + HantekDsoControl + + + Couldn't get calibration data from oscilloscope's EEPROM. Use a config file for calibration! + Не удалось получить данные калибровки из EEPROM осциллографа. Используйте файл конфигурации для калибровки! + + + + HorizontalDock + + + Horizontal + Развёртка + + + + Samplerate + Дискретизация + + + + /s + + + + + Timebase + Время/Деление + + + Frequencybase + Частота/Деление + + + + Format + Формат вывода + + + + Calibration out + Выход калибровки + + + + LegacyExportDrawer + + + Export &CSV .. + Экспорт &CSV .. + + + + Comma-Separated Values (*.csv) + Значения, разделённые запятыми (*.csv) + + + + Save CSV + Сохранить CSV + + + + MainWindow + + + MainWindow + ГлавноеОкно + + + + &File + &Файл + + + + &View + &Вид + + + + &Oscilloscope + &Осциллограф + + + + &Export + &Экспорт + + + + Save settings &as .. + Сохранить настройки &как .. + + + + &Exit + &Выход + + + + S&ampling + &Дискретизация + + + + &Phosphor + &Люминофор + + + + &Histogram + &Гистограмма + + + + &Zoom + &Увеличение + + + + &Measure + &Измерение + + + + &User Manual + &Руководство пользователя + + + + &About .. + &О программе .. + + + + &Settings .. + &Настройки .. + + + + &Manual command + Командная &строка + + + + AC &Modification + &Модификация AC + + + Manual &command + Командная &строка + + + + &Help + По&мощь + + + + toolBar + Панель инструментов + + + + &Open settings .. + &Открыть настройки .. + + + + Ctrl+O + Ctrl+O + + + + &Save settings + &Сохранить настройки + + + + Ctrl+S + Ctrl+S + + + + Ctrl+Q + Ctrl+Q + + + Sampling + Дискретизация + + + + Space + Пробел + + + + &Refresh + &Обновить + + + + Docking windows + Стыковочные окна + + + + Toolbars + Панели инструментов + + + + OpenHantek6022 (%1) - Device %2 (FW%3) + OpenHantek6022 (%1) - Устройство %2 (Прошивка %3) + + + + Screenshot .. + Снимок экрана .. + + + + Invalid command + Неправильная команда + + + + Stop the oscilloscope + Стоп осциллографа + + + + Stop + Стоп + + + OpenHantek6022 (%1) - Device %2 + OpenHantek6022 (%1) - Устройство %2 + + + + Demo Mode + Демо Режим + + + + OpenHantek6022 (%1) - + + + + + Hardcopy .. + Хардкопия .. + + + + Print screen .. + Печать осциллограммы .. + + + + Start + Пуск + + + + Start the oscilloscope + Пуск осциллографа + + + + Open file + Открыть файл + + + + + Settings (*.conf) + Настройки (*.conf) + + + Settings (*.ini) + Настройки (*.ini) + + + + Save settings + Сохранить настройки + + + + Disable fading of previous graphs + Отключить угасание предыдущих графиков + + + + Enable fading of previous graphs + Включить угасание предыдущих графиков + + + + Hide histogram + Спрятать гистограмму + + + + Show histogram + Показать гистограмму + + + + Hide magnified scope + Спрятать область увеличения + + + + Show magnified scope + Показать область увеличения + + + + Hide measurements + Спрятать измерения + + + + Show measurements + Показать измерения + + + + About OpenHantek6022 (%1) + О программе OpenHantek6022 (%1) + + + + <p>Open source software for Hantek6022 USB oscilloscopes</p><p>Maintainer: Martin Homuth-Rosemann</p><p>Copyright &copy; 2010, 2011 Oliver Haag</p><p>Copyright &copy; 2012-2020 OpenHantek community<br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Open source firmware copyright &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + <p>Открытое программное обеспечение для USB осциллографов Hantek6022</p><p>Мейнтейнер: Мартин Хомут-Розманн</p><p>Авторские права &copy; 2010, 2011 Оливер Хааг</p><p>Авторские права &copy; 2012-2020 Сообщество OpenHantek <br/><a href='https://github.com/OpenHantek'>https://github.com/OpenHantek</a></p><p>Авторские права на открытую микропрограмму &copy; 2019-2020 Ho-Ro<br/><a href='https://github.com/Ho-Ro/Hantek6022API'>https://github.com/Ho-Ro/Hantek6022API</a></p> + + + + <p>Running since %1 seconds.</p> + <p>Работает в течение %1 секунд.</p> + + + + %1: %2 + %1: %2 + + + + yyyy-MM-dd hh:mm:ss + yyyy-MM-dd hh:mm:ss + + + + yyyyMMdd_hhmmss + yyyyMMdd_hhmmss + + + + Image (*.png *.jpg) + Изображение (*.png *.jpg) + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + + Save screenshot + Сохранить снимок экрана + + + + Print oscillograph + Печать осциллограммы + + + + QApplication + + + %L1 mV + %L1 мВ + + + + %L1 V + %L1 В + + + + %L1 dB + %L1 дБ + + + + %L1 ps + %L1 пс + + + + %L1 ns + %L1 нс + + + + %L1 ms + %L1 мс + + + + %L1 s + %L1 с + + + + %L1 µV + %L1 мкВ + + + + %L1 µs + %L1 мкс + + + + %L1 min + %L1 мин + + + + %L1 h + %L1 ч + + + + %L1 Hz + %L1 Гц + + + + %L1 kHz + %L1 кГц + + + + %L1 MHz + %L1 МГц + + + + %L1 GHz + %L1 ГГц + + + + %L1 S + %L1 С + + + + %L1 kS + %L1 кС + + + + %L1 MS + %L1 МС + + + + %L1 GS + %L1 ГС + + + + QCoreApplication + + + T - Y + T - Y + + + + X - Y + X - Y + + + + AC + AC + + + + DC + DC + + + + GND + GND + + + + Roll + Скроллинг + + + None + Отключен + + + + Auto + Автоматический + + + + Normal + Нормальный + + + + Single + Однократный + + + + CH1 + CH2 + Канал 1 + Канал 2 + + + + CH1 - CH2 + Канал 1 - Канал 2 + + + + CH2 - CH1 + Канал 2 - Канал 1 + + + + CH1 * CH2 + Канал 1 * Канал 2 + + + + CH1 AC + Канал 1 AC + + + + CH2 AC + Канал 2 AC + + + + Rectangular + Прямоугольное + + + + Hamming + Хэмминга + + + + Hann + Ханна + + + + Cosine + Косинус + + + + Lanczos + Ланцоша + + + + Bartlett + Барлетта + + + + Triangular + Треугольное + + + + Gauss + Гаусса + + + + Bartlett-Hann + Барлетта-Ханна + + + + Blackman + Блэкмана + + + + Nuttall + Наталла + + + + Blackman-Harris + Блэкмана-Харриса + + + + Blackman-Nuttall + Блэкмана-Наталла + + + + Flat top + С плоской вершиной + + + Success (no error) + Успех (без ошибок) + + + Input/output error + Ошибка ввода/вывода + + + Invalid parameter + Недопустимый параметр + + + Access denied (insufficient permissions) + Доступ запрещён (несовместимые разрешения) + + + No such device (it may have been disconnected) + Нет такого устройства (возможно оно не подключено) + + + Entity not found + Объект не найден + + + Resource busy + Ресурс занят + + + Operation timed out + Таймаут операции + + + Overflow + Переполнение + + + Pipe error + Отказ линии + + + System call interrupted (perhaps due to signal) + Системный вызов прерван (возможно, из-за сигнала) + + + Insufficient memory + Недостаточно памяти + + + Operation not supported or unimplemented on this platform + Операция не поддерживается или не реализована на этой платформе + + + Other error + Другая ошибка + + + + QObject + + + %1:%2 - No access + %1:%2 - Нет доступа + + + + %1:%2 (%3 - %4) + %1:%2 (%3 - %4) + + + + Devicename + Имя устройства + + + + Status + Статус + + + + SelectSupportedDevice + + + Select device + Выберите устройство + + + Supported device + Поддерживаемое устройство + + + + Devices: + Устройства: + + + + + ... + ... + + + + Demo Mode + Демо Режим + + + + <p><br/><b>The device is ready for use.</b></p><p>Please observe the <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>user manual</a> for safe operation.</p> + <p><br/><b>Устройство готово к работе.</b></p><p>Пожалуйста изучите <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/OpenHantek6022_User_Manual.pdf'>руководство пользователя</a> для безопасной эксплуатации устройства.</p> + + + + <p>Upload in progress ...</p><p><b>If the upload takes more than 30 s, please close this window <br/>and restart the program!</b></p> + <p>Идёт процесс загрузки ...</p><p><b>Если загрузка занимает более 30 секунд, закройте это окно <br/>и перезапустите программу!</b></p> + + + + <p><br/><b>Connection failed!</b></p> + <p><br/><b>Соединение не удалось!</b></p> + + + + <p>OpenHantek6022 is searching for compatible devices ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p> + <p>OpenHantek6022 ищет совместимые устройства ...</p><p><img align='right' height='200' src='qrc:///switch_6022BL.png'>Не забудьте переключить ваше устройство в режим осциллографа если оно имеет несколько режимов.</p> + + + + <p>Please make sure you have installed the windows usb driver correctly</p> + <p>Убедитесь в корректно установленных USB драйверах для Windows.</p> + + + + <p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p> + <p>Пожалуйста, убедитесь, что вы скопировали файл правил udev <b>%1</b> для корректных прав доступа к USB.</p> + + + + <p>Visit the build and run instruction <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>website</a> for help.</p> + <p>Посетите <a href='https://github.com/OpenHantek/OpenHantek6022/blob/master/docs/build.md'>вебсайт</a> с инструкциями по компиляции и запуску программы.</p> + + + + <hr/><p>Even without a device you can explore the program's function. Just press the <b>Demo Mode</b> button below.</p> + <hr/><p>Вы можете изучать программу и без осциллографа. Для этого нажмите кнопку <b>Демо Режима</b>.</p> + + + + Can't initalize USB: %1 + Не удалось инициализировать USB: %1 + + + + SpectrumDock + + + Spectrum + Спектр + + + + Frequencybase + Частота/Деление + + + + TriggerDock + + + Trigger + Синхронизация + + + + CH%1 + Канал %1 + + + + CH%1 smooth + Канал %1 (сглаженный) + + + + Mode + Режим + + + + Slope + Наклон + + + + Source + Источник + + + + VoltageDock + + + Voltage + Напряжение + + + + CH&%1 + Канал &%1 + + + + MA&TH + Ма&тем. + + + &MATH + &MATH + + + + Invert + Инверсия + + + + x + x + + + diff --git a/openhantek/translations/translations.qrc b/openhantek/translations/translations.qrc deleted file mode 100644 index 2393e709..00000000 --- a/openhantek/translations/translations.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - @QRC_ITEMS@ - - diff --git a/openhantek/translations/translations.qrc.template b/openhantek/translations/translations.qrc.template new file mode 100644 index 00000000..e103847e --- /dev/null +++ b/openhantek/translations/translations.qrc.template @@ -0,0 +1,4 @@ + + @QRC_ITEMS@ + + diff --git a/readme.md b/readme.md index a55bbaac..75ee16b7 100644 --- a/readme.md +++ b/readme.md @@ -1,72 +1,136 @@ -# OpenHantek [![Build Status](https://travis-ci.org/OpenHantek/openhantek.svg?branch=master)](https://travis-ci.org/OpenHantek/openhantek) [![Build status](https://ci.appveyor.com/api/projects/status/github/openhantek/openhantek?branch=master&svg=true)](https://ci.appveyor.com/project/openhantek/openhantek/branch/master) [![Stability: Unsupported](https://masterminds.github.io/stability/unsupported.svg)](https://masterminds.github.io/stability/unsupported.html) +# OpenHantek6022 +[![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/OpenHantek/OpenHantek6022)][release] +[![GitHub Release Date](https://img.shields.io/github/release-date/OpenHantek/OpenHantek6022?color=blue)][release] -OpenHantek is a free software for Hantek and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes. +[![GitHub commits since latest release](https://img.shields.io/github/commits-since/OpenHantek/OpenHantek6022/latest?color=brightgreen)][commits] +[![Build Status](https://travis-ci.com/OpenHantek/OpenHantek6022.svg)](https://travis-ci.com/OpenHantek/OpenHantek6022) +[![Build status](https://ci.appveyor.com/api/projects/status/github/OpenHantek/openhantek6022?svg=true)](https://ci.appveyor.com/project/Ho-Ro/openhantek6022) +[![CodeFactor](https://www.codefactor.io/repository/github/openhantek/openhantek6022/badge)](https://www.codefactor.io/repository/github/openhantek/openhantek6022) - - - -
Image of main window on linux Image of main window on Windows
+[release]: https://github.com/OpenHantek/OpenHantek6022/releases +[commits]: https://github.com/OpenHantek/OpenHantek6022/commits/master -* Supported operating systems: Linux, MacOSX, Windows¹, Android -* Supported devices: DSO2xxx Series, DSO52xx Series, 6022BE/BL +OpenHantek6022 is a free software for Hantek and compatible (Voltcraft/Darkwire/Protek/Acetech) USB digital signal oscilloscopes. +It was initially developed by [David Gräff and others](https://github.com/OpenHantek/openhantek/graphs/contributors) on [github.com/OpenHantek/openhantek](https://github.com/OpenHantek/openhantek). +After David [stopped maintaining](https://github.com/OpenHantek/openhantek/issues/277) the programm I cloned the repo to provide updates - **but only for Hantek 6022BE/BL**. -## Features +

Image of main window on linux

+ +* Supported devices: + * Hantek 6022BE and 6022BL as well as compatible scopes (e.g. Voltcraft DSO-2020). + * SainSmart DDS120 (thx [msiegert](https://github.com/msiegert)) - this device has a different analog front end + and uses the [slightly improved sigrok firmware](https://github.com/Ho-Ro/sigrok-firmware-fx2lafw), which has [some limitations](https://sigrok.org/wiki/SainSmart_DDS120/Info#Open-source_firmware_details) + compared to the Hantek scopes (see [#69](https://github.com/OpenHantek/OpenHantek6022/issues/69#issuecomment-607341694)). +* Demo mode is provided by the `-d` or `--demoMode` command line option. +* Fully supported operating system: Linux; developed under debian stable for amd64 architecture. +* Raspberry Pi packages (raspbian stable) are available on the [Releases](https://github.com/OpenHantek/OpenHantek6022/releases) page, check this [setup requirement](docs/build.md#raspberrypi). +* Compiles under FreeBSD (packaging / installation: work in progress, thx [tspspi](https://github.com/tspspi)). +* Other operating systems builds: [Windows](docs/images/screenshot_mainwindow_win.png) (partly tested) & MacOSX (untested). +* Uses [free open source firmware](https://github.com/Ho-Ro/Hantek6022API), no longer dependent on nonfree Hantek firmware. +* Extensive [User Manual](docs/OpenHantek6022_User_Manual.pdf) with technical specs and schematics. -* Digital phosphor effect to notice even short spikes -* Voltage and Spectrum view for all device supported chanels -* Math channel with these modes: Ch1+Ch2, Ch1-Ch2 -* Freely configurable colors -* Export to CSV, JPG, PNG or print the graphs -* Supports hardware and software triggered devices -* A zoom view with a freely selectable range -* All settings can be saved to a configuration file and loaded again -* Multiple instances with a different device each can be started +## Features +* Voltage and Spectrum view for all device supported chanels. +* CH1 and CH2 name becomes red when input is clipped (bottom left). +* Settable probe attenuation factor 1..1000 to accommodate a variety of different probes. +* Measure and display Vpp, RMS, DC (average), AC (rms) and AC as dB values as well as frequency of active channels. +* Math channel modes: CH1+CH2, CH1-CH2, CH2-CH1, CH1*CH2 and AC part of CH1 or CH2. +* Time base 100 ms/div .. 10 ns/div. +* Sample rates 10, 20, 50, 100, 200, 500 kS/s, 1, 2, 5, 10, 12, 15, 24, 30 MS/s (24 & 30 MS/s in CH1-only mode). +* 48 MS/s not supported due to unstable USB data streaming. +* Downsampling (up to 100x) increases solution and SNR. +* Calibration output square wave signal frequency can be selected between 50 Hz .. 100 kHz in 1/2/5 steps. +* Trigger modes: Normal, Auto and Single with green/red status display (top left). +* Calibration values loaded from eeprom or a model configuration file. +* [Calibration program](https://github.com/Ho-Ro/Hantek6022API/blob/master/README.md#create-calibration-values-for-openhantek) to create these values automatically. +* Digital phosphor effect to notice even short spikes; simple eye-diagram display with alternating trigger slope. +* Histogram function for voltage channels on right screen margin. +* A [zoom view](docs/images/screenshot_mainwindow_with_zoom.png) with a freely selectable range. +* Cursor measurement function for voltage, time, amplitude and frequency. +* Export of the graphs to CSV, JPG, PNG file or to the printer. +* Freely configurable colors. +* Automatic adaption of iconset for light and [dark themes](docs/images/screenshot_mainwindow_dark.png). * The dock views on the main window can be customized by dragging them around and stacking them. This allows a minimum window size of 640*480 for old workstation computers. +* All settings can be saved to a configuration file and loaded again. +* French, German, Russian and Spanish localisation complete, Italian and Portuguese translation ongoing. -## Install prebuilt binary -Navigate to the [Releases](https://github.com/OpenHantek/openhantek/releases) page. -* [Download Windows build](https://ci.appveyor.com/project/openhantek/openhantek/branch/master/artifacts) +## AC coupling +* A [little HW modification](docs/HANTEK6022_AC_Modification.pdf) adds AC coupling. OpenHantek6022 supports this feature since v2.17-rc5 / FW0204. ## Building OpenHantek from source You need the following software, to build OpenHantek from source: * [CMake 3.5+](https://cmake.org/download/) * [Qt 5.4+](https://www1.qt.io/download-open-source/) -* [FFTW 3+ (prebuild files will be downloaded on windows)](http://www.fftw.org/) -* libusb 1.x (prebuild files will be used on windows) -* A compiler that supports C++11 +* [FFTW 3+](http://www.fftw.org/) (prebuild files will be downloaded on windows) +* [libusb-1.0](https://libusb.info/), version >= 1.0.16 (prebuild files will be used on windows) +* A compiler that supports C++11 - tested with gcc, clang and msvc + +We have build instructions available for [Linux](docs/build.md#linux), [Raspberry Pi](docs/build.md#raspberrypi), [FreeBSD](docs/build.md#freebsd), [Apple MacOSX](docs/build.md#macosx) and [Microsoft Windows](docs/build.md#windows). + +## Install prebuilt binary +* Download Linux, Raspberry Pi, Windows and MacOSX packages from the [Releases](https://github.com/OpenHantek/OpenHantek6022/releases) page. +(The RPi, Windows and MacOSX packages are not tested, neither is the installation of the `*.rpm` packages. For RPi4 see also [issue #28](https://github.com/OpenHantek/OpenHantek6022/issues/28).) +* If you want to follow ongoing development, packages built from the last commit are available in the [unstable release](https://github.com/OpenHantek/OpenHantek6022/releases/tag/unstable). +* As I develop on a *Debian stable* system the preferred (native) package format is `*.deb`. +* To install the downloaded `*.deb` package, open a terminal window, go to the download directory and enter the command (as root) `apt install ./openhantek_..._amd64.deb`. +This command will automatically install all dependencies of the program as well. +* For installation of `*.rpm` packages follow similar rules, e.g. `dnf install ./openhantek-...-1.x86_64.rpm`. +* The `*.tar.gz` achives contain the same files as the `*.deb` and `*.rpm` packages for quick testing - do not use for a permanent intallation. Do not report any issues about the `*.tar.gz`! +* Get MacOSX packages from [macports](https://www.macports.org/ports.php?by=name&substr=openhantek) - thx [ra1nb0w](https://github.com/ra1nb0w). +* Get [Fedora rpm packages](https://pkgs.org/download/openhantek) - thx [Vascom](https://github.com/Vascom). +* Download [(untested) Windows build from last commit](https://ci.appveyor.com/project/Ho-Ro/openhantek6022/build/artifacts). + +## Run OpenHantek6022 +On a Linux system start the program via the menu entry *OpenHantek (Digital Storage Oscilloscope)* or from a terminal window as `OpenHantek`. -We have build instructions available for [Linux](docs/build.md#linux), [Apple MacOSX](docs/build.md#apple) and [Microsoft Windows](docs/build.md#windows). +You can explore the look and feel of OpenHantek6022 without the need for real scope hardware by running it from the command line as: `OpenHantek --demoMode`. -## Run OpenHantek -You need an OpenGL 3.2+ or OpenGL ES 2.0+ capable graphics hardware for OpenHantek. -OpenGL is prefered, if available. Overwrite this behaviour by starting OpenHantek +OpenHantek6022 runs also on legacy HW/SW that supports at least *OpenGL* 2.1+ or *OpenGL ES* 1.2+. +OpenGL is preferred, select *OpenGL ES* by starting OpenHantek from the command line like this: `OpenHantek --useGLES`. -USB access for the device is required: -* As seen on the [Microsoft Windows build instructions](docs/build.md#windows) page, you need a -special driver for Windows systems. -* On Linux, you need to copy the file `firmware/60-hantek.rules` to `/lib/udev/rules.d/` and replug your device. +Raspberry Pi uses OpenGL ES automatically, check also the [graphics driver setup](docs/build.md#raspberrypi). + +USB access for the device is required (unless using demo mode): +* On Linux, you need to copy the file `utils/udev_rules/60-hantek.rules` to `/etc/udev/rules.d/` or `/lib/udev/rules.d/` and replug your device. If OpenHantek is installed from a *.deb or *.rpm package this file is installed automatically into `/lib/udev/rules.d/`. -## Specifications, Features and limitations -Please refer to the [Specifications, Features, Limitations](docs/limitations.md) page. +### Windows USB access +* **The original Hantek driver for Windows doesn't work!** You have to assign the correct WinUSB driver with the tool [Zadig](https://zadig.akeo.ie/). +Check the [Microsoft Windows build instructions](docs/build.md#microsoft-windows-usb-driver-install-with-zadig) and follow the good [step-by-step tutorial](docs/OpenHantek6022_zadig_Win10.pdf) provided by [DaPa](https://github.com/DaPa). + +## Important! +The scope doesn't store the firmware permanently in flash or eeprom, it must be uploaded after each power-up and is kept in ram 'til power-down. +If the scope was used with a different software (old openhantek, sigrok or the windows software) the scope must be unplugged and replugged one-time before using it with OpenHantek6022 to enable the automatic loading of the correct firmware. +The top line of the program must display the correct firmware version (FW0207). + +## Specifications, features, limitations and developer documentation +I use this project mainly to explore how DSP software can improve and extend the [limitations](docs/limitations.md) of this kind of low level hardware. It would have been easy to spend a few bucks more to buy a powerful scope - but it would be much less fun :) +Please refer also to the [developer documentation](openhantek/readme.md) pages. ## Contribute -We welcome any reported Github Issue if you have a problem with this software. Send us a pull request for enhancements and fixes. Some random notes: +We welcome any reported GitHub issue if you have a problem with this software. Send us a pull request for enhancements and fixes. Some random notes: - Read [how to properly contribute to open source projects on GitHub][10]. - Create a separate branch other than *master* for your changes. It is not possible to directly commit to master on this repository. - Write [good commit messages][11]. - - Use the same [coding style and spacing][13] - (install clang-format. Use make target: `make format` or execute directly from the openhantek directory: `clang-format -style=file src/*`). - - Open a [pull request][12] with a clear title and description. + - Use the same [coding style and spacing][13] -> install clang-format and use make target: `make format` or execute directly: `clang-format -style=file -i *.cpp *.h`. + - It is mandatory that your commits are [Signed-off-by:][12], e.g. use git's command line option `-s` to append it automatically to your commit message: + `git commit -s -m 'This is my good commit message'` + - Open a [pull request][14] with a clear title and description. - Read [Add a new device](docs/adddevice.md) if you want to know how to add a device. - We recommend QtCreator as IDE on all platforms. It comes with CMake support, a decent compiler, and Qt out of the box. [10]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request [11]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -[12]: https://help.github.com/articles/using-pull-requests +[12]: https://github.com/probot/dco/blob/master/README.md [13]: http://llvm.org/docs/CodingStandards.html +[14]: https://help.github.com/articles/using-pull-requests ## Other DSO open source software -* [SigRok](http://www.sigrok.org) -* [Software for the Hantek 6022BE/BL only](http://pididu.com/wordpress/basicscope/) +* [Firmware used by OpenHantek and python bindings for 6022BE/BL](https://github.com/Ho-Ro/Hantek6022API) +* [sigrok](http://www.sigrok.org) +* [Software for the Hantek 6022BE/BL (win only)](http://pididu.com/wordpress/basicscope/) + +## Other related software +* [HScope for Android](https://hscope.martinloren.com/) A one-channel basic version is available free of charge (with in-app purchases). diff --git a/utils/applications/OpenHantek.desktop b/utils/applications/OpenHantek.desktop new file mode 100644 index 00000000..c216e703 --- /dev/null +++ b/utils/applications/OpenHantek.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=OpenHantek +GenericName=Digital Storage Oscilloscope +Comment=DSO software for Hantek DSO6022 USB hardware +Exec=OpenHantek +Icon=OpenHantek +Categories=Development;Engineering;Electronics; diff --git a/utils/devd_rules_freebsd/openhantek.conf b/utils/devd_rules_freebsd/openhantek.conf new file mode 100644 index 00000000..b822b5d1 --- /dev/null +++ b/utils/devd_rules_freebsd/openhantek.conf @@ -0,0 +1,80 @@ +# Part of OpenHantek6022 digital storage oscilloscope project +# Change the device permissions for supported scope devices +# from "rw------- root operator" to "rw-rw---- root openhantek" +# user must be member of the group "openhantek" + + +# Hantek DSO-6022BE + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b4"; + match "product" "0x6022"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b5"; + match "product" "0x6022"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + + +# Hantek DSO-6022BL + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b4"; + match "product" "0x602a"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b5"; + match "product" "0x602a"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + + +# Voltcraft DSO-2020 +# becomes DSO-6022BE as soon as FW is loaded + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b4"; + match "product" "0x2020"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + + +# SainSmart DDS-120 + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x8102"; + match "product" "0x8102"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; + +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04B5"; + match "product" "0x0120"; + action "chgrp openhantek /dev/$ugen; chmod g+rw /dev/$ugen; chgrp -h openhantek /dev/$ugen; chmod -h g+rw /dev/$ugen"; +}; diff --git a/utils/macdeployqtfix b/utils/macdeployqtfix new file mode 160000 index 00000000..2adfc7e0 --- /dev/null +++ b/utils/macdeployqtfix @@ -0,0 +1 @@ +Subproject commit 2adfc7e0ff78f90a0daaeaa7d63429de25dc97ac diff --git a/utils/udev_rules/60-hantek.rules b/utils/udev_rules/60-hantek.rules new file mode 100644 index 00000000..77b34b85 --- /dev/null +++ b/utils/udev_rules/60-hantek.rules @@ -0,0 +1,16 @@ +# rules for OpenHantek6022 (DSO program) as well as Hankek6022API (python tools) + +# Hantek DSO-6022BE, without FW, with FW +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", TAG+="uaccess", TAG+="udev-acl" +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="6022", TAG+="uaccess", TAG+="udev-acl" + +# Hantek DSO-6022BL, without FW, with FW +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", TAG+="uaccess", TAG+="udev-acl" +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="602a", TAG+="uaccess", TAG+="udev-acl" + +# Voltcraft DSO-2020, without FW (becomes DSO-6022BE when FW is uploaded) +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2020", TAG+="uaccess", TAG+="udev-acl" + +# BUUDAI DDS120, without FW, with FW +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", TAG+="uaccess", TAG+="udev-acl" +SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="0120", TAG+="uaccess", TAG+="udev-acl" diff --git a/utils/upload-github-release-assets/upload-github-release-assets.py b/utils/upload-github-release-assets/upload-github-release-assets.py new file mode 100755 index 00000000..8492de26 --- /dev/null +++ b/utils/upload-github-release-assets/upload-github-release-assets.py @@ -0,0 +1,212 @@ +#!/usr/bin/python3 +''' +Upload local files (e.g. RPi installation packages) as assets to a GitHub release. +It authorizes with the repo token and gets the upload_url for the relevant tag. +Existing remote files with the same name will be deleted before uploading. +''' + +######################### +# Default repo settings # +######################### +# # +OWNER = 'OpenHantek' # +REPO = 'OpenHantek6022' # +# # +######################### + + +import sys +import os.path +import requests +from http.client import responses +import json +import mimetypes +import argparse + + +# construct the argument parser and parse the arguments +ap = argparse.ArgumentParser() +ap.add_argument( "-o", "--owner", default = OWNER, + help = f"specify the repository owner, default = '{OWNER}'" ) +ap.add_argument( "-r", "--repo", default = REPO, + help = f"specify the repository, default = '{REPO}'" ) +ap.add_argument( "-t", "--tag", default = 'latest', + help = f"specify a tag, default = 'latest'" ) +ap.add_argument( "-u", "--unstable", action='store_true', + help = f"select 'unstable' release, overide '-t' or '--tag'" ) +ap.add_argument( 'files', metavar='FILE', type=str, nargs='*', + help = f"file to be uploaded" ) + +options = ap.parse_args() + +OWNER = options.owner +REPO = options.repo +TAG = options.tag +if options.unstable: + TAG = 'unstable' + +files = options.files + +#print( OWNER, REPO, TAG, files ) + +########################### +# github api v3 settings: # +# developer.github.com/v3 # +########################### +# +API = 'https://api.github.com/repos/' + + +################################ +# get my personal access token # +# (github.com/settings/tokens) # +################################ +# +TOKENFILE = os.path.expanduser( '~/.config/GitHub/REPO_TOKEN' ) +with open( TOKENFILE, 'r' ) as tokenfile: + TOKEN = tokenfile.readline().strip() + if len( TOKEN ) != 40: + print( "Authorization token not found" ) + sys.exit() + + +headers = { 'Authorization': 'token ' + TOKEN, } + + +##################################### +# build the right URL for this repo # +##################################### +# +MYREPO = OWNER + '/' + REPO + + +####################### +# check authorization # +####################### +# +timeout = ( 10, 30 ) # (connect, response) seconds time out +response = requests.get( API + MYREPO, headers=headers, timeout=timeout ) +if not response: + print( 'Authorizing error', response.status_code, responses[response.status_code] ) + sys.exit( -1 ) + + +################################## +# build the releases part of URL # +################################## +# +RELEASES = MYREPO + '/releases/' + + +##################### +# get the right tag # +##################### +# +if TAG and TAG != 'latest': + TAG = 'tags/' + TAG +else: + TAG = 'latest' + + +################################### +# get the upload URL for this tag # +################################### +# +response = requests.get( API + RELEASES + TAG, headers=headers, timeout=timeout ) +if not response: + print( 'Communication error', response.status_code, responses[response.status_code] ) + sys.exit( -1 ) + +rj = response.json() + + +# UPLOADURL is an URI with a format like this: +# https://uploads.github.com/repos/OpenHantek/OpenHantek6022/releases/12345678/assets{?name,label} +# truncate URI at 1st '{', because the asset name will be applied via "param = {'name' : NAME}" +UPLOADURL = rj[ 'upload_url' ].rsplit('{')[0] + +# uncomment to pretty print the response +#print( json.dumps( rj, indent=4 ) ) + +assets = rj['assets'] +# check if we have any files to display +if not assets: + sys.exit() # we are done + + +# get (and show) already uploaded assets +print( f"{(RELEASES + TAG).ljust(50)} updated dl" ) +already_uploaded_assets = {} +for a in assets: + name = a['name'] + url = a['url'] + already_uploaded_assets[ name ] = url + print( f"{name.ljust(50)} {a['updated_at']} {a['download_count']:#4}" ) + + +# check if we have any files to upload +if not files: + sys.exit() # we are done + + +############################ +# try to upload all assets # +############################ +# + +print('Uploading ...') + +processed = 0 +success = 0 + +for ASSET in files: + processed += 1 + + print( ASSET, flush=True, end='' ) + + if not os.path.isfile( ASSET ): + print( ' - file not found' ) + continue + + NAME = os.path.basename( ASSET ) # remove the leading path + + # if an asset with same name already exists, delete its URL + if NAME in already_uploaded_assets.keys(): + print( ' - delete remote file', flush=True, end='' ) + try: # delete existing asset (URL) + response = requests.delete( already_uploaded_assets[ NAME ], headers=headers, timeout=timeout ) + if not response: + print( ' error', response.status_code, responses[response.status_code] ) + except Exception as ex: + print( ' error', ex ) + + # prepare the mimetype header entry accordig to the file extension (typically '.deb', '.rpm' or '.tgz') + MIMETYPE, app = mimetypes.guess_type( NAME ) + if MIMETYPE: + headers[ 'Content-Type' ] = MIMETYPE + else: + headers[ 'Content-Type' ] = 'application/octet-stream' + + # define parameter 'name' + params = { 'name' : NAME } + + try: + with open( ASSET, 'rb' ) as asset: + print( ' - upload', flush=True, end = '' ) + try: + response = requests.post( UPLOADURL, headers=headers, params=params, data=asset, timeout=timeout ) + if not response: + print( ' - upload error', response.status_code, responses[response.status_code] ) + continue + except Exception as ex: + print( ' - upload error', ex ) + continue + else: + print( ) + success += 1 + except Exception as ex: + print( ' - file error', ex ) + continue + +if processed: + print( success, 'of', processed, 'file(s) successfully uploaded' )